From d61f21666cac5e7981f8e6152c6d0beb5d0e74b9 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 27 Mar 2022 16:01:58 +0200 Subject: [PATCH 01/19] Add window resizing for X11 --- src/window.rs | 11 +++++++++++ src/x11/window.rs | 14 +++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/window.rs b/src/window.rs index a0d91f6..237e8b6 100644 --- a/src/window.rs +++ b/src/window.rs @@ -4,6 +4,7 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use crate::event::{Event, EventStatus}; use crate::window_open_options::WindowOpenOptions; +use crate::Size; #[cfg(target_os = "macos")] use crate::macos as platform; @@ -98,6 +99,16 @@ impl<'a> Window<'a> { self.window.close(); } + /// Resize the window to the given size. + /// + /// # TODO + /// + /// This is currently only supported on Linux. + #[cfg(target_os = "linux")] + pub fn resize(&mut self, size: Size) { + self.window.resize(size); + } + /// If provided, then an OpenGL context will be created for this window. You'll be able to /// access this context through [crate::Window::gl_context]. #[cfg(feature = "opengl")] diff --git a/src/x11/window.rs b/src/x11/window.rs index 2ad0cad..faebe44 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -12,7 +12,7 @@ use xcb::StructPtr; use super::XcbConnection; use crate::{ - Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, WindowEvent, + Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; @@ -389,6 +389,18 @@ impl Window { self.close_requested = true; } + pub fn resize(&mut self, size: Size) { + xcb::configure_window( + &self.xcb_connection.conn, + self.window_id, + &[ + (xcb::CONFIG_WINDOW_WIDTH as u16, size.width.round() as u32), + (xcb::CONFIG_WINDOW_HEIGHT as u16, size.height.round() as u32), + ], + ); + self.xcb_connection.conn.flush(); + } + #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option<&crate::gl::GlContext> { self.gl_context.as_ref() From 3dfff613b9164b16974a0423c82f7ccb649015be Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 27 Mar 2022 18:16:55 +0200 Subject: [PATCH 02/19] Use physical sizes in X11 window resize And update the window info for any future events. --- src/x11/window.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/x11/window.rs b/src/x11/window.rs index faebe44..c4d0314 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -390,12 +390,15 @@ impl Window { } pub fn resize(&mut self, size: Size) { + let scaling = self.window_info.scale(); + self.window_info = WindowInfo::from_logical_size(size, scaling); + xcb::configure_window( &self.xcb_connection.conn, self.window_id, &[ - (xcb::CONFIG_WINDOW_WIDTH as u16, size.width.round() as u32), - (xcb::CONFIG_WINDOW_HEIGHT as u16, size.height.round() as u32), + (xcb::CONFIG_WINDOW_WIDTH as u16, self.window_info.physical_size().width), + (xcb::CONFIG_WINDOW_HEIGHT as u16, self.window_info.physical_size().height), ], ); self.xcb_connection.conn.flush(); From c5867b6af6d8ec26651dd15df38e33e8e54cca51 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 30 Nov 2022 18:21:21 +0100 Subject: [PATCH 03/19] Use interior mutability for Windows WindowState This is a prerequisite for being able to handle reentrant messages. --- src/win/window.rs | 122 +++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index c7fa822..0dc9add 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -16,7 +16,7 @@ use winapi::um::winuser::{ XBUTTON1, XBUTTON2, }; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::ffi::{c_void, OsStr}; use std::marker::PhantomData; use std::os::windows::ffi::OsStrExt; @@ -126,11 +126,12 @@ unsafe extern "system" fn wnd_proc( return 0; } - let window_state_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut RefCell; + let window_state_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *const WindowState; if !window_state_ptr.is_null() { + let window_state = &*window_state_ptr; + match msg { WM_MOUSEMOVE => { - let mut window_state = (*window_state_ptr).borrow_mut(); let mut window = window_state.create_window(hwnd); let mut window = crate::Window::new(&mut window); @@ -138,18 +139,20 @@ unsafe extern "system" fn wnd_proc( let y = ((lparam >> 16) & 0xFFFF) as i16 as i32; let physical_pos = PhyPoint { x, y }; - let logical_pos = physical_pos.to_logical(&window_state.window_info); + let logical_pos = physical_pos.to_logical(&window_state.window_info.borrow()); let event = Event::Mouse(MouseEvent::CursorMoved { position: logical_pos, - modifiers: window_state.keyboard_state.get_modifiers_from_mouse_wparam(wparam), + modifiers: window_state + .keyboard_state + .borrow() + .get_modifiers_from_mouse_wparam(wparam), }); - window_state.handler.on_event(&mut window, event); + window_state.handler.borrow_mut().on_event(&mut window, event); return 0; } WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { - let mut window_state = (*window_state_ptr).borrow_mut(); let mut window = window_state.create_window(hwnd); let mut window = crate::Window::new(&mut window); @@ -163,20 +166,22 @@ unsafe extern "system" fn wnd_proc( } else { ScrollDelta::Lines { x: value, y: 0.0 } }, - modifiers: window_state.keyboard_state.get_modifiers_from_mouse_wparam(wparam), + modifiers: window_state + .keyboard_state + .borrow() + .get_modifiers_from_mouse_wparam(wparam), }); - window_state.handler.on_event(&mut window, event); + window_state.handler.borrow_mut().on_event(&mut window, event); return 0; } WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { - let mut window_state = (*window_state_ptr).borrow_mut(); let mut window = window_state.create_window(hwnd); let mut window = crate::Window::new(&mut window); - let mut mouse_button_counter = window_state.mouse_button_counter; + let mut mouse_button_counter = window_state.mouse_button_counter.get(); let button = match msg { WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left), @@ -200,6 +205,7 @@ unsafe extern "system" fn wnd_proc( button, modifiers: window_state .keyboard_state + .borrow() .get_modifiers_from_mouse_wparam(wparam), } } @@ -214,6 +220,7 @@ unsafe extern "system" fn wnd_proc( button, modifiers: window_state .keyboard_state + .borrow() .get_modifiers_from_mouse_wparam(wparam), } } @@ -222,30 +229,29 @@ unsafe extern "system" fn wnd_proc( } }; - window_state.mouse_button_counter = mouse_button_counter; + window_state.mouse_button_counter.set(mouse_button_counter); - window_state.handler.on_event(&mut window, Event::Mouse(event)); + window_state.handler.borrow_mut().on_event(&mut window, Event::Mouse(event)); } } WM_TIMER => { - let mut window_state = (*window_state_ptr).borrow_mut(); let mut window = window_state.create_window(hwnd); let mut window = crate::Window::new(&mut window); if wparam == WIN_FRAME_TIMER { - window_state.handler.on_frame(&mut window); + window_state.handler.borrow_mut().on_frame(&mut window); } return 0; } WM_CLOSE => { // Make sure to release the borrow before the DefWindowProc call { - let mut window_state = (*window_state_ptr).borrow_mut(); let mut window = window_state.create_window(hwnd); let mut window = crate::Window::new(&mut window); window_state .handler + .borrow_mut() .on_event(&mut window, Event::Window(WindowEvent::WillClose)); } @@ -255,15 +261,16 @@ unsafe extern "system" fn wnd_proc( } WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP | WM_INPUTLANGCHANGE => { - let mut window_state = (*window_state_ptr).borrow_mut(); let mut window = window_state.create_window(hwnd); let mut window = crate::Window::new(&mut window); - let opt_event = - window_state.keyboard_state.process_message(hwnd, msg, wparam, lparam); + let opt_event = window_state + .keyboard_state + .borrow_mut() + .process_message(hwnd, msg, wparam, lparam); if let Some(event) = opt_event { - window_state.handler.on_event(&mut window, Event::Keyboard(event)); + window_state.handler.borrow_mut().on_event(&mut window, Event::Keyboard(event)); } if msg != WM_SYSKEYDOWN { @@ -271,45 +278,45 @@ unsafe extern "system" fn wnd_proc( } } WM_SIZE => { - let mut window_state = (*window_state_ptr).borrow_mut(); let mut window = window_state.create_window(hwnd); let mut window = crate::Window::new(&mut window); let width = (lparam & 0xFFFF) as u16 as u32; let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; - window_state.window_info = WindowInfo::from_physical_size( - PhySize { width, height }, - window_state.window_info.scale(), - ); + let window_info = { + let mut window_info = window_state.window_info.borrow_mut(); + *window_info = WindowInfo::from_physical_size( + PhySize { width, height }, + window_info.scale(), + ); - let window_info = window_state.window_info; + window_info.clone() + }; window_state .handler + .borrow_mut() .on_event(&mut window, Event::Window(WindowEvent::Resized(window_info))); } WM_DPICHANGED => { - let mut window_state = (*window_state_ptr).borrow_mut(); - // To avoid weirdness with the realtime borrow checker. let new_rect = { if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy { let dpi = (wparam & 0xFFFF) as u16 as u32; let scale_factor = dpi as f64 / 96.0; - window_state.window_info = WindowInfo::from_logical_size( - window_state.window_info.logical_size(), - scale_factor, - ); + let mut window_info = window_state.window_info.borrow_mut(); + *window_info = + WindowInfo::from_logical_size(window_info.logical_size(), scale_factor); Some(( RECT { left: 0, top: 0, // todo: check if usize fits into i32 - right: window_state.window_info.physical_size().width as i32, - bottom: window_state.window_info.physical_size().height as i32, + right: window_info.physical_size().width as i32, + bottom: window_info.physical_size().height as i32, }, window_state.dw_style, )) @@ -336,8 +343,7 @@ unsafe extern "system" fn wnd_proc( } } WM_NCDESTROY => { - let window_state = Box::from_raw(window_state_ptr); - unregister_wnd_class(window_state.borrow().window_class); + unregister_wnd_class(window_state.window_class); SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); } _ => { @@ -378,13 +384,18 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) { UnregisterClassW(wnd_class as _, null_mut()); } +/// All data associated with the window. This uses internal mutability so the outer struct doesn't +/// need to be mutably borrowed. Mutably borrowing the entire `WindowState` can be problematic +/// because of the Windows message loops' reentrant nature. Care still needs to be taken to prevent +/// `handler` from indirectly triggering other events that would also need to be handled using +/// `handler`. struct WindowState { window_class: ATOM, - window_info: WindowInfo, + window_info: RefCell, _parent_handle: Option, - keyboard_state: KeyboardState, - mouse_button_counter: usize, - handler: Box, + keyboard_state: RefCell, + mouse_button_counter: Cell, + handler: RefCell>, scale_policy: WindowScalePolicy, dw_style: u32, @@ -536,29 +547,30 @@ impl Window { })); #[cfg(not(feature = "opengl"))] - let handler = Box::new(build(&mut crate::Window::new(&mut Window { hwnd }))); + let handler = build(&mut crate::Window::new(&mut Window { hwnd })); #[cfg(feature = "opengl")] - let handler = Box::new(build(&mut crate::Window::new(&mut Window { + let handler = build(&mut crate::Window::new(&mut Window { hwnd, gl_context: gl_context.clone(), - }))); + })); + let handler = RefCell::new(Box::new(handler)); let (parent_handle, window_handle) = ParentHandle::new(hwnd); let parent_handle = if parented { Some(parent_handle) } else { None }; - let mut window_state = Box::new(RefCell::new(WindowState { + let window_state = Box::new(WindowState { window_class, - window_info, + window_info: RefCell::new(window_info), _parent_handle: parent_handle, - keyboard_state: KeyboardState::new(), - mouse_button_counter: 0, + keyboard_state: RefCell::new(KeyboardState::new()), + mouse_button_counter: Cell::new(0), handler, scale_policy: options.scale, dw_style: flags, #[cfg(feature = "opengl")] gl_context, - })); + }); // Only works on Windows 10 unfortunately. SetProcessDpiAwarenessContext( @@ -571,19 +583,17 @@ impl Window { let dpi = GetDpiForWindow(hwnd); let scale_factor = dpi as f64 / 96.0; - let mut window_state = window_state.get_mut(); - if window_state.window_info.scale() != scale_factor { - window_state.window_info = WindowInfo::from_logical_size( - window_state.window_info.logical_size(), - scale_factor, - ); + let mut window_info = window_state.window_info.borrow_mut(); + if window_info.scale() != scale_factor { + *window_info = + WindowInfo::from_logical_size(window_info.logical_size(), scale_factor); Some(RECT { left: 0, top: 0, // todo: check if usize fits into i32 - right: window_state.window_info.physical_size().width as i32, - bottom: window_state.window_info.physical_size().height as i32, + right: window_info.physical_size().width as i32, + bottom: window_info.physical_size().height as i32, }) } else { None From b8a58674366467485ccf71ac018f3bc15a882a52 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 30 Nov 2022 18:25:05 +0100 Subject: [PATCH 04/19] Replace uses of Arc with Rc in Windows impl Everything is single threaded so there's no need to use Arcs. --- src/win/window.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index 0dc9add..9c3556b 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -21,8 +21,8 @@ use std::ffi::{c_void, OsStr}; use std::marker::PhantomData; use std::os::windows::ffi::OsStrExt; use std::ptr::null_mut; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle}; @@ -61,7 +61,7 @@ const WIN_FRAME_TIMER: usize = 4242; pub struct WindowHandle { hwnd: Option, - is_open: Arc, + is_open: Rc, // Ensure handle is !Send _phantom: PhantomData<*mut ()>, @@ -95,16 +95,16 @@ unsafe impl HasRawWindowHandle for WindowHandle { } struct ParentHandle { - is_open: Arc, + is_open: Rc, } impl ParentHandle { pub fn new(hwnd: HWND) -> (Self, WindowHandle) { - let is_open = Arc::new(AtomicBool::new(true)); + let is_open = Rc::new(AtomicBool::new(true)); let handle = WindowHandle { hwnd: Some(hwnd), - is_open: Arc::clone(&is_open), + is_open: Rc::clone(&is_open), _phantom: PhantomData::default(), }; @@ -400,7 +400,7 @@ struct WindowState { dw_style: u32, #[cfg(feature = "opengl")] - gl_context: Arc>, + gl_context: Rc>, } impl WindowState { @@ -419,7 +419,7 @@ pub struct Window { hwnd: HWND, #[cfg(feature = "opengl")] - gl_context: Arc>, + gl_context: Rc>, } impl Window { @@ -538,7 +538,7 @@ impl Window { // todo: manage error ^ #[cfg(feature = "opengl")] - let gl_context: Arc> = Arc::new(options.gl_config.map(|gl_config| { + let gl_context: Rc> = Rc::new(options.gl_config.map(|gl_config| { let mut handle = Win32Handle::empty(); handle.hwnd = hwnd as *mut c_void; let handle = RawWindowHandleWrapper { handle: RawWindowHandle::Win32(handle) }; From 80b30ce617cf2f3fabad1461f43018609bf10991 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 30 Nov 2022 18:57:56 +0100 Subject: [PATCH 05/19] Implement window resizing for Windows --- src/win/window.rs | 555 +++++++++++++++++++++++++++------------------- src/window.rs | 9 +- 2 files changed, 334 insertions(+), 230 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index 9c3556b..ef784c9 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -17,6 +17,7 @@ use winapi::um::winuser::{ }; use std::cell::{Cell, RefCell}; +use std::collections::VecDeque; use std::ffi::{c_void, OsStr}; use std::marker::PhantomData; use std::os::windows::ffi::OsStrExt; @@ -29,8 +30,8 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle}; const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1; use crate::{ - Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, WindowEvent, WindowHandler, - WindowInfo, WindowOpenOptions, WindowScalePolicy, + Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent, + WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; use super::keyboard::KeyboardState; @@ -130,234 +131,261 @@ unsafe extern "system" fn wnd_proc( if !window_state_ptr.is_null() { let window_state = &*window_state_ptr; - match msg { - WM_MOUSEMOVE => { - let mut window = window_state.create_window(hwnd); - let mut window = crate::Window::new(&mut window); + let result = wnd_proc_inner(hwnd, msg, wparam, lparam, window_state); - let x = (lparam & 0xFFFF) as i16 as i32; - let y = ((lparam >> 16) & 0xFFFF) as i16 as i32; + // If any of the above event handlers caused tasks to be pushed to the deferred tasks list, + // then we'll try to handle them now + loop { + // NOTE: This is written like this instead of using a `while let` loop to avoid exending + // the borrow of `window_state.deferred_tasks` into the call of + // `window_state.handle_deferred_task()` since that may also generate additional + // messages. + let task = match window_state.deferred_tasks.borrow_mut().pop_front() { + Some(task) => task, + None => break, + }; - let physical_pos = PhyPoint { x, y }; - let logical_pos = physical_pos.to_logical(&window_state.window_info.borrow()); - let event = Event::Mouse(MouseEvent::CursorMoved { - position: logical_pos, - modifiers: window_state - .keyboard_state - .borrow() - .get_modifiers_from_mouse_wparam(wparam), - }); + window_state.handle_deferred_task(task); + } - window_state.handler.borrow_mut().on_event(&mut window, event); - - return 0; - } - WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { - let mut window = window_state.create_window(hwnd); - let mut window = crate::Window::new(&mut window); - - let value = (wparam >> 16) as i16; - let value = value as i32; - let value = value as f32 / WHEEL_DELTA as f32; - - let event = Event::Mouse(MouseEvent::WheelScrolled { - delta: if msg == WM_MOUSEWHEEL { - ScrollDelta::Lines { x: 0.0, y: value } - } else { - ScrollDelta::Lines { x: value, y: 0.0 } - }, - modifiers: window_state - .keyboard_state - .borrow() - .get_modifiers_from_mouse_wparam(wparam), - }); - - window_state.handler.borrow_mut().on_event(&mut window, event); - - return 0; - } - WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN - | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { - let mut window = window_state.create_window(hwnd); - let mut window = crate::Window::new(&mut window); - - let mut mouse_button_counter = window_state.mouse_button_counter.get(); - - let button = match msg { - WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left), - WM_MBUTTONDOWN | WM_MBUTTONUP => Some(MouseButton::Middle), - WM_RBUTTONDOWN | WM_RBUTTONUP => Some(MouseButton::Right), - WM_XBUTTONDOWN | WM_XBUTTONUP => match GET_XBUTTON_WPARAM(wparam) { - XBUTTON1 => Some(MouseButton::Back), - XBUTTON2 => Some(MouseButton::Forward), - _ => None, - }, - _ => None, - }; - - if let Some(button) = button { - let event = match msg { - WM_LBUTTONDOWN | WM_MBUTTONDOWN | WM_RBUTTONDOWN | WM_XBUTTONDOWN => { - // Capture the mouse cursor on button down - mouse_button_counter = mouse_button_counter.saturating_add(1); - SetCapture(hwnd); - MouseEvent::ButtonPressed { - button, - modifiers: window_state - .keyboard_state - .borrow() - .get_modifiers_from_mouse_wparam(wparam), - } - } - WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => { - // Release the mouse cursor capture when all buttons are released - mouse_button_counter = mouse_button_counter.saturating_sub(1); - if mouse_button_counter == 0 { - ReleaseCapture(); - } - - MouseEvent::ButtonReleased { - button, - modifiers: window_state - .keyboard_state - .borrow() - .get_modifiers_from_mouse_wparam(wparam), - } - } - _ => { - unreachable!() - } - }; - - window_state.mouse_button_counter.set(mouse_button_counter); - - window_state.handler.borrow_mut().on_event(&mut window, Event::Mouse(event)); - } - } - WM_TIMER => { - let mut window = window_state.create_window(hwnd); - let mut window = crate::Window::new(&mut window); - - if wparam == WIN_FRAME_TIMER { - window_state.handler.borrow_mut().on_frame(&mut window); - } - return 0; - } - WM_CLOSE => { - // Make sure to release the borrow before the DefWindowProc call - { - let mut window = window_state.create_window(hwnd); - let mut window = crate::Window::new(&mut window); - - window_state - .handler - .borrow_mut() - .on_event(&mut window, Event::Window(WindowEvent::WillClose)); - } - - // DestroyWindow(hwnd); - // return 0; - return DefWindowProcW(hwnd, msg, wparam, lparam); - } - WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP - | WM_INPUTLANGCHANGE => { - let mut window = window_state.create_window(hwnd); - let mut window = crate::Window::new(&mut window); - - let opt_event = window_state - .keyboard_state - .borrow_mut() - .process_message(hwnd, msg, wparam, lparam); - - if let Some(event) = opt_event { - window_state.handler.borrow_mut().on_event(&mut window, Event::Keyboard(event)); - } - - if msg != WM_SYSKEYDOWN { - return 0; - } - } - WM_SIZE => { - let mut window = window_state.create_window(hwnd); - let mut window = crate::Window::new(&mut window); - - let width = (lparam & 0xFFFF) as u16 as u32; - let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; - - let window_info = { - let mut window_info = window_state.window_info.borrow_mut(); - *window_info = WindowInfo::from_physical_size( - PhySize { width, height }, - window_info.scale(), - ); - - window_info.clone() - }; - - window_state - .handler - .borrow_mut() - .on_event(&mut window, Event::Window(WindowEvent::Resized(window_info))); - } - WM_DPICHANGED => { - // To avoid weirdness with the realtime borrow checker. - let new_rect = { - if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy { - let dpi = (wparam & 0xFFFF) as u16 as u32; - let scale_factor = dpi as f64 / 96.0; - - let mut window_info = window_state.window_info.borrow_mut(); - *window_info = - WindowInfo::from_logical_size(window_info.logical_size(), scale_factor); - - Some(( - RECT { - left: 0, - top: 0, - // todo: check if usize fits into i32 - right: window_info.physical_size().width as i32, - bottom: window_info.physical_size().height as i32, - }, - window_state.dw_style, - )) - } else { - None - } - }; - if let Some((mut new_rect, dw_style)) = new_rect { - // Convert this desired "client rectangle" size to the actual "window rectangle" - // size (Because of course you have to do that). - AdjustWindowRectEx(&mut new_rect, dw_style, 0, 0); - - // Windows makes us resize the window manually. This will trigger another `WM_SIZE` event, - // which we can then send the user the new scale factor. - SetWindowPos( - hwnd, - hwnd, - new_rect.left as i32, - new_rect.top as i32, - new_rect.right - new_rect.left, - new_rect.bottom - new_rect.top, - SWP_NOZORDER | SWP_NOMOVE, - ); - } - } - WM_NCDESTROY => { - unregister_wnd_class(window_state.window_class); - SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); - } - _ => { - if msg == BV_WINDOW_MUST_CLOSE { - DestroyWindow(hwnd); - return 0; - } - } + // The actual custom window proc has been moved to another function so we can always handle + // the deferred tasks regardless of whether the custom window proc returns early or not + if let Some(result) = result { + return result; } } DefWindowProcW(hwnd, msg, wparam, lparam) } +/// Our custom `wnd_proc` handler. If the result contains a value, then this is returned after +/// handling any deferred tasks. otherwise the default window procedure is invoked. +unsafe fn wnd_proc_inner( + hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM, window_state: &WindowState, +) -> Option { + match msg { + WM_MOUSEMOVE => { + let mut window = window_state.create_window(hwnd); + let mut window = crate::Window::new(&mut window); + + let x = (lparam & 0xFFFF) as i16 as i32; + let y = ((lparam >> 16) & 0xFFFF) as i16 as i32; + + let physical_pos = PhyPoint { x, y }; + let logical_pos = physical_pos.to_logical(&window_state.window_info.borrow()); + let event = Event::Mouse(MouseEvent::CursorMoved { + position: logical_pos, + modifiers: window_state + .keyboard_state + .borrow() + .get_modifiers_from_mouse_wparam(wparam), + }); + + window_state.handler.borrow_mut().on_event(&mut window, event); + + return Some(0); + } + WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { + let mut window = window_state.create_window(hwnd); + let mut window = crate::Window::new(&mut window); + + let value = (wparam >> 16) as i16; + let value = value as i32; + let value = value as f32 / WHEEL_DELTA as f32; + + let event = Event::Mouse(MouseEvent::WheelScrolled { + delta: if msg == WM_MOUSEWHEEL { + ScrollDelta::Lines { x: 0.0, y: value } + } else { + ScrollDelta::Lines { x: value, y: 0.0 } + }, + modifiers: window_state + .keyboard_state + .borrow() + .get_modifiers_from_mouse_wparam(wparam), + }); + + window_state.handler.borrow_mut().on_event(&mut window, event); + + return Some(0); + } + WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN + | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { + let mut window = window_state.create_window(hwnd); + let mut window = crate::Window::new(&mut window); + + let mut mouse_button_counter = window_state.mouse_button_counter.get(); + + let button = match msg { + WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left), + WM_MBUTTONDOWN | WM_MBUTTONUP => Some(MouseButton::Middle), + WM_RBUTTONDOWN | WM_RBUTTONUP => Some(MouseButton::Right), + WM_XBUTTONDOWN | WM_XBUTTONUP => match GET_XBUTTON_WPARAM(wparam) { + XBUTTON1 => Some(MouseButton::Back), + XBUTTON2 => Some(MouseButton::Forward), + _ => None, + }, + _ => None, + }; + + if let Some(button) = button { + let event = match msg { + WM_LBUTTONDOWN | WM_MBUTTONDOWN | WM_RBUTTONDOWN | WM_XBUTTONDOWN => { + // Capture the mouse cursor on button down + mouse_button_counter = mouse_button_counter.saturating_add(1); + SetCapture(hwnd); + MouseEvent::ButtonPressed { + button, + modifiers: window_state + .keyboard_state + .borrow() + .get_modifiers_from_mouse_wparam(wparam), + } + } + WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => { + // Release the mouse cursor capture when all buttons are released + mouse_button_counter = mouse_button_counter.saturating_sub(1); + if mouse_button_counter == 0 { + ReleaseCapture(); + } + + MouseEvent::ButtonReleased { + button, + modifiers: window_state + .keyboard_state + .borrow() + .get_modifiers_from_mouse_wparam(wparam), + } + } + _ => { + unreachable!() + } + }; + + window_state.mouse_button_counter.set(mouse_button_counter); + + window_state.handler.borrow_mut().on_event(&mut window, Event::Mouse(event)); + } + } + WM_TIMER => { + let mut window = window_state.create_window(hwnd); + let mut window = crate::Window::new(&mut window); + + if wparam == WIN_FRAME_TIMER { + window_state.handler.borrow_mut().on_frame(&mut window); + } + return Some(0); + } + WM_CLOSE => { + // Make sure to release the borrow before the DefWindowProc call + { + let mut window = window_state.create_window(hwnd); + let mut window = crate::Window::new(&mut window); + + window_state + .handler + .borrow_mut() + .on_event(&mut window, Event::Window(WindowEvent::WillClose)); + } + + // DestroyWindow(hwnd); + // return Some(0); + return Some(DefWindowProcW(hwnd, msg, wparam, lparam)); + } + WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP + | WM_INPUTLANGCHANGE => { + let mut window = window_state.create_window(hwnd); + let mut window = crate::Window::new(&mut window); + + let opt_event = + window_state.keyboard_state.borrow_mut().process_message(hwnd, msg, wparam, lparam); + + if let Some(event) = opt_event { + window_state.handler.borrow_mut().on_event(&mut window, Event::Keyboard(event)); + } + + if msg != WM_SYSKEYDOWN { + return Some(0); + } + } + WM_SIZE => { + let mut window = window_state.create_window(hwnd); + let mut window = crate::Window::new(&mut window); + + let width = (lparam & 0xFFFF) as u16 as u32; + let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; + + let window_info = { + let mut window_info = window_state.window_info.borrow_mut(); + *window_info = + WindowInfo::from_physical_size(PhySize { width, height }, window_info.scale()); + + *window_info + }; + + window_state + .handler + .borrow_mut() + .on_event(&mut window, Event::Window(WindowEvent::Resized(window_info))); + } + WM_DPICHANGED => { + // To avoid weirdness with the realtime borrow checker. + let new_rect = { + if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy { + let dpi = (wparam & 0xFFFF) as u16 as u32; + let scale_factor = dpi as f64 / 96.0; + + let mut window_info = window_state.window_info.borrow_mut(); + *window_info = + WindowInfo::from_logical_size(window_info.logical_size(), scale_factor); + + Some(( + RECT { + left: 0, + top: 0, + // todo: check if usize fits into i32 + right: window_info.physical_size().width as i32, + bottom: window_info.physical_size().height as i32, + }, + window_state.dw_style, + )) + } else { + None + } + }; + if let Some((mut new_rect, dw_style)) = new_rect { + // Convert this desired "client rectangle" size to the actual "window rectangle" + // size (Because of course you have to do that). + AdjustWindowRectEx(&mut new_rect, dw_style, 0, 0); + + // Windows makes us resize the window manually. This will trigger another `WM_SIZE` event, + // which we can then send the user the new scale factor. + SetWindowPos( + hwnd, + hwnd, + new_rect.left as i32, + new_rect.top as i32, + new_rect.right - new_rect.left, + new_rect.bottom - new_rect.top, + SWP_NOZORDER | SWP_NOMOVE, + ); + } + } + WM_NCDESTROY => { + unregister_wnd_class(window_state.window_class); + SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); + } + _ => { + if msg == BV_WINDOW_MUST_CLOSE { + DestroyWindow(hwnd); + return Some(0); + } + } + } + + None +} + unsafe fn register_wnd_class() -> ATOM { // We generate a unique name for the new window class to prevent name collisions let class_name_str = format!("Baseview-{}", generate_guid()); @@ -390,6 +418,7 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) { /// `handler` from indirectly triggering other events that would also need to be handled using /// `handler`. struct WindowState { + hwnd: HWND, window_class: ATOM, window_info: RefCell, _parent_handle: Option, @@ -399,6 +428,13 @@ struct WindowState { scale_policy: WindowScalePolicy, dw_style: u32, + /// Tasks that should be executed at the end of `wnd_proc`. This is needed to avoid mutably + /// borrowing the fields from `WindowState` more than once. For instance, when the window + /// handler requests a resize in response to a keyboard event, the window state will already be + /// borrowed in `wnd_proc`. So the `resize()` function below cannot also mutably borrow that + /// window state at the same time. + deferred_tasks: Rc>>, + #[cfg(feature = "opengl")] gl_context: Rc>, } @@ -406,18 +442,64 @@ struct WindowState { impl WindowState { #[cfg(not(feature = "opengl"))] fn create_window(&self, hwnd: HWND) -> Window { - Window { hwnd } + Window { hwnd, deferred_tasks: self.deferred_tasks.clone() } } #[cfg(feature = "opengl")] fn create_window(&self, hwnd: HWND) -> Window { - Window { hwnd, gl_context: self.gl_context.clone() } + Window { + hwnd, + deferred_tasks: self.deferred_tasks.clone(), + gl_context: self.gl_context.clone(), + } + } + + /// Handle a deferred task as described in [`Self::deferred_tasks + pub(self) fn handle_deferred_task(&self, task: WindowTask) { + match task { + WindowTask::Resize(size) => { + let window_info = { + let mut window_info = self.window_info.borrow_mut(); + let scaling = window_info.scale(); + *window_info = WindowInfo::from_logical_size(size, scaling); + + *window_info + }; + + unsafe { + SetWindowPos( + self.hwnd, + self.hwnd, + 0, + 0, + window_info.physical_size().width as i32, + window_info.physical_size().height as i32, + SWP_NOZORDER | SWP_NOMOVE, + ) + }; + } + } } } +/// Tasks that must be deferred until the end of [`wnd_proc()`] to avoid reentrant `WindowState` +/// borrows. See the docstring on [`WindowState::deferred_tasks`] for more information. +#[derive(Debug, Clone)] +enum WindowTask { + /// Resize the window to the given size. The size is in logical pixels. DPI scaling is applied + /// automatically. + Resize(Size), +} + pub struct Window { + /// The HWND belonging to this window. The window's actual state is stored in the `WindowState` + /// struct associated with this HWND through `unsafe { GetWindowLongPtrW(self.hwnd, + /// GWLP_USERDATA) } as *const WindowState`. hwnd: HWND, + /// See [`WindowState::deferred_tasks`]. + deferred_tasks: Rc>>, + #[cfg(feature = "opengl")] gl_context: Rc>, } @@ -546,11 +628,20 @@ impl Window { GlContext::create(&handle, gl_config).expect("Could not create OpenGL context") })); + // The build closure shouldn't be enqueueing deferred tasks yet, but we'll try handling + // them just in case to avoid losing them + let deferred_tasks: Rc>> = + Rc::new(RefCell::new(VecDeque::new())); + #[cfg(not(feature = "opengl"))] - let handler = build(&mut crate::Window::new(&mut Window { hwnd })); + let handler = build(&mut crate::Window::new(&mut Window { + hwnd, + deferred_tasks: deferred_tasks.clone(), + })); #[cfg(feature = "opengl")] let handler = build(&mut crate::Window::new(&mut Window { hwnd, + deferred_tasks: deferred_tasks.clone(), gl_context: gl_context.clone(), })); let handler = RefCell::new(Box::new(handler)); @@ -559,6 +650,7 @@ impl Window { let parent_handle = if parented { Some(parent_handle) } else { None }; let window_state = Box::new(WindowState { + hwnd, window_class, window_info: RefCell::new(window_info), _parent_handle: parent_handle, @@ -568,10 +660,18 @@ impl Window { scale_policy: options.scale, dw_style: flags, + deferred_tasks: Rc::new(RefCell::new(VecDeque::with_capacity(4))), + #[cfg(feature = "opengl")] gl_context, }); + // If the plugin did queue up tasks as part of the build handler, then we'll process + // them now + for task in deferred_tasks.borrow_mut().drain(..) { + window_state.handle_deferred_task(task); + } + // Only works on Windows 10 unfortunately. SetProcessDpiAwarenessContext( winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, @@ -633,6 +733,13 @@ impl Window { } } + pub fn resize(&mut self, size: Size) { + // To avoid reentrant event handler calls we'll defer the actual resizing until after the + // event has been handled + let task = WindowTask::Resize(size); + self.deferred_tasks.borrow_mut().push_back(task); + } + #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option<&GlContext> { self.gl_context.as_ref().as_ref() diff --git a/src/window.rs b/src/window.rs index 237e8b6..e137502 100644 --- a/src/window.rs +++ b/src/window.rs @@ -99,12 +99,9 @@ impl<'a> Window<'a> { self.window.close(); } - /// Resize the window to the given size. - /// - /// # TODO - /// - /// This is currently only supported on Linux. - #[cfg(target_os = "linux")] + /// Resize the window to the given size. The size is always in logical pixels. DPI scaling will + /// automatically be accounted for. + #[cfg(any(target_os = "linux", target_os = "windows"))] pub fn resize(&mut self, size: Size) { self.window.resize(size); } From 5a1bb0f4d872801b0bc5741fc9b1bf6bbf93dc8e Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 1 Dec 2022 18:20:32 +0100 Subject: [PATCH 06/19] Ensure WindowEvent::Resized fires for Linux resize --- src/x11/window.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/x11/window.rs b/src/x11/window.rs index c4d0314..8056f90 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -391,17 +391,20 @@ impl Window { pub fn resize(&mut self, size: Size) { let scaling = self.window_info.scale(); - self.window_info = WindowInfo::from_logical_size(size, scaling); + let new_window_info = WindowInfo::from_logical_size(size, scaling); xcb::configure_window( &self.xcb_connection.conn, self.window_id, &[ - (xcb::CONFIG_WINDOW_WIDTH as u16, self.window_info.physical_size().width), - (xcb::CONFIG_WINDOW_HEIGHT as u16, self.window_info.physical_size().height), + (xcb::CONFIG_WINDOW_WIDTH as u16, new_window_info.physical_size().width), + (xcb::CONFIG_WINDOW_HEIGHT as u16, new_window_info.physical_size().height), ], ); self.xcb_connection.conn.flush(); + + // This will trigger a `ConfigureNotify` event which will in turn change `self.window_info` + // and notify the window handler about it } #[cfg(feature = "opengl")] From 537c303ee524193db6a5f867d0773e953a627f4d Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 1 Dec 2022 18:21:25 +0100 Subject: [PATCH 07/19] Only send resize event on Windows on actual change To be in line with the Linux implementation. --- src/win/window.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index ef784c9..58b924d 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -315,18 +315,25 @@ unsafe fn wnd_proc_inner( let width = (lparam & 0xFFFF) as u16 as u32; let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; - let window_info = { + let new_window_info = { let mut window_info = window_state.window_info.borrow_mut(); - *window_info = + let new_window_info = WindowInfo::from_physical_size(PhySize { width, height }, window_info.scale()); - *window_info + // Only send the event if anything changed + if window_info.physical_size() == new_window_info.physical_size() { + return None; + } + + *window_info = new_window_info; + + new_window_info }; window_state .handler .borrow_mut() - .on_event(&mut window, Event::Window(WindowEvent::Resized(window_info))); + .on_event(&mut window, Event::Window(WindowEvent::Resized(new_window_info))); } WM_DPICHANGED => { // To avoid weirdness with the realtime borrow checker. From 80fb0a00b8ccbe9f23ff3f4ed7ad7b6af269f794 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 1 Dec 2022 18:32:35 +0100 Subject: [PATCH 08/19] Only send resize event on macOS on actual change To follow the same behavior as on Linux and Windows. --- src/macos/view.rs | 11 ++++++++--- src/macos/window.rs | 27 +++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/macos/view.rs b/src/macos/view.rs index abcef68..1e95ff0 100644 --- a/src/macos/view.rs +++ b/src/macos/view.rs @@ -240,12 +240,17 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel, _: id) { let bounds: NSRect = msg_send![this, bounds]; - let window_info = WindowInfo::from_logical_size( + let new_window_info = WindowInfo::from_logical_size( Size::new(bounds.size.width, bounds.size.height), scale_factor, ); - state.trigger_event(Event::Window(WindowEvent::Resized(window_info))); + // Only send the event when the window's size has actually changed to be in line with the + // other platform implementations + if new_window_info.physical_size() != state.window_info.physical_size() { + state.window_info = new_window_info; + state.trigger_event(Event::Window(WindowEvent::Resized(new_window_info))); + } } } @@ -364,6 +369,6 @@ extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) { state.trigger_event(Event::Mouse(MouseEvent::WheelScrolled { delta, - modifiers: make_modifiers(modifiers) + modifiers: make_modifiers(modifiers), })); } diff --git a/src/macos/window.rs b/src/macos/window.rs index 0875377..d59b861 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -124,6 +124,13 @@ impl Window { { let pool = unsafe { NSAutoreleasePool::new(nil) }; + let scaling = match options.scale { + WindowScalePolicy::ScaleFactor(scale) => scale, + WindowScalePolicy::SystemScaleFactor => 1.0, + }; + + let window_info = WindowInfo::from_logical_size(options.size, scaling); + let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() { handle } else { @@ -144,7 +151,7 @@ impl Window { .map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)), }; - let window_handle = Self::init(true, window, build); + let window_handle = Self::init(true, window, window_info, build); unsafe { let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view]; @@ -164,6 +171,13 @@ impl Window { { let pool = unsafe { NSAutoreleasePool::new(nil) }; + let scaling = match options.scale { + WindowScalePolicy::ScaleFactor(scale) => scale, + WindowScalePolicy::SystemScaleFactor => 1.0, + }; + + let window_info = WindowInfo::from_logical_size(options.size, scaling); + let ns_view = unsafe { create_view(&options) }; let window = Window { @@ -178,7 +192,7 @@ impl Window { .map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)), }; - let window_handle = Self::init(true, window, build); + let window_handle = Self::init(true, window, window_info, build); unsafe { let () = msg_send![pool, drain]; @@ -254,7 +268,7 @@ impl Window { .map(|gl_config| Self::create_gl_context(Some(ns_window), ns_view, gl_config)), }; - let _ = Self::init(false, window, build); + let _ = Self::init(false, window, window_info, build); unsafe { ns_window.setContentView_(ns_view); @@ -266,7 +280,9 @@ impl Window { } } - fn init(parented: bool, mut window: Window, build: B) -> WindowHandle + fn init( + parented: bool, mut window: Window, window_info: WindowInfo, build: B, + ) -> WindowHandle where H: WindowHandler + 'static, B: FnOnce(&mut crate::Window) -> H, @@ -285,6 +301,7 @@ impl Window { keyboard_state: KeyboardState::new(), frame_timer: None, retain_count_after_build, + window_info, _parent_handle: parent_handle, })); @@ -325,6 +342,8 @@ pub(super) struct WindowState { frame_timer: Option, _parent_handle: Option, pub retain_count_after_build: usize, + /// The last known window info for this window. + pub window_info: WindowInfo, } impl WindowState { From e0c79c58fbc66d467c102f1c112192fa0009c0a3 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 1 Dec 2022 20:12:50 +0100 Subject: [PATCH 09/19] Implement window resizing for macOS Now this works on all baseview platforms. --- src/macos/window.rs | 30 ++++++++++++++++++++++++++---- src/window.rs | 1 - 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/macos/window.rs b/src/macos/window.rs index d59b861..8724dfa 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -5,10 +5,10 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use cocoa::appkit::{ - NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSWindow, - NSWindowStyleMask, + NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSView, + NSWindow, NSWindowStyleMask, }; -use cocoa::base::{id, nil, NO}; +use cocoa::base::{id, nil, NO, YES}; use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString}; use core_foundation::runloop::{ CFRunLoop, CFRunLoopTimer, CFRunLoopTimerContext, __CFRunLoopTimer, kCFRunLoopDefaultMode, @@ -20,7 +20,7 @@ use objc::{msg_send, runtime::Object, sel, sel_impl}; use raw_window_handle::{AppKitHandle, HasRawWindowHandle, RawWindowHandle}; use crate::{ - Event, EventStatus, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, + Event, EventStatus, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; @@ -319,6 +319,28 @@ impl Window { self.close_requested = true; } + pub fn resize(&mut self, size: Size) { + let size = NSSize::new(size.width, size.height); + + unsafe { NSView::setFrameSize(self.ns_view, size) }; + unsafe { + let _: () = msg_send![self.ns_view, setNeedsDisplay: YES]; + } + + // When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because macOS. + #[cfg(feature = "opengl")] + if let Some(gl_context) = &self.gl_context { + gl_context.resize(size); + } + + // If this is a standalone window then we'll also need to resize the window itself + if let Some(ns_window) = self.ns_window { + let mut frame = unsafe { NSWindow::frame(ns_window) }; + frame.size = size; + unsafe { NSWindow::setFrame_display_(ns_window, frame, YES) } + } + } + #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option<&GlContext> { self.gl_context.as_ref() diff --git a/src/window.rs b/src/window.rs index e137502..120ffa6 100644 --- a/src/window.rs +++ b/src/window.rs @@ -101,7 +101,6 @@ impl<'a> Window<'a> { /// Resize the window to the given size. The size is always in logical pixels. DPI scaling will /// automatically be accounted for. - #[cfg(any(target_os = "linux", target_os = "windows"))] pub fn resize(&mut self, size: Size) { self.window.resize(size); } From c7b61fa39be5b91b7f49a132bbcc3ae49a248468 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 2 Dec 2022 19:54:18 +0100 Subject: [PATCH 10/19] Also resize the OpenGL view on macOS This fixes resizing with OpenGL applications. --- src/gl/macos.rs | 9 +++++++++ src/gl/mod.rs | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/gl/macos.rs b/src/gl/macos.rs index 0610879..215dd94 100644 --- a/src/gl/macos.rs +++ b/src/gl/macos.rs @@ -11,6 +11,7 @@ use cocoa::appkit::{ NSOpenGLProfileVersionLegacy, NSOpenGLView, NSView, }; use cocoa::base::{id, nil, YES}; +use cocoa::foundation::NSSize; use core_foundation::base::TCFType; use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; @@ -134,6 +135,14 @@ impl GlContext { let () = msg_send![self.view, setNeedsDisplay: YES]; } } + + /// On macOS the `NSOpenGLView` needs to be resized separtely from our main view. + pub(crate) fn resize(&self, size: NSSize) { + unsafe { NSView::setFrameSize(self.view, size) }; + unsafe { + let _: () = msg_send![self.view, setNeedsDisplay: YES]; + } + } } impl Drop for GlContext { diff --git a/src/gl/mod.rs b/src/gl/mod.rs index a7922b7..adfc12d 100644 --- a/src/gl/mod.rs +++ b/src/gl/mod.rs @@ -106,4 +106,10 @@ impl GlContext { pub fn swap_buffers(&self) { self.context.swap_buffers(); } + + /// On macOS the `NSOpenGLView` needs to be resized separtely from our main view. + #[cfg(target_os = "macos")] + pub(crate) fn resize(&self, size: cocoa::foundation::NSSize) { + self.context.resize(size); + } } From bc2b2bd458e17e03762e8587cbfac2c6b0a8b750 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 2 Dec 2022 19:58:18 +0100 Subject: [PATCH 11/19] Factor in decorations when resizing macOS window Apparently macOS doesn't do this for you. If you don't do this then parts of the bottom and right of the window will be cut off. --- src/macos/window.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/macos/window.rs b/src/macos/window.rs index 8724dfa..259526c 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -336,7 +336,12 @@ impl Window { // If this is a standalone window then we'll also need to resize the window itself if let Some(ns_window) = self.ns_window { let mut frame = unsafe { NSWindow::frame(ns_window) }; + + // macOS wants you to manually add the size of the window decorations to the frame's + // size frame.size = size; + let frame = unsafe { NSWindow::frameRectForContentRect_(ns_window, frame) }; + unsafe { NSWindow::setFrame_display_(ns_window, frame, YES) } } } From d7a8c52d2389c726c8a87107064dc325976e798e Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 2 Dec 2022 20:02:32 +0100 Subject: [PATCH 12/19] Round size before resizing on macOS Otherwise the top row of pixels may contain flickering artifacts. --- src/macos/window.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/macos/window.rs b/src/macos/window.rs index 259526c..5e58e46 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -320,7 +320,9 @@ impl Window { } pub fn resize(&mut self, size: Size) { - let size = NSSize::new(size.width, size.height); + // NOTE: macOS gives you a personal rave if you pass in fractional pixels here. Even though + // the size is in fractional pixels. + let size = NSSize::new(size.width.round(), size.height.round()); unsafe { NSView::setFrameSize(self.ns_view, size) }; unsafe { From 512c3f2393d606f91963773c73cbf3ab8de5eb57 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 16 Dec 2022 23:20:56 +0100 Subject: [PATCH 13/19] Set macOS window size using setContentSize I somehow completely missed this the first five times I looked through the docs. --- src/macos/window.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/macos/window.rs b/src/macos/window.rs index 5e58e46..bf442da 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -337,14 +337,7 @@ impl Window { // If this is a standalone window then we'll also need to resize the window itself if let Some(ns_window) = self.ns_window { - let mut frame = unsafe { NSWindow::frame(ns_window) }; - - // macOS wants you to manually add the size of the window decorations to the frame's - // size - frame.size = size; - let frame = unsafe { NSWindow::frameRectForContentRect_(ns_window, frame) }; - - unsafe { NSWindow::setFrame_display_(ns_window, frame, YES) } + unsafe { NSWindow::setContentSize_(ns_window, size) }; } } From 96c0401865e2c53a2fa350ace7238dac9bb55771 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 1 Jan 2023 22:35:23 +0100 Subject: [PATCH 14/19] Replace AtomicBool with Cell https://github.com/RustAudio/baseview/pull/136#discussion_r1059198419 --- src/win/window.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index 58b924d..64dd9aa 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -23,7 +23,6 @@ use std::marker::PhantomData; use std::os::windows::ffi::OsStrExt; use std::ptr::null_mut; use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle}; @@ -62,7 +61,7 @@ const WIN_FRAME_TIMER: usize = 4242; pub struct WindowHandle { hwnd: Option, - is_open: Rc, + is_open: Rc>, // Ensure handle is !Send _phantom: PhantomData<*mut ()>, @@ -78,7 +77,7 @@ impl WindowHandle { } pub fn is_open(&self) -> bool { - self.is_open.load(Ordering::Relaxed) + self.is_open.get() } } @@ -96,12 +95,12 @@ unsafe impl HasRawWindowHandle for WindowHandle { } struct ParentHandle { - is_open: Rc, + is_open: Rc>, } impl ParentHandle { pub fn new(hwnd: HWND) -> (Self, WindowHandle) { - let is_open = Rc::new(AtomicBool::new(true)); + let is_open = Rc::new(Cell::new(true)); let handle = WindowHandle { hwnd: Some(hwnd), @@ -115,7 +114,7 @@ impl ParentHandle { impl Drop for ParentHandle { fn drop(&mut self) { - self.is_open.store(false, Ordering::Relaxed); + self.is_open.set(false); } } From 39e97ab2beeca85df3d25b748b59d6d8c3bc09d7 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 1 Jan 2023 22:38:41 +0100 Subject: [PATCH 15/19] Fix Clippy lints in Windows backend --- src/win/window.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index 64dd9aa..e2abd23 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -369,8 +369,8 @@ unsafe fn wnd_proc_inner( SetWindowPos( hwnd, hwnd, - new_rect.left as i32, - new_rect.top as i32, + new_rect.left, + new_rect.top, new_rect.right - new_rect.left, new_rect.bottom - new_rect.top, SWP_NOZORDER | SWP_NOMOVE, @@ -721,8 +721,8 @@ impl Window { SetWindowPos( hwnd, hwnd, - new_rect.left as i32, - new_rect.top as i32, + new_rect.left, + new_rect.top, new_rect.right - new_rect.left, new_rect.bottom - new_rect.top, SWP_NOZORDER | SWP_NOMOVE, From 8e3e078220990d15f986080c579c6f373e136e2a Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 1 Jan 2023 22:38:57 +0100 Subject: [PATCH 16/19] Fix Clippy lints in macOS backend --- src/macos/view.rs | 2 +- src/macos/window.rs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/macos/view.rs b/src/macos/view.rs index 1e95ff0..52c9311 100644 --- a/src/macos/view.rs +++ b/src/macos/view.rs @@ -234,7 +234,7 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel, _: id) { let ns_window: *mut Object = msg_send![this, window]; let scale_factor: f64 = - if ns_window.is_null() { 1.0 } else { NSWindow::backingScaleFactor(ns_window) as f64 }; + if ns_window.is_null() { 1.0 } else { NSWindow::backingScaleFactor(ns_window) }; let state: &mut WindowState = WindowState::from_field(this); diff --git a/src/macos/window.rs b/src/macos/window.rs index bf442da..fd67de7 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -229,10 +229,7 @@ impl Window { let rect = NSRect::new( NSPoint::new(0.0, 0.0), - NSSize::new( - window_info.logical_size().width as f64, - window_info.logical_size().height as f64, - ), + NSSize::new(window_info.logical_size().width, window_info.logical_size().height), ); let ns_window = unsafe { From c10ca6b40f369b0f06921024fecdd5e33d9fe514 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 1 Jan 2023 23:04:27 +0100 Subject: [PATCH 17/19] Clean up the Windows WindowState This would leak before this change. --- src/win/window.rs | 59 +++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index e2abd23..b9e4457 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -126,11 +126,9 @@ unsafe extern "system" fn wnd_proc( return 0; } - let window_state_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *const WindowState; + let window_state_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut WindowState; if !window_state_ptr.is_null() { - let window_state = &*window_state_ptr; - - let result = wnd_proc_inner(hwnd, msg, wparam, lparam, window_state); + let result = wnd_proc_inner(hwnd, msg, wparam, lparam, &*window_state_ptr); // If any of the above event handlers caused tasks to be pushed to the deferred tasks list, // then we'll try to handle them now @@ -139,12 +137,19 @@ unsafe extern "system" fn wnd_proc( // the borrow of `window_state.deferred_tasks` into the call of // `window_state.handle_deferred_task()` since that may also generate additional // messages. - let task = match window_state.deferred_tasks.borrow_mut().pop_front() { + let task = match (*window_state_ptr).deferred_tasks.borrow_mut().pop_front() { Some(task) => task, None => break, }; - window_state.handle_deferred_task(task); + (*window_state_ptr).handle_deferred_task(task); + } + + // NOTE: This is not handled in `wnd_proc_inner` because of the deferred task loop above + if msg == WM_NCDESTROY { + unregister_wnd_class((*window_state_ptr).window_class); + SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); + drop(Box::from_raw(window_state_ptr)); } // The actual custom window proc has been moved to another function so we can always handle @@ -182,7 +187,7 @@ unsafe fn wnd_proc_inner( window_state.handler.borrow_mut().on_event(&mut window, event); - return Some(0); + Some(0) } WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { let mut window = window_state.create_window(hwnd); @@ -206,7 +211,7 @@ unsafe fn wnd_proc_inner( window_state.handler.borrow_mut().on_event(&mut window, event); - return Some(0); + Some(0) } WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { @@ -265,6 +270,8 @@ unsafe fn wnd_proc_inner( window_state.handler.borrow_mut().on_event(&mut window, Event::Mouse(event)); } + + None } WM_TIMER => { let mut window = window_state.create_window(hwnd); @@ -273,7 +280,8 @@ unsafe fn wnd_proc_inner( if wparam == WIN_FRAME_TIMER { window_state.handler.borrow_mut().on_frame(&mut window); } - return Some(0); + + Some(0) } WM_CLOSE => { // Make sure to release the borrow before the DefWindowProc call @@ -288,8 +296,8 @@ unsafe fn wnd_proc_inner( } // DestroyWindow(hwnd); - // return Some(0); - return Some(DefWindowProcW(hwnd, msg, wparam, lparam)); + // Some(0) + Some(DefWindowProcW(hwnd, msg, wparam, lparam)) } WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP | WM_INPUTLANGCHANGE => { @@ -304,7 +312,9 @@ unsafe fn wnd_proc_inner( } if msg != WM_SYSKEYDOWN { - return Some(0); + Some(0) + } else { + None } } WM_SIZE => { @@ -333,6 +343,8 @@ unsafe fn wnd_proc_inner( .handler .borrow_mut() .on_event(&mut window, Event::Window(WindowEvent::Resized(new_window_info))); + + None } WM_DPICHANGED => { // To avoid weirdness with the realtime borrow checker. @@ -376,20 +388,17 @@ unsafe fn wnd_proc_inner( SWP_NOZORDER | SWP_NOMOVE, ); } - } - WM_NCDESTROY => { - unregister_wnd_class(window_state.window_class); - SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); - } - _ => { - if msg == BV_WINDOW_MUST_CLOSE { - DestroyWindow(hwnd); - return Some(0); - } - } - } - None + None + } + // NOTE: `WM_NCDESTROY` is handled in the outer function because this deallocates the window + // state + BV_WINDOW_MUST_CLOSE => { + DestroyWindow(hwnd); + Some(0) + } + _ => None, + } } unsafe fn register_wnd_class() -> ATOM { From be5238ec461c45f1412b4ef1ce5e0cc26402d0e6 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 1 Jan 2023 23:38:51 +0100 Subject: [PATCH 18/19] Replace Rcs by reference to entire WindowState This removes the need to pass through individual Rcs at the cost of adding more lifetime parameters to the internals of the shared `Window` struct and requiring an `Option` for the handler. --- src/win/window.rs | 129 ++++++++++++++++++++-------------------------- src/window.rs | 10 ++++ 2 files changed, 67 insertions(+), 72 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index b9e4457..4689515 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -169,7 +169,7 @@ unsafe fn wnd_proc_inner( ) -> Option { match msg { WM_MOUSEMOVE => { - let mut window = window_state.create_window(hwnd); + let mut window = window_state.create_window(); let mut window = crate::Window::new(&mut window); let x = (lparam & 0xFFFF) as i16 as i32; @@ -185,12 +185,12 @@ unsafe fn wnd_proc_inner( .get_modifiers_from_mouse_wparam(wparam), }); - window_state.handler.borrow_mut().on_event(&mut window, event); + window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event); Some(0) } WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { - let mut window = window_state.create_window(hwnd); + let mut window = window_state.create_window(); let mut window = crate::Window::new(&mut window); let value = (wparam >> 16) as i16; @@ -209,13 +209,13 @@ unsafe fn wnd_proc_inner( .get_modifiers_from_mouse_wparam(wparam), }); - window_state.handler.borrow_mut().on_event(&mut window, event); + window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event); Some(0) } WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { - let mut window = window_state.create_window(hwnd); + let mut window = window_state.create_window(); let mut window = crate::Window::new(&mut window); let mut mouse_button_counter = window_state.mouse_button_counter.get(); @@ -268,17 +268,22 @@ unsafe fn wnd_proc_inner( window_state.mouse_button_counter.set(mouse_button_counter); - window_state.handler.borrow_mut().on_event(&mut window, Event::Mouse(event)); + window_state + .handler + .borrow_mut() + .as_mut() + .unwrap() + .on_event(&mut window, Event::Mouse(event)); } None } WM_TIMER => { - let mut window = window_state.create_window(hwnd); + let mut window = window_state.create_window(); let mut window = crate::Window::new(&mut window); if wparam == WIN_FRAME_TIMER { - window_state.handler.borrow_mut().on_frame(&mut window); + window_state.handler.borrow_mut().as_mut().unwrap().on_frame(&mut window); } Some(0) @@ -286,12 +291,14 @@ unsafe fn wnd_proc_inner( WM_CLOSE => { // Make sure to release the borrow before the DefWindowProc call { - let mut window = window_state.create_window(hwnd); + let mut window = window_state.create_window(); let mut window = crate::Window::new(&mut window); window_state .handler .borrow_mut() + .as_mut() + .unwrap() .on_event(&mut window, Event::Window(WindowEvent::WillClose)); } @@ -301,14 +308,19 @@ unsafe fn wnd_proc_inner( } WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP | WM_INPUTLANGCHANGE => { - let mut window = window_state.create_window(hwnd); + let mut window = window_state.create_window(); let mut window = crate::Window::new(&mut window); let opt_event = window_state.keyboard_state.borrow_mut().process_message(hwnd, msg, wparam, lparam); if let Some(event) = opt_event { - window_state.handler.borrow_mut().on_event(&mut window, Event::Keyboard(event)); + window_state + .handler + .borrow_mut() + .as_mut() + .unwrap() + .on_event(&mut window, Event::Keyboard(event)); } if msg != WM_SYSKEYDOWN { @@ -318,7 +330,7 @@ unsafe fn wnd_proc_inner( } } WM_SIZE => { - let mut window = window_state.create_window(hwnd); + let mut window = window_state.create_window(); let mut window = crate::Window::new(&mut window); let width = (lparam & 0xFFFF) as u16 as u32; @@ -342,6 +354,8 @@ unsafe fn wnd_proc_inner( window_state .handler .borrow_mut() + .as_mut() + .unwrap() .on_event(&mut window, Event::Window(WindowEvent::Resized(new_window_info))); None @@ -433,13 +447,17 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) { /// `handler` from indirectly triggering other events that would also need to be handled using /// `handler`. struct WindowState { - hwnd: HWND, + /// The HWND belonging to this window. The window's actual state is stored in the `WindowState` + /// struct associated with this HWND through `unsafe { GetWindowLongPtrW(self.hwnd, + /// GWLP_USERDATA) } as *const WindowState`. + pub hwnd: HWND, window_class: ATOM, window_info: RefCell, _parent_handle: Option, keyboard_state: RefCell, mouse_button_counter: Cell, - handler: RefCell>, + // Initialized late so the `Window` can hold a reference to this `WindowState` + handler: RefCell>>, scale_policy: WindowScalePolicy, dw_style: u32, @@ -448,25 +466,15 @@ struct WindowState { /// handler requests a resize in response to a keyboard event, the window state will already be /// borrowed in `wnd_proc`. So the `resize()` function below cannot also mutably borrow that /// window state at the same time. - deferred_tasks: Rc>>, + pub deferred_tasks: RefCell>, #[cfg(feature = "opengl")] - gl_context: Rc>, + pub gl_context: Option, } impl WindowState { - #[cfg(not(feature = "opengl"))] - fn create_window(&self, hwnd: HWND) -> Window { - Window { hwnd, deferred_tasks: self.deferred_tasks.clone() } - } - - #[cfg(feature = "opengl")] - fn create_window(&self, hwnd: HWND) -> Window { - Window { - hwnd, - deferred_tasks: self.deferred_tasks.clone(), - gl_context: self.gl_context.clone(), - } + fn create_window(&self) -> Window { + Window { state: self } } /// Handle a deferred task as described in [`Self::deferred_tasks @@ -506,20 +514,11 @@ enum WindowTask { Resize(Size), } -pub struct Window { - /// The HWND belonging to this window. The window's actual state is stored in the `WindowState` - /// struct associated with this HWND through `unsafe { GetWindowLongPtrW(self.hwnd, - /// GWLP_USERDATA) } as *const WindowState`. - hwnd: HWND, - - /// See [`WindowState::deferred_tasks`]. - deferred_tasks: Rc>>, - - #[cfg(feature = "opengl")] - gl_context: Rc>, +pub struct Window<'a> { + state: &'a WindowState, } -impl Window { +impl Window<'_> { pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle where P: HasRawWindowHandle, @@ -635,31 +634,13 @@ impl Window { // todo: manage error ^ #[cfg(feature = "opengl")] - let gl_context: Rc> = Rc::new(options.gl_config.map(|gl_config| { + let gl_context: Option = options.gl_config.map(|gl_config| { let mut handle = Win32Handle::empty(); handle.hwnd = hwnd as *mut c_void; let handle = RawWindowHandleWrapper { handle: RawWindowHandle::Win32(handle) }; GlContext::create(&handle, gl_config).expect("Could not create OpenGL context") - })); - - // The build closure shouldn't be enqueueing deferred tasks yet, but we'll try handling - // them just in case to avoid losing them - let deferred_tasks: Rc>> = - Rc::new(RefCell::new(VecDeque::new())); - - #[cfg(not(feature = "opengl"))] - let handler = build(&mut crate::Window::new(&mut Window { - hwnd, - deferred_tasks: deferred_tasks.clone(), - })); - #[cfg(feature = "opengl")] - let handler = build(&mut crate::Window::new(&mut Window { - hwnd, - deferred_tasks: deferred_tasks.clone(), - gl_context: gl_context.clone(), - })); - let handler = RefCell::new(Box::new(handler)); + }); let (parent_handle, window_handle) = ParentHandle::new(hwnd); let parent_handle = if parented { Some(parent_handle) } else { None }; @@ -671,21 +652,25 @@ impl Window { _parent_handle: parent_handle, keyboard_state: RefCell::new(KeyboardState::new()), mouse_button_counter: Cell::new(0), - handler, + // The Window refers to this `WindowState`, so this `handler` needs to be + // initialized later + handler: RefCell::new(None), scale_policy: options.scale, dw_style: flags, - deferred_tasks: Rc::new(RefCell::new(VecDeque::with_capacity(4))), + deferred_tasks: RefCell::new(VecDeque::with_capacity(4)), #[cfg(feature = "opengl")] gl_context, }); - // If the plugin did queue up tasks as part of the build handler, then we'll process - // them now - for task in deferred_tasks.borrow_mut().drain(..) { - window_state.handle_deferred_task(task); - } + let handler = { + let mut window = window_state.create_window(); + let mut window = crate::Window::new(&mut window); + + build(&mut window) + }; + *window_state.handler.borrow_mut() = Some(Box::new(handler)); // Only works on Windows 10 unfortunately. SetProcessDpiAwarenessContext( @@ -744,7 +729,7 @@ impl Window { pub fn close(&mut self) { unsafe { - PostMessageW(self.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0); + PostMessageW(self.state.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0); } } @@ -752,19 +737,19 @@ impl Window { // To avoid reentrant event handler calls we'll defer the actual resizing until after the // event has been handled let task = WindowTask::Resize(size); - self.deferred_tasks.borrow_mut().push_back(task); + self.state.deferred_tasks.borrow_mut().push_back(task); } #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option<&GlContext> { - self.gl_context.as_ref().as_ref() + self.state.gl_context.as_ref() } } -unsafe impl HasRawWindowHandle for Window { +unsafe impl HasRawWindowHandle for Window<'_> { fn raw_window_handle(&self) -> RawWindowHandle { let mut handle = Win32Handle::empty(); - handle.hwnd = self.hwnd as *mut c_void; + handle.hwnd = self.state.hwnd as *mut c_void; RawWindowHandle::Win32(handle) } diff --git a/src/window.rs b/src/window.rs index 120ffa6..c0ef1ac 100644 --- a/src/window.rs +++ b/src/window.rs @@ -54,12 +54,22 @@ pub trait WindowHandler { } pub struct Window<'a> { + #[cfg(target_os = "windows")] + window: &'a mut platform::Window<'a>, + #[cfg(not(target_os = "windows"))] window: &'a mut platform::Window, + // so that Window is !Send on all platforms phantom: PhantomData<*mut ()>, } impl<'a> Window<'a> { + #[cfg(target_os = "windows")] + pub(crate) fn new(window: &'a mut platform::Window<'a>) -> Window<'a> { + Window { window, phantom: PhantomData } + } + + #[cfg(not(target_os = "windows"))] pub(crate) fn new(window: &mut platform::Window) -> Window { Window { window, phantom: PhantomData } } From 425ee7ac3532c2800322244c423584e5a9172790 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 1 Jan 2023 23:43:04 +0100 Subject: [PATCH 19/19] Add missing AdjustWindowRectEx() for resize --- src/win/window.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index 4689515..83ff6d2 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -489,14 +489,23 @@ impl WindowState { *window_info }; + // If the window is a standalone window then the size needs to include the window + // decorations + let mut rect = RECT { + left: 0, + top: 0, + right: window_info.physical_size().width as i32, + bottom: window_info.physical_size().height as i32, + }; unsafe { + AdjustWindowRectEx(&mut rect, self.dw_style, 0, 0); SetWindowPos( self.hwnd, self.hwnd, 0, 0, - window_info.physical_size().width as i32, - window_info.physical_size().height as i32, + rect.right - rect.left, + rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE, ) };