diff --git a/Cargo.lock b/Cargo.lock index 75fe423d..4e5359f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,6 +481,8 @@ dependencies = [ "bitflags 1.3.2", "bytemuck", "calloop", + "cosmic-comp-config", + "cosmic-config", "cosmic-protocols", "edid-rs", "egui", @@ -521,12 +523,21 @@ 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" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "atomicwrites", + "calloop", "cosmic-config-derive", "dirs 5.0.1", "iced_futures", @@ -538,7 +549,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "quote", "syn 1.0.109", @@ -578,7 +589,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "anyhow", "cosmic-config", @@ -1746,7 +1757,7 @@ dependencies = [ [[package]] name = "iced" version = "0.9.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "iced_core", "iced_futures", @@ -1759,7 +1770,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.9.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "bitflags 1.3.2", "instant", @@ -1772,7 +1783,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.6.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "futures", "iced_core", @@ -1784,7 +1795,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.8.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -1801,7 +1812,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -1813,7 +1824,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "iced_core", "iced_futures", @@ -1823,7 +1834,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.8.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "iced_core", "once_cell", @@ -1833,7 +1844,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "bytemuck", "cosmic-text", @@ -1851,7 +1862,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.10.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -1872,7 +1883,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "iced_renderer", "iced_runtime", @@ -1979,6 +1990,7 @@ dependencies = [ "input-sys", "io-lifetimes", "libc", + "log", "udev", ] @@ -2167,7 +2179,7 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=42d7baf#42d7baf0d5cb14ab476120be9dfcaea9bd1d0be4" +source = "git+https://github.com/pop-os/libcosmic/?rev=4895b0c#4895b0c9bda9e46fc7db173e239d155dac957186" dependencies = [ "apply", "cosmic-config", @@ -2186,6 +2198,7 @@ dependencies = [ "lazy_static", "palette", "slotmap", + "thiserror", "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index a5890a3c..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,9 +35,11 @@ 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 = "42d7baf", default-features = false } -iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic/", rev = "42d7baf" } +libcosmic = { git = "https://github.com/pop-os/libcosmic/", rev = "4895b0c", default-features = false } +iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic/", rev = "4895b0c" } tiny-skia = "0.9" ordered-float = "3.0" glow = "0.11.2" 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..d085a278 --- /dev/null +++ b/cosmic-comp-config/src/input.rs @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-3.0-only + +#![allow(non_snake_case)] + +pub use input::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, 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(Clone, Debug, Default, Deserialize, Serialize)] +pub struct AccelConfig { + #[serde(with = "AccelProfileDef")] + pub profile: Option, + pub speed: f64, +} + +#[derive(Clone, Debug, Default, 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(Clone, 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/backend/kms/mod.rs b/src/backend/kms/mod.rs index 13026233..305baeb5 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -57,7 +57,7 @@ use smithay::{ control::{connector, crtc, Device as ControlDevice, ModeTypeFlags}, Device as _, }, - input::Libinput, + input::{self, Libinput}, nix::{fcntl::OFlag, sys::stat::dev_t}, wayland_protocols::wp::{ linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1, @@ -97,6 +97,7 @@ const MIN_RENDER_TIME: Duration = Duration::from_millis(3); #[derive(Debug)] pub struct KmsState { devices: HashMap, + pub input_devices: HashMap, pub api: GpuManager>, pub primary: DrmNode, session: LibSeatSession, @@ -172,8 +173,15 @@ pub fn init_backend( let libinput_event_source = event_loop .handle() .insert_source(libinput_backend, move |mut event, _, data| { - if let &mut InputEvent::DeviceAdded { ref mut device } = &mut event { + if let InputEvent::DeviceAdded { ref mut device } = &mut event { data.state.common.config.read_device(device); + data.state + .backend + .kms() + .input_devices + .insert(device.name().into(), device.clone()); + } else if let InputEvent::DeviceRemoved { device } = &event { + data.state.backend.kms().input_devices.remove(device.name()); } data.state.process_input_event(event, true); for output in data.state.common.shell.outputs() { @@ -363,6 +371,7 @@ pub fn init_backend( primary, session, devices: HashMap::new(), + input_devices: HashMap::new(), }); // Create relative pointer global diff --git a/src/config/input_config.rs b/src/config/input_config.rs new file mode 100644 index 00000000..463d678d --- /dev/null +++ b/src/config/input_config.rs @@ -0,0 +1,217 @@ +use smithay::reexports::input::{ + Device as InputDevice, DeviceConfigError, ScrollMethod, SendEventsMode, +}; +use tracing::warn; + +use cosmic_comp_config::input::*; + +#[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 + }, + } +} + +// Get setting from `device_config` if present, then `default_config` +// Returns `is_default` to indicate this is a default value. +fn get_config<'a, T: 'a, F: Fn(&'a InputConfig) -> Option>( + device_config: Option<&'a InputConfig>, + default_config: &'a InputConfig, + f: F, +) -> Option<(T, bool)> { + if let Some(setting) = device_config.and_then(&f) { + Some((setting, false)) + } else if let Some(setting) = f(default_config) { + Some((setting, true)) + } else { + None + } +} + +fn config_set_error( + device: &InputDevice, + setting: &str, + value: T, + err: DeviceConfigError, + is_default: bool, +) { + if !(is_default && err == DeviceConfigError::Unsupported) { + warn!( + ?err, + "Failed to apply {} {:?} for device {:?}.", + setting, + value, + device.name(), + ); + } +} + +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, is_default)) = config!(|x| x.acceleration.as_ref()) { + if let Some(profile) = accel.profile { + if let Err(err) = device.config_accel_set_profile(profile) { + config_set_error(device, "acceleration profile", profile, err, is_default); + } + } + if let Err(err) = device.config_accel_set_speed(accel.speed) { + config_set_error(device, "acceleration speed", accel.speed, err, is_default); + } + } + if let Some((matrix, is_default)) = config!(|x| x.calibration) { + if let Err(err) = device.config_calibration_set_matrix(matrix) { + config_set_error(device, "calibration matrix", matrix, err, is_default); + } + } + if let Some((method, is_default)) = config!(|x| x.click_method) { + if let Err(err) = device.config_click_set_method(method) { + config_set_error(device, "click method", method, err, is_default); + } + } + if let Some((dwt, is_default)) = config!(|x| x.disable_while_typing) { + if let Err(err) = device.config_dwt_set_enabled(dwt) { + config_set_error(device, "disable-while-typing", dwt, err, is_default); + } + } + if let Some((left, is_default)) = config!(|x| x.left_handed) { + if let Err(err) = device.config_left_handed_set(left) { + config_set_error(device, "left-handed", left, err, is_default); + } + } + if let Some((middle, is_default)) = config!(|x| x.middle_button_emulation) { + if let Err(err) = device.config_middle_emulation_set_enabled(middle) { + config_set_error(device, "middle-button-emulation", middle, err, is_default); + } + } + if let Some((angle, is_default)) = config!(|x| x.rotation_angle) { + if let Err(err) = device.config_rotation_set_angle(angle) { + config_set_error(device, "rotation-angle", angle, err, is_default); + } + } + if let Some((scroll, is_default)) = config!(|x| x.scroll_config.as_ref()) { + if let Some(method) = scroll.method { + if let Err(err) = device.config_scroll_set_method(method) { + config_set_error(device, "scroll method", scroll, err, is_default); + } + } + if let Some(natural) = scroll.natural_scroll { + if let Err(err) = device.config_scroll_set_natural_scroll_enabled(natural) { + config_set_error(device, "natural scrolling", natural, err, is_default); + } + } + if let Some(button) = scroll.scroll_button { + if let Err(err) = device.config_scroll_set_button(button) { + config_set_error(device, "scroll button", button, err, is_default); + } + } + } + if let Some((tap, is_default)) = config!(|x| x.tap_config.as_ref()) { + if let Err(err) = device.config_tap_set_enabled(tap.enabled) { + config_set_error(device, "tap-to-click", tap.enabled, err, is_default); + } + if let Some(button_map) = tap.button_map { + if let Err(err) = device.config_tap_set_button_map(button_map) { + config_set_error(device, "button map", button_map, err, is_default); + } + } + if let Err(err) = device.config_tap_set_drag_enabled(tap.drag) { + config_set_error(device, "tap-drag", tap.drag, err, is_default); + } + if let Err(err) = device.config_tap_set_drag_lock_enabled(tap.drag_lock) { + config_set_error(device, "tap-drag-lock", tap.drag_lock, err, is_default); + } + } +} diff --git a/src/config/key_bindings.rs b/src/config/key_bindings.rs new file mode 100644 index 00000000..d8b8bd06 --- /dev/null +++ b/src/config/key_bindings.rs @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::shell::{ + focus::FocusDirection, grabs::ResizeEdge, layout::tiling::Direction, ResizeDirection, +}; +use serde::Deserialize; +use smithay::{ + backend::input::KeyState, + input::keyboard::{keysyms as KeySyms, xkb::keysym_get_name, ModifiersState}, +}; +use std::collections::HashMap; + +use super::{types::*, WorkspaceLayout}; + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +pub enum KeyModifier { + Ctrl, + Alt, + Shift, + Super, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct KeyModifiers { + pub ctrl: bool, + pub alt: bool, + pub shift: bool, + pub logo: bool, +} + +impl PartialEq for KeyModifiers { + fn eq(&self, other: &ModifiersState) -> bool { + self.ctrl == other.ctrl + && self.alt == other.alt + && self.shift == other.shift + && self.logo == other.logo + } +} + +impl Into for ModifiersState { + fn into(self) -> KeyModifiers { + KeyModifiers { + ctrl: self.ctrl, + alt: self.alt, + shift: self.shift, + logo: self.logo, + } + } +} + +impl std::ops::AddAssign for KeyModifiers { + fn add_assign(&mut self, rhs: KeyModifier) { + match rhs { + KeyModifier::Ctrl => self.ctrl = true, + KeyModifier::Alt => self.alt = true, + KeyModifier::Shift => self.shift = true, + KeyModifier::Super => self.logo = true, + }; + } +} + +impl std::ops::BitOr for KeyModifier { + type Output = KeyModifiers; + + fn bitor(self, rhs: KeyModifier) -> Self::Output { + let mut modifiers = self.into(); + modifiers += rhs; + modifiers + } +} + +impl Into for KeyModifier { + fn into(self) -> KeyModifiers { + let mut modifiers = KeyModifiers { + ctrl: false, + alt: false, + shift: false, + logo: false, + }; + modifiers += self; + modifiers + } +} + +/// Describtion of a key combination that might be +/// handled by the compositor. +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Hash)] +#[serde(deny_unknown_fields)] +pub struct KeyPattern { + /// What modifiers are expected to be pressed alongside the key + #[serde(deserialize_with = "deserialize_KeyModifiers")] + pub modifiers: KeyModifiers, + /// The actual key, that was pressed + #[serde(deserialize_with = "deserialize_Keysym")] + pub key: u32, +} + +impl KeyPattern { + pub fn new(modifiers: impl Into, key: u32) -> KeyPattern { + KeyPattern { + modifiers: modifiers.into(), + key, + } + } +} + +impl ToString for KeyPattern { + fn to_string(&self) -> String { + let mut result = String::new(); + if self.modifiers.logo { + result += "Super+"; + } + if self.modifiers.ctrl { + result += "Ctrl+"; + } + if self.modifiers.alt { + result += "Alt+"; + } + if self.modifiers.shift { + result += "Shift+"; + } + result += &keysym_get_name(self.key); + result + } +} + +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +pub enum Action { + Terminate, + Debug, + Close, + + Workspace(u8), + NextWorkspace, + PreviousWorkspace, + LastWorkspace, + MoveToWorkspace(u8), + MoveToNextWorkspace, + MoveToPreviousWorkspace, + MoveToLastWorkspace, + SendToWorkspace(u8), + SendToNextWorkspace, + SendToPreviousWorkspace, + SendToLastWorkspace, + + NextOutput, + PreviousOutput, + MoveToNextOutput, + MoveToPreviousOutput, + SendToNextOutput, + SendToPreviousOutput, + + Focus(FocusDirection), + Move(Direction), + + ToggleOrientation, + Orientation(crate::shell::layout::Orientation), + + ToggleStacking, + + ToggleTiling, + ToggleWindowFloating, + + Resizing(ResizeDirection), + #[serde(skip)] + _ResizingInternal(ResizeDirection, ResizeEdge, KeyState), + Maximize, + Spawn(String), +} + +fn insert_binding( + key_bindings: &mut HashMap, + modifiers: KeyModifiers, + keys: impl Iterator, + action: Action, +) { + if !key_bindings.values().any(|a| a == &action) { + for key in keys { + let pattern = KeyPattern { + modifiers: modifiers.clone(), + key, + }; + if !key_bindings.contains_key(&pattern) { + key_bindings.insert(pattern, action.clone()); + } + } + } +} + +pub fn add_default_bindings( + key_bindings: &mut HashMap, + workspace_layout: WorkspaceLayout, +) { + let (workspace_previous, workspace_next, output_previous, output_next) = match workspace_layout + { + WorkspaceLayout::Horizontal => ( + [KeySyms::KEY_Left, KeySyms::KEY_h], + [KeySyms::KEY_Right, KeySyms::KEY_l], + [KeySyms::KEY_Up, KeySyms::KEY_k], + [KeySyms::KEY_Down, KeySyms::KEY_j], + ), + WorkspaceLayout::Vertical => ( + [KeySyms::KEY_Up, KeySyms::KEY_k], + [KeySyms::KEY_Down, KeySyms::KEY_j], + [KeySyms::KEY_Left, KeySyms::KEY_h], + [KeySyms::KEY_Right, KeySyms::KEY_l], + ), + }; + + insert_binding( + key_bindings, + KeyModifiers { + logo: true, + ctrl: true, + ..Default::default() + }, + workspace_previous.iter().copied(), + Action::PreviousWorkspace, + ); + insert_binding( + key_bindings, + KeyModifiers { + logo: true, + ctrl: true, + ..Default::default() + }, + workspace_next.iter().copied(), + Action::NextWorkspace, + ); + insert_binding( + key_bindings, + KeyModifiers { + logo: true, + ctrl: true, + shift: true, + ..Default::default() + }, + workspace_previous.iter().copied(), + Action::MoveToPreviousWorkspace, + ); + insert_binding( + key_bindings, + KeyModifiers { + logo: true, + ctrl: true, + shift: true, + ..Default::default() + }, + workspace_next.iter().copied(), + Action::MoveToNextWorkspace, + ); + + insert_binding( + key_bindings, + KeyModifiers { + logo: true, + ctrl: true, + ..Default::default() + }, + output_previous.iter().copied(), + Action::PreviousOutput, + ); + insert_binding( + key_bindings, + KeyModifiers { + logo: true, + ctrl: true, + ..Default::default() + }, + output_next.iter().copied(), + Action::NextOutput, + ); + insert_binding( + key_bindings, + KeyModifiers { + logo: true, + ctrl: true, + shift: true, + ..Default::default() + }, + output_previous.iter().copied(), + Action::MoveToPreviousOutput, + ); + insert_binding( + key_bindings, + KeyModifiers { + logo: true, + ctrl: true, + shift: true, + ..Default::default() + }, + output_next.iter().copied(), + Action::MoveToNextOutput, + ); +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 663b4e62..826bf220 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,15 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::{ - focus::FocusDirection, grabs::ResizeEdge, layout::tiling::Direction, ResizeDirection, - Shell, WorkspaceAmount, - }, + shell::{Shell, WorkspaceAmount}, state::{BackendData, Data, State}, wayland::protocols::output_configuration::OutputConfigurationState, }; +use cosmic_config::ConfigGet; use serde::{Deserialize, Serialize}; -use smithay::input::{keyboard::xkb::keysym_get_name, Seat}; +use smithay::input::Seat; pub use smithay::{ backend::input::KeyState, input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState}, @@ -26,18 +24,27 @@ pub use smithay::{ use std::{cell::RefCell, collections::HashMap, fs::OpenOptions, path::PathBuf}; use tracing::{debug, error, info, warn}; +mod input_config; +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 { pub static_conf: StaticConfig, pub dynamic_conf: DynamicConfig, + pub config: cosmic_config::Config, + pub xkb: XkbConfig, + pub input_default: InputConfig, + pub input_touchpad: InputConfig, + pub input_devices: HashMap, } #[derive(Debug, Deserialize)] pub struct StaticConfig { - pub key_bindings: HashMap, + pub key_bindings: HashMap, pub workspace_mode: WorkspaceMode, pub workspace_amount: WorkspaceAmount, #[serde(default = "default_workspace_layout")] @@ -64,7 +71,6 @@ pub enum WorkspaceLayout { #[derive(Debug)] pub struct DynamicConfig { outputs: (Option, OutputsConfig), - inputs: (Option, InputsConfig), } #[derive(Debug, Deserialize, Serialize)] @@ -151,73 +157,24 @@ impl OutputConfig { } } -#[derive(Debug, Deserialize, Serialize)] -pub struct InputsConfig { - xkb: XkbConfig, - devices: HashMap, -} - -#[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, -} - -#[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 Config { - pub fn load() -> Config { + pub fn load(loop_handle: &LoopHandle<'_, Data>) -> Config { + let config = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap(); + let source = cosmic_config::calloop::ConfigWatchSource::new(&config).unwrap(); + loop_handle + .insert_source(source, |(config, keys), (), shared_data| { + config_changed(config, keys, &mut shared_data.state); + }) + .expect("Failed to add cosmic-config to the event loop"); let xdg = xdg::BaseDirectories::new().ok(); 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, } } @@ -247,125 +204,9 @@ impl Config { ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) .expect("Malformed config file"); - let (workspace_previous, workspace_next, output_previous, output_next) = - match config.workspace_layout { - WorkspaceLayout::Horizontal => ( - [KeySyms::KEY_Left, KeySyms::KEY_h], - [KeySyms::KEY_Right, KeySyms::KEY_j], - [KeySyms::KEY_Up, KeySyms::KEY_k], - [KeySyms::KEY_Down, KeySyms::KEY_j], - ), - WorkspaceLayout::Vertical => ( - [KeySyms::KEY_Up, KeySyms::KEY_k], - [KeySyms::KEY_Down, KeySyms::KEY_j], - [KeySyms::KEY_Left, KeySyms::KEY_h], - [KeySyms::KEY_Right, KeySyms::KEY_j], - ), - }; - - fn insert_binding( - key_bindings: &mut HashMap, - modifiers: KeyModifiers, - keys: impl Iterator, - action: Action, - ) { - if !key_bindings.values().any(|a| a == &action) { - for key in keys { - let pattern = KeyPattern { - modifiers: modifiers.clone(), - key, - }; - if !key_bindings.contains_key(&pattern) { - key_bindings.insert(pattern, action.clone()); - } - } - } - } - - insert_binding( - &mut config.key_bindings, - KeyModifiers { - logo: true, - ctrl: true, - ..Default::default() - }, - workspace_previous.iter().copied(), - Action::PreviousWorkspace, - ); - insert_binding( - &mut config.key_bindings, - KeyModifiers { - logo: true, - ctrl: true, - ..Default::default() - }, - workspace_next.iter().copied(), - Action::NextWorkspace, - ); - insert_binding( - &mut config.key_bindings, - KeyModifiers { - logo: true, - ctrl: true, - shift: true, - ..Default::default() - }, - workspace_previous.iter().copied(), - Action::MoveToPreviousWorkspace, - ); - insert_binding( - &mut config.key_bindings, - KeyModifiers { - logo: true, - ctrl: true, - shift: true, - ..Default::default() - }, - workspace_next.iter().copied(), - Action::MoveToNextWorkspace, - ); - - insert_binding( - &mut config.key_bindings, - KeyModifiers { - logo: true, - ctrl: true, - ..Default::default() - }, - output_previous.iter().copied(), - Action::PreviousOutput, - ); - insert_binding( - &mut config.key_bindings, - KeyModifiers { - logo: true, - ctrl: true, - ..Default::default() - }, - output_next.iter().copied(), - Action::NextOutput, - ); - insert_binding( + key_bindings::add_default_bindings( &mut config.key_bindings, - KeyModifiers { - logo: true, - ctrl: true, - shift: true, - ..Default::default() - }, - output_previous.iter().copied(), - Action::MoveToPreviousOutput, - ); - insert_binding( - &mut config.key_bindings, - KeyModifiers { - logo: true, - ctrl: true, - shift: true, - ..Default::default() - }, - output_next.iter().copied(), - Action::MoveToNextOutput, + config.workspace_layout, ); return config; @@ -388,12 +229,8 @@ impl Config { xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok()); let outputs = Self::load_outputs(&output_path); - let input_path = xdg.and_then(|base| base.place_state_file("cosmic-comp/inputs.ron").ok()); - let inputs = Self::load_inputs(&input_path); - DynamicConfig { outputs: (output_path, outputs), - inputs: (input_path, inputs), } } @@ -417,27 +254,6 @@ impl Config { } } - fn load_inputs(path: &Option) -> InputsConfig { - if let Some(path) = path.as_ref() { - if path.exists() { - match ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) { - Ok(config) => return config, - Err(err) => { - warn!(?err, "Failed to read input_config, resetting.."); - if let Err(err) = std::fs::remove_file(path) { - error!(?err, "Failed to remove input_config."); - } - } - }; - } - } - - InputsConfig { - xkb: XkbConfig::default(), - devices: HashMap::new(), - } - } - pub fn read_outputs( &mut self, output_state: &mut OutputConfigurationState, @@ -592,258 +408,17 @@ impl Config { } pub fn xkb_config(&self) -> XkbConfig { - self.dynamic_conf.inputs().xkb.clone() + self.xkb.clone() } - pub fn read_device(&mut self, device: &mut InputDevice) { - use std::collections::hash_map::Entry; - - let mut inputs = self.dynamic_conf.inputs_mut(); - match inputs.devices.entry(device.name().into()) { - Entry::Occupied(entry) => { - let config = entry.get(); - if let Err(err) = match config.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 {:?}.", - config.state, - device.name(), - ); - } - if let Some(accel) = config.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.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.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(dwt) = config.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.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.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.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.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) = config.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(), - ); - } - } - } - Entry::Vacant(entry) => { - entry.insert(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 - }, - }); - } - } + 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); } } @@ -892,168 +467,67 @@ impl DynamicConfig { pub fn outputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, OutputsConfig> { PersistenceGuard(self.outputs.0.clone(), &mut self.outputs.1) } - - pub fn inputs(&self) -> &InputsConfig { - &self.inputs.1 - } - - pub fn inputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, InputsConfig> { - PersistenceGuard(self.inputs.0.clone(), &mut self.inputs.1) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] -pub enum KeyModifier { - Ctrl, - Alt, - Shift, - Super, } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub struct KeyModifiers { - pub ctrl: bool, - pub alt: bool, - pub shift: bool, - pub logo: bool, +fn get_config( + config: &cosmic_config::Config, + key: &str, +) -> T { + config.get(key).unwrap_or_else(|err| { + error!(?err, "Failed to read config '{}'", key); + T::default() + }) } -impl PartialEq for KeyModifiers { - fn eq(&self, other: &ModifiersState) -> bool { - self.ctrl == other.ctrl - && self.alt == other.alt - && self.shift == other.shift - && self.logo == other.logo - } -} - -impl Into for ModifiersState { - fn into(self) -> KeyModifiers { - KeyModifiers { - ctrl: self.ctrl, - alt: self.alt, - shift: self.shift, - logo: self.logo, +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); } } } -impl std::ops::AddAssign for KeyModifiers { - fn add_assign(&mut self, rhs: KeyModifier) { - match rhs { - KeyModifier::Ctrl => self.ctrl = true, - KeyModifier::Alt => self.alt = true, - KeyModifier::Shift => self.shift = true, - KeyModifier::Super => self.logo = true, - }; - } -} - -impl std::ops::BitOr for KeyModifier { - type Output = KeyModifiers; - - fn bitor(self, rhs: KeyModifier) -> Self::Output { - let mut modifiers = self.into(); - modifiers += rhs; - modifiers - } -} - -impl Into for KeyModifier { - fn into(self) -> KeyModifiers { - let mut modifiers = KeyModifiers { - ctrl: false, - alt: false, - shift: false, - logo: false, - }; - modifiers += self; - modifiers - } -} - -/// Describtion of a key combination that might be -/// handled by the compositor. -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Hash)] -#[serde(deny_unknown_fields)] -pub struct KeyPattern { - /// What modifiers are expected to be pressed alongside the key - #[serde(deserialize_with = "deserialize_KeyModifiers")] - pub modifiers: KeyModifiers, - /// The actual key, that was pressed - #[serde(deserialize_with = "deserialize_Keysym")] - pub key: u32, -} - -impl KeyPattern { - pub fn new(modifiers: impl Into, key: u32) -> KeyPattern { - KeyPattern { - modifiers: modifiers.into(), - key, +fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut State) { + for key in &keys { + match key.as_str() { + "xkb-config" => { + 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, xkb_config_to_wl(&value)) { + error!(?err, "Failed to load provided xkb config"); + // TODO Revert to default? + } + } + } + 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"); + state.common.config.input_devices = value; + update_input(state); + } + _ => {} } } } -impl ToString for KeyPattern { - fn to_string(&self) -> String { - let mut result = String::new(); - if self.modifiers.logo { - result += "Super+"; - } - if self.modifiers.ctrl { - result += "Ctrl+"; - } - if self.modifiers.alt { - result += "Alt+"; - } - if self.modifiers.shift { - result += "Shift+"; - } - result += &keysym_get_name(self.key); - result +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(), } } - -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] -pub enum Action { - Terminate, - Debug, - Close, - - Workspace(u8), - NextWorkspace, - PreviousWorkspace, - LastWorkspace, - MoveToWorkspace(u8), - MoveToNextWorkspace, - MoveToPreviousWorkspace, - MoveToLastWorkspace, - SendToWorkspace(u8), - SendToNextWorkspace, - SendToPreviousWorkspace, - SendToLastWorkspace, - - NextOutput, - PreviousOutput, - MoveToNextOutput, - MoveToPreviousOutput, - SendToNextOutput, - SendToPreviousOutput, - - Focus(FocusDirection), - Move(Direction), - - ToggleOrientation, - Orientation(crate::shell::layout::Orientation), - - ToggleStacking, - - ToggleTiling, - ToggleWindowFloating, - - Resizing(ResizeDirection), - #[serde(skip)] - _ResizingInternal(ResizeDirection, ResizeEdge, KeyState), - Maximize, - Spawn(String), -} 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...", diff --git a/src/state.rs b/src/state.rs index 8ba5ff4f..f1d1740e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -278,7 +278,7 @@ impl State { .unwrap(); let clock = Clock::new().expect("Failed to initialize clock"); - let config = Config::load(); + let config = Config::load(&handle); let compositor_state = CompositorState::new::(dh); let data_device_state = DataDeviceState::new::(dh); let dmabuf_state = DmabufState::new();