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