util.rs

  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}