Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

windows: mouse and keyboard #8791

Merged
merged 13 commits into from
Mar 5, 2024
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,12 @@ version = "0.53.0"
features = [
"Win32_Graphics_Gdi",
"Win32_UI_WindowsAndMessaging",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_System_SystemServices",
"Win32_Security",
"Win32_System_Threading",
"Win32_System_DataExchange",
"Win32_System_Ole",
]


Expand Down
148 changes: 127 additions & 21 deletions crates/gpui/src/platform/windows/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use std::{
cell::RefCell,
collections::HashSet,
ffi::{c_uint, c_void},
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
Expand All @@ -15,27 +16,39 @@ use async_task::Runnable;
use futures::channel::oneshot::Receiver;
use parking_lot::Mutex;
use time::UtcOffset;
use util::SemanticVersion;
use util::{ResultExt, SemanticVersion};
use windows::Win32::{
Foundation::{CloseHandle, HANDLE, HWND},
Foundation::{CloseHandle, GetLastError, HANDLE, HWND, WAIT_EVENT},
System::Threading::{CreateEventW, INFINITE},
UI::WindowsAndMessaging::{
DispatchMessageW, MsgWaitForMultipleObjects, PeekMessageW, PostQuitMessage,
TranslateMessage, MSG, PM_REMOVE, QS_ALLINPUT, WM_QUIT,
DispatchMessageW, GetMessageW, MsgWaitForMultipleObjects, PostQuitMessage,
SystemParametersInfoW, TranslateMessage, MSG, QS_ALLINPUT, SPI_GETWHEELSCROLLCHARS,
SPI_GETWHEELSCROLLLINES, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE,
},
};

use crate::{
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor,
Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem,
PlatformWindow, Task, WindowAppearance, WindowOptions, WindowsDispatcher, WindowsDisplay,
WindowsTextSystem, WindowsWindow,
try_get_window_inner, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle,
ForegroundExecutor, Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput,
PlatformTextSystem, PlatformWindow, Task, WindowAppearance, WindowOptions, WindowsDispatcher,
WindowsDisplay, WindowsTextSystem, WindowsWindow,
};

pub(crate) struct WindowsPlatform {
inner: Rc<WindowsPlatformInner>,
}

/// Windows settings pulled from SystemParametersInfo
/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
#[derive(Default, Debug)]
pub(crate) struct WindowsPlatformSystemSettings {
/// SEE: SPI_GETWHEELSCROLLCHARS
pub(crate) wheel_scroll_chars: u32,

/// SEE: SPI_GETWHEELSCROLLLINES
pub(crate) wheel_scroll_lines: u32,
}

pub(crate) struct WindowsPlatformInner {
background_executor: BackgroundExecutor,
pub(crate) foreground_executor: ForegroundExecutor,
Expand All @@ -44,6 +57,7 @@ pub(crate) struct WindowsPlatformInner {
callbacks: Mutex<Callbacks>,
pub(crate) window_handles: RefCell<HashSet<AnyWindowHandle>>,
pub(crate) event: HANDLE,
pub(crate) settings: RefCell<WindowsPlatformSystemSettings>,
}

impl Drop for WindowsPlatformInner {
Expand All @@ -65,6 +79,57 @@ struct Callbacks {
validate_app_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
}

enum WindowsMessageWaitResult {
ForegroundExecution,
WindowsMessage(MSG),
Error,
}

impl WindowsPlatformSystemSettings {
fn new() -> Self {
let mut settings = Self::default();
settings.update_all();
settings
}

pub(crate) fn update_all(&mut self) {
self.update_wheel_scroll_lines();
self.update_wheel_scroll_chars();
}

pub(crate) fn update_wheel_scroll_lines(&mut self) {
let mut value = c_uint::default();
let result = unsafe {
SystemParametersInfoW(
SPI_GETWHEELSCROLLLINES,
0,
Some((&mut value) as *mut c_uint as *mut c_void),
SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(),
)
};

if result.log_err() != None {
self.wheel_scroll_lines = value;
}
}

pub(crate) fn update_wheel_scroll_chars(&mut self) {
let mut value = c_uint::default();
let result = unsafe {
SystemParametersInfoW(
SPI_GETWHEELSCROLLCHARS,
0,
Some((&mut value) as *mut c_uint as *mut c_void),
SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(),
)
};

if result.log_err() != None {
self.wheel_scroll_chars = value;
}
}
}

impl WindowsPlatform {
pub(crate) fn new() -> Self {
let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
Expand All @@ -75,6 +140,7 @@ impl WindowsPlatform {
let text_system = Arc::new(WindowsTextSystem::new());
let callbacks = Mutex::new(Callbacks::default());
let window_handles = RefCell::new(HashSet::new());
let settings = RefCell::new(WindowsPlatformSystemSettings::new());
let inner = Rc::new(WindowsPlatformInner {
background_executor,
foreground_executor,
Expand All @@ -83,9 +149,44 @@ impl WindowsPlatform {
callbacks,
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 let Some(inner) = try_get_window_inner(msg.hwnd) {
inner.handle_immediate_msg(msg.message, msg.wParam, msg.lParam)
} else {
false
}
}

fn wait_message(&self) -> WindowsMessageWaitResult {
let wait_result = unsafe {
MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT)
};

match wait_result {
WAIT_EVENT(0) => WindowsMessageWaitResult::ForegroundExecution,
WAIT_EVENT(1) => {
kvark marked this conversation as resolved.
Show resolved Hide resolved
let mut msg = MSG::default();
unsafe { GetMessageW(&mut msg, HWND::default(), 0, 0) };
WindowsMessageWaitResult::WindowsMessage(msg)
}
_ => {
log::error!("unhandled windows wait message: {}", wait_result.0);
WindowsMessageWaitResult::Error
}
}
}
}

impl Platform for WindowsPlatform {
Expand All @@ -103,22 +204,27 @@ impl Platform for WindowsPlatform {

fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
on_finish_launching();
'a: loop {
unsafe {
MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT)
};
let mut msg = MSG::default();
while unsafe { PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE) }.as_bool() {
if msg.message == WM_QUIT {
break 'a;
loop {
match self.wait_message() {
WindowsMessageWaitResult::ForegroundExecution => {
for runnable in self.inner.main_receiver.drain() {
runnable.run();
}
}
unsafe { TranslateMessage(&msg) };
unsafe { DispatchMessageW(&msg) };
}
while let Ok(runnable) = self.inner.main_receiver.try_recv() {
runnable.run();
WindowsMessageWaitResult::WindowsMessage(msg) => {
if msg.message == WM_QUIT {
break;
}

if !self.run_immediate_msg_handlers(&msg) {
unsafe { TranslateMessage(&msg) };
unsafe { DispatchMessageW(&msg) };
}
}
WindowsMessageWaitResult::Error => {}
}
}

let mut callbacks = self.inner.callbacks.lock();
if let Some(callback) = callbacks.quit.as_mut() {
callback()
Expand Down
18 changes: 18 additions & 0 deletions crates/gpui/src/platform/windows/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use windows::Win32::Foundation::{LPARAM, WPARAM};
pub(crate) trait HiLoWord {
fn hiword(&self) -> u16;
fn loword(&self) -> u16;
fn signed_hiword(&self) -> i16;
fn signed_loword(&self) -> i16;
}

impl HiLoWord for WPARAM {
Expand All @@ -13,6 +15,14 @@ impl HiLoWord for WPARAM {
fn loword(&self) -> u16 {
(self.0 & 0xFFFF) as u16
}

fn signed_hiword(&self) -> i16 {
((self.0 >> 16) & 0xFFFF) as i16
}

fn signed_loword(&self) -> i16 {
(self.0 & 0xFFFF) as i16
}
}

impl HiLoWord for LPARAM {
Expand All @@ -23,4 +33,12 @@ impl HiLoWord for LPARAM {
fn loword(&self) -> u16 {
(self.0 & 0xFFFF) as u16
}

fn signed_hiword(&self) -> i16 {
((self.0 >> 16) & 0xFFFF) as i16
}

fn signed_loword(&self) -> i16 {
(self.0 & 0xFFFF) as i16
}
}
Loading
Loading