I recently noticed that on my Windows 11 machine, Zed no longer receive the `WM_DWMCOLORIZATIONCOLORCHANGED` message when the system theme changes. This functionality was present in the past. While this change might be unexpected, it's understandable given Microsoft's history of system updates. This pull request proposes an alternative approach using the `WM_SETTINGCHANGE` message to handle theme changes. Release Notes: - N/A
185 lines
5.1 KiB
Rust
185 lines
5.1 KiB
Rust
use std::sync::OnceLock;
|
|
|
|
use ::util::ResultExt;
|
|
use windows::{
|
|
Wdk::System::SystemServices::RtlGetVersion,
|
|
Win32::{Foundation::*, Graphics::Dwm::*, UI::WindowsAndMessaging::*},
|
|
UI::{
|
|
Color,
|
|
ViewManagement::{UIColorType, UISettings},
|
|
},
|
|
};
|
|
|
|
use crate::*;
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub(crate) enum WindowsVersion {
|
|
Win10,
|
|
Win11,
|
|
}
|
|
|
|
impl WindowsVersion {
|
|
pub(crate) fn new() -> anyhow::Result<Self> {
|
|
let mut version = unsafe { std::mem::zeroed() };
|
|
let status = unsafe { RtlGetVersion(&mut version) };
|
|
|
|
status.ok()?;
|
|
if version.dwBuildNumber >= 22000 {
|
|
Ok(WindowsVersion::Win11)
|
|
} else {
|
|
Ok(WindowsVersion::Win10)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) trait HiLoWord {
|
|
fn hiword(&self) -> u16;
|
|
fn loword(&self) -> u16;
|
|
fn signed_hiword(&self) -> i16;
|
|
fn signed_loword(&self) -> i16;
|
|
}
|
|
|
|
impl HiLoWord for WPARAM {
|
|
fn hiword(&self) -> u16 {
|
|
((self.0 >> 16) & 0xFFFF) as u16
|
|
}
|
|
|
|
fn loword(&self) -> u16 {
|
|
(self.0 & 0xFFFF) as u16
|
|
}
|
|
|
|
fn signed_hiword(&self) -> i16 {
|
|
((self.0 >> 16) & 0xFFFF) as i16
|
|
}
|
|
|
|
fn signed_loword(&self) -> i16 {
|
|
(self.0 & 0xFFFF) as i16
|
|
}
|
|
}
|
|
|
|
impl HiLoWord for LPARAM {
|
|
fn hiword(&self) -> u16 {
|
|
((self.0 >> 16) & 0xFFFF) as u16
|
|
}
|
|
|
|
fn loword(&self) -> u16 {
|
|
(self.0 & 0xFFFF) as u16
|
|
}
|
|
|
|
fn signed_hiword(&self) -> i16 {
|
|
((self.0 >> 16) & 0xFFFF) as i16
|
|
}
|
|
|
|
fn signed_loword(&self) -> i16 {
|
|
(self.0 & 0xFFFF) as i16
|
|
}
|
|
}
|
|
|
|
pub(crate) unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize {
|
|
#[cfg(target_pointer_width = "64")]
|
|
unsafe {
|
|
GetWindowLongPtrW(hwnd, nindex)
|
|
}
|
|
#[cfg(target_pointer_width = "32")]
|
|
unsafe {
|
|
GetWindowLongW(hwnd, nindex) as isize
|
|
}
|
|
}
|
|
|
|
pub(crate) unsafe fn set_window_long(
|
|
hwnd: HWND,
|
|
nindex: WINDOW_LONG_PTR_INDEX,
|
|
dwnewlong: isize,
|
|
) -> isize {
|
|
#[cfg(target_pointer_width = "64")]
|
|
unsafe {
|
|
SetWindowLongPtrW(hwnd, nindex, dwnewlong)
|
|
}
|
|
#[cfg(target_pointer_width = "32")]
|
|
unsafe {
|
|
SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
|
|
}
|
|
}
|
|
|
|
pub(crate) fn windows_credentials_target_name(url: &str) -> String {
|
|
format!("zed:url={}", url)
|
|
}
|
|
|
|
pub(crate) fn load_cursor(style: CursorStyle) -> HCURSOR {
|
|
static ARROW: OnceLock<SafeCursor> = OnceLock::new();
|
|
static IBEAM: OnceLock<SafeCursor> = OnceLock::new();
|
|
static CROSS: OnceLock<SafeCursor> = OnceLock::new();
|
|
static HAND: OnceLock<SafeCursor> = OnceLock::new();
|
|
static SIZEWE: OnceLock<SafeCursor> = OnceLock::new();
|
|
static SIZENS: OnceLock<SafeCursor> = OnceLock::new();
|
|
static NO: OnceLock<SafeCursor> = OnceLock::new();
|
|
let (lock, name) = match style {
|
|
CursorStyle::IBeam | CursorStyle::IBeamCursorForVerticalLayout => (&IBEAM, IDC_IBEAM),
|
|
CursorStyle::Crosshair => (&CROSS, IDC_CROSS),
|
|
CursorStyle::PointingHand | CursorStyle::DragLink => (&HAND, IDC_HAND),
|
|
CursorStyle::ResizeLeft
|
|
| CursorStyle::ResizeRight
|
|
| CursorStyle::ResizeLeftRight
|
|
| CursorStyle::ResizeColumn => (&SIZEWE, IDC_SIZEWE),
|
|
CursorStyle::ResizeUp
|
|
| CursorStyle::ResizeDown
|
|
| CursorStyle::ResizeUpDown
|
|
| CursorStyle::ResizeRow => (&SIZENS, IDC_SIZENS),
|
|
CursorStyle::OperationNotAllowed => (&NO, IDC_NO),
|
|
_ => (&ARROW, IDC_ARROW),
|
|
};
|
|
*(*lock.get_or_init(|| {
|
|
HCURSOR(
|
|
unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) }
|
|
.log_err()
|
|
.unwrap_or_default()
|
|
.0,
|
|
)
|
|
.into()
|
|
}))
|
|
}
|
|
|
|
/// This function is used to configure the dark mode for the window built-in title bar.
|
|
pub(crate) fn configure_dwm_dark_mode(hwnd: HWND) {
|
|
let dark_mode_enabled: BOOL = match system_appearance().log_err().unwrap_or_default() {
|
|
WindowAppearance::Dark | WindowAppearance::VibrantDark => true.into(),
|
|
WindowAppearance::Light | WindowAppearance::VibrantLight => false.into(),
|
|
};
|
|
unsafe {
|
|
DwmSetWindowAttribute(
|
|
hwnd,
|
|
DWMWA_USE_IMMERSIVE_DARK_MODE,
|
|
&dark_mode_enabled as *const _ as _,
|
|
std::mem::size_of::<BOOL>() as u32,
|
|
)
|
|
.log_err();
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn logical_point(x: f32, y: f32, scale_factor: f32) -> Point<Pixels> {
|
|
Point {
|
|
x: px(x / scale_factor),
|
|
y: px(y / scale_factor),
|
|
}
|
|
}
|
|
|
|
// https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes
|
|
#[inline]
|
|
pub(crate) fn system_appearance() -> Result<WindowAppearance> {
|
|
let ui_settings = UISettings::new()?;
|
|
let foreground_color = ui_settings.GetColorValue(UIColorType::Foreground)?;
|
|
// If the foreground is light, then is_color_light will evaluate to true,
|
|
// meaning Dark mode is enabled.
|
|
if is_color_light(&foreground_color) {
|
|
Ok(WindowAppearance::Dark)
|
|
} else {
|
|
Ok(WindowAppearance::Light)
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn is_color_light(color: &Color) -> bool {
|
|
((5 * color.G as u32) + (2 * color.R as u32) + color.B as u32) > (8 * 128)
|
|
}
|