feat: select primary drm connector

This commit is contained in:
Samuel Onoja committed 2026-09-24 16:23:05 +01:00
1 parent c101b2cf8b
commit 9fdbb8e625
1 file changed
+108 -9
+108 -9
View File
@@ -10,8 +10,14 @@ use smithay::{
session::{Event as SessionEvent, Session, libseat::LibSeatSession}, session::{Event as SessionEvent, Session, libseat::LibSeatSession},
udev::{UdevBackend, UdevEvent, primary_gpu}, udev::{UdevBackend, UdevEvent, primary_gpu},
}, },
reexports::{calloop::EventLoop, input::Libinput, rustix::fs::OFlags}, output::{Mode as OutputMode, Output, PhysicalProperties, Scale, Subpixel},
utils::DeviceFd, reexports::{
calloop::EventLoop,
drm::control::{Device as ControlDevice, Mode as DrmMode, ModeTypeFlags, connector, crtc},
input::Libinput,
rustix::fs::OFlags,
},
utils::{DeviceFd, Transform},
}; };
use crate::Ferese; use crate::Ferese;
@@ -35,6 +41,14 @@ struct DirectDevice {
gbm: GbmDevice<DrmDeviceFd>, gbm: GbmDevice<DrmDeviceFd>,
#[allow(dead_code)] #[allow(dead_code)]
renderer: GlesRenderer, renderer: GlesRenderer,
#[allow(dead_code)]
connector: connector::Handle,
#[allow(dead_code)]
crtc: crtc::Handle,
#[allow(dead_code)]
mode: DrmMode,
#[allow(dead_code)]
output: Output,
} }
pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<(), Box<dyn Error>> { pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<(), Box<dyn Error>> {
@@ -121,12 +135,14 @@ fn open_primary_device(
state: &mut Ferese, state: &mut Ferese,
path: &Path, path: &Path,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let backend = state let mut session = state
.direct_backend .direct_backend
.as_mut() .as_ref()
.expect("direct backend state is initialized before the DRM device"); .expect("direct backend state is initialized before the DRM device")
.session
.clone();
let node = DrmNode::from_path(path)?; let node = DrmNode::from_path(path)?;
let fd = backend.session.open( let fd = session.open(
path, path,
OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK, OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK,
)?; )?;
@@ -138,6 +154,8 @@ fn open_primary_device(
let egl_context = EGLContext::new(&egl_display)?; let egl_context = EGLContext::new(&egl_display)?;
// SAFETY: the new context is not current on another thread and remains renderer-owned. // SAFETY: the new context is not current on another thread and remains renderer-owned.
let renderer = unsafe { GlesRenderer::new(egl_context)? }; let renderer = unsafe { GlesRenderer::new(egl_context)? };
let (connector, crtc, mode) = select_output(&drm)?;
let output = create_output(state, &connector, mode);
event_loop event_loop
.handle() .handle()
@@ -153,13 +171,94 @@ fn open_primary_device(
} }
})?; })?;
backend state
.direct_backend
.as_mut()
.expect("direct backend state remains initialized")
.devices .devices
.insert(node, DirectDevice { drm, gbm, renderer }); .insert(
tracing::info!(?node, ?path, "initialized primary DRM/GBM device"); node,
DirectDevice {
drm,
gbm,
renderer,
connector: connector.handle(),
crtc,
mode,
output,
},
);
state.relayout();
tracing::info!(?node, ?path, ?crtc, connector = %connector, "initialized primary DRM/GBM device");
Ok(()) Ok(())
} }
fn select_output(drm: &DrmDevice) -> io::Result<(connector::Info, crtc::Handle, DrmMode)> {
let resources = drm.resource_handles()?;
for handle in resources.connectors() {
let connector = drm.get_connector(*handle, true)?;
if connector.state() != connector::State::Connected {
continue;
}
let Some(mode) = connector
.modes()
.iter()
.find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED))
.or_else(|| connector.modes().first())
.copied()
else {
continue;
};
let current_crtc = connector
.current_encoder()
.and_then(|handle| drm.get_encoder(handle).ok())
.and_then(|encoder| encoder.crtc());
let compatible_crtc = connector.encoders().iter().find_map(|handle| {
let encoder = drm.get_encoder(*handle).ok()?;
resources
.filter_crtcs(encoder.possible_crtcs())
.into_iter()
.next()
});
if let Some(crtc) = current_crtc.or(compatible_crtc) {
return Ok((connector, crtc, mode));
}
}
Err(io::Error::new(
io::ErrorKind::NotFound,
"no connected desktop DRM connector with a usable CRTC",
))
}
fn create_output(state: &mut Ferese, connector: &connector::Info, mode: DrmMode) -> Output {
let name = connector.to_string();
let physical_size = connector.size().unwrap_or((0, 0));
let output = Output::new(
name,
PhysicalProperties {
size: (physical_size.0 as i32, physical_size.1 as i32).into(),
subpixel: Subpixel::from(connector.subpixel()),
make: "Unknown".into(),
model: "Unknown".into(),
},
);
let output_mode = OutputMode::from(mode);
output.create_global::<Ferese>(&state.display_handle);
output.set_preferred(output_mode);
output.change_current_state(
Some(output_mode),
Some(Transform::Normal),
Some(Scale::Integer(1)),
Some((0, 0).into()),
);
state.space.map_output(&output, (0, 0));
output
}
impl DirectBackendState { impl DirectBackendState {
pub fn record_presentation(&mut self, time: DrmEventTime, sequence: u32) -> Option<Duration> { pub fn record_presentation(&mut self, time: DrmEventTime, sequence: u32) -> Option<Duration> {
self.presentation.record(time, sequence) self.presentation.record(time, sequence)