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