diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 60b82859e21..3d738ddf664 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 9ee87aa1674..e609807cb70 100644 --- a/src/event.rs +++ b/src/event.rs @@ -12,12 +12,12 @@ use window::WindowId; use 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 476e7ed5e10..1812980cc7d 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 1fafb0ddb31..b1e9b37bb17 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 6895bf306a4..66a706ec4f5 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 c417c2810d3..1bf30e3ad6a 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 window::WindowId as RootWindowId; use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; -use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; +use dpi::{PhysicalPosition, PhysicalSize}; use event::{DeviceEvent, Touch, TouchPhase, StartCause, KeyboardInput, Event, WindowEvent}; use platform_impl::platform::{event, WindowId, DEVICE_ID, wrap_device_id, util}; use platform_impl::platform::dpi::{ @@ -57,25 +58,29 @@ use platform_impl::platform::raw_input::{get_raw_input_data, get_raw_mouse_butto use platform_impl::platform::window::adjust_size; use 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,11 +229,11 @@ 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, @@ -240,7 +245,15 @@ pub(crate) struct EventLoopRunner { 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) } } @@ -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,