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