From b1890894b2ca4a8d874fe95625f589dbbe96a190 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Fri, 11 Mar 2022 18:15:21 +0100 Subject: [PATCH] autocommit 2022-03-11 18:15:21 CET --- default-config/src/lib.rs | 12 +- i4config/src/_private/client.rs | 4 + i4config/src/_private/ipc.rs | 1 + i4config/src/lib.rs | 4 + src/backends/metal.rs | 88 ++- src/backends/metal/input.rs | 58 +- src/backends/metal/monitor.rs | 266 +++++-- src/backends/metal/video.rs | 615 ++++++++++++++++ src/backends/xorg.rs | 23 +- src/config.rs | 2 +- src/config/handler.rs | 18 +- src/drm/drm.rs | 1057 +++++++++++++++++++--------- src/drm/drm/sys.rs | 1034 +++++++++++++++++++++++++++ src/drm/gbm.rs | 48 +- src/format.rs | 12 + src/ifs/wl_seat/event_handling.rs | 3 +- src/libinput/device.rs | 5 +- src/libinput/event.rs | 9 +- src/logind.rs | 36 +- src/render/egl/mod.rs | 13 +- src/render/renderer/context.rs | 17 +- src/render/renderer/framebuffer.rs | 7 + src/state.rs | 4 +- src/tasks/input_device.rs | 7 +- src/tasks/mod.rs | 2 +- src/tasks/start_backend.rs | 2 +- src/udev.rs | 42 +- src/utils/mod.rs | 2 +- src/utils/oserror.rs | 16 + src/utils/syncqueue.rs | 6 +- 30 files changed, 2909 insertions(+), 504 deletions(-) create mode 100644 src/backends/metal/video.rs create mode 100644 src/drm/drm/sys.rs diff --git a/default-config/src/lib.rs b/default-config/src/lib.rs index 853117f8..c283b245 100644 --- a/default-config/src/lib.rs +++ b/default-config/src/lib.rs @@ -2,14 +2,12 @@ use i4config::embedded::grab_input_device; use i4config::keyboard::mods::{Modifiers, ALT, CTRL, SHIFT}; use i4config::keyboard::syms::{ SYM_Super_L, SYM_b, SYM_comma, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_p, SYM_period, - SYM_r, SYM_t, SYM_v, SYM_y, + SYM_q, SYM_r, SYM_t, SYM_v, SYM_y, }; use i4config::theme::{get_title_height, set_title_color, set_title_height, Color}; use i4config::Axis::{Horizontal, Vertical}; use i4config::Direction::{Down, Left, Right, Up}; -use i4config::{ - config, create_seat, input_devices, on_new_input_device, Command, Seat, -}; +use i4config::{config, create_seat, input_devices, on_new_input_device, quit, Command, Seat}; use rand::Rng; const MOD: Modifiers = ALT; @@ -71,6 +69,8 @@ fn configure_seat(s: Seat) { s.bind(MOD | SYM_p, || Command::new("xeyes").spawn()); + s.bind(MOD | SYM_q, || quit()); + fn do_grab(s: Seat, grab: bool) { for device in s.input_devices() { log::info!( @@ -97,9 +97,7 @@ pub fn configure() { for device in input_devices() { device.set_seat(seat); } - on_new_input_device(move |device| { - device.set_seat(seat) - }); + on_new_input_device(move |device| device.set_seat(seat)); } config!(configure); diff --git a/i4config/src/_private/client.rs b/i4config/src/_private/client.rs index 2b00fb16..967229ef 100644 --- a/i4config/src/_private/client.rs +++ b/i4config/src/_private/client.rs @@ -284,6 +284,10 @@ impl Client { *self.on_new_seat.borrow_mut() = Some(Rc::new(f)); } + pub fn quit(&self) { + self.send(&ClientMessage::Quit) + } + pub fn on_new_input_device(&self, f: F) { *self.on_new_input_device.borrow_mut() = Some(Rc::new(f)); } diff --git a/i4config/src/_private/ipc.rs b/i4config/src/_private/ipc.rs index 84047874..9e5afb76 100644 --- a/i4config/src/_private/ipc.rs +++ b/i4config/src/_private/ipc.rs @@ -35,6 +35,7 @@ pub enum ClientMessage<'a> { CreateSeat { name: &'a str, }, + Quit, SetSeat { device: InputDevice, seat: Seat, diff --git a/i4config/src/lib.rs b/i4config/src/lib.rs index f9e186f7..13824a6f 100644 --- a/i4config/src/lib.rs +++ b/i4config/src/lib.rs @@ -163,6 +163,10 @@ pub fn on_new_input_device(f: F) { get!().on_new_input_device(f) } +pub fn quit() { + get!().quit() +} + pub struct Command { prog: String, args: Vec, diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 3e5cbab1..33f437f6 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -1,22 +1,27 @@ mod input; mod monitor; +mod video; use crate::async_engine::AsyncFd; +use crate::backend::{Backend, InputDevice, InputDeviceId, InputEvent}; use crate::dbus::DbusError; +use crate::drm::drm::DrmError; +use crate::drm::gbm::GbmError; use crate::libinput::device::RegisteredDevice; use crate::libinput::{LibInput, LibInputAdapter, LibInputError}; use crate::logind::{LogindError, Session}; +use crate::metal::video::{MetalDrmDevice, PendingDrmDevice}; use crate::udev::{UdevError, UdevMonitor}; use crate::utils::copyhashmap::CopyHashMap; -use crate::{CloneCell, State, Udev}; +use crate::utils::oserror::OsError; +use crate::utils::syncqueue::SyncQueue; +use crate::{CloneCell, RenderError, State, Udev}; use std::cell::{Cell, RefCell}; use std::ffi::{CStr, CString}; use std::future::pending; use std::rc::Rc; use thiserror::Error; use uapi::{c, OwnedFd}; -use crate::backend::{Backend, InputDevice, InputDeviceId, InputEvent}; -use crate::utils::syncqueue::SyncQueue; #[derive(Debug, Error)] pub enum MetalError { @@ -36,6 +41,34 @@ pub enum MetalError { Dup(#[source] crate::utils::oserror::OsError), #[error("Metal backend terminated unexpectedly")] UnexpectedTermination, + #[error("Could not create GBM device")] + GbmDevice(#[source] GbmError), + #[error("Could not create a render context")] + CreateRenderContex(#[source] RenderError), + #[error("Cannot initialize connector because no CRTC is available")] + NoCrtcForConnector, + #[error("Cannot initialize connector because no primary plane is available")] + NoPrimaryPlaneForConnector, + #[error("Cannot initialize connector because no mode is available")] + NoModeForConnector, + #[error("Could not allocate scanout buffer")] + ScanoutBuffer(#[source] GbmError), + #[error("Could not create a framebuffer")] + Framebuffer(#[source] DrmError), + #[error("Could not import a framebuffer into EGL")] + ImportFb(#[source] RenderError), + #[error("Could not configure connector chain")] + Configure(#[source] DrmError), + #[error("Could not enable atomic modesetting")] + AtomicModesetting(#[source] OsError), + #[error("Could not inspect a plane")] + CreatePlane(#[source] DrmError), + #[error("Could not inspect a crtc")] + CreateCrtc(#[source] DrmError), + #[error("Could not inspect an encoder")] + CreateEncoder(#[source] DrmError), + #[error(transparent)] + DrmError(#[from] DrmError), } pub async fn run(state: Rc) -> MetalError { @@ -45,6 +78,8 @@ pub async fn run(state: Rc) -> MetalError { } } +linear_ids!(DrmIds, DrmId); + struct MetalBackend { state: Rc, udev: Rc, @@ -54,11 +89,10 @@ struct MetalBackend { libinput_fd: AsyncFd, device_holder: Rc, session: Session, + drm_ids: DrmIds, } -impl Backend for MetalBackend { - -} +impl Backend for MetalBackend {} async fn run_(state: Rc) -> Result<(), MetalError> { let socket = match state.dbus.system() { @@ -73,12 +107,15 @@ async fn run_(state: Rc) -> Result<(), MetalError> { return Err(MetalError::TakeControl(e)); } let device_holder = Rc::new(DeviceHolder { + devices: Default::default(), input_devices: Default::default(), - input_devices_: Default::default(), + drm_devices: Default::default(), + pending_drm_devices: Default::default(), }); let udev = Rc::new(Udev::new()?); let monitor = Rc::new(udev.create_monitor()?); monitor.add_match_subsystem_devtype(Some("input"), None)?; + monitor.add_match_subsystem_devtype(Some("drm"), None)?; monitor.enable_receiving()?; let libinput = Rc::new(LibInput::new(device_holder.clone())?); let monitor_fd = match uapi::fcntl_dupfd_cloexec(monitor.fd(), 0) { @@ -98,7 +135,22 @@ async fn run_(state: Rc) -> Result<(), MetalError> { libinput_fd, device_holder, session, + drm_ids: Default::default(), }); + let _pause_handler = { + let mtl = metal.clone(); + metal + .session + .on_pause(move |p| mtl.handle_device_pause(p)) + .unwrap() + }; + let _resume_handler = { + let mtl = metal.clone(); + metal + .session + .on_resume(move |p| mtl.handle_device_resume(p)) + .unwrap() + }; let _monitor = state.eng.spawn(metal.clone().monitor_devices()); let _events = state.eng.spawn(metal.clone().handle_libinput_events()); if let Err(e) = metal.enumerate_devices() { @@ -107,7 +159,7 @@ async fn run_(state: Rc) -> Result<(), MetalError> { pending().await } -struct MetalDevice { +struct MetalInputDevice { slot: usize, id: InputDeviceId, devnum: c::dev_t, @@ -120,9 +172,17 @@ struct MetalDevice { cb: CloneCell>>, } +#[derive(Clone)] +enum MetalDevice { + Input(Rc), + Drm(Rc), +} + struct DeviceHolder { - input_devices: CopyHashMap>, - input_devices_: RefCell>>>, + devices: CopyHashMap, + input_devices: RefCell>>>, + drm_devices: CopyHashMap>, + pending_drm_devices: CopyHashMap, } impl LibInputAdapter for DeviceHolder { @@ -131,8 +191,8 @@ impl LibInputAdapter for DeviceHolder { Ok(s) => s, Err(e) => return Err(LibInputError::Stat(e.into())), }; - match self.input_devices.get(&stat.st_rdev) { - Some(d) => match d.fd.get() { + match self.devices.get(&stat.st_rdev) { + Some(MetalDevice::Input(d)) => match d.fd.get() { Some(fd) => match uapi::fcntl_dupfd_cloexec(fd.raw(), 0) { Ok(fd) => Ok(fd), Err(e) => Err(LibInputError::DupFd(e.into())), @@ -144,7 +204,7 @@ impl LibInputAdapter for DeviceHolder { } } -impl InputDevice for MetalDevice { +impl InputDevice for MetalInputDevice { fn id(&self) -> InputDeviceId { self.id } @@ -166,7 +226,7 @@ impl InputDevice for MetalDevice { } } -impl MetalDevice { +impl MetalInputDevice { fn event(&self, event: InputEvent) { self.events.push(event); if let Some(cb) = self.cb.get() { diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs index f15a59b3..3fc36bac 100644 --- a/src/backends/metal/input.rs +++ b/src/backends/metal/input.rs @@ -1,35 +1,38 @@ use crate::async_engine::FdStatus; +use crate::backend::{InputEvent, KeyState}; +use crate::libinput::consts::LIBINPUT_KEY_STATE_PRESSED; use crate::libinput::event::LibInputEvent; use crate::metal::MetalBackend; use crate::ErrorFmt; use std::rc::Rc; -use crate::backend::{InputEvent, KeyState}; -use crate::libinput::consts::LIBINPUT_KEY_STATE_PRESSED; macro_rules! unpack { - ($slf:expr, $ev:expr) => { + ($slf:expr, $ev:expr) => {{ + let slot = match $ev.device().slot() { + Some(s) => s, + _ => return, + }; + let data = match $slf + .device_holder + .input_devices + .borrow_mut() + .get(slot) + .cloned() + .and_then(|v| v) { - let slot = match $ev.device().slot() { - Some(s) => s, - _ => return, - }; - let data = match $slf.device_holder.input_devices_.borrow_mut().get(slot).cloned().and_then(|v| v) { - Some(d) => d, - _ => return, - }; - data - } - }; - ($slf:expr, $ev:expr, $conv:ident) => { - { - let event = match $ev.$conv() { - Some(e) => e, - _ => return, - }; - let data = unpack!($slf, $ev); - (event, data) - } - }; + Some(d) => d, + _ => return, + }; + data + }}; + ($slf:expr, $ev:expr, $conv:ident) => {{ + let event = match $ev.$conv() { + Some(e) => e, + _ => return, + }; + let data = unpack!($slf, $ev); + (event, data) + }}; } impl MetalBackend { @@ -65,7 +68,7 @@ impl MetalBackend { match event.ty() { c::LIBINPUT_EVENT_DEVICE_ADDED => self.handle_device_added(event), - c::LIBINPUT_EVENT_DEVICE_REMOVED => self.handle_device_removed(event), + c::LIBINPUT_EVENT_DEVICE_REMOVED => self.handle_li_device_removed(event), c::LIBINPUT_EVENT_KEYBOARD_KEY => self.handle_keyboard_key(event), c::LIBINPUT_EVENT_POINTER_MOTION => self.handle_pointer_motion(event), _ => {} @@ -76,10 +79,9 @@ impl MetalBackend { // let dev = unpack!(self, event); } - fn handle_device_removed(self: &Rc, event: LibInputEvent) { + fn handle_li_device_removed(self: &Rc, event: LibInputEvent) { let dev = unpack!(self, event); - self.device_holder.input_devices.remove(&dev.devnum); - self.device_holder.input_devices_.borrow_mut()[dev.slot] = None; + dev.inputdev.set(None); event.device().unset_slot(); } diff --git a/src/backends/metal/monitor.rs b/src/backends/metal/monitor.rs index 10b5621b..76571996 100644 --- a/src/backends/metal/monitor.rs +++ b/src/backends/metal/monitor.rs @@ -1,11 +1,29 @@ -use std::cell::Cell; use crate::async_engine::FdStatus; +use crate::backend::BackendEvent; use crate::dbus::TRUE; -use crate::metal::{MetalBackend, MetalDevice, MetalError}; +use crate::drm::drm::DrmMaster; +use crate::metal::video::PendingDrmDevice; +use crate::metal::{MetalBackend, MetalDevice, MetalDrmDevice, MetalError, MetalInputDevice}; +use crate::org::freedesktop::login1::session::{PauseDevice, ResumeDevice}; use crate::udev::UdevDevice; use crate::ErrorFmt; +use bstr::ByteSlice; +use std::cell::Cell; use std::rc::Rc; -use crate::backend::BackendEvent; +use uapi::{c, OwnedFd}; + +const DRM: &[u8] = b"drm"; +const INPUT: &[u8] = b"input"; +const EVENT: &[u8] = b"event"; + +const CARD: &[u8] = b"card"; + +fn is_primary_node(n: &[u8]) -> bool { + match n.strip_prefix(CARD) { + Some(r) => r.iter().copied().all(|c| matches!(c, b'0'..=b'9')), + _ => false, + } +} impl MetalBackend { pub async fn monitor_devices(self: Rc) { @@ -25,58 +43,215 @@ impl MetalBackend { _ => {} } while let Some(dev) = self.monitor.receive_device() { - log::info!("x {:?}", dev.devnode()); + let action = match dev.action() { + Some(c) => c, + _ => continue, + }; + match action.to_bytes() { + b"add" => self.handle_device_add(dev), + b"change" => self.handle_device_change(dev), + _ => None, + }; } } log::error!("Monitor task exited. Future hotplug events will be ignored."); } + pub fn handle_device_pause(self: &Rc, pause: PauseDevice) { + if pause.ty == "pause" { + self.session.device_paused(pause.major, pause.minor); + } + let dev = uapi::makedev(pause.major as _, pause.minor as _); + if pause.ty == "gone" { + self.handle_device_removed(dev); + } else { + self.handle_device_paused(dev); + } + } + + pub fn handle_device_resume(self: &Rc, resume: ResumeDevice) { + let dev = uapi::makedev(resume.major as _, resume.minor as _); + let dev = match self.device_holder.devices.get(&dev) { + Some(d) => d, + _ => return, + }; + match dev { + MetalDevice::Input(id) => self.handle_input_device_resume(&id, resume.fd), + MetalDevice::Drm(dd) => self.handle_drm_device_resume(&dd, resume.fd), + } + } + + fn handle_drm_device_resume(self: &Rc, dev: &Rc, fd: Rc) { + log::info!("Device resumed: {}", dev.dev.devnode.to_bytes().as_bstr()); + } + + fn handle_input_device_resume(self: &Rc, dev: &Rc, fd: Rc) { + log::info!("Device resumed: {}", dev.devnode.to_bytes().as_bstr()); + dev.fd.set(Some(fd)); + let inputdev = match self.libinput.open(dev.devnode.as_c_str()) { + Ok(d) => d, + Err(_) => return, + }; + inputdev.device().set_slot(dev.slot); + dev.inputdev.set(Some(inputdev)); + } + + fn handle_device_removed(self: &Rc, dev: c::dev_t) { + let dev = match self.device_holder.devices.remove(&dev) { + Some(d) => d, + _ => return, + }; + match dev { + MetalDevice::Input(id) => self.handle_input_device_removed(&id), + MetalDevice::Drm(dd) => self.handle_drm_device_removed(&dd), + } + } + + fn handle_drm_device_removed(self: &Rc, dev: &Rc) { + log::info!("Device removed: {}", dev.dev.devnode.to_bytes().as_bstr()); + } + + fn handle_input_device_removed(self: &Rc, dev: &Rc) { + log::info!("Device removed: {}", dev.devnode.to_bytes().as_bstr()); + self.device_holder.input_devices.borrow_mut()[dev.slot] = None; + dev.fd.set(None); + if let Some(rd) = dev.inputdev.take() { + rd.device().unset_slot(); + } + dev.removed.set(true); + if let Some(cb) = dev.cb.take() { + cb(); + } + } + + fn handle_device_paused(self: &Rc, dev: c::dev_t) { + let dev = match self.device_holder.devices.get(&dev) { + Some(d) => d, + _ => return, + }; + match dev { + MetalDevice::Input(id) => self.handle_input_device_paused(&id), + MetalDevice::Drm(dd) => self.handle_drm_device_paused(&dd), + } + } + + fn handle_drm_device_paused(self: &Rc, dev: &Rc) { + log::info!("Device paused: {}", dev.dev.devnode.to_bytes().as_bstr()); + } + + fn handle_input_device_paused(self: &Rc, dev: &Rc) { + log::info!("Device paused: {}", dev.devnode.to_bytes().as_bstr()); + if let Some(rd) = dev.inputdev.take() { + rd.device().unset_slot(); + } + } + + fn handle_device_add(self: &Rc, dev: UdevDevice) -> Option<()> { + let ss = dev.subsystem()?; + match ss.to_bytes() { + INPUT => self.handle_input_device_add(dev), + DRM => self.handle_drm_add(dev), + _ => None, + } + } + + fn handle_input_device_add(self: &Rc, dev: UdevDevice) -> Option<()> { + let sysname = dev.sysname()?; + if sysname.to_bytes().starts_with(EVENT) { + self.add_input_device(&dev); + } + None + } + + fn handle_drm_add(self: &Rc, dev: UdevDevice) -> Option<()> { + let sysname = dev.sysname()?; + if !is_primary_node(sysname.to_bytes()) { + return None; + } + let devnum = dev.devnum(); + let devnode = dev.devnode()?; + let id = self.drm_ids.next(); + log::info!("Device added: {}", devnode.to_bytes().as_bstr()); + let dev = PendingDrmDevice { + id, + devnum, + devnode: devnode.to_owned(), + }; + self.device_holder.pending_drm_devices.set(devnum, dev); + let slf = self.clone(); + self.session.get_device(devnum, move |res| { + let dev = match slf.device_holder.pending_drm_devices.remove(&devnum) { + Some(d) if d.id == id => d, + _ => return, + }; + let res = match res { + Ok(r) => r, + Err(e) => { + log::error!("Could not take control of drm device: {}", ErrorFmt(e)); + return; + } + }; + if res.inactive == TRUE { + return; + } + let master = Rc::new(DrmMaster::new(res.fd.clone())); + let dev = match slf.creat_drm_device(dev, &master) { + Ok(d) => Rc::new(d), + Err(e) => { + log::error!("Could not initialize drm device: {}", ErrorFmt(e)); + return; + } + }; + slf.init_drm_device(&dev); + slf.device_holder + .drm_devices + .set(dev.dev.devnum, dev.clone()); + slf.device_holder + .devices + .set(dev.dev.devnum, MetalDevice::Drm(dev.clone())); + }); + None + } + + fn handle_device_change(self: &Rc, dev: UdevDevice) -> Option<()> { + let ss = dev.subsystem()?; + log::info!("Device changed: {}", dev.devnode()?.to_bytes().as_bstr()); + match ss.to_bytes() { + DRM => self.handle_drm_change(dev), + _ => None, + } + } + + fn handle_drm_change(self: &Rc, dev: UdevDevice) -> Option<()> { + None + } + pub fn enumerate_devices(self: &Rc) -> Result<(), MetalError> { let mut enumerate = self.udev.create_enumerate()?; - enumerate.add_match_subsystem("input")?; + enumerate.add_match_subsystem(INPUT)?; + enumerate.add_match_subsystem(DRM)?; enumerate.scan_devices()?; let mut entry_opt = enumerate.get_list_entry()?; while let Some(entry) = entry_opt.take() { - 'inner: { - let device = match self.udev.create_device_from_syspath(entry.name()) { - Ok(d) => d, - _ => break 'inner, - }; - let sysname = match device.sysname() { - Ok(s) => s, - _ => break 'inner, - }; - if sysname.to_bytes().starts_with(b"event") { - self.add_input_device(&device); - } + if let Ok(dev) = self.udev.create_device_from_syspath(entry.name()) { + self.handle_device_add(dev); } entry_opt = entry.next(); } Ok(()) } - fn add_input_device(self: &Rc, dev: &UdevDevice) { + fn add_input_device(self: &Rc, dev: &UdevDevice) -> Option<()> { if !dev.is_initialized() { - return; + return None; } let slf = self.clone(); let device_id = self.state.input_device_ids.next(); let devnum = dev.devnum(); - let devnode = match dev.devnode() { - Ok(n) => n, - Err(e) => { - log::error!("Could not retrieve devnode of udev device: {}", ErrorFmt(e)); - return; - } - }; - let sysname = match dev.sysname() { - Ok(n) => n, - Err(e) => { - log::error!("Could not retrieve sysname of udev device: {}", ErrorFmt(e)); - return; - } - }; - let mut slots = self.device_holder.input_devices_.borrow_mut(); + let devnode = dev.devnode()?; + let sysname = dev.sysname()?; + log::info!("Device added: {}", devnode.to_bytes().as_bstr()); + let mut slots = self.device_holder.input_devices.borrow_mut(); let slot = 'slot: { for (i, s) in slots.iter().enumerate() { if s.is_none() { @@ -86,7 +261,7 @@ impl MetalBackend { slots.push(None); slots.len() - 1 }; - let dev = Rc::new(MetalDevice { + let dev = Rc::new(MetalInputDevice { slot, id: device_id, devnum, @@ -99,12 +274,14 @@ impl MetalBackend { cb: Default::default(), }); slots[slot] = Some(dev.clone()); - self.device_holder.input_devices.set(devnum, dev); + self.device_holder + .devices + .set(devnum, MetalDevice::Input(dev)); self.session.get_device(devnum, move |res| { - let id = &slf.device_holder.input_devices; - let mut slots = slf.device_holder.input_devices_.borrow_mut(); + let id = &slf.device_holder.devices; + let mut slots = slf.device_holder.input_devices.borrow_mut(); let dev = 'dev: { - if let Some(dev) = id.get(&devnum) { + if let Some(dev) = slots[slot].clone() { if dev.id == device_id { break 'dev dev; } @@ -126,15 +303,14 @@ impl MetalBackend { dev.fd.set(Some(res.fd.clone())); let inputdev = match slf.libinput.open(dev.devnode.as_c_str()) { Ok(d) => d, - Err(_) => { - slots[dev.slot] = None; - id.remove(&devnum); - return; - } + Err(_) => return, }; inputdev.device().set_slot(slot); dev.inputdev.set(Some(inputdev)); - slf.state.backend_events.push(BackendEvent::NewInputDevice(dev.clone())); + slf.state + .backend_events + .push(BackendEvent::NewInputDevice(dev.clone())); }); + None } } diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs new file mode 100644 index 00000000..4fa75945 --- /dev/null +++ b/src/backends/metal/video.rs @@ -0,0 +1,615 @@ +use crate::backend::{BackendEvent, Output, OutputId}; +use crate::drm::drm::{ + ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc, DrmEncoder, DrmError, DrmFb, + DrmFramebuffer, DrmMaster, DrmModeInfo, DrmObject, DrmPlane, DrmProperty, + DrmPropertyDefinition, DrmPropertyType, PropBlob, DRM_CLIENT_CAP_ATOMIC, + DRM_MODE_ATOMIC_ALLOW_MODESET, +}; +use crate::drm::gbm::{GbmDevice, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT}; +use crate::drm::{ModifiedFormat, INVALID_MODIFIER}; +use crate::format::{Format, XRGB8888}; +use crate::metal::{DrmId, MetalBackend, MetalError}; +use crate::render::{Framebuffer, RenderContext}; +use crate::utils::bitflags::BitflagsExt; +use crate::{CloneCell, ErrorFmt, State}; +use ahash::AHashMap; +use bstr::{BString, ByteSlice}; +use std::cell::Cell; +use std::ffi::CString; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; +use uapi::c; + +pub struct PendingDrmDevice { + pub id: DrmId, + pub devnum: c::dev_t, + pub devnode: CString, +} + +#[derive(Debug)] +pub struct MetalDrmDeviceStatic { + pub id: DrmId, + pub devnum: c::dev_t, + pub devnode: CString, + pub master: Rc, + pub crtcs: AHashMap>, + pub encoders: AHashMap>, + pub planes: AHashMap>, + pub min_width: u32, + pub max_width: u32, + pub min_height: u32, + pub max_height: u32, + pub gbm: GbmDevice, + pub egl: Rc, +} + +#[derive(Debug)] +pub struct MetalDrmDevice { + pub dev: Rc, + pub connectors: AHashMap>, +} + +#[derive(Debug)] +pub struct MetalConnector { + pub id: DrmConnector, + pub master: Rc, + + pub output_id: OutputId, + + pub crtcs: AHashMap>, + pub modes: Vec, + pub mode: CloneCell>>, + + pub connector_type: ConnectorType, + pub connector_type_id: u32, + + pub connection: ConnectorStatus, + pub mm_width: u32, + pub mm_height: u32, + pub subpixel: u32, + + pub crtc_id: MutableProperty, + + pub egl_fb: CloneCell>>, + + pub on_change: OnChange, +} + +#[derive(Default)] +pub struct OnChange { + pub on_change: CloneCell>>, +} + +impl Debug for OnChange { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self.on_change.get() { + None => f.write_str("None"), + Some(_) => f.write_str("Some"), + } + } +} + +impl Output for MetalConnector { + fn id(&self) -> OutputId { + self.output_id + } + + fn removed(&self) -> bool { + false + } + + fn width(&self) -> i32 { + match self.mode.get() { + Some(m) => m.hdisplay as _, + _ => 0, + } + } + + fn height(&self) -> i32 { + match self.mode.get() { + Some(m) => m.vdisplay as _, + _ => 0, + } + } + + fn on_change(&self, cb: Rc) { + self.on_change.on_change.set(Some(cb)); + } +} + +#[derive(Debug)] +pub struct MetalCrtc { + pub id: DrmCrtc, + pub idx: usize, + pub master: Rc, + + pub possible_planes: AHashMap>, + + pub connector: Cell, + + pub primary_plane: Cell, + pub cursor_plane: Cell, + + pub active: MutableProperty, + pub mode_id: MutableProperty, + + pub mode_blob: CloneCell>>, +} + +#[derive(Debug)] +pub struct MetalEncoder { + pub id: DrmEncoder, + pub crtcs: AHashMap>, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum PlaneType { + Overlay, + Primary, + Cursor, +} + +#[derive(Debug)] +pub struct MetalPlane { + pub id: DrmPlane, + pub master: Rc, + + pub ty: PlaneType, + + pub possible_crtcs: u32, + pub formats: AHashMap, + + pub fb: CloneCell>>, + + pub fb_id: MutableProperty, + pub crtc_id: MutableProperty, + pub crtc_x: MutableProperty, + pub crtc_y: MutableProperty, + pub crtc_w: MutableProperty, + pub crtc_h: MutableProperty, + pub src_x: MutableProperty, + pub src_y: MutableProperty, + pub src_w: MutableProperty, + pub src_h: MutableProperty, +} + +impl MetalDrmDevice {} + +fn get_connectors( + state: &State, + dev: &MetalDrmDeviceStatic, + ids: &[DrmConnector], +) -> Result>, DrmError> { + let mut connectors = AHashMap::new(); + for connector in ids { + match create_connector(state, *connector, dev) { + Ok(e) => { + connectors.insert(e.id, Rc::new(e)); + } + Err(e) => return Err(DrmError::CreateConnector(Box::new(e))), + } + } + Ok(connectors) +} + +fn create_connector( + state: &State, + connector: DrmConnector, + dev: &MetalDrmDeviceStatic, +) -> Result { + let info = dev.master.get_connector_info(connector, true)?; + let mut crtcs = AHashMap::new(); + for encoder in info.encoders { + if let Some(encoder) = dev.encoders.get(&encoder) { + for (_, crtc) in &encoder.crtcs { + crtcs.insert(crtc.id, crtc.clone()); + } + } + } + let props = collect_properties(&dev.master, connector)?; + Ok(MetalConnector { + id: connector, + master: dev.master.clone(), + output_id: state.output_ids.next(), + crtcs, + modes: info.modes, + mode: Default::default(), + connector_type: info.connector_type.into(), + connector_type_id: info.connector_type_id, + connection: info.connection.into(), + mm_width: info.mm_width, + mm_height: info.mm_height, + subpixel: info.subpixel, + crtc_id: props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _)), + egl_fb: Default::default(), + on_change: Default::default(), + }) +} + +fn create_encoder( + encoder: DrmEncoder, + master: &Rc, + crtcs: &AHashMap>, +) -> Result { + let info = master.get_encoder_info(encoder)?; + let mut possible = AHashMap::new(); + for crtc in crtcs.values() { + if info.possible_crtcs.contains(1 << crtc.idx) { + possible.insert(crtc.id, crtc.clone()); + } + } + Ok(MetalEncoder { + id: encoder, + crtcs: possible, + }) +} + +fn create_crtc( + crtc: DrmCrtc, + idx: usize, + master: &Rc, + planes: &AHashMap>, +) -> Result { + let mask = 1 << idx; + let mut possible_planes = AHashMap::new(); + for plane in planes.values() { + if plane.possible_crtcs.contains(mask) { + possible_planes.insert(plane.id, plane.clone()); + } + } + let props = collect_properties(master, crtc)?; + Ok(MetalCrtc { + id: crtc, + idx, + master: master.clone(), + possible_planes, + connector: Cell::new(DrmConnector::NONE), + primary_plane: Cell::new(DrmPlane::NONE), + cursor_plane: Cell::new(DrmPlane::NONE), + active: props.get("ACTIVE")?.map(|v| v == 1), + mode_id: props.get("MODE_ID")?.map(|v| DrmBlob(v as u32)), + mode_blob: Default::default(), + }) +} + +fn create_plane(plane: DrmPlane, master: &Rc) -> Result { + let info = master.get_plane_info(plane)?; + let mut formats = AHashMap::new(); + for format in info.format_types { + if let Some(f) = crate::format::formats().get(&format) { + formats.insert(format, *f); + } else { + // log::warn!( + // "{:?} supports unknown format '{:?}'", + // plane, + // crate::format::debug(format) + // ); + } + } + let props = collect_properties(master, plane)?; + let ty = match props.props.get(b"type".as_bstr()) { + Some((def, val)) => match &def.ty { + DrmPropertyType::Enum { values, .. } => 'ty: { + for v in values { + if v.value == *val { + match v.name.as_bytes() { + b"Overlay" => break 'ty PlaneType::Overlay, + b"Primary" => break 'ty PlaneType::Primary, + b"Cursor" => break 'ty PlaneType::Cursor, + _ => return Err(DrmError::UnknownPlaneType(v.name.to_owned())), + } + } + } + return Err(DrmError::InvalidPlaneType(*val)); + } + _ => return Err(DrmError::InvalidPlaneTypeProperty), + }, + _ => { + return Err(DrmError::MissingProperty( + "type".to_string().into_boxed_str(), + )) + } + }; + Ok(MetalPlane { + id: plane, + master: master.clone(), + ty, + possible_crtcs: info.possible_crtcs, + formats, + fb: Default::default(), + fb_id: props.get("FB_ID")?.map(|v| DrmFb(v as _)), + crtc_id: props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _)), + crtc_x: props.get("CRTC_X")?.map(|v| v as i32), + crtc_y: props.get("CRTC_Y")?.map(|v| v as i32), + crtc_w: props.get("CRTC_W")?.map(|v| v as i32), + crtc_h: props.get("CRTC_H")?.map(|v| v as i32), + src_x: props.get("SRC_X")?.map(|v| v as u32), + src_y: props.get("SRC_Y")?.map(|v| v as u32), + src_w: props.get("SRC_W")?.map(|v| v as u32), + src_h: props.get("SRC_H")?.map(|v| v as u32), + }) +} + +fn collect_properties( + master: &Rc, + t: T, +) -> Result { + let mut props = AHashMap::new(); + for prop in master.get_properties(t)? { + let def = master.get_property(prop.id)?; + props.insert(def.name.clone(), (def, prop.value)); + } + Ok(CollectedProperties { props }) +} + +struct CollectedProperties { + props: AHashMap, +} + +impl CollectedProperties { + fn get(&self, name: &str) -> Result, DrmError> { + match self.props.get(name.as_bytes().as_bstr()) { + Some((def, value)) => Ok(MutableProperty { + id: def.id, + value: Cell::new(*value), + }), + _ => Err(DrmError::MissingProperty(name.to_string().into_boxed_str())), + } + } +} + +#[derive(Debug)] +pub struct MutableProperty { + pub id: DrmProperty, + pub value: Cell, +} + +impl MutableProperty { + fn map(self, f: F) -> MutableProperty + where + F: FnOnce(T) -> U, + { + MutableProperty { + id: self.id, + value: Cell::new(f(self.value.into_inner())), + } + } +} + +impl MetalBackend { + pub fn creat_drm_device( + &self, + pending: PendingDrmDevice, + master: &Rc, + ) -> Result { + if let Err(e) = master.set_client_cap(DRM_CLIENT_CAP_ATOMIC, 2) { + return Err(MetalError::AtomicModesetting(e)); + } + let resources = master.get_resources()?; + + let mut planes = AHashMap::new(); + for plane in master.get_planes()? { + match create_plane(plane, master) { + Ok(p) => { + planes.insert(p.id, Rc::new(p)); + } + Err(e) => return Err(MetalError::CreatePlane(e)), + } + } + + let mut crtcs = AHashMap::new(); + for (idx, crtc) in resources.crtcs.iter().copied().enumerate() { + match create_crtc(crtc, idx, master, &planes) { + Ok(c) => { + crtcs.insert(c.id, Rc::new(c)); + } + Err(e) => return Err(MetalError::CreateCrtc(e)), + } + } + + let mut encoders = AHashMap::new(); + for encoder in resources.encoders { + match create_encoder(encoder, master, &crtcs) { + Ok(e) => { + encoders.insert(e.id, Rc::new(e)); + } + Err(e) => return Err(MetalError::CreateEncoder(e)), + } + } + + let gbm = match GbmDevice::new(master) { + Ok(g) => g, + Err(e) => return Err(MetalError::GbmDevice(e)), + }; + let egl = match RenderContext::from_drm_device(master) { + Ok(r) => Rc::new(r), + Err(e) => return Err(MetalError::CreateRenderContex(e)), + }; + + let dev = MetalDrmDeviceStatic { + id: pending.id, + devnum: pending.devnum, + devnode: pending.devnode, + master: master.clone(), + crtcs, + encoders, + planes, + min_width: resources.min_width, + max_width: resources.max_width, + min_height: resources.min_height, + max_height: resources.max_height, + gbm, + egl, + }; + + let connectors = get_connectors(&self.state, &dev, &resources.connectors)?; + + let slf = MetalDrmDevice { + dev: Rc::new(dev), + connectors, + }; + + self.reset_drm_device(&slf)?; + + Ok(slf) + } + + pub fn refresh_drm_device(&self, dev: MetalDrmDevice) -> Result { + let resources = dev.dev.master.get_resources()?; + let connectors = get_connectors(&self.state, &dev.dev, &resources.connectors)?; + Ok(MetalDrmDevice { + dev: dev.dev.clone(), + connectors, + }) + } + + fn reset_drm_device(&self, dev: &MetalDrmDevice) -> Result<(), DrmError> { + let mut changes = dev.dev.master.change(DRM_MODE_ATOMIC_ALLOW_MODESET); + for connector in dev.connectors.values() { + if connector.crtc_id.value.take().is_some() { + changes.change_object(connector.id, |c| { + c.change(connector.crtc_id.id, 0); + }) + } + } + for plane in dev.dev.planes.values() { + changes.change_object(plane.id, |c| { + if plane.crtc_id.value.take().is_some() { + c.change(plane.crtc_id.id, 0); + } + if plane.fb_id.value.take().is_some() { + c.change(plane.fb_id.id, 0); + } + }) + } + for crtc in dev.dev.crtcs.values() { + changes.change_object(crtc.id, |c| { + if crtc.active.value.take() { + c.change(crtc.active.id, 0); + } + if crtc.mode_id.value.take().is_some() { + c.change(crtc.mode_id.id, 0); + } + }) + } + if let Err(e) = changes.commit(0) { + return Err(DrmError::ResetFailed(Box::new(e))); + } + Ok(()) + } + + pub fn init_drm_device(&self, dev: &Rc) { + for connector in dev.connectors.values() { + if let Err(e) = self.init_drm_connector(dev, connector) { + log::error!("Could not initialize drm connector: {}", ErrorFmt(e)); + } + } + } + + fn init_drm_connector( + &self, + dev: &Rc, + connector: &Rc, + ) -> Result<(), MetalError> { + if connector.connection != ConnectorStatus::Connected { + return Ok(()); + } + let crtc = 'crtc: { + for crtc in connector.crtcs.values() { + if crtc.connector.get().is_none() { + break 'crtc crtc.clone(); + } + } + return Err(MetalError::NoCrtcForConnector); + }; + let primary_plane = 'plane: { + for plane in crtc.possible_planes.values() { + if plane.ty == PlaneType::Primary + && plane.crtc_id.value.get().is_none() + && plane.formats.contains_key(&XRGB8888.drm) + { + break 'plane plane.clone(); + } + } + return Err(MetalError::NoPrimaryPlaneForConnector); + }; + let mode = match connector.modes.first() { + Some(m) => m, + _ => return Err(MetalError::NoModeForConnector), + }; + let mode_blob = mode.create_blob(&connector.master)?; + let format = ModifiedFormat { + format: XRGB8888, + modifier: INVALID_MODIFIER, + }; + let bo = dev.dev.gbm.create_bo( + mode.hdisplay as i32, + mode.vdisplay as i32, + &format, + GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT, + ); + let bo = match bo { + Ok(b) => b, + Err(e) => return Err(MetalError::ScanoutBuffer(e)), + }; + let drm_fb = match connector.master.add_fb(&bo) { + Ok(fb) => Rc::new(fb), + Err(e) => return Err(MetalError::Framebuffer(e)), + }; + let egl_fb = match dev.dev.egl.dmabuf_fb(&bo.dma()) { + Ok(fb) => fb, + Err(e) => return Err(MetalError::ImportFb(e)), + }; + let mut changes = connector.master.change(DRM_MODE_ATOMIC_ALLOW_MODESET); + changes.change_object(connector.id, |c| { + c.change(connector.crtc_id.id, crtc.id.0 as _); + }); + changes.change_object(crtc.id, |c| { + c.change(crtc.active.id, 1); + c.change(crtc.mode_id.id, mode_blob.id().0 as _); + }); + changes.change_object(primary_plane.id, |c| { + c.change(primary_plane.fb_id.id, drm_fb.id().0 as _); + c.change(primary_plane.crtc_id.id, crtc.id.0 as _); + c.change(primary_plane.crtc_x.id, 0); + c.change(primary_plane.crtc_y.id, 0); + c.change(primary_plane.crtc_w.id, mode.hdisplay as _); + c.change(primary_plane.crtc_h.id, mode.vdisplay as _); + c.change(primary_plane.src_x.id, 0); + c.change(primary_plane.src_y.id, 0); + c.change(primary_plane.src_w.id, (mode.hdisplay as u64) << 16); + c.change(primary_plane.src_h.id, (mode.vdisplay as u64) << 16); + }); + if let Err(e) = changes.commit(0) { + return Err(MetalError::Configure(e)); + } + connector.crtc_id.value.set(crtc.id); + connector.egl_fb.set(Some(egl_fb)); + connector.mode.set(Some(Rc::new(mode.clone()))); + crtc.connector.set(connector.id); + crtc.active.value.set(true); + crtc.mode_id.value.set(mode_blob.id()); + crtc.mode_blob.set(Some(Rc::new(mode_blob))); + primary_plane.fb_id.value.set(drm_fb.id()); + primary_plane.fb.set(Some(drm_fb)); + primary_plane.crtc_id.value.set(crtc.id); + primary_plane.crtc_x.value.set(0); + primary_plane.crtc_y.value.set(0); + primary_plane.crtc_w.value.set(mode.hdisplay as _); + primary_plane.crtc_h.value.set(mode.vdisplay as _); + primary_plane.src_x.value.set(0); + primary_plane.src_y.value.set(0); + primary_plane.src_w.value.set((mode.hdisplay as u32) << 16); + primary_plane.src_h.value.set((mode.vdisplay as u32) << 16); + self.state + .backend_events + .push(BackendEvent::NewOutput(connector.clone())); + log::info!( + "Initialized connector {}-{} with mode {:?}", + connector.connector_type, + connector.connector_type_id, + mode + ); + Ok(()) + } +} diff --git a/src/backends/xorg.rs b/src/backends/xorg.rs index 98d336db..fe96cd4a 100644 --- a/src/backends/xorg.rs +++ b/src/backends/xorg.rs @@ -1,4 +1,7 @@ -use crate::backend::{Backend, BackendEvent, KeyState, InputEvent, Output, OutputId, ScrollAxis, InputDeviceId, InputDevice}; +use crate::backend::{ + Backend, BackendEvent, InputDevice, InputDeviceId, InputEvent, KeyState, Output, OutputId, + ScrollAxis, +}; use crate::drm::drm::{Drm, DrmError}; use crate::drm::gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING}; use crate::drm::{ModifiedFormat, INVALID_MODIFIER}; @@ -97,7 +100,7 @@ impl XcbCon { let c = xcb.xcb_connect(ptr::null(), ptr::null_mut()); match xcb.xcb_connection_has_error(c) { - 0 => { }, + 0 => {} n => return Err(XorgBackendError::CannotConnect(n.into())), } let errors = XcbErrorParser::new(&xcb, c); @@ -231,7 +234,7 @@ fn get_drm(con: &XcbCon) -> Result { assert!(res.nfd == 1); let fd = *con.dri.xcb_dri3_open_reply_fds(con.c, &mut *res); let fd = OwnedFd::new(fd); - Ok(Drm::new(fd.raw(), true)?) + Ok(Drm::reopen(fd.raw(), true)?) } } @@ -574,10 +577,14 @@ impl XorgBackend { self.mouse_seats.set(info.attachment, seat.clone()); self.state .backend_events - .push(BackendEvent::NewInputDevice(Rc::new(XorgSeatMouse(seat.clone())))); + .push(BackendEvent::NewInputDevice(Rc::new(XorgSeatMouse( + seat.clone(), + )))); self.state .backend_events - .push(BackendEvent::NewInputDevice(Rc::new(XorgSeatKeyboard(seat.clone())))); + .push(BackendEvent::NewInputDevice(Rc::new(XorgSeatKeyboard( + seat.clone(), + )))); } } @@ -1118,7 +1125,11 @@ impl InputDevice for XorgSeatKeyboard { } }; if res.status != ffi::XCB_GRAB_STATUS_SUCCESS as _ { - log::error!("Could not grab device {}: status = {}", self.0.kb, res.status); + log::error!( + "Could not grab device {}: status = {}", + self.0.kb, + res.status + ); } } else { let cookie = con diff --git a/src/config.rs b/src/config.rs index fefaea87..10bd0525 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ mod handler; +use crate::backend::InputDeviceId; use crate::config::handler::ConfigProxyHandler; use crate::ifs::wl_seat::SeatId; use crate::utils::ptr_ext::PtrExt; @@ -13,7 +14,6 @@ use std::cell::Cell; use std::ptr; use std::rc::Rc; use thiserror::Error; -use crate::backend::InputDeviceId; #[derive(Debug, Error)] pub enum ConfigError { diff --git a/src/config/handler.rs b/src/config/handler.rs index b7bb2bd4..f60d7e07 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1,3 +1,4 @@ +use crate::backend::InputDeviceId; use crate::ifs::wl_seat::{SeatId, WlSeatGlobal}; use crate::state::DeviceHandlerData; use crate::tree::walker::NodeVisitorBase; @@ -19,7 +20,6 @@ use log::Level; use std::cell::Cell; use std::rc::Rc; use thiserror::Error; -use crate::backend::InputDeviceId; pub(super) struct ConfigProxyHandler { pub client_data: Cell<*const u8>, @@ -157,11 +157,11 @@ impl ConfigProxyHandler { device: InputDevice, ) -> Result, CphError> { let data = self - .state - .input_device_handlers - .borrow_mut() - .get(&InputDeviceId::from_raw(device.0 as _)) - .map(|d| d.data.clone()); + .state + .input_device_handlers + .borrow_mut() + .get(&InputDeviceId::from_raw(device.0 as _)) + .map(|d| d.data.clone()); match data { Some(d) => Ok(d), _ => Err(CphError::DeviceDoesNotExist(device)), @@ -332,6 +332,11 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_quit(&self) { + log::info!("Quitting"); + self.state.el.stop(); + } + fn handle_toggle_floating(&self, seat: Seat) -> Result<(), FocusParentError> { let seat = self.get_seat(seat)?; seat.toggle_floating(); @@ -466,6 +471,7 @@ impl ConfigProxyHandler { ClientMessage::CreateSplit { seat, axis } => self.handle_create_split(seat, axis)?, ClientMessage::FocusParent { seat } => self.handle_focus_parent(seat)?, ClientMessage::ToggleFloating { seat } => self.handle_toggle_floating(seat)?, + ClientMessage::Quit => self.handle_quit(), } Ok(()) } diff --git a/src/drm/drm.rs b/src/drm/drm.rs index c8851fb9..d84f4041 100644 --- a/src/drm/drm.rs +++ b/src/drm/drm.rs @@ -1,106 +1,114 @@ -use crate::utils::bitflags::BitflagsExt; -use crate::utils::debug_fn::debug_fn; -use crate::utils::ptr_ext::PtrExt; -use bstr::ByteSlice; -use std::ffi::{CStr, CString}; -use std::fmt::{Debug, Formatter}; -use std::ptr; +mod sys; + +use std::cell::RefCell; +use crate::drm::drm::sys::{create_lease, drm_mode_modeinfo, get_cap, get_device_name_from_fd2, get_minor_name_from_fd, get_node_type_from_fd, get_nodes, is_master, mode_addfb2, mode_atomic, mode_create_blob, mode_destroy_blob, mode_get_resources, mode_getconnector, mode_getencoder, mode_getplane, mode_getplaneresources, mode_getproperty, mode_obj_getproperties, mode_rmfb, set_client_cap, DRM_DISPLAY_MODE_LEN, DRM_MODE_ATOMIC_TEST_ONLY, DRM_MODE_FB_MODIFIERS, DRM_MODE_OBJECT_BLOB, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_CRTC, DRM_MODE_OBJECT_ENCODER, DRM_MODE_OBJECT_FB, DRM_MODE_OBJECT_MODE, DRM_MODE_OBJECT_PLANE, DRM_MODE_OBJECT_PROPERTY, gem_close, prime_fd_to_handle}; +use crate::utils::oserror::OsError; +use ahash::AHashMap; +use bstr::{BString, ByteSlice}; +use std::ffi::CString; +use std::fmt::{Debug, Display, Formatter}; +use std::mem; +use std::ops::Deref; +use std::rc::{Rc, Weak}; use thiserror::Error; -use uapi::c::c_char; -use uapi::{c, Errno, OwnedFd, Ustring}; +use uapi::{c, OwnedFd, Ustring}; + +use crate::drm::gbm::GbmBo; +use crate::drm::INVALID_MODIFIER; +use crate::utils::stack::Stack; +use crate::ErrorFmt; +pub use sys::{ + DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, + DRM_MODE_PAGE_FLIP_EVENT, +}; #[derive(Debug, Error)] pub enum DrmError { #[error("Could not reopen a node")] ReopenNode(#[source] crate::utils::oserror::OsError), #[error("Could not retrieve the render node name")] - RenderNodeName, + RenderNodeName(#[source] OsError), #[error("Could not retrieve the device node name")] - DeviceNodeName, - #[error("Could not retrieve device")] - GetDevice(#[source] crate::utils::oserror::OsError), -} - -#[allow(dead_code)] -const DRM_NODE_PRIMARY: c::c_int = 0; -#[allow(dead_code)] -const DRM_NODE_CONTROL: c::c_int = 1; -pub const DRM_NODE_RENDER: c::c_int = 2; -const DRM_NODE_MAX: c::c_int = 3; - -const DRM_BUS_PCI: c::c_int = 0; -const DRM_BUS_USB: c::c_int = 1; -const DRM_BUS_PLATFORM: c::c_int = 2; -const DRM_BUS_HOST1X: c::c_int = 3; - -#[link(name = "drm")] -extern "C" { - fn drmIsMaster(fd: c::c_int) -> c::c_int; - fn drmModeCreateLease( - fd: c::c_int, - o: *const u32, - num_objects: c::c_int, - flags: c::c_int, - lessee_id: *mut u32, - ) -> c::c_int; - fn drmGetNodeTypeFromFd(fd: c::c_int) -> c::c_int; - fn drmGetRenderDeviceNameFromFd(fd: c::c_int) -> *mut c::c_char; - fn drmGetDeviceNameFromFd2(fd: c::c_int) -> *mut c::c_char; - fn drmFreeDevice(device: *mut *mut drmDevice); - fn drmGetDevice(fd: c::c_int, device: *mut *mut drmDevice) -> c::c_int; + DeviceNodeName(#[source] OsError), + #[error("Could not retrieve device nodes")] + GetNodes(#[source] OsError), + #[error("Could not retrieve device type")] + GetDeviceType(#[source] OsError), + #[error("Could not perform drm property ioctl")] + GetProperty(#[source] OsError), + #[error("Could not perform drm getencoder ioctl")] + GetEncoder(#[source] OsError), + #[error("Could not perform drm getresources ioctl")] + GetResources(#[source] OsError), + #[error("Could not perform drm getplaneresources ioctl")] + GetPlaneResources(#[source] OsError), + #[error("Could not perform drm getplane ioctl")] + GetPlane(#[source] OsError), + #[error("Could not create a blob")] + CreateBlob(#[source] OsError), + #[error("Could not perform drm getconnector ioctl")] + GetConnector(#[source] OsError), + #[error("Could not perform drm properties ioctl")] + GetProperties(#[source] OsError), + #[error("Could not perform drm atomic ioctl")] + Atomic(#[source] OsError), + #[error("Could not inspect a connector")] + CreateConnector(#[source] Box), + #[error("Drm property has an unknown type {0}")] + UnknownPropertyType(u32), + #[error("Range property does not have exactly two values")] + RangeValues, + #[error("Object property does not have exactly one value")] + ObjectValues, + #[error("Object does not have the required property {0}")] + MissingProperty(Box), + #[error("Plane has an unknown type {0}")] + UnknownPlaneType(BString), + #[error("Plane has an invalid type {0}")] + InvalidPlaneType(u64), + #[error("Plane type property has an invalid property type")] + InvalidPlaneTypeProperty, + #[error("Could not reset drm objects")] + ResetFailed(#[source] Box), + #[error("Could not create a framebuffer")] + AddFb(#[source] OsError), + #[error("Could not convert prime fd to gem handle")] + GemHandle(#[source] OsError), } fn render_node_name(fd: c::c_int) -> Result { - unsafe { - let name = drmGetRenderDeviceNameFromFd(fd); - if name.is_null() { - Err(DrmError::RenderNodeName) - } else { - Ok(CString::from_raw(name).into()) - } - } + get_minor_name_from_fd(fd, NodeType::Render).map_err(DrmError::RenderNodeName) } fn device_node_name(fd: c::c_int) -> Result { - unsafe { - let name = drmGetDeviceNameFromFd2(fd); - if name.is_null() { - Err(DrmError::DeviceNodeName) - } else { - Ok(CString::from_raw(name).into()) - } - } + get_device_name_from_fd2(fd).map_err(DrmError::DeviceNodeName) } -fn reopen(fd: c::c_int, allow_downgrade: bool) -> Result { - unsafe { - if drmIsMaster(fd) != 0 { - let mut lessee = 0; - let lease_fd = drmModeCreateLease(fd, ptr::null(), 0, c::O_CLOEXEC, &mut lessee); - if lease_fd >= 0 { - return Ok(OwnedFd::new(lease_fd)); - } - } - let path = if drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER { - uapi::format_ustr!("/proc/self/fd/{}", fd) - } else if allow_downgrade { - render_node_name(fd)? - } else { - device_node_name(fd)? - }; - match uapi::open(&path, c::O_RDWR | c::O_CLOEXEC, 0) { - Ok(f) => Ok(f), - Err(e) => Err(DrmError::ReopenNode(e.into())), +fn reopen(fd: c::c_int, allow_downgrade: bool) -> Result, DrmError> { + if is_master(fd) && allow_downgrade { + if let Ok((fd, _)) = create_lease(fd, &[], c::O_CLOEXEC as _) { + return Ok(Rc::new(fd)); } } + let path = if get_node_type_from_fd(fd).map_err(DrmError::GetDeviceType)? == NodeType::Render { + uapi::format_ustr!("/proc/self/fd/{}", fd) + } else if allow_downgrade { + render_node_name(fd)? + } else { + device_node_name(fd)? + }; + match uapi::open(&path, c::O_RDWR | c::O_CLOEXEC, 0) { + Ok(f) => Ok(Rc::new(f)), + Err(e) => Err(DrmError::ReopenNode(e.into())), + } } pub struct Drm { - fd: OwnedFd, + fd: Rc, } impl Drm { - pub fn new(fd: c::c_int, allow_downgrade: bool) -> Result { + pub fn reopen(fd: c::c_int, allow_downgrade: bool) -> Result { Ok(Self { fd: reopen(fd, allow_downgrade)?, }) @@ -111,285 +119,640 @@ impl Drm { } pub fn dup_unprivileged(&self) -> Result { - Self::new(self.fd.raw(), true) + Self::reopen(self.fd.raw(), true) } - pub fn get_device(&self) -> Result { - unsafe { - let mut dev = ptr::null_mut(); - if drmGetDevice(self.fd.raw(), &mut dev) < 0 { - return Err(DrmError::GetDevice(Errno::default().into())); - } - Ok(DrmDevice { dev }) - } + pub fn get_nodes(&self) -> Result, DrmError> { + get_nodes(self.fd.raw()).map_err(DrmError::GetNodes) } } -#[repr(C)] -struct drmPciBusInfo { - domain: u16, - bus: u8, - dev: u8, - func: u8, +pub struct DrmMaster { + drm: Drm, + u32_bufs: Stack>, + u64_bufs: Stack>, + gem_handles: RefCell>>, } -#[repr(C)] -struct drmUsbBusInfo { - bus: u8, - dev: u8, -} - -const DRM_PLATFORM_DEVICE_NAME_LEN: usize = 512; - -#[repr(C)] -struct drmPlatformBusInfo { - fullname: [c::c_char; DRM_PLATFORM_DEVICE_NAME_LEN], -} - -const DRM_HOST1X_DEVICE_NAME_LEN: usize = 512; - -#[repr(C)] -struct drmHost1xBusInfo { - fullname: [c::c_char; DRM_HOST1X_DEVICE_NAME_LEN], -} - -#[repr(C)] -union businfo { - pci: *mut drmPciBusInfo, - usb: *mut drmUsbBusInfo, - platform: *mut drmPlatformBusInfo, - host1x: *mut drmHost1xBusInfo, -} - -#[repr(C)] -struct drmPciDeviceInfo { - vendor_id: u16, - device_id: u16, - subvendor_id: u16, - subdevice_id: u16, - revision_id: u8, -} - -#[repr(C)] -struct drmUsbDeviceInfo { - vendor: u16, - product: u16, -} - -#[repr(C)] -struct drmPlatformDeviceInfo { - compatible: *mut *mut c::c_char, -} - -#[repr(C)] -struct drmHost1xDeviceInfo { - compatible: *mut *mut c::c_char, -} - -#[repr(C)] -union deviceinfo { - pci: *mut drmPciDeviceInfo, - usb: *mut drmUsbDeviceInfo, - platform: *mut drmPlatformDeviceInfo, - host1x: *mut drmHost1xDeviceInfo, -} - -#[repr(C)] -struct drmDevice { - nodes: *mut *mut c::c_char, - available_nodes: c::c_int, - bustype: c::c_int, - businfo: businfo, - deviceinfo: deviceinfo, -} - -pub struct DrmDevice { - dev: *mut drmDevice, -} - -impl Drop for DrmDevice { - fn drop(&mut self) { - unsafe { - drmFreeDevice(&mut self.dev); - } - } -} - -impl DrmDevice { - pub fn nodes<'a>(&'a self) -> impl Iterator + 'a { - struct Iter<'a> { - next: usize, - dev: &'a DrmDevice, - } - impl<'a> Iterator for Iter<'a> { - type Item = (c::c_int, &'a CStr); - - fn next(&mut self) -> Option { - unsafe { - let dev = self.dev.dev.deref(); - while self.next < DRM_NODE_MAX as _ { - let idx = self.next; - self.next += 1; - if dev.available_nodes.contains(1 << idx) { - return Some((idx as _, CStr::from_ptr(*dev.nodes.add(idx)))); - } - } - None - } - } - } - Iter { next: 0, dev: self } - } -} - -impl Debug for DrmDevice { +impl Debug for DrmMaster { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - struct StrStr<'a> { - v: &'a [*mut c::c_char], + write!(f, "{}", self.drm.raw()) + } +} + +impl Deref for DrmMaster { + type Target = Drm; + + fn deref(&self) -> &Self::Target { + &self.drm + } +} + +impl DrmMaster { + pub fn new(fd: Rc) -> Self { + Self { + drm: Drm { fd }, + u32_bufs: Default::default(), + u64_bufs: Default::default(), + gem_handles: Default::default(), } - impl Debug for StrStr<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut list = f.debug_list(); - for &v in self.v { - if v.is_null() { - list.entry(&v); - } else { - unsafe { - list.entry(&CStr::from_ptr(v)); - } - } - } - list.finish() + } + + pub fn raw(&self) -> c::c_int { + self.drm.raw() + } + + pub fn get_property(&self, prop: DrmProperty) -> Result { + mode_getproperty(self.raw(), prop) + } + + pub fn get_properties(&self, t: T) -> Result, DrmError> { + mode_obj_getproperties(self.raw(), t.id(), T::TYPE) + } + + pub fn get_resources(&self) -> Result { + mode_get_resources(self.raw()) + } + + pub fn get_cap(&self, cap: u64) -> Result { + get_cap(self.raw(), cap) + } + + pub fn set_client_cap(&self, cap: u64, value: u64) -> Result<(), OsError> { + set_client_cap(self.raw(), cap, value) + } + + pub fn get_planes(&self) -> Result, DrmError> { + mode_getplaneresources(self.raw()) + } + + pub fn get_plane_info(&self, plane: DrmPlane) -> Result { + mode_getplane(self.raw(), plane.0) + } + + pub fn get_encoder_info(&self, encoder: DrmEncoder) -> Result { + mode_getencoder(self.raw(), encoder.0) + } + + pub fn get_connector_info( + &self, + connector: DrmConnector, + force: bool, + ) -> Result { + mode_getconnector(self.raw(), connector.0, force) + } + + pub fn change(self: &Rc, flags: u32) -> Change { + let mut res = Change { + master: self.clone(), + flags, + objects: self.u32_bufs.pop().unwrap_or_default(), + object_lengths: self.u32_bufs.pop().unwrap_or_default(), + props: self.u32_bufs.pop().unwrap_or_default(), + values: self.u64_bufs.pop().unwrap_or_default(), + }; + res.objects.clear(); + res.object_lengths.clear(); + res.props.clear(); + res.values.clear(); + res + } + + pub fn create_blob(self: &Rc, t: &T) -> Result { + match mode_create_blob(self.raw(), t) { + Ok(b) => Ok(PropBlob { + master: self.clone(), + id: b, + }), + Err(e) => Err(DrmError::CreateBlob(e)), + } + } + + pub fn add_fb(self: &Rc, bo: &GbmBo) -> Result { + let dma = bo.dma(); + let mut modifier = 0; + let mut flags = 0; + if dma.modifier != INVALID_MODIFIER { + modifier = dma.modifier; + flags |= DRM_MODE_FB_MODIFIERS; + } + let mut strides = [0; 4]; + let mut offsets = [0; 4]; + let mut handles = [0; 4]; + let mut handles_ = vec![]; + for (idx, plane) in dma.planes.iter().enumerate() { + strides[idx] = plane.stride; + offsets[idx] = plane.offset; + let handle = self.gem_handle(plane.fd.raw())?; + handles[idx] = handle.handle(); + handles_.push(handle); + } + match mode_addfb2( + self.raw(), + dma.width as _, + dma.height as _, + dma.format.drm, + flags, + handles, + strides, + offsets, + modifier, + ) { + Ok(fb) => Ok(DrmFramebuffer { + master: self.clone(), + fb, + }), + Err(e) => return Err(DrmError::AddFb(e)), + } + } + + pub fn gem_handle(self: &Rc, fd: c::c_int) -> Result, DrmError> { + let handle = match prime_fd_to_handle(self.raw(), fd) { + Ok(h) => h, + Err(e) => return Err(DrmError::GemHandle(e)), + }; + let mut handles = self.gem_handles.borrow_mut(); + if let Some(h) = handles.get(&handle) { + if let Some(h) = h.upgrade() { + return Ok(h); } } - impl<'a> StrStr<'a> { - fn from_nt(nt: *const *mut c_char) -> Self { - unsafe { - let mut num = 0; - let mut tmp = nt; - while !tmp.deref().is_null() { - num += 1; - tmp = tmp.add(1); - } - Self { - v: std::slice::from_raw_parts(nt, num), - } - } - } - } - let mut ds = f.debug_struct("DrmDevice"); - unsafe { - let dev = self.dev.deref(); - let nodes = std::slice::from_raw_parts(dev.nodes, DRM_NODE_MAX as _); - ds.field( - "available_nodes", - &debug_fn(|f| write!(f, "0b{:b}", dev.available_nodes)), - ); - ds.field("nodes", &StrStr { v: nodes }); - ds.field("bustype", &dev.bustype); - match dev.bustype { - DRM_BUS_PCI => { - ds.field( - "businfo", - &debug_fn(|f| { - let pci = dev.businfo.pci.deref(); - f.debug_struct("drmPciBusInfo") - .field("domain", &pci.domain) - .field("bus", &pci.bus) - .field("dev", &pci.dev) - .field("func", &pci.func) - .finish() - }), - ); - ds.field( - "deviceinfo", - &debug_fn(|f| { - let pci = dev.deviceinfo.pci.deref(); - f.debug_struct("drmPciDeviceInfo") - .field("vendor_id", &pci.vendor_id) - .field("device_id", &pci.device_id) - .field("subvendor_id", &pci.subvendor_id) - .field("subdevice_id", &pci.subdevice_id) - .field("revision_id", &pci.revision_id) - .finish() - }), - ); - } - DRM_BUS_USB => { - ds.field( - "businfo", - &debug_fn(|f| { - let usb = dev.businfo.usb.deref(); - f.debug_struct("drmUsbBusInfo") - .field("bus", &usb.bus) - .field("dev", &usb.dev) - .finish() - }), - ); - ds.field( - "deviceinfo", - &debug_fn(|f| { - let usb = dev.deviceinfo.usb.deref(); - f.debug_struct("drmUsbDeviceInfo") - .field("vendor", &usb.vendor) - .field("product", &usb.product) - .finish() - }), - ); - } - DRM_BUS_PLATFORM => { - ds.field( - "businfo", - &debug_fn(|f| { - let platform = dev.businfo.platform.deref(); - f.debug_struct("drmPlatformBusInfo") - .field( - "fullname", - &CStr::from_ptr(platform.fullname.as_ptr()) - .to_bytes() - .as_bstr(), - ) - .finish() - }), - ); - ds.field( - "deviceinfo", - &debug_fn(|f| { - let platform = dev.deviceinfo.platform.deref(); - f.debug_struct("drmPlatformDeviceInfo") - .field("compatible", &StrStr::from_nt(platform.compatible)) - .finish() - }), - ); - } - DRM_BUS_HOST1X => { - ds.field( - "businfo", - &debug_fn(|f| { - let host1x = dev.businfo.host1x.deref(); - f.debug_struct("drmHost1xBusInfo") - .field( - "fullname", - &CStr::from_ptr(host1x.fullname.as_ptr()) - .to_bytes() - .as_bstr(), - ) - .finish() - }), - ); - ds.field( - "deviceinfo", - &debug_fn(|f| { - let host1x = dev.deviceinfo.host1x.deref(); - f.debug_struct("drmHost1xDeviceInfo") - .field("compatible", &StrStr::from_nt(host1x.compatible)) - .finish() - }), - ); - } - _ => {} - } - ds.finish() + let h = Rc::new(GemHandle { + master: self.clone(), + handle, + }); + handles.insert(handle, Rc::downgrade(&h)); + Ok(h) + } +} + +pub struct DrmFramebuffer { + master: Rc, + fb: DrmFb, +} + +impl Debug for DrmFramebuffer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DrmFramebuffer") + .field("fb", &self.fb) + .finish_non_exhaustive() + } +} + +impl DrmFramebuffer { + pub fn id(&self) -> DrmFb { + self.fb + } +} + +impl Drop for DrmFramebuffer { + fn drop(&mut self) { + if let Err(e) = mode_rmfb(self.master.raw(), self.fb) { + log::error!("Could not delete framebuffer: {}", ErrorFmt(e)); + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum NodeType { + Primary, + Control, + Render, +} + +impl NodeType { + fn name(self) -> &'static str { + match self { + NodeType::Primary => "card", + NodeType::Control => "controlD", + NodeType::Render => "renderD", + } + } +} + +#[derive(Debug)] +pub struct DrmPropertyDefinition { + pub id: DrmProperty, + pub name: BString, + pub immutable: bool, + pub atomic: bool, + pub ty: DrmPropertyType, +} + +#[derive(Debug)] +pub enum DrmPropertyType { + Range { + min: u64, + max: u64, + }, + SignedRange { + min: i64, + max: i64, + }, + Object { + ty: u32, + }, + Blob, + Enum { + values: Vec, + bitmask: bool, + }, +} + +#[derive(Debug)] +pub struct DrmPropertyEnumValue { + pub value: u64, + pub name: BString, +} + +#[derive(Debug)] +pub struct DrmPropertyValue { + pub id: DrmProperty, + pub value: u64, +} + +pub trait DrmObject { + const TYPE: u32; + const NONE: Self; + fn id(&self) -> u32; + fn is_some(&self) -> bool; + fn is_none(&self) -> bool; +} + +macro_rules! drm_obj { + ($name:ident, $ty:expr) => { + #[repr(transparent)] + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] + pub struct $name(pub u32); + + impl DrmObject for $name { + const TYPE: u32 = $ty; + const NONE: Self = Self(0); + + fn id(&self) -> u32 { + self.0 + } + + fn is_some(&self) -> bool { + self.0 != 0 + } + + fn is_none(&self) -> bool { + self.0 == 0 + } + } + }; +} +drm_obj!(DrmCrtc, DRM_MODE_OBJECT_CRTC); +drm_obj!(DrmConnector, DRM_MODE_OBJECT_CONNECTOR); +drm_obj!(DrmEncoder, DRM_MODE_OBJECT_ENCODER); +drm_obj!(DrmMode, DRM_MODE_OBJECT_MODE); +drm_obj!(DrmProperty, DRM_MODE_OBJECT_PROPERTY); +drm_obj!(DrmFb, DRM_MODE_OBJECT_FB); +drm_obj!(DrmBlob, DRM_MODE_OBJECT_BLOB); +drm_obj!(DrmPlane, DRM_MODE_OBJECT_PLANE); + +#[derive(Debug)] +pub struct DrmCardResources { + pub min_width: u32, + pub max_width: u32, + pub min_height: u32, + pub max_height: u32, + pub fbs: Vec, + pub crtcs: Vec, + pub connectors: Vec, + pub encoders: Vec, +} + +#[derive(Debug)] +pub struct DrmPlaneInfo { + pub plane_id: DrmPlane, + pub crtc_id: DrmCrtc, + pub fb_id: DrmFb, + pub possible_crtcs: u32, + pub gamma_size: u32, + pub format_types: Vec, +} + +#[derive(Debug)] +pub struct DrmEncoderInfo { + pub encoder_id: DrmEncoder, + pub encoder_type: u32, + pub crtc_id: DrmCrtc, + pub possible_crtcs: u32, + pub possible_clones: u32, +} + +#[derive(Debug, Clone)] +pub struct DrmModeInfo { + pub clock: u32, + pub hdisplay: u16, + pub hsync_start: u16, + pub hsync_end: u16, + pub htotal: u16, + pub hskew: u16, + pub vdisplay: u16, + pub vsync_start: u16, + pub vsync_end: u16, + pub vtotal: u16, + pub vscan: u16, + + pub vrefresh: u32, + + pub flags: u32, + pub ty: u32, + pub name: BString, +} + +impl DrmModeInfo { + pub fn create_blob(&self, master: &Rc) -> Result { + let raw = self.into_raw(); + master.create_blob(&raw) + } + + fn into_raw(&self) -> drm_mode_modeinfo { + let mut name = [0u8; DRM_DISPLAY_MODE_LEN]; + let len = name.len().min(self.name.len()); + name[..len].copy_from_slice(&self.name.as_bytes()[..len]); + drm_mode_modeinfo { + clock: self.clock, + hdisplay: self.hdisplay, + hsync_start: self.hsync_start, + hsync_end: self.hsync_end, + htotal: self.htotal, + hskew: self.hskew, + vdisplay: self.vdisplay, + vsync_start: self.vsync_start, + vsync_end: self.vsync_end, + vtotal: self.vtotal, + vscan: self.vscan, + vrefresh: self.vrefresh, + flags: self.flags, + ty: self.ty, + name, + } + } +} + +#[derive(Debug)] +pub struct DrmConnectorInfo { + pub encoders: Vec, + pub modes: Vec, + pub props: Vec, + + pub encoder_id: DrmEncoder, + pub connector_id: DrmConnector, + pub connector_type: u32, + pub connector_type_id: u32, + + pub connection: u32, + pub mm_width: u32, + pub mm_height: u32, + pub subpixel: u32, +} + +pub struct Change { + master: Rc, + flags: u32, + objects: Vec, + object_lengths: Vec, + props: Vec, + values: Vec, +} + +pub struct ObjectChange<'a> { + change: &'a mut Change, +} + +impl Change { + pub fn test(&self) -> Result<(), DrmError> { + mode_atomic( + self.master.raw(), + self.flags | DRM_MODE_ATOMIC_TEST_ONLY, + &self.objects, + &self.object_lengths, + &self.props, + &self.values, + 0, + ) + } + + pub fn commit(&self, user_data: u64) -> Result<(), DrmError> { + mode_atomic( + self.master.raw(), + self.flags, + &self.objects, + &self.object_lengths, + &self.props, + &self.values, + user_data, + ) + } + + pub fn change_object(&mut self, obj: T, f: F) + where + T: DrmObject, + F: FnOnce(&mut ObjectChange), + { + let old_len = self.props.len(); + let mut oc = ObjectChange { change: self }; + f(&mut oc); + if self.props.len() > old_len { + let new = (self.props.len() - old_len) as u32; + if self.objects.last() == Some(&obj.id()) { + *self.object_lengths.last_mut().unwrap() += new; + } else { + self.objects.push(obj.id()); + self.object_lengths.push(new); + } + } + } +} + +impl<'a> ObjectChange<'a> { + pub fn change(&mut self, property_id: DrmProperty, value: u64) { + self.change.props.push(property_id.0); + self.change.values.push(value); + } +} + +impl Drop for Change { + fn drop(&mut self) { + self.master.u32_bufs.push(mem::take(&mut self.objects)); + self.master + .u32_bufs + .push(mem::take(&mut self.object_lengths)); + self.master.u32_bufs.push(mem::take(&mut self.props)); + self.master.u64_bufs.push(mem::take(&mut self.values)); + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug)] +pub enum ConnectorType { + Unknown(u32), + VGA, + DVII, + DVID, + DVIA, + Composite, + SVIDEO, + LVDS, + Component, + _9PinDIN, + DisplayPort, + HDMIA, + HDMIB, + TV, + eDP, + VIRTUAL, + DSI, + DPI, + WRITEBACK, + SPI, + USB, +} + +impl Display for ConnectorType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::Unknown(n) => return write!(f, "Unknown({})", n), + Self::VGA => "VGA", + Self::DVII => "DVI-I", + Self::DVID => "DVI-D", + Self::DVIA => "DVI-A", + Self::Composite => "Composite", + Self::SVIDEO => "SVIDEO", + Self::LVDS => "LVDS", + Self::Component => "Component", + Self::_9PinDIN => "DIN", + Self::DisplayPort => "DP", + Self::HDMIA => "HDMI-A", + Self::HDMIB => "HDMI-B", + Self::TV => "TV", + Self::eDP => "eDP", + Self::VIRTUAL => "Virtual", + Self::DSI => "DSI", + Self::DPI => "DPI", + Self::WRITEBACK => "Writeback", + Self::SPI => "SPI", + Self::USB => "USB", + }; + f.write_str(s) + } +} + +impl From for ConnectorType { + fn from(v: u32) -> Self { + match v { + sys::DRM_MODE_CONNECTOR_VGA => Self::VGA, + sys::DRM_MODE_CONNECTOR_DVII => Self::DVII, + sys::DRM_MODE_CONNECTOR_DVID => Self::DVID, + sys::DRM_MODE_CONNECTOR_DVIA => Self::DVIA, + sys::DRM_MODE_CONNECTOR_Composite => Self::Composite, + sys::DRM_MODE_CONNECTOR_SVIDEO => Self::SVIDEO, + sys::DRM_MODE_CONNECTOR_LVDS => Self::LVDS, + sys::DRM_MODE_CONNECTOR_Component => Self::Component, + sys::DRM_MODE_CONNECTOR_9PinDIN => Self::_9PinDIN, + sys::DRM_MODE_CONNECTOR_DisplayPort => Self::DisplayPort, + sys::DRM_MODE_CONNECTOR_HDMIA => Self::HDMIA, + sys::DRM_MODE_CONNECTOR_HDMIB => Self::HDMIB, + sys::DRM_MODE_CONNECTOR_TV => Self::TV, + sys::DRM_MODE_CONNECTOR_eDP => Self::eDP, + sys::DRM_MODE_CONNECTOR_VIRTUAL => Self::VIRTUAL, + sys::DRM_MODE_CONNECTOR_DSI => Self::DSI, + sys::DRM_MODE_CONNECTOR_DPI => Self::DPI, + sys::DRM_MODE_CONNECTOR_WRITEBACK => Self::WRITEBACK, + sys::DRM_MODE_CONNECTOR_SPI => Self::SPI, + sys::DRM_MODE_CONNECTOR_USB => Self::USB, + _ => Self::Unknown(v), + } + } +} + +impl Into for ConnectorType { + fn into(self) -> u32 { + match self { + Self::Unknown(n) => n, + Self::VGA => sys::DRM_MODE_CONNECTOR_VGA, + Self::DVII => sys::DRM_MODE_CONNECTOR_DVII, + Self::DVID => sys::DRM_MODE_CONNECTOR_DVID, + Self::DVIA => sys::DRM_MODE_CONNECTOR_DVIA, + Self::Composite => sys::DRM_MODE_CONNECTOR_Composite, + Self::SVIDEO => sys::DRM_MODE_CONNECTOR_SVIDEO, + Self::LVDS => sys::DRM_MODE_CONNECTOR_LVDS, + Self::Component => sys::DRM_MODE_CONNECTOR_Component, + Self::_9PinDIN => sys::DRM_MODE_CONNECTOR_9PinDIN, + Self::DisplayPort => sys::DRM_MODE_CONNECTOR_DisplayPort, + Self::HDMIA => sys::DRM_MODE_CONNECTOR_HDMIA, + Self::HDMIB => sys::DRM_MODE_CONNECTOR_HDMIB, + Self::TV => sys::DRM_MODE_CONNECTOR_TV, + Self::eDP => sys::DRM_MODE_CONNECTOR_eDP, + Self::VIRTUAL => sys::DRM_MODE_CONNECTOR_VIRTUAL, + Self::DSI => sys::DRM_MODE_CONNECTOR_DSI, + Self::DPI => sys::DRM_MODE_CONNECTOR_DPI, + Self::WRITEBACK => sys::DRM_MODE_CONNECTOR_WRITEBACK, + Self::SPI => sys::DRM_MODE_CONNECTOR_SPI, + Self::USB => sys::DRM_MODE_CONNECTOR_USB, + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum ConnectorStatus { + Connected, + Disconnected, + Unknown, + Other(u32), +} + +impl From for ConnectorStatus { + fn from(v: u32) -> Self { + match v { + sys::CONNECTOR_STATUS_CONNECTED => Self::Connected, + sys::CONNECTOR_STATUS_DISCONNECTED => Self::Disconnected, + sys::CONNECTOR_STATUS_UNKNOWN => Self::Unknown, + _ => Self::Other(v), + } + } +} + +#[derive(Debug)] +pub struct PropBlob { + master: Rc, + id: DrmBlob, +} + +impl PropBlob { + pub fn id(&self) -> DrmBlob { + self.id + } +} + +impl Drop for PropBlob { + fn drop(&mut self) { + if let Err(e) = mode_destroy_blob(self.master.raw(), self.id) { + log::error!("Could not destroy blob: {}", ErrorFmt(e)); + } + } +} + +pub struct GemHandle { + master: Rc, + handle: u32, +} + +impl GemHandle { + pub fn handle(&self) -> u32 { + self.handle + } +} + +impl Drop for GemHandle { + fn drop(&mut self) { + self.master.gem_handles.borrow_mut().remove(&self.handle); + if let Err(e) = gem_close(self.master.raw(), self.handle) { + log::error!("Could not close gem handle: {}", ErrorFmt(e)); } } } diff --git a/src/drm/drm/sys.rs b/src/drm/drm/sys.rs new file mode 100644 index 00000000..bceb511d --- /dev/null +++ b/src/drm/drm/sys.rs @@ -0,0 +1,1034 @@ +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] + +use crate::drm::drm::{ + DrmBlob, DrmCardResources, DrmConnector, DrmConnectorInfo, DrmCrtc, DrmEncoder, DrmEncoderInfo, + DrmError, DrmFb, DrmModeInfo, DrmPlane, DrmPlaneInfo, DrmProperty, DrmPropertyDefinition, + DrmPropertyEnumValue, DrmPropertyType, DrmPropertyValue, NodeType, +}; +use crate::utils::bitflags::BitflagsExt; +use crate::utils::oserror::OsError; +use ahash::AHashMap; +use bstr::ByteSlice; +use std::ffi::CString; +use std::io::{BufRead, BufReader}; +use std::mem; +use uapi::{c, OwnedFd, Ustring}; + +pub unsafe fn ioctl(fd: c::c_int, request: c::c_ulong, t: &mut T) -> Result { + let mut ret; + loop { + ret = c::ioctl(fd, request, &mut *t); + if ret != -1 { + return Ok(ret); + } + let err = uapi::get_errno(); + if !matches!(err, c::EINTR | c::EAGAIN) { + return Err(OsError(err)); + } + } +} + +pub const DRM_IOCTL_BASE: u64 = b'd' as u64; + +pub const fn drm_iow(nr: u64) -> u64 { + uapi::_IOW::(DRM_IOCTL_BASE, nr) +} + +pub const fn drm_iowr(nr: u64) -> u64 { + uapi::_IOWR::(DRM_IOCTL_BASE, nr) +} + +const DRM_IOCTL_AUTH_MAGIC: u64 = drm_iow::(0x11); + +pub type drm_magic_t = c::c_int; + +#[repr(C)] +struct drm_auth { + magic: drm_magic_t, +} + +pub fn auth_magic(fd: c::c_int, magic: drm_magic_t) -> Result<(), OsError> { + let mut auth = drm_auth { magic }; + unsafe { ioctl(fd, DRM_IOCTL_AUTH_MAGIC, &mut auth).map(drop) } +} + +pub fn is_master(fd: c::c_int) -> bool { + match auth_magic(fd, 0) { + Err(OsError(c::EACCES)) => false, + _ => true, + } +} + +const DRM_IOCTL_MODE_CREATE_LEASE: u64 = drm_iowr::(0xc6); + +#[repr(C)] +struct drm_mode_create_lease { + object_ids: u64, + object_count: u32, + flags: u32, + lessee_id: u32, + fd: u32, +} + +pub fn create_lease(fd: c::c_int, objects: &[u32], flags: u32) -> Result<(OwnedFd, u32), OsError> { + let mut create = drm_mode_create_lease { + object_ids: objects.as_ptr() as usize as _, + object_count: objects.len() as _, + flags, + lessee_id: 0, + fd: 0, + }; + unsafe { + ioctl(fd, DRM_IOCTL_MODE_CREATE_LEASE, &mut create)?; + } + Ok((OwnedFd::new(create.fd as _), create.lessee_id)) +} + +pub fn get_node_type_from_fd(fd: c::c_int) -> Result { + let (_, _, min) = drm_stat(fd)?; + get_minor_type(min) +} + +pub fn node_is_drm(maj: u64, min: u64) -> bool { + let path = device_dir(maj, min); + uapi::stat(&path).is_ok() +} + +pub fn get_minor_type(min: u64) -> Result { + const DRM_NODE_PRIMARY: u64 = 0; + const DRM_NODE_CONTROL: u64 = 1; + const DRM_NODE_RENDER: u64 = 2; + match min >> 6 { + DRM_NODE_PRIMARY => Ok(NodeType::Primary), + DRM_NODE_CONTROL => Ok(NodeType::Control), + DRM_NODE_RENDER => Ok(NodeType::Render), + _ => Err(OsError(c::ENODEV)), + } +} + +const DRM_DIR_NAME: &'static str = "/dev/dri"; + +fn device_dir(maj: u64, min: u64) -> Ustring { + uapi::format_ustr!("/sys/dev/char/{maj}:{min}/device/drm") +} + +pub fn get_minor_name_from_fd(fd: c::c_int, ty: NodeType) -> Result { + let (_, maj, min) = drm_stat(fd)?; + + let dir = device_dir(maj, min); + let mut dir = uapi::opendir(&dir)?; + + while let Some(entry) = uapi::readdir(&mut dir) { + let entry = entry?; + if entry.name().to_bytes().starts_with_str(ty.name()) { + return Ok(uapi::format_ustr!( + "{}/{}", + DRM_DIR_NAME, + entry.name().to_bytes().as_bstr() + )); + } + } + Err(OsError(c::ENOENT)) +} + +fn drm_stat(fd: c::c_int) -> Result<(c::stat, u64, u64), OsError> { + let stat = uapi::fstat(fd)?; + + let maj = uapi::major(stat.st_rdev); + let min = uapi::minor(stat.st_rdev); + + if !is_drm(maj, min, &stat) { + return Err(OsError(c::ENODEV)); + } + + Ok((stat, maj, min)) +} + +fn is_drm(maj: u64, min: u64, stat: &c::stat) -> bool { + stat.st_mode & c::S_IFMT == c::S_IFCHR && node_is_drm(maj, min) +} + +pub fn get_device_name_from_fd2(fd: c::c_int) -> Result { + let (_, maj, min) = drm_stat(fd)?; + let path = uapi::format_ustr!("/sys/dev/char/{maj}:{min}/uevent"); + let mut buf = vec![]; + let mut br = BufReader::new(uapi::open(&path, c::O_RDONLY, 0)?); + loop { + buf.clear(); + if br.read_until(b'\n', &mut buf)? == 0 { + break; + } + if let Some(pf) = buf.strip_prefix(b"DEVNAME=") { + return Ok(uapi::format_ustr!("/dev/{}", pf.trim_end().as_bstr())); + } + } + Err(OsError(c::ENOENT)) +} + +pub fn get_nodes(fd: c::c_int) -> Result, OsError> { + let (_, maj, min) = drm_stat(fd)?; + + let dir = device_dir(maj, min); + let mut dir = uapi::opendir(&dir)?; + + let mut res = AHashMap::new(); + + 'outer: while let Some(entry) = uapi::readdir(&mut dir) { + let entry = entry?; + let name = entry.name().to_bytes(); + let ty = 'ty: { + for ty in [NodeType::Render, NodeType::Control, NodeType::Primary] { + if name.starts_with_str(ty.name()) { + break 'ty ty; + } + } + continue 'outer; + }; + res.insert( + ty, + uapi::format_ustr!("{}/{}", DRM_DIR_NAME, name.as_bstr()) + .into_c_string() + .unwrap(), + ); + } + + Ok(res) +} + +const DRM_PROP_NAME_LEN: usize = 32; + +#[repr(C)] +#[derive(Default)] +struct drm_mode_get_property { + values_ptr: u64, + enum_blob_ptr: u64, + prop_id: u32, + flags: u32, + name: [u8; DRM_PROP_NAME_LEN], + count_values: u32, + count_enum_blobs: u32, +} + +const DRM_IOCTL_MODE_GETPROPERTY: u64 = drm_iowr::(0xaa); + +const DRM_MODE_PROP_PENDING: u32 = 1 << 0; +const DRM_MODE_PROP_RANGE: u32 = 1 << 1; +const DRM_MODE_PROP_IMMUTABLE: u32 = 1 << 2; +const DRM_MODE_PROP_ENUM: u32 = 1 << 3; +const DRM_MODE_PROP_BLOB: u32 = 1 << 4; +const DRM_MODE_PROP_BITMASK: u32 = 1 << 5; + +const DRM_MODE_PROP_LEGACY_TYPE: u32 = + DRM_MODE_PROP_RANGE | DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BLOB | DRM_MODE_PROP_BITMASK; + +const DRM_MODE_PROP_EXTENDED_TYPE: u32 = 0x0000ffc0; +const fn drm_mode_prop_type(n: u32) -> u32 { + n << 6 +} +const DRM_MODE_PROP_OBJECT: u32 = drm_mode_prop_type(1); +const DRM_MODE_PROP_SIGNED_RANGE: u32 = drm_mode_prop_type(2); + +const DRM_MODE_PROP_ATOMIC: u32 = 0x80000000; + +#[repr(C)] +struct drm_mode_property_enum { + value: u64, + name: [u8; DRM_PROP_NAME_LEN], +} + +pub fn mode_getproperty( + fd: c::c_int, + property_id: DrmProperty, +) -> Result { + let mut prop = drm_mode_get_property { + prop_id: property_id.0, + ..Default::default() + }; + + let get = |prop: &mut drm_mode_get_property| { + unsafe { + if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETPROPERTY, prop) { + return Err(DrmError::GetProperty(e)); + } + } + Ok(()) + }; + + get(&mut prop)?; + + let ty = prop.flags & (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE); + let ty = match ty { + DRM_MODE_PROP_RANGE | DRM_MODE_PROP_SIGNED_RANGE => { + if prop.count_values != 2 { + return Err(DrmError::RangeValues); + } + prop.count_enum_blobs = 0; + let mut vals = [0u64, 0]; + prop.values_ptr = vals.as_mut_ptr() as _; + get(&mut prop)?; + if ty == DRM_MODE_PROP_RANGE { + DrmPropertyType::Range { + min: vals[0], + max: vals[1], + } + } else { + DrmPropertyType::SignedRange { + min: vals[0] as _, + max: vals[1] as _, + } + } + } + DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK => { + prop.count_values = 0; + let mut props = + Vec::::with_capacity(prop.count_enum_blobs as usize); + unsafe { + props.set_len(prop.count_enum_blobs as usize); + } + prop.enum_blob_ptr = props.as_mut_ptr() as _; + get(&mut prop)?; + let mut values = Vec::with_capacity(props.len()); + for v in props { + values.push(DrmPropertyEnumValue { + value: v.value, + name: v.name.split(|n| *n == 0).next().unwrap().to_vec().into(), + }) + } + DrmPropertyType::Enum { + values, + bitmask: ty == DRM_MODE_PROP_BITMASK, + } + } + DRM_MODE_PROP_BLOB => DrmPropertyType::Blob, + DRM_MODE_PROP_OBJECT => { + if prop.count_values != 1 { + return Err(DrmError::ObjectValues); + } + let mut ty = 0u64; + prop.values_ptr = &mut ty as *mut _ as u64; + get(&mut prop)?; + DrmPropertyType::Object { ty: ty as _ } + } + _ => return Err(DrmError::UnknownPropertyType(ty)), + }; + + Ok(DrmPropertyDefinition { + id: property_id, + name: prop.name.split(|n| *n == 0).next().unwrap().to_vec().into(), + immutable: prop.flags.contains(DRM_MODE_PROP_IMMUTABLE), + atomic: prop.flags.contains(DRM_MODE_PROP_ATOMIC), + ty, + }) +} + +#[repr(C)] +#[derive(Debug)] +struct drm_mode_obj_get_properties { + props_ptr: u64, + prop_values_ptr: u64, + count_props: u32, + obj_id: u32, + obj_type: u32, +} + +const DRM_IOCTL_MODE_OBJ_GETPROPERTIES: u64 = drm_iowr::(0xb9); + +pub fn mode_obj_getproperties( + fd: c::c_int, + obj_id: u32, + obj_type: u32, +) -> Result, DrmError> { + let mut props = drm_mode_obj_get_properties { + props_ptr: 0, + prop_values_ptr: 0, + count_props: 0, + obj_id, + obj_type, + }; + + let get = |prop: &mut drm_mode_obj_get_properties| { + unsafe { + if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, prop) { + return Err(DrmError::GetProperties(e)); + } + } + Ok(()) + }; + + get(&mut props)?; + + let mut ids = Vec::::new(); + let mut values = Vec::::new(); + let mut num_props = 0; + + while num_props != props.count_props { + num_props = props.count_props; + + ids.reserve(num_props as _); + values.reserve(num_props as _); + + props.props_ptr = ids.as_mut_ptr() as _; + props.prop_values_ptr = values.as_mut_ptr() as _; + + get(&mut props)?; + } + + unsafe { + ids.set_len(num_props as _); + values.set_len(num_props as _); + } + + let mut props = Vec::with_capacity(num_props as _); + for (id, value) in ids.into_iter().zip(values.into_iter()) { + props.push(DrmPropertyValue { + id: DrmProperty(id), + value, + }) + } + Ok(props) +} + +pub const DRM_MODE_OBJECT_CRTC: u32 = 0xcccccccc; +pub const DRM_MODE_OBJECT_CONNECTOR: u32 = 0xc0c0c0c0; +pub const DRM_MODE_OBJECT_ENCODER: u32 = 0xe0e0e0e0; +pub const DRM_MODE_OBJECT_MODE: u32 = 0xdededede; +pub const DRM_MODE_OBJECT_PROPERTY: u32 = 0xb0b0b0b0; +pub const DRM_MODE_OBJECT_FB: u32 = 0xfbfbfbfb; +pub const DRM_MODE_OBJECT_BLOB: u32 = 0xbbbbbbbb; +pub const DRM_MODE_OBJECT_PLANE: u32 = 0xeeeeeeee; +pub const DRM_MODE_OBJECT_ANY: u32 = 0; + +pub const DRM_MODE_CONNECTOR_Unknown: u32 = 0; +pub const DRM_MODE_CONNECTOR_VGA: u32 = 1; +pub const DRM_MODE_CONNECTOR_DVII: u32 = 2; +pub const DRM_MODE_CONNECTOR_DVID: u32 = 3; +pub const DRM_MODE_CONNECTOR_DVIA: u32 = 4; +pub const DRM_MODE_CONNECTOR_Composite: u32 = 5; +pub const DRM_MODE_CONNECTOR_SVIDEO: u32 = 6; +pub const DRM_MODE_CONNECTOR_LVDS: u32 = 7; +pub const DRM_MODE_CONNECTOR_Component: u32 = 8; +pub const DRM_MODE_CONNECTOR_9PinDIN: u32 = 9; +pub const DRM_MODE_CONNECTOR_DisplayPort: u32 = 10; +pub const DRM_MODE_CONNECTOR_HDMIA: u32 = 11; +pub const DRM_MODE_CONNECTOR_HDMIB: u32 = 12; +pub const DRM_MODE_CONNECTOR_TV: u32 = 13; +pub const DRM_MODE_CONNECTOR_eDP: u32 = 14; +pub const DRM_MODE_CONNECTOR_VIRTUAL: u32 = 15; +pub const DRM_MODE_CONNECTOR_DSI: u32 = 16; +pub const DRM_MODE_CONNECTOR_DPI: u32 = 17; +pub const DRM_MODE_CONNECTOR_WRITEBACK: u32 = 18; +pub const DRM_MODE_CONNECTOR_SPI: u32 = 19; +pub const DRM_MODE_CONNECTOR_USB: u32 = 20; + +#[repr(C)] +struct drm_set_client_cap { + capability: u64, + value: u64, +} + +const DRM_IOCTL_SET_CLIENT_CAP: u64 = drm_iow::(0x0d); + +pub const DRM_CLIENT_CAP_ATOMIC: u64 = 3; + +pub fn set_client_cap(fd: c::c_int, capability: u64, value: u64) -> Result<(), OsError> { + let mut cap = drm_set_client_cap { capability, value }; + unsafe { + ioctl(fd, DRM_IOCTL_SET_CLIENT_CAP, &mut cap)?; + } + Ok(()) +} + +#[repr(C)] +struct drm_get_cap { + capability: u64, + value: u64, +} + +const DRM_IOCTL_GET_CAP: u64 = drm_iowr::(0x0c); + +pub fn get_cap(fd: c::c_int, capability: u64) -> Result { + let mut cap = drm_get_cap { + capability, + value: 0, + }; + unsafe { + ioctl(fd, DRM_IOCTL_GET_CAP, &mut cap)?; + } + Ok(cap.value) +} + +#[repr(C)] +#[derive(Default)] +struct drm_mode_card_res { + fb_id_ptr: u64, + crtc_id_ptr: u64, + connector_id_ptr: u64, + encoder_id_ptr: u64, + count_fbs: u32, + count_crtcs: u32, + count_connectors: u32, + count_encoders: u32, + min_width: u32, + max_width: u32, + min_height: u32, + max_height: u32, +} + +const DRM_IOCTL_MODE_GETRESOURCES: u64 = drm_iowr::(0xa0); + +pub fn mode_get_resources(fd: c::c_int) -> Result { + let mut res = drm_mode_card_res::default(); + + let get = |res: &mut drm_mode_card_res| { + unsafe { + if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, res) { + return Err(DrmError::GetResources(e)); + } + } + Ok(()) + }; + + get(&mut res)?; + + let mut count_fbs = 0; + let mut count_crtcs = 0; + let mut count_connectors = 0; + let mut count_encoders = 0; + + let mut fbs = Vec::::new(); + let mut crtcs = Vec::::new(); + let mut connectors = Vec::::new(); + let mut encoders = Vec::::new(); + + while (count_fbs, count_crtcs, count_connectors, count_encoders) + != ( + res.count_fbs, + res.count_crtcs, + res.count_connectors, + res.count_encoders, + ) + { + count_fbs = res.count_fbs; + count_crtcs = res.count_crtcs; + count_connectors = res.count_connectors; + count_encoders = res.count_encoders; + + fbs.reserve(count_fbs as _); + crtcs.reserve(count_crtcs as _); + connectors.reserve(count_connectors as _); + encoders.reserve(count_encoders as _); + + res.fb_id_ptr = fbs.as_mut_ptr() as _; + res.crtc_id_ptr = crtcs.as_mut_ptr() as _; + res.connector_id_ptr = connectors.as_mut_ptr() as _; + res.encoder_id_ptr = encoders.as_mut_ptr() as _; + + get(&mut res)?; + } + + unsafe { + fbs.set_len(count_fbs as _); + crtcs.set_len(count_crtcs as _); + connectors.set_len(count_connectors as _); + encoders.set_len(count_encoders as _); + } + + Ok(DrmCardResources { + min_width: res.min_width, + max_width: res.max_width, + min_height: res.min_height, + max_height: res.max_height, + fbs, + crtcs, + connectors, + encoders, + }) +} + +#[repr(C)] +struct drm_mode_get_plane_res { + plane_id_ptr: u64, + count_planes: u32, +} + +const DRM_IOCTL_MODE_GETPLANERESOURCES: u64 = drm_iowr::(0xb5); + +pub fn mode_getplaneresources(fd: c::c_int) -> Result, DrmError> { + let mut res = drm_mode_get_plane_res { + plane_id_ptr: 0, + count_planes: 0, + }; + + let get = |res: &mut drm_mode_get_plane_res| { + unsafe { + if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETPLANERESOURCES, res) { + return Err(DrmError::GetPlaneResources(e)); + } + } + Ok(()) + }; + + get(&mut res)?; + + let mut count_planes = 0; + let mut planes = Vec::::new(); + + while count_planes != res.count_planes { + count_planes = res.count_planes; + planes.reserve(count_planes as _); + res.plane_id_ptr = planes.as_mut_ptr() as _; + get(&mut res)?; + } + + unsafe { + planes.set_len(count_planes as _); + } + + Ok(planes) +} + +#[repr(C)] +#[derive(Default)] +struct drm_mode_get_plane { + plane_id: u32, + + crtc_id: u32, + fb_id: u32, + + possible_crtcs: u32, + gamma_size: u32, + + count_format_types: u32, + format_type_ptr: u64, +} + +const DRM_IOCTL_MODE_GETPLANE: u64 = drm_iowr::(0xb6); + +pub fn mode_getplane(fd: c::c_int, plane_id: u32) -> Result { + let mut res = drm_mode_get_plane { + plane_id, + ..Default::default() + }; + + let get = |res: &mut drm_mode_get_plane| { + unsafe { + if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETPLANE, res) { + return Err(DrmError::GetPlane(e)); + } + } + Ok(()) + }; + + get(&mut res)?; + + let mut count_formats = 0; + let mut formats = Vec::::new(); + + while count_formats != res.count_format_types { + count_formats = res.count_format_types; + formats.reserve(count_formats as _); + res.format_type_ptr = formats.as_mut_ptr() as _; + get(&mut res)?; + } + + unsafe { + formats.set_len(count_formats as _); + } + + Ok(DrmPlaneInfo { + plane_id: DrmPlane(plane_id), + crtc_id: DrmCrtc(res.crtc_id), + fb_id: DrmFb(res.fb_id), + possible_crtcs: res.possible_crtcs, + gamma_size: res.gamma_size, + format_types: formats, + }) +} + +#[repr(C)] +#[derive(Default)] +struct drm_mode_get_encoder { + encoder_id: u32, + encoder_type: u32, + + crtc_id: u32, + + possible_crtcs: u32, + possible_clones: u32, +} + +const DRM_IOCTL_MODE_GETENCODER: u64 = drm_iowr::(0xa6); + +pub fn mode_getencoder(fd: c::c_int, encoder_id: u32) -> Result { + let mut res = drm_mode_get_encoder { + encoder_id, + ..Default::default() + }; + + unsafe { + if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETENCODER, &mut res) { + return Err(DrmError::GetEncoder(e)); + } + } + + Ok(DrmEncoderInfo { + encoder_id: DrmEncoder(encoder_id), + encoder_type: res.encoder_type, + crtc_id: DrmCrtc(res.crtc_id), + possible_crtcs: res.possible_crtcs, + possible_clones: res.possible_clones, + }) +} + +pub const DRM_DISPLAY_MODE_LEN: usize = 32; + +#[repr(C)] +pub struct drm_mode_modeinfo { + pub clock: u32, + pub hdisplay: u16, + pub hsync_start: u16, + pub hsync_end: u16, + pub htotal: u16, + pub hskew: u16, + pub vdisplay: u16, + pub vsync_start: u16, + pub vsync_end: u16, + pub vtotal: u16, + pub vscan: u16, + + pub vrefresh: u32, + + pub flags: u32, + pub ty: u32, + pub name: [u8; DRM_DISPLAY_MODE_LEN], +} + +impl Into for drm_mode_modeinfo { + fn into(self) -> DrmModeInfo { + DrmModeInfo { + clock: self.clock, + hdisplay: self.hdisplay, + hsync_start: self.hsync_start, + hsync_end: self.hsync_end, + htotal: self.htotal, + hskew: self.hskew, + vdisplay: self.vdisplay, + vsync_start: self.vsync_start, + vsync_end: self.vsync_end, + vtotal: self.vtotal, + vscan: self.vscan, + vrefresh: self.vrefresh, + flags: self.flags, + ty: self.ty, + name: self.name.split(|n| *n == 0).next().unwrap().to_vec().into(), + } + } +} + +pub const CONNECTOR_STATUS_CONNECTED: u32 = 1; +pub const CONNECTOR_STATUS_DISCONNECTED: u32 = 2; +pub const CONNECTOR_STATUS_UNKNOWN: u32 = 3; + +#[derive(Default)] +#[repr(C)] +struct drm_mode_get_connector { + encoders_ptr: u64, + modes_ptr: u64, + props_ptr: u64, + prop_values_ptr: u64, + + count_modes: u32, + count_props: u32, + count_encoders: u32, + + encoder_id: u32, + connector_id: u32, + connector_type: u32, + connector_type_id: u32, + + connection: u32, + mm_width: u32, + mm_height: u32, + subpixel: u32, + + pad: u32, +} + +const DRM_IOCTL_MODE_GETCONNECTOR: u64 = drm_iowr::(0xa7); + +pub fn mode_getconnector( + fd: c::c_int, + connector: u32, + force: bool, +) -> Result { + let mut count_modes = if force { 0 } else { 1 }; + let mut count_props = 0; + let mut count_encoders = 0; + + let mut modes = Vec::::with_capacity(1); + let mut props = Vec::::new(); + let mut prop_values = Vec::::new(); + let mut encoders = Vec::::new(); + + let mut res = drm_mode_get_connector { + connector_id: connector, + count_modes, + modes_ptr: modes.as_mut_ptr() as _, + ..Default::default() + }; + + let get = |res: &mut drm_mode_get_connector| { + unsafe { + if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, res) { + return Err(DrmError::GetConnector(e)); + } + } + Ok(()) + }; + + get(&mut res)?; + + while (count_modes, count_props, count_encoders) + != (res.count_modes, res.count_props, res.count_encoders) + { + count_modes = res.count_modes; + count_props = res.count_props; + count_encoders = res.count_encoders; + + modes.reserve(count_modes as _); + props.reserve(count_props as _); + prop_values.reserve(count_props as _); + encoders.reserve(count_encoders as _); + + res.modes_ptr = modes.as_mut_ptr() as _; + res.props_ptr = props.as_mut_ptr() as _; + res.prop_values_ptr = prop_values.as_mut_ptr() as _; + res.encoders_ptr = encoders.as_mut_ptr() as _; + + get(&mut res)?; + } + + unsafe { + modes.set_len(count_modes as _); + props.set_len(count_props as _); + prop_values.set_len(count_props as _); + encoders.set_len(count_encoders as _); + } + + Ok(DrmConnectorInfo { + encoders, + modes: modes.into_iter().map(|m| m.into()).collect(), + props: props + .into_iter() + .zip(prop_values.into_iter()) + .map(|(id, value)| DrmPropertyValue { + id: DrmProperty(id), + value, + }) + .collect(), + encoder_id: DrmEncoder(res.encoder_id), + connector_id: DrmConnector(res.connector_id), + connector_type: res.connector_type, + connector_type_id: res.connector_type_id, + connection: res.connection, + mm_width: res.mm_width, + mm_height: res.mm_height, + subpixel: res.subpixel, + }) +} + +#[repr(C)] +struct drm_mode_atomic { + flags: u32, + count_objs: u32, + objs_ptr: u64, + count_props_ptr: u64, + props_ptr: u64, + prop_values_ptr: u64, + reserved: u64, + user_data: u64, +} + +const DRM_IOCTL_MODE_ATOMIC: u64 = drm_iowr::(0xbc); + +pub const DRM_MODE_PAGE_FLIP_EVENT: u32 = 0x01; +pub const DRM_MODE_ATOMIC_TEST_ONLY: u32 = 0x0100; +pub const DRM_MODE_ATOMIC_NONBLOCK: u32 = 0x0200; +pub const DRM_MODE_ATOMIC_ALLOW_MODESET: u32 = 0x0400; + +pub fn mode_atomic( + fd: c::c_int, + flags: u32, + objs: &[u32], + count_props: &[u32], + props: &[u32], + prop_values: &[u64], + user_data: u64, +) -> Result<(), DrmError> { + assert_eq!(objs.len(), count_props.len()); + assert_eq!(props.len(), prop_values.len()); + assert_eq!( + count_props.iter().copied().sum::() as usize, + props.len() + ); + + if objs.is_empty() { + return Ok(()); + } + + let mut req = drm_mode_atomic { + flags, + count_objs: objs.len() as _, + objs_ptr: objs.as_ptr() as _, + count_props_ptr: count_props.as_ptr() as _, + props_ptr: props.as_ptr() as _, + prop_values_ptr: prop_values.as_ptr() as _, + reserved: 0, + user_data, + }; + + unsafe { + if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_ATOMIC, &mut req) { + return Err(DrmError::Atomic(e)); + } + } + Ok(()) +} + +#[repr(C)] +struct drm_mode_create_blob { + data: u64, + length: u32, + blob_id: u32, +} + +const DRM_IOCTL_MODE_CREATEPROPBLOB: u64 = drm_iowr::(0xbd); + +pub fn mode_create_blob(fd: c::c_int, t: &T) -> Result { + let mut res = drm_mode_create_blob { + data: t as *const T as _, + length: mem::size_of_val(t) as _, + blob_id: 0, + }; + + unsafe { + ioctl(fd, DRM_IOCTL_MODE_CREATEPROPBLOB, &mut res)?; + } + Ok(DrmBlob(res.blob_id)) +} + +#[repr(C)] +struct drm_mode_destroy_blob { + blob_id: u32, +} + +const DRM_IOCTL_MODE_DESTROYPROPBLOB: u64 = drm_iowr::(0xbe); + +pub fn mode_destroy_blob(fd: c::c_int, id: DrmBlob) -> Result<(), OsError> { + let mut res = drm_mode_destroy_blob { blob_id: id.0 }; + + unsafe { + ioctl(fd, DRM_IOCTL_MODE_DESTROYPROPBLOB, &mut res)?; + } + Ok(()) +} + +#[repr(C)] +#[derive(Debug)] +struct drm_mode_fb_cmd2 { + fb_id: u32, + width: u32, + height: u32, + pixel_format: u32, + flags: u32, + handles: [u32; 4], + pitches: [u32; 4], + offsets: [u32; 4], + modifier: [u64; 4], +} + +pub const DRM_MODE_FB_INTERLACED: u32 = 1 << 0; +pub const DRM_MODE_FB_MODIFIERS: u32 = 1 << 1; + +const DRM_IOCTL_MODE_ADDFB2: u64 = drm_iowr::(0xb8); + +pub fn mode_addfb2( + fd: c::c_int, + width: u32, + height: u32, + pixel_format: u32, + flags: u32, + handles: [u32; 4], + strides: [u32; 4], + offsets: [u32; 4], + modifier: u64, +) -> Result { + let mut res = drm_mode_fb_cmd2 { + fb_id: 0, + width, + height, + pixel_format, + flags, + handles, + pitches: strides, + offsets, + modifier: [modifier; 4], + }; + + unsafe { + ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &mut res)?; + } + + Ok(DrmFb(res.fb_id)) +} + +const DRM_IOCTL_MODE_RMFB: u64 = drm_iowr::(0xaf); + +pub fn mode_rmfb(fd: c::c_int, id: DrmFb) -> Result<(), OsError> { + let mut res = id.0 as c::c_uint; + unsafe { + ioctl(fd, DRM_IOCTL_MODE_RMFB, &mut res)?; + } + Ok(()) +} + +#[repr(C)] +struct drm_prime_handle { + handle: u32, + flags: u32, + fd: i32, +} + +const DRM_IOCTL_PRIME_FD_TO_HANDLE: u64 = drm_iowr::(0x2e); + +pub fn prime_fd_to_handle(fd: c::c_int, prime: c::c_int) -> Result { + let mut res = drm_prime_handle { + handle: 0, + flags: 0, + fd: prime, + }; + unsafe { + ioctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &mut res)?; + } + Ok(res.handle) +} + +#[repr(C)] +struct drm_gem_close { + handle: u32, + pad: u32, +} + +const DRM_IOCTL_GEM_CLOSE: u64 = drm_iow::(0x09); + +pub fn gem_close(fd: c::c_int, handle: u32) -> Result<(), OsError> { + let mut res = drm_gem_close { + handle, + pad: 0, + }; + unsafe { + ioctl(fd, DRM_IOCTL_GEM_CLOSE, &mut res)?; + } + Ok(()) +} diff --git a/src/drm/gbm.rs b/src/drm/gbm.rs index eb85b84c..8fedaf3b 100644 --- a/src/drm/gbm.rs +++ b/src/drm/gbm.rs @@ -2,10 +2,12 @@ use crate::drm::dma::{DmaBuf, DmaBufPlane}; use crate::drm::drm::{Drm, DrmError}; use crate::drm::{ModifiedFormat, INVALID_MODIFIER}; use crate::format::formats; +use crate::utils::oserror::OsError; +use std::fmt::{Debug, Formatter}; use std::ptr; use std::rc::Rc; use thiserror::Error; -use uapi::{c, OwnedFd}; +use uapi::{c, Errno, OwnedFd}; #[derive(Debug, Error)] pub enum GbmError { @@ -19,12 +21,13 @@ pub enum GbmError { UnknownFormat, #[error("Could not retrieve a drm-buf fd")] DrmFd, + #[error("Could not retrieve a GEM handle")] + GemHandle(#[source] OsError), } type Device = u8; type Bo = u8; -#[allow(dead_code)] pub const GBM_BO_USE_SCANOUT: u32 = 1 << 0; #[allow(dead_code)] pub const GBM_BO_USE_CURSOR: u32 = 1 << 1; @@ -36,6 +39,16 @@ pub const GBM_BO_USE_LINEAR: u32 = 1 << 4; #[allow(dead_code)] pub const GBM_BO_USE_PROTECTED: u32 = 1 << 5; +#[allow(non_camel_case_types)] +#[repr(C)] +union gbm_bo_handle { + ptr: *mut u8, + s32: i32, + u32: u32, + s64: i64, + u64: u64, +} + #[link(name = "gbm")] extern "C" { fn gbm_create_device(fd: c::c_int) -> *mut Device; @@ -59,6 +72,7 @@ extern "C" { fn gbm_bo_get_modifier(bo: *mut Bo) -> u64; fn gbm_bo_get_stride_for_plane(bo: *mut Bo, plane: c::c_int) -> u32; fn gbm_bo_get_fd_for_plane(bo: *mut Bo, plane: c::c_int) -> c::c_int; + fn gbm_bo_get_handle_for_plane(bo: *mut Bo, plane: c::c_int) -> gbm_bo_handle; fn gbm_bo_get_offset(bo: *mut Bo, plane: c::c_int) -> u32; fn gbm_bo_get_format(bo: *mut Bo) -> u32; #[allow(dead_code)] @@ -70,6 +84,12 @@ pub struct GbmDevice { dev: *mut Device, } +impl Debug for GbmDevice { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GbmDevice").finish_non_exhaustive() + } +} + struct BoHolder { bo: *mut Bo, } @@ -77,6 +97,7 @@ struct BoHolder { pub struct GbmBo { _bo: BoHolder, dma: DmaBuf, + handles: Vec, } unsafe fn export_bo(bo: *mut Bo) -> Result { @@ -111,6 +132,18 @@ unsafe fn export_bo(bo: *mut Bo) -> Result { }) } +unsafe fn export_handles(bo: *mut Bo) -> Result, GbmError> { + let mut planes = vec![]; + for plane in 0..gbm_bo_get_plane_count(bo) { + let handle = gbm_bo_get_handle_for_plane(bo, plane); + if handle.s32 < 0 { + return Err(GbmError::GemHandle(Errno::default().into())); + } + planes.push(handle.u32); + } + Ok(planes) +} + impl GbmDevice { pub fn new(drm: &Drm) -> Result { let drm = drm.dup_unprivileged()?; @@ -149,7 +182,12 @@ impl GbmDevice { } let bo = BoHolder { bo }; let dma = export_bo(bo.bo)?; - Ok(GbmBo { _bo: bo, dma }) + let handles = export_handles(bo.bo)?; + Ok(GbmBo { + _bo: bo, + dma, + handles, + }) } } } @@ -166,6 +204,10 @@ impl GbmBo { pub fn dma(&self) -> &DmaBuf { &self.dma } + + pub fn gem(&self) -> &[u32] { + &self.handles + } } impl Drop for BoHolder { diff --git a/src/format.rs b/src/format.rs index 87ef0c17..ab64fa6f 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,7 +1,9 @@ use crate::pixman; use crate::render::sys::{GLint, GL_BGRA_EXT, GL_UNSIGNED_BYTE}; +use crate::utils::debug_fn::debug_fn; use ahash::AHashMap; use once_cell::sync::Lazy; +use std::fmt::{Debug, Write}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Format { @@ -32,6 +34,16 @@ const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 { (a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24) } +pub fn debug(fourcc: u32) -> impl Debug { + debug_fn(move |fmt| { + fmt.write_char(fourcc as u8 as char)?; + fmt.write_char((fourcc >> 8) as u8 as char)?; + fmt.write_char((fourcc >> 16) as u8 as char)?; + fmt.write_char((fourcc >> 24) as u8 as char)?; + Ok(()) + }) +} + const ARGB8888_ID: u32 = 0; const ARGB8888_DRM: u32 = fourcc_code('A', 'R', '2', '4'); diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 33f10ae7..56e13b5e 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -1,4 +1,4 @@ -use crate::backend::{KeyState, InputEvent, OutputId, ScrollAxis}; +use crate::backend::{InputEvent, KeyState, OutputId, ScrollAxis}; use crate::client::{Client, ClientId}; use crate::fixed::Fixed; use crate::ifs::ipc; @@ -136,7 +136,6 @@ impl WlSeatGlobal { } fn motion_event(self: &Rc, dx: Fixed, dy: Fixed) { - log::info!("motion: {}x{}", dx, dy); let (x, y) = self.pos.get(); self.set_new_position(x + dx, y + dy); } diff --git a/src/libinput/device.rs b/src/libinput/device.rs index 44475706..9113251f 100644 --- a/src/libinput/device.rs +++ b/src/libinput/device.rs @@ -1,4 +1,7 @@ -use crate::libinput::sys::{libinput_device, libinput_device_get_user_data, libinput_device_set_user_data, libinput_device_unref, libinput_path_remove_device}; +use crate::libinput::sys::{ + libinput_device, libinput_device_get_user_data, libinput_device_set_user_data, + libinput_device_unref, libinput_path_remove_device, +}; use crate::libinput::LibInput; use std::marker::PhantomData; use std::rc::Rc; diff --git a/src/libinput/event.rs b/src/libinput/event.rs index 39e870ef..2fbf3a2a 100644 --- a/src/libinput/event.rs +++ b/src/libinput/event.rs @@ -1,6 +1,13 @@ use crate::libinput::consts::{EventType, KeyState}; use crate::libinput::device::LibInputDevice; -use crate::libinput::sys::{libinput_event, libinput_event_destroy, libinput_event_get_device, libinput_event_get_keyboard_event, libinput_event_get_pointer_event, libinput_event_get_type, libinput_event_keyboard, libinput_event_keyboard_get_key, libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec, libinput_event_pointer, libinput_event_pointer_get_dx, libinput_event_pointer_get_dy, libinput_event_pointer_get_time_usec}; +use crate::libinput::sys::{ + libinput_event, libinput_event_destroy, libinput_event_get_device, + libinput_event_get_keyboard_event, libinput_event_get_pointer_event, libinput_event_get_type, + libinput_event_keyboard, libinput_event_keyboard_get_key, + libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec, + libinput_event_pointer, libinput_event_pointer_get_dx, libinput_event_pointer_get_dy, + libinput_event_pointer_get_time_usec, +}; use std::marker::PhantomData; pub struct LibInputEvent<'a> { diff --git a/src/logind.rs b/src/logind.rs index 3c79eff7..d8002852 100644 --- a/src/logind.rs +++ b/src/logind.rs @@ -1,5 +1,5 @@ -use crate::dbus::{DbusError, DbusSocket}; -use crate::org::freedesktop::login1::session::TakeDeviceReply; +use crate::dbus::{DbusError, DbusSocket, SignalHandler}; +use crate::org::freedesktop::login1::session::{PauseDevice, ResumeDevice, TakeDeviceReply}; use crate::{org, FALSE}; use std::rc::Rc; use thiserror::Error; @@ -91,4 +91,36 @@ impl Session { move |r| f(r), ); } + + pub fn on_pause(&self, f: F) -> Result + where + F: for<'b> Fn(PauseDevice<'b>) + 'static, + { + self.socket + .handle_signal::( + Some(LOGIND_NAME), + Some(&self.session_path), + move |v| f(v), + ) + } + + pub fn on_resume(&self, f: F) -> Result + where + F: Fn(ResumeDevice) + 'static, + { + self.socket + .handle_signal::( + Some(LOGIND_NAME), + Some(&self.session_path), + move |v| f(v), + ) + } + + pub fn device_paused(&self, major: u32, minor: u32) { + self.socket.call_noreply( + LOGIND_NAME, + &self.session_path, + org::freedesktop::login1::session::PauseDeviceComplete { major, minor }, + ); + } } diff --git a/src/render/egl/mod.rs b/src/render/egl/mod.rs index 903314ec..45e47314 100644 --- a/src/render/egl/mod.rs +++ b/src/render/egl/mod.rs @@ -1,4 +1,4 @@ -use crate::drm::drm::DrmDevice; +use crate::drm::drm::NodeType; use crate::render::egl::device::EglDevice; use crate::render::egl::sys::{ eglBindAPI, EGLAttrib, EGLLabelKHR, EGLenum, EGLint, EGL_DEBUG_MSG_CRITICAL_KHR, @@ -8,10 +8,11 @@ use crate::render::egl::sys::{ use crate::render::ext::{get_client_ext, get_device_ext, ClientExt, DeviceExt}; use crate::render::proc::ExtProc; use crate::render::RenderError; +use ahash::AHashMap; use bstr::ByteSlice; use log::Level; use once_cell::sync::Lazy; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::ptr; use sys::{ EGL_BAD_ACCESS, EGL_BAD_ALLOC, EGL_BAD_ATTRIBUTE, EGL_BAD_CONFIG, EGL_BAD_CONTEXT, @@ -63,12 +64,14 @@ pub fn init() -> Result<(), RenderError> { Ok(()) } -pub fn find_drm_device(drm_dev: &DrmDevice) -> Result, RenderError> { +pub fn find_drm_device( + drm_dev: &AHashMap, +) -> Result, RenderError> { for device in query_devices()? { if device.exts.contains(DeviceExt::EXT_DEVICE_DRM) { let device_file = device.query_string(EGL_DRM_DEVICE_FILE_EXT)?; - for (_, name) in drm_dev.nodes() { - if device_file == name { + for name in drm_dev.values() { + if device_file == &**name { return Ok(Some(device)); } } diff --git a/src/render/renderer/context.rs b/src/render/renderer/context.rs index 4d5fdadd..dcc1358b 100644 --- a/src/render/renderer/context.rs +++ b/src/render/renderer/context.rs @@ -1,5 +1,5 @@ use crate::drm::dma::DmaBuf; -use crate::drm::drm::{Drm, DRM_NODE_RENDER}; +use crate::drm::drm::{Drm, NodeType}; use crate::format::{Format, XRGB8888}; use crate::render::egl::context::EglContext; use crate::render::egl::find_drm_device; @@ -15,6 +15,7 @@ use ahash::AHashMap; use renderdoc::{RenderDoc, V100}; use std::cell::{Cell, RefCell}; use std::ffi::CString; +use std::fmt::{Debug, Formatter}; use std::rc::Rc; use uapi::ustr; @@ -51,14 +52,20 @@ pub struct RenderContext { pub(super) fill_prog_color: GLint, } +impl Debug for RenderContext { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RenderContext").finish_non_exhaustive() + } +} + impl RenderContext { pub fn from_drm_device(drm: &Drm) -> Result { - let drm_dev = drm.get_device()?; - let node = match drm_dev.nodes().find(|(ty, _)| *ty == DRM_NODE_RENDER) { + let nodes = drm.get_nodes()?; + let node = match nodes.get(&NodeType::Render) { None => return Err(RenderError::NoRenderNode), - Some((_, n)) => Rc::new(n.to_owned()), + Some(path) => Rc::new(path.to_owned()), }; - let egl_dev = match find_drm_device(&drm_dev)? { + let egl_dev = match find_drm_device(&nodes)? { Some(d) => d, None => return Err(RenderError::UnknownDrmDevice), }; diff --git a/src/render/renderer/framebuffer.rs b/src/render/renderer/framebuffer.rs index 70a78c63..dd338a36 100644 --- a/src/render/renderer/framebuffer.rs +++ b/src/render/renderer/framebuffer.rs @@ -8,6 +8,7 @@ use crate::render::renderer::renderer::Renderer; use crate::render::sys::{glBlendFunc, GL_ONE, GL_ONE_MINUS_SRC_ALPHA}; use crate::tree::Node; use crate::State; +use std::fmt::{Debug, Formatter}; use std::ptr; use std::rc::Rc; @@ -16,6 +17,12 @@ pub struct Framebuffer { pub(super) gl: GlFrameBuffer, } +impl Debug for Framebuffer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Framebuffer").finish_non_exhaustive() + } +} + impl Framebuffer { pub fn render(&self, node: &dyn Node, state: &State, cursor_rect: Option) { let _ = self.ctx.ctx.with_current(|| { diff --git a/src/state.rs b/src/state.rs index 473cb4ea..f831b789 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,5 +1,7 @@ use crate::async_engine::{AsyncEngine, SpawnedFuture}; -use crate::backend::{BackendEvent, InputDevice, InputDeviceId, InputDeviceIds, OutputId, OutputIds}; +use crate::backend::{ + BackendEvent, InputDevice, InputDeviceId, InputDeviceIds, OutputId, OutputIds, +}; use crate::client::{Client, Clients}; use crate::config::ConfigProxy; use crate::cursor::ServerCursors; diff --git a/src/tasks/input_device.rs b/src/tasks/input_device.rs index 8abac35d..95f7f1b0 100644 --- a/src/tasks/input_device.rs +++ b/src/tasks/input_device.rs @@ -1,4 +1,4 @@ -use crate::backend::{InputDevice}; +use crate::backend::InputDevice; use crate::state::{DeviceHandlerData, InputDeviceData}; use crate::utils::asyncevent::AsyncEvent; use crate::State; @@ -64,6 +64,9 @@ impl DeviceHandler { if let Some(config) = self.state.config.get() { config.del_input_device(self.dev.id()); } - self.state.input_device_handlers.borrow_mut().remove(&self.dev.id()); + self.state + .input_device_handlers + .borrow_mut() + .remove(&self.dev.id()); } } diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs index 9b787a0e..4911c536 100644 --- a/src/tasks/mod.rs +++ b/src/tasks/mod.rs @@ -7,8 +7,8 @@ mod start_backend; use crate::tasks::backend::BackendEventHandler; use crate::tasks::slow_clients::SlowClientHandler; use crate::State; -use std::rc::Rc; pub use start_backend::start_backend; +use std::rc::Rc; pub async fn handle_backend_events(state: Rc) { let mut beh = BackendEventHandler { state }; diff --git a/src/tasks/start_backend.rs b/src/tasks/start_backend.rs index c60d9a5c..f4e1162e 100644 --- a/src/tasks/start_backend.rs +++ b/src/tasks/start_backend.rs @@ -1,6 +1,6 @@ +use crate::{metal, ErrorFmt, State, XorgBackend}; use std::future::pending; use std::rc::Rc; -use crate::{ErrorFmt, metal, State, XorgBackend}; pub async fn start_backend(state: Rc) { log::info!("Trying to start X backend"); diff --git a/src/udev.rs b/src/udev.rs index 4073be66..b0663801 100644 --- a/src/udev.rs +++ b/src/udev.rs @@ -47,7 +47,10 @@ extern "C" { 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_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; } #[derive(Debug, Error)] @@ -70,10 +73,6 @@ pub enum UdevError { ScanDevices(#[source] crate::utils::oserror::OsError), #[error("Could not create a udev_device from a syspath")] DeviceFromSyspath(#[source] crate::utils::oserror::OsError), - #[error("Could not retrieve the sysname of a udev_device")] - GetSysname(#[source] crate::utils::oserror::OsError), - #[error("Could not retrieve the devnode of a udev_device")] - GetDevnode(#[source] crate::utils::oserror::OsError), } pub struct Udev { @@ -215,7 +214,7 @@ impl Drop for UdevMonitor { } impl UdevEnumerate { - pub fn add_match_subsystem(&self, subsystem: &str) -> Result<(), UdevError> { + pub fn add_match_subsystem(&self, subsystem: &[u8]) -> Result<(), UdevError> { let subsystem = subsystem.into_ustr(); let res = unsafe { udev_enumerate_add_match_subsystem(self.enumerate, subsystem.as_ptr()) }; if res < 0 { @@ -283,24 +282,25 @@ impl<'a> UdevListEntry<'a> { } } -impl UdevDevice { - pub fn sysname(&self) -> Result<&CStr, UdevError> { - let res = unsafe { udev_device_get_sysname(self.device) }; - if res.is_null() { - Err(UdevError::GetSysname(Errno::default().into())) - } else { - unsafe { Ok(CStr::from_ptr(res)) } +macro_rules! strfn { + ($f:ident, $raw:ident) => { + pub fn $f(&self) -> Option<&CStr> { + let res = unsafe { $raw(self.device) }; + if res.is_null() { + None + } else { + unsafe { Some(CStr::from_ptr(res)) } + } } - } + }; +} - pub fn devnode(&self) -> Result<&CStr, UdevError> { - let res = unsafe { udev_device_get_devnode(self.device) }; - if res.is_null() { - Err(UdevError::GetDevnode(Errno::default().into())) - } else { - unsafe { Ok(CStr::from_ptr(res)) } - } - } +impl UdevDevice { + strfn!(sysname, udev_device_get_sysname); + strfn!(devnode, udev_device_get_devnode); + strfn!(devtype, udev_device_get_devtype); + strfn!(action, udev_device_get_action); + strfn!(subsystem, udev_device_get_subsystem); pub fn devnum(&self) -> c::dev_t { unsafe { udev_device_get_devnum(self.device) } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 3a317ec8..63fd2a3f 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -16,8 +16,8 @@ pub mod queue; pub mod run_toplevel; pub mod smallmap; pub mod stack; +pub mod syncqueue; pub mod tri; pub mod vasprintf; pub mod vec_ext; pub mod vecstorage; -pub mod syncqueue; diff --git a/src/utils/oserror.rs b/src/utils/oserror.rs index d1205523..8d8a6fce 100644 --- a/src/utils/oserror.rs +++ b/src/utils/oserror.rs @@ -1,6 +1,7 @@ use once_cell::sync::Lazy; use std::error::Error; use std::fmt::{Display, Formatter}; +use uapi::c::c_int; use uapi::{c, Errno}; static ERRORS: Lazy<&'static [Option<&'static str>]> = Lazy::new(|| { @@ -168,6 +169,21 @@ impl From for OsError { } } +impl From for OsError { + fn from(v: c_int) -> Self { + Self(v) + } +} + +impl From for OsError { + fn from(v: std::io::Error) -> Self { + match v.raw_os_error() { + Some(v) => Self(v), + None => Self(c::EINVAL), + } + } +} + impl Error for OsError {} impl Display for OsError { diff --git a/src/utils/syncqueue.rs b/src/utils/syncqueue.rs index d3467b04..bfde9b2d 100644 --- a/src/utils/syncqueue.rs +++ b/src/utils/syncqueue.rs @@ -1,6 +1,6 @@ +use crate::utils::ptr_ext::MutPtrExt; use std::cell::UnsafeCell; use std::collections::VecDeque; -use crate::utils::ptr_ext::MutPtrExt; pub struct SyncQueue { el: UnsafeCell>, @@ -22,8 +22,6 @@ impl SyncQueue { } pub fn pop(&self) -> Option { - unsafe { - self.el.get().deref_mut().pop_front() - } + unsafe { self.el.get().deref_mut().pop_front() } } }