From 2a13e736e4792a8b1fb0cca202f2d3e97e42dba0 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Thu, 28 Sep 2023 10:17:28 -0500 Subject: [PATCH 1/5] Add xkbconfig to API --- api/lua/input.lua | 26 ++++++++++++++++++++++++++ api/lua/msg.lua | 4 +++- src/config/api/msg.rs | 14 ++++++++++++++ src/state/api_handlers.rs | 22 ++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/api/lua/input.lua b/api/lua/input.lua index 599b891..bbe9914 100644 --- a/api/lua/input.lua +++ b/api/lua/input.lua @@ -26,6 +26,13 @@ local buttons = { back = 0x116, } +---@class XkbConfig +---@field rules string? +---@field model string? +---@field layout string? +---@field variant string? +---@field options string? + ---Input management. --- ---This module provides utilities to set keybinds. @@ -114,4 +121,23 @@ function input_module.mousebind(modifiers, button, edge, action) }) end +---Set the xkbconfig for your input device. +--- +---Fields not present will be set to their default values. +--- +---### Example +---```lua +---input.set_xkb_config({ +--- layout = "us,fr,ge", +--- options = "ctrl:swapcaps,caps:shift" +---}) +---``` +--- +---@param xkb_config XkbConfig +function input_module.set_xkb_config(xkb_config) + SendMsg({ + SetXkbConfig = xkb_config, + }) +end + return input_module diff --git a/api/lua/msg.lua b/api/lua/msg.lua index d3b0a14..982944f 100644 --- a/api/lua/msg.lua +++ b/api/lua/msg.lua @@ -19,7 +19,6 @@ -- ---@field Spawn { command: string[], callback_id: integer? }? ---@field SetEnv { key: string, value: string }? ----@field Request Request? --Tags ---@field ToggleTag { tag_id: TagId }? ---@field SwitchToTag { tag_id: TagId }? @@ -29,6 +28,9 @@ --Outputs ---@field ConnectForAllOutputs { callback_id: integer }? ---@field SetOutputLocation { output_name: OutputName, x: integer?, y: integer? }? +--Input +---@field SetXkbConfig XkbConfig? +---@field Request Request? ---@alias Msg _Msg | "Quit" diff --git a/src/config/api/msg.rs b/src/config/api/msg.rs index 11ef8c3..96a1ff2 100644 --- a/src/config/api/msg.rs +++ b/src/config/api/msg.rs @@ -134,6 +134,20 @@ pub enum Msg { /// Quit the compositor. Quit, + // Input management + SetXkbConfig { + #[serde(default)] + rules: Option, + #[serde(default)] + variant: Option, + #[serde(default)] + layout: Option, + #[serde(default)] + model: Option, + #[serde(default)] + options: Option, + }, + Request { request_id: RequestId, request: Request, diff --git a/src/state/api_handlers.rs b/src/state/api_handlers.rs index e90dcc2..406e09b 100644 --- a/src/state/api_handlers.rs +++ b/src/state/api_handlers.rs @@ -4,6 +4,7 @@ use async_process::Stdio; use futures_lite::AsyncBufReadExt; use smithay::{ desktop::space::SpaceElement, + input::keyboard::XkbConfig, reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge, utils::{Point, Rectangle, SERIAL_COUNTER}, wayland::{compositor, shell::xdg::XdgToplevelSurfaceData}, @@ -349,6 +350,27 @@ impl State { self.loop_signal.stop(); } + Msg::SetXkbConfig { + rules, + variant, + layout, + model, + options, + } => { + let new_config = XkbConfig { + rules: &rules.unwrap_or_default(), + model: &model.unwrap_or_default(), + layout: &layout.unwrap_or_default(), + variant: &variant.unwrap_or_default(), + options, + }; + if let Some(kb) = self.seat.get_keyboard() { + if let Err(err) = kb.set_xkb_config(self, new_config) { + tracing::error!("Failed to set xkbconfig: {err}"); + } + } + } + Msg::Request { request_id, request, From d26b9b968c3494245d3d18bb4dd86b95af616862 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Thu, 28 Sep 2023 18:46:08 -0500 Subject: [PATCH 2/5] Add libinput to API --- Cargo.toml | 1 - api/lua/example_config.lua | 2 + api/lua/input.lua | 7 +- api/lua/input/libinput.lua | 213 +++++++++++++++++++++++++++++++++++++ api/lua/msg.lua | 1 + src/backend/udev.rs | 1 + src/config.rs | 1 + src/config/api/msg.rs | 5 +- src/input.rs | 122 +++++++++++++++++++++ src/state/api_handlers.rs | 9 ++ 10 files changed, 359 insertions(+), 3 deletions(-) create mode 100644 api/lua/input/libinput.lua diff --git a/Cargo.toml b/Cargo.toml index 420ad30..4a60477 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,6 @@ xdg = "2.5.2" lazy_static = "1.4.0" sysinfo = "0.29.10" - [features] default = ["egl", "winit", "udev", "xwayland"] egl = ["smithay/use_system_lib", "smithay/backend_egl"] diff --git a/api/lua/example_config.lua b/api/lua/example_config.lua index 4439645..0c3983c 100644 --- a/api/lua/example_config.lua +++ b/api/lua/example_config.lua @@ -39,6 +39,8 @@ require("pinnacle").setup(function(pinnacle) -- -- dell:set_loc_left_of(lg, "bottom") + input.libinput.set_accel_profile("Flat") + -- Mousebinds -------------------------------------------------------------------- input.mousebind({ "Ctrl" }, buttons.left, "Press", function() diff --git a/api/lua/input.lua b/api/lua/input.lua index bbe9914..3825eee 100644 --- a/api/lua/input.lua +++ b/api/lua/input.lua @@ -43,6 +43,9 @@ local input_module = { --- A table with mouse button codes. You can use indexes (1, 2, and 3 are left, right, and middle) --- or keyed values (buttons.left, buttons.right, etc.). buttons = buttons, + ---@type Libinput + --- Libinput settings. + libinput = require("input.libinput"), } ---Set a keybind. If called with an already existing keybind, it gets replaced. @@ -121,10 +124,12 @@ function input_module.mousebind(modifiers, button, edge, action) }) end ----Set the xkbconfig for your input device. +---Set the xkbconfig for your keyboard. --- ---Fields not present will be set to their default values. --- +---Read `xkeyboard-config(7)` for more information. +--- ---### Example ---```lua ---input.set_xkb_config({ diff --git a/api/lua/input/libinput.lua b/api/lua/input/libinput.lua new file mode 100644 index 0000000..34075e1 --- /dev/null +++ b/api/lua/input/libinput.lua @@ -0,0 +1,213 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later + +---@meta _ + +---@class LibinputSetting +---@field AccelProfile (AccelProfile)? +---@field AccelSpeed float? +---@field CalibrationMatrix float[]? +---@field ClickMethod (ClickMethod)? +---@field DisableWhileTypingEnabled boolean? +---@field LeftHanded boolean? +---@field MiddleEmulationEnabled boolean? +---@field RotationAngle integer? A u32 +---@field ScrollMethod (ScrollMethod)? +---@field NaturalScrollEnabled boolean? +---@field ScrollButton integer? A u32 +---@field TapButtonMap TapButtonMap? +---@field TapDragEnabled boolean? +---@field TapDragLockEnabled boolean? +---@field TapEnabled boolean? + +---@alias AccelProfile +---| "Flat" # Flat pointer acceleration. +---| "Adaptive" Adaptive pointer acceleration. This is the default for most devices. + +---@alias ClickMethod +---| "ButtonAreas" # Use software-button areas to generate button events. +---| "Clickfinger" # The number of fingers decides which button press to generate. + +---@alias ScrollMethod +---| "NoScroll" # Never send scroll events. +---| "TwoFinger" # Send scroll events when two fingers are logically down on the device. +---| "Edge" # Send scroll events when a finger moves along the bottom or right edge of a device. +---| "OnButtonDown" # Send scroll events when a button is down and the device moves along a scroll-capable axis. + +---@alias TapButtonMap +---| "LeftRightMiddle" # 1/2/3 finger tap is mapped to left/right/middle click. +---| "LeftMiddleRight" # 1/2/3 finger tap is mapped to left/middle/right click. + +---Configuration options for libinput. +--- +---Here, you can configure how input devices like your mouse and touchpad function. +---@class Libinput +local libinput = {} + +---Set the acceleration profile. +---@param profile AccelProfile +function libinput.set_accel_profile(profile) + SendMsg({ + SetLibinputSetting = { + AccelProfile = profile, + }, + }) +end + +---Set the acceleration speed. +---@param speed float The speed from -1 to 1. +function libinput.set_accel_speed(speed) + SendMsg({ + SetLibinputSetting = { + AccelSpeed = speed, + }, + }) +end + +---Set the calibration matrix. +---@param matrix float[] A 6-element float array. +function libinput.set_calibration_matrix(matrix) + if #matrix ~= 6 then + return + end + + SendMsg({ + SetLibinputSetting = { + CalibrationMatrix = matrix, + }, + }) +end + +---Set the click method. +--- +---The click method defines when to generate software-emulated buttons, usually on a device +---that does not have a specific physical button available. +---@param method ClickMethod +function libinput.set_click_method(method) + SendMsg({ + SetLibinputSetting = { + ClickMethod = method, + }, + }) +end + +---Set whether or not the device will be disabled while typing. +---@param enabled boolean +function libinput.set_disable_while_typing_enabled(enabled) + SendMsg({ + SetLibinputSetting = { + DisableWhileTypingEnabled = enabled, + }, + }) +end + +---Set device left-handedness. +---@param enabled boolean +function libinput.set_left_handed(enabled) + SendMsg({ + SetLibinputSetting = { + LeftHanded = enabled, + }, + }) +end + +---Set whether or not the middle click can be emulated. +---@param enabled boolean +function libinput.set_middle_emulation_enabled(enabled) + SendMsg({ + SetLibinputSetting = { + MiddleEmulationEnabled = enabled, + }, + }) +end + +---Set the rotation angle of a device. +---@param angle integer An integer in the range [0, 360]. +function libinput.set_rotation_angle(angle) + SendMsg({ + SetLibinputSetting = { + RotationAngle = angle, + }, + }) +end + +---Set the scroll method. +---@param method ScrollMethod +function libinput.set_scroll_method(method) + SendMsg({ + SetLibinputSetting = { + ScrollMethod = method, + }, + }) +end + +---Set whether or not natural scroll is enabled. +--- +---This reverses the direction of scrolling and is mainly used with touchpads. +---@param enabled boolean +function libinput.set_natural_scroll_enabled(enabled) + SendMsg({ + SetLibinputSetting = { + NaturalScrollEnabled = enabled, + }, + }) +end + +---Set the scroll button. +---@param button MouseButton +function libinput.set_scroll_button(button) + SendMsg({ + SetLibinputSetting = { + ScrollButton = button, + }, + }) +end + +---Set the tap button map. +--- +---This determines whether taps with 2 and 3 fingers register as right and middle clicks or the reverse. +---@param map TapButtonMap +function libinput.set_tap_button_map(map) + SendMsg({ + SetLibinputSetting = { + TapButtonMap = map, + }, + }) +end + +---Set whether or not tap-to-click is enabled. +---@param enabled boolean +function libinput.set_tap_enabled(enabled) + SendMsg({ + SetLibinputSetting = { + TapEnabled = enabled, + }, + }) +end + +---Set whether or not tap-and-drag is enabled. +--- +---When enabled, a single-finger tap immediately followed by a finger down results in +---a button down event, and subsequent finger motion thus triggers a drag. The button is released on finger up. +---@param enabled boolean +function libinput.set_tap_drag_enabled(enabled) + SendMsg({ + SetLibinputSetting = { + TapDragEnabled = enabled, + }, + }) +end + +---Set whether or not tap drag lock is enabled. +--- +---When enabled, a finger may be lifted and put back on the touchpad within a timeout and the drag process +---continues. When disabled, lifting the finger during a tap-and-drag will immediately stop the drag. +---@param enabled boolean +function libinput.set_tap_drag_lock_enabled(enabled) + SendMsg({ + SetLibinputSetting = { + TapDragLockEnabled = enabled, + }, + }) +end + +return libinput diff --git a/api/lua/msg.lua b/api/lua/msg.lua index 982944f..612d1fc 100644 --- a/api/lua/msg.lua +++ b/api/lua/msg.lua @@ -30,6 +30,7 @@ ---@field SetOutputLocation { output_name: OutputName, x: integer?, y: integer? }? --Input ---@field SetXkbConfig XkbConfig? +---@field SetLibinputSetting LibinputSetting? ---@field Request Request? ---@alias Msg _Msg | "Quit" diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 4a5cd3b..63248fa 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -259,6 +259,7 @@ pub fn run_udev() -> anyhow::Result<()> { .handle() .insert_source(libinput_backend, move |event, _, data| { // println!("event: {:?}", event); + data.state.apply_libinput_settings(&event); data.state.process_input_event(event); }); diff --git a/src/config.rs b/src/config.rs index 7d0cec4..166379a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -150,6 +150,7 @@ impl State { tracing::debug!("Clearing mouse and keybinds"); self.input_state.keybinds.clear(); self.input_state.mousebinds.clear(); + self.input_state.libinput_settings.clear(); self.config.window_rules.clear(); tracing::debug!("Killing old config"); diff --git a/src/config/api/msg.rs b/src/config/api/msg.rs index 96a1ff2..c48921d 100644 --- a/src/config/api/msg.rs +++ b/src/config/api/msg.rs @@ -8,6 +8,7 @@ pub mod window_rules; use smithay::input::keyboard::ModifiersState; use crate::{ + input::LibinputSetting, layout::Layout, output::OutputName, tag::TagId, @@ -31,7 +32,7 @@ pub enum MouseEdge { Release, } -#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub enum Msg { // Input SetKeybind { @@ -148,6 +149,8 @@ pub enum Msg { options: Option, }, + SetLibinputSetting(LibinputSetting), + Request { request_id: RequestId, request: Request, diff --git a/src/input.rs b/src/input.rs index af612ad..35c23b9 100644 --- a/src/input.rs +++ b/src/input.rs @@ -14,6 +14,7 @@ use smithay::{ AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionEvent, }, + libinput::LibinputInputBackend, session::Session, }, desktop::{layer_map_for_output, space::SpaceElement}, @@ -21,6 +22,7 @@ use smithay::{ keyboard::{keysyms, FilterResult}, pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent}, }, + reexports::input::{self, AccelProfile, ClickMethod, ScrollMethod, TapButtonMap}, utils::{Logical, Point, SERIAL_COUNTER}, wayland::{seat::WaylandFocus, shell::wlr_layer}, }; @@ -35,6 +37,8 @@ pub struct InputState { pub mousebinds: HashMap<(ModifierMask, u32, MouseEdge), CallbackId>, pub reload_keybind: Option<(ModifierMask, u32)>, pub kill_keybind: Option<(ModifierMask, u32)>, + pub libinput_settings: Vec, + pub libinput_devices: Vec, } impl InputState { @@ -51,7 +55,125 @@ enum KeyAction { ReloadConfig, } +#[derive(Debug, serde::Deserialize)] +#[serde(remote = "AccelProfile")] +enum AccelProfileDef { + Flat, + Adaptive, +} + +#[derive(Debug, serde::Deserialize)] +#[serde(remote = "ClickMethod")] +enum ClickMethodDef { + ButtonAreas, + Clickfinger, +} + +#[derive(Debug, serde::Deserialize)] +#[serde(remote = "ScrollMethod")] +enum ScrollMethodDef { + NoScroll, + TwoFinger, + Edge, + OnButtonDown, +} + +#[derive(Debug, serde::Deserialize)] +#[serde(remote = "TapButtonMap")] +enum TapButtonMapDef { + LeftRightMiddle, + LeftMiddleRight, +} + +#[derive(Debug, PartialEq, Copy, Clone, serde::Deserialize)] +pub enum LibinputSetting { + #[serde(with = "AccelProfileDef")] + AccelProfile(AccelProfile), + AccelSpeed(f64), + CalibrationMatrix([f32; 6]), + #[serde(with = "ClickMethodDef")] + ClickMethod(ClickMethod), + DisableWhileTypingEnabled(bool), + LeftHanded(bool), + MiddleEmulationEnabled(bool), + RotationAngle(u32), + #[serde(with = "ScrollMethodDef")] + ScrollMethod(ScrollMethod), + NaturalScrollEnabled(bool), + ScrollButton(u32), + #[serde(with = "TapButtonMapDef")] + TapButtonMap(TapButtonMap), + TapDragEnabled(bool), + TapDragLockEnabled(bool), + TapEnabled(bool), +} + +impl LibinputSetting { + pub fn apply_to_device(&self, device: &mut input::Device) { + let _ = match self { + LibinputSetting::AccelProfile(profile) => device.config_accel_set_profile(*profile), + LibinputSetting::AccelSpeed(speed) => device.config_accel_set_speed(*speed), + LibinputSetting::CalibrationMatrix(matrix) => { + device.config_calibration_set_matrix(*matrix) + } + LibinputSetting::ClickMethod(method) => device.config_click_set_method(*method), + LibinputSetting::DisableWhileTypingEnabled(enabled) => { + device.config_dwt_set_enabled(*enabled) + } + LibinputSetting::LeftHanded(enabled) => device.config_left_handed_set(*enabled), + LibinputSetting::MiddleEmulationEnabled(enabled) => { + device.config_middle_emulation_set_enabled(*enabled) + } + LibinputSetting::RotationAngle(angle) => device.config_rotation_set_angle(*angle), + LibinputSetting::ScrollMethod(method) => device.config_scroll_set_method(*method), + LibinputSetting::NaturalScrollEnabled(enabled) => { + device.config_scroll_set_natural_scroll_enabled(*enabled) + } + LibinputSetting::ScrollButton(button) => device.config_scroll_set_button(*button), + LibinputSetting::TapButtonMap(map) => device.config_tap_set_button_map(*map), + LibinputSetting::TapDragEnabled(enabled) => { + device.config_tap_set_drag_enabled(*enabled) + } + LibinputSetting::TapDragLockEnabled(enabled) => { + device.config_tap_set_drag_lock_enabled(*enabled) + } + LibinputSetting::TapEnabled(enabled) => device.config_tap_set_enabled(*enabled), + }; + } +} + +// We want to completely replace old settings, so we hash only the discriminant. +impl std::hash::Hash for LibinputSetting { + fn hash(&self, state: &mut H) { + core::mem::discriminant(self).hash(state); + } +} + impl State { + /// Apply current libinput settings to new devices. + pub fn apply_libinput_settings(&mut self, event: &InputEvent) { + let mut device = match event { + InputEvent::DeviceAdded { device } => device.clone(), + InputEvent::DeviceRemoved { device } => { + self.input_state + .libinput_devices + .retain(|dev| dev != device); + return; + } + _ => return, + }; + + if self.input_state.libinput_devices.contains(&device) { + return; + } + + for setting in self.input_state.libinput_settings.iter() { + setting.apply_to_device(&mut device); + } + + self.input_state.libinput_devices.push(device); + } + pub fn process_input_event(&mut self, event: InputEvent) { match event { // TODO: rest of input events diff --git a/src/state/api_handlers.rs b/src/state/api_handlers.rs index 406e09b..4d4c17b 100644 --- a/src/state/api_handlers.rs +++ b/src/state/api_handlers.rs @@ -371,6 +371,15 @@ impl State { } } + Msg::SetLibinputSetting(setting) => { + for device in self.input_state.libinput_devices.iter_mut() { + // We're just gonna indiscriminately apply everything and ignore errors + setting.apply_to_device(device); + } + + self.input_state.libinput_settings.push(setting); + } + Msg::Request { request_id, request, From de6bf3ee82b39c93fab92af0754ad455ef10d3be Mon Sep 17 00:00:00 2001 From: Ottatop Date: Thu, 28 Sep 2023 18:46:56 -0500 Subject: [PATCH 3/5] Remove meta tag --- api/lua/input.lua | 1 - api/lua/input/libinput.lua | 2 -- 2 files changed, 3 deletions(-) diff --git a/api/lua/input.lua b/api/lua/input.lua index 3825eee..adca7a8 100644 --- a/api/lua/input.lua +++ b/api/lua/input.lua @@ -43,7 +43,6 @@ local input_module = { --- A table with mouse button codes. You can use indexes (1, 2, and 3 are left, right, and middle) --- or keyed values (buttons.left, buttons.right, etc.). buttons = buttons, - ---@type Libinput --- Libinput settings. libinput = require("input.libinput"), } diff --git a/api/lua/input/libinput.lua b/api/lua/input/libinput.lua index 34075e1..ee8d408 100644 --- a/api/lua/input/libinput.lua +++ b/api/lua/input/libinput.lua @@ -1,7 +1,5 @@ -- SPDX-License-Identifier: GPL-3.0-or-later ----@meta _ - ---@class LibinputSetting ---@field AccelProfile (AccelProfile)? ---@field AccelSpeed float? From 40f90e45d9e1565d49eee931ed46a3ada5a6338a Mon Sep 17 00:00:00 2001 From: Ottatop Date: Thu, 28 Sep 2023 18:56:54 -0500 Subject: [PATCH 4/5] Update example config, remove mouse button type --- api/lua/example_config.lua | 9 ++++++--- api/lua/input/libinput.lua | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/api/lua/example_config.lua b/api/lua/example_config.lua index 0c3983c..5b1fad5 100644 --- a/api/lua/example_config.lua +++ b/api/lua/example_config.lua @@ -31,15 +31,18 @@ require("pinnacle").setup(function(pinnacle) process.set_env("MOZ_ENABLE_WAYLAND", "1") -- Outputs ----------------------------------------------------------------------- - -- You can set your own monitor layout as I have done below for my monitors. - + -- -- local lg = output.get_by_name("DP-2") --[[@as Output]] -- local dell = output.get_by_name("DP-3") --[[@as Output]] -- -- dell:set_loc_left_of(lg, "bottom") - input.libinput.set_accel_profile("Flat") + -- Libinput settings ------------------------------------------------------------- + -- If you want to change settings like pointer acceleration, + -- you can do them in `input.libinput`. + -- + -- input.libinput.set_accel_profile("Flat") -- Mousebinds -------------------------------------------------------------------- diff --git a/api/lua/input/libinput.lua b/api/lua/input/libinput.lua index ee8d408..fc44b26 100644 --- a/api/lua/input/libinput.lua +++ b/api/lua/input/libinput.lua @@ -151,7 +151,7 @@ function libinput.set_natural_scroll_enabled(enabled) end ---Set the scroll button. ----@param button MouseButton +---@param button integer function libinput.set_scroll_button(button) SendMsg({ SetLibinputSetting = { From b7f096dd667a05552cb0526d471693b4acf703f5 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Thu, 28 Sep 2023 19:10:36 -0500 Subject: [PATCH 5/5] Update keyboard leds --- src/input.rs | 142 +++++++++++++++++++++++++++------------------------ 1 file changed, 76 insertions(+), 66 deletions(-) diff --git a/src/input.rs b/src/input.rs index 35c23b9..4529ed6 100644 --- a/src/input.rs +++ b/src/input.rs @@ -22,7 +22,7 @@ use smithay::{ keyboard::{keysyms, FilterResult}, pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent}, }, - reexports::input::{self, AccelProfile, ClickMethod, ScrollMethod, TapButtonMap}, + reexports::input::{self, AccelProfile, ClickMethod, Led, ScrollMethod, TapButtonMap}, utils::{Logical, Point, SERIAL_COUNTER}, wayland::{seat::WaylandFocus, shell::wlr_layer}, }; @@ -274,79 +274,89 @@ impl State { let reload_keybind = self.input_state.reload_keybind; let kill_keybind = self.input_state.kill_keybind; - let action = self - .seat - .get_keyboard() - .expect("Seat has no keyboard") // FIXME: handle err - .input( - self, - event.key_code(), - press_state, - serial, - time, - |state, modifiers, keysym| { - if press_state == KeyState::Pressed { - let mut modifier_mask = Vec::::new(); - if modifiers.alt { - modifier_mask.push(Modifier::Alt); - } - if modifiers.shift { - modifier_mask.push(Modifier::Shift); - } - if modifiers.ctrl { - modifier_mask.push(Modifier::Ctrl); - } - if modifiers.logo { - modifier_mask.push(Modifier::Super); - } - let modifier_mask = ModifierMask::from(modifier_mask); - let raw_sym = keysym.raw_syms().iter().next(); - let mod_sym = keysym.modified_sym(); + let keyboard = self.seat.get_keyboard().expect("Seat has no keyboard"); - let cb_id_mod = state - .input_state - .keybinds - .get(&(modifier_mask, mod_sym)); + let modifiers = keyboard.modifier_state(); - let cb_id_raw = if let Some(raw_sym) = raw_sym { - state.input_state.keybinds.get(&(modifier_mask, *raw_sym)) - } else { - None - }; + let mut leds = Led::empty(); + if modifiers.num_lock { + leds |= Led::NUMLOCK; + } + if modifiers.caps_lock { + leds |= Led::CAPSLOCK; + } - match (cb_id_mod, cb_id_raw) { - (Some(cb_id), _) | (None, Some(cb_id)) => { - return FilterResult::Intercept(KeyAction::CallCallback(*cb_id)); - } - (None, None) => () + // FIXME: Leds only update once another key is pressed. + for device in self.input_state.libinput_devices.iter_mut() { + device.led_update(leds); + } + + let action = keyboard.input( + self, + event.key_code(), + press_state, + serial, + time, + |state, modifiers, keysym| { + if press_state == KeyState::Pressed { + let mut modifier_mask = Vec::::new(); + if modifiers.alt { + modifier_mask.push(Modifier::Alt); + } + if modifiers.shift { + modifier_mask.push(Modifier::Shift); + } + if modifiers.ctrl { + modifier_mask.push(Modifier::Ctrl); + } + if modifiers.logo { + modifier_mask.push(Modifier::Super); + } + let modifier_mask = ModifierMask::from(modifier_mask); + let raw_sym = keysym.raw_syms().iter().next(); + let mod_sym = keysym.modified_sym(); + + let cb_id_mod = state.input_state.keybinds.get(&(modifier_mask, mod_sym)); + + let cb_id_raw = if let Some(raw_sym) = raw_sym { + state.input_state.keybinds.get(&(modifier_mask, *raw_sym)) + } else { + None + }; + + match (cb_id_mod, cb_id_raw) { + (Some(cb_id), _) | (None, Some(cb_id)) => { + return FilterResult::Intercept(KeyAction::CallCallback(*cb_id)); } - - if Some((modifier_mask, mod_sym)) == kill_keybind { - return FilterResult::Intercept(KeyAction::Quit); - } else if Some((modifier_mask, mod_sym)) == reload_keybind { - return FilterResult::Intercept(KeyAction::ReloadConfig); - } else if let mut vt @ keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12 = - keysym.modified_sym() { - vt = vt - keysyms::KEY_XF86Switch_VT_1 + 1; - tracing::info!("Switching to vt {vt}"); - return FilterResult::Intercept(KeyAction::SwitchVt(vt as i32)); - } - + (None, None) => (), } - if keysym.modified_sym() == keysyms::KEY_Control_L { - match press_state { - KeyState::Pressed => { - move_mode = true; - } - KeyState::Released => { - move_mode = false; - } + if Some((modifier_mask, mod_sym)) == kill_keybind { + return FilterResult::Intercept(KeyAction::Quit); + } else if Some((modifier_mask, mod_sym)) == reload_keybind { + return FilterResult::Intercept(KeyAction::ReloadConfig); + } else if let mut vt @ keysyms::KEY_XF86Switch_VT_1 + ..=keysyms::KEY_XF86Switch_VT_12 = keysym.modified_sym() + { + vt = vt - keysyms::KEY_XF86Switch_VT_1 + 1; + tracing::info!("Switching to vt {vt}"); + return FilterResult::Intercept(KeyAction::SwitchVt(vt as i32)); + } + } + + if keysym.modified_sym() == keysyms::KEY_Control_L { + match press_state { + KeyState::Pressed => { + move_mode = true; + } + KeyState::Released => { + move_mode = false; } } - FilterResult::Forward - }, - ); + } + FilterResult::Forward + }, + ); match action { Some(KeyAction::CallCallback(callback_id)) => {