use { crate::{ backend::{InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod}, cli::{ GlobalArgs, json::{JsonInputData, JsonInputDevice, JsonSeat, jsonl}, }, clientmem::ClientMem, libinput::consts::{ ConfigClickMethod, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS, LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER, LIBINPUT_CONFIG_CLICK_METHOD_NONE, }, tools::tool_client::{Handle, ToolClient, with_tool_client}, utils::{errorfmt::ErrorFmt, static_text::StaticText, string_ext::StringExt}, wire::{JayInputId, jay_compositor, jay_input}, }, clap::{Args, Subcommand, ValueEnum, ValueHint}, isnt::std_1::vec::IsntVecExt, std::{ cell::RefCell, io::{Read, Write, stdin, stdout}, mem, ops::DerefMut, rc::Rc, }, uapi::{OwnedFd, 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), /// Set the keymap from RMLVO names. SetKeymapFromNames(SetKeymapFromNamesArgs), /// Retrieve the keymap. Keymap, /// Configure whether this seat uses the hardware cursor. UseHardwareCursor(UseHardwareCursorArgs), /// Set the size of the cursor. SetCursorSize(SetCursorSizeArgs), /// Configure the simple, XCompose based input method. SimpleIm(SimpleImArgs), } #[derive(Args, Debug, Clone)] pub struct SimpleImArgs { #[clap(subcommand)] pub command: SimpleImCommand, } #[derive(Subcommand, Debug, Clone)] pub enum SimpleImCommand { /// Enable the simple IM. /// /// Even if the IM is enabled, it will not be used if an external IM is running. Enable, /// Disable the simple IM. Disable, /// Reload the simple IM. /// /// This is useful if you change the XCompose files after starting the compositor. Reload, } 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(SetTapEnabledArgs), /// 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), /// Set the keymap of this device. SetKeymap(SetKeymapArgs), /// Set the keymap of this device from RMLVO names. SetKeymapFromNames(SetKeymapFromNamesArgs), /// Retrieve the keymap of this device. Keymap, /// Attach the device to a seat. Attach(AttachArgs), /// Detach the device from its seat. Detach, /// Maps this device to an output. MapToOutput(MapToOutputArgs), /// Removes the mapping from this device to an output. RemoveMapping, /// Set the calibration matrix. SetCalibrationMatrix(SetCalibrationMatrixArgs), /// Set the click method. SetClickMethod(SetClickMethodArgs), /// Set whether the device uses middle button emulation. SetMiddleButtonEmulation(SetMiddleButtonEmulationArgs), } #[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 SetCalibrationMatrixArgs { pub m00: f32, pub m01: f32, pub m02: f32, pub m10: f32, pub m11: f32, pub m12: f32, } #[derive(ValueEnum, Debug, Clone)] pub enum ClickMethod { None, ButtonAreas, Clickfinger, } #[derive(Args, Debug, Clone)] pub struct SetClickMethodArgs { /// The method. pub method: ClickMethod, } #[derive(Args, Debug, Clone)] pub struct SetMiddleButtonEmulationArgs { /// Whether middle button emulation is enabled. #[arg(action = clap::ArgAction::Set)] pub middle_button_emulation: bool, } #[derive(Args, Debug, Clone)] pub struct MapToOutputArgs { /// The output to map to. pub output: String, } #[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. #[clap(value_hint = ValueHint::FilePath)] pub file: Option, } #[derive(Args, Debug, Clone)] pub struct SetKeymapFromNamesArgs { /// The rules file. #[clap(short, long)] pub rules: Option, /// The model name. #[clap(short, long)] pub model: Option, /// The comma-separated list of layouts. #[clap(short, long)] pub layout: Option, /// The comma-separated list of layout variants. #[clap(short, long)] pub variant: Option, /// The comma-separated list of options. #[clap(short, long)] pub options: 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, |tc| async move { let idle = Rc::new(Input { tc: tc.clone() }); idle.run(&global, 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]>, pub output: Option, pub calibration_matrix: Option<[[f32; 3]; 2]>, pub click_method: Option, pub middle_button_emulation_enabled: Option, } #[derive(Clone, Debug, Default)] struct Data { seats: Vec, input_device: Vec, } struct Input { tc: Rc, } impl Input { async fn run(self: &Rc, global: &GlobalArgs, 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(global, input, args).await, InputCmd::Seat(args) => self.seat(global, input, args).await, InputCmd::Device(args) => self.device(global, 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); }); } fn prepare_keymap(&self, a: &SetKeymapArgs) -> (Rc, usize) { 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(); (Rc::new(memfd), map.len()) } async fn handle_keymap(&self, input: JayInputId) -> Vec { let data = Rc::new(RefCell::new(Vec::new())); jay_input::Keymap::handle(&self.tc, input, data.clone(), |d, map| { let len = map.keymap_len as _; let mem = Rc::new(ClientMem::new_private(&map.keymap, len, true, None, None).unwrap()) .offset(0, len); mem.read(d.borrow_mut().deref_mut()).unwrap(); }); self.tc.round_trip().await; data.take() } async fn seat(self: &Rc, global: &GlobalArgs, 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; if global.json { self.print_data_json(data); } else { 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) => { let (memfd, len) = self.prepare_keymap(&a); self.handle_error(input, |e| { eprintln!("Could not set keymap: {}", e); }); tc.send(jay_input::SetKeymap { self_id: input, seat: &args.seat, keymap: memfd, keymap_len: 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); }); tc.send(jay_input::GetKeymap { self_id: input, seat: &args.seat, }); let map = self.handle_keymap(input).await; 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, }); } SeatCommand::SimpleIm(a) => match a.command { SimpleImCommand::Enable | SimpleImCommand::Disable => { self.handle_error(input, |e| { eprintln!("Could not enable/disable the simple IM: {}", e); }); tc.send(jay_input::SetSimpleImEnabled { self_id: input, seat: &args.seat, enabled: matches!(a.command, SimpleImCommand::Enable) as _, }); } SimpleImCommand::Reload => { self.handle_error(input, |e| { eprintln!("Could not reload the simple IM: {}", e); }); tc.send(jay_input::ReloadSimpleIm { self_id: input, seat: &args.seat, }); } }, SeatCommand::SetKeymapFromNames(a) => { self.handle_error(input, |e| { eprintln!("Could not set keymap: {}", e); }); tc.send(jay_input::SetKeymapFromNames { self_id: input, seat: &args.seat, rules: a.rules.as_deref(), model: a.model.as_deref(), layout: a.layout.as_deref(), variant: a.variant.as_deref(), options: a.options.as_deref(), }); } } tc.round_trip().await; } async fn device(self: &Rc, global: &GlobalArgs, 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; if global.json { self.print_data_json(data); } else { 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, }); } DeviceCommand::SetKeymap(a) => { let (memfd, len) = self.prepare_keymap(&a); self.handle_error(input, |e| { eprintln!("Could not set keymap: {}", e); }); tc.send(jay_input::SetDeviceKeymap { self_id: input, id: args.device, keymap: memfd, keymap_len: len as _, }); } DeviceCommand::Keymap => { self.handle_error(input, |e| { eprintln!("Could not retrieve the keymap: {}", e); }); tc.send(jay_input::GetDeviceKeymap { self_id: input, id: args.device, }); let map = self.handle_keymap(input).await; stdout().write_all(&map).unwrap(); } DeviceCommand::MapToOutput(a) => { self.handle_error(input, |e| { eprintln!("Could not map the device to an output: {}", e); }); tc.send(jay_input::MapToOutput { self_id: input, id: args.device, output: Some(&a.output), }); } DeviceCommand::RemoveMapping => { self.handle_error(input, |e| { eprintln!("Could not remove the output mapping: {}", e); }); tc.send(jay_input::MapToOutput { self_id: input, id: args.device, output: None, }); } DeviceCommand::SetCalibrationMatrix(a) => { self.handle_error(input, |e| { eprintln!("Could not modify the calibration matrix: {}", e); }); tc.send(jay_input::SetCalibrationMatrix { self_id: input, id: args.device, m00: a.m00, m01: a.m01, m02: a.m02, m10: a.m10, m11: a.m11, m12: a.m12, }); } DeviceCommand::SetClickMethod(a) => { let method = match a.method { ClickMethod::None => LIBINPUT_CONFIG_CLICK_METHOD_NONE.0, ClickMethod::ButtonAreas => LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS.0, ClickMethod::Clickfinger => LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER.0, }; self.handle_error(input, |e| { eprintln!("Could not set the click method: {}", e); }); tc.send(jay_input::SetClickMethod { self_id: input, id: args.device, method, }); } DeviceCommand::SetMiddleButtonEmulation(a) => { self.handle_error(input, |e| { eprintln!( "Could not modify the middle-button-emulation setting: {}", e ); }); tc.send(jay_input::SetMiddleButtonEmulation { self_id: input, id: args.device, enabled: a.middle_button_emulation as _, }); } DeviceCommand::SetKeymapFromNames(a) => { self.handle_error(input, |e| { eprintln!("Could not set keymap: {}", e); }); tc.send(jay_input::SetDeviceKeymapFromNames { self_id: input, id: args.device, rules: a.rules.as_deref(), model: a.model.as_deref(), layout: a.layout.as_deref(), variant: a.variant.as_deref(), options: a.options.as_deref(), }); } } tc.round_trip().await; } async fn show(self: &Rc, global: &GlobalArgs, input: JayInputId, args: ShowArgs) { self.tc.send(jay_input::GetAll { self_id: input }); let data = self.get(input).await; if global.json { self.print_data_json(data); } else { 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 = device.seat.as_deref().unwrap_or(""); 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 { print!(" "); if !mem::take(&mut first) { print!("| "); } print!("{}", cap.text()); } 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); } if let Some(v) = &device.output { println!("{prefix} mapped to output: {}", v); } if let Some(v) = &device.calibration_matrix { println!("{prefix} calibration matrix: {:?}", v); } if let Some(v) = &device.click_method { let name = match v { InputDeviceClickMethod::None => "none", InputDeviceClickMethod::ButtonAreas => "button-areas", InputDeviceClickMethod::Clickfinger => "clickfinger", }; println!("{prefix} click method: {}", name); } if let Some(v) = &device.middle_button_emulation_enabled { println!("{prefix} middle button emulation: {}", v); } } fn print_data_json(&self, mut data: Data) { data.seats.sort_by(|l, r| l.name.cmp(&r.name)); data.input_device.sort_by_key(|l| l.id); let mut seats = Vec::new(); for seat in &data.seats { let devices = data .input_device .iter() .filter(|c| c.seat.as_ref() == Some(&seat.name)) .map(make_json_device) .collect(); seats.push(JsonSeat { name: &seat.name, repeat_rate: seat.repeat_rate, repeat_delay: seat.repeat_delay, hardware_cursor: seat.hardware_cursor, devices, }); } let detached_devices = data .input_device .iter() .filter(|c| c.seat.is_none()) .map(make_json_device) .collect(); let json = JsonInputData { seats, detached_devices, }; jsonl(&json); } 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(), output: None, calibration_matrix: None, click_method: None, middle_button_emulation_enabled: None, }); }); jay_input::InputDeviceOutput::handle(tc, input, data.clone(), |data, msg| { let mut data = data.borrow_mut(); if let Some(last) = data.input_device.last_mut() { last.output = Some(msg.output.to_string()); } }); jay_input::CalibrationMatrix::handle(tc, input, data.clone(), |data, msg| { let mut data = data.borrow_mut(); if let Some(last) = data.input_device.last_mut() { last.calibration_matrix = Some([[msg.m00, msg.m01, msg.m02], [msg.m10, msg.m11, msg.m12]]); } }); jay_input::ClickMethod::handle(tc, input, data.clone(), |data, msg| { let click_method = match ConfigClickMethod(msg.click_method) { LIBINPUT_CONFIG_CLICK_METHOD_NONE => Some(InputDeviceClickMethod::None), LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS => { Some(InputDeviceClickMethod::ButtonAreas) } LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER => { Some(InputDeviceClickMethod::Clickfinger) } _ => None, }; let mut data = data.borrow_mut(); if let Some(last) = data.input_device.last_mut() { last.click_method = click_method; } }); jay_input::MiddleButtonEmulation::handle(tc, input, data.clone(), |data, msg| { let mut data = data.borrow_mut(); if let Some(last) = data.input_device.last_mut() { last.middle_button_emulation_enabled = Some(msg.middle_button_emulation_enabled != 0); } }); tc.round_trip().await; data.borrow_mut().clone() } } fn make_json_device(device: &InputDevice) -> JsonInputDevice<'_> { JsonInputDevice { input_device_id: device.id, name: &device.name, seat: device.seat.as_deref(), syspath: device.syspath.as_deref(), devnode: device.devnode.as_deref(), capabilities: device.capabilities.iter().map(|c| c.text()).collect(), accel_profile: device.accel_profile.as_ref().map(|v| v.text()), accel_speed: device.accel_speed, tap_enabled: device.tap_enabled, tap_drag_enabled: device.tap_drag_enabled, tap_drag_lock_enabled: device.tap_drag_lock_enabled, left_handed: device.left_handed, natural_scrolling: device.natural_scrolling_enabled, px_per_wheel_scroll: device.px_per_wheel_scroll, transform_matrix: device.transform_matrix, output: device.output.as_deref(), calibration_matrix: device.calibration_matrix, click_method: device.click_method.as_ref().map(|v| v.text()), middle_button_emulation: device.middle_button_emulation_enabled, } }