diff --git a/Cargo.toml b/Cargo.toml index 18b7e954b8..e5f1c84ed8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ core-foundation = "0.6" core-graphics = "0.17.3" core-video-sys = "0.1.2" dispatch = "0.1.4" -objc = "0.2.3" +objc = "0.2.6" [target.'cfg(target_os = "windows")'.dependencies] bitflags = "1" diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index cccbb098b6..38cd438b0c 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -10,12 +10,22 @@ use std::{ time::Instant, }; -use cocoa::{appkit::NSApp, base::nil}; +use cocoa::{ + appkit::{NSApp, NSWindow}, + base::nil, + foundation::NSSize, +}; use crate::{ + dpi::LogicalSize, event::{Event, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget}, - platform_impl::platform::{observer::EventLoopWaker, util::Never}, + platform_impl::platform::{ + event::{EventProxy, EventWrapper}, + observer::EventLoopWaker, + util::{IdRef, Never}, + window::get_window_id, + }, window::WindowId, }; @@ -23,8 +33,8 @@ lazy_static! { static ref HANDLER: Handler = Default::default(); } -impl Event { - fn userify(self) -> Event { +impl<'a, Never> Event<'a, Never> { + fn userify(self) -> Event<'a, T> { self.map_nonuser_event() // `Never` can't be constructed, so the `UserEvent` variant can't // be present here. @@ -33,7 +43,8 @@ impl Event { } pub trait EventHandler: Debug { - fn handle_nonuser_event(&mut self, event: Event, control_flow: &mut ControlFlow); + // Not sure probably it should accept Event<'static, Never> + fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow); fn handle_user_events(&mut self, control_flow: &mut ControlFlow); } @@ -54,10 +65,10 @@ impl Debug for EventLoopHandler { impl EventHandler for EventLoopHandler where - F: 'static + FnMut(Event, &RootWindowTarget, &mut ControlFlow), + F: 'static + FnMut(Event<'_, T>, &RootWindowTarget, &mut ControlFlow), T: 'static, { - fn handle_nonuser_event(&mut self, event: Event, control_flow: &mut ControlFlow) { + fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow) { (self.callback)(event.userify(), &self.window_target, control_flow); self.will_exit |= *control_flow == ControlFlow::Exit; if self.will_exit { @@ -86,8 +97,8 @@ struct Handler { control_flow_prev: Mutex, start_time: Mutex>, callback: Mutex>>, - pending_events: Mutex>>, - deferred_events: Mutex>>, + pending_events: Mutex>>, + deferred_events: Mutex>, pending_redraw: Mutex>, waker: Mutex, } @@ -96,19 +107,19 @@ unsafe impl Send for Handler {} unsafe impl Sync for Handler {} impl Handler { - fn events<'a>(&'a self) -> MutexGuard<'a, VecDeque>> { + fn events(&self) -> MutexGuard<'_, VecDeque>> { self.pending_events.lock().unwrap() } - fn deferred<'a>(&'a self) -> MutexGuard<'a, VecDeque>> { + fn deferred(&self) -> MutexGuard<'_, VecDeque> { self.deferred_events.lock().unwrap() } - fn redraw<'a>(&'a self) -> MutexGuard<'a, Vec> { + fn redraw(&self) -> MutexGuard<'_, Vec> { self.pending_redraw.lock().unwrap() } - fn waker<'a>(&'a self) -> MutexGuard<'a, EventLoopWaker> { + fn waker(&self) -> MutexGuard<'_, EventLoopWaker> { self.waker.lock().unwrap() } @@ -144,11 +155,11 @@ impl Handler { *self.start_time.lock().unwrap() = Some(Instant::now()); } - fn take_events(&self) -> VecDeque> { + fn take_events(&self) -> VecDeque> { mem::replace(&mut *self.events(), Default::default()) } - fn take_deferred(&self) -> VecDeque> { + fn take_deferred(&self) -> VecDeque { mem::replace(&mut *self.deferred(), Default::default()) } @@ -164,7 +175,7 @@ impl Handler { self.in_callback.store(in_callback, Ordering::Release); } - fn handle_nonuser_event(&self, event: Event) { + fn handle_nonuser_event(&self, event: Event<'_, Never>) { if let Some(ref mut callback) = *self.callback.lock().unwrap() { callback.handle_nonuser_event(event, &mut *self.control_flow.lock().unwrap()); } @@ -175,6 +186,42 @@ impl Handler { callback.handle_user_events(&mut *self.control_flow.lock().unwrap()); } } + + fn handle_hidpi_factor_changed_event( + &self, + ns_window: IdRef, + suggested_size: LogicalSize, + hidpi_factor: f64, + ) { + let size = suggested_size.to_physical(hidpi_factor); + let new_inner_size = &mut Some(size); + let event = Event::WindowEvent { + window_id: WindowId(get_window_id(*ns_window)), + event: WindowEvent::HiDpiFactorChanged { + hidpi_factor, + new_inner_size, + }, + }; + + self.handle_nonuser_event(event); + + // let origin = unsafe { NSWindow::frame(*ns_window).origin }; + let physical_size = new_inner_size.unwrap_or(size); + let logical_size = physical_size.to_logical(hidpi_factor); + let size = NSSize::new(logical_size.width, logical_size.height); + // let rect = NSRect::new(origin, size); + unsafe { NSWindow::setContentSize_(*ns_window, size) }; + } + + fn handle_event(&self, proxy: EventProxy) { + match proxy { + EventProxy::HiDpiFactorChangedProxy { + ns_window, + suggested_size, + hidpi_factor, + } => self.handle_hidpi_factor_changed_event(ns_window, suggested_size, hidpi_factor), + } + } } pub enum AppState {} @@ -182,7 +229,7 @@ pub enum AppState {} impl AppState { pub fn set_callback(callback: F, window_target: RootWindowTarget) where - F: 'static + FnMut(Event, &RootWindowTarget, &mut ControlFlow), + F: 'static + FnMut(Event<'_, T>, &RootWindowTarget, &mut ControlFlow), T: 'static, { *HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler { @@ -245,29 +292,32 @@ impl AppState { } } - pub fn queue_event(event: Event) { + pub fn queue_event(event: Event<'static, Never>) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { panic!("Event queued from different thread: {:#?}", event); } HANDLER.events().push_back(event); } - pub fn queue_events(mut events: VecDeque>) { + pub fn queue_events(mut events: VecDeque>) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { panic!("Events queued from different thread: {:#?}", events); } HANDLER.events().append(&mut events); } - pub fn send_event_immediately(event: Event) { + pub fn send_event_immediately(wrapper: EventWrapper) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { - panic!("Event sent from different thread: {:#?}", event); + panic!("Event sent from different thread: {:#?}", wrapper); } - HANDLER.deferred().push_back(event); + HANDLER.deferred().push_back(wrapper); if !HANDLER.get_in_callback() { HANDLER.set_in_callback(true); - for event in HANDLER.take_deferred() { - HANDLER.handle_nonuser_event(event); + for wrapper in HANDLER.take_deferred() { + match wrapper { + EventWrapper::StaticEvent(event) => HANDLER.handle_nonuser_event(event), + EventWrapper::EventProxy(proxy) => HANDLER.handle_event(proxy), + }; } HANDLER.set_in_callback(false); } diff --git a/src/platform_impl/macos/event.rs b/src/platform_impl/macos/event.rs index 02d6b9c266..29b3df7c10 100644 --- a/src/platform_impl/macos/event.rs +++ b/src/platform_impl/macos/event.rs @@ -6,10 +6,29 @@ use cocoa::{ }; use crate::{ - event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}, - platform_impl::platform::DEVICE_ID, + dpi::LogicalSize, + event::{ElementState, Event, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}, + platform_impl::platform::{ + util::{IdRef, Never}, + DEVICE_ID, + }, }; +#[derive(Debug)] +pub enum EventWrapper { + StaticEvent(Event<'static, Never>), + EventProxy(EventProxy), +} + +#[derive(Debug, PartialEq)] +pub enum EventProxy { + HiDpiFactorChangedProxy { + ns_window: IdRef, + suggested_size: LogicalSize, + hidpi_factor: f64, + }, +} + pub fn char_to_keycode(c: char) -> Option { // We only translate keys that are affected by keyboard layout. // @@ -244,7 +263,7 @@ pub unsafe fn modifier_event( ns_event: id, keymask: NSEventModifierFlags, was_key_pressed: bool, -) -> Option { +) -> Option> { if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask) || was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) { diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 2ad17e1d1d..e904b9bdb9 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -83,7 +83,7 @@ impl EventLoop { pub fn run(self, callback: F) -> ! where - F: 'static + FnMut(Event, &RootWindowTarget, &mut ControlFlow), + F: 'static + FnMut(Event<'_, T>, &RootWindowTarget, &mut ControlFlow), { unsafe { let _pool = NSAutoreleasePool::new(nil); @@ -98,7 +98,7 @@ impl EventLoop { pub fn run_return(&mut self, _callback: F) where - F: FnMut(Event, &RootWindowTarget, &mut ControlFlow), + F: FnMut(Event<'_, T>, &RootWindowTarget, &mut ControlFlow), { unimplemented!(); } diff --git a/src/platform_impl/macos/util/mod.rs b/src/platform_impl/macos/util/mod.rs index 238c9e5ddc..3a454cbba4 100644 --- a/src/platform_impl/macos/util/mod.rs +++ b/src/platform_impl/macos/util/mod.rs @@ -31,6 +31,7 @@ pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange { length: 0, }; +#[derive(Debug, PartialEq)] pub struct IdRef(id); impl IdRef { diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 67469b126e..789543af12 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -24,7 +24,9 @@ use objc::{ }; use crate::{ - dpi::{LogicalPosition, LogicalSize}, + dpi::{ + LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical, + }, error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::Icon, monitor::MonitorHandle as RootMonitorHandle, @@ -115,17 +117,22 @@ fn create_window( let screen = match attrs.fullscreen { Some(ref monitor_id) => { let monitor_screen = monitor_id.inner.ns_screen(); - Some(monitor_screen.unwrap_or(appkit::NSScreen::mainScreen(nil))) + Some(monitor_screen.unwrap_or(NSScreen::mainScreen(nil))) } _ => None, }; let frame = match screen { - Some(screen) => appkit::NSScreen::frame(screen), + Some(screen) => NSScreen::frame(screen), None => { - let (width, height) = attrs - .inner_size - .map(|logical| (logical.width, logical.height)) - .unwrap_or_else(|| (800.0, 600.0)); + let screen = NSScreen::mainScreen(nil); + let hidpi_factor = NSScreen::backingScaleFactor(screen) as f64; + let (width, height) = match attrs.inner_size { + Some(size) => { + let logical = size.to_logical(hidpi_factor); + (logical.width, logical.height) + } + None => (800.0, 600.0), + }; NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(width, height)) } }; @@ -273,6 +280,7 @@ pub struct UnownedWindow { decorations: AtomicBool, cursor: Weak>, cursor_visible: AtomicBool, + pub inner_rect: Option, } unsafe impl Send for UnownedWindow {} @@ -308,6 +316,8 @@ impl UnownedWindow { let input_context = unsafe { util::create_input_context(*ns_view) }; + let dpi_factor = unsafe { NSWindow::backingScaleFactor(*ns_window) as f64 }; + unsafe { if win_attribs.transparent { ns_window.setOpaque_(NO); @@ -315,13 +325,14 @@ impl UnownedWindow { } ns_app.activateIgnoringOtherApps_(YES); - - win_attribs - .min_inner_size - .map(|dim| set_min_inner_size(*ns_window, dim)); - win_attribs - .max_inner_size - .map(|dim| set_max_inner_size(*ns_window, dim)); + win_attribs.min_inner_size.map(|dim| { + let logical_dim = dim.to_logical(dpi_factor); + set_min_inner_size(*ns_window, logical_dim) + }); + win_attribs.max_inner_size.map(|dim| { + let logical_dim = dim.to_logical(dpi_factor); + set_max_inner_size(*ns_window, logical_dim) + }); use cocoa::foundation::NSArray; // register for drag and drop operations. @@ -341,6 +352,9 @@ impl UnownedWindow { let maximized = win_attribs.maximized; let visible = win_attribs.visible; let decorations = win_attribs.decorations; + let inner_rect = win_attribs + .inner_size + .map(|size| size.to_physical(dpi_factor)); let window = Arc::new(UnownedWindow { ns_view, @@ -350,6 +364,7 @@ impl UnownedWindow { decorations: AtomicBool::new(decorations), cursor, cursor_visible: AtomicBool::new(true), + inner_rect, }); let delegate = new_delegate(&window, fullscreen.is_some()); @@ -415,27 +430,31 @@ impl UnownedWindow { AppState::queue_redraw(RootWindowId(self.id())); } - pub fn outer_position(&self) -> Result { + pub fn outer_position(&self) -> Result { let frame_rect = unsafe { NSWindow::frame(*self.ns_window) }; - Ok(( + let position = LogicalPosition::new( frame_rect.origin.x as f64, util::bottom_left_to_top_left(frame_rect), - ) - .into()) + ); + let dpi_factor = self.hidpi_factor(); + Ok(position.to_physical(dpi_factor)) } - pub fn inner_position(&self) -> Result { + pub fn inner_position(&self) -> Result { let content_rect = unsafe { NSWindow::contentRectForFrameRect_(*self.ns_window, NSWindow::frame(*self.ns_window)) }; - Ok(( + let position = LogicalPosition::new( content_rect.origin.x as f64, util::bottom_left_to_top_left(content_rect), - ) - .into()) + ); + let dpi_factor = self.hidpi_factor(); + Ok(position.to_physical(dpi_factor)) } - pub fn set_outer_position(&self, position: LogicalPosition) { + pub fn set_outer_position(&self, position: Position) { + let dpi_factor = self.hidpi_factor(); + let position = position.to_logical(dpi_factor); let dummy = NSRect::new( NSPoint::new( position.x, @@ -451,35 +470,50 @@ impl UnownedWindow { } #[inline] - pub fn inner_size(&self) -> LogicalSize { + pub fn inner_size(&self) -> PhysicalSize { let view_frame = unsafe { NSView::frame(*self.ns_view) }; - (view_frame.size.width as f64, view_frame.size.height as f64).into() + let logical: LogicalSize = + (view_frame.size.width as f64, view_frame.size.height as f64).into(); + let dpi_factor = self.hidpi_factor(); + logical.to_physical(dpi_factor) } #[inline] - pub fn outer_size(&self) -> LogicalSize { + pub fn outer_size(&self) -> PhysicalSize { let view_frame = unsafe { NSWindow::frame(*self.ns_window) }; - (view_frame.size.width as f64, view_frame.size.height as f64).into() + let logical: LogicalSize = + (view_frame.size.width as f64, view_frame.size.height as f64).into(); + let dpi_factor = self.hidpi_factor(); + logical.to_physical(dpi_factor) } #[inline] - pub fn set_inner_size(&self, size: LogicalSize) { + pub fn set_inner_size(&self, size: Size) { unsafe { - util::set_content_size_async(*self.ns_window, size); + let dpi_factor = self.hidpi_factor(); + util::set_content_size_async(*self.ns_window, size.to_logical(dpi_factor)); } } - pub fn set_min_inner_size(&self, dimensions: Option) { + pub fn set_min_inner_size(&self, dimensions: Option) { unsafe { - let dimensions = dimensions.unwrap_or_else(|| (0, 0).into()); - set_min_inner_size(*self.ns_window, dimensions); + let dimensions = dimensions.unwrap_or(Logical(LogicalSize { + width: 0.0, + height: 0.0, + })); + let dpi_factor = self.hidpi_factor(); + set_min_inner_size(*self.ns_window, dimensions.to_logical(dpi_factor)); } } - pub fn set_max_inner_size(&self, dimensions: Option) { + pub fn set_max_inner_size(&self, dimensions: Option) { unsafe { - let dimensions = dimensions.unwrap_or_else(|| (!0, !0).into()); - set_max_inner_size(*self.ns_window, dimensions); + let dimensions = dimensions.unwrap_or(Logical(LogicalSize { + width: std::f32::MAX as f64, + height: std::f32::MAX as f64, + })); + let dpi_factor = self.hidpi_factor(); + set_max_inner_size(*self.ns_window, dimensions.to_logical(dpi_factor)); } } @@ -543,14 +577,14 @@ impl UnownedWindow { } #[inline] - pub fn set_cursor_position( - &self, - cursor_position: LogicalPosition, - ) -> Result<(), ExternalError> { - let window_position = self.inner_position().unwrap(); + pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), ExternalError> { + let physical_window_position = self.inner_position().unwrap(); + let dpi_factor = self.hidpi_factor(); + let window_position = physical_window_position.to_logical(dpi_factor); + let logical_cursor_position = cursor_position.to_logical(dpi_factor); let point = appkit::CGPoint { - x: (cursor_position.x + window_position.x) as CGFloat, - y: (cursor_position.y + window_position.y) as CGFloat, + x: (logical_cursor_position.x + window_position.x) as CGFloat, + y: (logical_cursor_position.y + window_position.y) as CGFloat, }; CGDisplay::warp_mouse_cursor_position(point) .map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?; @@ -730,7 +764,9 @@ impl UnownedWindow { } #[inline] - pub fn set_ime_position(&self, logical_spot: LogicalPosition) { + pub fn set_ime_position(&self, spot: Position) { + let dpi_factor = self.hidpi_factor(); + let logical_spot = spot.to_logical(dpi_factor); unsafe { view::set_ime_position( *self.ns_view, diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index c9680d892a..6bb73bbf6b 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -19,6 +19,7 @@ use crate::{ event::{Event, WindowEvent}, platform_impl::platform::{ app_state::AppState, + event::{EventProxy, EventWrapper}, util::{self, IdRef}, window::{get_window_id, UnownedWindow}, }, @@ -47,20 +48,18 @@ pub struct WindowDelegateState { impl WindowDelegateState { pub fn new(window: &Arc, initial_fullscreen: bool) -> Self { - let dpi_factor = window.hidpi_factor(); - + let hidpi_factor = window.hidpi_factor(); let mut delegate_state = WindowDelegateState { ns_window: window.ns_window.clone(), ns_view: window.ns_view.clone(), window: Arc::downgrade(&window), initial_fullscreen, previous_position: None, - previous_dpi_factor: dpi_factor, + previous_dpi_factor: hidpi_factor, }; - if dpi_factor != 1.0 { - delegate_state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor)); - delegate_state.emit_resize_event(); + if hidpi_factor != 1.0 { + delegate_state.emit_static_hidpi_factor_changed_event(); } delegate_state @@ -73,7 +72,7 @@ impl WindowDelegateState { self.window.upgrade().map(|ref window| callback(window)) } - pub fn emit_event(&mut self, event: WindowEvent) { + pub fn emit_event(&mut self, event: WindowEvent<'static>) { let event = Event::WindowEvent { window_id: WindowId(get_window_id(*self.ns_window)), event, @@ -81,14 +80,30 @@ impl WindowDelegateState { AppState::queue_event(event); } + pub fn emit_static_hidpi_factor_changed_event(&mut self) { + let hidpi_factor = self.get_hidpi_factor(); + if hidpi_factor == self.previous_dpi_factor { + return (); + }; + + self.previous_dpi_factor = hidpi_factor; + let wrapper = EventWrapper::EventProxy(EventProxy::HiDpiFactorChangedProxy { + ns_window: IdRef::retain(*self.ns_window), + suggested_size: self.view_size(), + hidpi_factor, + }); + AppState::send_event_immediately(wrapper); + } + pub fn emit_resize_event(&mut self) { - let rect = unsafe { NSView::frame(*self.ns_view) }; - let size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64); + let hidpi_factor = self.get_hidpi_factor(); + let physical_size = self.view_size().to_physical(hidpi_factor); let event = Event::WindowEvent { window_id: WindowId(get_window_id(*self.ns_window)), - event: WindowEvent::Resized(size), + event: WindowEvent::Resized(physical_size), }; - AppState::send_event_immediately(event); + let wrapper = EventWrapper::StaticEvent(event); + AppState::send_event_immediately(wrapper); } fn emit_move_event(&mut self) { @@ -101,6 +116,15 @@ impl WindowDelegateState { self.emit_event(WindowEvent::Moved((x, y).into())); } } + + fn get_hidpi_factor(&self) -> f64 { + (unsafe { NSWindow::backingScaleFactor(*self.ns_window) }) as f64 + } + + fn view_size(&self) -> LogicalSize { + let ns_size = unsafe { NSView::frame(*self.ns_view).size }; + LogicalSize::new(ns_size.width as f64, ns_size.height as f64) + } } pub fn new_delegate(window: &Arc, initial_fullscreen: bool) -> IdRef { @@ -144,10 +168,6 @@ lazy_static! { sel!(windowDidMove:), window_did_move as extern "C" fn(&Object, Sel, id), ); - decl.add_method( - sel!(windowDidChangeScreen:), - window_did_change_screen as extern "C" fn(&Object, Sel, id), - ); decl.add_method( sel!(windowDidChangeBackingProperties:), window_did_change_backing_properties as extern "C" fn(&Object, Sel, id), @@ -272,29 +292,10 @@ extern "C" fn window_did_move(this: &Object, _: Sel, _: id) { trace!("Completed `windowDidMove:`"); } -extern "C" fn window_did_change_screen(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidChangeScreen:`"); - with_state(this, |state| { - let dpi_factor = unsafe { NSWindow::backingScaleFactor(*state.ns_window) } as f64; - if state.previous_dpi_factor != dpi_factor { - state.previous_dpi_factor = dpi_factor; - state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor)); - state.emit_resize_event(); - } - }); - trace!("Completed `windowDidChangeScreen:`"); -} - -// This will always be called before `window_did_change_screen`. extern "C" fn window_did_change_backing_properties(this: &Object, _: Sel, _: id) { trace!("Triggered `windowDidChangeBackingProperties:`"); with_state(this, |state| { - let dpi_factor = unsafe { NSWindow::backingScaleFactor(*state.ns_window) } as f64; - if state.previous_dpi_factor != dpi_factor { - state.previous_dpi_factor = dpi_factor; - state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor)); - state.emit_resize_event(); - } + state.emit_static_hidpi_factor_changed_event(); }); trace!("Completed `windowDidChangeBackingProperties:`"); }