tenseleyflow/hyprkvm / 237c8cf

Browse files

Fix modifier keys not working in virtual keyboard injection

When pressing modifier keys (Shift, Ctrl, Alt, Super), the key event
was sent but the XKB modifier state was never updated. This caused
the compositor to not recognize modifiers as held.

Added ModifierTracker to track pressed modifier keys and send
keyboard.modifiers() calls with the appropriate XKB modifier masks
after each modifier key press/release.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
237c8cfa8edd8214dfd3060d83cab81c9b83d163
Parents
046f70a
Tree
49f04d6

2 changed files

StatusFile+-
M hyprkvm-daemon/src/input/emulation.rs 65 3
M hyprkvm-daemon/src/main.rs 1 1
hyprkvm-daemon/src/input/emulation.rsmodified
@@ -102,11 +102,60 @@ pub struct VirtualKeyboard {
102102
     keyboard: ZwpVirtualKeyboardV1,
103103
     connection: Arc<Connection>,
104104
     keymap_set: bool,
105
+    /// Track pressed modifier keys for state updates
106
+    modifier_state: ModifierTracker,
107
+}
108
+
109
+/// Tracks which modifier keys are currently pressed
110
+#[derive(Default)]
111
+struct ModifierTracker {
112
+    left_shift: bool,
113
+    right_shift: bool,
114
+    left_ctrl: bool,
115
+    right_ctrl: bool,
116
+    left_alt: bool,
117
+    right_alt: bool,
118
+    left_super: bool,
119
+    right_super: bool,
120
+    caps_lock: bool,
121
+}
122
+
123
+impl ModifierTracker {
124
+    /// Update state based on key event, returns true if this was a modifier key
125
+    fn update(&mut self, keycode: u32, pressed: bool) -> bool {
126
+        match keycode {
127
+            42 => { self.left_shift = pressed; true }   // KEY_LEFTSHIFT
128
+            54 => { self.right_shift = pressed; true }  // KEY_RIGHTSHIFT
129
+            29 => { self.left_ctrl = pressed; true }    // KEY_LEFTCTRL
130
+            97 => { self.right_ctrl = pressed; true }   // KEY_RIGHTCTRL
131
+            56 => { self.left_alt = pressed; true }     // KEY_LEFTALT
132
+            100 => { self.right_alt = pressed; true }   // KEY_RIGHTALT
133
+            125 => { self.left_super = pressed; true }  // KEY_LEFTMETA
134
+            126 => { self.right_super = pressed; true } // KEY_RIGHTMETA
135
+            58 => { self.caps_lock = pressed; true }    // KEY_CAPSLOCK
136
+            _ => false,
137
+        }
138
+    }
139
+
140
+    /// Get XKB modifier mask for depressed modifiers
141
+    fn depressed(&self) -> u32 {
142
+        let mut mask = 0u32;
143
+        if self.left_shift || self.right_shift { mask |= 1; }      // Shift
144
+        if self.left_ctrl || self.right_ctrl { mask |= 4; }        // Control
145
+        if self.left_alt || self.right_alt { mask |= 8; }          // Mod1 (Alt)
146
+        if self.left_super || self.right_super { mask |= 64; }     // Mod4 (Super)
147
+        mask
148
+    }
149
+
150
+    /// Get XKB modifier mask for locked modifiers (Caps Lock)
151
+    fn locked(&self) -> u32 {
152
+        if self.caps_lock { 2 } else { 0 }  // Lock bit
153
+    }
105154
 }
106155
 
107156
 impl VirtualKeyboard {
108157
     /// Send key event
109
-    pub fn key(&self, keycode: u32, state: KeyState) {
158
+    pub fn key(&mut self, keycode: u32, state: KeyState) {
110159
         if !self.keymap_set {
111160
             tracing::warn!("Keymap not set, key event may not work correctly");
112161
         }
@@ -116,17 +165,29 @@ impl VirtualKeyboard {
116165
             .unwrap()
117166
             .as_millis() as u32;
118167
 
168
+        let pressed = state == KeyState::Pressed;
119169
         let wl_state = match state {
120170
             KeyState::Pressed => wl_keyboard_key_state::PRESSED,
121171
             KeyState::Released => wl_keyboard_key_state::RELEASED,
122172
         };
123173
 
124
-        // Note: keycode needs to be offset by 8 for evdev->xkb conversion
174
+        // Send the key event
125175
         self.keyboard.key(time, keycode, wl_state);
176
+
177
+        // If this is a modifier key, update and send modifier state
178
+        if self.modifier_state.update(keycode, pressed) {
179
+            self.keyboard.modifiers(
180
+                self.modifier_state.depressed(),
181
+                0, // latched
182
+                self.modifier_state.locked(),
183
+                0, // group
184
+            );
185
+        }
186
+
126187
         let _ = self.connection.flush();
127188
     }
128189
 
129
-    /// Send modifier state
190
+    /// Send modifier state directly
130191
     pub fn modifiers(&self, depressed: u32, latched: u32, locked: u32, group: u32) {
131192
         self.keyboard.modifiers(depressed, latched, locked, group);
132193
         let _ = self.connection.flush();
@@ -340,6 +401,7 @@ impl InputEmulator {
340401
                 keyboard,
341402
                 connection: conn.clone(),
342403
                 keymap_set: true,
404
+                modifier_state: ModifierTracker::default(),
343405
             },
344406
             connection: conn,
345407
             event_queue,
hyprkvm-daemon/src/main.rsmodified
@@ -712,7 +712,7 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> {
712712
                                     Message::InputEvent(input_payload) => {
713713
                                         tracing::trace!("Received input event: {:?}", input_payload);
714714
                                         // Inject input via emulation module
715
-                                        if let Some(ref emu) = input_emulator {
715
+                                        if let Some(ref mut emu) = input_emulator {
716716
                                             use hyprkvm_common::protocol::InputEventType;
717717
                                             match input_payload.event {
718718
                                                 InputEventType::KeyDown { keycode } => {