From e27cf2969391f8a55279a0b7ee1e86f163210698 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 10 May 2022 16:43:09 +0200 Subject: [PATCH] config: tell the config about drm devices --- jay-config/src/_private/client.rs | 64 +++++++++++++++- jay-config/src/_private/ipc.rs | 44 ++++++++++- jay-config/src/drm.rs | 50 +++++++++++-- jay-config/src/input.rs | 4 +- jay-config/src/lib.rs | 18 ++++- src/backend.rs | 16 ++++ src/backends/dummy.rs | 8 +- src/backends/metal.rs | 4 - src/backends/metal/monitor.rs | 2 +- src/backends/metal/video.rs | 39 ++++++++-- src/backends/x.rs | 9 ++- src/compositor.rs | 3 + src/config.rs | 16 +++- src/config/handler.rs | 79 +++++++++++++++++++- src/it/test_backend.rs | 9 ++- src/it/test_config.rs | 2 + src/state.rs | 22 +++++- src/tasks.rs | 1 + src/tasks/backend.rs | 2 + src/tasks/connector.rs | 14 ++++ src/tasks/drmdev.rs | 120 ++++++++++++++++++++++++++++++ src/udev.rs | 104 ++++++++++++++++++++++---- src/xwayland.rs | 1 + 23 files changed, 581 insertions(+), 50 deletions(-) create mode 100644 src/tasks/drmdev.rs diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index e4cb5817..d981ab03 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -9,12 +9,12 @@ use { }, drm::{ connector_type::{ConnectorType, CON_UNKNOWN}, - Connector, Mode, + Connector, DrmDevice, Mode, }, input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat}, keyboard::keymap::Keymap, theme::Color, - Axis, Command, Direction, LogLevel, ModifiedKeySym, Timer, Workspace, + Axis, Command, Direction, LogLevel, ModifiedKeySym, PciId, Timer, Workspace, }, std::{ cell::{Cell, RefCell}, @@ -40,6 +40,8 @@ pub(crate) struct Client { on_connector_connected: RefCell>>, on_graphics_initialized: Cell>>, on_new_connector: RefCell>>, + on_new_drm_device: RefCell>>, + on_del_drm_device: RefCell>>, bufs: RefCell>>, reload: Cell, } @@ -124,6 +126,8 @@ pub unsafe extern "C" fn init( on_connector_connected: Default::default(), on_graphics_initialized: Default::default(), on_new_connector: Default::default(), + on_new_drm_device: Default::default(), + on_del_drm_device: Default::default(), bufs: Default::default(), reload: Cell::new(false), }); @@ -400,6 +404,36 @@ impl Client { self.send(&ClientMessage::ConnectorSetPosition { connector, x, y }); } + pub fn device_connectors(&self, device: DrmDevice) -> Vec { + let res = self.send_with_response(&ClientMessage::GetDeviceConnectors { device }); + get_response!(res, vec![], GetDeviceConnectors, connectors); + connectors + } + + pub fn drm_device_syspath(&self, device: DrmDevice) -> String { + let res = self.send_with_response(&ClientMessage::GetDrmDeviceSyspath { device }); + get_response!(res, String::new(), GetDrmDeviceSyspath, syspath); + syspath + } + + pub fn drm_device_vendor(&self, device: DrmDevice) -> String { + let res = self.send_with_response(&ClientMessage::GetDrmDeviceVendor { device }); + get_response!(res, String::new(), GetDrmDeviceVendor, vendor); + vendor + } + + pub fn drm_device_model(&self, device: DrmDevice) -> String { + let res = self.send_with_response(&ClientMessage::GetDrmDeviceModel { device }); + get_response!(res, String::new(), GetDrmDeviceModel, model); + model + } + + pub fn drm_device_pci_id(&self, device: DrmDevice) -> PciId { + let res = self.send_with_response(&ClientMessage::GetDrmDevicePciId { device }); + get_response!(res, Default::default(), GetDrmDevicePciId, pci_id); + pci_id + } + pub fn connector_connected(&self, connector: Connector) -> bool { let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector }); get_response!(res, false, ConnectorConnected, connected); @@ -429,6 +463,20 @@ impl Client { } } + pub fn drm_devices(&self) -> Vec { + let res = self.send_with_response(&ClientMessage::GetDrmDevices); + get_response!(res, vec![], GetDrmDevices, devices); + devices + } + + pub fn on_new_drm_device(&self, f: F) { + *self.on_new_drm_device.borrow_mut() = Some(Rc::new(f)); + } + + pub fn on_del_drm_device(&self, f: F) { + *self.on_del_drm_device.borrow_mut() = Some(Rc::new(f)); + } + pub fn on_new_connector(&self, f: F) { *self.on_new_connector.borrow_mut() = Some(Rc::new(f)); } @@ -591,6 +639,18 @@ impl Client { ServerMessage::Clear => { // only used by test config } + ServerMessage::NewDrmDev { device } => { + let handler = self.on_new_drm_device.borrow_mut(); + if let Some(handler) = handler.deref() { + handler(device); + } + } + ServerMessage::DelDrmDev { device } => { + let handler = self.on_del_drm_device.borrow_mut(); + if let Some(handler) = handler.deref() { + handler(device); + } + } } } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index c7871739..643eaeae 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -1,10 +1,10 @@ use { crate::{ - drm::{connector_type::ConnectorType, Connector}, + drm::{connector_type::ConnectorType, Connector, DrmDevice}, input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat}, keyboard::{keymap::Keymap, mods::Modifiers, syms::KeySym}, theme::Color, - Axis, Direction, LogLevel, Timer, Workspace, + Axis, Direction, LogLevel, PciId, Timer, Workspace, }, bincode::{BorrowDecode, Decode, Encode}, std::time::Duration, @@ -46,6 +46,12 @@ pub enum ServerMessage { timer: Timer, }, Clear, + NewDrmDev { + device: DrmDevice, + }, + DelDrmDev { + device: DrmDevice, + }, } #[derive(Encode, BorrowDecode, Debug)] @@ -241,6 +247,22 @@ pub enum ClientMessage<'a> { seat: Seat, }, Reload, + GetDeviceConnectors { + device: DrmDevice, + }, + GetDrmDeviceSyspath { + device: DrmDevice, + }, + GetDrmDeviceVendor { + device: DrmDevice, + }, + GetDrmDeviceModel { + device: DrmDevice, + }, + GetDrmDevices, + GetDrmDevicePciId { + device: DrmDevice, + }, } #[derive(Encode, Decode, Debug)] @@ -303,6 +325,24 @@ pub enum Response { GetFullscreen { fullscreen: bool, }, + GetDeviceConnectors { + connectors: Vec, + }, + GetDrmDeviceSyspath { + syspath: String, + }, + GetDrmDeviceVendor { + vendor: String, + }, + GetDrmDeviceModel { + model: String, + }, + GetDrmDevices { + devices: Vec, + }, + GetDrmDevicePciId { + pci_id: PciId, + }, } #[derive(Encode, Decode, Debug)] diff --git a/jay-config/src/drm.rs b/jay-config/src/drm.rs index 4faa61bf..00ba9990 100644 --- a/jay-config/src/drm.rs +++ b/jay-config/src/drm.rs @@ -1,9 +1,12 @@ use { - crate::drm::connector_type::{ - ConnectorType, CON_9PIN_DIN, CON_COMPONENT, CON_COMPOSITE, CON_DISPLAY_PORT, CON_DPI, - CON_DSI, CON_DVIA, CON_DVID, CON_DVII, CON_EDP, CON_EMBEDDED_WINDOW, CON_HDMIA, CON_HDMIB, - CON_LVDS, CON_SPI, CON_SVIDEO, CON_TV, CON_UNKNOWN, CON_USB, CON_VGA, CON_VIRTUAL, - CON_WRITEBACK, + crate::{ + drm::connector_type::{ + ConnectorType, CON_9PIN_DIN, CON_COMPONENT, CON_COMPOSITE, CON_DISPLAY_PORT, CON_DPI, + CON_DSI, CON_DVIA, CON_DVID, CON_DVII, CON_EDP, CON_EMBEDDED_WINDOW, CON_HDMIA, + CON_HDMIB, CON_LVDS, CON_SPI, CON_SVIDEO, CON_TV, CON_UNKNOWN, CON_USB, CON_VGA, + CON_VIRTUAL, CON_WRITEBACK, + }, + PciId, }, bincode::{Decode, Encode}, std::str::FromStr, @@ -88,6 +91,18 @@ impl Connector { } } +pub fn drm_devices() -> Vec { + get!().drm_devices() +} + +pub fn on_new_drm_device(f: F) { + get!().on_new_drm_device(f) +} + +pub fn on_drm_device_removed(f: F) { + get!().on_del_drm_device(f) +} + pub fn on_new_connector(f: F) { get!().on_new_connector(f) } @@ -186,3 +201,28 @@ pub mod connector_type { pub const CON_USB: ConnectorType = ConnectorType(20); pub const CON_EMBEDDED_WINDOW: ConnectorType = ConnectorType(u32::MAX); } + +#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub struct DrmDevice(pub u64); + +impl DrmDevice { + pub fn connectors(self) -> Vec { + get!().device_connectors(self) + } + + pub fn syspath(self) -> String { + get!().drm_device_syspath(self) + } + + pub fn vendor(self) -> String { + get!().drm_device_vendor(self) + } + + pub fn model(self) -> String { + get!().drm_device_model(self) + } + + pub fn pci_id(self) -> PciId { + get!().drm_device_pci_id(self) + } +} diff --git a/jay-config/src/input.rs b/jay-config/src/input.rs index 33a2c306..61261930 100644 --- a/jay-config/src/input.rs +++ b/jay-config/src/input.rs @@ -156,9 +156,7 @@ pub fn get_seats() -> Vec { } pub fn input_devices() -> Vec { - let mut res = vec![]; - (|| res = get!().get_input_devices(None))(); - res + get!().get_input_devices(None) } pub fn remove_all_seats() {} diff --git a/jay-config/src/lib.rs b/jay-config/src/lib.rs index 2aadfcc1..c01e9269 100644 --- a/jay-config/src/lib.rs +++ b/jay-config/src/lib.rs @@ -1,7 +1,11 @@ use { crate::keyboard::{keymap::Keymap, ModifiedKeySym}, bincode::{Decode, Encode}, - std::{collections::HashMap, time::Duration}, + std::{ + collections::HashMap, + fmt::{Debug, Display, Formatter}, + time::Duration, + }, }; #[macro_use] @@ -129,3 +133,15 @@ pub fn reload() { pub fn is_reload() -> bool { get!(false).is_reload() } + +#[derive(Encode, Decode, Debug, Copy, Clone, Hash, Eq, PartialEq, Default)] +pub struct PciId { + pub vendor: u32, + pub model: u32, +} + +impl Display for PciId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:04x}:{:04x}", self.vendor, self.model) + } +} diff --git a/src/backend.rs b/src/backend.rs index 36e8b7d5..90de80cf 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -11,10 +11,12 @@ use { fmt::{Debug, Display, Formatter}, rc::Rc, }, + uapi::c, }; linear_ids!(ConnectorIds, ConnectorId); linear_ids!(InputDeviceIds, InputDeviceId); +linear_ids!(DrmDeviceIds, DrmDeviceId); pub trait Backend { fn run(self: Rc) -> SpawnedFuture>>; @@ -77,6 +79,7 @@ pub trait Connector { fn event(&self) -> Option; fn on_change(&self, cb: Rc); fn damage(&self); + fn drm_dev(&self) -> Option; } #[derive(Debug)] @@ -121,6 +124,7 @@ pub enum InputDeviceAccelProfile { } pub enum BackendEvent { + NewDrmDevice(Rc), NewConnector(Rc), NewInputDevice(Rc), } @@ -163,3 +167,15 @@ pub enum InputEvent { AxisDiscrete(i32, ScrollAxis), Frame, } + +pub enum DrmEvent { + #[allow(dead_code)] + Removed, +} + +pub trait BackendDrmDevice { + fn id(&self) -> DrmDeviceId; + fn event(&self) -> Option; + fn on_change(&self, cb: Rc); + fn dev_t(&self) -> c::dev_t; +} diff --git a/src/backends/dummy.rs b/src/backends/dummy.rs index 4473c060..ace3dbfa 100644 --- a/src/backends/dummy.rs +++ b/src/backends/dummy.rs @@ -1,7 +1,9 @@ use { crate::{ async_engine::SpawnedFuture, - backend::{Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId}, + backend::{ + Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, + }, video::drm::ConnectorType, }, std::{any::Any, error::Error, rc::Rc}, @@ -46,4 +48,8 @@ impl Connector for DummyOutput { fn damage(&self) { // nothing } + + fn drm_dev(&self) -> Option { + None + } } diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 0a8cff95..154f396a 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -111,8 +111,6 @@ pub enum MetalError { DeviceResumeSignalHandler(#[source] DbusError), } -linear_ids!(DrmIds, DrmId); - pub struct MetalBackend { state: Rc, udev: Rc, @@ -122,7 +120,6 @@ pub struct MetalBackend { libinput_fd: AsyncFd, device_holder: Rc, session: Session, - drm_ids: DrmIds, pause_handler: Cell>, resume_handler: Cell>, ctx: CloneCell>>, @@ -252,7 +249,6 @@ pub async fn create(state: &Rc) -> Result, MetalError> { libinput_fd, device_holder, session, - drm_ids: Default::default(), pause_handler: Default::default(), resume_handler: Default::default(), ctx: Default::default(), diff --git a/src/backends/metal/monitor.rs b/src/backends/metal/monitor.rs index ff10fca6..598b644f 100644 --- a/src/backends/metal/monitor.rs +++ b/src/backends/metal/monitor.rs @@ -183,7 +183,7 @@ impl MetalBackend { } let devnum = dev.devnum(); let devnode = dev.devnode()?; - let id = self.drm_ids.next(); + let id = self.state.drm_dev_ids.next(); log::info!("Device added: {}", devnode.to_bytes().as_bstr()); let dev = PendingDrmDevice { id, diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index e6aab9f2..98394040 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -2,9 +2,10 @@ use { crate::{ async_engine::{AsyncFd, Phase, SpawnedFuture}, backend::{ - BackendEvent, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, MonitorInfo, + BackendDrmDevice, BackendEvent, Connector, ConnectorEvent, ConnectorId, + ConnectorKernelId, DrmDeviceId, MonitorInfo, }, - backends::metal::{DrmId, MetalBackend, MetalError}, + backends::metal::{MetalBackend, MetalError}, edid::Descriptor, format::{Format, XRGB8888}, ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC}, @@ -35,11 +36,11 @@ use { fmt::{Debug, Formatter}, rc::Rc, }, - uapi::c, + uapi::{c, c::dev_t}, }; pub struct PendingDrmDevice { - pub id: DrmId, + pub id: DrmDeviceId, pub devnum: c::dev_t, pub devnode: CString, } @@ -51,7 +52,7 @@ pub struct MetalRenderContext { #[derive(Debug)] pub struct MetalDrmDeviceStatic { - pub id: DrmId, + pub id: DrmDeviceId, pub devnum: c::dev_t, pub devnode: CString, pub master: Rc, @@ -67,6 +68,24 @@ pub struct MetalDrmDeviceStatic { pub handle_events: HandleEvents, } +impl BackendDrmDevice for MetalDrmDeviceStatic { + fn id(&self) -> DrmDeviceId { + self.id + } + + fn event(&self) -> Option { + None + } + + fn on_change(&self, _cb: Rc) { + // nothing + } + + fn dev_t(&self) -> dev_t { + self.devnum + } +} + pub struct HandleEvents { pub handle_events: Cell>>, } @@ -249,6 +268,10 @@ impl Connector for MetalConnector { self.schedule_present(); } } + + fn drm_dev(&self) -> Option { + Some(self.dev.id) + } } #[derive(Debug)] @@ -760,13 +783,17 @@ impl MetalBackend { let (connectors, futures) = get_connectors(&self, &dev, &resources.connectors)?; let slf = Rc::new(MetalDrmDevice { - dev, + dev: dev.clone(), connectors, futures, }); self.init_drm_device(&slf)?; + self.state + .backend_events + .push(BackendEvent::NewDrmDevice(dev.clone())); + for connector in slf.connectors.values() { self.state .backend_events diff --git a/src/backends/x.rs b/src/backends/x.rs index ee99c66f..e6e433fa 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -3,8 +3,9 @@ use { async_engine::{Phase, SpawnedFuture}, backend::{ AxisSource, Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId, - ConnectorKernelId, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, - InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo, ScrollAxis, TransformMatrix, + ConnectorKernelId, DrmDeviceId, InputDevice, InputDeviceAccelProfile, + InputDeviceCapability, InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo, + ScrollAxis, TransformMatrix, }, fixed::Fixed, format::XRGB8888, @@ -948,6 +949,10 @@ impl Connector for XOutput { fn damage(&self) { // nothing } + + fn drm_dev(&self) -> Option { + None + } } struct XSeat { diff --git a/src/compositor.rs b/src/compositor.rs index 05e201c2..92c10d89 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -157,6 +157,7 @@ fn start_compositor2( logger, connectors: Default::default(), outputs: Default::default(), + drm_devs: Default::default(), status: Default::default(), idle: IdleState { input: Default::default(), @@ -180,6 +181,7 @@ fn start_compositor2( config_file_id: NumCell::new(1), tracker: Default::default(), data_offer_ids: Default::default(), + drm_dev_ids: Default::default(), }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); @@ -350,6 +352,7 @@ fn create_dummy_output(state: &Rc) { handler: Cell::new(None), connected: Cell::new(true), name: "Dummy".to_string(), + drm_dev: None, }), 0, &backend::Mode { diff --git a/src/config.rs b/src/config.rs index b86faf9c..6926868b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ mod handler; use crate::it::test_config::TEST_CONFIG_ENTRY; use { crate::{ - backend::{ConnectorId, InputDeviceId}, + backend::{ConnectorId, DrmDeviceId, InputDeviceId}, config::handler::ConfigProxyHandler, ifs::wl_seat::SeatId, state::State, @@ -19,7 +19,7 @@ use { ipc::{InitMessage, ServerMessage, V1InitMessage}, ConfigEntry, VERSION, }, - drm::Connector, + drm::{Connector, DrmDevice}, input::{InputDevice, Seat}, keyboard::ModifiedKeySym, }, @@ -70,6 +70,18 @@ impl ConfigProxy { }); } + pub fn new_drm_dev(&self, dev: DrmDeviceId) { + self.send(&ServerMessage::NewDrmDev { + device: DrmDevice(dev.raw() as _), + }); + } + + pub fn del_drm_dev(&self, dev: DrmDeviceId) { + self.send(&ServerMessage::DelDrmDev { + device: DrmDevice(dev.raw() as _), + }); + } + pub fn new_connector(&self, connector: ConnectorId) { self.send(&ServerMessage::NewConnector { device: Connector(connector.raw() as _), diff --git a/src/config/handler.rs b/src/config/handler.rs index ef1db086..b0445f3b 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -2,12 +2,13 @@ use { crate::{ async_engine::{AsyncError, SpawnedFuture, Timer}, backend::{ - self, ConnectorId, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, + self, ConnectorId, DrmDeviceId, InputDeviceAccelProfile, InputDeviceCapability, + InputDeviceId, }, compositor::MAX_EXTENTS, config::ConfigProxy, ifs::wl_seat::{SeatId, WlSeatGlobal}, - state::{ConnectorData, DeviceHandlerData, OutputData, State}, + state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State}, tree::{ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase}, utils::{ copyhashmap::CopyHashMap, debug_fn::debug_fn, errorfmt::ErrorFmt, numcell::NumCell, @@ -21,7 +22,7 @@ use { bincode_ops, ipc::{ClientMessage, Response, ServerMessage}, }, - drm::Connector, + drm::{Connector, DrmDevice}, input::{ acceleration::{AccelProfile, ACCEL_PROFILE_ADAPTIVE, ACCEL_PROFILE_FLAT}, capability::{ @@ -152,6 +153,53 @@ impl ConfigProxyHandler { res } + fn handle_get_drm_device_connectors(&self, dev: DrmDevice) -> Result<(), CphError> { + let dev = self.get_drm_device(dev)?; + let mut connectors = vec![]; + for c in dev.connectors.lock().values() { + connectors.push(Connector(c.connector.id().raw() as _)); + } + self.respond(Response::GetDeviceConnectors { connectors }); + Ok(()) + } + + fn handle_get_drm_device_syspath(&self, dev: DrmDevice) -> Result<(), CphError> { + let dev = self.get_drm_device(dev)?; + let syspath = dev.syspath.clone().unwrap_or_default(); + self.respond(Response::GetDrmDeviceSyspath { syspath }); + Ok(()) + } + + fn handle_get_drm_device_vendor(&self, dev: DrmDevice) -> Result<(), CphError> { + let dev = self.get_drm_device(dev)?; + let vendor = dev.vendor.clone().unwrap_or_default(); + self.respond(Response::GetDrmDeviceVendor { vendor }); + Ok(()) + } + + fn handle_get_drm_devices(&self) { + let devs = self.state.drm_devs.lock(); + let mut res = vec![]; + for dev in devs.values() { + res.push(DrmDevice(dev.dev.id().raw() as _)); + } + self.respond(Response::GetDrmDevices { devices: res }); + } + + fn handle_get_drm_device_model(&self, dev: DrmDevice) -> Result<(), CphError> { + let dev = self.get_drm_device(dev)?; + let model = dev.model.clone().unwrap_or_default(); + self.respond(Response::GetDrmDeviceModel { model }); + Ok(()) + } + + fn handle_get_drm_device_pci_id(&self, dev: DrmDevice) -> Result<(), CphError> { + let dev = self.get_drm_device(dev)?; + let pci_id = dev.pci_id.unwrap_or_default(); + self.respond(Response::GetDrmDevicePciId { pci_id }); + Ok(()) + } + fn handle_reload(&self) { log::info!("Reloading config"); let config = match ConfigProxy::from_config_dir(&self.state) { @@ -357,6 +405,13 @@ impl ConfigProxyHandler { } } + fn get_drm_device(&self, dev: DrmDevice) -> Result, CphError> { + match self.state.drm_devs.get(&DrmDeviceId::from_raw(dev.0 as _)) { + Some(dev) => Ok(dev), + _ => Err(CphError::DrmDeviceDoesNotExist(dev)), + } + } + fn get_seat(&self, seat: Seat) -> Result, CphError> { let seats = self.state.globals.seats.lock(); for seat_global in seats.values() { @@ -944,6 +999,22 @@ impl ConfigProxyHandler { self.handle_get_fullscreen(seat).wrn("get_fullscreen")? } ClientMessage::Reload => self.handle_reload(), + ClientMessage::GetDeviceConnectors { device } => self + .handle_get_drm_device_connectors(device) + .wrn("get_device_connectors")?, + ClientMessage::GetDrmDeviceSyspath { device } => self + .handle_get_drm_device_syspath(device) + .wrn("get_drm_device_syspath")?, + ClientMessage::GetDrmDeviceVendor { device } => self + .handle_get_drm_device_vendor(device) + .wrn("get_drm_device_vendor")?, + ClientMessage::GetDrmDeviceModel { device } => self + .handle_get_drm_device_model(device) + .wrn("get_drm_device_model")?, + ClientMessage::GetDrmDevices => self.handle_get_drm_devices(), + ClientMessage::GetDrmDevicePciId { device } => self + .handle_get_drm_device_pci_id(device) + .wrn("get_drm_device_pci_id")?, } Ok(()) } @@ -985,6 +1056,8 @@ enum CphError { KeymapDoesNotExist(Keymap), #[error("Seat {0:?} does not exist")] SeatDoesNotExist(Seat), + #[error("DRM device {0:?} does not exist")] + DrmDeviceDoesNotExist(DrmDevice), #[error("Workspace {0:?} does not exist")] WorkspaceDoesNotExist(Workspace), #[error("Keyboard {0:?} does not exist")] diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index 9fdbe9c0..35b3ba7d 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -3,8 +3,9 @@ use { async_engine::SpawnedFuture, backend::{ AxisSource, Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId, - ConnectorKernelId, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, - InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo, ScrollAxis, TransformMatrix, + ConnectorKernelId, DrmDeviceId, InputDevice, InputDeviceAccelProfile, + InputDeviceCapability, InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo, + ScrollAxis, TransformMatrix, }, compositor::TestFuture, fixed::Fixed, @@ -241,6 +242,10 @@ impl Connector for TestConnector { fn damage(&self) { // nothing } + + fn drm_dev(&self) -> Option { + None + } } pub struct TestMouseClick { diff --git a/src/it/test_config.rs b/src/it/test_config.rs index c84d8853..b0feb417 100644 --- a/src/it/test_config.rs +++ b/src/it/test_config.rs @@ -102,6 +102,8 @@ unsafe extern "C" fn handle_msg(data: *const u8, msg: *const u8, size: usize) { ServerMessage::TimerExpired { .. } => {} ServerMessage::GraphicsInitialized => tc.graphics_initialized.set(true), ServerMessage::Clear => tc.clear(), + ServerMessage::NewDrmDev { .. } => {} + ServerMessage::DelDrmDev { .. } => {} } } diff --git a/src/state.rs b/src/state.rs index c738a332..cabe89d1 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,8 +3,8 @@ use { acceptor::Acceptor, async_engine::{AsyncEngine, SpawnedFuture}, backend::{ - Backend, BackendEvent, Connector, ConnectorId, ConnectorIds, InputDevice, - InputDeviceId, InputDeviceIds, MonitorInfo, + Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorId, ConnectorIds, + DrmDeviceId, DrmDeviceIds, InputDevice, InputDeviceId, InputDeviceIds, MonitorInfo, }, backends::dummy::DummyBackend, cli::RunArgs, @@ -43,7 +43,7 @@ use { xwayland::{self, XWaylandEvent}, }, ahash::AHashMap, - jay_config::Direction, + jay_config::{Direction, PciId}, std::{ cell::{Cell, RefCell}, fmt::{Debug, Formatter}, @@ -71,6 +71,7 @@ pub struct State { pub clients: Clients, pub globals: Globals, pub connector_ids: ConnectorIds, + pub drm_dev_ids: DrmDeviceIds, pub seat_ids: SeatIds, pub idle_inhibitor_ids: IdleInhibitorIds, pub input_device_ids: InputDeviceIds, @@ -95,6 +96,7 @@ pub struct State { pub logger: Option>, pub connectors: CopyHashMap>, pub outputs: CopyHashMap>, + pub drm_devs: CopyHashMap>, pub status: CloneCell>, pub idle: IdleState, pub run_args: RunArgs, @@ -171,6 +173,7 @@ pub struct ConnectorData { pub handler: Cell>>, pub connected: Cell, pub name: String, + pub drm_dev: Option>, } pub struct OutputData { @@ -179,6 +182,16 @@ pub struct OutputData { pub node: Rc, } +pub struct DrmDevData { + pub dev: Rc, + pub handler: Cell>>, + pub connectors: CopyHashMap>, + pub syspath: Option, + pub vendor: Option, + pub model: Option, + pub pci_id: Option, +} + impl State { pub fn set_render_ctx(&self, ctx: Option<&Rc>) { self.render_ctx.set(ctx.cloned()); @@ -486,6 +499,9 @@ impl State { self.xwayland.handler.borrow_mut().take(); self.xwayland.queue.clear(); self.idle.inhibitors.clear(); + for (_, drm_dev) in self.drm_devs.lock().drain() { + drm_dev.handler.take(); + } for (_, connector) in self.connectors.lock().drain() { connector.handler.take(); } diff --git a/src/tasks.rs b/src/tasks.rs index 00042607..e20b1037 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -1,5 +1,6 @@ mod backend; mod connector; +mod drmdev; mod idle; mod input_device; mod slow_clients; diff --git a/src/tasks/backend.rs b/src/tasks/backend.rs index 02dad2e9..291a1af4 100644 --- a/src/tasks/backend.rs +++ b/src/tasks/backend.rs @@ -1,4 +1,5 @@ use { + super::drmdev, crate::{ backend::BackendEvent, state::State, @@ -23,6 +24,7 @@ impl BackendEventHandler { match event { BackendEvent::NewConnector(connector) => connector::handle(&self.state, &connector), BackendEvent::NewInputDevice(s) => input_device::handle(&self.state, s), + BackendEvent::NewDrmDevice(d) => drmdev::handle(&self.state, d), } } } diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 74dd6bed..5dfbf1dd 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -13,13 +13,24 @@ use { }; pub fn handle(state: &Rc, connector: &Rc) { + let mut drm_dev = None; + if let Some(dev_id) = connector.drm_dev() { + drm_dev = match state.drm_devs.get(&dev_id) { + Some(dev) => Some(dev), + _ => panic!("connector's drm device does not exist"), + }; + } let id = connector.id(); let data = Rc::new(ConnectorData { connector: connector.clone(), handler: Default::default(), connected: Cell::new(false), name: connector.kernel_id().to_string(), + drm_dev: drm_dev.clone(), }); + if let Some(dev) = drm_dev { + dev.connectors.set(id, data.clone()); + } let oh = ConnectorHandler { id, state: state.clone(), @@ -150,5 +161,8 @@ impl ConnectorHandler { self.state.root.outputs.remove(&self.id); self.data.connected.set(false); self.state.outputs.remove(&self.id); + if let Some(dev) = &self.data.drm_dev { + dev.connectors.remove(&self.id); + } } } diff --git a/src/tasks/drmdev.rs b/src/tasks/drmdev.rs new file mode 100644 index 00000000..02742ec7 --- /dev/null +++ b/src/tasks/drmdev.rs @@ -0,0 +1,120 @@ +use { + crate::{ + backend::{BackendDrmDevice, DrmDeviceId, DrmEvent}, + state::{DrmDevData, State}, + udev::{Udev, UdevDeviceType}, + utils::{asyncevent::AsyncEvent, errorfmt::ErrorFmt}, + }, + jay_config::PciId, + std::{cell::Cell, rc::Rc}, +}; + +pub fn handle(state: &Rc, dev: Rc) { + let id = dev.id(); + let mut syspath = None; + let mut vendor = None; + let mut model = None; + let mut pci_id = None; + 'properties: { + let udev = match Udev::new() { + Ok(udev) => Rc::new(udev), + Err(e) => { + log::error!("Could not create a udev instance: {}", e); + break 'properties; + } + }; + let odev = match udev.create_device_from_devnum(UdevDeviceType::Character, dev.dev_t()) { + Ok(dev) => dev, + Err(e) => { + log::error!("{}", ErrorFmt(e)); + break 'properties; + } + }; + let dev = match odev.parent() { + Ok(dev) => dev, + Err(e) => { + log::error!("{}", ErrorFmt(e)); + break 'properties; + } + }; + syspath = dev.syspath().map(|s| s.to_string_lossy().into_owned()); + vendor = dev.vendor().map(|s| s.to_string_lossy().into_owned()); + model = dev.model().map(|s| s.to_string_lossy().into_owned()); + 'get_pci_id: { + let id = match dev.pci_id() { + Some(id) => id, + _ => break 'get_pci_id, + }; + let id = id.to_string_lossy(); + let colon = match id.find(':') { + Some(pos) => pos, + _ => break 'get_pci_id, + }; + let vendor = &id[..colon]; + let model = &id[colon + 1..]; + let vendor = match u32::from_str_radix(vendor, 16) { + Ok(v) => v, + _ => break 'get_pci_id, + }; + let model = match u32::from_str_radix(model, 16) { + Ok(v) => v, + _ => break 'get_pci_id, + }; + pci_id = Some(PciId { vendor, model }); + } + } + let data = Rc::new(DrmDevData { + dev: dev.clone(), + handler: Cell::new(None), + connectors: Default::default(), + syspath, + vendor, + model, + pci_id, + }); + let oh = DrvDevHandler { + id, + state: state.clone(), + data: data.clone(), + }; + let future = state.eng.spawn(oh.handle()); + data.handler.set(Some(future)); + if state.drm_devs.set(id, data).is_some() { + panic!("Drm device id has been reused"); + } +} + +struct DrvDevHandler { + id: DrmDeviceId, + state: Rc, + data: Rc, +} + +impl DrvDevHandler { + async fn handle(self) { + let ae = Rc::new(AsyncEvent::default()); + { + let ae = ae.clone(); + self.data.dev.on_change(Rc::new(move || ae.trigger())); + } + if let Some(config) = self.state.config.get() { + config.new_drm_dev(self.id); + } + 'outer: loop { + while let Some(event) = self.data.dev.event() { + match event { + DrmEvent::Removed => break 'outer, + } + } + ae.triggered().await; + } + if !self.data.connectors.is_empty() { + panic!("DRM device removed before its connectors"); + } + if let Some(config) = self.state.config.get() { + config.del_drm_dev(self.id); + } + self.data.handler.set(None); + self.state.drm_devs.remove(&self.id); + } +} diff --git a/src/udev.rs b/src/udev.rs index 7eb699ad..fe4e32ce 100644 --- a/src/udev.rs +++ b/src/udev.rs @@ -1,7 +1,8 @@ use { + crate::utils::oserror::OsError, std::{ffi::CStr, marker::PhantomData, ptr, rc::Rc}, thiserror::Error, - uapi::{c, Errno, IntoUstr}, + uapi::{c, ustr, Errno, IntoUstr, Ustr}, }; #[link(name = "udev")] @@ -42,36 +43,52 @@ extern "C" { fn udev_device_new_from_syspath(udev: *mut udev, syspath: *const c::c_char) -> *mut udev_device; + fn udev_device_ref(udev_device: *mut udev_device) -> *mut udev_device; fn udev_device_unref(udev_device: *mut udev_device) -> *mut udev_device; fn udev_device_get_sysname(udev_device: *mut udev_device) -> *const c::c_char; fn udev_device_get_is_initialized(udev_device: *mut udev_device) -> c::c_int; fn udev_device_get_devnode(udev_device: *mut udev_device) -> *const c::c_char; + fn udev_device_get_syspath(udev_device: *mut udev_device) -> *const c::c_char; fn udev_device_get_devtype(udev_device: *mut udev_device) -> *const c::c_char; fn udev_device_get_devnum(udev_device: *mut udev_device) -> c::dev_t; fn udev_device_get_action(udev_device: *mut udev_device) -> *const c::c_char; fn udev_device_get_subsystem(udev_device: *mut udev_device) -> *const c::c_char; + fn udev_device_new_from_devnum( + udev: *mut udev, + ty: c::c_char, + devnum: c::dev_t, + ) -> *mut udev_device; + fn udev_device_get_parent(udev_device: *mut udev_device) -> *mut udev_device; + fn udev_device_get_property_value( + udev_device: *mut udev_device, + key: *const c::c_char, + ) -> *const c::c_char; } #[derive(Debug, Error)] pub enum UdevError { #[error("Could not create a new udev instance")] - New(#[source] crate::utils::oserror::OsError), + New(#[source] OsError), #[error("Could not create a new udev_monitor instance")] - NewMonitor(#[source] crate::utils::oserror::OsError), + NewMonitor(#[source] OsError), #[error("Could not create a new udev_enumerate instance")] - NewEnumerate(#[source] crate::utils::oserror::OsError), + NewEnumerate(#[source] OsError), #[error("Could not enable receiving on a udev_monitor")] - EnableReceiving(#[source] crate::utils::oserror::OsError), + EnableReceiving(#[source] OsError), #[error("Could not add a match rule to a udev_monitor")] - MonitorAddMatch(#[source] crate::utils::oserror::OsError), + MonitorAddMatch(#[source] OsError), #[error("Could not add a match rule to a udev_enumerate")] - EnumerateAddMatch(#[source] crate::utils::oserror::OsError), + EnumerateAddMatch(#[source] OsError), #[error("Could not list devices of a udev_enumerate")] - EnumerateGetListEntry(#[source] crate::utils::oserror::OsError), + EnumerateGetListEntry(#[source] OsError), #[error("Could not scan devices of a udev_enumerate")] - ScanDevices(#[source] crate::utils::oserror::OsError), + ScanDevices(#[source] OsError), #[error("Could not create a udev_device from a syspath")] - DeviceFromSyspath(#[source] crate::utils::oserror::OsError), + DeviceFromSyspath(#[source] OsError), + #[error("Could not create a udev_device from a devnum")] + DeviceFromDevnum(#[source] OsError), + #[error("Could not get the device parent")] + DeviceParent(#[source] OsError), } pub struct Udev { @@ -94,10 +111,16 @@ pub struct UdevListEntry<'a> { } pub struct UdevDevice { - _udev: Rc, + udev: Rc, device: *mut udev_device, } +pub enum UdevDeviceType { + Character, + #[allow(dead_code)] + Block, +} + impl Udev { pub fn new() -> Result { let res = unsafe { udev_new() }; @@ -139,7 +162,26 @@ impl Udev { return Err(UdevError::DeviceFromSyspath(Errno::default().into())); } Ok(UdevDevice { - _udev: self.clone(), + udev: self.clone(), + device: res, + }) + } + + pub fn create_device_from_devnum<'a>( + self: &Rc, + ty: UdevDeviceType, + devnum: c::dev_t, + ) -> Result { + let ty = match ty { + UdevDeviceType::Character => b'c', + UdevDeviceType::Block => b'b', + }; + let res = unsafe { udev_device_new_from_devnum(self.udev, ty as _, devnum) }; + if res.is_null() { + return Err(UdevError::DeviceFromDevnum(Errno::default().into())); + } + Ok(UdevDevice { + udev: self.clone(), device: res, }) } @@ -197,7 +239,7 @@ impl UdevMonitor { None } else { Some(UdevDevice { - _udev: self.udev.clone(), + udev: self.udev.clone(), device: res, }) } @@ -297,6 +339,7 @@ macro_rules! strfn { impl UdevDevice { strfn!(sysname, udev_device_get_sysname); + strfn!(syspath, udev_device_get_syspath); strfn!(devnode, udev_device_get_devnode); strfn!(devtype, udev_device_get_devtype); strfn!(action, udev_device_get_action); @@ -306,10 +349,45 @@ impl UdevDevice { unsafe { udev_device_get_devnum(self.device) } } + pub fn parent(&self) -> Result { + let res = unsafe { udev_device_get_parent(self.device) }; + if res.is_null() { + return Err(UdevError::DeviceParent(Errno::default().into())); + } + unsafe { + udev_device_ref(res); + } + Ok(UdevDevice { + udev: self.udev.clone(), + device: res, + }) + } + #[allow(dead_code)] pub fn is_initialized(&self) -> bool { unsafe { udev_device_get_is_initialized(self.device) != 0 } } + + fn get_property(&self, prop: &Ustr) -> Option<&CStr> { + let prop = unsafe { udev_device_get_property_value(self.device, prop.as_ptr()) }; + if prop.is_null() { + None + } else { + unsafe { Some(CStr::from_ptr(prop)) } + } + } + + pub fn vendor(&self) -> Option<&CStr> { + self.get_property(ustr!("ID_VENDOR_FROM_DATABASE")) + } + + pub fn model(&self) -> Option<&CStr> { + self.get_property(ustr!("ID_MODEL_FROM_DATABASE")) + } + + pub fn pci_id(&self) -> Option<&CStr> { + self.get_property(ustr!("PCI_ID")) + } } impl Drop for UdevDevice { diff --git a/src/xwayland.rs b/src/xwayland.rs index 2668f044..823ff0f0 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -293,6 +293,7 @@ pub enum XWaylandEvent { Activate(Rc), ActivateRoot, Close(Rc), + #[allow(dead_code)] SeatChanged, PrimarySelectionCancelSource(Rc),