@@ -102,11 +102,60 @@ pub struct VirtualKeyboard { |
| 102 | 102 | keyboard: ZwpVirtualKeyboardV1, |
| 103 | 103 | connection: Arc<Connection>, |
| 104 | 104 | 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 | + } |
| 105 | 154 | } |
| 106 | 155 | |
| 107 | 156 | impl VirtualKeyboard { |
| 108 | 157 | /// Send key event |
| 109 | | - pub fn key(&self, keycode: u32, state: KeyState) { |
| 158 | + pub fn key(&mut self, keycode: u32, state: KeyState) { |
| 110 | 159 | if !self.keymap_set { |
| 111 | 160 | tracing::warn!("Keymap not set, key event may not work correctly"); |
| 112 | 161 | } |
@@ -116,17 +165,29 @@ impl VirtualKeyboard { |
| 116 | 165 | .unwrap() |
| 117 | 166 | .as_millis() as u32; |
| 118 | 167 | |
| 168 | + let pressed = state == KeyState::Pressed; |
| 119 | 169 | let wl_state = match state { |
| 120 | 170 | KeyState::Pressed => wl_keyboard_key_state::PRESSED, |
| 121 | 171 | KeyState::Released => wl_keyboard_key_state::RELEASED, |
| 122 | 172 | }; |
| 123 | 173 | |
| 124 | | - // Note: keycode needs to be offset by 8 for evdev->xkb conversion |
| 174 | + // Send the key event |
| 125 | 175 | 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 | + |
| 126 | 187 | let _ = self.connection.flush(); |
| 127 | 188 | } |
| 128 | 189 | |
| 129 | | - /// Send modifier state |
| 190 | + /// Send modifier state directly |
| 130 | 191 | pub fn modifiers(&self, depressed: u32, latched: u32, locked: u32, group: u32) { |
| 131 | 192 | self.keyboard.modifiers(depressed, latched, locked, group); |
| 132 | 193 | let _ = self.connection.flush(); |
@@ -340,6 +401,7 @@ impl InputEmulator { |
| 340 | 401 | keyboard, |
| 341 | 402 | connection: conn.clone(), |
| 342 | 403 | keymap_set: true, |
| 404 | + modifier_state: ModifierTracker::default(), |
| 343 | 405 | }, |
| 344 | 406 | connection: conn, |
| 345 | 407 | event_queue, |