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) -> 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/// This function is used to configure the dark mode for the window built-in title bar.
143pub(crate) fn configure_dwm_dark_mode(hwnd: HWND) {
144    let dark_mode_enabled: BOOL = match system_appearance().log_err().unwrap_or_default() {
145        WindowAppearance::Dark | WindowAppearance::VibrantDark => true.into(),
146        WindowAppearance::Light | WindowAppearance::VibrantLight => false.into(),
147    };
148    unsafe {
149        DwmSetWindowAttribute(
150            hwnd,
151            DWMWA_USE_IMMERSIVE_DARK_MODE,
152            &dark_mode_enabled as *const _ as _,
153            std::mem::size_of::<BOOL>() as u32,
154        )
155        .log_err();
156    }
157}
158
159#[inline]
160pub(crate) fn logical_point(x: f32, y: f32, scale_factor: f32) -> Point<Pixels> {
161    Point {
162        x: px(x / scale_factor),
163        y: px(y / scale_factor),
164    }
165}
166
167// https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes
168#[inline]
169pub(crate) fn system_appearance() -> Result<WindowAppearance> {
170    let ui_settings = UISettings::new()?;
171    let foreground_color = ui_settings.GetColorValue(UIColorType::Foreground)?;
172    // If the foreground is light, then is_color_light will evaluate to true,
173    // meaning Dark mode is enabled.
174    if is_color_light(&foreground_color) {
175        Ok(WindowAppearance::Dark)
176    } else {
177        Ok(WindowAppearance::Light)
178    }
179}
180
181#[inline(always)]
182fn is_color_light(color: &Color) -> bool {
183    ((5 * color.G as u32) + (2 * color.R as u32) + color.B as u32) > (8 * 128)
184}