• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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