use { crate::{ async_engine::SpawnedFuture, backend::transaction::{ BackendConnectorTransaction, BackendConnectorTransactionError, BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn, }, cmm::cmm_primaries::Primaries, drm_feedback::DrmFeedback, fixed::Fixed, format::Format, gfx_api::{FdSync, GfxApi, GfxFramebuffer}, ifs::{ wl_output::OutputId, wl_seat::{ tablet::{ PadButtonState, TabletInit, TabletPadId, TabletPadInit, TabletRingEventSource, TabletStripEventSource, TabletToolChanges, TabletToolId, TabletToolInit, ToolButtonState, }, wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL}, }, }, libinput::consts::DeviceCapability, utils::static_text::StaticText, video::drm::{ ConnectorType, DRM_MODE_COLORIMETRY_BT2020_RGB, DRM_MODE_COLORIMETRY_DEFAULT, DrmConnector, DrmError, DrmVersion, HDMI_EOTF_SMPTE_ST2084, HDMI_EOTF_TRADITIONAL_GAMMA_SDR, }, }, jay_config::input::SwitchEvent, linearize::Linearize, std::{ any::Any, error::Error, fmt::{Debug, Display, Formatter}, hash::Hash, rc::Rc, }, uapi::{OwnedFd, Packed, Pod, c}, }; pub mod transaction; linear_ids!(ConnectorIds, ConnectorId); linear_ids!(InputDeviceIds, InputDeviceId); linear_ids!(DrmDeviceIds, DrmDeviceId); pub trait Backend: Any { fn run(self: Rc) -> SpawnedFuture>>; fn clear(&self) { // nothing } fn switch_to(&self, vtnr: u32) { let _ = vtnr; } fn import_environment(&self) -> bool { false } fn supports_presentation_feedback(&self) -> bool { false } fn get_input_fds(&self) -> Vec> { vec![] } } #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)] pub struct Mode { pub width: i32, pub height: i32, pub refresh_rate_millihz: u32, } impl Mode { pub fn refresh_nsec(&self) -> u64 { match self.refresh_rate_millihz { 0 => u64::MAX, n => 1_000_000_000_000 / (n as u64), } } pub fn size(&self) -> (i32, i32) { (self.width, self.height) } } impl Display for Mode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, "{}x{}@{}", self.width, self.height, self.refresh_rate_millihz as f64 / 1000.0, ) } } #[derive(Clone, Debug)] pub struct MonitorInfo { pub modes: Option>, pub output_id: Rc, pub width_mm: i32, pub height_mm: i32, pub non_desktop: bool, pub non_desktop_effective: bool, pub vrr_capable: bool, pub eotfs: Vec, pub color_spaces: Vec, pub primaries: Primaries, pub luminance: Option, pub state: BackendConnectorState, } #[derive(Copy, Clone, Debug)] pub struct ConnectorKernelId { pub ty: ConnectorType, pub idx: u32, } impl Display for ConnectorKernelId { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}-{}", self.ty, self.idx) } } bitflags! { ConnectorCaps: u32; CONCAP_CONNECTOR, CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY, } pub trait Connector: Any { fn id(&self) -> ConnectorId; fn kernel_id(&self) -> ConnectorKernelId; fn event(&self) -> Option; fn on_change(&self, cb: Rc); fn damage(&self); fn drm_dev(&self) -> Option; fn effectively_locked(&self) -> bool; fn state(&self) -> BackendConnectorState; fn caps(&self) -> ConnectorCaps { ConnectorCaps::none() } fn drm_feedback(&self) -> Option> { None } fn drm_object_id(&self) -> Option { None } fn before_non_desktop_override_update(&self, overrd: Option) { let _ = overrd; } fn transaction_type(&self) -> Box { #[derive(Hash, Eq, PartialEq)] struct UnimplementedConnectorTransactionType; impl BackendConnectorTransactionType for UnimplementedConnectorTransactionType {} Box::new(UnimplementedConnectorTransactionType) } fn create_transaction( &self, ) -> Result, BackendConnectorTransactionError> { Err(BackendConnectorTransactionError::TransactionsNotSupported( self.kernel_id(), )) } fn gamma_lut_size(&self) -> Option { None } fn name(&self) -> String { self.kernel_id().to_string() } } #[derive(Debug)] pub enum ConnectorEvent { Connected(MonitorInfo), HardwareCursor(Option>), Disconnected, Removed, Unavailable, Available, State(BackendConnectorState), FormatsChanged(Rc>), } pub trait HardwareCursorUpdate { fn set_enabled(&mut self, enabled: bool); fn get_buffer(&self) -> Rc; fn set_position(&mut self, x: i32, y: i32); fn swap_buffer(&mut self, sync: Option); fn size(&self) -> (i32, i32); } pub trait HardwareCursor: Debug { fn damage(&self); } pub type TransformMatrix = [[f64; 2]; 2]; linear_ids!(InputDeviceGroupIds, InputDeviceGroupId, usize); pub trait InputDevice { fn id(&self) -> InputDeviceId; fn removed(&self) -> bool; fn event(&self) -> Option; fn on_change(&self, cb: Rc); fn grab(&self, grab: bool); fn has_capability(&self, cap: InputDeviceCapability) -> bool; fn left_handed(&self) -> Option { None } fn set_left_handed(&self, left_handed: bool); fn accel_profile(&self) -> Option { None } fn set_accel_profile(&self, profile: InputDeviceAccelProfile); fn accel_speed(&self) -> Option { None } fn set_accel_speed(&self, speed: f64); fn transform_matrix(&self) -> Option { None } fn set_transform_matrix(&self, matrix: TransformMatrix); fn calibration_matrix(&self) -> Option<[[f32; 3]; 2]> { None } fn set_calibration_matrix(&self, m: [[f32; 3]; 2]) { let _ = m; } fn name(&self) -> Rc; fn dev_t(&self) -> Option { None } fn tap_enabled(&self) -> Option { None } fn set_tap_enabled(&self, enabled: bool); fn drag_enabled(&self) -> Option { None } fn set_drag_enabled(&self, enabled: bool); fn drag_lock_enabled(&self) -> Option { None } fn set_drag_lock_enabled(&self, enabled: bool); fn natural_scrolling_enabled(&self) -> Option { None } fn set_natural_scrolling_enabled(&self, enabled: bool); fn click_method(&self) -> Option { None } fn set_click_method(&self, method: InputDeviceClickMethod); fn middle_button_emulation_enabled(&self) -> Option { None } fn set_middle_button_emulation_enabled(&self, enabled: bool); fn tablet_info(&self) -> Option> { None } fn tablet_pad_info(&self) -> Option> { None } fn set_enabled_leds(&self, leds: Leds) { let _ = leds; } } #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Linearize)] pub enum InputDeviceCapability { Keyboard, Pointer, Touch, TabletTool, TabletPad, Gesture, Switch, } impl StaticText for InputDeviceCapability { fn text(&self) -> &'static str { match self { InputDeviceCapability::Keyboard => "keyboard", InputDeviceCapability::Pointer => "pointer", InputDeviceCapability::Touch => "touch", InputDeviceCapability::TabletTool => "tablet tool", InputDeviceCapability::TabletPad => "tablet pad", InputDeviceCapability::Gesture => "gesture", InputDeviceCapability::Switch => "switch", } } } impl InputDeviceCapability { pub fn to_libinput(self) -> DeviceCapability { use crate::libinput::consts::*; match self { InputDeviceCapability::Keyboard => LIBINPUT_DEVICE_CAP_KEYBOARD, InputDeviceCapability::Pointer => LIBINPUT_DEVICE_CAP_POINTER, InputDeviceCapability::Touch => LIBINPUT_DEVICE_CAP_TOUCH, InputDeviceCapability::TabletTool => LIBINPUT_DEVICE_CAP_TABLET_TOOL, InputDeviceCapability::TabletPad => LIBINPUT_DEVICE_CAP_TABLET_PAD, InputDeviceCapability::Gesture => LIBINPUT_DEVICE_CAP_GESTURE, InputDeviceCapability::Switch => LIBINPUT_DEVICE_CAP_SWITCH, } } } #[derive(Debug, Copy, Clone, PartialEq, Linearize)] pub enum InputDeviceAccelProfile { Flat, Adaptive, } impl StaticText for InputDeviceAccelProfile { fn text(&self) -> &'static str { match self { InputDeviceAccelProfile::Flat => "Flat", InputDeviceAccelProfile::Adaptive => "Adaptive", } } } #[derive(Debug, Copy, Clone, PartialEq, Linearize)] pub enum InputDeviceClickMethod { None, ButtonAreas, Clickfinger, } impl StaticText for InputDeviceClickMethod { fn text(&self) -> &'static str { match self { InputDeviceClickMethod::None => "none", InputDeviceClickMethod::ButtonAreas => "button-areas", InputDeviceClickMethod::Clickfinger => "clickfinger", } } } pub enum BackendEvent { NewDrmDevice(Rc), NewConnector(Rc), NewInputDevice(Rc), DevicesEnumerated, } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum KeyState { Released, Pressed, Repeated, } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum ButtonState { Released, Pressed, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Linearize)] pub enum ScrollAxis { Horizontal = HORIZONTAL_SCROLL as _, Vertical = VERTICAL_SCROLL as _, } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum AxisSource { Wheel = WHEEL as _, Finger = FINGER as _, Continuous = CONTINUOUS as _, } pub const AXIS_120: i32 = 120; bitflags! { Leds: u32; LED_NUM_LOCK, LED_CAPS_LOCK, LED_SCROLL_LOCK, LED_COMPOSE, LED_KANA, } #[derive(Debug)] pub enum InputEvent { Key { time_usec: u64, key: u32, state: KeyState, }, ConnectorPosition { time_usec: u64, connector: ConnectorId, x: Fixed, y: Fixed, }, Motion { time_usec: u64, dx: Fixed, dy: Fixed, dx_unaccelerated: Fixed, dy_unaccelerated: Fixed, }, MotionAbsolute { time_usec: u64, x_normed: f32, y_normed: f32, }, Button { time_usec: u64, button: u32, state: ButtonState, }, AxisPx { dist: Fixed, axis: ScrollAxis, inverted: bool, }, AxisSource { source: AxisSource, }, AxisStop { axis: ScrollAxis, }, Axis120 { dist: i32, axis: ScrollAxis, inverted: bool, }, AxisFrame { time_usec: u64, }, SwipeBegin { time_usec: u64, finger_count: u32, }, SwipeUpdate { time_usec: u64, dx: Fixed, dy: Fixed, dx_unaccelerated: Fixed, dy_unaccelerated: Fixed, }, SwipeEnd { time_usec: u64, cancelled: bool, }, PinchBegin { time_usec: u64, finger_count: u32, }, PinchUpdate { time_usec: u64, dx: Fixed, dy: Fixed, dx_unaccelerated: Fixed, dy_unaccelerated: Fixed, scale: Fixed, rotation: Fixed, }, PinchEnd { time_usec: u64, cancelled: bool, }, HoldBegin { time_usec: u64, finger_count: u32, }, HoldEnd { time_usec: u64, cancelled: bool, }, SwitchEvent { time_usec: u64, event: SwitchEvent, }, TabletToolAdded { time_usec: u64, init: Box, }, TabletToolChanged { time_usec: u64, id: TabletToolId, changes: Box, }, TabletToolButton { time_usec: u64, id: TabletToolId, button: u32, state: ToolButtonState, }, TabletToolRemoved { time_usec: u64, id: TabletToolId, }, TabletPadButton { time_usec: u64, id: TabletPadId, button: u32, state: PadButtonState, }, TabletPadModeSwitch { time_usec: u64, pad: TabletPadId, group: u32, mode: u32, }, TabletPadRing { time_usec: u64, pad: TabletPadId, ring: u32, source: Option, angle: Option, }, TabletPadStrip { time_usec: u64, pad: TabletPadId, strip: u32, source: Option, position: Option, }, TabletPadDial { time_usec: u64, pad: TabletPadId, dial: u32, value120: i32, }, TouchDown { time_usec: u64, id: i32, x_normed: Fixed, y_normed: Fixed, }, TouchUp { time_usec: u64, id: i32, }, TouchMotion { time_usec: u64, id: i32, x_normed: Fixed, y_normed: Fixed, }, TouchCancel { time_usec: u64, id: i32, }, TouchFrame { time_usec: u64, }, } pub enum DrmEvent { #[expect(dead_code)] Removed, GfxApiChanged, } pub trait BackendDrmDevice { fn id(&self) -> DrmDeviceId; fn event(&self) -> Option; fn on_change(&self, cb: Rc); fn dev_t(&self) -> c::dev_t; fn make_render_device(&self); fn set_gfx_api(&self, api: GfxApi); fn gtx_api(&self) -> GfxApi; fn version(&self) -> Result; fn set_direct_scanout_enabled(&self, enabled: bool); fn is_render_device(&self) -> bool; fn direct_scanout_enabled(&self) -> bool { false } fn create_lease( self: Rc, lessee: Rc, connector_ids: &[ConnectorId], ) { let _ = lessee; let _ = connector_ids; } fn set_flip_margin(&self, margin: u64) { let _ = margin; } fn flip_margin(&self) -> Option { None } } pub trait BackendDrmLease { fn fd(&self) -> &Rc; } pub trait BackendDrmLessee { fn created(&self, lease: Rc); } #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Linearize)] pub enum BackendEotfs { #[default] Default, Pq, } #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Linearize)] pub enum BackendColorSpace { #[default] Default, Bt2020, } #[derive(Copy, Clone, Debug)] pub struct BackendLuminance { pub min: f64, pub max: f64, pub max_fall: f64, } impl BackendEotfs { pub fn to_drm(self) -> u8 { match self { BackendEotfs::Default => HDMI_EOTF_TRADITIONAL_GAMMA_SDR, BackendEotfs::Pq => HDMI_EOTF_SMPTE_ST2084, } } pub const fn name(self) -> &'static str { match self { BackendEotfs::Default => "default", BackendEotfs::Pq => "pq", } } } impl BackendColorSpace { pub fn to_drm(self) -> u64 { match self { BackendColorSpace::Default => DRM_MODE_COLORIMETRY_DEFAULT, BackendColorSpace::Bt2020 => DRM_MODE_COLORIMETRY_BT2020_RGB, } } pub const fn name(self) -> &'static str { match self { BackendColorSpace::Default => "default", BackendColorSpace::Bt2020 => "bt2020", } } } // kernel: struct drm_color_lut #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)] #[repr(C)] pub struct BackendGammaLutElement { pub red: u16, pub green: u16, pub blue: u16, pub reserved: u16, } unsafe impl Pod for BackendGammaLutElement {} unsafe impl Packed for BackendGammaLutElement {} #[derive(Debug, Eq)] pub struct BackendGammaLut { id: [u8; 32], pub gamma_lut: Vec, } impl BackendGammaLut { pub fn new(mut gamma_lut: Vec) -> Self { for element in &mut gamma_lut { element.reserved = 0; } let gamma_lut_bytes = uapi::as_bytes(&gamma_lut as &[_]); let id = *blake3::hash(gamma_lut_bytes).as_bytes(); Self { id, gamma_lut } } } impl PartialEq for BackendGammaLut { fn eq(&self, other: &Self) -> bool { self.id == other.id } } linear_ids!( BackendConnectorStateSerials, BackendConnectorStateSerial, u64 ); #[derive(Clone, Debug, Eq, PartialEq)] pub struct BackendConnectorState { pub serial: BackendConnectorStateSerial, pub enabled: bool, pub active: bool, pub mode: Mode, pub non_desktop_override: Option, pub vrr: bool, pub tearing: bool, pub format: &'static Format, pub color_space: BackendColorSpace, pub eotf: BackendEotfs, pub gamma_lut: Option>, }