util.rs

  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}