1use std::sync::OnceLock;
2
3use ::util::ResultExt;
4use windows::{
5 Win32::{Foundation::*, UI::WindowsAndMessaging::*},
6 UI::{
7 Color,
8 ViewManagement::{UIColorType, UISettings},
9 },
10};
11
12use crate::*;
13
14pub(crate) trait HiLoWord {
15 fn hiword(&self) -> u16;
16 fn loword(&self) -> u16;
17 fn signed_hiword(&self) -> i16;
18 fn signed_loword(&self) -> i16;
19}
20
21impl HiLoWord for WPARAM {
22 fn hiword(&self) -> u16 {
23 ((self.0 >> 16) & 0xFFFF) as u16
24 }
25
26 fn loword(&self) -> u16 {
27 (self.0 & 0xFFFF) as u16
28 }
29
30 fn signed_hiword(&self) -> i16 {
31 ((self.0 >> 16) & 0xFFFF) as i16
32 }
33
34 fn signed_loword(&self) -> i16 {
35 (self.0 & 0xFFFF) as i16
36 }
37}
38
39impl HiLoWord for LPARAM {
40 fn hiword(&self) -> u16 {
41 ((self.0 >> 16) & 0xFFFF) as u16
42 }
43
44 fn loword(&self) -> u16 {
45 (self.0 & 0xFFFF) as u16
46 }
47
48 fn signed_hiword(&self) -> i16 {
49 ((self.0 >> 16) & 0xFFFF) as i16
50 }
51
52 fn signed_loword(&self) -> i16 {
53 (self.0 & 0xFFFF) as i16
54 }
55}
56
57pub(crate) unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize {
58 #[cfg(target_pointer_width = "64")]
59 unsafe {
60 GetWindowLongPtrW(hwnd, nindex)
61 }
62 #[cfg(target_pointer_width = "32")]
63 unsafe {
64 GetWindowLongW(hwnd, nindex) as isize
65 }
66}
67
68pub(crate) unsafe fn set_window_long(
69 hwnd: HWND,
70 nindex: WINDOW_LONG_PTR_INDEX,
71 dwnewlong: isize,
72) -> isize {
73 #[cfg(target_pointer_width = "64")]
74 unsafe {
75 SetWindowLongPtrW(hwnd, nindex, dwnewlong)
76 }
77 #[cfg(target_pointer_width = "32")]
78 unsafe {
79 SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
80 }
81}
82
83pub(crate) fn windows_credentials_target_name(url: &str) -> String {
84 format!("zed:url={}", url)
85}
86
87pub(crate) fn load_cursor(style: CursorStyle) -> HCURSOR {
88 static ARROW: OnceLock<HCURSOR> = OnceLock::new();
89 static IBEAM: OnceLock<HCURSOR> = OnceLock::new();
90 static CROSS: OnceLock<HCURSOR> = OnceLock::new();
91 static HAND: OnceLock<HCURSOR> = OnceLock::new();
92 static SIZEWE: OnceLock<HCURSOR> = OnceLock::new();
93 static SIZENS: OnceLock<HCURSOR> = OnceLock::new();
94 static NO: OnceLock<HCURSOR> = OnceLock::new();
95 let (lock, name) = match style {
96 CursorStyle::IBeam | CursorStyle::IBeamCursorForVerticalLayout => (&IBEAM, IDC_IBEAM),
97 CursorStyle::Crosshair => (&CROSS, IDC_CROSS),
98 CursorStyle::PointingHand | CursorStyle::DragLink => (&HAND, IDC_HAND),
99 CursorStyle::ResizeLeft
100 | CursorStyle::ResizeRight
101 | CursorStyle::ResizeLeftRight
102 | CursorStyle::ResizeColumn => (&SIZEWE, IDC_SIZEWE),
103 CursorStyle::ResizeUp
104 | CursorStyle::ResizeDown
105 | CursorStyle::ResizeUpDown
106 | CursorStyle::ResizeRow => (&SIZENS, IDC_SIZENS),
107 CursorStyle::OperationNotAllowed => (&NO, IDC_NO),
108 _ => (&ARROW, IDC_ARROW),
109 };
110 *lock.get_or_init(|| {
111 HCURSOR(
112 unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) }
113 .log_err()
114 .unwrap_or_default()
115 .0,
116 )
117 })
118}
119
120#[inline]
121pub(crate) fn logical_point(x: f32, y: f32, scale_factor: f32) -> Point<Pixels> {
122 Point {
123 x: px(x / scale_factor),
124 y: px(y / scale_factor),
125 }
126}
127
128// https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes
129#[inline]
130pub(crate) fn system_appearance() -> Result<WindowAppearance> {
131 let ui_settings = UISettings::new()?;
132 let foreground_color = ui_settings.GetColorValue(UIColorType::Foreground)?;
133 // If the foreground is light, then is_color_light will evaluate to true,
134 // meaning Dark mode is enabled.
135 if is_color_light(&foreground_color) {
136 Ok(WindowAppearance::Dark)
137 } else {
138 Ok(WindowAppearance::Light)
139 }
140}
141
142#[inline(always)]
143fn is_color_light(color: &Color) -> bool {
144 ((5 * color.G as u32) + (2 * color.R as u32) + color.B as u32) > (8 * 128)
145}