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