util.rs

  1use std::sync::OnceLock;
  2
  3use ::util::ResultExt;
  4use anyhow::Context;
  5use windows::{
  6    UI::{
  7        Color,
  8        ViewManagement::{UIColorType, UISettings},
  9    },
 10    Win32::{
 11        Foundation::*, Graphics::Dwm::*, System::LibraryLoader::LoadLibraryA,
 12        UI::WindowsAndMessaging::*,
 13    },
 14    core::{BOOL, PCSTR},
 15};
 16
 17use crate::*;
 18use gpui::*;
 19
 20pub(crate) trait HiLoWord {
 21    fn hiword(&self) -> u16;
 22    fn loword(&self) -> u16;
 23    fn signed_hiword(&self) -> i16;
 24    fn signed_loword(&self) -> i16;
 25}
 26
 27impl HiLoWord for WPARAM {
 28    fn hiword(&self) -> u16 {
 29        ((self.0 >> 16) & 0xFFFF) as u16
 30    }
 31
 32    fn loword(&self) -> u16 {
 33        (self.0 & 0xFFFF) as u16
 34    }
 35
 36    fn signed_hiword(&self) -> i16 {
 37        ((self.0 >> 16) & 0xFFFF) as i16
 38    }
 39
 40    fn signed_loword(&self) -> i16 {
 41        (self.0 & 0xFFFF) as i16
 42    }
 43}
 44
 45impl HiLoWord for LPARAM {
 46    fn hiword(&self) -> u16 {
 47        ((self.0 >> 16) & 0xFFFF) as u16
 48    }
 49
 50    fn loword(&self) -> u16 {
 51        (self.0 & 0xFFFF) as u16
 52    }
 53
 54    fn signed_hiword(&self) -> i16 {
 55        ((self.0 >> 16) & 0xFFFF) as i16
 56    }
 57
 58    fn signed_loword(&self) -> i16 {
 59        (self.0 & 0xFFFF) as i16
 60    }
 61}
 62
 63pub(crate) unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize {
 64    #[cfg(target_pointer_width = "64")]
 65    unsafe {
 66        GetWindowLongPtrW(hwnd, nindex)
 67    }
 68    #[cfg(target_pointer_width = "32")]
 69    unsafe {
 70        GetWindowLongW(hwnd, nindex) as isize
 71    }
 72}
 73
 74pub(crate) unsafe fn set_window_long(
 75    hwnd: HWND,
 76    nindex: WINDOW_LONG_PTR_INDEX,
 77    dwnewlong: isize,
 78) -> isize {
 79    #[cfg(target_pointer_width = "64")]
 80    unsafe {
 81        SetWindowLongPtrW(hwnd, nindex, dwnewlong)
 82    }
 83    #[cfg(target_pointer_width = "32")]
 84    unsafe {
 85        SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
 86    }
 87}
 88
 89pub(crate) fn windows_credentials_target_name(url: &str) -> String {
 90    format!("zed:url={}", url)
 91}
 92
 93pub(crate) fn load_cursor(style: CursorStyle) -> Option<HCURSOR> {
 94    static ARROW: OnceLock<SafeCursor> = OnceLock::new();
 95    static IBEAM: OnceLock<SafeCursor> = OnceLock::new();
 96    static CROSS: OnceLock<SafeCursor> = OnceLock::new();
 97    static HAND: OnceLock<SafeCursor> = OnceLock::new();
 98    static SIZEWE: OnceLock<SafeCursor> = OnceLock::new();
 99    static SIZENS: OnceLock<SafeCursor> = OnceLock::new();
100    static SIZENWSE: OnceLock<SafeCursor> = OnceLock::new();
101    static SIZENESW: OnceLock<SafeCursor> = OnceLock::new();
102    static NO: OnceLock<SafeCursor> = OnceLock::new();
103    let (lock, name) = match style {
104        CursorStyle::IBeam | CursorStyle::IBeamCursorForVerticalLayout => (&IBEAM, IDC_IBEAM),
105        CursorStyle::Crosshair => (&CROSS, IDC_CROSS),
106        CursorStyle::PointingHand | CursorStyle::DragLink => (&HAND, IDC_HAND),
107        CursorStyle::ResizeLeft
108        | CursorStyle::ResizeRight
109        | CursorStyle::ResizeLeftRight
110        | CursorStyle::ResizeColumn => (&SIZEWE, IDC_SIZEWE),
111        CursorStyle::ResizeUp
112        | CursorStyle::ResizeDown
113        | CursorStyle::ResizeUpDown
114        | CursorStyle::ResizeRow => (&SIZENS, IDC_SIZENS),
115        CursorStyle::ResizeUpLeftDownRight => (&SIZENWSE, IDC_SIZENWSE),
116        CursorStyle::ResizeUpRightDownLeft => (&SIZENESW, IDC_SIZENESW),
117        CursorStyle::OperationNotAllowed => (&NO, IDC_NO),
118        CursorStyle::None => return None,
119        _ => (&ARROW, IDC_ARROW),
120    };
121    Some(
122        *(*lock.get_or_init(|| {
123            HCURSOR(
124                unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) }
125                    .log_err()
126                    .unwrap_or_default()
127                    .0,
128            )
129            .into()
130        })),
131    )
132}
133
134/// This function is used to configure the dark mode for the window built-in title bar.
135pub(crate) fn configure_dwm_dark_mode(hwnd: HWND, appearance: WindowAppearance) {
136    let dark_mode_enabled: BOOL = match appearance {
137        WindowAppearance::Dark | WindowAppearance::VibrantDark => true.into(),
138        WindowAppearance::Light | WindowAppearance::VibrantLight => false.into(),
139    };
140    unsafe {
141        DwmSetWindowAttribute(
142            hwnd,
143            DWMWA_USE_IMMERSIVE_DARK_MODE,
144            &dark_mode_enabled as *const _ as _,
145            std::mem::size_of::<BOOL>() as u32,
146        )
147        .log_err();
148    }
149}
150
151#[inline]
152pub(crate) fn logical_point(x: f32, y: f32, scale_factor: f32) -> Point<Pixels> {
153    Point {
154        x: px(x / scale_factor),
155        y: px(y / scale_factor),
156    }
157}
158
159// https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes
160#[inline]
161pub(crate) fn system_appearance() -> Result<WindowAppearance> {
162    let ui_settings = UISettings::new()?;
163    let foreground_color = ui_settings.GetColorValue(UIColorType::Foreground)?;
164    // If the foreground is light, then is_color_light will evaluate to true,
165    // meaning Dark mode is enabled.
166    if is_color_light(&foreground_color) {
167        Ok(WindowAppearance::Dark)
168    } else {
169        Ok(WindowAppearance::Light)
170    }
171}
172
173#[inline(always)]
174fn is_color_light(color: &Color) -> bool {
175    ((5 * color.G as u32) + (2 * color.R as u32) + color.B as u32) > (8 * 128)
176}
177
178pub(crate) fn with_dll_library<R, F>(dll_name: PCSTR, f: F) -> Result<R>
179where
180    F: FnOnce(HMODULE) -> Result<R>,
181{
182    let library = unsafe {
183        LoadLibraryA(dll_name).with_context(|| format!("Loading dll: {}", dll_name.display()))?
184    };
185    let result = f(library);
186    unsafe {
187        FreeLibrary(library)
188            .with_context(|| format!("Freeing dll: {}", dll_name.display()))
189            .log_err();
190    }
191    result
192}