diff --git a/Cargo.lock b/Cargo.lock index 038845e9..cf51603e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1042,9 +1042,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "uapi" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "651f13cef1d1988a4c73d215c39fec385fc1be4c7eb6daf89822d5021f7029f8" +checksum = "3bf073840d1b16485bfe28b4e752eccee38d9ac53f152adf869708e3136561e6" dependencies = [ "cc", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 98706ef5..763b423b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ panic = "abort" panic = "abort" [dependencies] -uapi = "0.2.12" +uapi = "0.2.13" thiserror = "1.0.56" ahash = "0.8.7" log = { version = "0.4.20", features = ["std"] } diff --git a/src/backend.rs b/src/backend.rs index 9b05dc4f..82a2569e 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -5,6 +5,7 @@ use { fixed::Fixed, gfx_api::GfxFramebuffer, ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL}, + libinput::consts::DeviceCapability, video::drm::{ConnectorType, DrmError, DrmVersion}, }, jay_config::video::GfxApi, @@ -118,14 +119,41 @@ pub trait InputDevice { 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 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); } @@ -140,6 +168,21 @@ pub enum InputDeviceCapability { 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)] pub enum InputDeviceAccelProfile { Flat, diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 6f18a7d7..7130bf70 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -16,10 +16,7 @@ use { libinput::{ consts::{ AccelProfile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, - LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, LIBINPUT_DEVICE_CAP_GESTURE, - LIBINPUT_DEVICE_CAP_KEYBOARD, LIBINPUT_DEVICE_CAP_POINTER, - LIBINPUT_DEVICE_CAP_SWITCH, LIBINPUT_DEVICE_CAP_TABLET_PAD, - LIBINPUT_DEVICE_CAP_TABLET_TOOL, LIBINPUT_DEVICE_CAP_TOUCH, + LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, }, device::RegisteredDevice, LibInput, LibInputAdapter, LibInputError, @@ -281,7 +278,7 @@ pub async fn create(state: &Rc) -> Result, MetalError> { struct MetalInputDevice { slot: usize, id: InputDeviceId, - _devnum: c::dev_t, + devnum: c::dev_t, fd: CloneCell>>, inputdev: CloneCell>>, devnode: CString, @@ -290,17 +287,22 @@ struct MetalInputDevice { events: SyncQueue, cb: CloneCell>>, name: CloneCell>, - natural_scrolling: Cell, + transform_matrix: Cell>, // state pressed_keys: SmallMap, pressed_buttons: SmallMap, // config + desired: InputDeviceProperties, + effective: InputDeviceProperties, +} + +#[derive(Default)] +struct InputDeviceProperties { left_handed: Cell>, accel_profile: Cell>, accel_speed: Cell>, - transform_matrix: Cell>, tap_enabled: Cell>, drag_enabled: Cell>, drag_lock_enabled: Cell>, @@ -341,30 +343,58 @@ impl LibInputAdapter for DeviceHolder { impl MetalInputDevice { fn apply_config(&self) { - let dev = match self.inputdev.get() { - Some(dev) => dev, - _ => return, - }; - if let Some(lh) = self.left_handed.get() { - dev.device().set_left_handed(lh); + if self.inputdev.is_none() { + return; + } + if let Some(lh) = self.desired.left_handed.get() { + self.set_left_handed(lh); } - if let Some(profile) = self.accel_profile.get() { - dev.device().set_accel_profile(profile); + if let Some(profile) = self.desired.accel_profile.get() { + self.set_accel_profile_(profile); } - if let Some(speed) = self.accel_speed.get() { - dev.device().set_accel_speed(speed); + if let Some(speed) = self.desired.accel_speed.get() { + self.set_accel_speed(speed); } - if let Some(enabled) = self.tap_enabled.get() { - dev.device().set_tap_enabled(enabled); + if let Some(enabled) = self.desired.tap_enabled.get() { + self.set_tap_enabled(enabled); + } + if let Some(enabled) = self.desired.drag_enabled.get() { + self.set_drag_enabled(enabled); + } + if let Some(enabled) = self.desired.drag_lock_enabled.get() { + self.set_drag_lock_enabled(enabled); + } + if let Some(enabled) = self.desired.natural_scrolling_enabled.get() { + self.set_natural_scrolling_enabled(enabled); + } + self.fetch_effective(); + } + + fn fetch_effective(&self) { + let Some(dev) = self.inputdev.get() else { + return; + }; + let device = dev.device(); + if device.left_handed_available() { + self.effective.left_handed.set(Some(device.left_handed())); } - if let Some(enabled) = self.drag_enabled.get() { - dev.device().set_drag_enabled(enabled); + if device.accel_available() { + self.effective + .accel_profile + .set(Some(device.accel_profile())); + self.effective.accel_speed.set(Some(device.accel_speed())); } - if let Some(enabled) = self.drag_lock_enabled.get() { - dev.device().set_drag_lock_enabled(enabled); + if device.tap_available() { + self.effective.tap_enabled.set(Some(device.tap_enabled())); + self.effective.drag_enabled.set(Some(device.drag_enabled())); + self.effective + .drag_lock_enabled + .set(Some(device.drag_lock_enabled())); } - if let Some(enabled) = self.natural_scrolling_enabled.get() { - self.do_set_natural_scrolling_enabled(&dev, enabled); + if device.has_natural_scrolling() { + self.effective + .natural_scrolling_enabled + .set(Some(device.natural_scrolling_enabled())); } } @@ -386,10 +416,16 @@ impl MetalInputDevice { } } - fn do_set_natural_scrolling_enabled(&self, dev: &RegisteredDevice, enabled: bool) { - dev.device().set_natural_scrolling_enabled(enabled); - self.natural_scrolling - .set(dev.device().natural_scrolling_enabled()); + fn set_accel_profile_(&self, profile: AccelProfile) { + self.desired.accel_profile.set(Some(profile)); + if let Some(dev) = self.inputdev.get() { + if dev.device().accel_available() { + dev.device().set_accel_profile(profile); + self.effective + .accel_profile + .set(Some(dev.device().accel_profile())); + } + } } } @@ -415,15 +451,7 @@ impl InputDevice for MetalInputDevice { } fn has_capability(&self, cap: InputDeviceCapability) -> bool { - let li = match cap { - 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, - }; + let li = cap.to_libinput(); match self.inputdev.get() { Some(dev) => dev.device().has_cap(li), _ => false, @@ -431,9 +459,14 @@ impl InputDevice for MetalInputDevice { } fn set_left_handed(&self, left_handed: bool) { - self.left_handed.set(Some(left_handed)); + self.desired.left_handed.set(Some(left_handed)); if let Some(dev) = self.inputdev.get() { - dev.device().set_left_handed(left_handed); + if dev.device().left_handed_available() { + dev.device().set_left_handed(left_handed); + self.effective + .left_handed + .set(Some(dev.device().left_handed())); + } } } @@ -442,16 +475,18 @@ impl InputDevice for MetalInputDevice { InputDeviceAccelProfile::Flat => LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, InputDeviceAccelProfile::Adaptive => LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, }; - self.accel_profile.set(Some(profile)); - if let Some(dev) = self.inputdev.get() { - dev.device().set_accel_profile(profile); - } + self.set_accel_profile_(profile); } fn set_accel_speed(&self, speed: f64) { - self.accel_speed.set(Some(speed)); + self.desired.accel_speed.set(Some(speed)); if let Some(dev) = self.inputdev.get() { - dev.device().set_accel_speed(speed); + if dev.device().accel_available() { + dev.device().set_accel_speed(speed); + self.effective + .accel_speed + .set(Some(dev.device().accel_speed())); + } } } @@ -463,33 +498,95 @@ impl InputDevice for MetalInputDevice { self.name.get() } + fn dev_t(&self) -> Option { + Some(self.devnum) + } + fn set_tap_enabled(&self, enabled: bool) { - self.tap_enabled.set(Some(enabled)); + self.desired.tap_enabled.set(Some(enabled)); if let Some(dev) = self.inputdev.get() { - dev.device().set_tap_enabled(enabled); + if dev.device().tap_available() { + dev.device().set_tap_enabled(enabled); + self.effective + .tap_enabled + .set(Some(dev.device().tap_enabled())); + } } } fn set_drag_enabled(&self, enabled: bool) { - self.drag_enabled.set(Some(enabled)); + self.desired.drag_enabled.set(Some(enabled)); if let Some(dev) = self.inputdev.get() { - dev.device().set_drag_enabled(enabled); + if dev.device().tap_available() { + dev.device().set_drag_enabled(enabled); + self.effective + .drag_enabled + .set(Some(dev.device().drag_enabled())); + } } } fn set_drag_lock_enabled(&self, enabled: bool) { - self.drag_lock_enabled.set(Some(enabled)); + self.desired.drag_lock_enabled.set(Some(enabled)); if let Some(dev) = self.inputdev.get() { - dev.device().set_drag_lock_enabled(enabled); + if dev.device().tap_available() { + dev.device().set_drag_lock_enabled(enabled); + self.effective + .drag_lock_enabled + .set(Some(dev.device().drag_lock_enabled())); + } } } fn set_natural_scrolling_enabled(&self, enabled: bool) { - self.natural_scrolling_enabled.set(Some(enabled)); + self.desired.natural_scrolling_enabled.set(Some(enabled)); if let Some(dev) = self.inputdev.get() { - self.do_set_natural_scrolling_enabled(&dev, enabled); + if dev.device().has_natural_scrolling() { + dev.device().set_natural_scrolling_enabled(enabled); + self.effective + .natural_scrolling_enabled + .set(Some(dev.device().natural_scrolling_enabled())); + } } } + + fn left_handed(&self) -> Option { + self.effective.left_handed.get() + } + + fn accel_profile(&self) -> Option { + let p = self.effective.accel_profile.get()?; + let p = match p { + LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT => InputDeviceAccelProfile::Flat, + LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive, + _ => return None, + }; + Some(p) + } + + fn accel_speed(&self) -> Option { + self.effective.accel_speed.get() + } + + fn transform_matrix(&self) -> Option { + self.transform_matrix.get() + } + + fn tap_enabled(&self) -> Option { + self.effective.tap_enabled.get() + } + + fn drag_enabled(&self) -> Option { + self.effective.drag_enabled.get() + } + + fn drag_lock_enabled(&self) -> Option { + self.effective.drag_lock_enabled.get() + } + + fn natural_scrolling_enabled(&self) -> Option { + self.effective.natural_scrolling_enabled.get() + } } impl MetalInputDevice { diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs index 6ca6f4e5..0f168bf7 100644 --- a/src/backends/metal/input.rs +++ b/src/backends/metal/input.rs @@ -149,13 +149,21 @@ impl MetalBackend { InputEvent::Axis120 { dist: scroll as _, axis, - inverted: dev.natural_scrolling.get(), + inverted: dev + .effective + .natural_scrolling_enabled + .get() + .unwrap_or_default(), } } else { InputEvent::AxisPx { dist: Fixed::from_f64(scroll), axis, - inverted: dev.natural_scrolling.get(), + inverted: dev + .effective + .natural_scrolling_enabled + .get() + .unwrap_or_default(), } }; dev.event(ie); @@ -192,10 +200,14 @@ impl MetalBackend { let mut dx_unaccelerated = event.dx_unaccelerated(); let mut dy_unaccelerated = event.dy_unaccelerated(); if let Some(matrix) = dev.transform_matrix.get() { - dx = matrix[0][0] * dx + matrix[0][1] * dy; - dy = matrix[1][0] * dx + matrix[1][1] * dy; - dx_unaccelerated = matrix[0][0] * dx_unaccelerated + matrix[0][1] * dy_unaccelerated; - dy_unaccelerated = matrix[1][0] * dx_unaccelerated + matrix[1][1] * dy_unaccelerated; + (dx, dy) = ( + matrix[0][0] * dx + matrix[0][1] * dy, + matrix[1][0] * dx + matrix[1][1] * dy, + ); + (dx_unaccelerated, dy_unaccelerated) = ( + matrix[0][0] * dx_unaccelerated + matrix[0][1] * dy_unaccelerated, + matrix[1][0] * dx_unaccelerated + matrix[1][1] * dy_unaccelerated, + ); } dev.event(InputEvent::Motion { time_usec: event.time_usec(), diff --git a/src/backends/metal/monitor.rs b/src/backends/metal/monitor.rs index 829ad07a..425c5a11 100644 --- a/src/backends/metal/monitor.rs +++ b/src/backends/metal/monitor.rs @@ -278,7 +278,7 @@ impl MetalBackend { let dev = Rc::new(MetalInputDevice { slot, id: device_id, - _devnum: devnum, + devnum, fd: Default::default(), inputdev: Default::default(), devnode: devnode.to_owned(), @@ -287,17 +287,11 @@ impl MetalBackend { events: Default::default(), cb: Default::default(), name: Default::default(), - natural_scrolling: Default::default(), pressed_keys: Default::default(), pressed_buttons: Default::default(), - left_handed: Default::default(), - accel_profile: Default::default(), - accel_speed: Default::default(), + desired: Default::default(), transform_matrix: Default::default(), - tap_enabled: Default::default(), - drag_enabled: Default::default(), - drag_lock_enabled: Default::default(), - natural_scrolling_enabled: Default::default(), + effective: Default::default(), }); slots[slot] = Some(dev.clone()); self.device_holder @@ -337,8 +331,6 @@ impl MetalBackend { }; inputdev.device().set_slot(slot); dev.name.set(Rc::new(inputdev.device().name())); - dev.natural_scrolling - .set(inputdev.device().natural_scrolling_enabled()); dev.inputdev.set(Some(inputdev)); dev.apply_config(); slf.state diff --git a/src/backends/x.rs b/src/backends/x.rs index 6aaeb5ae..77a35c81 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -1177,6 +1177,10 @@ impl InputDevice for XSeatKeyboard { self.0.kb_name.clone() } + fn dev_t(&self) -> Option { + None + } + fn set_tap_enabled(&self, enabled: bool) { let _ = enabled; } @@ -1242,6 +1246,10 @@ impl InputDevice for XSeatMouse { self.0.mouse_name.clone() } + fn dev_t(&self) -> Option { + None + } + fn set_tap_enabled(&self, enabled: bool) { let _ = enabled; } diff --git a/src/cli.rs b/src/cli.rs index 1191a2b7..9162103b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,6 @@ mod generate; mod idle; +mod input; mod log; mod quit; mod randr; @@ -10,7 +11,11 @@ mod set_log_level; mod unlock; use { - crate::{cli::randr::RandrArgs, compositor::start_compositor, portal}, + crate::{ + cli::{input::InputArgs, randr::RandrArgs}, + compositor::start_compositor, + portal, + }, ::log::Level, clap::{Args, Parser, Subcommand, ValueEnum}, clap_complete::Shell, @@ -58,6 +63,8 @@ pub enum Cmd { Portal, /// Inspect/modify graphics card and connector settings. Randr(RandrArgs), + /// Inspect/modify input settings. + Input(InputArgs), #[cfg(feature = "it")] RunTests, } @@ -221,6 +228,7 @@ pub fn main() { Cmd::SeatTest(a) => seat_test::main(cli.global, a), Cmd::Portal => portal::run(cli.global), Cmd::Randr(a) => randr::main(cli.global, a), + Cmd::Input(a) => input::main(cli.global, a), #[cfg(feature = "it")] Cmd::RunTests => crate::it::run_tests(), } diff --git a/src/cli/input.rs b/src/cli/input.rs new file mode 100644 index 00000000..6b55249b --- /dev/null +++ b/src/cli/input.rs @@ -0,0 +1,732 @@ +use { + crate::{ + backend::{InputDeviceAccelProfile, InputDeviceCapability}, + cli::GlobalArgs, + clientmem::ClientMem, + libinput::consts::{ + LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, + }, + tools::tool_client::{with_tool_client, Handle, ToolClient}, + utils::{errorfmt::ErrorFmt, string_ext::StringExt}, + wire::{jay_compositor, jay_input, JayInputId}, + }, + clap::{Args, Subcommand, ValueEnum}, + isnt::std_1::vec::IsntVecExt, + std::{ + cell::RefCell, + io::{stdin, stdout, Read, Write}, + mem, + ops::DerefMut, + rc::Rc, + }, + uapi::c, +}; + +#[derive(Args, Debug)] +pub struct InputArgs { + #[clap(subcommand)] + pub command: Option, +} + +#[derive(Subcommand, Debug)] +pub enum InputCmd { + /// Show the current settings. + Show(ShowArgs), + /// Modify the settings of a seat. + Seat(SeatArgs), + /// Modify the settings of a device. + Device(DeviceArgs), +} + +impl Default for InputCmd { + fn default() -> Self { + Self::Show(Default::default()) + } +} + +#[derive(Args, Debug, Default)] +pub struct ShowArgs { + /// Print more information about devices. + #[arg(short, long)] + pub verbose: bool, +} + +#[derive(Args, Debug)] +pub struct SeatArgs { + /// The seat to modify, e.g. default. + pub seat: String, + #[clap(subcommand)] + pub command: Option, +} + +#[derive(Args, Debug)] +pub struct DeviceArgs { + /// The ID of the device to modify. + pub device: u32, + #[clap(subcommand)] + pub command: Option, +} + +#[derive(Subcommand, Debug, Clone)] +pub enum SeatCommand { + /// Show information about this seat. + Show(SeatShowArgs), + /// Set the repeat rate of the keyboard. + SetRepeatRate(SetRepeatRateArgs), + /// Set the keymap. + SetKeymap(SetKeymapArgs), + /// Retrieve the keymap. + Keymap, + /// Configure whether this seat uses the hardware cursor. + UseHardwareCursor(UseHardwareCursorArgs), + /// Set the size of the cursor. + SetCursorSize(SetCursorSizeArgs), +} + +impl Default for SeatCommand { + fn default() -> Self { + Self::Show(SeatShowArgs::default()) + } +} + +#[derive(Args, Debug, Default, Clone)] +pub struct SeatShowArgs { + /// Print more information about devices. + #[arg(short, long)] + pub verbose: bool, +} + +#[derive(Subcommand, Debug, Clone, Default)] +pub enum DeviceCommand { + /// Show information about this device. + #[default] + Show, + /// Set the acceleration profile. + SetAccelProfile(SetAccelProfileArgs), + /// Set the acceleration speed. + SetAccelSpeed(SetAccelSpeedArgs), + /// Set whether tap is enabled. + SetTapEnabled(SetTapDragEnabledArgs), + /// Set whether tap-drag is enabled. + SetTapDragEnabled(SetTapDragEnabledArgs), + /// Set whether tap-drag-lock is enabled. + SetTapDragLockEnabled(SetTapDragLockEnabledArgs), + /// Set whether the device is left-handed. + SetLeftHanded(SetLeftHandedArgs), + /// Set whether the device uses natural scrolling. + SetNaturalScrolling(SetNaturalScrollingArgs), + /// Set the pixels to scroll per scroll-wheel dedent. + SetPxPerWheelScroll(SetPxPerWheelScrollArgs), + /// Set the transformation matrix. + SetTransformMatrix(SetTransformMatrixArgs), + /// Attach the device to a seat. + Attach(AttachArgs), + /// Detach the device from its seat. + Detach, +} + +#[derive(ValueEnum, Debug, Clone)] +pub enum AccelProfile { + Flat, + Adaptive, +} + +#[derive(Args, Debug, Clone)] +pub struct SetAccelProfileArgs { + /// The profile. + pub profile: AccelProfile, +} + +#[derive(Args, Debug, Clone)] +pub struct SetAccelSpeedArgs { + /// The speed. Must be in the range \[-1, 1]. + pub speed: f64, +} + +#[derive(Args, Debug, Clone)] +pub struct SetTapEnabledArgs { + /// Whether tap is enabled. + #[arg(action = clap::ArgAction::Set)] + pub enabled: bool, +} + +#[derive(Args, Debug, Clone)] +pub struct SetTapDragEnabledArgs { + /// Whether tap-drag is enabled. + #[arg(action = clap::ArgAction::Set)] + pub enabled: bool, +} + +#[derive(Args, Debug, Clone)] +pub struct SetTapDragLockEnabledArgs { + /// Whether tap-drag-lock is enabled. + #[arg(action = clap::ArgAction::Set)] + pub enabled: bool, +} + +#[derive(Args, Debug, Clone)] +pub struct SetLeftHandedArgs { + /// Whether the device is left handed. + #[arg(action = clap::ArgAction::Set)] + pub left_handed: bool, +} + +#[derive(Args, Debug, Clone)] +pub struct SetNaturalScrollingArgs { + /// Whether natural scrolling is enabled. + #[arg(action = clap::ArgAction::Set)] + pub natural_scrolling: bool, +} + +#[derive(Args, Debug, Clone)] +pub struct SetPxPerWheelScrollArgs { + /// The number of pixels to scroll. + pub px: f64, +} + +#[derive(Args, Debug, Clone)] +pub struct SetTransformMatrixArgs { + pub m11: f64, + pub m12: f64, + pub m21: f64, + pub m22: f64, +} + +#[derive(Args, Debug, Clone)] +pub struct AttachArgs { + /// The seat to attach to. + pub seat: String, +} + +#[derive(Args, Debug, Clone)] +pub struct SetRepeatRateArgs { + /// The number of repeats per second. + pub rate: i32, + /// The delay before the first repeat in milliseconds. + pub delay: i32, +} + +#[derive(Args, Debug, Clone)] +pub struct SetCursorSizeArgs { + /// The size of the cursor. + pub size: u32, +} + +#[derive(Args, Debug, Clone)] +pub struct SetKeymapArgs { + /// The file to read the keymap from. Omit for stdin. + pub file: Option, +} + +#[derive(Args, Debug, Clone)] +pub struct UseHardwareCursorArgs { + /// Whether the seat uses the hardware cursor. + #[arg(action = clap::ArgAction::Set)] + pub enabled: bool, +} + +pub fn main(global: GlobalArgs, args: InputArgs) { + with_tool_client(global.log_level.into(), |tc| async move { + let idle = Rc::new(Input { tc: tc.clone() }); + idle.run(args).await; + }); +} + +#[derive(Clone, Debug)] +struct Seat { + pub name: String, + pub repeat_rate: i32, + pub repeat_delay: i32, + pub hardware_cursor: bool, +} + +#[derive(Clone, Debug)] +struct InputDevice { + pub id: u32, + pub name: String, + pub seat: Option, + pub syspath: Option, + pub devnode: Option, + pub capabilities: Vec, + pub accel_profile: Option, + pub accel_speed: Option, + pub tap_enabled: Option, + pub tap_drag_enabled: Option, + pub tap_drag_lock_enabled: Option, + pub left_handed: Option, + pub natural_scrolling_enabled: Option, + pub px_per_wheel_scroll: Option, + pub transform_matrix: Option<[[f64; 2]; 2]>, +} + +#[derive(Clone, Debug, Default)] +struct Data { + seats: Vec, + input_device: Vec, +} + +struct Input { + tc: Rc, +} + +impl Input { + async fn run(self: &Rc, args: InputArgs) { + let tc = &self.tc; + let comp = tc.jay_compositor().await; + let input = tc.id(); + tc.send(jay_compositor::GetInput { + self_id: comp, + id: input, + }); + match args.command.unwrap_or_default() { + InputCmd::Show(args) => self.show(input, args).await, + InputCmd::Seat(args) => self.seat(input, args).await, + InputCmd::Device(args) => self.device(input, args).await, + } + } + + fn handle_error(&self, input: JayInputId, f: F) { + jay_input::Error::handle(&self.tc, input, (), move |_, msg| { + f(msg.msg); + std::process::exit(1); + }); + } + + async fn seat(self: &Rc, input: JayInputId, args: SeatArgs) { + let tc = &self.tc; + match args.command.unwrap_or_default() { + SeatCommand::Show(a) => { + self.handle_error(input, |e| { + eprintln!("Could not retrieve seat data: {}", e); + }); + tc.send(jay_input::GetSeat { + self_id: input, + name: &args.seat, + }); + let data = self.get(input).await; + self.print_data(data, a.verbose); + } + SeatCommand::SetRepeatRate(a) => { + self.handle_error(input, |e| { + eprintln!("Could not set repeat rate: {}", e); + }); + tc.send(jay_input::SetRepeatRate { + self_id: input, + seat: &args.seat, + repeat_rate: a.rate, + repeat_delay: a.delay, + }); + } + SeatCommand::SetKeymap(a) => { + self.handle_error(input, |e| { + eprintln!("Could not set keymap: {}", e); + }); + let map = match &a.file { + None => { + let mut map = vec![]; + if let Err(e) = stdin().read_to_end(&mut map) { + eprintln!("Could not read from stdin: {}", ErrorFmt(e)); + std::process::exit(1); + } + map + } + Some(f) => match std::fs::read(f) { + Ok(m) => m, + Err(e) => { + eprintln!("Could not read {}: {}", f, ErrorFmt(e)); + std::process::exit(1); + } + }, + }; + let mut memfd = + uapi::memfd_create("keymap", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap(); + memfd.write_all(&map).unwrap(); + uapi::lseek(memfd.raw(), 0, c::SEEK_SET).unwrap(); + uapi::fcntl_add_seals( + memfd.raw(), + c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE, + ) + .unwrap(); + tc.send(jay_input::SetKeymap { + self_id: input, + seat: &args.seat, + keymap: Rc::new(memfd), + keymap_len: map.len() as _, + }); + } + SeatCommand::UseHardwareCursor(a) => { + self.handle_error(input, |e| { + eprintln!("Could not set hardware cursor: {}", e); + }); + tc.send(jay_input::UseHardwareCursor { + self_id: input, + seat: &args.seat, + use_hardware_cursor: a.enabled as _, + }); + } + SeatCommand::Keymap => { + self.handle_error(input, |e| { + eprintln!("Could not retrieve the keymap: {}", e); + }); + let data = Rc::new(RefCell::new(Vec::new())); + jay_input::Keymap::handle(tc, input, data.clone(), |d, map| { + let mem = Rc::new( + ClientMem::new(map.keymap.raw(), map.keymap_len as _, true).unwrap(), + ) + .offset(0); + mem.read(d.borrow_mut().deref_mut()).unwrap(); + }); + tc.send(jay_input::GetKeymap { + self_id: input, + seat: &args.seat, + }); + tc.round_trip().await; + let map = data.take(); + stdout().write_all(&map).unwrap(); + } + SeatCommand::SetCursorSize(a) => { + self.handle_error(input, |e| { + eprintln!("Could not set cursor size: {}", e); + }); + tc.send(jay_input::SetCursorSize { + self_id: input, + seat: &args.seat, + size: a.size, + }); + } + } + tc.round_trip().await; + } + + async fn device(self: &Rc, input: JayInputId, args: DeviceArgs) { + let tc = &self.tc; + match args.command.unwrap_or_default() { + DeviceCommand::Show => { + self.handle_error(input, |e| { + eprintln!("Could not retrieve device data: {}", e); + }); + tc.send(jay_input::GetDevice { + self_id: input, + id: args.device, + }); + let data = self.get(input).await; + for device in &data.input_device { + self.print_device("", true, device); + } + } + DeviceCommand::SetAccelProfile(a) => { + let profile = match a.profile { + AccelProfile::Flat => LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT.0, + AccelProfile::Adaptive => LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE.0, + }; + self.handle_error(input, |e| { + eprintln!("Could not set the acceleration profile: {}", e); + }); + tc.send(jay_input::SetAccelProfile { + self_id: input, + id: args.device, + profile, + }); + } + DeviceCommand::SetAccelSpeed(a) => { + self.handle_error(input, |e| { + eprintln!("Could not set the acceleration speed: {}", e); + }); + tc.send(jay_input::SetAccelSpeed { + self_id: input, + id: args.device, + speed: a.speed, + }); + } + DeviceCommand::SetTapEnabled(a) => { + self.handle_error(input, |e| { + eprintln!("Could not modify the tap-enabled setting: {}", e); + }); + tc.send(jay_input::SetTapEnabled { + self_id: input, + id: args.device, + enabled: a.enabled as _, + }); + } + DeviceCommand::SetTapDragEnabled(a) => { + self.handle_error(input, |e| { + eprintln!("Could not modify the tap-drag-enabled setting: {}", e); + }); + tc.send(jay_input::SetTapDragEnabled { + self_id: input, + id: args.device, + enabled: a.enabled as _, + }); + } + DeviceCommand::SetTapDragLockEnabled(a) => { + self.handle_error(input, |e| { + eprintln!("Could not modify the tap-drag-lock-enabled setting: {}", e); + }); + tc.send(jay_input::SetTapDragLockEnabled { + self_id: input, + id: args.device, + enabled: a.enabled as _, + }); + } + DeviceCommand::SetLeftHanded(a) => { + self.handle_error(input, |e| { + eprintln!("Could not modify the left-handed setting: {}", e); + }); + tc.send(jay_input::SetLeftHanded { + self_id: input, + id: args.device, + enabled: a.left_handed as _, + }); + } + DeviceCommand::SetNaturalScrolling(a) => { + self.handle_error(input, |e| { + eprintln!("Could not modify the natural-scrolling setting: {}", e); + }); + tc.send(jay_input::SetNaturalScrolling { + self_id: input, + id: args.device, + enabled: a.natural_scrolling as _, + }); + } + DeviceCommand::SetPxPerWheelScroll(a) => { + self.handle_error(input, |e| { + eprintln!("Could not modify the px-per-wheel-scroll setting: {}", e); + }); + tc.send(jay_input::SetPxPerWheelScroll { + self_id: input, + id: args.device, + px: a.px, + }); + } + DeviceCommand::SetTransformMatrix(a) => { + self.handle_error(input, |e| { + eprintln!("Could not modify the transform matrix: {}", e); + }); + tc.send(jay_input::SetTransformMatrix { + self_id: input, + id: args.device, + m11: a.m11, + m12: a.m12, + m21: a.m21, + m22: a.m22, + }); + } + DeviceCommand::Attach(a) => { + self.handle_error(input, |e| { + eprintln!("Could not attach the device: {}", e); + }); + tc.send(jay_input::Attach { + self_id: input, + id: args.device, + seat: &a.seat, + }); + } + DeviceCommand::Detach => { + self.handle_error(input, |e| { + eprintln!("Could not detach the device: {}", e); + }); + tc.send(jay_input::Detach { + self_id: input, + id: args.device, + }); + } + } + tc.round_trip().await; + } + + async fn show(self: &Rc, input: JayInputId, args: ShowArgs) { + self.tc.send(jay_input::GetAll { self_id: input }); + let data = self.get(input).await; + self.print_data(data, args.verbose); + } + + fn print_data(self: &Rc, mut data: Data, verbose: bool) { + data.seats.sort_by(|l, r| l.name.cmp(&r.name)); + data.input_device.sort_by_key(|l| l.id); + let mut first = true; + let print_devices = |d: &[&InputDevice]| { + for device in d { + if verbose { + self.print_device(" ", false, device); + } else { + println!(" {}: {}", device.id, device.name); + } + } + }; + for seat in &data.seats { + if !mem::take(&mut first) { + println!(); + } + self.print_seat(seat); + let input_devices: Vec<_> = data + .input_device + .iter() + .filter(|c| c.seat.as_ref() == Some(&seat.name)) + .collect(); + if input_devices.is_not_empty() { + println!(" devices:"); + } + print_devices(&input_devices); + } + { + let input_devices: Vec<_> = data + .input_device + .iter() + .filter(|c| c.seat.is_none()) + .collect(); + if input_devices.is_not_empty() { + if !mem::take(&mut first) { + println!(); + } + println!("Detached devices:"); + print_devices(&input_devices); + } + } + } + + fn print_seat(&self, seat: &Seat) { + println!("Seat {}:", seat.name); + println!(" repeat rate: {}", seat.repeat_rate); + println!(" repeat delay: {}", seat.repeat_delay); + if !seat.hardware_cursor { + println!(" hardware cursor disabled"); + } + } + + fn print_device(&self, prefix: &str, print_seat: bool, device: &InputDevice) { + println!("{prefix}{}:", device.id); + println!("{prefix} name: {}", device.name); + if print_seat { + let seat = match device.seat.as_deref() { + Some(s) => s, + _ => "", + }; + println!("{prefix} seat: {}", seat); + } + if let Some(v) = &device.syspath { + println!("{prefix} syspath: {}", v); + } + if let Some(v) = &device.devnode { + println!("{prefix} devnode: {}", v); + } + print!("{prefix} capabilities:"); + let mut first = true; + for cap in &device.capabilities { + use InputDeviceCapability::*; + print!(" "); + if !mem::take(&mut first) { + print!("| "); + } + let name = match cap { + Keyboard => "keyboard", + Pointer => "pointer", + Touch => "touch", + TabletTool => "tablet tool", + TabletPad => "tablet pad", + Gesture => "gesture", + Switch => "switch", + }; + print!("{}", name); + } + println!(); + if let Some(v) = &device.accel_profile { + let name = match v { + InputDeviceAccelProfile::Flat => "flat", + InputDeviceAccelProfile::Adaptive => "adaptive", + }; + println!("{prefix} accel profile: {}", name); + } + if let Some(v) = &device.accel_speed { + println!("{prefix} accel speed: {}", v); + } + if let Some(v) = &device.tap_enabled { + println!("{prefix} tap enabled: {}", v); + } + if let Some(v) = &device.tap_drag_enabled { + println!("{prefix} tap drag enabled: {}", v); + } + if let Some(v) = &device.tap_drag_lock_enabled { + println!("{prefix} tap drag lock enabled: {}", v); + } + if let Some(v) = &device.left_handed { + println!("{prefix} left handed: {}", v); + } + if let Some(v) = &device.natural_scrolling_enabled { + println!("{prefix} natural scrolling: {}", v); + } + if let Some(v) = &device.px_per_wheel_scroll { + println!("{prefix} px per wheel scroll: {}", v); + } + if let Some(v) = &device.transform_matrix { + println!("{prefix} transform matrix: {:?}", v); + } + } + + async fn get(self: &Rc, input: JayInputId) -> Data { + let tc = &self.tc; + let data = Rc::new(RefCell::new(Data::default())); + jay_input::Seat::handle(tc, input, data.clone(), |data, msg| { + data.borrow_mut().seats.push(Seat { + name: msg.name.to_string(), + repeat_rate: msg.repeat_rate, + repeat_delay: msg.repeat_delay, + hardware_cursor: msg.hardware_cursor != 0, + }); + }); + jay_input::InputDevice::handle(tc, input, data.clone(), |data, msg| { + use crate::{backend::InputDeviceCapability::*, libinput::consts::*}; + let mut capabilities = vec![]; + let mut is_pointer = false; + for cap in msg.capabilities { + let cap = match DeviceCapability(*cap) { + LIBINPUT_DEVICE_CAP_KEYBOARD => Keyboard, + LIBINPUT_DEVICE_CAP_POINTER => { + is_pointer = true; + Pointer + } + LIBINPUT_DEVICE_CAP_TOUCH => Touch, + LIBINPUT_DEVICE_CAP_TABLET_TOOL => TabletTool, + LIBINPUT_DEVICE_CAP_TABLET_PAD => TabletPad, + LIBINPUT_DEVICE_CAP_GESTURE => Gesture, + LIBINPUT_DEVICE_CAP_SWITCH => InputDeviceCapability::Switch, + _ => continue, + }; + capabilities.push(cap); + } + let accel_available = msg.accel_available != 0; + let tap_available = msg.tap_available != 0; + let left_handed_available = msg.left_handed_available != 0; + let natural_scrolling_available = msg.natural_scrolling_available != 0; + let mut accel_profile = match AccelProfile(msg.accel_profile) { + LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT => Some(InputDeviceAccelProfile::Flat), + LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE => Some(InputDeviceAccelProfile::Adaptive), + _ => None, + }; + if !accel_available { + accel_profile = None; + } + let mut data = data.borrow_mut(); + data.input_device.push(InputDevice { + id: msg.id, + name: msg.name.to_string(), + seat: msg.seat.to_string_if_not_empty(), + syspath: msg.syspath.to_string_if_not_empty(), + devnode: msg.devnode.to_string_if_not_empty(), + capabilities, + accel_profile, + accel_speed: accel_available.then_some(msg.accel_speed), + tap_enabled: tap_available.then_some(msg.tap_enabled != 0), + tap_drag_enabled: tap_available.then_some(msg.tap_drag_enabled != 0), + tap_drag_lock_enabled: tap_available.then_some(msg.tap_drag_lock_enabled != 0), + left_handed: left_handed_available.then_some(msg.left_handed != 0), + natural_scrolling_enabled: natural_scrolling_available + .then_some(msg.natural_scrolling_enabled != 0), + px_per_wheel_scroll: is_pointer.then_some(msg.px_per_wheel_scroll), + transform_matrix: uapi::pod_read(msg.transform_matrix).ok(), + }); + }); + tc.round_trip().await; + let x = data.borrow_mut().clone(); + x + } +} diff --git a/src/clientmem.rs b/src/clientmem.rs index 685b67ae..f85dc5e7 100644 --- a/src/clientmem.rs +++ b/src/clientmem.rs @@ -1,4 +1,5 @@ use { + crate::utils::vec_ext::VecExt, std::{ cell::Cell, mem::MaybeUninit, @@ -33,7 +34,7 @@ pub struct ClientMemOffset { } impl ClientMem { - pub fn new(fd: i32, len: usize) -> Result { + pub fn new(fd: i32, len: usize, read_only: bool) -> Result { let mut sigbus_impossible = false; if let Ok(seals) = uapi::fcntl_get_seals(fd) { if seals & c::F_SEAL_SHRINK != 0 { @@ -45,15 +46,12 @@ impl ClientMem { let data = if len == 0 { &mut [][..] } else { + let prot = match read_only { + true => c::PROT_READ, + false => c::PROT_READ | c::PROT_WRITE, + }; unsafe { - let data = c::mmap64( - ptr::null_mut(), - len, - c::PROT_READ | c::PROT_WRITE, - c::MAP_SHARED, - fd, - 0, - ); + let data = c::mmap64(ptr::null_mut(), len, prot, c::MAP_SHARED, fd, 0); if data == c::MAP_FAILED { return Err(ClientMemError::MmapFailed(uapi::Errno::default().into())); } @@ -101,6 +99,17 @@ impl ClientMemOffset { } } } + + pub fn read(&self, dst: &mut Vec) -> Result<(), ClientMemError> { + self.access(|v| { + dst.reserve(v.len()); + let (_, unused) = dst.split_at_spare_mut_ext(); + unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v)); + unsafe { + dst.set_len(dst.len() + v.len()); + } + }) + } } impl Drop for ClientMem { diff --git a/src/cursor.rs b/src/cursor.rs index 57bb62b0..126923cc 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -656,6 +656,7 @@ fn parser_cursor_file( size: u32, scale: Scale, best_fit: i64, + best_fit_size: u32, } let mut targets = Vec::new(); for scale in scales { @@ -668,6 +669,7 @@ fn parser_cursor_file( size: *size, scale: *scale, best_fit: i64::MAX, + best_fit_size: 0, }); } } @@ -682,9 +684,10 @@ fn parser_cursor_file( let fit = (size as i64 - target.effective_size as i64).abs(); if fit < target.best_fit { target.best_fit = fit; + target.best_fit_size = size; target.positions.clear(); } - if fit == target.best_fit { + if size == target.best_fit_size { target.positions.push(position); } } diff --git a/src/ifs.rs b/src/ifs.rs index e77d55c8..d1e0c6ea 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -7,6 +7,7 @@ pub mod ext_session_lock_v1; pub mod ipc; pub mod jay_compositor; pub mod jay_idle; +pub mod jay_input; pub mod jay_log_file; pub mod jay_output; pub mod jay_pointer; diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index cdad0134..2b9487be 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -4,10 +4,11 @@ use { client::{Client, ClientError}, globals::{Global, GlobalName}, ifs::{ - jay_idle::JayIdle, jay_log_file::JayLogFile, jay_output::JayOutput, - jay_pointer::JayPointer, jay_randr::JayRandr, jay_render_ctx::JayRenderCtx, - jay_screencast::JayScreencast, jay_screenshot::JayScreenshot, - jay_seat_events::JaySeatEvents, jay_workspace_watcher::JayWorkspaceWatcher, + jay_idle::JayIdle, jay_input::JayInput, jay_log_file::JayLogFile, + jay_output::JayOutput, jay_pointer::JayPointer, jay_randr::JayRandr, + jay_render_ctx::JayRenderCtx, jay_screencast::JayScreencast, + jay_screenshot::JayScreenshot, jay_seat_events::JaySeatEvents, + jay_workspace_watcher::JayWorkspaceWatcher, }, leaks::Tracker, object::Object, @@ -316,6 +317,14 @@ impl JayCompositor { self.client.add_client_obj(&sc)?; Ok(()) } + + fn get_input(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> { + let req: GetInput = self.client.parse(self, parser)?; + let sc = Rc::new(JayInput::new(req.id, &self.client)); + track!(self.client, sc); + self.client.add_client_obj(&sc)?; + Ok(()) + } } object_base! { @@ -338,6 +347,7 @@ object_base! { WATCH_WORKSPACES => watch_workspaces, CREATE_SCREENCAST => create_screencast, GET_RANDR => get_randr, + GET_INPUT => get_input, } impl Object for JayCompositor {} diff --git a/src/ifs/jay_input.rs b/src/ifs/jay_input.rs new file mode 100644 index 00000000..560b301a --- /dev/null +++ b/src/ifs/jay_input.rs @@ -0,0 +1,411 @@ +use { + crate::{ + backend::{self, InputDeviceAccelProfile, InputDeviceId}, + client::{Client, ClientError}, + clientmem::{ClientMem, ClientMemError}, + ifs::wl_seat::WlSeatGlobal, + leaks::Tracker, + libinput::consts::{ + AccelProfile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, + LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, + }, + object::Object, + state::{DeviceHandlerData, InputDeviceData}, + utils::{ + buffd::{MsgParser, MsgParserError}, + errorfmt::ErrorFmt, + }, + wire::{jay_input::*, JayInputId}, + xkbcommon::XkbCommonError, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct JayInput { + pub id: JayInputId, + pub client: Rc, + pub tracker: Tracker, +} + +impl JayInput { + pub fn new(id: JayInputId, client: &Rc) -> Self { + Self { + id, + client: client.clone(), + tracker: Default::default(), + } + } + + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let _req: Destroy = self.client.parse(self, parser)?; + self.client.remove_obj(self)?; + Ok(()) + } + + fn get_all(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let _req: GetAll = self.client.parse(self, parser)?; + let state = &self.client.state; + for seat in state.globals.seats.lock().values() { + self.send_seat(seat); + } + for dev in state.input_device_handlers.borrow().values() { + self.send_input_device(dev); + } + Ok(()) + } + + fn get_seat(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: GetSeat = self.client.parse(self, parser)?; + self.or_error(|| { + let seat = self.seat(req.name)?; + self.send_seat(&seat); + for dev in self.client.state.input_device_handlers.borrow().values() { + if let Some(attached) = dev.data.seat.get() { + if attached.id() == seat.id() { + self.send_input_device(dev); + } + } + } + Ok(()) + }) + } + + fn get_device(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: GetDevice = self.client.parse(self, parser)?; + self.or_error(|| { + match self + .client + .state + .input_device_handlers + .borrow() + .get(&InputDeviceId::from_raw(req.id)) + { + None => Err(JayInputError::DeviceDoesNotExist(req.id)), + Some(d) => { + self.send_input_device(d); + Ok(()) + } + } + }) + } + + fn seat(&self, name: &str) -> Result, JayInputError> { + for seat in self.client.state.globals.seats.lock().values() { + if seat.seat_name() == name { + return Ok(seat.clone()); + } + } + Err(JayInputError::SeatDoesNotExist(name.to_string())) + } + + fn or_error(&self, f: impl FnOnce() -> Result<(), JayInputError>) -> Result<(), JayInputError> { + if let Err(e) = f() { + self.send_error(&ErrorFmt(e).to_string()); + } + Ok(()) + } + + fn set_repeat_rate(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetRepeatRate = self.client.parse(self, parser)?; + self.or_error(|| { + if req.repeat_rate < 0 { + return Err(JayInputError::NegativeRepeatRate); + } + if req.repeat_delay < 0 { + return Err(JayInputError::NegativeRepeatDelay); + } + let seat = self.seat(req.seat)?; + seat.set_rate(req.repeat_rate, req.repeat_delay); + Ok(()) + }) + } + + fn set_keymap(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetKeymap = self.client.parse(self, parser)?; + let cm = Rc::new(ClientMem::new(req.keymap.raw(), req.keymap_len as _, true)?).offset(0); + let mut map = vec![]; + cm.read(&mut map)?; + self.or_error(|| { + let map = self.client.state.xkb_ctx.keymap_from_str(&map)?; + let seat = self.seat(req.seat)?; + seat.set_keymap(&map); + Ok(()) + }) + } + + fn get_keymap(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: GetKeymap = self.client.parse(self, parser)?; + self.or_error(|| { + let seat = self.seat(req.seat)?; + self.send_keymap(&seat); + Ok(()) + }) + } + + fn use_hardware_cursor(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: UseHardwareCursor = self.client.parse(self, parser)?; + self.or_error(|| { + let seat = self.seat(req.seat)?; + seat.set_hardware_cursor(req.use_hardware_cursor != 0); + Ok(()) + }) + } + + fn send_seat(&self, data: &WlSeatGlobal) { + self.client.event(Seat { + self_id: self.id, + name: data.seat_name(), + repeat_rate: data.get_rate().0, + repeat_delay: data.get_rate().1, + hardware_cursor: data.hardware_cursor() as _, + }); + } + + fn send_error(&self, error: &str) { + self.client.event(Error { + self_id: self.id, + msg: error, + }); + } + + fn send_keymap(&self, data: &WlSeatGlobal) { + let map = data.keymap(); + self.client.event(Keymap { + self_id: self.id, + keymap: map.map.clone(), + keymap_len: (map.map_len - 1) as _, + }); + } + + fn send_input_device(&self, data: &InputDeviceData) { + use backend::InputDeviceCapability::*; + let mut caps = vec![]; + for cap in [ + Keyboard, Pointer, Touch, TabletTool, TabletPad, Gesture, Switch, + ] { + if data.data.device.has_capability(cap) { + caps.push(cap.to_libinput().raw()); + } + } + let dev = &data.data.device; + let accel_profile = dev.accel_profile(); + let left_handed = dev.left_handed(); + let natural_scrolling = dev.natural_scrolling_enabled(); + let tap_enabled = dev.tap_enabled(); + let transform_matrix = dev.transform_matrix(); + self.client.event(InputDevice { + self_id: self.id, + seat: data + .data + .seat + .get() + .as_deref() + .map(|s| s.seat_name()) + .unwrap_or_default(), + id: data.id.raw(), + syspath: data.syspath.as_deref().unwrap_or_default(), + devnode: data.devnode.as_deref().unwrap_or_default(), + name: dev.name().as_str(), + capabilities: &caps, + accel_available: accel_profile.is_some() as _, + accel_profile: match accel_profile { + None => 0, + Some(p) => match p { + InputDeviceAccelProfile::Flat => LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT.0, + InputDeviceAccelProfile::Adaptive => LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE.0, + }, + }, + accel_speed: dev.accel_speed().unwrap_or_default(), + left_handed_available: left_handed.is_some() as _, + left_handed: left_handed.unwrap_or_default() as _, + natural_scrolling_available: natural_scrolling.is_some() as _, + natural_scrolling_enabled: natural_scrolling.unwrap_or_default() as _, + px_per_wheel_scroll: data.data.px_per_scroll_wheel.get(), + tap_available: tap_enabled.is_some() as _, + tap_enabled: tap_enabled.unwrap_or_default() as _, + tap_drag_enabled: dev.drag_enabled().unwrap_or_default() as _, + tap_drag_lock_enabled: dev.drag_lock_enabled().unwrap_or_default() as _, + transform_matrix: transform_matrix + .as_ref() + .map(uapi::as_bytes) + .unwrap_or_default(), + }); + } + + fn device(&self, id: u32) -> Result, JayInputError> { + let idh = self.client.state.input_device_handlers.borrow_mut(); + match idh.get(&InputDeviceId::from_raw(id)) { + None => Err(JayInputError::DeviceDoesNotExist(id)), + Some(d) => Ok(d.data.clone()), + } + } + + fn set_accel_profile(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetAccelProfile = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + let profile = match AccelProfile(req.profile) { + LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT => InputDeviceAccelProfile::Flat, + LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive, + _ => return Err(JayInputError::UnknownAccelerationProfile(req.profile)), + }; + dev.device.set_accel_profile(profile); + Ok(()) + }) + } + + fn set_accel_speed(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetAccelSpeed = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + dev.device.set_accel_speed(req.speed); + Ok(()) + }) + } + + fn set_tap_enabled(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetTapEnabled = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + dev.device.set_tap_enabled(req.enabled != 0); + Ok(()) + }) + } + + fn set_tap_drag_enabled(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetTapDragEnabled = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + dev.device.set_drag_enabled(req.enabled != 0); + Ok(()) + }) + } + + fn set_tap_drag_lock_enabled(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetTapDragEnabled = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + dev.device.set_drag_lock_enabled(req.enabled != 0); + Ok(()) + }) + } + + fn set_left_handed(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetLeftHanded = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + dev.device.set_left_handed(req.enabled != 0); + Ok(()) + }) + } + + fn set_natural_scrolling(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetNaturalScrolling = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + dev.device.set_natural_scrolling_enabled(req.enabled != 0); + Ok(()) + }) + } + + fn set_px_per_wheel_scroll(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetPxPerWheelScroll = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + dev.px_per_scroll_wheel.set(req.px); + Ok(()) + }) + } + + fn set_transform_matrix(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetTransformMatrix = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + dev.device + .set_transform_matrix([[req.m11, req.m12], [req.m21, req.m22]]); + Ok(()) + }) + } + + fn set_cursor_size(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: SetCursorSize = self.client.parse(self, parser)?; + self.or_error(|| { + let seat = self.seat(req.seat)?; + seat.set_cursor_size(req.size); + Ok(()) + }) + } + + fn attach(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: Attach = self.client.parse(self, parser)?; + self.or_error(|| { + let seat = self.seat(req.seat)?; + let dev = self.device(req.id)?; + dev.seat.set(Some(seat)); + Ok(()) + }) + } + + fn detach(&self, parser: MsgParser<'_, '_>) -> Result<(), JayInputError> { + let req: Detach = self.client.parse(self, parser)?; + self.or_error(|| { + let dev = self.device(req.id)?; + dev.seat.set(None); + Ok(()) + }) + } +} + +object_base! { + self = JayInput; + + DESTROY => destroy, + GET_ALL => get_all, + SET_REPEAT_RATE => set_repeat_rate, + SET_KEYMAP => set_keymap, + USE_HARDWARE_CURSOR => use_hardware_cursor, + GET_KEYMAP => get_keymap, + SET_ACCEL_PROFILE => set_accel_profile, + SET_ACCEL_SPEED => set_accel_speed, + SET_TAP_ENABLED => set_tap_enabled, + SET_TAP_DRAG_ENABLED => set_tap_drag_enabled, + SET_TAP_DRAG_LOCK_ENABLED => set_tap_drag_lock_enabled, + SET_LEFT_HANDED => set_left_handed, + SET_NATURAL_SCROLLING => set_natural_scrolling, + SET_PX_PER_WHEEL_SCROLL => set_px_per_wheel_scroll, + SET_TRANSFORM_MATRIX => set_transform_matrix, + SET_CURSOR_SIZE => set_cursor_size, + ATTACH => attach, + DETACH => detach, + GET_SEAT => get_seat, + GET_DEVICE => get_device, +} + +impl Object for JayInput {} + +simple_add_obj!(JayInput); + +#[derive(Debug, Error)] +pub enum JayInputError { + #[error("Parsing failed")] + MsgParserError(Box), + #[error(transparent)] + ClientError(Box), + #[error("There is no seat called {0}")] + SeatDoesNotExist(String), + #[error("There is no device with id {0}")] + DeviceDoesNotExist(u32), + #[error("There is no acceleration profile with id {0}")] + UnknownAccelerationProfile(i32), + #[error("Repeat rate must not be negative")] + NegativeRepeatRate, + #[error("Repeat delay must not be negative")] + NegativeRepeatDelay, + #[error("Could not access client memory")] + ClientMemError(#[from] ClientMemError), + #[error("Could not parse keymap")] + XkbCommonError(#[from] XkbCommonError), +} +efrom!(JayInputError, MsgParserError); +efrom!(JayInputError, ClientError); diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 5c036ed9..d027602e 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -231,6 +231,10 @@ impl WlSeatGlobal { slf } + pub fn keymap(&self) -> Rc { + self.kb_map.get() + } + pub fn toplevel_drag(&self) -> Option> { self.pointer_owner.toplevel_drag() } diff --git a/src/ifs/wl_shm_pool.rs b/src/ifs/wl_shm_pool.rs index 8d512774..e4124c4d 100644 --- a/src/ifs/wl_shm_pool.rs +++ b/src/ifs/wl_shm_pool.rs @@ -35,7 +35,7 @@ impl WlShmPool { Ok(Self { id, client: client.clone(), - mem: CloneCell::new(Rc::new(ClientMem::new(fd.raw(), len)?)), + mem: CloneCell::new(Rc::new(ClientMem::new(fd.raw(), len, false)?)), fd, tracker: Default::default(), }) @@ -80,8 +80,11 @@ impl WlShmPool { if (req.size as usize) < self.mem.get().len() { return Err(WlShmPoolError::CannotShrink); } - self.mem - .set(Rc::new(ClientMem::new(self.fd.raw(), req.size as usize)?)); + self.mem.set(Rc::new(ClientMem::new( + self.fd.raw(), + req.size as usize, + false, + )?)); Ok(()) } } diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index b60d380c..00a2d219 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -492,6 +492,10 @@ impl InputDevice for T { self.common().name.clone() } + fn dev_t(&self) -> Option { + None + } + fn set_tap_enabled(&self, enabled: bool) { ::set_tap_enabled(self, enabled) } diff --git a/src/libinput/device.rs b/src/libinput/device.rs index b12f04bf..73eb1965 100644 --- a/src/libinput/device.rs +++ b/src/libinput/device.rs @@ -7,13 +7,19 @@ use { LIBINPUT_CONFIG_TAP_DISABLED, LIBINPUT_CONFIG_TAP_ENABLED, }, sys::{ - libinput_device, libinput_device_config_accel_set_profile, - libinput_device_config_accel_set_speed, libinput_device_config_left_handed_set, + libinput_device, libinput_device_config_accel_get_profile, + libinput_device_config_accel_get_speed, libinput_device_config_accel_is_available, + libinput_device_config_accel_set_profile, libinput_device_config_accel_set_speed, + libinput_device_config_left_handed_get, + libinput_device_config_left_handed_is_available, + libinput_device_config_left_handed_set, libinput_device_config_scroll_get_natural_scroll_enabled, + libinput_device_config_scroll_has_natural_scroll, libinput_device_config_scroll_set_natural_scroll_enabled, libinput_device_config_tap_get_drag_enabled, libinput_device_config_tap_get_drag_lock_enabled, - libinput_device_config_tap_get_enabled, libinput_device_config_tap_set_drag_enabled, + libinput_device_config_tap_get_enabled, libinput_device_config_tap_get_finger_count, + libinput_device_config_tap_set_drag_enabled, libinput_device_config_tap_set_drag_lock_enabled, libinput_device_config_tap_set_enabled, libinput_device_get_name, libinput_device_get_user_data, libinput_device_has_capability, @@ -64,12 +70,32 @@ impl<'a> LibInputDevice<'a> { res != 0 } + pub fn left_handed_available(&self) -> bool { + unsafe { libinput_device_config_left_handed_is_available(self.dev) != 0 } + } + + pub fn left_handed(&self) -> bool { + unsafe { libinput_device_config_left_handed_get(self.dev) != 0 } + } + pub fn set_left_handed(&self, left_handed: bool) { unsafe { libinput_device_config_left_handed_set(self.dev, left_handed as _); } } + pub fn accel_available(&self) -> bool { + unsafe { libinput_device_config_accel_is_available(self.dev) != 0 } + } + + pub fn accel_profile(&self) -> AccelProfile { + unsafe { AccelProfile(libinput_device_config_accel_get_profile(self.dev)) } + } + + pub fn accel_speed(&self) -> f64 { + unsafe { libinput_device_config_accel_get_speed(self.dev) } + } + pub fn set_accel_profile(&self, profile: AccelProfile) { unsafe { libinput_device_config_accel_set_profile(self.dev, profile.raw() as _); @@ -99,7 +125,10 @@ impl<'a> LibInputDevice<'a> { } } - #[allow(dead_code)] + pub fn tap_available(&self) -> bool { + unsafe { libinput_device_config_tap_get_finger_count(self.dev) != 0 } + } + pub fn tap_enabled(&self) -> bool { let enabled = unsafe { ConfigTapState(libinput_device_config_tap_get_enabled(self.dev)) }; match enabled { @@ -158,6 +187,10 @@ impl<'a> LibInputDevice<'a> { pub fn natural_scrolling_enabled(&self) -> bool { unsafe { libinput_device_config_scroll_get_natural_scroll_enabled(self.dev) != 0 } } + + pub fn has_natural_scrolling(&self) -> bool { + unsafe { libinput_device_config_scroll_has_natural_scroll(self.dev) != 0 } + } } impl RegisteredDevice { diff --git a/src/libinput/sys.rs b/src/libinput/sys.rs index acd9dc7b..77e90a1e 100644 --- a/src/libinput/sys.rs +++ b/src/libinput/sys.rs @@ -41,19 +41,29 @@ extern "C" { device: *mut libinput_device, cap: libinput_device_capability, ) -> c::c_int; + pub fn libinput_device_config_left_handed_is_available( + device: *mut libinput_device, + ) -> c::c_int; + pub fn libinput_device_config_left_handed_get(device: *mut libinput_device) -> c::c_int; pub fn libinput_device_config_left_handed_set( device: *mut libinput_device, left_handed: c::c_int, ) -> libinput_config_status; + pub fn libinput_device_config_accel_is_available(device: *mut libinput_device) -> c::c_int; + pub fn libinput_device_config_accel_get_profile( + device: *mut libinput_device, + ) -> libinput_config_accel_profile; pub fn libinput_device_config_accel_set_profile( device: *mut libinput_device, profile: libinput_config_accel_profile, ) -> libinput_config_status; + pub fn libinput_device_config_accel_get_speed(device: *mut libinput_device) -> f64; pub fn libinput_device_config_accel_set_speed( device: *mut libinput_device, speed: f64, ) -> libinput_config_status; pub fn libinput_device_get_name(device: *mut libinput_device) -> *const c::c_char; + pub fn libinput_device_config_tap_get_finger_count(device: *mut libinput_device) -> c::c_int; pub fn libinput_device_config_tap_set_enabled( device: *mut libinput_device, enable: libinput_config_tap_state, @@ -82,6 +92,9 @@ extern "C" { pub fn libinput_device_config_scroll_get_natural_scroll_enabled( device: *mut libinput_device, ) -> c::c_int; + pub fn libinput_device_config_scroll_has_natural_scroll( + device: *mut libinput_device, + ) -> c::c_int; pub fn libinput_event_destroy(event: *mut libinput_event); pub fn libinput_event_get_type(event: *mut libinput_event) -> libinput_event_type; diff --git a/src/macros.rs b/src/macros.rs index d1cdfe42..f570d831 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -257,7 +257,7 @@ macro_rules! linear_ids { macro_rules! cenum { ($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => { #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub struct $name(pub(super) i32); + pub struct $name(pub i32); impl $name { pub fn raw(self) -> i32 { diff --git a/src/state.rs b/src/state.rs index ee6ae0a2..31062e8b 100644 --- a/src/state.rs +++ b/src/state.rs @@ -215,6 +215,8 @@ pub struct InputDeviceData { pub id: InputDeviceId, pub data: Rc, pub async_event: Rc, + pub syspath: Option, + pub devnode: Option, } pub struct DeviceHandlerData { diff --git a/src/tasks.rs b/src/tasks.rs index 7df2c4fa..ccaf6812 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -5,6 +5,7 @@ mod hardware_cursor; mod idle; mod input_device; mod slow_clients; +mod udev_utils; use { crate::{ diff --git a/src/tasks/drmdev.rs b/src/tasks/drmdev.rs index f5c58ff3..993c4aee 100644 --- a/src/tasks/drmdev.rs +++ b/src/tasks/drmdev.rs @@ -2,78 +2,24 @@ use { crate::{ backend::{BackendDrmDevice, DrmDeviceId, DrmEvent}, state::{DrmDevData, State}, - udev::{Udev, UdevDeviceType}, - utils::{asyncevent::AsyncEvent, errorfmt::ErrorFmt}, + tasks::udev_utils::udev_props, + utils::asyncevent::AsyncEvent, }, - jay_config::PciId, std::{cell::Cell, rc::Rc}, }; pub fn handle(state: &Rc, dev: Rc) { let id = dev.id(); - let mut syspath = None; - let mut devnode = None; - let mut vendor = None; - let mut model = None; - let mut pci_id = None; - 'properties: { - let udev = match Udev::new() { - Ok(udev) => Rc::new(udev), - Err(e) => { - log::error!("Could not create a udev instance: {}", e); - break 'properties; - } - }; - let odev = match udev.create_device_from_devnum(UdevDeviceType::Character, dev.dev_t()) { - Ok(dev) => dev, - Err(e) => { - log::error!("{}", ErrorFmt(e)); - break 'properties; - } - }; - let dev = match odev.parent() { - Ok(dev) => dev, - Err(e) => { - log::error!("{}", ErrorFmt(e)); - break 'properties; - } - }; - syspath = dev.syspath().map(|s| s.to_string_lossy().into_owned()); - vendor = dev.vendor().map(|s| s.to_string_lossy().into_owned()); - model = dev.model().map(|s| s.to_string_lossy().into_owned()); - devnode = odev.devnode().map(|s| s.to_string_lossy().into_owned()); - 'get_pci_id: { - let id = match dev.pci_id() { - Some(id) => id, - _ => break 'get_pci_id, - }; - let id = id.to_string_lossy(); - let colon = match id.find(':') { - Some(pos) => pos, - _ => break 'get_pci_id, - }; - let vendor = &id[..colon]; - let model = &id[colon + 1..]; - let vendor = match u32::from_str_radix(vendor, 16) { - Ok(v) => v, - _ => break 'get_pci_id, - }; - let model = match u32::from_str_radix(model, 16) { - Ok(v) => v, - _ => break 'get_pci_id, - }; - pci_id = Some(PciId { vendor, model }); - } - } + let props = udev_props(dev.dev_t(), 1); let data = Rc::new(DrmDevData { dev: dev.clone(), handler: Cell::new(None), connectors: Default::default(), - syspath, - devnode, - vendor, - model, - pci_id, + syspath: props.syspath, + devnode: props.devnode, + vendor: props.vendor, + model: props.model, + pci_id: props.pci_id, }); let oh = DrvDevHandler { id, diff --git a/src/tasks/input_device.rs b/src/tasks/input_device.rs index 4901eb86..2d7601d5 100644 --- a/src/tasks/input_device.rs +++ b/src/tasks/input_device.rs @@ -3,6 +3,7 @@ use { backend::InputDevice, ifs::wl_seat::PX_PER_SCROLL, state::{DeviceHandlerData, InputDeviceData, State}, + tasks::udev_utils::{udev_props, UdevProps}, utils::asyncevent::AsyncEvent, }, std::{cell::Cell, rc::Rc}, @@ -22,6 +23,10 @@ pub fn handle(state: &Rc, dev: Rc) { ae: ae.clone(), }; let handler = state.eng.spawn(oh.handle()); + let props = match dev.dev_t() { + None => UdevProps::default(), + Some(dev_t) => udev_props(dev_t, 3), + }; state.input_device_handlers.borrow_mut().insert( dev.id(), InputDeviceData { @@ -29,6 +34,8 @@ pub fn handle(state: &Rc, dev: Rc) { id: dev.id(), data, async_event: ae, + syspath: props.syspath, + devnode: props.devnode, }, ); } diff --git a/src/tasks/udev_utils.rs b/src/tasks/udev_utils.rs new file mode 100644 index 00000000..f825254f --- /dev/null +++ b/src/tasks/udev_utils.rs @@ -0,0 +1,72 @@ +use { + crate::{ + udev::{Udev, UdevDeviceType}, + utils::errorfmt::ErrorFmt, + }, + jay_config::PciId, + std::rc::Rc, + uapi::c, +}; + +#[derive(Default, Debug)] +pub struct UdevProps { + pub syspath: Option, + pub devnode: Option, + pub vendor: Option, + pub model: Option, + pub pci_id: Option, +} + +pub fn udev_props(dev_t: c::dev_t, depth: usize) -> UdevProps { + let mut res = UdevProps::default(); + let udev = match Udev::new() { + Ok(udev) => Rc::new(udev), + Err(e) => { + log::error!("Could not create a udev instance: {}", e); + return res; + } + }; + let mut dev = match udev.create_device_from_devnum(UdevDeviceType::Character, dev_t) { + Ok(dev) => dev, + Err(e) => { + log::error!("{}", ErrorFmt(e)); + return res; + } + }; + res.devnode = dev.devnode().map(|s| s.to_string_lossy().into_owned()); + for _ in 0..depth { + dev = match dev.parent() { + Ok(dev) => dev, + Err(e) => { + log::error!("{}", ErrorFmt(e)); + return res; + } + } + } + res.syspath = dev.syspath().map(|s| s.to_string_lossy().into_owned()); + res.vendor = dev.vendor().map(|s| s.to_string_lossy().into_owned()); + res.model = dev.model().map(|s| s.to_string_lossy().into_owned()); + { + let id = match dev.pci_id() { + Some(id) => id, + _ => return res, + }; + let id = id.to_string_lossy(); + let colon = match id.find(':') { + Some(pos) => pos, + _ => return res, + }; + let vendor = &id[..colon]; + let model = &id[colon + 1..]; + let vendor = match u32::from_str_radix(vendor, 16) { + Ok(v) => v, + _ => return res, + }; + let model = match u32::from_str_radix(model, 16) { + Ok(v) => v, + _ => return res, + }; + res.pci_id = Some(PciId { vendor, model }); + } + res +} diff --git a/src/utils.rs b/src/utils.rs index b6ac70e6..db7b8e05 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -35,6 +35,7 @@ pub mod run_toplevel; pub mod scroller; pub mod smallmap; pub mod stack; +pub mod string_ext; pub mod syncqueue; pub mod threshold_counter; pub mod timer; diff --git a/src/utils/string_ext.rs b/src/utils/string_ext.rs new file mode 100644 index 00000000..3dc19eb5 --- /dev/null +++ b/src/utils/string_ext.rs @@ -0,0 +1,11 @@ +use isnt::std_1::primitive::IsntStrExt; + +pub trait StringExt { + fn to_string_if_not_empty(&self) -> Option; +} + +impl StringExt for str { + fn to_string_if_not_empty(&self) -> Option { + self.is_not_empty().then(|| self.to_string()) + } +} diff --git a/src/xkbcommon.rs b/src/xkbcommon.rs index 0fba7212..bbedd6ae 100644 --- a/src/xkbcommon.rs +++ b/src/xkbcommon.rs @@ -148,11 +148,15 @@ impl XkbContext { })) } - pub fn keymap_from_str(&self, s: &str) -> Result, XkbCommonError> { + pub fn keymap_from_str(&self, s: &S) -> Result, XkbCommonError> + where + S: AsRef<[u8]> + ?Sized, + { + let s = s.as_ref(); unsafe { let keymap = xkb_keymap_new_from_buffer( self.context, - s.as_bytes().as_ptr(), + s.as_ptr(), s.len(), XKB_KEYMAP_FORMAT_TEXT_V1.raw(), 0, diff --git a/wire/jay_compositor.txt b/wire/jay_compositor.txt index ac69231f..10fb4d60 100644 --- a/wire/jay_compositor.txt +++ b/wire/jay_compositor.txt @@ -69,6 +69,10 @@ msg get_randr = 16 { id: id(jay_randr), } +msg get_input = 17 { + id: id(jay_input), +} + # events msg client_id = 0 { diff --git a/wire/jay_input.txt b/wire/jay_input.txt new file mode 100644 index 00000000..f083c743 --- /dev/null +++ b/wire/jay_input.txt @@ -0,0 +1,140 @@ +# requests + +msg destroy = 0 { + +} + +msg get_all = 1 { + +} + +msg set_repeat_rate = 2 { + seat: str, + repeat_rate: i32, + repeat_delay: i32, +} + +msg set_keymap = 3 { + seat: str, + keymap: fd, + keymap_len: u32, +} + +msg use_hardware_cursor = 4 { + seat: str, + use_hardware_cursor: u32, +} + +msg get_keymap = 5 { + seat: str, +} + +msg set_accel_profile = 6 { + id: u32, + profile: i32, +} + +msg set_accel_speed = 7 { + id: u32, + speed: pod(f64), +} + +msg set_tap_enabled = 8 { + id: u32, + enabled: u32, +} + +msg set_tap_drag_enabled = 9 { + id: u32, + enabled: u32, +} + +msg set_tap_drag_lock_enabled = 10 { + id: u32, + enabled: u32, +} + +msg set_left_handed = 11 { + id: u32, + enabled: u32, +} + +msg set_natural_scrolling = 12 { + id: u32, + enabled: u32, +} + +msg set_px_per_wheel_scroll = 13 { + id: u32, + px: pod(f64), +} + +msg set_transform_matrix = 14 { + id: u32, + m11: pod(f64), + m12: pod(f64), + m21: pod(f64), + m22: pod(f64), +} + +msg set_cursor_size = 15 { + seat: str, + size: u32, +} + +msg attach = 16 { + id: u32, + seat: str, +} + +msg detach = 17 { + id: u32, +} + +msg get_seat = 18 { + name: str, +} + +msg get_device = 19 { + id: u32, +} + +# events + +msg seat = 0 { + name: str, + repeat_rate: i32, + repeat_delay: i32, + hardware_cursor: u32, +} + +msg input_device = 1 { + id: u32, + name: str, + seat: str, + syspath: str, + devnode: str, + capabilities: array(pod(i32)), + accel_available: u32, + accel_profile: i32, + accel_speed: pod(f64), + tap_available: u32, + tap_enabled: u32, + tap_drag_enabled: u32, + tap_drag_lock_enabled: u32, + left_handed_available: u32, + left_handed: u32, + natural_scrolling_available: u32, + natural_scrolling_enabled: u32, + px_per_wheel_scroll: pod(f64), + transform_matrix: array(pod(u8)), +} + +msg error = 2 { + msg: str, +} + +msg keymap = 3 { + keymap: fd, + keymap_len: u32, +}