From af87fb98d0744880b515ac83d608e37c1455637d Mon Sep 17 00:00:00 2001 From: Small White <364772080@qq.com> Date: Thu, 7 Mar 2024 04:48:43 +0800 Subject: [PATCH] Implement more GPUI services on windows. (#8940) ### Description This is a part of #8809 , impl the following functions: - `os_version` - `local_timezone` - `double_click_interval` - `set_cursor_style` - `open_url` - `reveal_path` Release Notes: - N/A --------- Co-authored-by: Mikayla Maki --- Cargo.toml | 20 ++- crates/gpui/src/platform/windows/platform.rs | 146 ++++++++++++++++--- 2 files changed, 140 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 049f093c74cc6..846411eb5fdbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -215,7 +215,10 @@ hex = "0.4.3" ignore = "0.4.22" indoc = "1" # We explicitly disable http2 support in isahc. -isahc = { version = "1.7.2", default-features = false, features = ["static-curl", "text-decoding"] } +isahc = { version = "1.7.2", default-features = false, features = [ + "static-curl", + "text-decoding", +] } itertools = "0.11.0" lazy_static = "1.4.0" linkify = "0.10.0" @@ -238,7 +241,10 @@ semver = "1.0" serde = { version = "1.0", features = ["derive", "rc"] } serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] } -serde_json_lenient = { version = "0.1", features = ["preserve_order", "raw_value"] } +serde_json_lenient = { version = "0.1", features = [ + "preserve_order", + "raw_value", +] } serde_repr = "0.1" sha2 = "0.10" shellexpand = "2.1.0" @@ -249,7 +255,11 @@ sysinfo = "0.29.10" tempfile = "3.9.0" thiserror = "1.0.29" tiktoken-rs = "0.5.7" -time = { version = "0.3", features = ["serde", "serde-well-known", "formatting"] } +time = { version = "0.3", features = [ + "serde", + "serde-well-known", + "formatting", +] } toml = "0.8" tower-http = "0.4.4" tree-sitter = { version = "0.20", features = ["wasm"] } @@ -312,11 +322,15 @@ sys-locale = "0.3.1" [workspace.dependencies.windows] version = "0.53.0" features = [ + "Wdk_System_SystemServices", "Win32_Graphics_Gdi", "Win32_Graphics_DirectComposition", "Win32_UI_WindowsAndMessaging", "Win32_UI_Input_KeyboardAndMouse", + "Win32_UI_Shell", + "Win32_System_SystemInformation", "Win32_System_SystemServices", + "Win32_System_Time", "Win32_Security", "Win32_System_Threading", "Win32_System_DataExchange", diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 92f065ec67234..53e16ab595967 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -17,14 +17,28 @@ use futures::channel::oneshot::Receiver; use parking_lot::Mutex; use time::UtcOffset; use util::{ResultExt, SemanticVersion}; -use windows::Win32::{ - Foundation::{CloseHandle, BOOL, HANDLE, HWND, LPARAM, TRUE}, - Graphics::DirectComposition::DCompositionWaitForCompositorClock, - System::Threading::{CreateEventW, GetCurrentThreadId, INFINITE}, - UI::WindowsAndMessaging::{ - DispatchMessageW, EnumThreadWindows, PeekMessageW, PostQuitMessage, SystemParametersInfoW, - TranslateMessage, MSG, PM_REMOVE, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, - SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE, +use windows::{ + core::{HSTRING, PCWSTR}, + Wdk::System::SystemServices::RtlGetVersion, + Win32::{ + Foundation::{CloseHandle, BOOL, HANDLE, HWND, LPARAM, TRUE}, + Graphics::DirectComposition::DCompositionWaitForCompositorClock, + System::{ + Threading::{CreateEventW, GetCurrentThreadId, INFINITE}, + Time::{GetTimeZoneInformation, TIME_ZONE_ID_INVALID}, + }, + UI::{ + Input::KeyboardAndMouse::GetDoubleClickTime, + Shell::ShellExecuteW, + WindowsAndMessaging::{ + DispatchMessageW, EnumThreadWindows, LoadImageW, MsgWaitForMultipleObjects, + 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, QS_ALLINPUT, + SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SW_SHOWDEFAULT, + SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE, + }, + }, }, }; @@ -300,9 +314,16 @@ impl Platform for WindowsPlatform { WindowAppearance::Dark } - // todo!("windows") fn open_url(&self, url: &str) { - // todo!("windows") + let url_string = url.to_string(); + self.background_executor() + .spawn(async move { + if url_string.is_empty() { + return; + } + open_target(url_string.as_str()); + }) + .detach(); } // todo!("windows") @@ -320,9 +341,22 @@ impl Platform for WindowsPlatform { unimplemented!() } - // todo!("windows") fn reveal_path(&self, path: &Path) { - unimplemented!() + let Ok(file_full_path) = path.canonicalize() else { + log::error!("unable to parse file path"); + return; + }; + self.background_executor() + .spawn(async move { + let Some(path) = file_full_path.to_str() else { + return; + }; + if path.is_empty() { + return; + } + open_target(path); + }) + .detach(); } fn on_become_active(&self, callback: Box) { @@ -365,11 +399,20 @@ impl Platform for WindowsPlatform { } fn os_version(&self) -> Result { - Ok(SemanticVersion { - major: 1, - minor: 0, - patch: 0, - }) + let mut info = unsafe { std::mem::zeroed() }; + let status = unsafe { RtlGetVersion(&mut info) }; + if status.is_ok() { + Ok(SemanticVersion { + major: info.dwMajorVersion as _, + minor: info.dwMinorVersion as _, + patch: info.dwBuildNumber as _, + }) + } else { + Err(anyhow::anyhow!( + "unable to get Windows version: {}", + std::io::Error::last_os_error() + )) + } } fn app_version(&self) -> Result { @@ -385,14 +428,28 @@ impl Platform for WindowsPlatform { Err(anyhow!("not yet implemented")) } - // todo!("windows") fn local_timezone(&self) -> UtcOffset { - UtcOffset::from_hms(9, 0, 0).unwrap() + let mut info = unsafe { std::mem::zeroed() }; + let ret = unsafe { GetTimeZoneInformation(&mut info) }; + if ret == TIME_ZONE_ID_INVALID { + log::error!( + "Unable to get local timezone: {}", + std::io::Error::last_os_error() + ); + return UtcOffset::UTC; + } + // Windows treat offset as: + // UTC = localtime + offset + // so we add a minus here + let hours = -info.Bias / 60; + let minutes = -info.Bias % 60; + + UtcOffset::from_hms(hours as _, minutes as _, 0).unwrap() } - // todo!("windows") fn double_click_interval(&self) -> Duration { - Duration::from_millis(100) + let millis = unsafe { GetDoubleClickTime() }; + Duration::from_millis(millis as _) } // todo!("windows") @@ -400,8 +457,31 @@ impl Platform for WindowsPlatform { Err(anyhow!("not yet implemented")) } - // todo!("windows") - fn set_cursor_style(&self, style: CursorStyle) {} + fn set_cursor_style(&self, style: CursorStyle) { + let handle = match style { + CursorStyle::IBeam | CursorStyle::IBeamCursorForVerticalLayout => unsafe { + load_cursor(IDC_IBEAM) + }, + CursorStyle::Crosshair => unsafe { load_cursor(IDC_CROSS) }, + CursorStyle::PointingHand | CursorStyle::DragLink => unsafe { load_cursor(IDC_HAND) }, + CursorStyle::ResizeLeft | CursorStyle::ResizeRight | CursorStyle::ResizeLeftRight => unsafe { + load_cursor(IDC_SIZEWE) + }, + CursorStyle::ResizeUp | CursorStyle::ResizeDown | CursorStyle::ResizeUpDown => unsafe { + load_cursor(IDC_SIZENS) + }, + CursorStyle::OperationNotAllowed => unsafe { load_cursor(IDC_NO) }, + _ => unsafe { load_cursor(IDC_ARROW) }, + }; + if handle.is_err() { + log::error!( + "Error loading cursor image: {}", + std::io::Error::last_os_error() + ); + return; + } + let _ = unsafe { SetCursor(HCURSOR(handle.unwrap().0)) }; + } // todo!("windows") fn should_auto_hide_scrollbars(&self) -> bool { @@ -437,3 +517,23 @@ impl Platform for WindowsPlatform { Task::ready(Err(anyhow!("register_url_scheme unimplemented"))) } } + +unsafe fn load_cursor(name: PCWSTR) -> Result { + LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED).map_err(|e| anyhow!(e)) +} + +fn open_target(target: &str) { + unsafe { + let ret = ShellExecuteW( + None, + windows::core::w!("open"), + &HSTRING::from(target), + None, + None, + SW_SHOWDEFAULT, + ); + if ret.0 <= 32 { + log::error!("Unable to open target: {}", std::io::Error::last_os_error()); + } + } +}