Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ tracing = { version = "0.1", optional = true }
x11rb = { version = "0.13.2", features = ["cursor", "dri3", "present", "resource_manager", "allow-unsafe-code", "dl-libxcb"], default-features = false }
xkbcommon-dl = { version = "0.4.2", features = ["x11"] }
x11-dl = { version = "2.21.0" }
calloop = "0.14.4"
calloop = "=0.14.4" # 0.14.5 has MSRV of 1.86
percent-encoding = "2.3.2"
bytemuck = { version = "1.25.2", features = ["extern_crate_alloc"] }
libloading = "0.8.9" # Libloading 0.9 is out but its MSRV is 1.88
Expand Down
43 changes: 38 additions & 5 deletions src/platform/win/dpi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use std::ops::Deref;
#[derive(Copy, Clone, Default)]
pub(crate) struct DpiScalingStrategy {
pub assume_96_dpi: bool,
pub should_use_host_suggested_scale_factor: bool,
pub should_enable_nc_dpi_scaling_manually: bool,
pub thread_dpi_awareness_context: Option<DpiAwarenessContext>,
}

Expand Down Expand Up @@ -47,6 +49,11 @@ impl DpiScalingStrategy {
return Self::get_from_process(user32, &shcore);
};

crate::debug!(
"Parent DPI Awareness Context detected: {:?}",
parent_dpi_ctx.get_type(user32_lib)
);

if parent.supports_mixed_dpi_hosting_behavior(user32_lib) {
Self::get_best_matching_with_dpi_parent_awareness_context(
parent_dpi_ctx,
Expand Down Expand Up @@ -91,9 +98,11 @@ impl DpiScalingStrategy {
// These are documented to not be compatible with per-monitor awareness types, so we'll fall back to System-aware
// See: https://learn.microsoft.com/en-us/windows/win32/api/windef/ne-windef-dpi_hosting_behavior#remarks
if matches!(dpi_awareness_type, Some(Unaware | UnawareGDIScaled | SystemDpiAware)) {
crate::debug!("Parent has DPI Awareness Context with Per-Monitor DPI awareness, falling back to System DPI Awareness.");
crate::debug!("Parent has DPI Awareness Context without Per-Monitor DPI awareness, falling back to System DPI Awareness.");
return Self {
assume_96_dpi: false,
should_use_host_suggested_scale_factor: false,
should_enable_nc_dpi_scaling_manually: false,
thread_dpi_awareness_context: Some(SystemDpiAware.into()),
};
}
Expand All @@ -113,7 +122,19 @@ impl DpiScalingStrategy {
// If type is unknown, assume it's better than System-Aware, and we can at least fetch the actual DPI.
let assume_96_dpi = matches!(dpi_awareness_type, Some(Unaware | UnawareGDIScaled));

Self { assume_96_dpi, thread_dpi_awareness_context: Some(dpi_awareness_context) }
// If type is unknown, assume it's better than Per-Monitor-Aware-V2, and we'll get WM_DPICHANGED messages from the OS
let should_use_host_suggested_scale_factor =
matches!(dpi_awareness_type, Some(PerMonitorDpiAware));

let should_enable_nc_dpi_scaling_manually =
matches!(dpi_awareness_type, Some(PerMonitorDpiAware));

Self {
assume_96_dpi,
should_use_host_suggested_scale_factor,
should_enable_nc_dpi_scaling_manually,
thread_dpi_awareness_context: Some(dpi_awareness_context),
}
}

fn get_from_process_legacy(shcore: &LazyLibraryModule<ExtendedShCore>) -> Self {
Expand All @@ -126,12 +147,24 @@ impl DpiScalingStrategy {
crate::debug!("Using legacy Process DPI Awareness: {:?}", awareness);

let assume_96_dpi = matches!(awareness, None | Some(Unaware));

Self { assume_96_dpi, thread_dpi_awareness_context: None }
let should_use_host_suggested_scale_factor = matches!(awareness, Some(PerMonitorDpiAware));
let should_enable_nc_dpi_scaling_manually = matches!(awareness, Some(PerMonitorDpiAware));

Self {
assume_96_dpi,
should_use_host_suggested_scale_factor,
should_enable_nc_dpi_scaling_manually,
thread_dpi_awareness_context: None,
}
}

fn completely_unaware() -> Self {
Self { assume_96_dpi: true, thread_dpi_awareness_context: None }
Self {
assume_96_dpi: true,
should_use_host_suggested_scale_factor: false,
should_enable_nc_dpi_scaling_manually: false,
thread_dpi_awareness_context: None,
}
}

fn get_best_supported(
Expand Down
13 changes: 11 additions & 2 deletions src/platform/win/error.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use crate::HandlerError;
use dpi::PhysicalSize;
use std::fmt::Display;

pub type Result<T> = std::result::Result<T, PlatformError>;

#[derive(Debug)]
pub enum PlatformError {
Win32(windows_core::Error),
ResizeFailed,
UnexpectedResizeResult { new_actual_size: PhysicalSize<u32> },
InvalidScaleFactor,
Handler(HandlerError),
}

Expand All @@ -27,7 +29,14 @@ impl Display for PlatformError {
match self {
PlatformError::Win32(e) => Display::fmt(e, f),
PlatformError::Handler(e) => Display::fmt(e, f),
PlatformError::ResizeFailed => f.write_str("Window resize request failed."),
PlatformError::UnexpectedResizeResult { new_actual_size } => {
write!(
f,
"Resize operation did not result in expected size, got {:?} instead.",
new_actual_size
)
}
PlatformError::InvalidScaleFactor => f.write_str("Invalid window scale factor."),
}
}
}
Expand Down
118 changes: 80 additions & 38 deletions src/platform/win/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,22 +98,31 @@ impl WindowHandle {
if self.state.current_size.get() == new_size {
Ok(())
} else {
Err(PlatformError::ResizeFailed)
Err(PlatformError::UnexpectedResizeResult {
new_actual_size: self.state.current_size.get(),
})
}
}

pub fn sizing_strategy(&self) -> SizingStrategy {
self.state.sizing_strategy
}

pub fn suggest_scale_factor(&self, scale_factor: f64) -> Result<()> {
pub fn suggest_scale_factor(&self, new_host_scale_factor: f64) -> Result<()> {
let new_dpi = Dpi::from_scale_factor(new_host_scale_factor)
.ok_or(PlatformError::InvalidScaleFactor)?;

let current_scale_factor = self.state.scale_factor();
self.state.fallback_scale_factor.set(Some(scale_factor));
self.state.fallback_scale_factor.set(Some(new_host_scale_factor));

let dpi_scaling_strategy = self.state.dpi_scaling_strategy.get();

if self.state.current_dpi.get().is_some() {
if !dpi_scaling_strategy.should_use_host_suggested_scale_factor {
return Ok(());
}

self.state.current_dpi.set(new_dpi);

let Some(hwnd) = self.hwnd.get() else { return Ok(()) };

let current_size = self.state.current_size.get();
Expand All @@ -122,23 +131,24 @@ impl WindowHandle {
.current_size
.get()
.to_logical::<f64>(current_scale_factor)
.to_physical(self.state.scale_factor());
.to_physical(new_host_scale_factor);

// This call doesn't meaningfully change the scaling factor, ignore the result
if current_size == new_size {
return Ok(());
}

let _guard = self.state.originate_host_resize();
let dpi_ctx =
DpiAwarenessGuard::new(&self.state.user32, self.state.dpi_scaling_strategy.get())?;
let dpi_ctx = DpiAwarenessGuard::new(&self.state.user32, dpi_scaling_strategy)?;

hwnd.resize_and_activate(new_size, None, &dpi_ctx)?;
hwnd.resize_and_activate(new_size, new_dpi, &dpi_ctx)?;

if self.state.current_size.get() == new_size {
Ok(())
} else {
Err(PlatformError::ResizeFailed)
Err(PlatformError::UnexpectedResizeResult {
new_actual_size: self.state.current_size.get(),
})
}
}

Expand Down Expand Up @@ -289,16 +299,37 @@ impl BaseviewWindow {
self.host.notify_destroyed()
}

fn request_resize_from_host(
&self, new_size: WindowSize,
) -> core::result::Result<(), HandlerError> {
fn request_host_resize(&self, new_size: WindowSize) -> core::result::Result<(), HandlerError> {
if self.shared_state.resize_host_originated.get() {
return Ok(());
};

self.host.request_resize(new_size)
}

fn adapt_host_window_to_size(
&self, previous_size: PhysicalSize<u32>, new_size: WindowSize,
) -> core::result::Result<(), ()> {
if let Err(e) = self.request_host_resize(new_size) {
warn!("Resize request from Host failed: {}. Reverting to previous size.", e);

if let Some(handler) = self.handler.get() {
if let Err(e) = handler.resized(new_size) {
warn!("Window Handler failed to resize to previous window size: {}", e);
}
};

self.window_state.shared.current_size.set(previous_size);
if let Err(e) = self.window_state.resize(previous_size.into()) {
warn!("Failed to resize back to previous window size: {}", e);
}

return Err(());
}

Ok(())
}

pub(crate) fn handle_on_frame(&self) {
let Some(handler) = self.handler.get() else { return };

Expand Down Expand Up @@ -326,7 +357,7 @@ impl Drop for BaseviewWindow {

impl WindowImpl for BaseviewWindow {
fn non_client_create(&self, window: HWnd) -> std::result::Result<(), PlatformError> {
if self.shared_state.dpi_scaling_strategy.get().assume_96_dpi {
if self.shared_state.dpi_scaling_strategy.get().should_enable_nc_dpi_scaling_manually {
window.enable_non_client_dpi_scaling(&self.shared_state.user32);
}

Expand All @@ -346,23 +377,37 @@ impl WindowImpl for BaseviewWindow {
.get_dpi_for_window(window, &self.shared_state.user32);

if let Some(dpi) = dpi {
if Some(dpi) != window_state.shared.current_dpi.get() {
window_state.shared.current_dpi.set(Some(dpi));
if dpi != window_state.shared.current_dpi.get() {
window_state.shared.current_dpi.set(dpi);

// We cannot create a window in "logical" pixels, and we can't DPI-scale to physical pixels because we
// have no way to know where the window will end up.
// So, at window creation, we assume a DPI=96, and if it ends up wrong, we resize the window
// So, at window creation, we assume a DPI=96 (or parent DPI if available), and if it ends up wrong, we resize the window
// to the actual logical size the user desired.
let new_size = self.initial_size.to_physical(dpi.scale_factor());

// Preemptively update so a synchronous WM_SIZE from SetWindowPos below
// doesn't also emit Resized.
window_state.shared.current_size.set(new_size);
let previous_size = window_state.shared.current_size.replace(new_size);
let guard = DpiAwarenessGuard::new(
&window_state.shared.user32,
self.shared_state.dpi_scaling_strategy.get(),
)?;
window.resize_and_activate(new_size, Some(dpi), &guard)?;
window.resize_and_activate(new_size, dpi, &guard)?;

let _ = self.adapt_host_window_to_size(
previous_size,
WindowSize::from_physical(new_size, dpi.scale_factor()),
);
} else {
// If the host queried size before it gave us a parent, this is the one it's been using.
let previous_host_size = self.initial_size.to_physical(1.0);
let current_size = self.initial_size.to_physical(dpi.scale_factor());

let _ = self.adapt_host_window_to_size(
previous_host_size,
WindowSize::from_physical(current_size, dpi.scale_factor()),
);
}
}

Expand Down Expand Up @@ -598,7 +643,7 @@ unsafe fn wnd_proc_inner(
return Some(-1);
}

if let Err(e) = window_bv.request_resize_from_host(new_size) {
if let Err(e) = window_bv.request_host_resize(new_size) {
warn!("Resize request from Host failed: {}. Reverting to previous size.", e);

if let Err(e) = handler.resized(new_size) {
Expand Down Expand Up @@ -634,9 +679,9 @@ unsafe fn wnd_proc_inner(
let new_size = suggested_rect.size();

let changed = window_state.shared.current_size.get() != new_size
|| window_state.shared.current_dpi.get() != Some(dpi);
|| window_state.shared.current_dpi.get() != dpi;

window_state.shared.current_dpi.set(Some(dpi));
window_state.shared.current_dpi.set(dpi);
let previous_size = window_state.shared.current_size.replace(new_size);

// Windows makes us resize the window manually. This however will not send a WM_SIZE event,
Expand All @@ -654,20 +699,11 @@ unsafe fn wnd_proc_inner(
if let Err(e) = window_state.resize(previous_size.into()) {
warn!("Failed to resize back to previous window size: {}", e);
}
}

if let Err(e) = window_bv.request_resize_from_host(new_size) {
warn!("Resize request from Host failed: {}. Reverting to previous size.", e);

if let Err(e) = handler.resized(new_size) {
warn!("Window Handler failed to resize to previous window size: {}", e);
}

window_state.shared.current_size.set(previous_size);
if let Err(e) = window_state.resize(previous_size.into()) {
warn!("Failed to resize back to previous window size: {}", e);
}
return Some(-1);
}

if let Err(()) = window_bv.adapt_host_window_to_size(previous_size, new_size) {
return Some(-1);
}
}
Expand Down Expand Up @@ -710,16 +746,22 @@ unsafe fn wnd_proc_inner(

if let Some(size) = sizing.min_size() {
let size = size.to_physical(window_state.shared.scale_factor());
let size =
ctx.client_area_to_nc_area(size.into(), style, dpi).unwrap().size().cast();
let size = ctx
.client_area_to_nc_area(size.into(), style, Some(dpi))
.unwrap()
.size()
.cast();
let pt = POINT { x: size.width, y: size.height };
(&raw mut (*info).ptMinTrackSize).write(pt);
}

if let Some(size) = sizing.max_size() {
let size = size.to_physical(window_state.shared.scale_factor());
let size =
ctx.client_area_to_nc_area(size.into(), style, dpi).unwrap().size().cast();
let size = ctx
.client_area_to_nc_area(size.into(), style, Some(dpi))
.unwrap()
.size()
.cast();
let pt = POINT { x: size.width, y: size.height };
(&raw mut (*info).ptMaxTrackSize).write(pt);
}
Expand All @@ -740,7 +782,7 @@ impl WindowHandle {
pub fn create_window(init: WindowInitializer) -> Result<WindowHandle> {
let extended_user_32 = LibraryModule::load()?;

let shared_state = WindowSharedState::new(extended_user_32, &init.settings);
let shared_state = WindowSharedState::new(extended_user_32, &init);

if init.settings.wait_for_parent && init.settings.parent.is_none() {
return Ok(WindowHandle {
Expand Down
Loading
Loading