From 5cb7714caf2e3e8d535fcccf36f95dee2d6cf821 Mon Sep 17 00:00:00 2001 From: Osspial Date: Sat, 1 Jun 2019 15:42:56 -0400 Subject: [PATCH 1/7] Modify DPI API publicly and on Windows --- examples/window.rs | 1 + src/dpi.rs | 69 ++++++--- src/platform_impl/windows/event_loop.rs | 53 +------ src/platform_impl/windows/monitor.rs | 8 - src/platform_impl/windows/util.rs | 6 +- src/platform_impl/windows/window.rs | 175 +++++++--------------- src/platform_impl/windows/window_state.rs | 6 +- src/window.rs | 53 ++++--- 8 files changed, 131 insertions(+), 240 deletions(-) diff --git a/examples/window.rs b/examples/window.rs index 34f36793c6..437289520f 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -8,6 +8,7 @@ fn main() { let window = WindowBuilder::new() .with_title("A fantastic window!") + .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .build(&event_loop) .unwrap(); diff --git a/src/dpi.rs b/src/dpi.rs index afd1cbfd0b..3f4d2076f1 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -236,7 +236,7 @@ impl LogicalSize { assert!(validate_hidpi_factor(dpi_factor)); let width = self.width * dpi_factor; let height = self.height * dpi_factor; - PhysicalSize::new(width, height) + PhysicalSize::new(width.round() as _, height.round() as _) } } @@ -270,20 +270,16 @@ impl Into<(u32, u32)> for LogicalSize { } /// A size represented in physical pixels. -/// -/// The size is stored as floats, so please be careful. Casting floats to integers truncates the fractional part, -/// which can cause noticable issues. To help with that, an `Into<(u32, u32)>` implementation is provided which -/// does the rounding for you. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct PhysicalSize { - pub width: f64, - pub height: f64, + pub width: u32, + pub height: u32, } impl PhysicalSize { #[inline] - pub fn new(width: f64, height: f64) -> Self { + pub fn new(width: u32, height: u32) -> Self { PhysicalSize { width, height } } @@ -295,37 +291,64 @@ impl PhysicalSize { #[inline] pub fn to_logical(&self, dpi_factor: f64) -> LogicalSize { assert!(validate_hidpi_factor(dpi_factor)); - let width = self.width / dpi_factor; - let height = self.height / dpi_factor; + let width = self.width as f64 / dpi_factor; + let height = self.height as f64 / dpi_factor; LogicalSize::new(width, height) } } -impl From<(f64, f64)> for PhysicalSize { +impl From<(u32, u32)> for PhysicalSize { #[inline] - fn from((width, height): (f64, f64)) -> Self { + fn from((width, height): (u32, u32)) -> Self { Self::new(width, height) } } -impl From<(u32, u32)> for PhysicalSize { +impl Into<(u32, u32)> for PhysicalSize { + /// Note that this rounds instead of truncating. #[inline] - fn from((width, height): (u32, u32)) -> Self { - Self::new(width as f64, height as f64) + fn into(self) -> (u32, u32) { + (self.width, self.height) } } -impl Into<(f64, f64)> for PhysicalSize { +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Size { + Physical(PhysicalSize), + Logical(LogicalSize), +} + +impl Size { + pub fn new>(size: S) -> Size { + size.into() + } + + pub fn to_logical(&self, dpi_factor: f64) -> LogicalSize { + match *self { + Size::Physical(size) => size.to_logical(dpi_factor), + Size::Logical(size) => size, + } + } + + pub fn to_physical(&self, dpi_factor: f64) -> PhysicalSize { + match *self { + Size::Physical(size) => size, + Size::Logical(size) => size.to_physical(dpi_factor), + } + } +} + +impl From for Size { #[inline] - fn into(self) -> (f64, f64) { - (self.width, self.height) + fn from(size: PhysicalSize) -> Size { + Size::Physical(size) } } -impl Into<(u32, u32)> for PhysicalSize { - /// Note that this rounds instead of truncating. +impl From for Size { #[inline] - fn into(self) -> (u32, u32) { - (self.width.round() as _, self.height.round() as _) + fn from(size: LogicalSize) -> Size { + Size::Logical(size) } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4c399386ae..1078aaf82e 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -26,9 +26,7 @@ use std::collections::VecDeque; use std::marker::PhantomData; use parking_lot::Mutex; -use winapi::ctypes::c_int; use winapi::shared::minwindef::{ - BOOL, DWORD, HIWORD, INT, @@ -41,11 +39,11 @@ use winapi::shared::minwindef::{ use winapi::shared::windef::{HWND, POINT, RECT}; use winapi::shared::{windowsx, winerror}; use winapi::um::{winuser, winbase, ole2, processthreadsapi, commctrl, libloaderapi}; -use winapi::um::winnt::{LONG, LPCSTR, SHORT}; +use winapi::um::winnt::{LPCSTR, SHORT}; use crate::window::WindowId as RootWindowId; use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; -use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize}; +use crate::dpi::{LogicalPosition, LogicalSize}; use crate::event::{DeviceEvent, Touch, TouchPhase, StartCause, KeyboardInput, Event, WindowEvent}; use crate::platform_impl::platform::{event, WindowId, DEVICE_ID, wrap_device_id, util}; use crate::platform_impl::platform::dpi::{ @@ -643,13 +641,6 @@ lazy_static! { winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as LPCSTR) } }; - // Message sent by a `Window` after creation if it has a DPI != 96. - // WPARAM is the the DPI (u32). LOWORD of LPARAM is width, and HIWORD is height. - pub static ref INITIAL_DPI_MSG_ID: u32 = { - unsafe { - winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR) - } - }; // Message sent by a `Window` if it's requesting a redraw without sending a NewEvents. pub static ref REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID: u32 = { unsafe { @@ -1472,46 +1463,6 @@ unsafe extern "system" fn public_window_callback( let mut window_state = subclass_input.window_state.lock(); window_state.set_window_flags_in_place(|f| f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0)); 0 - } else if msg == *INITIAL_DPI_MSG_ID { - use crate::event::WindowEvent::HiDpiFactorChanged; - let scale_factor = dpi_to_scale_factor(wparam as u32); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: HiDpiFactorChanged(scale_factor), - }); - // Automatically resize for actual DPI - let width = LOWORD(lparam as DWORD) as u32; - let height = HIWORD(lparam as DWORD) as u32; - let (adjusted_width, adjusted_height): (u32, u32) = PhysicalSize::from_logical( - (width, height), - scale_factor, - ).into(); - // We're not done yet! `SetWindowPos` needs the window size, not the client area size. - let mut rect = RECT { - top: 0, - left: 0, - bottom: adjusted_height as LONG, - right: adjusted_width as LONG, - }; - let dw_style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD; - let b_menu = !winuser::GetMenu(window).is_null() as BOOL; - let dw_style_ex = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD; - winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex); - let outer_x = (rect.right - rect.left).abs() as c_int; - let outer_y = (rect.top - rect.bottom).abs() as c_int; - winuser::SetWindowPos( - window, - ptr::null_mut(), - 0, - 0, - outer_x, - outer_y, - winuser::SWP_NOMOVE - | winuser::SWP_NOREPOSITION - | winuser::SWP_NOZORDER - | winuser::SWP_NOACTIVATE, - ); - 0 } else { commctrl::DefSubclassProc(window, msg, wparam, lparam) } diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index ec01b74c8b..0cc0342933 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -138,14 +138,6 @@ impl MonitorHandle { } } - pub(crate) fn contains_point(&self, point: &POINT) -> bool { - let left = self.position.0 as LONG; - let right = left + self.dimensions.0 as LONG; - let top = self.position.1 as LONG; - let bottom = top + self.dimensions.1 as LONG; - point.x >= left && point.x <= right && point.y >= top && point.y <= bottom - } - #[inline] pub fn name(&self) -> Option { Some(self.monitor_name.clone()) diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index a8f30e22ab..952dc9f366 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -5,7 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use crate::window::CursorIcon; use winapi::ctypes::wchar_t; use winapi::shared::minwindef::{BOOL, DWORD}; -use winapi::shared::windef::{HWND, POINT, RECT}; +use winapi::shared::windef::{HWND, RECT}; use winapi::um::winbase::lstrlenW; use winapi::um::winuser; @@ -43,10 +43,6 @@ fn win_to_err BOOL>(f: F) -> Result<(), io::Error> { } } -pub fn get_cursor_pos() -> Option { - unsafe { status_map(|cursor_pos| winuser::GetCursorPos(cursor_pos)) } -} - pub fn get_window_rect(hwnd: HWND) -> Option { unsafe { status_map(|rect| winuser::GetWindowRect(hwnd, rect)) } } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index b16dd6f187..b733496679 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -9,7 +9,7 @@ use std::sync::mpsc::channel; use parking_lot::Mutex; use winapi::ctypes::c_int; -use winapi::shared::minwindef::{DWORD, LPARAM, UINT, WORD, WPARAM}; +use winapi::shared::minwindef::{DWORD, UINT}; use winapi::shared::windef::{HWND, POINT, RECT}; use winapi::um::{combaseapi, dwmapi, libloaderapi, ole2, winuser}; use winapi::um::objbase::COINIT_APARTMENTTHREADED; @@ -20,13 +20,13 @@ use winapi::um::winnt::{LONG, LPCWSTR}; use crate::window::{Icon, CursorIcon, WindowAttributes}; use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError}; -use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize}; +use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize, Size}; use crate::monitor::MonitorHandle as RootMonitorHandle; use crate::platform_impl::platform::{ {PlatformSpecificWindowBuilderAttributes, WindowId}, dpi::{dpi_to_scale_factor, hwnd_dpi}, drop_handler::FileDropHandler, - event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID, REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID}, + event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID, REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID}, icon::{self, IconType, WinIcon}, monitor, raw_input::register_all_mice_and_keyboards_for_raw_input, @@ -105,10 +105,17 @@ impl Window { #[inline] pub fn set_visible(&self, visible: bool) { - match visible { - true => unsafe { winuser::ShowWindow(self.window.0, winuser::SW_SHOW); }, - false => unsafe { winuser::ShowWindow(self.window.0, winuser::SW_HIDE); }, - } + let window = self.window.clone(); + let window_state = Arc::clone(&self.window_state); + + self.thread_executor.execute_in_thread(move || { + WindowState::set_window_flags( + window_state.lock(), + window.0, + None, + |f| f.set(WindowFlags::VISIBLE, visible), + ); + }); } #[inline] @@ -127,32 +134,20 @@ impl Window { } } - pub(crate) fn outer_position_physical(&self) -> (i32, i32) { + #[inline] + pub fn outer_position(&self) -> Result { util::get_window_rect(self.window.0) - .map(|rect| (rect.left as i32, rect.top as i32)) - .unwrap() + .map(|rect| Ok(PhysicalPosition::new(rect.left as f64, rect.top as f64))) + .expect("Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit") } #[inline] - pub fn outer_position(&self) -> Result { - let physical_position = self.outer_position_physical(); - let dpi_factor = self.hidpi_factor(); - Ok(LogicalPosition::from_physical(physical_position, dpi_factor)) - } - - pub(crate) fn inner_position_physical(&self) -> (i32, i32) { + pub fn inner_position(&self) -> Result { let mut position: POINT = unsafe { mem::zeroed() }; if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 { panic!("Unexpected ClientToScreen failure: please report this error to https://github.com/rust-windowing/winit") } - (position.x, position.y) - } - - #[inline] - pub fn inner_position(&self) -> Result { - let physical_position = self.inner_position_physical(); - let dpi_factor = self.hidpi_factor(); - Ok(LogicalPosition::from_physical(physical_position, dpi_factor)) + Ok(PhysicalPosition::new(position.x as f64, position.y as f64)) } pub(crate) fn set_position_physical(&self, x: i32, y: i32) { @@ -171,46 +166,45 @@ impl Window { } #[inline] - pub fn set_outer_position(&self, logical_position: LogicalPosition) { - let dpi_factor = self.hidpi_factor(); - let (x, y) = logical_position.to_physical(dpi_factor).into(); + pub fn set_outer_position(&self, position: PhysicalPosition) { + let (x, y): (i32, i32) = position.into(); self.set_position_physical(x, y); + unsafe { + winuser::SetWindowPos( + self.window.0, + ptr::null_mut(), + x as c_int, + y as c_int, + 0, + 0, + winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOSIZE, + ); + winuser::UpdateWindow(self.window.0); + } } - pub(crate) fn inner_size_physical(&self) -> (u32, u32) { + #[inline] + pub fn inner_size(&self) -> PhysicalSize { let mut rect: RECT = unsafe { mem::uninitialized() }; if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 { panic!("Unexpected GetClientRect failure: please report this error to https://github.com/rust-windowing/winit") } - ( + PhysicalSize::new( (rect.right - rect.left) as u32, (rect.bottom - rect.top) as u32, ) } #[inline] - pub fn inner_size(&self) -> LogicalSize { - let physical_size = self.inner_size_physical(); - let dpi_factor = self.hidpi_factor(); - LogicalSize::from_physical(physical_size, dpi_factor) - } - - pub(crate) fn outer_size_physical(&self) -> (u32, u32) { + pub fn outer_size(&self) -> PhysicalSize { util::get_window_rect(self.window.0) - .map(|rect| ( + .map(|rect| PhysicalSize::new( (rect.right - rect.left) as u32, (rect.bottom - rect.top) as u32, )) .unwrap() } - #[inline] - pub fn outer_size(&self) -> LogicalSize { - let physical_size = self.outer_size_physical(); - let dpi_factor = self.hidpi_factor(); - LogicalSize::from_physical(physical_size, dpi_factor) - } - pub(crate) fn set_inner_size_physical(&self, x: u32, y: u32) { unsafe { let rect = util::adjust_window_rect( @@ -242,42 +236,26 @@ impl Window { } #[inline] - pub fn set_inner_size(&self, logical_size: LogicalSize) { + pub fn set_inner_size(&self, size: Size) { let dpi_factor = self.hidpi_factor(); - let (width, height) = logical_size.to_physical(dpi_factor).into(); - self.set_inner_size_physical(width, height); - } - - pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) { - self.window_state.lock().min_size = dimensions.map(Into::into); - // Make windows re-check the window size bounds. - let (width, height) = self.inner_size_physical(); + let (width, height) = size.to_physical(dpi_factor).into(); self.set_inner_size_physical(width, height); } #[inline] - pub fn set_min_inner_size(&self, logical_size: Option) { - let physical_size = logical_size.map(|logical_size| { - let dpi_factor = self.hidpi_factor(); - logical_size.to_physical(dpi_factor).into() - }); - self.set_min_inner_size_physical(physical_size); - } - - pub fn set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>) { - self.window_state.lock().max_size = dimensions.map(Into::into); + pub fn set_min_inner_size(&self, size: Option) { + self.window_state.lock().min_size = size; // Make windows re-check the window size bounds. - let (width, height) = self.inner_size_physical(); - self.set_inner_size_physical(width, height); + let size = self.inner_size(); + self.set_inner_size(size.into()); } #[inline] - pub fn set_max_inner_size(&self, logical_size: Option) { - let physical_size = logical_size.map(|logical_size| { - let dpi_factor = self.hidpi_factor(); - logical_size.to_physical(dpi_factor).into() - }); - self.set_max_inner_size_physical(physical_size); + pub fn set_max_inner_size(&self, size: Option) { + self.window_state.lock().max_size = size; + // Make windows re-check the window size bounds. + let size = self.inner_size(); + self.set_inner_size(size.into()); } #[inline] @@ -596,38 +574,6 @@ unsafe fn init( // registering the window class let class_name = register_window_class(&window_icon, &taskbar_icon); - let guessed_dpi_factor = { - let monitors = monitor::available_monitors(); - let dpi_factor = if !monitors.is_empty() { - let mut dpi_factor = Some(monitors[0].hidpi_factor()); - for monitor in &monitors { - if Some(monitor.hidpi_factor()) != dpi_factor { - dpi_factor = None; - } - } - dpi_factor - } else { - return Err(os_error!(io::Error::new(io::ErrorKind::NotFound, "No monitors were detected."))); - }; - dpi_factor.unwrap_or_else(|| { - util::get_cursor_pos() - .and_then(|cursor_pos| { - let mut dpi_factor = None; - for monitor in &monitors { - if monitor.contains_point(&cursor_pos) { - dpi_factor = Some(monitor.hidpi_factor()); - break; - } - } - dpi_factor - }) - .unwrap_or(1.0) - }) - }; - info!("Guessed window DPI factor: {}", guessed_dpi_factor); - - let dimensions = attributes.inner_size.unwrap_or_else(|| (1024, 768).into()); - let mut window_flags = WindowFlags::empty(); window_flags.set(WindowFlags::DECORATIONS, attributes.decorations); window_flags.set(WindowFlags::ALWAYS_ON_TOP, attributes.always_on_top); @@ -674,20 +620,6 @@ unsafe fn init( let dpi = hwnd_dpi(real_window.0); let dpi_factor = dpi_to_scale_factor(dpi); - if dpi_factor != guessed_dpi_factor { - let (width, height): (u32, u32) = dimensions.into(); - let mut packed_dimensions = 0; - // MAKELPARAM isn't provided by winapi yet. - let ptr = &mut packed_dimensions as *mut LPARAM as *mut WORD; - *ptr.offset(0) = width as WORD; - *ptr.offset(1) = height as WORD; - winuser::PostMessageW( - real_window.0, - *INITIAL_DPI_MSG_ID, - dpi as WPARAM, - packed_dimensions, - ); - } // making the window transparent if attributes.transparent && !pl_attribs.no_redirection_bitmap { @@ -716,7 +648,6 @@ unsafe fn init( } } - window_flags.set(WindowFlags::VISIBLE, attributes.visible); window_flags.set(WindowFlags::MAXIMIZED, attributes.maximized); let window_state = { @@ -747,9 +678,9 @@ unsafe fn init( force_window_active(win.window.0); } - if let Some(dimensions) = attributes.inner_size { - win.set_inner_size(dimensions); - } + let dimensions = attributes.inner_size.unwrap_or_else(|| PhysicalSize::new(1024, 768).into()); + win.set_inner_size(dimensions); + win.set_visible(attributes.visible); Ok(win) } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 290c6eb3b3..4d1d81f0c0 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -2,7 +2,7 @@ use crate::monitor::MonitorHandle; use crate::window::{CursorIcon, WindowAttributes}; use std::{io, ptr}; use parking_lot::MutexGuard; -use crate::dpi::LogicalSize; +use crate::dpi::Size; use crate::platform_impl::platform::{util, event_loop}; use crate::platform_impl::platform::icon::WinIcon; use winapi::shared::windef::{RECT, HWND}; @@ -15,8 +15,8 @@ pub struct WindowState { pub mouse: MouseProperties, /// Used by `WM_GETMINMAXINFO`. - pub min_size: Option, - pub max_size: Option, + pub min_size: Option, + pub max_size: Option, pub window_icon: Option, pub taskbar_icon: Option, diff --git a/src/window.rs b/src/window.rs index c45c09118a..b05aeb8f11 100644 --- a/src/window.rs +++ b/src/window.rs @@ -5,7 +5,7 @@ use crate::platform_impl; use crate::error::{ExternalError, NotSupportedError, OsError}; use crate::event_loop::EventLoopWindowTarget; use crate::monitor::{AvailableMonitorsIter, MonitorHandle}; -use crate::dpi::{LogicalPosition, LogicalSize}; +use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Size}; pub use crate::icon::*; @@ -85,17 +85,17 @@ pub struct WindowAttributes { /// used. /// /// The default is `None`. - pub inner_size: Option, + pub inner_size: Option, /// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved). /// /// The default is `None`. - pub min_inner_size: Option, + pub min_inner_size: Option, /// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform. /// /// The default is `None`. - pub max_inner_size: Option, + pub max_inner_size: Option, /// Whether the window is resizable or not. /// @@ -175,22 +175,22 @@ impl WindowBuilder { /// Requests the window to be of specific dimensions. #[inline] - pub fn with_inner_size(mut self, size: LogicalSize) -> WindowBuilder { - self.window.inner_size = Some(size); + pub fn with_inner_size>(mut self, size: S) -> WindowBuilder { + self.window.inner_size = Some(size.into()); self } /// Sets a minimum dimension size for the window #[inline] - pub fn with_min_inner_size(mut self, min_size: LogicalSize) -> WindowBuilder { - self.window.min_inner_size = Some(min_size); + pub fn with_min_inner_size>(mut self, min_size: S) -> WindowBuilder { + self.window.min_inner_size = Some(min_size.into()); self } /// Sets a maximum dimension size for the window #[inline] - pub fn with_max_inner_size(mut self, max_size: LogicalSize) -> WindowBuilder { - self.window.max_inner_size = Some(max_size); + pub fn with_max_inner_size>(mut self, max_size: S) -> WindowBuilder { + self.window.max_inner_size = Some(max_size.into()); self } @@ -286,11 +286,10 @@ impl WindowBuilder { pub fn build(mut self, window_target: &EventLoopWindowTarget) -> Result { self.window.inner_size = Some(self.window.inner_size.unwrap_or_else(|| { if let Some(ref monitor) = self.window.fullscreen { - // resizing the window to the dimensions of the monitor when fullscreen - LogicalSize::from_physical(monitor.size(), monitor.hidpi_factor()) // DPI factor applies here since this is a borderless window and not real fullscreen + Size::new(monitor.size()) } else { // default dimensions - (1024, 768).into() + Size::new(LogicalSize::new(1024.0, 768.0)) } })); @@ -379,7 +378,7 @@ impl Window { /// /// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc #[inline] - pub fn inner_position(&self) -> Result { + pub fn inner_position(&self) -> Result { self.window.inner_position() } @@ -398,7 +397,7 @@ impl Window { /// - **iOS:** Can only be called on the main thread. Returns the top left coordinates of the /// window in the screen space coordinate system. #[inline] - pub fn outer_position(&self) -> Result { + pub fn outer_position(&self) -> Result { self.window.outer_position() } @@ -413,7 +412,7 @@ impl Window { /// - **iOS:** Can only be called on the main thread. Sets the top left coordinates of the /// window in the screen space coordinate system. #[inline] - pub fn set_outer_position(&self, position: LogicalPosition) { + pub fn set_outer_position(&self, position: PhysicalPosition) { self.window.set_outer_position(position) } @@ -421,16 +420,14 @@ impl Window { /// /// The client area is the content of the window, excluding the title bar and borders. /// - /// Converting the returned `LogicalSize` to `PhysicalSize` produces the size your framebuffer should be. - /// /// ## Platform-specific /// - /// - **iOS:** Can only be called on the main thread. Returns the `LogicalSize` of the window's + /// - **iOS:** Can only be called on the main thread. Returns the `PhysicalSize` of the window's /// [safe area] in screen space coordinates. /// /// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc #[inline] - pub fn inner_size(&self) -> LogicalSize { + pub fn inner_size(&self) -> PhysicalSize { self.window.inner_size() } @@ -443,8 +440,8 @@ impl Window { /// - **iOS:** Unimplemented. Currently this panics, as it's not clear what `set_inner_size` /// would mean for iOS. #[inline] - pub fn set_inner_size(&self, size: LogicalSize) { - self.window.set_inner_size(size) + pub fn set_inner_size>(&self, size: S) { + self.window.set_inner_size(size.into()) } /// Returns the logical size of the entire window. @@ -454,10 +451,10 @@ impl Window { /// /// ## Platform-specific /// - /// - **iOS:** Can only be called on the main thread. Returns the `LogicalSize` of the window in + /// - **iOS:** Can only be called on the main thread. Returns the `PhysicalSize` of the window in /// screen space coordinates. #[inline] - pub fn outer_size(&self) -> LogicalSize { + pub fn outer_size(&self) -> PhysicalSize { self.window.outer_size() } @@ -467,8 +464,8 @@ impl Window { /// /// - **iOS:** Has no effect. #[inline] - pub fn set_min_inner_size(&self, dimensions: Option) { - self.window.set_min_inner_size(dimensions) + pub fn set_min_inner_size>(&self, min_size: Option) { + self.window.set_min_inner_size(min_size.map(|s| s.into())) } /// Sets a maximum dimension size for the window. @@ -477,8 +474,8 @@ impl Window { /// /// - **iOS:** Has no effect. #[inline] - pub fn set_max_inner_size(&self, dimensions: Option) { - self.window.set_max_inner_size(dimensions) + pub fn set_max_inner_size>(&self, max_size: Option) { + self.window.set_max_inner_size(max_size.map(|s| s.into())) } } From 95aca874b86cc33898a2beffd8976b443af132da Mon Sep 17 00:00:00 2001 From: Osspial Date: Sun, 2 Jun 2019 21:35:36 -0400 Subject: [PATCH 2/7] Add generic Position and make dpi creation functions const --- src/dpi.rs | 50 ++++++++++++++++++++++++++--- src/platform_impl/windows/window.rs | 37 ++++++--------------- src/window.rs | 14 ++++---- 3 files changed, 62 insertions(+), 39 deletions(-) diff --git a/src/dpi.rs b/src/dpi.rs index 3f4d2076f1..5eddbbb517 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -100,7 +100,7 @@ pub struct LogicalPosition { impl LogicalPosition { #[inline] - pub fn new(x: f64, y: f64) -> Self { + pub const fn new(x: f64, y: f64) -> Self { LogicalPosition { x, y } } @@ -161,7 +161,7 @@ pub struct PhysicalPosition { impl PhysicalPosition { #[inline] - pub fn new(x: f64, y: f64) -> Self { + pub const fn new(x: f64, y: f64) -> Self { PhysicalPosition { x, y } } @@ -222,7 +222,7 @@ pub struct LogicalSize { impl LogicalSize { #[inline] - pub fn new(width: f64, height: f64) -> Self { + pub const fn new(width: f64, height: f64) -> Self { LogicalSize { width, height } } @@ -279,7 +279,7 @@ pub struct PhysicalSize { impl PhysicalSize { #[inline] - pub fn new(width: u32, height: u32) -> Self { + pub const fn new(width: u32, height: u32) -> Self { PhysicalSize { width, height } } @@ -352,3 +352,45 @@ impl From for Size { Size::Logical(size) } } + + +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Position { + Physical(PhysicalPosition), + Logical(LogicalPosition), +} + +impl Position { + pub fn new>(position: S) -> Position { + position.into() + } + + pub fn to_logical(&self, dpi_factor: f64) -> LogicalPosition { + match *self { + Position::Physical(position) => position.to_logical(dpi_factor), + Position::Logical(position) => position, + } + } + + pub fn to_physical(&self, dpi_factor: f64) -> PhysicalPosition { + match *self { + Position::Physical(position) => position, + Position::Logical(position) => position.to_physical(dpi_factor), + } + } +} + +impl From for Position { + #[inline] + fn from(position: PhysicalPosition) -> Position { + Position::Physical(position) + } +} + +impl From for Position { + #[inline] + fn from(position: LogicalPosition) -> Position { + Position::Logical(position) + } +} diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index b733496679..bbe403af2b 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -20,7 +20,7 @@ use winapi::um::winnt::{LONG, LPCWSTR}; use crate::window::{Icon, CursorIcon, WindowAttributes}; use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError}; -use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize, Size}; +use crate::dpi::{PhysicalPosition, PhysicalSize, Size, Position}; use crate::monitor::MonitorHandle as RootMonitorHandle; use crate::platform_impl::platform::{ {PlatformSpecificWindowBuilderAttributes, WindowId}, @@ -150,25 +150,9 @@ impl Window { Ok(PhysicalPosition::new(position.x as f64, position.y as f64)) } - pub(crate) fn set_position_physical(&self, x: i32, y: i32) { - unsafe { - winuser::SetWindowPos( - self.window.0, - ptr::null_mut(), - x as c_int, - y as c_int, - 0, - 0, - winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOSIZE, - ); - winuser::UpdateWindow(self.window.0); - } - } - #[inline] - pub fn set_outer_position(&self, position: PhysicalPosition) { - let (x, y): (i32, i32) = position.into(); - self.set_position_physical(x, y); + pub fn set_outer_position(&self, position: Position) { + let (x, y): (i32, i32) = position.to_physical(self.hidpi_factor()).into(); unsafe { winuser::SetWindowPos( self.window.0, @@ -326,7 +310,11 @@ impl Window { self.window_state.lock().dpi_factor } - fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> { + #[inline] + pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { + let dpi_factor = self.hidpi_factor(); + let (x, y) = position.to_physical(dpi_factor).into(); + let mut point = POINT { x, y }; unsafe { if winuser::ClientToScreen(self.window.0, &mut point) == 0 { @@ -339,13 +327,6 @@ impl Window { Ok(()) } - #[inline] - pub fn set_cursor_position(&self, logical_position: LogicalPosition) -> Result<(), ExternalError> { - let dpi_factor = self.hidpi_factor(); - let (x, y) = logical_position.to_physical(dpi_factor).into(); - self.set_cursor_position_physical(x, y) - } - #[inline] pub fn id(&self) -> WindowId { WindowId(self.window.0) @@ -496,7 +477,7 @@ impl Window { } #[inline] - pub fn set_ime_position(&self, _logical_spot: LogicalPosition) { + pub fn set_ime_position(&self, _position: Position) { unimplemented!(); } } diff --git a/src/window.rs b/src/window.rs index b05aeb8f11..8dfbbd239b 100644 --- a/src/window.rs +++ b/src/window.rs @@ -5,7 +5,7 @@ use crate::platform_impl; use crate::error::{ExternalError, NotSupportedError, OsError}; use crate::event_loop::EventLoopWindowTarget; use crate::monitor::{AvailableMonitorsIter, MonitorHandle}; -use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Size}; +use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Size, Position}; pub use crate::icon::*; @@ -412,8 +412,8 @@ impl Window { /// - **iOS:** Can only be called on the main thread. Sets the top left coordinates of the /// window in the screen space coordinate system. #[inline] - pub fn set_outer_position(&self, position: PhysicalPosition) { - self.window.set_outer_position(position) + pub fn set_outer_position>(&self, position: P) { + self.window.set_outer_position(position.into()) } /// Returns the logical size of the window's client area. @@ -594,8 +594,8 @@ impl Window { /// /// **iOS:** Has no effect. #[inline] - pub fn set_ime_position(&self, position: LogicalPosition) { - self.window.set_ime_position(position) + pub fn set_ime_position>(&self, position: P) { + self.window.set_ime_position(position.into()) } } @@ -618,8 +618,8 @@ impl Window { /// /// - **iOS:** Always returns an `Err`. #[inline] - pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), ExternalError> { - self.window.set_cursor_position(position) + pub fn set_cursor_position>(&self, position: P) -> Result<(), ExternalError> { + self.window.set_cursor_position(position.into()) } /// Grabs the cursor, preventing it from leaving the window. From b98a7801cedfb62e64925f6674457c780ab219cb Mon Sep 17 00:00:00 2001 From: Osspial Date: Sun, 2 Jun 2019 21:36:13 -0400 Subject: [PATCH 3/7] Make examples work --- examples/multithreaded.rs | 19 ++++++++++--------- examples/resizable.rs | 3 ++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 054087994e..60b82859e2 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -4,12 +4,13 @@ extern crate winit; use std::{collections::HashMap, sync::mpsc, thread, time::Duration}; use winit::{ + dpi::{PhysicalPosition, PhysicalSize}, event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, WindowBuilder}, }; const WINDOW_COUNT: usize = 3; -const WINDOW_SIZE: (u32, u32) = (600, 400); +const WINDOW_SIZE: PhysicalSize = PhysicalSize::new(600, 400); fn main() { env_logger::init(); @@ -17,7 +18,7 @@ fn main() { let mut window_senders = HashMap::with_capacity(WINDOW_COUNT); for _ in 0..WINDOW_COUNT { let window = WindowBuilder::new() - .with_inner_size(WINDOW_SIZE.into()) + .with_inner_size(WINDOW_SIZE) .build(&event_loop) .unwrap(); let (tx, rx) = mpsc::channel(); @@ -55,7 +56,7 @@ fn main() { println!("-> inner_size : {:?}", window.inner_size()); }, L => window.set_min_inner_size(match state { - true => Some(WINDOW_SIZE.into()), + true => Some(WINDOW_SIZE), false => None, }), M => window.set_maximized(state), @@ -69,13 +70,13 @@ fn main() { Q => window.request_redraw(), R => window.set_resizable(state), S => window.set_inner_size(match state { - true => (WINDOW_SIZE.0 + 100, WINDOW_SIZE.1 + 100), + true => PhysicalSize::new(WINDOW_SIZE.width + 100, WINDOW_SIZE.height + 100), false => WINDOW_SIZE, - }.into()), - W => window.set_cursor_position(( - WINDOW_SIZE.0 as i32 / 2, - WINDOW_SIZE.1 as i32 / 2, - ).into()).unwrap(), + }), + W => window.set_cursor_position(PhysicalPosition::new( + WINDOW_SIZE.width as f64 / 2.0, + WINDOW_SIZE.height as f64 / 2.0, + )).unwrap(), Z => { window.set_visible(false); thread::sleep(Duration::from_secs(1)); diff --git a/examples/resizable.rs b/examples/resizable.rs index 969ce04b3a..f05a21e3b9 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,5 +1,6 @@ extern crate winit; use winit::window::WindowBuilder; +use winit::dpi::LogicalSize; use winit::event::{Event, WindowEvent, VirtualKeyCode, ElementState, KeyboardInput}; use winit::event_loop::{EventLoop, ControlFlow}; @@ -10,7 +11,7 @@ fn main() { let window = WindowBuilder::new() .with_title("Hit space to toggle resizability.") - .with_inner_size((400, 200).into()) + .with_inner_size(LogicalSize::new(400.0, 200.0)) .with_resizable(resizable) .build(&event_loop) .unwrap(); From 61f43719067058c70dee4ef4ba5ed182c6dedaec Mon Sep 17 00:00:00 2001 From: Osspial Date: Tue, 11 Jun 2019 14:31:05 -0400 Subject: [PATCH 4/7] Fix fullscreen windows not appearing --- src/platform_impl/windows/window.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index bbe403af2b..d381624f8b 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -654,15 +654,15 @@ unsafe fn init( thread_executor: event_loop.create_thread_executor(), }; + let dimensions = attributes.inner_size.unwrap_or_else(|| PhysicalSize::new(1024, 768).into()); + win.set_inner_size(dimensions); + win.set_visible(attributes.visible); + if let Some(_) = attributes.fullscreen { win.set_fullscreen(attributes.fullscreen); force_window_active(win.window.0); } - let dimensions = attributes.inner_size.unwrap_or_else(|| PhysicalSize::new(1024, 768).into()); - win.set_inner_size(dimensions); - win.set_visible(attributes.visible); - Ok(win) } From 1898f66bc7f7869230a47b644fd905f8862a50c8 Mon Sep 17 00:00:00 2001 From: Osspial Date: Thu, 13 Jun 2019 16:17:33 -0400 Subject: [PATCH 5/7] Replace Logical coordinates in window events with Physical coordinates --- src/event.rs | 10 +++++----- src/platform_impl/windows/dpi.rs | 4 ---- src/platform_impl/windows/event_loop.rs | 22 ++++++++-------------- src/platform_impl/windows/monitor.rs | 1 - 4 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/event.rs b/src/event.rs index e8db64a0a3..35d04e4c46 100644 --- a/src/event.rs +++ b/src/event.rs @@ -7,7 +7,7 @@ use std::time::Instant; use std::path::PathBuf; -use crate::dpi::{LogicalPosition, LogicalSize}; +use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::window::WindowId; use crate::platform_impl; @@ -87,10 +87,10 @@ pub enum StartCause { #[derive(Clone, Debug, PartialEq)] pub enum WindowEvent { /// The size of the window has changed. Contains the client area's new dimensions. - Resized(LogicalSize), + Resized(PhysicalSize), /// The position of the window has changed. Contains the window's new position. - Moved(LogicalPosition), + Moved(PhysicalPosition), /// The window has been requested to close. CloseRequested, @@ -134,7 +134,7 @@ pub enum WindowEvent { /// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is /// limited by the display area and it may have been transformed by the OS to implement effects such as cursor /// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control. - position: LogicalPosition, + position: PhysicalPosition, modifiers: ModifiersState }, @@ -291,7 +291,7 @@ pub enum TouchPhase { pub struct Touch { pub device_id: DeviceId, pub phase: TouchPhase, - pub location: LogicalPosition, + pub location: PhysicalPosition, /// unique identifier of a finger. pub id: u64 } diff --git a/src/platform_impl/windows/dpi.rs b/src/platform_impl/windows/dpi.rs index 4bf31f3922..a1266ce473 100644 --- a/src/platform_impl/windows/dpi.rs +++ b/src/platform_impl/windows/dpi.rs @@ -183,7 +183,3 @@ pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 { } } } - -pub fn hwnd_scale_factor(hwnd: HWND) -> f64 { - dpi_to_scale_factor(unsafe { hwnd_dpi(hwnd) }) -} diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 1078aaf82e..b924fb954d 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -43,14 +43,13 @@ use winapi::um::winnt::{LPCSTR, SHORT}; use crate::window::WindowId as RootWindowId; use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; -use crate::dpi::{LogicalPosition, LogicalSize}; +use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; use crate::event::{DeviceEvent, Touch, TouchPhase, StartCause, KeyboardInput, Event, WindowEvent}; use crate::platform_impl::platform::{event, WindowId, DEVICE_ID, wrap_device_id, util}; use crate::platform_impl::platform::dpi::{ become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, - hwnd_scale_factor, }; use crate::platform_impl::platform::drop_handler::FileDropHandler; use crate::platform_impl::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey}; @@ -876,14 +875,12 @@ unsafe extern "system" fn public_window_callback( let windowpos = lparam as *const winuser::WINDOWPOS; if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE { - let dpi_factor = hwnd_scale_factor(window); - let logical_position = LogicalPosition::from_physical( - ((*windowpos).x, (*windowpos).y), - dpi_factor, + let physical_position = PhysicalPosition::new( + (*windowpos).x as f64, (*windowpos).y as f64, ); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: Moved(logical_position), + event: Moved(physical_position), }); } @@ -896,11 +893,10 @@ unsafe extern "system" fn public_window_callback( let w = LOWORD(lparam as DWORD) as u32; let h = HIWORD(lparam as DWORD) as u32; - let dpi_factor = hwnd_scale_factor(window); - let logical_size = LogicalSize::from_physical((w, h), dpi_factor); + let physical_size = PhysicalSize::new(w, h); let event = Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: Resized(logical_size), + event: Resized(physical_size), }; { @@ -961,8 +957,7 @@ unsafe extern "system" fn public_window_callback( let x = windowsx::GET_X_LPARAM(lparam) as f64; let y = windowsx::GET_Y_LPARAM(lparam) as f64; - let dpi_factor = hwnd_scale_factor(window); - let position = LogicalPosition::from_physical((x, y), dpi_factor); + let position = PhysicalPosition::new(x, y); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1312,11 +1307,10 @@ unsafe extern "system" fn public_window_callback( inputs.as_mut_ptr(), mem::size_of::() as INT, ) > 0 { - let dpi_factor = hwnd_scale_factor(window); for input in &inputs { let x = (input.x as f64) / 100f64; let y = (input.y as f64) / 100f64; - let location = LogicalPosition::from_physical((x, y), dpi_factor); + let location = PhysicalPosition::new(x, y); subclass_input.send_event( Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::Touch(Touch { diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 0cc0342933..6920129786 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -1,6 +1,5 @@ use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, TRUE, WORD}; use winapi::shared::windef::{HDC, HMONITOR, HWND, LPRECT, POINT}; -use winapi::um::winnt::LONG; use winapi::um::{wingdi, winuser}; use std::collections::{HashSet, VecDeque}; From bb647e8a57a4275d093e48232344d66d4e404dd2 Mon Sep 17 00:00:00 2001 From: Osspial Date: Thu, 13 Jun 2019 18:09:55 -0400 Subject: [PATCH 6/7] Update HiDpiFactorChanged --- examples/multithreaded.rs | 4 +- src/event.rs | 65 ++++++++++-- src/event_loop.rs | 2 +- src/platform/desktop.rs | 4 +- src/platform_impl/windows/drop_handler.rs | 6 +- src/platform_impl/windows/event_loop.rs | 122 +++++++++++++++------- 6 files changed, 152 insertions(+), 51 deletions(-) diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 60b82859e2..3d738ddf66 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -106,7 +106,9 @@ fn main() { window_senders.remove(&window_id); }, _ => if let Some(tx) = window_senders.get(&window_id) { - tx.send(event).unwrap(); + if let Some(event) = event.to_static() { + tx.send(event).unwrap(); + } }, } } diff --git a/src/event.rs b/src/event.rs index 35d04e4c46..021e4912ee 100644 --- a/src/event.rs +++ b/src/event.rs @@ -12,12 +12,12 @@ use crate::window::WindowId; use crate::platform_impl; /// Describes a generic event. -#[derive(Clone, Debug, PartialEq)] -pub enum Event { +#[derive(Debug, PartialEq)] +pub enum Event<'a, T: 'static> { /// Emitted when the OS sends an event to a winit window. WindowEvent { window_id: WindowId, - event: WindowEvent, + event: WindowEvent<'a>, }, /// Emitted when the OS sends an event to a device. DeviceEvent { @@ -42,8 +42,8 @@ pub enum Event { Suspended(bool), } -impl Event { - pub fn map_nonuser_event(self) -> Result, Event> { +impl<'a, T> Event<'a, T> { + pub fn map_nonuser_event(self) -> Result, Event<'a, T>> { use self::Event::*; match self { UserEvent(_) => Err(self), @@ -55,6 +55,19 @@ impl Event { Suspended(suspended) => Ok(Suspended(suspended)), } } + + pub fn to_static(self) -> Option> { + use self::Event::*; + match self { + WindowEvent{window_id, event} => event.to_static().map(|event| WindowEvent{window_id, event}), + UserEvent(e) => Some(UserEvent(e)), + DeviceEvent{device_id, event} => Some(DeviceEvent{device_id, event}), + NewEvents(cause) => Some(NewEvents(cause)), + EventsCleared => Some(EventsCleared), + LoopDestroyed => Some(LoopDestroyed), + Suspended(suspended) => Some(Suspended(suspended)), + } + } } /// Describes the reason the event loop is resuming. @@ -84,8 +97,8 @@ pub enum StartCause { } /// Describes an event from a `Window`. -#[derive(Clone, Debug, PartialEq)] -pub enum WindowEvent { +#[derive(Debug, PartialEq)] +pub enum WindowEvent<'a> { /// The size of the window has changed. Contains the client area's new dimensions. Resized(PhysicalSize), @@ -175,8 +188,44 @@ pub enum WindowEvent { /// * Changing the display's DPI factor (e.g. in Control Panel on Windows). /// * Moving the window to a display with a different DPI factor. /// + /// After this event callback has been processed, the window will be resized to whatever value + /// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested + /// by the OS, but it can be changed to any value. If `new_inner_size` is set to `None`, no resizing + /// will occur. + /// /// For more information about DPI in general, see the [`dpi`](dpi/index.html) module. - HiDpiFactorChanged(f64), + HiDpiFactorChanged { + hidpi_factor: f64, + new_inner_size: &'a mut Option + }, +} + +impl<'a> WindowEvent<'a> { + pub fn to_static(self) -> Option> { + use self::WindowEvent::*; + match self { + Resized(size) => Some(Resized(size)), + Moved(position) => Some(Moved(position)), + CloseRequested => Some(CloseRequested), + Destroyed => Some(Destroyed), + DroppedFile(file) => Some(DroppedFile(file)), + HoveredFile(file) => Some(HoveredFile(file)), + HoveredFileCancelled => Some(HoveredFileCancelled), + ReceivedCharacter(c) => Some(ReceivedCharacter(c)), + Focused(focused) => Some(Focused(focused)), + KeyboardInput { device_id, input } => Some(KeyboardInput { device_id, input }), + CursorMoved { device_id, position, modifiers } => Some(CursorMoved { device_id, position, modifiers }), + CursorEntered { device_id } => Some(CursorEntered { device_id }), + CursorLeft { device_id } => Some(CursorLeft { device_id }), + MouseWheel { device_id, delta, phase, modifiers } => Some(MouseWheel { device_id, delta, phase, modifiers }), + MouseInput { device_id, state, button, modifiers } => Some(MouseInput { device_id, state, button, modifiers }), + TouchpadPressure { device_id, pressure, stage } => Some(TouchpadPressure { device_id, pressure, stage }), + AxisMotion { device_id, axis, value } => Some(AxisMotion { device_id, axis, value }), + RedrawRequested => Some(RedrawRequested), + Touch(touch) => Some(Touch(touch)), + HiDpiFactorChanged{..} => None, + } + } } /// Identifier of an input device. diff --git a/src/event_loop.rs b/src/event_loop.rs index f45f07b5fd..234e5224cc 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -133,7 +133,7 @@ impl EventLoop { /// [`ControlFlow`]: ./enum.ControlFlow.html #[inline] pub fn run(self, event_handler: F) -> ! - where F: 'static + FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow) + where F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget, &mut ControlFlow) { self.event_loop.run(event_handler) } diff --git a/src/platform/desktop.rs b/src/platform/desktop.rs index 7ae56832a7..f0016105ec 100644 --- a/src/platform/desktop.rs +++ b/src/platform/desktop.rs @@ -27,14 +27,14 @@ pub trait EventLoopExtDesktop { /// /// You are strongly encouraged to use `run`, unless the use of this is absolutely necessary. fn run_return(&mut self, event_handler: F) - where F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow); + where F: FnMut(Event<'_, Self::UserEvent>, &EventLoopWindowTarget, &mut ControlFlow); } impl EventLoopExtDesktop for EventLoop { type UserEvent = T; fn run_return(&mut self, event_handler: F) - where F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow) + where F: FnMut(Event<'_, Self::UserEvent>, &EventLoopWindowTarget, &mut ControlFlow) { self.event_loop.run_return(event_handler) } diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 66620c5a1e..20d4fdd1f4 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -24,7 +24,7 @@ pub struct FileDropHandlerData { pub interface: IDropTarget, refcount: AtomicUsize, window: HWND, - send_event: Box)>, + send_event: Box)>, cursor_effect: DWORD, hovered_is_valid: bool, // If the currently hovered item is not valid there must not be any `HoveredFileCancelled` emitted } @@ -35,7 +35,7 @@ pub struct FileDropHandler { #[allow(non_snake_case)] impl FileDropHandler { - pub fn new(window: HWND, send_event: Box)>) -> FileDropHandler { + pub fn new(window: HWND, send_event: Box)>) -> FileDropHandler { let data = Box::new(FileDropHandlerData { interface: IDropTarget { lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl, @@ -208,7 +208,7 @@ impl FileDropHandler { } impl FileDropHandlerData { - fn send_event(&self, event: Event<()>) { + fn send_event(&self, event: Event<'static, ()>) { (self.send_event)(event); } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index b924fb954d..cce7608a2f 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -27,6 +27,7 @@ use std::marker::PhantomData; use parking_lot::Mutex; use winapi::shared::minwindef::{ + BOOL, DWORD, HIWORD, INT, @@ -43,7 +44,7 @@ use winapi::um::winnt::{LPCSTR, SHORT}; use crate::window::WindowId as RootWindowId; use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; -use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; +use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::event::{DeviceEvent, Touch, TouchPhase, StartCause, KeyboardInput, Event, WindowEvent}; use crate::platform_impl::platform::{event, WindowId, DEVICE_ID, wrap_device_id, util}; use crate::platform_impl::platform::dpi::{ @@ -57,25 +58,29 @@ use crate::platform_impl::platform::raw_input::{get_raw_input_data, get_raw_mous use crate::platform_impl::platform::window::adjust_size; use crate::platform_impl::platform::window_state::{CursorFlags, WindowFlags, WindowState}; -pub(crate) struct SubclassInput { +pub(crate) struct SubclassInput { pub window_state: Arc>, pub event_loop_runner: EventLoopRunnerShared, pub file_drop_handler: FileDropHandler, } impl SubclassInput { - unsafe fn send_event(&self, event: Event) { + unsafe fn send_event(&self, event: Event<'static, T>) { self.event_loop_runner.send_event(event); } + + unsafe fn send_event_unbuffered<'e>(&self, event: Event<'e, T>) -> Result<(), Event<'e, T>>{ + self.event_loop_runner.send_event_unbuffered(event) + } } -struct ThreadMsgTargetSubclassInput { +struct ThreadMsgTargetSubclassInput { event_loop_runner: EventLoopRunnerShared, user_event_receiver: Receiver, } impl ThreadMsgTargetSubclassInput { - unsafe fn send_event(&self, event: Event) { + unsafe fn send_event(&self, event: Event<'static, T>) { self.event_loop_runner.send_event(event); } } @@ -85,7 +90,7 @@ pub struct EventLoop { window_target: RootELW, } -pub struct EventLoopWindowTarget { +pub struct EventLoopWindowTarget { thread_id: DWORD, trigger_newevents_on_redraw: Arc, thread_msg_target: HWND, @@ -126,14 +131,14 @@ impl EventLoop { } pub fn run(mut self, event_handler: F) -> ! - where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) + where F: 'static + FnMut(Event<'_, T>, &RootELW, &mut ControlFlow) { self.run_return(event_handler); ::std::process::exit(0); } pub fn run_return(&mut self, mut event_handler: F) - where F: FnMut(Event, &RootELW, &mut ControlFlow) + where F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow) { unsafe{ winuser::IsGUIThread(1); } @@ -224,23 +229,31 @@ impl EventLoopWindowTarget { } pub(crate) type EventLoopRunnerShared = Rc>; -pub(crate) struct ELRShared { +pub(crate) struct ELRShared { runner: RefCell>>, - buffer: RefCell>>, + buffer: RefCell>>, } -pub(crate) struct EventLoopRunner { +pub(crate) struct EventLoopRunner { trigger_newevents_on_redraw: Arc, control_flow: ControlFlow, runner_state: RunnerState, modal_redraw_window: HWND, in_modal_loop: bool, - event_handler: Box, &mut ControlFlow)>, + event_handler: Box, &mut ControlFlow)>, panic_error: Option, } type PanicError = Box; impl ELRShared { - pub(crate) unsafe fn send_event(&self, event: Event) { + pub(crate) unsafe fn send_event(&self, event: Event<'static, T>) { + if let Err(event) = self.send_event_unbuffered(event) { + // If the runner is already borrowed, we're in the middle of an event loop invocation. Add + // the event to a buffer to be processed later. + self.buffer.borrow_mut().push_back(event) + } + } + + pub(crate) unsafe fn send_event_unbuffered<'e>(&self, event: Event<'e, T>) -> Result<(), Event<'e, T>> { if let Ok(mut runner_ref) = self.runner.try_borrow_mut() { if let Some(ref mut runner) = *runner_ref { runner.process_event(event); @@ -258,13 +271,11 @@ impl ELRShared { } } - return; + return Ok(()); } } - // If the runner is already borrowed, we're in the middle of an event loop invocation. Add - // the event to a buffer to be processed later. - self.buffer.borrow_mut().push_back(event) + Err(event) } } @@ -285,7 +296,7 @@ enum RunnerState { impl EventLoopRunner { unsafe fn new(event_loop: &EventLoop, f: F) -> EventLoopRunner - where F: FnMut(Event, &mut ControlFlow) + where F: FnMut(Event<'_, T>, &mut ControlFlow) { EventLoopRunner { trigger_newevents_on_redraw: event_loop.window_target.p.trigger_newevents_on_redraw.clone(), @@ -294,8 +305,8 @@ impl EventLoopRunner { in_modal_loop: false, modal_redraw_window: event_loop.window_target.p.thread_msg_target, event_handler: mem::transmute::< - Box, &mut ControlFlow)>, - Box, &mut ControlFlow)> + Box, &mut ControlFlow)>, + Box, &mut ControlFlow)> >(Box::new(f)), panic_error: None, } @@ -352,7 +363,7 @@ impl EventLoopRunner { }; } - fn process_event(&mut self, event: Event) { + fn process_event(&mut self, event: Event<'_, T>) { // If we're in the modal loop, we need to have some mechanism for finding when the event // queue has been cleared so we can call `events_cleared`. Windows doesn't give any utilities // for doing this, but it DOES guarantee that WM_PAINT will only occur after input events have @@ -457,7 +468,7 @@ impl EventLoopRunner { } } - fn call_event_handler(&mut self, event: Event) { + fn call_event_handler(&mut self, event: Event<'_, T>) { match event { Event::NewEvents(_) => self.trigger_newevents_on_redraw.store(true, Ordering::Relaxed), Event::EventsCleared => self.trigger_newevents_on_redraw.store(false, Ordering::Relaxed), @@ -759,7 +770,7 @@ pub(crate) fn subclass_window(window: HWND, subclass_input: SubclassInput) // // Returning 0 tells the Win32 API that the message has been processed. // FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary -unsafe extern "system" fn public_window_callback( +unsafe extern "system" fn public_window_callback( window: HWND, msg: UINT, wparam: WPARAM, @@ -1425,27 +1436,66 @@ unsafe extern "system" fn public_window_callback( new_dpi_factor != old_dpi_factor && window_state.fullscreen.is_none() }; - // This prevents us from re-applying DPI adjustment to the restored size after exiting - // fullscreen (the restored size is already DPI adjusted). - if allow_resize { - // Resize window to the size suggested by Windows. - let rect = &*(lparam as *const RECT); + let style = winuser::GetWindowLongW(window, winuser::GWL_STYLE) as _; + let style_ex = winuser::GetWindowLongW(window, winuser::GWL_EXSTYLE) as _; + let b_menu = !winuser::GetMenu(window).is_null() as BOOL; + + // New size as suggested by Windows. + let rect = *(lparam as *const RECT); + + // The window rect provided is the window's outer size, not it's inner size. However, + // win32 doesn't provide an `UnadjustWindowRectEx` function to get the client rect from + // the outer rect, so we instead adjust the window rect to get the decoration margins + // and remove them from the outer size. + let margins_horizontal: u32; + let margins_vertical: u32; + { + let mut adjusted_rect = rect; + winuser::AdjustWindowRectExForDpi( + &mut adjusted_rect, + style, + b_menu, + style_ex, + new_dpi_x + ); + let margin_left = rect.left - adjusted_rect.left; + let margin_right = adjusted_rect.right - rect.right; + let margin_top = rect.top - adjusted_rect.top; + let margin_bottom = adjusted_rect.bottom - rect.bottom; + + margins_horizontal = (margin_left + margin_right) as u32; + margins_vertical = (margin_bottom + margin_top) as u32; + } + + let physical_inner_rect = PhysicalSize::new( + (rect.right - rect.left) as u32 - margins_horizontal, + (rect.bottom - rect.top) as u32 - margins_vertical + ); + + // `allow_resize` prevents us from re-applying DPI adjustment to the restored size after + // exiting fullscreen (the restored size is already DPI adjusted). + let mut new_inner_rect_opt = Some(physical_inner_rect).filter(|_| allow_resize); + + let _ = subclass_input.send_event_unbuffered(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: HiDpiFactorChanged { + hidpi_factor: new_dpi_factor, + new_inner_size: &mut new_inner_rect_opt + }, + }); + + if let Some(new_inner_rect) = new_inner_rect_opt { winuser::SetWindowPos( window, ptr::null_mut(), rect.left, rect.top, - rect.right - rect.left, - rect.bottom - rect.top, + (new_inner_rect.width + margins_horizontal) as _, + (new_inner_rect.height + margins_vertical) as _, winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE, ); } - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: HiDpiFactorChanged(new_dpi_factor), - }); - 0 }, @@ -1464,7 +1514,7 @@ unsafe extern "system" fn public_window_callback( } } -unsafe extern "system" fn thread_event_target_callback( +unsafe extern "system" fn thread_event_target_callback( window: HWND, msg: UINT, wparam: WPARAM, From 6b13b8e2f443c025be3d3c7920d82231d0d78d18 Mon Sep 17 00:00:00 2001 From: Osspial Date: Fri, 14 Jun 2019 15:37:17 -0400 Subject: [PATCH 7/7] Document to_static --- src/event.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/event.rs b/src/event.rs index 021e4912ee..61f9acbaea 100644 --- a/src/event.rs +++ b/src/event.rs @@ -56,6 +56,8 @@ impl<'a, T> Event<'a, T> { } } + /// If the event doesn't contain a reference, turn it into an event with a `'static` lifetime. + /// Otherwise, return `None`. pub fn to_static(self) -> Option> { use self::Event::*; match self {