From ca90a231b481da62e5a3b621f952e680efc21ef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduard=20M=C3=BCller?= Date: Fri, 20 Feb 2026 15:41:15 +0100 Subject: [PATCH 1/2] Allow querying window scale factors from editors - Add Window scale accessors to plugin editors and window adapters - Add new Window `screen_scale` functions, which can be used when the host queries the plugin's window size, and there's no window created yet --- examples/gain-plugin/src/editor.rs | 14 ++++++++- plinth-plugin/src/editor.rs | 3 ++ plugin-canvas-slint/src/editor.rs | 8 +++-- plugin-canvas-slint/src/window_adapter.rs | 14 +++------ plugin-canvas/src/platform/interface.rs | 3 +- plugin-canvas/src/platform/mac/window.rs | 15 +++++++++- .../src/platform/win32/drop_target.rs | 2 +- plugin-canvas/src/platform/win32/window.rs | 30 +++++++++++++++---- plugin-canvas/src/platform/x11/window.rs | 8 ++++- plugin-canvas/src/window.rs | 12 +++++--- 10 files changed, 82 insertions(+), 27 deletions(-) diff --git a/examples/gain-plugin/src/editor.rs b/examples/gain-plugin/src/editor.rs index cf304d1..5e1a3b5 100644 --- a/examples/gain-plugin/src/editor.rs +++ b/examples/gain-plugin/src/editor.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use plinth_plugin::{raw_window_handle::RawWindowHandle, Editor, Host}; -use plugin_canvas_slint::{editor::{EditorHandle, SlintEditor}, plugin_canvas::window::WindowAttributes}; +use plugin_canvas_slint::{editor::{EditorHandle, SlintEditor}, plugin_canvas::{Window, window::WindowAttributes}}; use crate::{parameters::GainParameters, view::GainPluginView}; @@ -48,6 +48,18 @@ impl Editor for GainPluginEditor { self.editor_handle = None; } + fn scale(&self) -> f64 { + if let Some(editor_handle) = self.editor_handle.as_ref() { + editor_handle.scale() + } else { + Window::sceen_scale() + } + } + + fn set_scale(&mut self, _scale: f64) { + // ignore + } + fn on_frame(&mut self) { if let Some(editor_handle) = self.editor_handle.as_ref() { editor_handle.on_frame(); diff --git a/plinth-plugin/src/editor.rs b/plinth-plugin/src/editor.rs index 641ff1e..c81aa4e 100644 --- a/plinth-plugin/src/editor.rs +++ b/plinth-plugin/src/editor.rs @@ -9,6 +9,9 @@ pub trait Editor { fn open(&mut self, parent: RawWindowHandle); fn close(&mut self); + /// Get applied window scale + fn scale(&self) -> f64 { 1.0 } + /// Returns current window size fn window_size(&self) -> (f64, f64) { Self::DEFAULT_SIZE diff --git a/plugin-canvas-slint/src/editor.rs b/plugin-canvas-slint/src/editor.rs index 6b4dce9..b029e9f 100644 --- a/plugin-canvas-slint/src/editor.rs +++ b/plugin-canvas-slint/src/editor.rs @@ -3,7 +3,7 @@ use std::rc::{Rc, Weak}; use std::sync::Arc; use raw_window_handle::RawWindowHandle; -use plugin_canvas::{event::EventResponse, window::WindowAttributes, Event}; +use plugin_canvas::{event::EventResponse, window::WindowAttributes, Event, Window}; use slint::platform::WindowAdapter; use crate::{platform::PluginCanvasPlatform, view::PluginView, window_adapter::{PluginCanvasWindowAdapter, WINDOW_ADAPTER_FROM_SLINT, WINDOW_TO_SLINT}}; @@ -87,9 +87,11 @@ impl EditorHandle { } } - pub fn set_scale(&self, scale: f64) { + pub fn scale(&self) -> f64 { if let Some(window_adapter) = self.window_adapter() { - window_adapter.set_scale(scale); + window_adapter.scale() + } else { + Window::sceen_scale() } } diff --git a/plugin-canvas-slint/src/window_adapter.rs b/plugin-canvas-slint/src/window_adapter.rs index 5ddbb69..2989955 100644 --- a/plugin-canvas-slint/src/window_adapter.rs +++ b/plugin-canvas-slint/src/window_adapter.rs @@ -44,7 +44,7 @@ impl PluginCanvasWindowAdapter { let window_attributes = plugin_canvas_window.attributes(); let scale = window_attributes.scale(); - let combined_scale = scale * plugin_canvas_window.os_scale(); + let combined_scale = scale * plugin_canvas_window.scale(); let plugin_canvas_size = window_attributes.size() * combined_scale; let slint_size = slint::PhysicalSize { @@ -95,14 +95,8 @@ impl PluginCanvasWindowAdapter { *self.view.borrow_mut() = Some(view); } - pub fn set_scale(&self, scale: f64) { - self.scale.store(scale, Ordering::Release); - - let combined_scale = scale * self.plugin_canvas_window.os_scale(); - - self.slint_window.dispatch_event( - WindowEvent::ScaleFactorChanged { scale_factor: combined_scale as f32 } - ); + pub fn scale(&self) -> f64 { + self.scale.load(Ordering::Relaxed) * self.plugin_canvas_window.scale() } pub fn close(&self) { @@ -342,7 +336,7 @@ impl WindowAdapter for PluginCanvasWindowAdapter { fn set_size(&self, size: slint::WindowSize) { let scale = self.scale.load(Ordering::Acquire); - let os_scale = self.plugin_canvas_window.os_scale(); + let os_scale = self.plugin_canvas_window.scale(); let physical_size = size.to_physical(os_scale as _); let logical_size = size.to_logical(os_scale as _); diff --git a/plugin-canvas/src/platform/interface.rs b/plugin-canvas/src/platform/interface.rs index a240252..49409bc 100644 --- a/plugin-canvas/src/platform/interface.rs +++ b/plugin-canvas/src/platform/interface.rs @@ -12,7 +12,8 @@ pub(crate) trait OsWindowInterface: HasDisplayHandle + HasWindowHandle + Sized { event_callback: Box, ) -> Result; - fn os_scale(&self) -> f64; + fn sceen_scale() -> f64; + fn scale(&self) -> f64; fn resized(&self, size: LogicalSize); diff --git a/plugin-canvas/src/platform/mac/window.rs b/plugin-canvas/src/platform/mac/window.rs index 0ced541..31af908 100644 --- a/plugin-canvas/src/platform/mac/window.rs +++ b/plugin-canvas/src/platform/mac/window.rs @@ -113,7 +113,20 @@ impl OsWindowInterface for OsWindow { Ok(OsWindowHandle::new(window)) } - fn os_scale(&self) -> f64 { + fn sceen_scale() -> f64 { + if let Some(main_thread_marker) = MainThreadMarker::new() { + if let Some(screen) = NSScreen::mainScreen(main_thread_marker) { + return screen.backingScaleFactor() + } + } + else { + #[cfg(debug_assertions)] + panic!("Calling screen_scale from an unexpected thread"); + } + 1.0 + } + + fn scale(&self) -> f64 { self.view() .window() .map(|window| window.backingScaleFactor()) diff --git a/plugin-canvas/src/platform/win32/drop_target.rs b/plugin-canvas/src/platform/win32/drop_target.rs index 1f9b396..637e4f1 100644 --- a/plugin-canvas/src/platform/win32/drop_target.rs +++ b/plugin-canvas/src/platform/win32/drop_target.rs @@ -96,7 +96,7 @@ impl DropTarget { return LogicalPosition::default(); }; - let scale = window.os_scale(); + let scale = window.scale(); // It looks like MapWindowPoints isn't DPI aware (and neither is ScreenToClient), // so we need to pre-scale the point here? diff --git a/plugin-canvas/src/platform/win32/window.rs b/plugin-canvas/src/platform/win32/window.rs index 164bc81..ea9ec52 100644 --- a/plugin-canvas/src/platform/win32/window.rs +++ b/plugin-canvas/src/platform/win32/window.rs @@ -5,6 +5,7 @@ use cursor_icon::CursorIcon; use keyboard_types::Code; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle, Win32WindowHandle}; use uuid::Uuid; +use windows::Win32::Graphics::Gdi::{GetDC, GetDeviceCaps, LOGPIXELSX, LOGPIXELSY, ReleaseDC}; use windows::{core::PCWSTR, Win32::UI::Input::KeyboardAndMouse::{VK_LWIN, VK_RWIN}}; use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, POINT, WPARAM}; use windows::Win32::Graphics::{Dwm::{DwmFlush, DwmIsCompositionEnabled}, Dxgi::{CreateDXGIFactory, IDXGIFactory, IDXGIOutput}, Gdi::{ClientToScreen, MonitorFromWindow, ScreenToClient, HBRUSH, MONITOR_DEFAULTTOPRIMARY}}; @@ -33,6 +34,20 @@ pub struct OsWindow { keyboard_modifiers: RefCell, } +fn window_scale(hwnd: Option) -> f64 { + // Could use `GetDpiForWindow` here, but that's available for Windows 10 only + unsafe { + let hdc = GetDC(hwnd); + if !hdc.is_invalid() { + let dpi = GetDeviceCaps(Some(hdc), LOGPIXELSX).min(GetDeviceCaps(Some(hdc), LOGPIXELSY)); + ReleaseDC(hwnd, hdc); + dpi.max(96) as f64 / 96.0 + } else { + 1.0 + } + } +} + impl OsWindow { pub(super) fn send_event(&self, event: Event) -> EventResponse { (self.event_callback)(event) @@ -59,7 +74,7 @@ impl OsWindow { } fn logical_mouse_position(&self, lparam: LPARAM) -> LogicalPosition { - let scale = self.os_scale(); + let scale = self.scale(); PhysicalPosition { x: (lparam.0 & 0xFFFF) as i16 as i32, @@ -100,7 +115,8 @@ impl OsWindowInterface for OsWindow { }; let class_name = to_wstr("plugin-canvas-".to_string() + &Uuid::new_v4().simple().to_string()); - let size = Size::with_logical_size(window_attributes.size, window_attributes.scale); + let os_scale = window_scale(Some(HWND(parent_window_handle.hwnd.get() as _))); + let size = Size::with_logical_size(window_attributes.size, window_attributes.scale * os_scale); let cursor = unsafe { LoadCursorW(None, IDC_ARROW).unwrap() }; @@ -209,8 +225,12 @@ impl OsWindowInterface for OsWindow { Ok(OsWindowHandle::new(window)) } - fn os_scale(&self) -> f64 { - 1.0 + fn sceen_scale() -> f64 { + window_scale(None) + } + + fn scale(&self) -> f64 { + window_scale(Some(self.hwnd())) } fn resized(&self, size: LogicalSize) { @@ -271,7 +291,7 @@ impl OsWindowInterface for OsWindow { } fn warp_mouse(&self, position: LogicalPosition) { - let scale = self.os_scale(); + let scale = self.scale(); let physical_position = position.to_physical(scale); let mut point = POINT { diff --git a/plugin-canvas/src/platform/x11/window.rs b/plugin-canvas/src/platform/x11/window.rs index 5f70f7b..7dab0ff 100644 --- a/plugin-canvas/src/platform/x11/window.rs +++ b/plugin-canvas/src/platform/x11/window.rs @@ -304,7 +304,13 @@ impl OsWindowInterface for OsWindow { Ok(OsWindowHandle::new(Arc::new(window.into()))) } - fn os_scale(&self) -> f64 { + fn sceen_scale() -> f64 { + // TODO: implement me + return 1.0 + } + + fn scale(&self) -> f64 { + // TODO: implement me 1.0 } diff --git a/plugin-canvas/src/window.rs b/plugin-canvas/src/window.rs index 003078f..06d69c3 100644 --- a/plugin-canvas/src/window.rs +++ b/plugin-canvas/src/window.rs @@ -62,12 +62,16 @@ impl Window { }) } - pub fn attributes(&self) -> &WindowAttributes { - &self.attributes + pub fn sceen_scale() -> f64 { + OsWindow::sceen_scale() + } + + pub fn scale(&self) -> f64 { + self.os_window_handle.scale() } - pub fn os_scale(&self) -> f64 { - self.os_window_handle.os_scale() + pub fn attributes(&self) -> &WindowAttributes { + &self.attributes } pub fn resized(&self, size: LogicalSize) { From 177b1fd0ef099f2781b278121b0fee772ffbfa36 Mon Sep 17 00:00:00 2001 From: taktik Date: Mon, 23 Feb 2026 20:38:04 +0100 Subject: [PATCH 2/2] Pass physical editor sizes to hosts on Windows --- plinth-plugin/src/editor.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plinth-plugin/src/editor.rs b/plinth-plugin/src/editor.rs index c81aa4e..408548a 100644 --- a/plinth-plugin/src/editor.rs +++ b/plinth-plugin/src/editor.rs @@ -9,12 +9,20 @@ pub trait Editor { fn open(&mut self, parent: RawWindowHandle); fn close(&mut self); - /// Get applied window scale + /// Get applied window scale. This should be the total applied scale: os window scale * custom window scales, if any fn scale(&self) -> f64 { 1.0 } /// Returns current window size fn window_size(&self) -> (f64, f64) { - Self::DEFAULT_SIZE + if cfg!(target_os = "windows") { + // on windows, window sizes are physical sizes + let (width, height) = Self::DEFAULT_SIZE; + let scale = self.scale(); + (width * scale, height * scale) + } + else { + Self::DEFAULT_SIZE + } } fn can_resize(&self) -> bool {