1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use base::error;
6 use base::warn;
7 use linux_input_sys::constants::LED_CAPSL;
8 use linux_input_sys::constants::LED_NUML;
9 use linux_input_sys::virtio_input_event;
10 use sync::Mutex;
11 use winapi::shared::minwindef::BYTE;
12 use winapi::shared::minwindef::LPARAM;
13 use winapi::shared::minwindef::WPARAM;
14 use winapi::um::errhandlingapi::GetLastError;
15 use winapi::um::winuser::GetKeyboardState;
16 use winapi::um::winuser::MapVirtualKeyW;
17 use winapi::um::winuser::MAPVK_VK_TO_VSC;
18 use winapi::um::winuser::VK_CAPITAL;
19 use winapi::um::winuser::VK_NUMLOCK;
20 use winapi::um::winuser::VK_SCROLL;
21
22 use super::window::GuiWindow;
23 use super::window_message_dispatcher::DisplayEventDispatcher;
24 use crate::gpu_display_win::window_message_processor::WindowMessage;
25 use crate::keycode_converter::KeycodeTranslator;
26 use crate::keycode_converter::KeycodeTypes;
27 use crate::EventDeviceKind;
28
29 // From linux/include/uapi/linux/input-event-codes.h.
30 const LINUX_CAPS_LOCK_KEY: u16 = 58;
31 const LINUX_NUM_LOCK_KEY: u16 = 69;
32
33 pub struct KeyboardInputManager {
34 display_event_dispatcher: DisplayEventDispatcher,
35 keycode_translator: KeycodeTranslator,
36 guest_key_states: Mutex<KeyStates>,
37 }
38
39 impl KeyboardInputManager {
new(display_event_dispatcher: DisplayEventDispatcher) -> KeyboardInputManager40 pub fn new(display_event_dispatcher: DisplayEventDispatcher) -> KeyboardInputManager {
41 Self {
42 display_event_dispatcher,
43 keycode_translator: KeycodeTranslator::new(KeycodeTypes::WindowsScancode),
44 guest_key_states: Mutex::new(KeyStates {
45 caps_lock_state: false,
46 num_lock_state: false,
47 }),
48 }
49 }
50
handle_window_message(&self, window: &GuiWindow, message: &WindowMessage)51 pub fn handle_window_message(&self, window: &GuiWindow, message: &WindowMessage) {
52 match message {
53 WindowMessage::WindowActivate { is_activated } => {
54 self.handle_window_activate(window, *is_activated)
55 }
56 WindowMessage::KeyboardFocus => self.sync_key_states(window),
57 WindowMessage::Key {
58 is_sys_key: _,
59 is_down,
60 w_param,
61 l_param,
62 } => self.handle_host_key_event(window, *is_down, *w_param, *l_param),
63 _ => (),
64 }
65 }
66
handle_guest_event(&self, window: &GuiWindow, event: virtio_input_event)67 pub fn handle_guest_event(&self, window: &GuiWindow, event: virtio_input_event) {
68 if let Some(numlock_on) = event.get_led_state(LED_NUML) {
69 self.guest_key_states.lock().num_lock_state = numlock_on;
70 self.sync_key_states(window);
71 } else if let Some(capslock_on) = event.get_led_state(LED_CAPSL) {
72 self.guest_key_states.lock().caps_lock_state = capslock_on;
73 self.sync_key_states(window);
74 }
75 }
76
77 #[inline]
handle_window_activate(&self, window: &GuiWindow, is_activated: bool)78 fn handle_window_activate(&self, window: &GuiWindow, is_activated: bool) {
79 if !is_activated {
80 // To avoid sticky keys, we release keys when our window is deactivated. This prevents
81 // common shortcuts like Alt+Tab from leaving a stuck alt key in the guest.
82 self.release_any_down_keys(window);
83 }
84 // If either caps lock or num lock is set, reflect that change in the guest.
85 self.sync_key_states(window);
86 }
87
88 #[inline]
handle_host_key_event( &self, window: &GuiWindow, key_down: bool, _w_param: WPARAM, l_param: LPARAM, )89 fn handle_host_key_event(
90 &self,
91 window: &GuiWindow,
92 key_down: bool,
93 _w_param: WPARAM,
94 l_param: LPARAM,
95 ) {
96 let scancode = win_util::scancode_from_lparam(l_param);
97 let is_repeat = key_down && get_previous_key_down_from_lparam(l_param);
98 if let Some(linux_keycode) = self.keycode_translator.translate(scancode) {
99 self.dispatch_linux_key_event(window, linux_keycode, key_down, is_repeat)
100 } else {
101 error!("Unhandled scancode while handling key event.");
102 }
103 }
104
105 /// Checks if the caps lock and num lock key states differ between the guest & host. If they do,
106 /// send keys to the guest to resync it with the host.
sync_key_states(&self, window: &GuiWindow)107 fn sync_key_states(&self, window: &GuiWindow) {
108 if let Some(host_key_states) = get_host_key_states() {
109 let mut toggle_caps_lock = false;
110 let mut toggle_num_lock = false;
111 {
112 let states = self.guest_key_states.lock();
113 if states.caps_lock_state != host_key_states.caps_lock_state {
114 toggle_caps_lock = true;
115 }
116 if states.num_lock_state != host_key_states.num_lock_state {
117 toggle_num_lock = true;
118 }
119 }
120 if toggle_caps_lock {
121 self.press_and_release_key(window, LINUX_CAPS_LOCK_KEY);
122 }
123 if toggle_num_lock {
124 self.press_and_release_key(window, LINUX_NUM_LOCK_KEY);
125 }
126 }
127 }
128
129 /// Releases any keys that are down when the surface is no longer active. Should be called when
130 /// the display window becomes inactive to avoid sticky keys.
131 #[inline]
release_any_down_keys(&self, window: &GuiWindow)132 fn release_any_down_keys(&self, window: &GuiWindow) {
133 let mut events = Vec::with_capacity(256);
134 let mut keyboard_state: [u8; 256] = [0; 256];
135 // SAFETY:
136 // Safe because `keyboard_state` is guaranteed to exist, and is of the expected size.
137 if unsafe { GetKeyboardState(keyboard_state.as_mut_ptr()) } == 0 {
138 error!(
139 "Failed in GetKeyboardState: {}",
140 // SAFETY: trivially safe
141 unsafe { GetLastError() }
142 );
143 return;
144 }
145
146 for (virtual_keycode, key_state) in keyboard_state.iter().enumerate() {
147 // Safe because virtual_keycode < 256.
148 let virtual_keycode = virtual_keycode as i32;
149
150 // Ignore toggle keys.
151 if virtual_keycode == VK_CAPITAL
152 || virtual_keycode == VK_NUMLOCK
153 || virtual_keycode == VK_SCROLL
154 {
155 continue;
156 }
157 if key_state >> 7 == 0u8 {
158 // Key is already up.
159 continue;
160 }
161
162 // SAFETY:
163 // Trivially safe (no pointers or errors to check).
164 let scancode = unsafe { MapVirtualKeyW(virtual_keycode as u32, MAPVK_VK_TO_VSC) };
165 if let Some(linux_keycode) = self.keycode_translator.translate(scancode) {
166 events.push(virtio_input_event::key(linux_keycode, false, false));
167 } else {
168 error!("Failed to translate key while releasing down keys.");
169 }
170 }
171
172 if !events.is_empty() {
173 self.display_event_dispatcher.dispatch(
174 window,
175 events.as_slice(),
176 EventDeviceKind::Keyboard,
177 );
178 }
179 }
180
181 /// Sends a key press and release event to Linux/Android.
press_and_release_key(&self, window: &GuiWindow, key: u16)182 fn press_and_release_key(&self, window: &GuiWindow, key: u16) {
183 self.dispatch_linux_key_event(
184 window, key, /* key_down= */ true, /* is_repeat= */ false,
185 );
186 self.dispatch_linux_key_event(
187 window, key, /* key_down= */ false, /* is_repeat= */ false,
188 );
189 }
190
191 /// Directly dispatches a Linux input keycode to the guest.
dispatch_linux_key_event( &self, window: &GuiWindow, linux_keycode: u16, key_down: bool, is_repeat: bool, )192 fn dispatch_linux_key_event(
193 &self,
194 window: &GuiWindow,
195 linux_keycode: u16,
196 key_down: bool,
197 is_repeat: bool,
198 ) {
199 self.display_event_dispatcher.dispatch(
200 window,
201 &[virtio_input_event::key(linux_keycode, key_down, is_repeat)],
202 EventDeviceKind::Keyboard,
203 );
204 }
205 }
206
207 struct KeyStates {
208 caps_lock_state: bool,
209 num_lock_state: bool,
210 }
211
212 /// On success, returns a tuple containing current state of caps lock and num lock keys.
get_host_key_states() -> Option<KeyStates>213 fn get_host_key_states() -> Option<KeyStates> {
214 let mut keyboard_state: [BYTE; 256] = [0; 256];
215 // SAFETY:
216 // Safe because `keyboard_state` is guaranteed to exist, and is of the expected size.
217 if unsafe { GetKeyboardState(keyboard_state.as_mut_ptr()) } != 0 {
218 Some(KeyStates {
219 caps_lock_state: toggle_to_bool(keyboard_state[VK_CAPITAL as usize]),
220 num_lock_state: toggle_to_bool(keyboard_state[VK_NUMLOCK as usize]),
221 })
222 } else {
223 warn!(
224 "Failed in GetKeyboardState: {}",
225 // SAFETY: trivially safe
226 unsafe { GetLastError() }
227 );
228 None
229 }
230 }
231
232 /// Returns whether the given toggle key state indicates the key is toggled/on (true).
toggle_to_bool(key_state: BYTE) -> bool233 fn toggle_to_bool(key_state: BYTE) -> bool {
234 key_state & 0x1 == 0x1
235 }
236
237 /// Extracts the previous key up/down state from the l_param of a WM_KEY/WM_SYSKEY DOWN event.
238 /// The previous key state is bit #30.
get_previous_key_down_from_lparam(l_param: isize) -> bool239 fn get_previous_key_down_from_lparam(l_param: isize) -> bool {
240 ((l_param >> 30) & 1) == 1
241 }
242