1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-22 21:01:36 +03:00

Improve RESIZE window_decoration on Windows

This commit is contained in:
David Rios 2022-03-24 15:20:26 +01:00 committed by Wez Furlong
parent 32bf0d281e
commit b036bb3b05
12 changed files with 487 additions and 88 deletions

1
Cargo.lock generated
View File

@ -5018,6 +5018,7 @@ dependencies = [
"wezterm-font", "wezterm-font",
"wezterm-input-types", "wezterm-input-types",
"winapi 0.3.9", "winapi 0.3.9",
"windows",
"winreg 0.6.2", "winreg 0.6.2",
"x11", "x11",
"xcb 0.9.0", "xcb 0.9.0",

View File

@ -1,9 +1,43 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" <assembly
manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" > manifestVersion="1.0"
xmlns="urn:schemas-microsoft-com:asm.v1"
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"
>
<asmv3:application> <asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAwareness>PerMonitorV2</dpiAwareness> <dpiAwareness>PerMonitorV2</dpiAwareness>
</asmv3:windowsSettings> </asmv3:windowsSettings>
</asmv3:application> </asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<!--
UAC settings:
- app should run at same integrity level as calling process
- app does not need to manipulate windows belonging to
higher-integrity-level processes
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly> </assembly>

View File

@ -103,8 +103,8 @@ fn main() {
#include <winres.h> #include <winres.h>
// This ID is coupled with code in window/src/os/windows/window.rs // This ID is coupled with code in window/src/os/windows/window.rs
#define IDI_ICON 0x101 #define IDI_ICON 0x101
1 RT_MANIFEST "{win}\\manifest.manifest"
IDI_ICON ICON "{win}\\terminal.ico" IDI_ICON ICON "{win}\\terminal.ico"
APP_MANIFEST RT_MANIFEST "{win}\\manifest.manifest"
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0 FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0

View File

@ -285,6 +285,7 @@ pub struct TermWindow {
pub window: Option<Window>, pub window: Option<Window>,
pub config: ConfigHandle, pub config: ConfigHandle,
pub config_overrides: serde_json::Value, pub config_overrides: serde_json::Value,
os_parameters: Option<parameters::Parameters>,
/// When we most recently received keyboard focus /// When we most recently received keyboard focus
focused: Option<Instant>, focused: Option<Instant>,
fonts: Rc<FontConfiguration>, fonts: Rc<FontConfiguration>,
@ -446,6 +447,8 @@ impl TermWindow {
} }
} }
self.os_parameters = window.get_os_parameters(&self.config).unwrap_or(None);
window.show(); window.show();
if self.render_state.is_none() { if self.render_state.is_none() {
@ -690,6 +693,7 @@ impl TermWindow {
let myself = Self { let myself = Self {
config_subscription: None, config_subscription: None,
os_parameters: None,
gl: None, gl: None,
window: None, window: None,
window_background, window_background,
@ -1432,6 +1436,14 @@ impl TermWindow {
}; };
if let Some(window) = self.window.as_ref().map(|w| w.clone()) { if let Some(window) = self.window.as_ref().map(|w| w.clone()) {
self.os_parameters = match window.get_os_parameters(&config) {
Ok(os_parameters) => os_parameters,
Err(_) => {
log::warn!("Error while getting OS parameters");
None
}
};
self.apply_scale_change(&dimensions, self.fonts.get_font_scale(), &window); self.apply_scale_change(&dimensions, self.fonts.get_font_scale(), &window);
self.apply_dimensions(&dimensions, None, &window); self.apply_dimensions(&dimensions, None, &window);
window.config_did_change(&config); window.config_did_change(&config);

View File

@ -920,6 +920,58 @@ impl super::TermWindow {
Ok(()) Ok(())
} }
fn paint_window_borders(&mut self) -> anyhow::Result<()> {
if let Some(ref os_params) = self.os_parameters {
if let Some(ref border_dimensions) = os_params.border_dimensions {
let gl_state = self.render_state.as_ref().unwrap();
let vb = &gl_state.vb[1];
let mut vb_mut = vb.current_vb_mut();
let mut layer1 = vb.map(&mut vb_mut);
let height = self.dimensions.pixel_height as f32;
let width = self.dimensions.pixel_width as f32;
let border_top = border_dimensions.top.get() as f32;
if border_top > 0.0 {
self.filled_rectangle(
&mut layer1,
euclid::rect(0.0, 0.0, width, border_top),
border_dimensions.color,
)?;
}
let border_left = border_dimensions.left.get() as f32;
if border_left > 0.0 {
self.filled_rectangle(
&mut layer1,
euclid::rect(0.0, 0.0, border_left, height),
border_dimensions.color,
)?;
}
let border_bottom = border_dimensions.bottom.get() as f32;
if border_bottom > 0.0 {
self.filled_rectangle(
&mut layer1,
euclid::rect(0.0, height - border_bottom, width, height),
border_dimensions.color,
)?;
}
let border_right = border_dimensions.right.get() as f32;
if border_right > 0.0 {
self.filled_rectangle(
&mut layer1,
euclid::rect(width - border_right, 0.0, width, height),
border_dimensions.color,
)?;
}
}
}
Ok(())
}
pub fn paint_pane_opengl( pub fn paint_pane_opengl(
&mut self, &mut self,
pos: &PositionedPane, pos: &PositionedPane,
@ -1557,6 +1609,8 @@ impl super::TermWindow {
self.paint_tab_bar()?; self.paint_tab_bar()?;
} }
self.paint_window_borders()?;
Ok(()) Ok(())
} }

View File

@ -47,18 +47,22 @@ wezterm-font = { path = "../wezterm-font" }
wezterm-input-types = { path = "../wezterm-input-types" } wezterm-input-types = { path = "../wezterm-input-types" }
[target."cfg(windows)".dependencies] [target."cfg(windows)".dependencies]
clipboard-win = "2.2"
shared_library = "0.1"
winapi = { version = "0.3", features = [ winapi = { version = "0.3", features = [
"dwmapi", "dwmapi",
"handleapi", "handleapi",
"imm", "imm",
"libloaderapi", "libloaderapi",
"synchapi", "synchapi",
"sysinfoapi",
"winerror", "winerror",
"winuser", "winuser",
]} ]}
windows = { version="0.33.0", features = [
"UI_ViewManagement",
]}
winreg = "0.6" winreg = "0.6"
clipboard-win = "2.2"
shared_library = "0.1"
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] [target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
dirs-next = "2.0" dirs-next = "2.0"

View File

@ -1,5 +1,6 @@
use async_trait::async_trait; use async_trait::async_trait;
use bitflags::bitflags; use bitflags::bitflags;
use config::ConfigHandle;
use promise::Future; use promise::Future;
use std::any::Any; use std::any::Any;
use std::rc::Rc; use std::rc::Rc;
@ -50,6 +51,8 @@ pub struct Dimensions {
pub dpi: usize, pub dpi: usize,
} }
pub type Length = euclid::Length<isize, PixelUnit>;
pub type LengthF = euclid::Length<f32, PixelUnit>;
pub type Rect = euclid::Rect<isize, PixelUnit>; pub type Rect = euclid::Rect<isize, PixelUnit>;
pub type RectF = euclid::Rect<f32, PixelUnit>; pub type RectF = euclid::Rect<f32, PixelUnit>;
pub type Size = euclid::Size2D<isize, PixelUnit>; pub type Size = euclid::Size2D<isize, PixelUnit>;
@ -281,7 +284,10 @@ pub trait WindowOps {
/// environment. /// environment.
fn set_resize_increments(&self, _x: u16, _y: u16) {} fn set_resize_increments(&self, _x: u16, _y: u16) {}
fn get_title_font_and_point_size(&self) -> Option<(wezterm_font::parser::ParsedFont, f64)> { fn get_os_parameters(
None &self,
_config: &ConfigHandle,
) -> anyhow::Result<Option<os::parameters::Parameters>> {
Ok(None)
} }
} }

View File

@ -16,3 +16,5 @@ pub use x_and_wayland::*;
pub mod macos; pub mod macos;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub use self::macos::*; pub use self::macos::*;
pub mod parameters;

View File

@ -0,0 +1,24 @@
use wezterm_color_types::LinearRgba;
use wezterm_font::parser::ParsedFont;
use crate::Length;
pub struct TitleBar {
pub padding_left: Length,
pub padding_right: Length,
pub height: Option<Length>,
pub font_and_size: Option<(ParsedFont, f64)>,
}
pub struct Border {
pub top: Length,
pub left: Length,
pub bottom: Length,
pub right: Length,
pub color: LinearRgba,
}
pub struct Parameters {
pub title_bar: TitleBar,
pub border_dimensions: Option<Border>, // If present, the application should draw it
}

View File

@ -0,0 +1,7 @@
use winapi::um::winuser::WM_USER;
pub const TMT_CAPTIONFONT: i32 = 801;
pub const HP_HEADERITEM: i32 = 1;
pub const HIS_NORMAL: i32 = 1;
pub const UM_APPEARANCE_CHANGED: u32 = WM_USER + 1;

View File

@ -1,5 +1,6 @@
pub mod connection; pub mod connection;
pub mod event; pub mod event;
mod extra_constants;
mod keycodes; mod keycodes;
mod wgl; mod wgl;
pub mod window; pub mod window;

View File

@ -1,9 +1,11 @@
use super::*; use super::*;
use crate::connection::ConnectionOps; use crate::connection::ConnectionOps;
use crate::parameters::Parameters;
use crate::{ use crate::{
Appearance, Clipboard, DeadKeyStatus, Dimensions, Handled, KeyCode, KeyEvent, Modifiers, Appearance, Clipboard, DeadKeyStatus, Dimensions, Handled, KeyCode, KeyEvent, Length,
MouseButtons, MouseCursor, MouseEvent, MouseEventKind, MousePress, Point, RawKeyEvent, Rect, Modifiers, MouseButtons, MouseCursor, MouseEvent, MouseEventKind, MousePress, Point,
ScreenPoint, WindowDecorations, WindowEvent, WindowEventSender, WindowOps, WindowState, RawKeyEvent, Rect, ScreenPoint, WindowDecorations, WindowEvent, WindowEventSender, WindowOps,
WindowState,
}; };
use anyhow::{bail, Context}; use anyhow::{bail, Context};
use async_trait::async_trait; use async_trait::async_trait;
@ -22,6 +24,7 @@ use std::io::{self, Error as IoError};
use std::os::windows::ffi::OsStringExt; use std::os::windows::ffi::OsStringExt;
use std::ptr::{null, null_mut}; use std::ptr::{null, null_mut};
use std::rc::Rc; use std::rc::Rc;
use wezterm_color_types::LinearRgba;
use wezterm_font::FontConfiguration; use wezterm_font::FontConfiguration;
use winapi::shared::minwindef::*; use winapi::shared::minwindef::*;
use winapi::shared::ntdef::*; use winapi::shared::ntdef::*;
@ -29,11 +32,15 @@ use winapi::shared::windef::*;
use winapi::shared::winerror::S_OK; use winapi::shared::winerror::S_OK;
use winapi::um::imm::*; use winapi::um::imm::*;
use winapi::um::libloaderapi::GetModuleHandleW; use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::sysinfoapi::{GetTickCount, GetVersionExW};
use winapi::um::uxtheme::{ use winapi::um::uxtheme::{
CloseThemeData, GetThemeFont, GetThemeSysFont, OpenThemeData, SetWindowTheme, CloseThemeData, GetThemeFont, GetThemeSysFont, OpenThemeData, SetWindowTheme,
}; };
use winapi::um::wingdi::LOGFONTW; use winapi::um::wingdi::{LOGFONTW, MAKEPOINTS};
use winapi::um::winnt::OSVERSIONINFOW;
use winapi::um::winuser::*; use winapi::um::winuser::*;
use windows::UI::Color as WUIColor;
use windows::UI::ViewManagement::{UIColorType, UISettings};
use winreg::enums::HKEY_CURRENT_USER; use winreg::enums::HKEY_CURRENT_USER;
use winreg::RegKey; use winreg::RegKey;
@ -51,6 +58,7 @@ unsafe impl Sync for HWindow {}
pub(crate) struct WindowInner { pub(crate) struct WindowInner {
/// Non-owning reference to the window handle /// Non-owning reference to the window handle
hwnd: HWindow, hwnd: HWindow,
is_win10: bool,
events: WindowEventSender, events: WindowEventSender,
gl_state: Option<Rc<glium::backend::Context>>, gl_state: Option<Rc<glium::backend::Context>>,
/// Fraction of mouse scroll /// Fraction of mouse scroll
@ -62,6 +70,7 @@ pub(crate) struct WindowInner {
dead_pending: Option<(Modifiers, u32)>, dead_pending: Option<(Modifiers, u32)>,
saved_placement: Option<WINDOWPLACEMENT>, saved_placement: Option<WINDOWPLACEMENT>,
track_mouse_leave: bool, track_mouse_leave: bool,
last_resize_type: WPARAM,
keyboard_info: KeyboardLayoutInfo, keyboard_info: KeyboardLayoutInfo,
appearance: Appearance, appearance: Appearance,
@ -72,6 +81,10 @@ pub(crate) struct WindowInner {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct Window(HWindow); pub struct Window(HWindow);
fn wuicolor_to_linearrgba(color: WUIColor) -> LinearRgba {
LinearRgba::with_srgba(color.R, color.G, color.B, 255)
}
fn rect_width(r: &RECT) -> i32 { fn rect_width(r: &RECT) -> i32 {
r.right - r.left r.right - r.left
} }
@ -266,14 +279,19 @@ fn apply_decoration_immediate(hwnd: HWND, decorations: WindowDecorations) {
0, 0,
0, 0,
0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED, SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOOWNERZORDER
| SWP_FRAMECHANGED,
); );
} }
} }
fn decorations_to_style(decorations: WindowDecorations) -> u32 { fn decorations_to_style(decorations: WindowDecorations) -> u32 {
if decorations == WindowDecorations::RESIZE { if decorations == WindowDecorations::RESIZE {
WS_THICKFRAME WS_OVERLAPPEDWINDOW
} else if decorations == WindowDecorations::TITLE { } else if decorations == WindowDecorations::TITLE {
WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
} else if decorations == WindowDecorations::NONE { } else if decorations == WindowDecorations::NONE {
@ -294,12 +312,6 @@ impl Window {
height: usize, height: usize,
lparam: *const RefCell<WindowInner>, lparam: *const RefCell<WindowInner>,
) -> anyhow::Result<HWND> { ) -> anyhow::Result<HWND> {
// Jamming this in here; it should really live in the application manifest,
// but having it here means that we don't have to create a manifest
unsafe {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}
let class_name = wide_string(class_name); let class_name = wide_string(class_name);
let h_inst = unsafe { GetModuleHandleW(null()) }; let h_inst = unsafe { GetModuleHandleW(null()) };
let class = WNDCLASSW { let class = WNDCLASSW {
@ -404,8 +416,10 @@ impl Window {
None => config::configuration(), None => config::configuration(),
}; };
let appearance = get_appearance(); let appearance = get_appearance();
let inner = Rc::new(RefCell::new(WindowInner { let inner = Rc::new(RefCell::new(WindowInner {
hwnd: HWindow(null_mut()), hwnd: HWindow(null_mut()),
is_win10: is_win10(),
appearance, appearance,
events, events,
gl_state: None, gl_state: None,
@ -417,6 +431,7 @@ impl Window {
dead_pending: None, dead_pending: None,
saved_placement: None, saved_placement: None,
track_mouse_leave: false, track_mouse_leave: false,
last_resize_type: SIZE_RESTORED,
config: config.clone(), config: config.clone(),
})); }));
@ -569,6 +584,10 @@ impl WindowInner {
}) })
.detach(); .detach();
} }
promise::spawn::spawn(async move {
PostMessageW(hwnd, extra_constants::UM_APPEARANCE_CHANGED, 0, 0);
})
.detach();
} }
} }
} }
@ -716,72 +735,129 @@ impl WindowOps for Window {
clipboard_win::set_clipboard_string(&text).ok(); clipboard_win::set_clipboard_string(&text).ok();
} }
fn get_title_font_and_point_size(&self) -> Option<(wezterm_font::parser::ParsedFont, f64)> { fn get_os_parameters(&self, config: &ConfigHandle) -> anyhow::Result<Option<Parameters>> {
const TMT_CAPTIONFONT: i32 = 801; let hwnd = self.0 .0;
const HP_HEADERITEM: i32 = 1; if hwnd.is_null() {
const HIS_NORMAL: i32 = 1; return Err(anyhow::anyhow!("HWND is null"));
unsafe fn populate_log_font(hwnd: HWND, hdc: HDC) -> Option<LOGFONTW> {
let mut log_font = LOGFONTW {
lfHeight: 0,
lfWidth: 0,
lfEscapement: 0,
lfOrientation: 0,
lfWeight: 0,
lfItalic: 0,
lfUnderline: 0,
lfStrikeOut: 0,
lfCharSet: 0,
lfOutPrecision: 0,
lfClipPrecision: 0,
lfQuality: 0,
lfPitchAndFamily: 0,
lfFaceName: [0u16; 32],
};
let theme = OpenThemeData(
hwnd,
[
'H' as u16, 'E' as u16, 'A' as u16, 'D' as u16, 'E' as u16, 'R' as u16, 0u16,
]
.as_ptr(),
);
if !theme.is_null() {
let res = GetThemeFont(
theme,
hdc,
HP_HEADERITEM,
HIS_NORMAL,
TMT_CAPTIONFONT,
&mut log_font,
);
if res == S_OK {
CloseThemeData(theme);
return Some(log_font);
}
}
let res = GetThemeSysFont(theme, TMT_CAPTIONFONT, &mut log_font);
if !theme.is_null() {
CloseThemeData(theme);
}
if res == S_OK {
Some(log_font)
} else {
None
}
} }
unsafe {
let hwnd = self.0 .0; let has_focus = !unsafe { GetFocus() }.is_null();
let is_maximized = window_is_maximized(hwnd);
let is_full_screen = unsafe {
let mut mi = MONITORINFO {
cbSize: std::mem::size_of::<MONITORINFO>() as u32,
..Default::default()
};
if GetMonitorInfoW(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &mut mi)
== winapi::shared::minwindef::TRUE
{
let mut client_rect = RECT::default();
if GetClientRect(hwnd, &mut client_rect) == winapi::shared::minwindef::TRUE {
client_rect.right >= mi.rcMonitor.right - mi.rcMonitor.left
&& client_rect.bottom >= mi.rcMonitor.bottom - mi.rcMonitor.top
} else {
false
}
} else {
false
}
};
let title_font = unsafe {
let hdc = GetDC(hwnd); let hdc = GetDC(hwnd);
let result = match populate_log_font(hwnd, hdc) { if hdc.is_null() {
Some(lf) => wezterm_font::locator::gdi::parse_log_font(&lf, hdc).ok(), return Err(anyhow::anyhow!("Could not get device context"));
}
let result = match get_title_log_font(hwnd, hdc) {
Some(lf) => Some(wezterm_font::locator::gdi::parse_log_font(&lf, hdc)?),
None => None, None => None,
}; };
ReleaseDC(hwnd, hdc); ReleaseDC(hwnd, hdc);
result result
};
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let use_accent = hkcu
.open_subkey("SOFTWARE\\Microsoft\\Windows\\DWM")?
.get_value::<u32, _>("ColorPrevalence")?;
let settings = UISettings::new()?;
let top_border_color = if has_focus {
if use_accent == 1 {
wuicolor_to_linearrgba(settings.GetColorValue(UIColorType::Accent)?)
} else {
if is_win10() {
LinearRgba(0.01, 0.01, 0.01, 0.67)
} else {
LinearRgba(0.026, 0.026, 0.026, 0.5)
}
}
} else {
if is_win10() {
LinearRgba(0.024, 0.024, 0.024, 0.5)
} else {
LinearRgba(0.028, 0.028, 0.028, 0.5)
}
};
const BASE_BORDER: Length = Length::new(0);
let is_resize = config.window_decorations == WindowDecorations::RESIZE;
let is_win10 = is_win10();
Ok(Some(Parameters {
title_bar: crate::parameters::TitleBar {
padding_left: Length::new(0),
padding_right: Length::new(0),
height: None,
font_and_size: title_font,
},
border_dimensions: Some(crate::parameters::Border {
top: if is_resize && !is_win10 && !is_maximized && !is_full_screen {
BASE_BORDER + Length::new(1)
} else {
BASE_BORDER
},
left: BASE_BORDER,
bottom: if is_resize && is_win10 && !is_maximized && !is_full_screen {
BASE_BORDER + Length::new(2)
} else {
BASE_BORDER
},
right: BASE_BORDER,
color: top_border_color,
}),
}))
}
}
unsafe fn get_title_log_font(hwnd: HWND, hdc: HDC) -> Option<LOGFONTW> {
let mut log_font = LOGFONTW::default();
let theme = OpenThemeData(hwnd, wide_string("HEADER").as_ptr());
if !theme.is_null() {
let res = GetThemeFont(
theme,
hdc,
extra_constants::HP_HEADERITEM,
extra_constants::HIS_NORMAL,
extra_constants::TMT_CAPTIONFONT,
&mut log_font,
);
if res == S_OK {
CloseThemeData(theme);
return Some(log_font);
} }
} }
let res = GetThemeSysFont(theme, extra_constants::TMT_CAPTIONFONT, &mut log_font);
if !theme.is_null() {
CloseThemeData(theme);
}
if res == S_OK {
Some(log_font)
} else {
None
}
} }
/// Set up bidirectional pointers: /// Set up bidirectional pointers:
@ -817,6 +893,152 @@ unsafe fn wm_ncdestroy(
None None
} }
unsafe fn wm_nccalcsize(hwnd: HWND, _msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<LRESULT> {
if let Some(inner) = rc_from_hwnd(hwnd) {
let inner = inner.borrow_mut();
if !(wparam == 1 && inner.config.window_decorations == WindowDecorations::RESIZE) {
return None;
}
if inner.saved_placement.is_none() {
let dpi = GetDpiForWindow(hwnd);
let frame_x = GetSystemMetricsForDpi(SM_CXFRAME, dpi);
let frame_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi);
let padding = GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
let params = (lparam as *mut NCCALCSIZE_PARAMS).as_mut().unwrap();
let mut requested_client_rect = &mut params.rgrc[0];
requested_client_rect.right -= frame_x + padding;
requested_client_rect.left += frame_x + padding;
// Handle bugged top window border on Windows 10
if inner.is_win10 {
if window_is_maximized(hwnd) {
requested_client_rect.top += frame_y + padding;
requested_client_rect.bottom -= frame_y + padding;
} else {
requested_client_rect.top += 1;
requested_client_rect.bottom -= frame_y - padding;
}
} else {
requested_client_rect.bottom -= frame_y + padding;
if window_is_maximized(hwnd) {
requested_client_rect.top += frame_y + padding;
}
}
}
return Some(0);
}
None
}
unsafe fn wm_nchittest(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<LRESULT> {
if let Some(inner) = rc_from_hwnd(hwnd) {
let inner = inner.borrow_mut();
if inner.config.window_decorations != WindowDecorations::RESIZE {
return None;
}
// Let the default procedure handle resizing areas
let result = DefWindowProcW(hwnd, msg, wparam, lparam);
if matches!(
result,
HTNOWHERE
| HTRIGHT
| HTLEFT
| HTTOPLEFT
| HTTOP
| HTTOPRIGHT
| HTBOTTOMRIGHT
| HTBOTTOM
| HTBOTTOMLEFT
) {
return Some(result);
}
// The adjustment in NCCALCSIZE messes with the detection
// of the top hit area so manually fixing that.
let dpi = GetDpiForWindow(hwnd);
let frame_x = GetSystemMetricsForDpi(SM_CXFRAME, dpi) as isize;
let frame_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi) as isize;
let padding = GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) as isize;
let coords = mouse_coords(lparam);
let cursor_point = screen_to_client(hwnd, ScreenPoint::new(coords.x, coords.y));
let is_maximized = window_is_maximized(hwnd);
// check if mouse is in any of the resize areas (HTTOP, HTBOTTOM, etc)
let mut client_rect = RECT::default();
let client_rect_is_valid =
GetClientRect(hwnd, &mut client_rect) == winapi::shared::minwindef::TRUE;
// Since we are eating the bottom window frame to deal with a Windows 10 bug,
// we detect resizing in the window client area as a workaround
if !is_maximized
&& inner.is_win10
&& client_rect_is_valid
&& cursor_point.y >= (client_rect.bottom as isize) - (frame_y + padding)
{
if cursor_point.x <= (frame_x + padding) {
return Some(HTBOTTOMLEFT);
} else if cursor_point.x >= (client_rect.right as isize) - (frame_x + padding) {
return Some(HTBOTTOMRIGHT);
} else {
return Some(HTBOTTOM);
}
}
if !is_maximized && cursor_point.y >= 0 && cursor_point.y < frame_y {
if cursor_point.x <= (frame_x + padding) {
return Some(HTTOPLEFT);
} else if cursor_point.x >= (client_rect.right as isize) - (frame_x + padding) {
return Some(HTTOPRIGHT);
} else {
return Some(HTTOP);
}
}
return Some(HTCLIENT);
}
None
}
fn window_is_maximized(hwnd: HWND) -> bool {
let mut placement = WINDOWPLACEMENT {
length: std::mem::size_of::<WINDOWPLACEMENT>() as _,
..Default::default()
};
if unsafe { GetWindowPlacement(hwnd, &mut placement) } != winapi::shared::minwindef::TRUE {
false
} else {
placement.showCmd == SW_SHOWMAXIMIZED as u32
}
}
fn is_win10() -> bool {
let osver = OSVERSIONINFOW {
dwOSVersionInfoSize: std::mem::size_of::<OSVERSIONINFOW>() as _,
..Default::default()
};
if unsafe { GetVersionExW(&osver as *const _ as _) } == winapi::shared::minwindef::TRUE {
osver.dwBuildNumber < 22000
} else {
true
}
}
/// "Blur behind" is the old vista term for a cool blurring /// "Blur behind" is the old vista term for a cool blurring
/// effect that the DWM could enable. Subsequent windows /// effect that the DWM could enable. Subsequent windows
/// versions have removed the blurring. We use this call /// versions have removed the blurring. We use this call
@ -913,6 +1135,17 @@ fn apply_theme(hwnd: HWND) -> Option<LRESULT> {
None None
} }
fn dispatch_appearance_changed(hwnd: HWND) -> Option<LRESULT> {
if let Some(inner) = rc_from_hwnd(hwnd) {
let mut inner = inner.borrow_mut();
let appearance = inner.appearance;
inner
.events
.dispatch(WindowEvent::AppearanceChanged(appearance));
}
None
}
unsafe fn wm_enter_exit_size_move( unsafe fn wm_enter_exit_size_move(
hwnd: HWND, hwnd: HWND,
msg: UINT, msg: UINT,
@ -947,6 +1180,23 @@ unsafe fn wm_windowposchanged(
Some(0) Some(0)
} }
unsafe fn wm_sizeraw(hwnd: HWND, _msg: UINT, wparam: WPARAM, _lparam: LPARAM) -> Option<LRESULT> {
if wparam == SIZE_RESTORED || wparam == SIZE_MAXIMIZED {
if let Some(inner) = rc_from_hwnd(hwnd) {
let mut inner = inner.borrow_mut();
if inner.last_resize_type != wparam {
inner.last_resize_type = wparam;
let appearance = inner.appearance;
inner
.events
.dispatch(WindowEvent::AppearanceChanged(appearance));
}
}
}
None
}
unsafe fn wm_size(hwnd: HWND, _msg: UINT, _wparam: WPARAM, _lparam: LPARAM) -> Option<LRESULT> { unsafe fn wm_size(hwnd: HWND, _msg: UINT, _wparam: WPARAM, _lparam: LPARAM) -> Option<LRESULT> {
let mut should_paint = false; let mut should_paint = false;
let mut should_pump = false; let mut should_pump = false;
@ -974,10 +1224,12 @@ unsafe fn wm_set_focus(
_lparam: LPARAM, _lparam: LPARAM,
) -> Option<LRESULT> { ) -> Option<LRESULT> {
if let Some(inner) = rc_from_hwnd(hwnd) { if let Some(inner) = rc_from_hwnd(hwnd) {
let mut inner = inner.borrow_mut();
inner.events.dispatch(WindowEvent::FocusChanged(true));
let appearance = inner.appearance;
inner inner
.borrow_mut()
.events .events
.dispatch(WindowEvent::FocusChanged(true)); .dispatch(WindowEvent::AppearanceChanged(appearance));
} }
None None
} }
@ -989,10 +1241,12 @@ unsafe fn wm_kill_focus(
_lparam: LPARAM, _lparam: LPARAM,
) -> Option<LRESULT> { ) -> Option<LRESULT> {
if let Some(inner) = rc_from_hwnd(hwnd) { if let Some(inner) = rc_from_hwnd(hwnd) {
let mut inner = inner.borrow_mut();
inner.events.dispatch(WindowEvent::FocusChanged(false));
let appearance = inner.appearance;
inner inner
.borrow_mut()
.events .events
.dispatch(WindowEvent::FocusChanged(false)); .dispatch(WindowEvent::AppearanceChanged(appearance));
} }
None None
} }
@ -1050,11 +1304,8 @@ fn mods_and_buttons(wparam: WPARAM) -> (Modifiers, MouseButtons) {
} }
fn mouse_coords(lparam: LPARAM) -> Point { fn mouse_coords(lparam: LPARAM) -> Point {
// Take care to get the signedness correct! let point = MAKEPOINTS(lparam as _);
let x = (lparam & 0xffff) as u16 as i16 as isize; Point::new(point.x as _, point.y as _)
let y = ((lparam >> 16) & 0xffff) as u16 as i16 as isize;
Point::new(x, y)
} }
fn screen_to_client(hwnd: HWND, point: ScreenPoint) -> Point { fn screen_to_client(hwnd: HWND, point: ScreenPoint) -> Point {
@ -1660,7 +1911,6 @@ impl KeyboardLayoutInfo {
/// Generate a MSG and call TranslateMessage upon it /// Generate a MSG and call TranslateMessage upon it
unsafe fn translate_message(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) { unsafe fn translate_message(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) {
use winapi::um::sysinfoapi::GetTickCount;
TranslateMessage(&MSG { TranslateMessage(&MSG {
hwnd, hwnd,
message: msg, message: msg,
@ -2019,7 +2269,10 @@ unsafe fn do_wnd_proc(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) ->
match msg { match msg {
WM_NCCREATE => wm_nccreate(hwnd, msg, wparam, lparam), WM_NCCREATE => wm_nccreate(hwnd, msg, wparam, lparam),
WM_NCDESTROY => wm_ncdestroy(hwnd, msg, wparam, lparam), WM_NCDESTROY => wm_ncdestroy(hwnd, msg, wparam, lparam),
WM_NCCALCSIZE => wm_nccalcsize(hwnd, msg, wparam, lparam),
WM_NCHITTEST => wm_nchittest(hwnd, msg, wparam, lparam),
WM_PAINT => wm_paint(hwnd, msg, wparam, lparam), WM_PAINT => wm_paint(hwnd, msg, wparam, lparam),
WM_SIZE => wm_sizeraw(hwnd, msg, wparam, lparam),
WM_ENTERSIZEMOVE | WM_EXITSIZEMOVE => wm_enter_exit_size_move(hwnd, msg, wparam, lparam), WM_ENTERSIZEMOVE | WM_EXITSIZEMOVE => wm_enter_exit_size_move(hwnd, msg, wparam, lparam),
WM_WINDOWPOSCHANGED => wm_windowposchanged(hwnd, msg, wparam, lparam), WM_WINDOWPOSCHANGED => wm_windowposchanged(hwnd, msg, wparam, lparam),
WM_SETFOCUS => wm_set_focus(hwnd, msg, wparam, lparam), WM_SETFOCUS => wm_set_focus(hwnd, msg, wparam, lparam),
@ -2050,6 +2303,7 @@ unsafe fn do_wnd_proc(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) ->
} }
None None
} }
extra_constants::UM_APPEARANCE_CHANGED => dispatch_appearance_changed(hwnd),
_ => None, _ => None,
} }
} }