util.rs

  1use std::sync::OnceLock;
  2
  3use ::util::ResultExt;
  4use windows::{
  5    Wdk::System::SystemServices::RtlGetVersion,
  6    Win32::{Foundation::*, 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) -> 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        _ => (&ARROW, IDC_ARROW),
130    };
131    *(*lock.get_or_init(|| {
132        HCURSOR(
133            unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) }
134                .log_err()
135                .unwrap_or_default()
136                .0,
137        )
138        .into()
139    }))
140}
141
142#[inline]
143pub(crate) fn logical_point(x: f32, y: f32, scale_factor: f32) -> Point<Pixels> {
144    Point {
145        x: px(x / scale_factor),
146        y: px(y / scale_factor),
147    }
148}
149
150// https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes
151#[inline]
152pub(crate) fn system_appearance() -> Result<WindowAppearance> {
153    let ui_settings = UISettings::new()?;
154    let foreground_color = ui_settings.GetColorValue(UIColorType::Foreground)?;
155    // If the foreground is light, then is_color_light will evaluate to true,
156    // meaning Dark mode is enabled.
157    if is_color_light(&foreground_color) {
158        Ok(WindowAppearance::Dark)
159    } else {
160        Ok(WindowAppearance::Light)
161    }
162}
163
164#[inline(always)]
165fn is_color_light(color: &Color) -> bool {
166    ((5 * color.G as u32) + (2 * color.R as u32) + color.B as u32) > (8 * 128)
167}