feat: identify DRM outputs by EDID
This commit is contained in:
1 parent
8d305bf338
commit
5fce7227ed
1 file changed
+45
-3
@@ -26,7 +26,9 @@ use smithay::{
|
||||
output::{Mode as OutputMode, Output, PhysicalProperties, Scale, Subpixel},
|
||||
reexports::{
|
||||
calloop::EventLoop,
|
||||
drm::control::{Device as ControlDevice, Mode as DrmMode, ModeTypeFlags, connector, crtc},
|
||||
drm::control::{
|
||||
Device as ControlDevice, Mode as DrmMode, ModeTypeFlags, connector, crtc, property,
|
||||
},
|
||||
input::Libinput,
|
||||
rustix::fs::OFlags,
|
||||
wayland_protocols::wp::presentation_time::server::wp_presentation_feedback::Kind,
|
||||
@@ -619,7 +621,8 @@ fn create_direct_output(
|
||||
crtc: crtc::Handle,
|
||||
mode: DrmMode,
|
||||
) -> Result<DirectOutput, Box<dyn Error>> {
|
||||
let (output, global) = create_output(state, &connector, mode);
|
||||
let identity = connector_identity(drm, &connector);
|
||||
let (output, global) = create_output(state, &connector, mode, identity);
|
||||
let drm_surface = drm.create_surface(crtc, mode, &[connector.handle()])?;
|
||||
let allocator = GbmAllocator::new(
|
||||
gbm.clone(),
|
||||
@@ -649,9 +652,9 @@ fn create_output(
|
||||
state: &mut Ferese,
|
||||
connector: &connector::Info,
|
||||
mode: DrmMode,
|
||||
identity: String,
|
||||
) -> (Output, GlobalId) {
|
||||
let name = connector.to_string();
|
||||
let identity = format!("drm:{name}");
|
||||
let physical_size = connector.size().unwrap_or((0, 0));
|
||||
let output = Output::new(
|
||||
name,
|
||||
@@ -684,6 +687,40 @@ fn create_output(
|
||||
(output, global)
|
||||
}
|
||||
|
||||
fn connector_identity(drm: &DrmDevice, connector: &connector::Info) -> String {
|
||||
let edid = drm
|
||||
.get_properties(connector.handle())
|
||||
.ok()
|
||||
.and_then(|properties| {
|
||||
properties.iter().find_map(|(handle, raw)| {
|
||||
let info = drm.get_property(*handle).ok()?;
|
||||
if info.name().to_bytes() != b"EDID" {
|
||||
return None;
|
||||
}
|
||||
match info.value_type().convert_value(*raw) {
|
||||
property::Value::Blob(blob) if blob != 0 => drm.get_property_blob(blob).ok(),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
match edid {
|
||||
Some(edid) if !edid.is_empty() => {
|
||||
format!("drm:{}:{:016x}", connector, stable_hash(&edid))
|
||||
}
|
||||
_ => format!("drm:{connector}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn stable_hash(bytes: &[u8]) -> u64 {
|
||||
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
|
||||
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
|
||||
|
||||
bytes.iter().fold(FNV_OFFSET, |hash, byte| {
|
||||
(hash ^ u64::from(*byte)).wrapping_mul(FNV_PRIME)
|
||||
})
|
||||
}
|
||||
|
||||
impl DirectBackendState {
|
||||
pub fn record_presentation(
|
||||
&mut self,
|
||||
@@ -750,6 +787,11 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn connector_identity_hash_is_stable() {
|
||||
assert_eq!(stable_hash(b"hello"), 0xa430_d846_80aa_bd0b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn presentation_clock_uses_measured_monotonic_deltas() {
|
||||
let mut clock = PresentationClock::default();
|
||||
|
||||
Reference in new issue
Block a user