windows: Better keyboard input support (#9180)

张小白 created

### Description

Currently, there are some issues with input handling on Windows:

#### 1. Direct crash when encountering IME input.



https://github.com/zed-industries/zed/assets/14981363/598f7272-1948-4a42-99c5-2ef7b9162a1e


#### 2. Handling messages every 1/60 seconds in the main thread. Despite
being named "immediate_handle," it's not exactly immediate.

```rust
// actually halt here
let wait_result =
    unsafe { DCompositionWaitForCompositorClock(Some(&[self.inner.event]), INFINITE) };

// compositor clock ticked so we should draw a frame
if wait_result == 1 {
    unsafe { invalidate_thread_windows(GetCurrentThreadId()) };

    while unsafe { PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE) }.as_bool()
```

#### 3. According to Windows recommendations, character input should be
obtained using `WM_CHAR` instead of `WM_KEYDOWN`. Additionally, there
are problems with the handling within `WM_CHAR`.

```rust
fn handle_char_msg(&self, wparam: WPARAM) -> LRESULT {
        let mut callbacks = self.callbacks.borrow_mut();
        if let Some(callback) = callbacks.input.as_mut() {
            let modifiers = self.current_modifiers();
            let msg_char = wparam.0 as u8 as char; // these are u16 chars, cant treat them as u8
```

And, we don't handle `WM_SYSKEYDOWN` properly, which leads to `Alt + F4`
not working.

Release Notes:

- N/A

Change summary

Cargo.toml                                   |  28 
crates/gpui/src/platform/windows/platform.rs | 125 ++----
crates/gpui/src/platform/windows/window.rs   | 396 +++++++++++++--------
3 files changed, 304 insertions(+), 245 deletions(-)

Detailed changes

Cargo.toml 🔗

@@ -323,7 +323,13 @@ url = "2.2"
 uuid = { version = "1.1.2", features = ["v4"] }
 wasmparser = "0.121"
 wasm-encoder = "0.41"
-wasmtime = { version = "18.0", default-features = false, features = ["async", "demangle", "runtime", "cranelift", "component-model"] }
+wasmtime = { version = "18.0", default-features = false, features = [
+    "async",
+    "demangle",
+    "runtime",
+    "cranelift",
+    "component-model",
+] }
 wasmtime-wasi = "18.0"
 which = "6.0.0"
 wit-component = "0.20"
@@ -334,23 +340,21 @@ version = "0.53.0"
 features = [
     "implement",
     "Wdk_System_SystemServices",
-    "Win32_Graphics_Gdi",
     "Win32_Graphics_DirectComposition",
-    "Win32_UI_Controls",
-    "Win32_UI_WindowsAndMessaging",
-    "Win32_UI_Input_KeyboardAndMouse",
-    "Win32_UI_Shell",
-    "Win32_System_Com",
-    "Win32_System_SystemInformation",
-    "Win32_System_SystemServices",
-    "Win32_System_Time",
+    "Win32_Graphics_Gdi",
     "Win32_Security",
     "Win32_System_Com",
     "Win32_System_Com_StructuredStorage",
-    "Win32_System_Threading",
     "Win32_System_DataExchange",
     "Win32_System_Ole",
-    "Win32_System_Com",
+    "Win32_System_SystemInformation",
+    "Win32_System_SystemServices",
+    "Win32_System_Time",
+    "Win32_System_Threading",
+    "Win32_UI_Controls",
+    "Win32_UI_Input_KeyboardAndMouse",
+    "Win32_UI_Shell",
+    "Win32_UI_WindowsAndMessaging",
 ]
 
 [patch.crates-io]

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -3,7 +3,6 @@
 
 use std::{
     cell::{Cell, RefCell},
-    collections::HashSet,
     ffi::{c_uint, c_void, OsString},
     os::windows::ffi::{OsStrExt, OsStringExt},
     path::{Path, PathBuf},
@@ -17,19 +16,23 @@ use async_task::Runnable;
 use copypasta::{ClipboardContext, ClipboardProvider};
 use futures::channel::oneshot::{self, Receiver};
 use itertools::Itertools;
-use parking_lot::Mutex;
+use parking_lot::{Mutex, RwLock};
+use smallvec::SmallVec;
 use time::UtcOffset;
 use util::{ResultExt, SemanticVersion};
 use windows::{
     core::{IUnknown, HRESULT, HSTRING, PCWSTR, PWSTR},
     Wdk::System::SystemServices::RtlGetVersion,
     Win32::{
-        Foundation::{CloseHandle, BOOL, HANDLE, HWND, LPARAM, TRUE},
-        Graphics::DirectComposition::DCompositionWaitForCompositorClock,
+        Foundation::{CloseHandle, HANDLE, HWND},
+        Graphics::{
+            DirectComposition::DCompositionWaitForCompositorClock,
+            Gdi::{RedrawWindow, HRGN, RDW_INVALIDATE, RDW_UPDATENOW},
+        },
         System::{
             Com::{CoCreateInstance, CreateBindCtx, CLSCTX_ALL},
             Ole::{OleInitialize, OleUninitialize},
-            Threading::{CreateEventW, GetCurrentThreadId, INFINITE},
+            Threading::{CreateEventW, INFINITE},
             Time::{GetTimeZoneInformation, TIME_ZONE_ID_INVALID},
         },
         UI::{
@@ -40,21 +43,21 @@ use windows::{
                 FOS_ALLOWMULTISELECT, FOS_FILEMUSTEXIST, FOS_PICKFOLDERS, SIGDN_FILESYSPATH,
             },
             WindowsAndMessaging::{
-                DispatchMessageW, EnumThreadWindows, LoadImageW, PeekMessageW, PostQuitMessage,
-                SetCursor, SystemParametersInfoW, TranslateMessage, HCURSOR, IDC_ARROW, IDC_CROSS,
-                IDC_HAND, IDC_IBEAM, IDC_NO, IDC_SIZENS, IDC_SIZEWE, IMAGE_CURSOR, LR_DEFAULTSIZE,
-                LR_SHARED, MSG, PM_REMOVE, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES,
-                SW_SHOWDEFAULT, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE,
+                DispatchMessageW, LoadImageW, PeekMessageW, PostQuitMessage, SetCursor,
+                SystemParametersInfoW, TranslateMessage, HCURSOR, IDC_ARROW, IDC_CROSS, IDC_HAND,
+                IDC_IBEAM, IDC_NO, IDC_SIZENS, IDC_SIZEWE, IMAGE_CURSOR, LR_DEFAULTSIZE, LR_SHARED,
+                MSG, PM_REMOVE, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SW_SHOWDEFAULT,
+                SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE,
             },
         },
     },
 };
 
 use crate::{
-    try_get_window_inner, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle,
-    ForegroundExecutor, Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput,
-    PlatformTextSystem, PlatformWindow, Task, WindowAppearance, WindowOptions, WindowParams,
-    WindowsDispatcher, WindowsDisplay, WindowsTextSystem, WindowsWindow,
+    Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor,
+    Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem,
+    PlatformWindow, Task, WindowAppearance, WindowParams, WindowsDispatcher, WindowsDisplay,
+    WindowsTextSystem, WindowsWindow,
 };
 
 pub(crate) struct WindowsPlatform {
@@ -72,15 +75,13 @@ pub(crate) struct WindowsPlatformSystemSettings {
     pub(crate) wheel_scroll_lines: u32,
 }
 
-type WindowHandleValues = HashSet<isize>;
-
 pub(crate) struct WindowsPlatformInner {
     background_executor: BackgroundExecutor,
     pub(crate) foreground_executor: ForegroundExecutor,
     main_receiver: flume::Receiver<Runnable>,
     text_system: Arc<WindowsTextSystem>,
     callbacks: Mutex<Callbacks>,
-    pub(crate) window_handle_values: RefCell<WindowHandleValues>,
+    pub raw_window_handles: RwLock<SmallVec<[HWND; 4]>>,
     pub(crate) event: HANDLE,
     pub(crate) settings: RefCell<WindowsPlatformSystemSettings>,
 }
@@ -167,7 +168,7 @@ impl WindowsPlatform {
         let foreground_executor = ForegroundExecutor::new(dispatcher);
         let text_system = Arc::new(WindowsTextSystem::new());
         let callbacks = Mutex::new(Callbacks::default());
-        let window_handle_values = RefCell::new(HashSet::new());
+        let raw_window_handles = RwLock::new(SmallVec::new());
         let settings = RefCell::new(WindowsPlatformSystemSettings::new());
         let inner = Rc::new(WindowsPlatformInner {
             background_executor,
@@ -175,64 +176,31 @@ impl WindowsPlatform {
             main_receiver,
             text_system,
             callbacks,
-            window_handle_values,
+            raw_window_handles,
             event,
             settings,
         });
         Self { inner }
     }
 
-    /// runs message handlers that should be processed before dispatching to prevent translating unnecessary messages
-    /// returns true if message is handled and should not dispatch
-    fn run_immediate_msg_handlers(&self, msg: &MSG) -> bool {
-        if msg.message == WM_SETTINGCHANGE {
-            self.inner.settings.borrow_mut().update_all();
-            return true;
-        }
-
-        if !self
-            .inner
-            .window_handle_values
-            .borrow()
-            .contains(&msg.hwnd.0)
-        {
-            return false;
-        }
-
-        if let Some(inner) = try_get_window_inner(msg.hwnd) {
-            inner.handle_immediate_msg(msg.message, msg.wParam, msg.lParam)
-        } else {
-            false
-        }
-    }
-
     fn run_foreground_tasks(&self) {
         for runnable in self.inner.main_receiver.drain() {
             runnable.run();
         }
     }
-}
 
-unsafe extern "system" fn invalidate_window_callback(hwnd: HWND, lparam: LPARAM) -> BOOL {
-    let window_handle_values = unsafe { &*(lparam.0 as *const WindowHandleValues) };
-    if !window_handle_values.contains(&hwnd.0) {
-        return TRUE;
-    }
-    if let Some(inner) = try_get_window_inner(hwnd) {
-        inner.invalidate_client_area();
+    fn redraw_all(&self) {
+        for handle in self.inner.raw_window_handles.read().iter() {
+            unsafe {
+                RedrawWindow(
+                    *handle,
+                    None,
+                    HRGN::default(),
+                    RDW_INVALIDATE | RDW_UPDATENOW,
+                );
+            }
+        }
     }
-    TRUE
-}
-
-/// invalidates all windows belonging to a thread causing a paint message to be scheduled
-fn invalidate_thread_windows(win32_thread_id: u32, window_handle_values: &WindowHandleValues) {
-    unsafe {
-        EnumThreadWindows(
-            win32_thread_id,
-            Some(invalidate_window_callback),
-            LPARAM(window_handle_values as *const _ as isize),
-        )
-    };
 }
 
 impl Platform for WindowsPlatform {
@@ -250,35 +218,34 @@ impl Platform for WindowsPlatform {
 
     fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
         on_finish_launching();
+        let dispatch_event = self.inner.event;
+
         'a: loop {
             let mut msg = MSG::default();
             // will be 0 if woken up by self.inner.event or 1 if the compositor clock ticked
             // SEE: https://learn.microsoft.com/en-us/windows/win32/directcomp/compositor-clock/compositor-clock
             let wait_result =
-                unsafe { DCompositionWaitForCompositorClock(Some(&[self.inner.event]), INFINITE) };
+                unsafe { DCompositionWaitForCompositorClock(Some(&[dispatch_event]), INFINITE) };
 
             // compositor clock ticked so we should draw a frame
             if wait_result == 1 {
+                self.redraw_all();
                 unsafe {
-                    invalidate_thread_windows(
-                        GetCurrentThreadId(),
-                        &self.inner.window_handle_values.borrow(),
-                    )
-                };
-
-                while unsafe { PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE) }.as_bool()
-                {
-                    if msg.message == WM_QUIT {
-                        break 'a;
-                    }
+                    let mut msg = MSG::default();
 
-                    if !self.run_immediate_msg_handlers(&msg) {
-                        unsafe { TranslateMessage(&msg) };
-                        unsafe { DispatchMessageW(&msg) };
+                    while PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE).as_bool() {
+                        if msg.message == WM_QUIT {
+                            break 'a;
+                        }
+                        if msg.message == WM_SETTINGCHANGE {
+                            self.inner.settings.borrow_mut().update_all();
+                            continue;
+                        }
+                        TranslateMessage(&msg);
+                        DispatchMessageW(&msg);
                     }
                 }
             }
-
             self.run_foreground_tasks();
         }
 

crates/gpui/src/platform/windows/window.rs 🔗

@@ -40,9 +40,9 @@ use windows::{
                 TD_INFORMATION_ICON, TD_WARNING_ICON,
             },
             Input::KeyboardAndMouse::{
-                GetKeyState, VIRTUAL_KEY, VK_BACK, VK_CONTROL, VK_DOWN, VK_END, VK_ESCAPE, VK_F1,
-                VK_F24, VK_HOME, VK_INSERT, VK_LEFT, VK_LWIN, VK_MENU, VK_NEXT, VK_PRIOR,
-                VK_RETURN, VK_RIGHT, VK_RWIN, VK_SHIFT, VK_SPACE, VK_TAB, VK_UP,
+                GetKeyState, VIRTUAL_KEY, VK_0, VK_A, VK_BACK, VK_CONTROL, VK_DOWN, VK_END,
+                VK_ESCAPE, VK_F1, VK_F24, VK_HOME, VK_INSERT, VK_LEFT, VK_LWIN, VK_MENU, VK_NEXT,
+                VK_PRIOR, VK_RETURN, VK_RIGHT, VK_RWIN, VK_SHIFT, VK_TAB, VK_UP,
             },
             Shell::{DragQueryFileW, HDROP},
             WindowsAndMessaging::{
@@ -52,9 +52,9 @@ use windows::{
                 WINDOW_EX_STYLE, WINDOW_LONG_PTR_INDEX, WM_CHAR, WM_CLOSE, WM_DESTROY, WM_KEYDOWN,
                 WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
                 WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOVE, WM_NCCREATE, WM_NCDESTROY,
-                WM_PAINT, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN,
-                WM_SYSKEYUP, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_OVERLAPPEDWINDOW,
-                WS_VISIBLE, XBUTTON1, XBUTTON2,
+                WM_PAINT, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SIZE, WM_SYSKEYDOWN, WM_SYSKEYUP,
+                WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_OVERLAPPEDWINDOW, WS_VISIBLE, XBUTTON1,
+                XBUTTON2,
             },
         },
     },
@@ -68,25 +68,6 @@ use crate::{
     WindowAppearance, WindowParams, WindowsDisplay, WindowsPlatformInner,
 };
 
-#[derive(PartialEq)]
-pub(crate) enum CallbackResult {
-    /// handled by system or user callback
-    Handled {
-        /// `true` if user callback handled event
-        by_callback: bool,
-    },
-    Unhandled,
-}
-
-impl CallbackResult {
-    pub fn is_handled(&self) -> bool {
-        match self {
-            Self::Handled { by_callback: _ } => true,
-            _ => false,
-        }
-    }
-}
-
 pub(crate) struct WindowsWindowInner {
     hwnd: HWND,
     origin: Cell<Point<GlobalPixels>>,
@@ -182,15 +163,6 @@ impl WindowsWindowInner {
         unsafe { InvalidateRect(self.hwnd, None, FALSE) };
     }
 
-    /// returns true if message is handled and should not dispatch
-    pub(crate) fn handle_immediate_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> bool {
-        match msg {
-            WM_KEYDOWN | WM_SYSKEYDOWN => self.handle_keydown_msg(wparam).is_handled(),
-            WM_KEYUP | WM_SYSKEYUP => self.handle_keyup_msg(wparam).is_handled(),
-            _ => false,
-        }
-    }
-
     fn handle_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
         log::debug!("msg: {msg}, wparam: {}, lparam: {}", wparam.0, lparam.0);
         match msg {
@@ -234,9 +206,11 @@ impl WindowsWindowInner {
             }
             WM_MOUSEWHEEL => self.handle_mouse_wheel_msg(wparam, lparam),
             WM_MOUSEHWHEEL => self.handle_mouse_horizontal_wheel_msg(wparam, lparam),
-            WM_CHAR | WM_SYSCHAR => self.handle_char_msg(wparam),
-            // These events are handled by the immediate handler
-            WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP => LRESULT(0),
+            WM_SYSKEYDOWN => self.handle_syskeydown_msg(msg, wparam, lparam),
+            WM_SYSKEYUP => self.handle_syskeyup_msg(msg, wparam, lparam),
+            WM_KEYDOWN => self.handle_keydown_msg(msg, wparam, lparam),
+            WM_KEYUP => self.handle_keyup_msg(msg, wparam),
+            WM_CHAR => self.handle_char_msg(msg, wparam, lparam),
             _ => unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) },
         }
     }
@@ -302,9 +276,15 @@ impl WindowsWindowInner {
         if let Some(callback) = callbacks.close.take() {
             callback()
         }
-        let mut window_handles = self.platform_inner.window_handle_values.borrow_mut();
-        window_handles.remove(&self.hwnd.0);
-        if window_handles.is_empty() {
+        let index = self
+            .platform_inner
+            .raw_window_handles
+            .read()
+            .iter()
+            .position(|handle| *handle == self.hwnd)
+            .unwrap();
+        self.platform_inner.raw_window_handles.write().remove(index);
+        if self.platform_inner.raw_window_handles.read().is_empty() {
             self.platform_inner
                 .foreground_executor
                 .spawn(async {
@@ -345,155 +325,210 @@ impl WindowsWindowInner {
         LRESULT(1)
     }
 
-    fn parse_key_msg_keystroke(&self, wparam: WPARAM) -> Option<Keystroke> {
-        let vk_code = wparam.loword();
+    fn parse_syskeydown_msg_keystroke(&self, wparam: WPARAM) -> Option<Keystroke> {
+        let modifiers = self.current_modifiers();
+        if !modifiers.alt {
+            // on Windows, F10 can trigger this event, not just the alt key
+            // and we just don't care about F10
+            return None;
+        }
 
-        // 0-9 https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
-        if vk_code >= 0x30 && vk_code <= 0x39 {
-            let modifiers = self.current_modifiers();
+        let vk_code = wparam.loword();
+        let basic_key = basic_vkcode_to_string(vk_code, modifiers);
+        if basic_key.is_some() {
+            return basic_key;
+        }
 
-            if modifiers.shift {
-                return None;
-            }
+        let key = match VIRTUAL_KEY(vk_code) {
+            VK_BACK => Some("backspace"),
+            VK_RETURN => Some("enter"),
+            VK_TAB => Some("tab"),
+            VK_UP => Some("up"),
+            VK_DOWN => Some("down"),
+            VK_RIGHT => Some("right"),
+            VK_LEFT => Some("left"),
+            VK_HOME => Some("home"),
+            VK_END => Some("end"),
+            VK_PRIOR => Some("pageup"),
+            VK_NEXT => Some("pagedown"),
+            VK_ESCAPE => Some("escape"),
+            VK_INSERT => Some("insert"),
+            _ => None,
+        };
 
-            let digit_char = (b'0' + ((vk_code - 0x30) as u8)) as char;
-            return Some(Keystroke {
+        if let Some(key) = key {
+            Some(Keystroke {
                 modifiers,
-                key: digit_char.to_string(),
-                ime_key: Some(digit_char.to_string()),
-            });
+                key: key.to_string(),
+                ime_key: None,
+            })
+        } else {
+            None
         }
+    }
 
-        // A-Z https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
-        if vk_code >= 0x41 && vk_code <= 0x5A {
-            let offset = (vk_code - 0x41) as u8;
-            let alpha_char = (b'a' + offset) as char;
-            let alpha_char_upper = (b'A' + offset) as char;
-            let modifiers = self.current_modifiers();
-            return Some(Keystroke {
-                modifiers,
-                key: alpha_char.to_string(),
-                ime_key: Some(if modifiers.shift {
-                    alpha_char_upper.to_string()
-                } else {
-                    alpha_char.to_string()
-                }),
-            });
+    fn parse_keydown_msg_keystroke(&self, wparam: WPARAM) -> Option<Keystroke> {
+        let vk_code = wparam.loword();
+
+        let modifiers = self.current_modifiers();
+        if modifiers.control || modifiers.alt {
+            let basic_key = basic_vkcode_to_string(vk_code, modifiers);
+            if basic_key.is_some() {
+                return basic_key;
+            }
         }
 
         if vk_code >= VK_F1.0 && vk_code <= VK_F24.0 {
             let offset = vk_code - VK_F1.0;
             return Some(Keystroke {
-                modifiers: self.current_modifiers(),
+                modifiers,
                 key: format!("f{}", offset + 1),
                 ime_key: None,
             });
         }
 
         let key = match VIRTUAL_KEY(vk_code) {
-            VK_SPACE => Some(("space", Some(" "))),
-            VK_TAB => Some(("tab", Some("\t"))),
-            VK_BACK => Some(("backspace", None)),
-            VK_RETURN => Some(("enter", None)),
-            VK_UP => Some(("up", None)),
-            VK_DOWN => Some(("down", None)),
-            VK_RIGHT => Some(("right", None)),
-            VK_LEFT => Some(("left", None)),
-            VK_HOME => Some(("home", None)),
-            VK_END => Some(("end", None)),
-            VK_PRIOR => Some(("pageup", None)),
-            VK_NEXT => Some(("pagedown", None)),
-            VK_ESCAPE => Some(("escape", None)),
-            VK_INSERT => Some(("insert", None)),
+            VK_BACK => Some("backspace"),
+            VK_RETURN => Some("enter"),
+            VK_TAB => Some("tab"),
+            VK_UP => Some("up"),
+            VK_DOWN => Some("down"),
+            VK_RIGHT => Some("right"),
+            VK_LEFT => Some("left"),
+            VK_HOME => Some("home"),
+            VK_END => Some("end"),
+            VK_PRIOR => Some("pageup"),
+            VK_NEXT => Some("pagedown"),
+            VK_ESCAPE => Some("escape"),
+            VK_INSERT => Some("insert"),
             _ => None,
         };
 
-        if let Some((key, ime_key)) = key {
+        if let Some(key) = key {
             Some(Keystroke {
-                modifiers: self.current_modifiers(),
+                modifiers,
                 key: key.to_string(),
-                ime_key: ime_key.map(|k| k.to_string()),
+                ime_key: None,
             })
         } else {
             None
         }
     }
 
-    fn handle_keydown_msg(&self, wparam: WPARAM) -> CallbackResult {
-        let mut callbacks = self.callbacks.borrow_mut();
-        let keystroke = self.parse_key_msg_keystroke(wparam);
-        if let Some(keystroke) = keystroke {
-            if let Some(callback) = callbacks.input.as_mut() {
-                let ime_key = keystroke.ime_key.clone();
-                let event = KeyDownEvent {
-                    keystroke,
-                    is_held: true,
-                };
-
-                if callback(PlatformInput::KeyDown(event)) {
-                    CallbackResult::Handled { by_callback: true }
-                } else if let Some(mut input_handler) = self.input_handler.take() {
-                    if let Some(ime_key) = ime_key {
-                        input_handler.replace_text_in_range(None, &ime_key);
-                    }
-                    self.input_handler.set(Some(input_handler));
-                    CallbackResult::Handled { by_callback: true }
-                } else {
-                    CallbackResult::Handled { by_callback: false }
-                }
-            } else {
-                CallbackResult::Handled { by_callback: false }
-            }
+    fn parse_char_msg_keystroke(&self, wparam: WPARAM) -> Option<Keystroke> {
+        let src = [wparam.0 as u16];
+        let Ok(first_char) = char::decode_utf16(src).collect::<Vec<_>>()[0] else {
+            return None;
+        };
+        if first_char.is_control() {
+            None
         } else {
-            CallbackResult::Unhandled
+            Some(Keystroke {
+                modifiers: self.current_modifiers(),
+                key: first_char.to_lowercase().to_string(),
+                ime_key: Some(first_char.to_string()),
+            })
         }
     }
 
-    fn handle_keyup_msg(&self, wparam: WPARAM) -> CallbackResult {
-        let mut callbacks = self.callbacks.borrow_mut();
-        let keystroke = self.parse_key_msg_keystroke(wparam);
-        if let Some(keystroke) = keystroke {
-            if let Some(callback) = callbacks.input.as_mut() {
-                let event = KeyUpEvent { keystroke };
-                let by_callback = callback(PlatformInput::KeyUp(event));
-                CallbackResult::Handled { by_callback }
-            } else {
-                CallbackResult::Handled { by_callback: false }
-            }
-        } else {
-            CallbackResult::Unhandled
+    fn handle_syskeydown_msg(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
+        // we need to call `DefWindowProcW`, or we will lose the system-wide `Alt+F4`, `Alt+{other keys}`
+        // shortcuts.
+        let Some(keystroke) = self.parse_syskeydown_msg_keystroke(wparam) else {
+            return unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) };
+        };
+        let Some(ref mut func) = self.callbacks.borrow_mut().input else {
+            return unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) };
+        };
+        let event = KeyDownEvent {
+            keystroke,
+            is_held: lparam.0 & (0x1 << 30) > 0,
+        };
+        if func(PlatformInput::KeyDown(event)) {
+            self.invalidate_client_area();
+            return LRESULT(0);
         }
+        unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) }
     }
 
-    fn handle_char_msg(&self, wparam: WPARAM) -> LRESULT {
-        let mut callbacks = self.callbacks.borrow_mut();
-        if let Some(callback) = callbacks.input.as_mut() {
-            let modifiers = self.current_modifiers();
-            let msg_char = wparam.0 as u8 as char;
-            let keystroke = Keystroke {
-                modifiers,
-                key: msg_char.to_string(),
-                ime_key: Some(msg_char.to_string()),
-            };
-            let ime_key = keystroke.ime_key.clone();
-            let event = KeyDownEvent {
-                keystroke,
-                is_held: false,
-            };
+    fn handle_syskeyup_msg(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
+        // we need to call `DefWindowProcW`, or we will lose the system-wide `Alt+F4`, `Alt+{other keys}`
+        // shortcuts.
+        let Some(keystroke) = self.parse_syskeydown_msg_keystroke(wparam) else {
+            return unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) };
+        };
+        let Some(ref mut func) = self.callbacks.borrow_mut().input else {
+            return unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) };
+        };
+        let event = KeyUpEvent { keystroke };
+        if func(PlatformInput::KeyUp(event)) {
+            self.invalidate_client_area();
+            return LRESULT(0);
+        }
+        unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) }
+    }
 
-            if callback(PlatformInput::KeyDown(event)) {
-                return LRESULT(0);
-            }
+    fn handle_keydown_msg(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
+        let Some(keystroke) = self.parse_keydown_msg_keystroke(wparam) else {
+            return LRESULT(1);
+        };
+        let Some(ref mut func) = self.callbacks.borrow_mut().input else {
+            return LRESULT(1);
+        };
+        let event = KeyDownEvent {
+            keystroke,
+            is_held: lparam.0 & (0x1 << 30) > 0,
+        };
+        if func(PlatformInput::KeyDown(event)) {
+            self.invalidate_client_area();
+            return LRESULT(0);
+        }
+        LRESULT(1)
+    }
 
-            if let Some(mut input_handler) = self.input_handler.take() {
-                if let Some(ime_key) = ime_key {
-                    input_handler.replace_text_in_range(None, &ime_key);
-                }
-                self.input_handler.set(Some(input_handler));
-                return LRESULT(0);
-            }
+    fn handle_keyup_msg(&self, message: u32, wparam: WPARAM) -> LRESULT {
+        let Some(keystroke) = self.parse_keydown_msg_keystroke(wparam) else {
+            return LRESULT(1);
+        };
+        let Some(ref mut func) = self.callbacks.borrow_mut().input else {
+            return LRESULT(1);
+        };
+        let event = KeyUpEvent { keystroke };
+        if func(PlatformInput::KeyUp(event)) {
+            self.invalidate_client_area();
+            return LRESULT(0);
         }
-        return LRESULT(1);
+        LRESULT(1)
+    }
+
+    fn handle_char_msg(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
+        let Some(keystroke) = self.parse_char_msg_keystroke(wparam) else {
+            return LRESULT(1);
+        };
+        let mut callbacks = self.callbacks.borrow_mut();
+        let Some(ref mut func) = callbacks.input else {
+            return LRESULT(1);
+        };
+        let ime_key = keystroke.ime_key.clone();
+        let event = KeyDownEvent {
+            keystroke,
+            is_held: lparam.0 & (0x1 << 30) > 0,
+        };
+        if func(PlatformInput::KeyDown(event)) {
+            self.invalidate_client_area();
+            return LRESULT(0);
+        }
+        drop(callbacks);
+        let Some(ime_char) = ime_key else {
+            return LRESULT(1);
+        };
+        let Some(mut input_handler) = self.input_handler.take() else {
+            return LRESULT(1);
+        };
+        input_handler.replace_text_in_range(None, &ime_char);
+        self.input_handler.set(Some(input_handler));
+        self.invalidate_client_area();
+        LRESULT(0)
     }
 
     fn handle_mouse_down_msg(&self, button: MouseButton, lparam: LPARAM) -> LRESULT {
@@ -671,9 +706,9 @@ impl WindowsWindow {
             drag_drop_handler,
         };
         platform_inner
-            .window_handle_values
-            .borrow_mut()
-            .insert(wnd.inner.hwnd.0);
+            .raw_window_handles
+            .write()
+            .push(wnd.inner.hwnd);
 
         unsafe { ShowWindow(wnd.inner.hwnd, SW_SHOW) };
         wnd
@@ -1134,5 +1169,58 @@ unsafe fn set_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX, dwnewlong:
     }
 }
 
+fn basic_vkcode_to_string(code: u16, modifiers: Modifiers) -> Option<Keystroke> {
+    match code {
+        // VK_0 - VK_9
+        48..=57 => Some(Keystroke {
+            modifiers,
+            key: format!("{}", code - VK_0.0),
+            ime_key: None,
+        }),
+        // VK_A - VK_Z
+        65..=90 => Some(Keystroke {
+            modifiers,
+            key: format!("{}", (b'a' + code as u8 - VK_A.0 as u8) as char),
+            ime_key: None,
+        }),
+        // VK_F1 - VK_F24
+        112..=135 => Some(Keystroke {
+            modifiers,
+            key: format!("f{}", code - VK_F1.0 + 1),
+            ime_key: None,
+        }),
+        // OEM3: `/~, OEM_MINUS: -/_, OEM_PLUS: =/+, ...
+        _ => {
+            if let Some(key) = oemkey_vkcode_to_string(code) {
+                Some(Keystroke {
+                    modifiers,
+                    key,
+                    ime_key: None,
+                })
+            } else {
+                None
+            }
+        }
+    }
+}
+
+fn oemkey_vkcode_to_string(code: u16) -> Option<String> {
+    match code {
+        186 => Some(";".to_string()), // VK_OEM_1
+        187 => Some("=".to_string()), // VK_OEM_PLUS
+        188 => Some(",".to_string()), // VK_OEM_COMMA
+        189 => Some("-".to_string()), // VK_OEM_MINUS
+        190 => Some(".".to_string()), // VK_OEM_PERIOD
+        // https://kbdlayout.info/features/virtualkeys/VK_ABNT_C1
+        191 | 193 => Some("/".to_string()), // VK_OEM_2 VK_ABNT_C1
+        192 => Some("`".to_string()),       // VK_OEM_3
+        219 => Some("[".to_string()),       // VK_OEM_4
+        220 => Some("\\".to_string()),      // VK_OEM_5
+        221 => Some("]".to_string()),       // VK_OEM_6
+        222 => Some("'".to_string()),       // VK_OEM_7
+        _ => None,
+    }
+}
+
 // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragqueryfilew
 const DRAGDROP_GET_FILES_COUNT: u32 = 0xFFFFFFFF;