1
0
Fork 0
forked from wry/wry

cli: add an input subcommand

This commit is contained in:
Julian Orth 2024-03-12 17:43:26 +01:00
parent 283e438d1b
commit efae1cd16a
19 changed files with 1383 additions and 36 deletions

4
Cargo.lock generated
View file

@ -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",

View file

@ -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"] }

View file

@ -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,
@ -167,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,

View file

@ -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,
@ -454,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,

View file

@ -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(),
}

732
src/cli/input.rs Normal file
View file

@ -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<InputCmd>,
}
#[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<SeatCommand>,
}
#[derive(Args, Debug)]
pub struct DeviceArgs {
/// The ID of the device to modify.
pub device: u32,
#[clap(subcommand)]
pub command: Option<DeviceCommand>,
}
#[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<String>,
}
#[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<String>,
pub syspath: Option<String>,
pub devnode: Option<String>,
pub capabilities: Vec<InputDeviceCapability>,
pub accel_profile: Option<InputDeviceAccelProfile>,
pub accel_speed: Option<f64>,
pub tap_enabled: Option<bool>,
pub tap_drag_enabled: Option<bool>,
pub tap_drag_lock_enabled: Option<bool>,
pub left_handed: Option<bool>,
pub natural_scrolling_enabled: Option<bool>,
pub px_per_wheel_scroll: Option<f64>,
pub transform_matrix: Option<[[f64; 2]; 2]>,
}
#[derive(Clone, Debug, Default)]
struct Data {
seats: Vec<Seat>,
input_device: Vec<InputDevice>,
}
struct Input {
tc: Rc<ToolClient>,
}
impl Input {
async fn run(self: &Rc<Self>, 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<F: Fn(&str) + 'static>(&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<Self>, 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<Self>, 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<Self>, 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<Self>, 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,
_ => "<detached>",
};
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<Self>, 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
}
}

View file

@ -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<Self, ClientMemError> {
pub fn new(fd: i32, len: usize, read_only: bool) -> Result<Self, ClientMemError> {
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<u8>) -> 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 {

View file

@ -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;

View file

@ -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 {}

411
src/ifs/jay_input.rs Normal file
View file

@ -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<Client>,
pub tracker: Tracker<Self>,
}
impl JayInput {
pub fn new(id: JayInputId, client: &Rc<Client>) -> 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<Rc<WlSeatGlobal>, 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<Rc<DeviceHandlerData>, 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<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
#[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);

View file

@ -231,6 +231,10 @@ impl WlSeatGlobal {
slf
}
pub fn keymap(&self) -> Rc<XkbKeymap> {
self.kb_map.get()
}
pub fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>> {
self.pointer_owner.toplevel_drag()
}

View file

@ -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(())
}
}

View file

@ -492,6 +492,10 @@ impl<T: TestInputDevice> InputDevice for T {
self.common().name.clone()
}
fn dev_t(&self) -> Option<c::dev_t> {
None
}
fn set_tap_enabled(&self, enabled: bool) {
<Self as TestInputDevice>::set_tap_enabled(self, enabled)
}

View file

@ -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 {

View file

@ -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;

11
src/utils/string_ext.rs Normal file
View file

@ -0,0 +1,11 @@
use isnt::std_1::primitive::IsntStrExt;
pub trait StringExt {
fn to_string_if_not_empty(&self) -> Option<String>;
}
impl StringExt for str {
fn to_string_if_not_empty(&self) -> Option<String> {
self.is_not_empty().then(|| self.to_string())
}
}

View file

@ -148,11 +148,15 @@ impl XkbContext {
}))
}
pub fn keymap_from_str(&self, s: &str) -> Result<Rc<XkbKeymap>, XkbCommonError> {
pub fn keymap_from_str<S>(&self, s: &S) -> Result<Rc<XkbKeymap>, 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,

View file

@ -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 {

140
wire/jay_input.txt Normal file
View file

@ -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,
}