From 56467755a8d44cca0cb1bc03cb656d44a3fd8fc6 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 29 Aug 2023 13:49:41 -0700 Subject: [PATCH] `cosmic-comp-config` crate, and default input config This adds a `input-default` setting, which is used for input settings if a device isn't set in `input-devices`. More awkwardly, it also adds an `input-touchpad` setting, which is used instead of `input-default` for touchpad devices, so we can separate mouse and touchpad settings even though they use the same capability and settings. This no longer sets the input config, and only reads it. If we add a UI for per-device config, we'll need some IPC mechanism to list connected devices. (Assuming cosmic-settings can't use libinput directly for that.) This moves `InputConfig` and `XkbConfig` to a new `cosmic-comp-config` crate, so they can be used in `cosmic-settings`. --- Cargo.lock | 10 + Cargo.toml | 6 + cosmic-comp-config/Cargo.toml | 8 + cosmic-comp-config/src/input.rs | 209 ++++++++++++++ cosmic-comp-config/src/lib.rs | 26 ++ src/config/input_config.rs | 496 +++++++++++++++----------------- src/config/mod.rs | 70 +++-- src/config/types.rs | 175 ----------- src/input/mod.rs | 4 +- 9 files changed, 536 insertions(+), 468 deletions(-) create mode 100644 cosmic-comp-config/Cargo.toml create mode 100644 cosmic-comp-config/src/input.rs create mode 100644 cosmic-comp-config/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index e51b8f51..4e5359f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,6 +481,7 @@ dependencies = [ "bitflags 1.3.2", "bytemuck", "calloop", + "cosmic-comp-config", "cosmic-config", "cosmic-protocols", "edid-rs", @@ -522,6 +523,14 @@ dependencies = [ "xkbcommon 0.4.1", ] +[[package]] +name = "cosmic-comp-config" +version = "0.1.0" +dependencies = [ + "input", + "serde", +] + [[package]] name = "cosmic-config" version = "0.1.0" @@ -1981,6 +1990,7 @@ dependencies = [ "input-sys", "io-lifetimes", "libc", + "log", "udev", ] diff --git a/Cargo.toml b/Cargo.toml index 495b9459..8d7bec91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,11 @@ edition = "2021" license = "GPL-3.0-only" authors = ["Victoria Brekenfeld"] +[workspace] +members = [ + "cosmic-comp-config" +] + [dependencies] apply = "0.3.0" anyhow = { version = "1.0.51", features = ["backtrace"] } @@ -30,6 +35,7 @@ ron = "0.7" libsystemd = { version = "0.5", optional = true } wayland-backend = "0.1.0" wayland-scanner = "0.30.0" +cosmic-comp-config = { path = "cosmic-comp-config" } cosmic-config = { git = "https://github.com/pop-os/libcosmic/", rev = "4895b0c", features = ["calloop"] } cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"] } libcosmic = { git = "https://github.com/pop-os/libcosmic/", rev = "4895b0c", default-features = false } diff --git a/cosmic-comp-config/Cargo.toml b/cosmic-comp-config/Cargo.toml new file mode 100644 index 00000000..7f4f6588 --- /dev/null +++ b/cosmic-comp-config/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "cosmic-comp-config" +version = "0.1.0" +edition = "2021" + +[dependencies] +input = "0.8.3" +serde = { version = "1", features = ["derive"] } diff --git a/cosmic-comp-config/src/input.rs b/cosmic-comp-config/src/input.rs new file mode 100644 index 00000000..1d15abb0 --- /dev/null +++ b/cosmic-comp-config/src/input.rs @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-3.0-only + +#![allow(non_snake_case)] + +use input::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct InputConfig { + pub state: DeviceState, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub acceleration: Option, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub calibration: Option<[f32; 6]>, + #[serde(with = "ClickMethodDef")] + #[serde(skip_serializing_if = "Option::is_none", default)] + pub click_method: Option, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub disable_while_typing: Option, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub left_handed: Option, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub middle_button_emulation: Option, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub rotation_angle: Option, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub scroll_config: Option, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub tap_config: Option, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct AccelConfig { + #[serde(with = "AccelProfileDef")] + pub profile: Option, + pub speed: f64, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ScrollConfig { + #[serde(with = "ScrollMethodDef")] + pub method: Option, + pub natural_scroll: Option, + pub scroll_button: Option, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub enum DeviceState { + Enabled, + Disabled, + DisabledOnExternalMouse, +} + +impl Default for DeviceState { + fn default() -> Self { + Self::Enabled + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct TapConfig { + pub enabled: bool, + #[serde(with = "TapButtonMapDef")] + pub button_map: Option, + pub drag: bool, + pub drag_lock: bool, +} + +mod ClickMethodDef { + use input::ClickMethod as ClickMethodOrig; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + #[derive(Debug, Serialize, Deserialize)] + pub enum ClickMethod { + ButtonAreas, + Clickfinger, + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let o = Option::deserialize(deserializer)?; + Ok(o.map(|x| match x { + ClickMethod::ButtonAreas => ClickMethodOrig::ButtonAreas, + ClickMethod::Clickfinger => ClickMethodOrig::Clickfinger, + })) + } + + pub fn serialize(arg: &Option, ser: S) -> Result + where + S: Serializer, + { + let arg = match arg { + Some(ClickMethodOrig::ButtonAreas) => Some(ClickMethod::ButtonAreas), + Some(ClickMethodOrig::Clickfinger) => Some(ClickMethod::Clickfinger), + Some(_) | None => None, + }; + Option::serialize(&arg, ser) + } +} + +mod AccelProfileDef { + use input::AccelProfile as AccelProfileOrig; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + #[derive(Debug, Serialize, Deserialize)] + enum AccelProfile { + Flat, + Adaptive, + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let o = Option::deserialize(deserializer)?; + Ok(o.map(|x| match x { + AccelProfile::Flat => AccelProfileOrig::Flat, + AccelProfile::Adaptive => AccelProfileOrig::Adaptive, + })) + } + + pub fn serialize(arg: &Option, ser: S) -> Result + where + S: Serializer, + { + let arg = match arg { + Some(AccelProfileOrig::Flat) => Some(AccelProfile::Flat), + Some(AccelProfileOrig::Adaptive) => Some(AccelProfile::Adaptive), + Some(_) | None => None, + }; + Option::serialize(&arg, ser) + } +} + +mod ScrollMethodDef { + use input::ScrollMethod as ScrollMethodOrig; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + #[derive(Debug, Serialize, Deserialize)] + pub enum ScrollMethod { + NoScroll, + TwoFinger, + Edge, + OnButtonDown, + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let o = Option::deserialize(deserializer)?; + Ok(o.map(|x| match x { + ScrollMethod::NoScroll => ScrollMethodOrig::NoScroll, + ScrollMethod::TwoFinger => ScrollMethodOrig::TwoFinger, + ScrollMethod::Edge => ScrollMethodOrig::Edge, + ScrollMethod::OnButtonDown => ScrollMethodOrig::OnButtonDown, + })) + } + + pub fn serialize(arg: &Option, ser: S) -> Result + where + S: Serializer, + { + let arg = match arg { + Some(ScrollMethodOrig::NoScroll) => Some(ScrollMethod::NoScroll), + Some(ScrollMethodOrig::TwoFinger) => Some(ScrollMethod::TwoFinger), + Some(ScrollMethodOrig::Edge) => Some(ScrollMethod::Edge), + Some(ScrollMethodOrig::OnButtonDown) => Some(ScrollMethod::OnButtonDown), + Some(_) | None => None, + }; + Option::serialize(&arg, ser) + } +} + +mod TapButtonMapDef { + use input::TapButtonMap as TapButtonMapOrig; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + #[derive(Debug, Serialize, Deserialize)] + pub enum TapButtonMap { + LeftRightMiddle, + LeftMiddleRight, + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let o = Option::deserialize(deserializer)?; + Ok(o.map(|x| match x { + TapButtonMap::LeftRightMiddle => TapButtonMapOrig::LeftRightMiddle, + TapButtonMap::LeftMiddleRight => TapButtonMapOrig::LeftMiddleRight, + })) + } + + pub fn serialize(arg: &Option, ser: S) -> Result + where + S: Serializer, + { + let arg = match arg { + Some(TapButtonMapOrig::LeftRightMiddle) => Some(TapButtonMap::LeftRightMiddle), + Some(TapButtonMapOrig::LeftMiddleRight) => Some(TapButtonMap::LeftMiddleRight), + Some(_) | None => None, + }; + Option::serialize(&arg, ser) + } +} diff --git a/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs new file mode 100644 index 00000000..b65c114b --- /dev/null +++ b/cosmic-comp-config/src/lib.rs @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use serde::{Deserialize, Serialize}; + +pub mod input; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct XkbConfig { + pub rules: String, + pub model: String, + pub layout: String, + pub variant: String, + pub options: Option, +} + +impl Default for XkbConfig { + fn default() -> XkbConfig { + XkbConfig { + rules: String::new(), + model: String::new(), + layout: String::new(), + variant: String::new(), + options: None, + } + } +} diff --git a/src/config/input_config.rs b/src/config/input_config.rs index 86a01140..e979ec51 100644 --- a/src/config/input_config.rs +++ b/src/config/input_config.rs @@ -1,4 +1,3 @@ -use serde::{Deserialize, Serialize}; pub use smithay::{ backend::input::KeyState, input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState}, @@ -14,302 +13,269 @@ pub use smithay::{ }; use tracing::warn; -use super::types::*; +use cosmic_comp_config::input::*; -#[derive(Debug, Deserialize, Serialize)] -pub struct InputConfig { - state: DeviceState, - #[serde(skip_serializing_if = "Option::is_none", default)] - acceleration: Option, - #[serde(skip_serializing_if = "Option::is_none", default)] - calibration: Option<[f32; 6]>, - #[serde(with = "ClickMethodDef")] - #[serde(skip_serializing_if = "Option::is_none", default)] - click_method: Option, - #[serde(skip_serializing_if = "Option::is_none", default)] - disable_while_typing: Option, - #[serde(skip_serializing_if = "Option::is_none", default)] - left_handed: Option, - #[serde(skip_serializing_if = "Option::is_none", default)] - middle_button_emulation: Option, - #[serde(skip_serializing_if = "Option::is_none", default)] - rotation_angle: Option, - #[serde(skip_serializing_if = "Option::is_none", default)] - scroll_config: Option, - #[serde(skip_serializing_if = "Option::is_none", default)] - tap_config: Option, +#[allow(dead_code)] +pub fn for_device(device: &InputDevice) -> InputConfig { + InputConfig { + state: match device.config_send_events_mode() { + x if x.contains(SendEventsMode::ENABLED) => DeviceState::Enabled, + x if x.contains(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE) => { + DeviceState::DisabledOnExternalMouse + } + x if x.contains(SendEventsMode::DISABLED) => DeviceState::Disabled, + _ => DeviceState::Disabled, + }, + acceleration: if device.config_accel_is_available() { + Some(AccelConfig { + profile: device.config_accel_profile(), + speed: device.config_accel_speed(), + }) + } else { + None + }, + calibration: device.config_calibration_matrix(), + click_method: device.config_click_method(), + disable_while_typing: if device.config_dwt_is_available() { + Some(device.config_dwt_enabled()) + } else { + None + }, + left_handed: if device.config_left_handed_is_available() { + Some(device.config_left_handed()) + } else { + None + }, + middle_button_emulation: if device.config_middle_emulation_is_available() { + Some(device.config_middle_emulation_enabled()) + } else { + None + }, + rotation_angle: if device.config_rotation_is_available() { + Some(device.config_rotation_angle()) + } else { + None + }, + scroll_config: if device + .config_scroll_methods() + .iter() + .any(|x| *x != ScrollMethod::NoScroll) + { + Some(ScrollConfig { + method: device.config_scroll_method(), + natural_scroll: if device.config_scroll_has_natural_scroll() { + Some(device.config_scroll_natural_scroll_enabled()) + } else { + None + }, + scroll_button: if device.config_scroll_method() == Some(ScrollMethod::OnButtonDown) + { + Some(device.config_scroll_button()) + } else { + None + }, + }) + } else { + None + }, + tap_config: if device.config_tap_finger_count() > 0 { + Some(TapConfig { + enabled: device.config_tap_enabled(), + button_map: device.config_tap_button_map(), + drag: device.config_tap_drag_enabled(), + drag_lock: device.config_tap_drag_lock_enabled(), + }) + } else { + None + }, + } } -#[derive(Debug, Deserialize, Serialize)] -pub struct AccelConfig { - #[serde(with = "AccelProfileDef")] - profile: Option, - speed: f64, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ScrollConfig { - #[serde(with = "ScrollMethodDef")] - method: Option, - natural_scroll: Option, - scroll_button: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum DeviceState { - Enabled, - Disabled, - DisabledOnExternalMouse, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct TapConfig { - enabled: bool, - #[serde(with = "TapButtonMapDef")] - button_map: Option, - drag: bool, - drag_lock: bool, -} - -impl InputConfig { - pub fn for_device(device: &InputDevice) -> Self { - InputConfig { - state: match device.config_send_events_mode() { - x if x.contains(SendEventsMode::ENABLED) => DeviceState::Enabled, - x if x.contains(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE) => { - DeviceState::DisabledOnExternalMouse - } - x if x.contains(SendEventsMode::DISABLED) => DeviceState::Disabled, - _ => DeviceState::Disabled, - }, - acceleration: if device.config_accel_is_available() { - Some(AccelConfig { - profile: device.config_accel_profile(), - speed: device.config_accel_speed(), - }) - } else { - None - }, - calibration: device.config_calibration_matrix(), - click_method: device.config_click_method(), - disable_while_typing: if device.config_dwt_is_available() { - Some(device.config_dwt_enabled()) - } else { - None - }, - left_handed: if device.config_left_handed_is_available() { - Some(device.config_left_handed()) - } else { - None - }, - middle_button_emulation: if device.config_middle_emulation_is_available() { - Some(device.config_middle_emulation_enabled()) - } else { - None - }, - rotation_angle: if device.config_rotation_is_available() { - Some(device.config_rotation_angle()) - } else { - None - }, - scroll_config: if device - .config_scroll_methods() - .iter() - .any(|x| *x != ScrollMethod::NoScroll) - { - Some(ScrollConfig { - method: device.config_scroll_method(), - natural_scroll: if device.config_scroll_has_natural_scroll() { - Some(device.config_scroll_natural_scroll_enabled()) - } else { - None - }, - scroll_button: if device.config_scroll_method() - == Some(ScrollMethod::OnButtonDown) - { - Some(device.config_scroll_button()) - } else { - None - }, - }) - } else { - None - }, - tap_config: if device.config_tap_finger_count() > 0 { - Some(TapConfig { - enabled: device.config_tap_enabled(), - button_map: device.config_tap_button_map(), - drag: device.config_tap_drag_enabled(), - drag_lock: device.config_tap_drag_lock_enabled(), - }) - } else { - None - }, +fn get_config<'a, T: 'a, F: Fn(&'a InputConfig) -> Option>( + device_config: Option<&'a InputConfig>, + default_config: &'a InputConfig, + f: F, +) -> Option { + if let Some(device_config) = device_config { + if let Some(setting) = f(device_config) { + return Some(setting); } } + f(default_config) +} - pub fn update_device(&self, device: &mut InputDevice) { - if let Err(err) = match self.state { - DeviceState::Enabled => device.config_send_events_set_mode(SendEventsMode::ENABLED), - DeviceState::Disabled => device.config_send_events_set_mode(SendEventsMode::DISABLED), - DeviceState::DisabledOnExternalMouse => { - device.config_send_events_set_mode(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE) +pub fn update_device( + device: &mut InputDevice, + device_config: Option<&InputConfig>, + default_config: &InputConfig, +) { + macro_rules! config { + ($f:expr) => { + get_config(device_config, default_config, $f) + }; + } + + let state = device_config.unwrap_or(default_config).state; + if let Err(err) = match state { + DeviceState::Enabled => device.config_send_events_set_mode(SendEventsMode::ENABLED), + DeviceState::Disabled => device.config_send_events_set_mode(SendEventsMode::DISABLED), + DeviceState::DisabledOnExternalMouse => { + device.config_send_events_set_mode(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE) + } + } { + warn!( + ?err, + "Failed to apply mode {:?} for device {:?}.", + state, + device.name(), + ); + } + if let Some(accel) = config!(|x| x.acceleration.as_ref()) { + if let Some(profile) = accel.profile { + if let Err(err) = device.config_accel_set_profile(profile) { + warn!( + ?err, + "Failed to apply acceleration profile {:?} for device {:?}.", + profile, + device.name(), + ); } - } { + } + if let Err(err) = device.config_accel_set_speed(accel.speed) { warn!( ?err, - "Failed to apply mode {:?} for device {:?}.", - self.state, + "Failed to apply acceleration speed {:?} for device {:?}.", + accel.speed, device.name(), ); } - if let Some(accel) = self.acceleration.as_ref() { - if let Some(profile) = accel.profile { - if let Err(err) = device.config_accel_set_profile(profile) { - warn!( - ?err, - "Failed to apply acceleration profile {:?} for device {:?}.", - profile, - device.name(), - ); - } - } - if let Err(err) = device.config_accel_set_speed(accel.speed) { - warn!( - ?err, - "Failed to apply acceleration speed {:?} for device {:?}.", - accel.speed, - device.name(), - ); - } + } + if let Some(matrix) = config!(|x| x.calibration) { + if let Err(err) = device.config_calibration_set_matrix(matrix) { + warn!( + ?err, + "Failed to apply calibration matrix {:?} for device {:?}.", + matrix, + device.name(), + ); } - if let Some(matrix) = self.calibration { - if let Err(err) = device.config_calibration_set_matrix(matrix) { - warn!( - ?err, - "Failed to apply calibration matrix {:?} for device {:?}.", - matrix, - device.name(), - ); - } + } + if let Some(method) = config!(|x| x.click_method) { + if let Err(err) = device.config_click_set_method(method) { + warn!( + ?err, + "Failed to apply click method {:?} for device {:?}.", + method, + device.name(), + ); } - if let Some(method) = self.click_method { - if let Err(err) = device.config_click_set_method(method) { + } + if let Some(dwt) = config!(|x| x.disable_while_typing) { + if let Err(err) = device.config_dwt_set_enabled(dwt) { + warn!( + ?err, + "Failed to apply disable-while-typing {:?} for device {:?}.", + dwt, + device.name(), + ); + } + } + if let Some(left) = config!(|x| x.left_handed) { + if let Err(err) = device.config_left_handed_set(left) { + warn!( + ?err, + "Failed to apply left-handed {:?} for device {:?}.", + left, + device.name(), + ); + } + } + if let Some(middle) = config!(|x| x.middle_button_emulation) { + if let Err(err) = device.config_middle_emulation_set_enabled(middle) { + warn!( + ?err, + "Failed to apply middle-button-emulation {:?} for device {:?}.", + middle, + device.name(), + ); + } + } + if let Some(angle) = config!(|x| x.rotation_angle) { + if let Err(err) = device.config_rotation_set_angle(angle) { + warn!( + ?err, + "Failed to apply rotation-angle {:?} for device {:?}", + angle, + device.name(), + ); + } + } + if let Some(scroll) = config!(|x| x.scroll_config.as_ref()) { + if let Some(method) = scroll.method { + if let Err(err) = device.config_scroll_set_method(method) { warn!( ?err, - "Failed to apply click method {:?} for device {:?}.", + "Failed to apply scroll method {:?} for device {:?}.", method, device.name(), ); } } - if let Some(dwt) = self.disable_while_typing { - if let Err(err) = device.config_dwt_set_enabled(dwt) { + if let Some(natural) = scroll.natural_scroll { + if let Err(err) = device.config_scroll_set_natural_scroll_enabled(natural) { warn!( ?err, - "Failed to apply disable-while-typing {:?} for device {:?}.", - dwt, + "Failed to apply natural scrolling {:?} for device {:?}.", + natural, device.name(), ); } } - if let Some(left) = self.left_handed { - if let Err(err) = device.config_left_handed_set(left) { + if let Some(button) = scroll.scroll_button { + if let Err(err) = device.config_scroll_set_button(button) { warn!( ?err, - "Failed to apply left-handed {:?} for device {:?}.", - left, - device.name(), - ); - } - } - if let Some(middle) = self.middle_button_emulation { - if let Err(err) = device.config_middle_emulation_set_enabled(middle) { - warn!( - ?err, - "Failed to apply middle-button-emulation {:?} for device {:?}.", - middle, - device.name(), - ); - } - } - if let Some(angle) = self.rotation_angle { - if let Err(err) = device.config_rotation_set_angle(angle) { - warn!( - ?err, - "Failed to apply rotation-angle {:?} for device {:?}", - angle, - device.name(), - ); - } - } - if let Some(scroll) = self.scroll_config.as_ref() { - if let Some(method) = scroll.method { - if let Err(err) = device.config_scroll_set_method(method) { - warn!( - ?err, - "Failed to apply scroll method {:?} for device {:?}.", - method, - device.name(), - ); - } - } - if let Some(natural) = scroll.natural_scroll { - if let Err(err) = device.config_scroll_set_natural_scroll_enabled(natural) { - warn!( - ?err, - "Failed to apply natural scrolling {:?} for device {:?}.", - natural, - device.name(), - ); - } - } - if let Some(button) = scroll.scroll_button { - if let Err(err) = device.config_scroll_set_button(button) { - warn!( - ?err, - "Failed to apply scroll button {:?} for device {:?}.", - button, - device.name(), - ); - } - } - } - if let Some(tap) = self.tap_config.as_ref() { - if let Err(err) = device.config_tap_set_enabled(tap.enabled) { - warn!( - ?err, - "Failed to apply tap-to-click {:?} for device {:?}.", - tap.enabled, - device.name(), - ); - } - if let Some(button_map) = tap.button_map { - if let Err(err) = device.config_tap_set_button_map(button_map) { - warn!( - ?err, - "Failed to apply button map {:?} for device {:?}.", - button_map, - device.name(), - ); - } - } - if let Err(err) = device.config_tap_set_drag_enabled(tap.drag) { - warn!( - ?err, - "Failed to apply tap-drag {:?} for device {:?}.", - tap.drag, - device.name(), - ); - } - if let Err(err) = device.config_tap_set_drag_lock_enabled(tap.drag_lock) { - warn!( - ?err, - "Failed to apply tap-drag-lock {:?} for device {:?}.", - tap.drag_lock, + "Failed to apply scroll button {:?} for device {:?}.", + button, device.name(), ); } } } + if let Some(tap) = config!(|x| x.tap_config.as_ref()) { + if let Err(err) = device.config_tap_set_enabled(tap.enabled) { + warn!( + ?err, + "Failed to apply tap-to-click {:?} for device {:?}.", + tap.enabled, + device.name(), + ); + } + if let Some(button_map) = tap.button_map { + if let Err(err) = device.config_tap_set_button_map(button_map) { + warn!( + ?err, + "Failed to apply button map {:?} for device {:?}.", + button_map, + device.name(), + ); + } + } + if let Err(err) = device.config_tap_set_drag_enabled(tap.drag) { + warn!( + ?err, + "Failed to apply tap-drag {:?} for device {:?}.", + tap.drag, + device.name(), + ); + } + if let Err(err) = device.config_tap_set_drag_lock_enabled(tap.drag_lock) { + warn!( + ?err, + "Failed to apply tap-drag-lock {:?} for device {:?}.", + tap.drag_lock, + device.name(), + ); + } + } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 81dd8da4..826bf220 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -5,7 +5,7 @@ use crate::{ state::{BackendData, Data, State}, wayland::protocols::output_configuration::OutputConfigurationState, }; -use cosmic_config::{ConfigGet, ConfigSet}; +use cosmic_config::ConfigGet; use serde::{Deserialize, Serialize}; use smithay::input::Seat; pub use smithay::{ @@ -25,11 +25,11 @@ use std::{cell::RefCell, collections::HashMap, fs::OpenOptions, path::PathBuf}; use tracing::{debug, error, info, warn}; mod input_config; -use input_config::InputConfig; mod key_bindings; pub use key_bindings::{Action, KeyModifier, KeyModifiers, KeyPattern}; mod types; pub use self::types::*; +use cosmic_comp_config::{input::InputConfig, XkbConfig}; #[derive(Debug)] pub struct Config { @@ -37,6 +37,8 @@ pub struct Config { pub dynamic_conf: DynamicConfig, pub config: cosmic_config::Config, pub xkb: XkbConfig, + pub input_default: InputConfig, + pub input_touchpad: InputConfig, pub input_devices: HashMap, } @@ -169,6 +171,8 @@ impl Config { static_conf: Self::load_static(xdg.as_ref()), dynamic_conf: Self::load_dynamic(xdg.as_ref()), xkb: get_config(&config, "xkb-config"), + input_default: get_config(&config, "input-default"), + input_touchpad: get_config(&config, "input-touchpad"), input_devices: get_config(&config, "input-devices"), config, } @@ -407,22 +411,14 @@ impl Config { self.xkb.clone() } - pub fn read_device(&mut self, device: &mut InputDevice) { - use std::collections::hash_map::Entry; - - let mut config_changed = false; - match self.input_devices.entry(device.name().into()) { - Entry::Occupied(entry) => entry.get().update_device(device), - Entry::Vacant(entry) => { - entry.insert(InputConfig::for_device(device)); - config_changed = true; - } - } - if config_changed { - if let Err(err) = self.config.set("input-devices", &self.input_devices) { - error!(?err, "Failed to write config 'input-devices'"); - } - } + pub fn read_device(&self, device: &mut InputDevice) { + let default_config = if device.config_tap_finger_count() > 0 { + &self.input_touchpad + } else { + &self.input_default + }; + let device_config = self.input_devices.get(device.name()); + input_config::update_device(device, device_config, default_config); } } @@ -483,6 +479,14 @@ fn get_config( }) } +fn update_input(state: &mut State) { + if let BackendData::Kms(ref mut kms_state) = &mut state.backend { + for device in kms_state.input_devices.values_mut() { + state.common.config.read_device(device); + } + } +} + fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut State) { for key in &keys { match key.as_str() { @@ -490,7 +494,7 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut let value = get_config::(&config, "xkb-config"); for seat in state.common.seats().cloned().collect::>().iter() { if let Some(keyboard) = seat.get_keyboard() { - if let Err(err) = keyboard.set_xkb_config(state, (&value).into()) { + if let Err(err) = keyboard.set_xkb_config(state, xkb_config_to_wl(&value)) { error!(?err, "Failed to load provided xkb config"); // TODO Revert to default? } @@ -498,18 +502,32 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut } state.common.config.xkb = value; } + "input-default" => { + let value = get_config::(&config, "input-default"); + state.common.config.input_default = value; + update_input(state); + } + "input-touchpad" => { + let value = get_config::(&config, "input-touchpad"); + state.common.config.input_touchpad = value; + update_input(state); + } "input-devices" => { let value = get_config::>(&config, "input-devices"); - if let BackendData::Kms(ref mut kms_state) = &mut state.backend { - for (name, device) in kms_state.input_devices.iter_mut() { - if let Some(input_config) = value.get(name) { - input_config.update_device(device); - } - } - } state.common.config.input_devices = value; + update_input(state); } _ => {} } } } + +pub fn xkb_config_to_wl(config: &XkbConfig) -> WlXkbConfig<'_> { + WlXkbConfig { + rules: &config.rules, + model: &config.model, + layout: &config.layout, + variant: &config.variant, + options: config.options.clone(), + } +} diff --git a/src/config/types.rs b/src/config/types.rs index d9f2577c..349dc216 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -13,147 +13,6 @@ pub use smithay::{ use tracing::warn; use xkbcommon::xkb; -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct XkbConfig { - pub rules: String, - pub model: String, - pub layout: String, - pub variant: String, - pub options: Option, -} - -impl Default for XkbConfig { - fn default() -> XkbConfig { - XkbConfig { - rules: String::new(), - model: String::new(), - layout: String::new(), - variant: String::new(), - options: None, - } - } -} - -impl<'a> Into> for &'a XkbConfig { - fn into(self) -> WlXkbConfig<'a> { - WlXkbConfig { - rules: &self.rules, - model: &self.model, - layout: &self.layout, - variant: &self.variant, - options: self.options.clone(), - } - } -} - -pub mod ClickMethodDef { - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use smithay::reexports::input::ClickMethod as ClickMethodOrig; - - #[derive(Debug, Serialize, Deserialize)] - pub enum ClickMethod { - ButtonAreas, - Clickfinger, - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let o = Option::deserialize(deserializer)?; - Ok(o.map(|x| match x { - ClickMethod::ButtonAreas => ClickMethodOrig::ButtonAreas, - ClickMethod::Clickfinger => ClickMethodOrig::Clickfinger, - })) - } - - pub fn serialize(arg: &Option, ser: S) -> Result - where - S: Serializer, - { - let arg = match arg { - Some(ClickMethodOrig::ButtonAreas) => Some(ClickMethod::ButtonAreas), - Some(ClickMethodOrig::Clickfinger) => Some(ClickMethod::Clickfinger), - Some(_) | None => None, - }; - Option::serialize(&arg, ser) - } -} - -pub mod AccelProfileDef { - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use smithay::reexports::input::AccelProfile as AccelProfileOrig; - - #[derive(Debug, Serialize, Deserialize)] - enum AccelProfile { - Flat, - Adaptive, - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let o = Option::deserialize(deserializer)?; - Ok(o.map(|x| match x { - AccelProfile::Flat => AccelProfileOrig::Flat, - AccelProfile::Adaptive => AccelProfileOrig::Adaptive, - })) - } - - pub fn serialize(arg: &Option, ser: S) -> Result - where - S: Serializer, - { - let arg = match arg { - Some(AccelProfileOrig::Flat) => Some(AccelProfile::Flat), - Some(AccelProfileOrig::Adaptive) => Some(AccelProfile::Adaptive), - Some(_) | None => None, - }; - Option::serialize(&arg, ser) - } -} - -pub mod ScrollMethodDef { - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use smithay::reexports::input::ScrollMethod as ScrollMethodOrig; - - #[derive(Debug, Serialize, Deserialize)] - pub enum ScrollMethod { - NoScroll, - TwoFinger, - Edge, - OnButtonDown, - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let o = Option::deserialize(deserializer)?; - Ok(o.map(|x| match x { - ScrollMethod::NoScroll => ScrollMethodOrig::NoScroll, - ScrollMethod::TwoFinger => ScrollMethodOrig::TwoFinger, - ScrollMethod::Edge => ScrollMethodOrig::Edge, - ScrollMethod::OnButtonDown => ScrollMethodOrig::OnButtonDown, - })) - } - - pub fn serialize(arg: &Option, ser: S) -> Result - where - S: Serializer, - { - let arg = match arg { - Some(ScrollMethodOrig::NoScroll) => Some(ScrollMethod::NoScroll), - Some(ScrollMethodOrig::TwoFinger) => Some(ScrollMethod::TwoFinger), - Some(ScrollMethodOrig::Edge) => Some(ScrollMethod::Edge), - Some(ScrollMethodOrig::OnButtonDown) => Some(ScrollMethod::OnButtonDown), - Some(_) | None => None, - }; - Option::serialize(&arg, ser) - } -} - #[derive(Serialize, Deserialize)] #[serde(remote = "Transform")] pub enum TransformDef { @@ -167,40 +26,6 @@ pub enum TransformDef { Flipped270, } -pub mod TapButtonMapDef { - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use smithay::reexports::input::TapButtonMap as TapButtonMapOrig; - - #[derive(Debug, Serialize, Deserialize)] - pub enum TapButtonMap { - LeftRightMiddle, - LeftMiddleRight, - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let o = Option::deserialize(deserializer)?; - Ok(o.map(|x| match x { - TapButtonMap::LeftRightMiddle => TapButtonMapOrig::LeftRightMiddle, - TapButtonMap::LeftMiddleRight => TapButtonMapOrig::LeftMiddleRight, - })) - } - - pub fn serialize(arg: &Option, ser: S) -> Result - where - S: Serializer, - { - let arg = match arg { - Some(TapButtonMapOrig::LeftRightMiddle) => Some(TapButtonMap::LeftRightMiddle), - Some(TapButtonMapOrig::LeftMiddleRight) => Some(TapButtonMap::LeftMiddleRight), - Some(_) | None => None, - }; - Option::serialize(&arg, ser) - } -} - #[derive(Deserialize)] #[serde(transparent)] pub struct KeyModifiersDef(Vec); diff --git a/src/input/mod.rs b/src/input/mod.rs index 0c647243..27ec163f 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2,7 +2,7 @@ use crate::{ backend::render::cursor::CursorState, - config::{Action, Config, KeyPattern, WorkspaceLayout}, + config::{xkb_config_to_wl, Action, Config, KeyPattern, WorkspaceLayout}, shell::{ focus::{target::PointerFocusTarget, FocusDirection}, grabs::{ResizeEdge, SeatMoveGrabState}, @@ -153,7 +153,7 @@ pub fn add_seat( // So instead of doing the right thing (and initialize these capabilities as matching // devices appear), we have to surrender to reality and just always expose a keyboard and pointer. let conf = config.xkb_config(); - if let Err(err) = seat.add_keyboard((&conf).into(), 200, 25) { + if let Err(err) = seat.add_keyboard(xkb_config_to_wl(&conf), 200, 25) { warn!( ?err, "Failed to load provided xkb config. Trying default...",