util.rs

  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}