1use std::sync::OnceLock;
2
3use ::util::ResultExt;
4use anyhow::Context;
5use windows::{
6 UI::{
7 Color,
8 ViewManagement::{UIColorType, UISettings},
9 },
10 Win32::{
11 Foundation::*, Graphics::Dwm::*, System::LibraryLoader::LoadLibraryA,
12 UI::WindowsAndMessaging::*,
13 },
14 core::{BOOL, PCSTR},
15};
16
17use crate::*;
18use gpui::*;
19
20pub(crate) trait HiLoWord {
21 fn hiword(&self) -> u16;
22 fn loword(&self) -> u16;
23 fn signed_hiword(&self) -> i16;
24 fn signed_loword(&self) -> i16;
25}
26
27impl HiLoWord for WPARAM {
28 fn hiword(&self) -> u16 {
29 ((self.0 >> 16) & 0xFFFF) as u16
30 }
31
32 fn loword(&self) -> u16 {
33 (self.0 & 0xFFFF) as u16
34 }
35
36 fn signed_hiword(&self) -> i16 {
37 ((self.0 >> 16) & 0xFFFF) as i16
38 }
39
40 fn signed_loword(&self) -> i16 {
41 (self.0 & 0xFFFF) as i16
42 }
43}
44
45impl HiLoWord for LPARAM {
46 fn hiword(&self) -> u16 {
47 ((self.0 >> 16) & 0xFFFF) as u16
48 }
49
50 fn loword(&self) -> u16 {
51 (self.0 & 0xFFFF) as u16
52 }
53
54 fn signed_hiword(&self) -> i16 {
55 ((self.0 >> 16) & 0xFFFF) as i16
56 }
57
58 fn signed_loword(&self) -> i16 {
59 (self.0 & 0xFFFF) as i16
60 }
61}
62
63pub(crate) unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize {
64 #[cfg(target_pointer_width = "64")]
65 unsafe {
66 GetWindowLongPtrW(hwnd, nindex)
67 }
68 #[cfg(target_pointer_width = "32")]
69 unsafe {
70 GetWindowLongW(hwnd, nindex) as isize
71 }
72}
73
74pub(crate) unsafe fn set_window_long(
75 hwnd: HWND,
76 nindex: WINDOW_LONG_PTR_INDEX,
77 dwnewlong: isize,
78) -> isize {
79 #[cfg(target_pointer_width = "64")]
80 unsafe {
81 SetWindowLongPtrW(hwnd, nindex, dwnewlong)
82 }
83 #[cfg(target_pointer_width = "32")]
84 unsafe {
85 SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
86 }
87}
88
89pub(crate) fn windows_credentials_target_name(url: &str) -> String {
90 format!("zed:url={}", url)
91}
92
93pub(crate) fn load_cursor(style: CursorStyle) -> Option<HCURSOR> {
94 static ARROW: OnceLock<SafeCursor> = OnceLock::new();
95 static IBEAM: OnceLock<SafeCursor> = OnceLock::new();
96 static CROSS: OnceLock<SafeCursor> = OnceLock::new();
97 static HAND: OnceLock<SafeCursor> = OnceLock::new();
98 static SIZEWE: OnceLock<SafeCursor> = OnceLock::new();
99 static SIZENS: OnceLock<SafeCursor> = OnceLock::new();
100 static SIZENWSE: OnceLock<SafeCursor> = OnceLock::new();
101 static SIZENESW: OnceLock<SafeCursor> = OnceLock::new();
102 static NO: OnceLock<SafeCursor> = OnceLock::new();
103 let (lock, name) = match style {
104 CursorStyle::IBeam | CursorStyle::IBeamCursorForVerticalLayout => (&IBEAM, IDC_IBEAM),
105 CursorStyle::Crosshair => (&CROSS, IDC_CROSS),
106 CursorStyle::PointingHand | CursorStyle::DragLink => (&HAND, IDC_HAND),
107 CursorStyle::ResizeLeft
108 | CursorStyle::ResizeRight
109 | CursorStyle::ResizeLeftRight
110 | CursorStyle::ResizeColumn => (&SIZEWE, IDC_SIZEWE),
111 CursorStyle::ResizeUp
112 | CursorStyle::ResizeDown
113 | CursorStyle::ResizeUpDown
114 | CursorStyle::ResizeRow => (&SIZENS, IDC_SIZENS),
115 CursorStyle::ResizeUpLeftDownRight => (&SIZENWSE, IDC_SIZENWSE),
116 CursorStyle::ResizeUpRightDownLeft => (&SIZENESW, IDC_SIZENESW),
117 CursorStyle::OperationNotAllowed => (&NO, IDC_NO),
118 CursorStyle::None => return None,
119 _ => (&ARROW, IDC_ARROW),
120 };
121 Some(
122 *(*lock.get_or_init(|| {
123 HCURSOR(
124 unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) }
125 .log_err()
126 .unwrap_or_default()
127 .0,
128 )
129 .into()
130 })),
131 )
132}
133
134/// This function is used to configure the dark mode for the window built-in title bar.
135pub(crate) fn configure_dwm_dark_mode(hwnd: HWND, appearance: WindowAppearance) {
136 let dark_mode_enabled: BOOL = match appearance {
137 WindowAppearance::Dark | WindowAppearance::VibrantDark => true.into(),
138 WindowAppearance::Light | WindowAppearance::VibrantLight => false.into(),
139 };
140 unsafe {
141 DwmSetWindowAttribute(
142 hwnd,
143 DWMWA_USE_IMMERSIVE_DARK_MODE,
144 &dark_mode_enabled as *const _ as _,
145 std::mem::size_of::<BOOL>() as u32,
146 )
147 .log_err();
148 }
149}
150
151#[inline]
152pub(crate) fn logical_point(x: f32, y: f32, scale_factor: f32) -> Point<Pixels> {
153 Point {
154 x: px(x / scale_factor),
155 y: px(y / scale_factor),
156 }
157}
158
159// https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes
160#[inline]
161pub(crate) fn system_appearance() -> Result<WindowAppearance> {
162 let ui_settings = UISettings::new()?;
163 let foreground_color = ui_settings.GetColorValue(UIColorType::Foreground)?;
164 // If the foreground is light, then is_color_light will evaluate to true,
165 // meaning Dark mode is enabled.
166 if is_color_light(&foreground_color) {
167 Ok(WindowAppearance::Dark)
168 } else {
169 Ok(WindowAppearance::Light)
170 }
171}
172
173#[inline(always)]
174fn is_color_light(color: &Color) -> bool {
175 ((5 * color.G as u32) + (2 * color.R as u32) + color.B as u32) > (8 * 128)
176}
177
178pub(crate) fn with_dll_library<R, F>(dll_name: PCSTR, f: F) -> Result<R>
179where
180 F: FnOnce(HMODULE) -> Result<R>,
181{
182 let library = unsafe {
183 LoadLibraryA(dll_name).with_context(|| format!("Loading dll: {}", dll_name.display()))?
184 };
185 let result = f(library);
186 unsafe {
187 FreeLibrary(library)
188 .with_context(|| format!("Freeing dll: {}", dll_name.display()))
189 .log_err();
190 }
191 result
192}