@@ -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();
}
@@ -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;