diff --git a/Cargo.toml b/Cargo.toml index f46f61a9..c2f5c9a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ cocoa = "0.19.1" core-foundation = "0.6" core-graphics = "0.17.3" dispatch = "0.1.4" -objc = "0.2.3" +objc = "0.2.6" [target.'cfg(target_os = "macos")'.dependencies.core-video-sys] version = "0.1.3" diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 955cdcc9..388a4af5 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -5,7 +5,7 @@ fn main() { use std::{collections::HashMap, sync::mpsc, thread, time::Duration}; use winit::{ - dpi::{PhysicalPosition, PhysicalSize}, + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, Fullscreen, WindowBuilder}, @@ -122,12 +122,18 @@ fn main() { ), false => WINDOW_SIZE, }), - W => window - .set_cursor_position(PhysicalPosition::new( - WINDOW_SIZE.width as f64 / 2.0, - WINDOW_SIZE.height as f64 / 2.0, - )) - .unwrap(), + W => { + if let Size::Physical(size) = WINDOW_SIZE.into() { + window + .set_cursor_position(Position::Physical( + PhysicalPosition::new( + size.width as f64 / 2.0, + size.height as f64 / 2.0, + ), + )) + .unwrap() + } + } Z => { window.set_visible(false); thread::sleep(Duration::from_secs(1)); diff --git a/src/platform_impl/macos/app.rs b/src/platform_impl/macos/app.rs index 3278cb13..fba48ae5 100644 --- a/src/platform_impl/macos/app.rs +++ b/src/platform_impl/macos/app.rs @@ -11,7 +11,7 @@ use objc::{ use crate::{ event::{DeviceEvent, ElementState, Event}, - platform_impl::platform::{app_state::AppState, util, DEVICE_ID}, + platform_impl::platform::{app_state::AppState, event::EventWrapper, util, DEVICE_ID}, }; pub struct AppClass(pub *const Class); @@ -71,32 +71,32 @@ unsafe fn maybe_dispatch_device_event(event: id) { let delta_y = event.deltaY() as f64; if delta_x != 0.0 { - events.push_back(Event::DeviceEvent { + events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { device_id: DEVICE_ID, event: DeviceEvent::Motion { axis: 0, value: delta_x, }, - }); + })); } if delta_y != 0.0 { - events.push_back(Event::DeviceEvent { + events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { device_id: DEVICE_ID, event: DeviceEvent::Motion { axis: 1, value: delta_y, }, - }); + })); } if delta_x != 0.0 || delta_y != 0.0 { - events.push_back(Event::DeviceEvent { + events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { device_id: DEVICE_ID, event: DeviceEvent::MouseMotion { delta: (delta_x, delta_y), }, - }); + })); } AppState::queue_events(events); @@ -104,26 +104,26 @@ unsafe fn maybe_dispatch_device_event(event: id) { appkit::NSLeftMouseDown | appkit::NSRightMouseDown | appkit::NSOtherMouseDown => { let mut events = VecDeque::with_capacity(1); - events.push_back(Event::DeviceEvent { + events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { device_id: DEVICE_ID, event: DeviceEvent::Button { button: event.buttonNumber() as u32, state: ElementState::Pressed, }, - }); + })); AppState::queue_events(events); } appkit::NSLeftMouseUp | appkit::NSRightMouseUp | appkit::NSOtherMouseUp => { let mut events = VecDeque::with_capacity(1); - events.push_back(Event::DeviceEvent { + events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { device_id: DEVICE_ID, event: DeviceEvent::Button { button: event.buttonNumber() as u32, state: ElementState::Released, }, - }); + })); AppState::queue_events(events); } diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 626b95fc..618be002 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -12,15 +12,21 @@ use std::{ }; use cocoa::{ - appkit::NSApp, + appkit::{NSApp, NSWindow}, base::nil, - foundation::{NSAutoreleasePool, NSString}, + foundation::{NSAutoreleasePool, NSSize, NSString}, }; use crate::{ - event::{Event, StartCause}, + 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, }; use objc::runtime::Object; @@ -29,8 +35,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. @@ -39,12 +45,13 @@ 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); } struct EventLoopHandler { - callback: Box, &RootWindowTarget, &mut ControlFlow)>, + callback: Box, &RootWindowTarget, &mut ControlFlow)>, will_exit: bool, window_target: Rc>, } @@ -59,7 +66,7 @@ impl Debug for EventLoopHandler { } impl EventHandler for EventLoopHandler { - 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 { @@ -88,7 +95,7 @@ struct Handler { control_flow_prev: Mutex, start_time: Mutex>, callback: Mutex>>, - pending_events: Mutex>>, + pending_events: Mutex>, pending_redraw: Mutex>, waker: Mutex, } @@ -97,7 +104,7 @@ 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() } @@ -105,7 +112,7 @@ impl Handler { self.pending_redraw.lock().unwrap() } - fn waker<'a>(&'a self) -> MutexGuard<'a, EventLoopWaker> { + fn waker(&self) -> MutexGuard<'_, EventLoopWaker> { self.waker.lock().unwrap() } @@ -141,7 +148,7 @@ 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()) } @@ -157,9 +164,14 @@ impl Handler { self.in_callback.store(in_callback, Ordering::Release); } - fn handle_nonuser_event(&self, event: Event) { + fn handle_nonuser_event(&self, wrapper: EventWrapper) { if let Some(ref mut callback) = *self.callback.lock().unwrap() { - callback.handle_nonuser_event(event, &mut *self.control_flow.lock().unwrap()); + match wrapper { + EventWrapper::StaticEvent(event) => { + callback.handle_nonuser_event(event, &mut *self.control_flow.lock().unwrap()) + } + EventWrapper::EventProxy(proxy) => self.handle_proxy(proxy, callback), + } } } @@ -168,6 +180,46 @@ impl Handler { callback.handle_user_events(&mut *self.control_flow.lock().unwrap()); } } + + fn handle_hidpi_factor_changed_event( + &self, + callback: &mut Box, + 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, + }, + }; + + callback.handle_nonuser_event(event, &mut *self.control_flow.lock().unwrap()); + + 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); + unsafe { NSWindow::setContentSize_(*ns_window, size) }; + } + + fn handle_proxy(&self, proxy: EventProxy, callback: &mut Box) { + match proxy { + EventProxy::HiDpiFactorChangedProxy { + ns_window, + suggested_size, + hidpi_factor, + } => self.handle_hidpi_factor_changed_event( + callback, + ns_window, + suggested_size, + hidpi_factor, + ), + } + } } pub enum AppState {} @@ -176,7 +228,7 @@ impl AppState { // This function extends lifetime of `callback` to 'static as its side effect pub unsafe fn set_callback(callback: F, window_target: Rc>) where - F: FnMut(Event, &RootWindowTarget, &mut ControlFlow), + F: FnMut(Event<'_, T>, &RootWindowTarget, &mut ControlFlow), { *HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler { // This transmute is always safe, in case it was reached through `run`, since our @@ -184,8 +236,8 @@ impl AppState { // they passed to callback will actually outlive it, some apps just can't move // everything to event loop, so this is something that they should care about. callback: mem::transmute::< - Box, &RootWindowTarget, &mut ControlFlow)>, - Box, &RootWindowTarget, &mut ControlFlow)>, + Box, &RootWindowTarget, &mut ControlFlow)>, + Box, &RootWindowTarget, &mut ControlFlow)>, >(Box::new(callback)), will_exit: false, window_target, @@ -194,7 +246,7 @@ impl AppState { pub fn exit() { HANDLER.set_in_callback(true); - HANDLER.handle_nonuser_event(Event::LoopDestroyed); + HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::LoopDestroyed)); HANDLER.set_in_callback(false); HANDLER.callback.lock().unwrap().take(); } @@ -203,7 +255,9 @@ impl AppState { HANDLER.set_ready(); HANDLER.waker().start(); HANDLER.set_in_callback(true); - HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init)); + HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents( + StartCause::Init, + ))); HANDLER.set_in_callback(false); } @@ -234,7 +288,7 @@ impl AppState { ControlFlow::Exit => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"), }; HANDLER.set_in_callback(true); - HANDLER.handle_nonuser_event(Event::NewEvents(cause)); + HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents(cause))); HANDLER.set_in_callback(false); } @@ -246,18 +300,18 @@ impl AppState { } } - pub fn queue_event(event: Event) { + pub fn queue_event(wrapper: EventWrapper) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { - panic!("Event queued from different thread: {:#?}", event); + panic!("Event queued from different thread: {:#?}", wrapper); } - HANDLER.events().push_back(event); + HANDLER.events().push_back(wrapper); } - pub fn queue_events(mut events: VecDeque>) { + pub fn queue_events(mut wrappers: VecDeque) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { - panic!("Events queued from different thread: {:#?}", events); + panic!("Events queued from different thread: {:#?}", wrappers); } - HANDLER.events().append(&mut events); + HANDLER.events().append(&mut wrappers); } pub fn cleared() { @@ -270,11 +324,13 @@ impl AppState { for event in HANDLER.take_events() { HANDLER.handle_nonuser_event(event); } - HANDLER.handle_nonuser_event(Event::MainEventsCleared); + HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::MainEventsCleared)); for window_id in HANDLER.should_redraw() { - HANDLER.handle_nonuser_event(Event::RedrawRequested(window_id)); + HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawRequested( + window_id, + ))); } - HANDLER.handle_nonuser_event(Event::RedrawEventsCleared); + HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawEventsCleared)); HANDLER.set_in_callback(false); } if HANDLER.should_exit() { diff --git a/src/platform_impl/macos/event.rs b/src/platform_impl/macos/event.rs index 4f8123d4..bbf11164 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. // @@ -256,7 +275,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 57e07b92..cc3d3928 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -84,7 +84,7 @@ impl EventLoop { pub fn run(mut self, callback: F) -> ! where - F: 'static + FnMut(Event, &RootWindowTarget, &mut ControlFlow), + F: 'static + FnMut(Event<'_, T>, &RootWindowTarget, &mut ControlFlow), { self.run_return(callback); process::exit(0); @@ -92,7 +92,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), { unsafe { let pool = NSAutoreleasePool::new(nil); diff --git a/src/platform_impl/macos/util/mod.rs b/src/platform_impl/macos/util/mod.rs index 238c9e5d..3a454cbb 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/view.rs b/src/platform_impl/macos/view.rs index 8795ea18..2914c192 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -25,7 +25,7 @@ use crate::{ app_state::AppState, event::{ char_to_keycode, check_function_keys, event_mods, get_scancode, modifier_event, - scancode_to_keycode, + scancode_to_keycode, EventWrapper, }, ffi::*, util::{self, IdRef}, @@ -512,10 +512,10 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran let mut events = VecDeque::with_capacity(characters.len()); for character in string.chars().filter(|c| !is_corporate_character(*c)) { - events.push_back(Event::WindowEvent { + events.push_back(EventWrapper::StaticEvent(Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), event: WindowEvent::ReceivedCharacter(character), - }); + })); } AppState::queue_events(events); @@ -536,10 +536,10 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) { // The `else` condition would emit the same character, but I'm keeping this here both... // 1) as a reminder for how `doCommandBySelector` works // 2) to make our use of carriage return explicit - events.push_back(Event::WindowEvent { + events.push_back(EventWrapper::StaticEvent(Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), event: WindowEvent::ReceivedCharacter('\r'), - }); + })); } else { let raw_characters = state.raw_characters.take(); if let Some(raw_characters) = raw_characters { @@ -547,10 +547,10 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) { .chars() .filter(|c| !is_corporate_character(*c)) { - events.push_back(Event::WindowEvent { + events.push_back(EventWrapper::StaticEvent(Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), event: WindowEvent::ReceivedCharacter(character), - }); + })); } } }; @@ -646,14 +646,14 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { }; let pass_along = { - AppState::queue_event(window_event); + AppState::queue_event(EventWrapper::StaticEvent(window_event)); // Emit `ReceivedCharacter` for key repeats if is_repeat && state.is_key_down { for character in characters.chars().filter(|c| !is_corporate_character(*c)) { - AppState::queue_event(Event::WindowEvent { + AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { window_id, event: WindowEvent::ReceivedCharacter(character), - }); + })); } false } else { @@ -697,7 +697,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) { }, }; - AppState::queue_event(window_event); + AppState::queue_event(EventWrapper::StaticEvent(window_event)); } trace!("Completed `keyUp`"); } @@ -747,16 +747,16 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) { } for event in events { - AppState::queue_event(Event::WindowEvent { + AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), event, - }); + })); } - AppState::queue_event(Event::DeviceEvent { + AppState::queue_event(EventWrapper::StaticEvent(Event::DeviceEvent { device_id: DEVICE_ID, event: DeviceEvent::ModifiersChanged(state.modifiers), - }); + })); } trace!("Completed `flagsChanged`"); } @@ -811,7 +811,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { }, }; - AppState::queue_event(window_event); + AppState::queue_event(EventWrapper::StaticEvent(window_event)); } trace!("Completed `cancelOperation`"); } @@ -831,7 +831,7 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem }, }; - AppState::queue_event(window_event); + AppState::queue_event(EventWrapper::StaticEvent(window_event)); } } @@ -892,7 +892,7 @@ fn mouse_motion(this: &Object, event: id) { }, }; - AppState::queue_event(window_event); + AppState::queue_event(EventWrapper::StaticEvent(window_event)); } } @@ -944,8 +944,8 @@ extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) { } }; - AppState::queue_event(enter_event); - AppState::queue_event(move_event); + AppState::queue_event(EventWrapper::StaticEvent(enter_event)); + AppState::queue_event(EventWrapper::StaticEvent(move_event)); } trace!("Completed `mouseEntered`"); } @@ -963,7 +963,7 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) { }, }; - AppState::queue_event(window_event); + AppState::queue_event(EventWrapper::StaticEvent(window_event)); } trace!("Completed `mouseExited`"); } @@ -1005,8 +1005,8 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { }, }; - AppState::queue_event(device_event); - AppState::queue_event(window_event); + AppState::queue_event(EventWrapper::StaticEvent(device_event)); + AppState::queue_event(EventWrapper::StaticEvent(window_event)); } trace!("Completed `scrollWheel`"); } @@ -1029,7 +1029,7 @@ extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) { }, }; - AppState::queue_event(window_event); + AppState::queue_event(EventWrapper::StaticEvent(window_event)); } trace!("Completed `pressureChangeWithEvent`"); } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index ff66f82a..4ed62756 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -10,7 +10,9 @@ use std::{ }; 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, VideoMode as RootVideoMode}, @@ -130,12 +132,17 @@ fn create_window( None => 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)) } }; @@ -292,6 +299,7 @@ pub struct UnownedWindow { pub shared_state: Arc>, decorations: AtomicBool, cursor_state: Weak>, + pub inner_rect: Option, } unsafe impl Send for UnownedWindow {} @@ -328,6 +336,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); @@ -335,13 +345,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. @@ -361,6 +372,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, @@ -369,6 +383,7 @@ impl UnownedWindow { shared_state: Arc::new(Mutex::new(win_attribs.into())), decorations: AtomicBool::new(decorations), cursor_state, + inner_rect, }); let delegate = new_delegate(&window, fullscreen.is_some()); @@ -425,27 +440,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, @@ -461,35 +480,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)); } } @@ -554,14 +588,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))))?; @@ -894,7 +928,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 ecdd4eef..d4ac37b5 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,17 +72,34 @@ 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, }; - AppState::queue_event(event); + AppState::queue_event(EventWrapper::StaticEvent(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::queue_event(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 logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64); + let size = logical_size.to_physical(hidpi_factor); self.emit_event(WindowEvent::Resized(size)); } @@ -97,6 +113,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 { @@ -140,10 +165,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), @@ -277,29 +298,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:`"); }