macOS: Dpi overhaul (#997) (and rebase changes)

* WIP - Make EL2 DPI changes and implement on Windows (#895)

* Modify DPI API publicly and on Windows

* Add generic Position and make dpi creation functions const

* Make examples work

* Fix fullscreen windows not appearing

* Replace Logical coordinates in window events with Physical coordinates

* Update HiDpiFactorChanged

* Document to_static

* fix app_state errors

* fixes hidpi related errors in window_delegate

* fix bad merge

* dpi_factor edits in window_delegate

* fixes type and lifetime errors in window and window_delegate

* applies fmt

* complies with @aleksijuvani requested changes

* modifies Handler lifetimes

* fixes lifetime isues, adds propper handling for HiDpiChanged

* applies fmt

* restore original lifetimes

* solution is somewhere out there

* applies fmt

* pass as references

* resolves issue with HANDLER

* crate visible type error

* fixes visibility issues

* applies fmt

* deals with warnings

* simplifies new_inner_size setting algorthm

* moves proxy instead of referencing it and removes double deref from proxy.ns_window

* makes @Osspial tests (https://github.com/rust-windowing/winit/pull/997\#discussion_r301852354) pass

* complies with @aleksijuvani suggested changes

* makes max window size std::f32::MAX

Changes from rebasing:

* fixes compile errors

* applies fmt

* reimplements HiDpiFactorChanged after #1173 merge

* uses EventWrappers
This commit is contained in:
Vladimir Bogaevsky 2020-01-04 01:32:34 -05:00 committed by Osspial
parent 7b43b0bc94
commit 077ee4d851
10 changed files with 273 additions and 153 deletions

View file

@ -42,7 +42,7 @@ cocoa = "0.19.1"
core-foundation = "0.6" core-foundation = "0.6"
core-graphics = "0.17.3" core-graphics = "0.17.3"
dispatch = "0.1.4" dispatch = "0.1.4"
objc = "0.2.3" objc = "0.2.6"
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys] [target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
version = "0.1.3" version = "0.1.3"

View file

@ -5,7 +5,7 @@ fn main() {
use std::{collections::HashMap, sync::mpsc, thread, time::Duration}; use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
use winit::{ use winit::{
dpi::{PhysicalPosition, PhysicalSize}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop}, event_loop::{ControlFlow, EventLoop},
window::{CursorIcon, Fullscreen, WindowBuilder}, window::{CursorIcon, Fullscreen, WindowBuilder},
@ -122,12 +122,18 @@ fn main() {
), ),
false => WINDOW_SIZE, false => WINDOW_SIZE,
}), }),
W => window W => {
.set_cursor_position(PhysicalPosition::new( if let Size::Physical(size) = WINDOW_SIZE.into() {
WINDOW_SIZE.width as f64 / 2.0, window
WINDOW_SIZE.height as f64 / 2.0, .set_cursor_position(Position::Physical(
)) PhysicalPosition::new(
.unwrap(), size.width as f64 / 2.0,
size.height as f64 / 2.0,
),
))
.unwrap()
}
}
Z => { Z => {
window.set_visible(false); window.set_visible(false);
thread::sleep(Duration::from_secs(1)); thread::sleep(Duration::from_secs(1));

View file

@ -11,7 +11,7 @@ use objc::{
use crate::{ use crate::{
event::{DeviceEvent, ElementState, Event}, 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); 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; let delta_y = event.deltaY() as f64;
if delta_x != 0.0 { if delta_x != 0.0 {
events.push_back(Event::DeviceEvent { events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event: DeviceEvent::Motion { event: DeviceEvent::Motion {
axis: 0, axis: 0,
value: delta_x, value: delta_x,
}, },
}); }));
} }
if delta_y != 0.0 { if delta_y != 0.0 {
events.push_back(Event::DeviceEvent { events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event: DeviceEvent::Motion { event: DeviceEvent::Motion {
axis: 1, axis: 1,
value: delta_y, value: delta_y,
}, },
}); }));
} }
if delta_x != 0.0 || delta_y != 0.0 { 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, device_id: DEVICE_ID,
event: DeviceEvent::MouseMotion { event: DeviceEvent::MouseMotion {
delta: (delta_x, delta_y), delta: (delta_x, delta_y),
}, },
}); }));
} }
AppState::queue_events(events); AppState::queue_events(events);
@ -104,26 +104,26 @@ unsafe fn maybe_dispatch_device_event(event: id) {
appkit::NSLeftMouseDown | appkit::NSRightMouseDown | appkit::NSOtherMouseDown => { appkit::NSLeftMouseDown | appkit::NSRightMouseDown | appkit::NSOtherMouseDown => {
let mut events = VecDeque::with_capacity(1); let mut events = VecDeque::with_capacity(1);
events.push_back(Event::DeviceEvent { events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event: DeviceEvent::Button { event: DeviceEvent::Button {
button: event.buttonNumber() as u32, button: event.buttonNumber() as u32,
state: ElementState::Pressed, state: ElementState::Pressed,
}, },
}); }));
AppState::queue_events(events); AppState::queue_events(events);
} }
appkit::NSLeftMouseUp | appkit::NSRightMouseUp | appkit::NSOtherMouseUp => { appkit::NSLeftMouseUp | appkit::NSRightMouseUp | appkit::NSOtherMouseUp => {
let mut events = VecDeque::with_capacity(1); let mut events = VecDeque::with_capacity(1);
events.push_back(Event::DeviceEvent { events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event: DeviceEvent::Button { event: DeviceEvent::Button {
button: event.buttonNumber() as u32, button: event.buttonNumber() as u32,
state: ElementState::Released, state: ElementState::Released,
}, },
}); }));
AppState::queue_events(events); AppState::queue_events(events);
} }

View file

@ -12,15 +12,21 @@ use std::{
}; };
use cocoa::{ use cocoa::{
appkit::NSApp, appkit::{NSApp, NSWindow},
base::nil, base::nil,
foundation::{NSAutoreleasePool, NSString}, foundation::{NSAutoreleasePool, NSSize, NSString},
}; };
use crate::{ use crate::{
event::{Event, StartCause}, dpi::LogicalSize,
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget}, 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, window::WindowId,
}; };
use objc::runtime::Object; use objc::runtime::Object;
@ -29,8 +35,8 @@ lazy_static! {
static ref HANDLER: Handler = Default::default(); static ref HANDLER: Handler = Default::default();
} }
impl Event<Never> { impl<'a, Never> Event<'a, Never> {
fn userify<T: 'static>(self) -> Event<T> { fn userify<T: 'static>(self) -> Event<'a, T> {
self.map_nonuser_event() self.map_nonuser_event()
// `Never` can't be constructed, so the `UserEvent` variant can't // `Never` can't be constructed, so the `UserEvent` variant can't
// be present here. // be present here.
@ -39,12 +45,13 @@ impl Event<Never> {
} }
pub trait EventHandler: Debug { pub trait EventHandler: Debug {
fn handle_nonuser_event(&mut self, event: Event<Never>, 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); fn handle_user_events(&mut self, control_flow: &mut ControlFlow);
} }
struct EventLoopHandler<T: 'static> { struct EventLoopHandler<T: 'static> {
callback: Box<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>, callback: Box<dyn FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow)>,
will_exit: bool, will_exit: bool,
window_target: Rc<RootWindowTarget<T>>, window_target: Rc<RootWindowTarget<T>>,
} }
@ -59,7 +66,7 @@ impl<T> Debug for EventLoopHandler<T> {
} }
impl<T> EventHandler for EventLoopHandler<T> { impl<T> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>, 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.callback)(event.userify(), &self.window_target, control_flow);
self.will_exit |= *control_flow == ControlFlow::Exit; self.will_exit |= *control_flow == ControlFlow::Exit;
if self.will_exit { if self.will_exit {
@ -88,7 +95,7 @@ struct Handler {
control_flow_prev: Mutex<ControlFlow>, control_flow_prev: Mutex<ControlFlow>,
start_time: Mutex<Option<Instant>>, start_time: Mutex<Option<Instant>>,
callback: Mutex<Option<Box<dyn EventHandler>>>, callback: Mutex<Option<Box<dyn EventHandler>>>,
pending_events: Mutex<VecDeque<Event<Never>>>, pending_events: Mutex<VecDeque<EventWrapper>>,
pending_redraw: Mutex<Vec<WindowId>>, pending_redraw: Mutex<Vec<WindowId>>,
waker: Mutex<EventLoopWaker>, waker: Mutex<EventLoopWaker>,
} }
@ -97,7 +104,7 @@ unsafe impl Send for Handler {}
unsafe impl Sync for Handler {} unsafe impl Sync for Handler {}
impl Handler { impl Handler {
fn events<'a>(&'a self) -> MutexGuard<'a, VecDeque<Event<Never>>> { fn events(&self) -> MutexGuard<'_, VecDeque<EventWrapper>> {
self.pending_events.lock().unwrap() self.pending_events.lock().unwrap()
} }
@ -105,7 +112,7 @@ impl Handler {
self.pending_redraw.lock().unwrap() self.pending_redraw.lock().unwrap()
} }
fn waker<'a>(&'a self) -> MutexGuard<'a, EventLoopWaker> { fn waker(&self) -> MutexGuard<'_, EventLoopWaker> {
self.waker.lock().unwrap() self.waker.lock().unwrap()
} }
@ -141,7 +148,7 @@ impl Handler {
*self.start_time.lock().unwrap() = Some(Instant::now()); *self.start_time.lock().unwrap() = Some(Instant::now());
} }
fn take_events(&self) -> VecDeque<Event<Never>> { fn take_events(&self) -> VecDeque<EventWrapper> {
mem::replace(&mut *self.events(), Default::default()) mem::replace(&mut *self.events(), Default::default())
} }
@ -157,9 +164,14 @@ impl Handler {
self.in_callback.store(in_callback, Ordering::Release); self.in_callback.store(in_callback, Ordering::Release);
} }
fn handle_nonuser_event(&self, event: Event<Never>) { fn handle_nonuser_event(&self, wrapper: EventWrapper) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() { 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()); callback.handle_user_events(&mut *self.control_flow.lock().unwrap());
} }
} }
fn handle_hidpi_factor_changed_event(
&self,
callback: &mut Box<dyn EventHandler + 'static>,
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<dyn EventHandler + 'static>) {
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 {} pub enum AppState {}
@ -176,7 +228,7 @@ impl AppState {
// This function extends lifetime of `callback` to 'static as its side effect // This function extends lifetime of `callback` to 'static as its side effect
pub unsafe fn set_callback<F, T>(callback: F, window_target: Rc<RootWindowTarget<T>>) pub unsafe fn set_callback<F, T>(callback: F, window_target: Rc<RootWindowTarget<T>>)
where where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
{ {
*HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler { *HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler {
// This transmute is always safe, in case it was reached through `run`, since our // 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 // 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. // everything to event loop, so this is something that they should care about.
callback: mem::transmute::< callback: mem::transmute::<
Box<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>, Box<dyn FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow)>,
Box<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>, Box<dyn FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow)>,
>(Box::new(callback)), >(Box::new(callback)),
will_exit: false, will_exit: false,
window_target, window_target,
@ -194,7 +246,7 @@ impl AppState {
pub fn exit() { pub fn exit() {
HANDLER.set_in_callback(true); 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.set_in_callback(false);
HANDLER.callback.lock().unwrap().take(); HANDLER.callback.lock().unwrap().take();
} }
@ -203,7 +255,9 @@ impl AppState {
HANDLER.set_ready(); HANDLER.set_ready();
HANDLER.waker().start(); HANDLER.waker().start();
HANDLER.set_in_callback(true); 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); HANDLER.set_in_callback(false);
} }
@ -234,7 +288,7 @@ impl AppState {
ControlFlow::Exit => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"), ControlFlow::Exit => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"),
}; };
HANDLER.set_in_callback(true); 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); HANDLER.set_in_callback(false);
} }
@ -246,18 +300,18 @@ impl AppState {
} }
} }
pub fn queue_event(event: Event<Never>) { pub fn queue_event(wrapper: EventWrapper) {
if !unsafe { msg_send![class!(NSThread), isMainThread] } { 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<Event<Never>>) { pub fn queue_events(mut wrappers: VecDeque<EventWrapper>) {
if !unsafe { msg_send![class!(NSThread), isMainThread] } { 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() { pub fn cleared() {
@ -270,11 +324,13 @@ impl AppState {
for event in HANDLER.take_events() { for event in HANDLER.take_events() {
HANDLER.handle_nonuser_event(event); 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() { 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); HANDLER.set_in_callback(false);
} }
if HANDLER.should_exit() { if HANDLER.should_exit() {

View file

@ -6,10 +6,29 @@ use cocoa::{
}; };
use crate::{ use crate::{
event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}, dpi::LogicalSize,
platform_impl::platform::DEVICE_ID, 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<VirtualKeyCode> { pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
// We only translate keys that are affected by keyboard layout. // We only translate keys that are affected by keyboard layout.
// //
@ -256,7 +275,7 @@ pub unsafe fn modifier_event(
ns_event: id, ns_event: id,
keymask: NSEventModifierFlags, keymask: NSEventModifierFlags,
was_key_pressed: bool, was_key_pressed: bool,
) -> Option<WindowEvent> { ) -> Option<WindowEvent<'static>> {
if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask) if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|| was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) || was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask)
{ {

View file

@ -84,7 +84,7 @@ impl<T> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> ! pub fn run<F>(mut self, callback: F) -> !
where where
F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow), F: 'static + FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
{ {
self.run_return(callback); self.run_return(callback);
process::exit(0); process::exit(0);
@ -92,7 +92,7 @@ impl<T> EventLoop<T> {
pub fn run_return<F>(&mut self, callback: F) pub fn run_return<F>(&mut self, callback: F)
where where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
{ {
unsafe { unsafe {
let pool = NSAutoreleasePool::new(nil); let pool = NSAutoreleasePool::new(nil);

View file

@ -31,6 +31,7 @@ pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
length: 0, length: 0,
}; };
#[derive(Debug, PartialEq)]
pub struct IdRef(id); pub struct IdRef(id);
impl IdRef { impl IdRef {

View file

@ -25,7 +25,7 @@ use crate::{
app_state::AppState, app_state::AppState,
event::{ event::{
char_to_keycode, check_function_keys, event_mods, get_scancode, modifier_event, char_to_keycode, check_function_keys, event_mods, get_scancode, modifier_event,
scancode_to_keycode, scancode_to_keycode, EventWrapper,
}, },
ffi::*, ffi::*,
util::{self, IdRef}, 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()); let mut events = VecDeque::with_capacity(characters.len());
for character in string.chars().filter(|c| !is_corporate_character(*c)) { 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)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character), event: WindowEvent::ReceivedCharacter(character),
}); }));
} }
AppState::queue_events(events); 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... // The `else` condition would emit the same character, but I'm keeping this here both...
// 1) as a reminder for how `doCommandBySelector` works // 1) as a reminder for how `doCommandBySelector` works
// 2) to make our use of carriage return explicit // 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)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter('\r'), event: WindowEvent::ReceivedCharacter('\r'),
}); }));
} else { } else {
let raw_characters = state.raw_characters.take(); let raw_characters = state.raw_characters.take();
if let Some(raw_characters) = raw_characters { 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() .chars()
.filter(|c| !is_corporate_character(*c)) .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)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character), event: WindowEvent::ReceivedCharacter(character),
}); }));
} }
} }
}; };
@ -646,14 +646,14 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
}; };
let pass_along = { let pass_along = {
AppState::queue_event(window_event); AppState::queue_event(EventWrapper::StaticEvent(window_event));
// Emit `ReceivedCharacter` for key repeats // Emit `ReceivedCharacter` for key repeats
if is_repeat && state.is_key_down { if is_repeat && state.is_key_down {
for character in characters.chars().filter(|c| !is_corporate_character(*c)) { 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, window_id,
event: WindowEvent::ReceivedCharacter(character), event: WindowEvent::ReceivedCharacter(character),
}); }));
} }
false false
} else { } 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`"); trace!("Completed `keyUp`");
} }
@ -747,16 +747,16 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) {
} }
for event in events { 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)), window_id: WindowId(get_window_id(state.ns_window)),
event, event,
}); }));
} }
AppState::queue_event(Event::DeviceEvent { AppState::queue_event(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event: DeviceEvent::ModifiersChanged(state.modifiers), event: DeviceEvent::ModifiersChanged(state.modifiers),
}); }));
} }
trace!("Completed `flagsChanged`"); 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`"); 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(EventWrapper::StaticEvent(enter_event));
AppState::queue_event(move_event); AppState::queue_event(EventWrapper::StaticEvent(move_event));
} }
trace!("Completed `mouseEntered`"); 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`"); 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(EventWrapper::StaticEvent(device_event));
AppState::queue_event(window_event); AppState::queue_event(EventWrapper::StaticEvent(window_event));
} }
trace!("Completed `scrollWheel`"); 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`"); trace!("Completed `pressureChangeWithEvent`");
} }

View file

@ -10,7 +10,9 @@ use std::{
}; };
use crate::{ use crate::{
dpi::{LogicalPosition, LogicalSize}, dpi::{
LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical,
},
error::{ExternalError, NotSupportedError, OsError as RootOsError}, error::{ExternalError, NotSupportedError, OsError as RootOsError},
icon::Icon, icon::Icon,
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
@ -130,12 +132,17 @@ fn create_window(
None => None, None => None,
}; };
let frame = match screen { let frame = match screen {
Some(screen) => appkit::NSScreen::frame(screen), Some(screen) => NSScreen::frame(screen),
None => { None => {
let (width, height) = attrs let screen = NSScreen::mainScreen(nil);
.inner_size let hidpi_factor = NSScreen::backingScaleFactor(screen) as f64;
.map(|logical| (logical.width, logical.height)) let (width, height) = match attrs.inner_size {
.unwrap_or_else(|| (800.0, 600.0)); 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)) NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(width, height))
} }
}; };
@ -292,6 +299,7 @@ pub struct UnownedWindow {
pub shared_state: Arc<Mutex<SharedState>>, pub shared_state: Arc<Mutex<SharedState>>,
decorations: AtomicBool, decorations: AtomicBool,
cursor_state: Weak<Mutex<CursorState>>, cursor_state: Weak<Mutex<CursorState>>,
pub inner_rect: Option<PhysicalSize>,
} }
unsafe impl Send for UnownedWindow {} unsafe impl Send for UnownedWindow {}
@ -328,6 +336,8 @@ impl UnownedWindow {
let input_context = unsafe { util::create_input_context(*ns_view) }; let input_context = unsafe { util::create_input_context(*ns_view) };
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*ns_window) as f64 };
unsafe { unsafe {
if win_attribs.transparent { if win_attribs.transparent {
ns_window.setOpaque_(NO); ns_window.setOpaque_(NO);
@ -335,13 +345,14 @@ impl UnownedWindow {
} }
ns_app.activateIgnoringOtherApps_(YES); ns_app.activateIgnoringOtherApps_(YES);
win_attribs.min_inner_size.map(|dim| {
win_attribs let logical_dim = dim.to_logical(dpi_factor);
.min_inner_size set_min_inner_size(*ns_window, logical_dim)
.map(|dim| set_min_inner_size(*ns_window, dim)); });
win_attribs win_attribs.max_inner_size.map(|dim| {
.max_inner_size let logical_dim = dim.to_logical(dpi_factor);
.map(|dim| set_max_inner_size(*ns_window, dim)); set_max_inner_size(*ns_window, logical_dim)
});
use cocoa::foundation::NSArray; use cocoa::foundation::NSArray;
// register for drag and drop operations. // register for drag and drop operations.
@ -361,6 +372,9 @@ impl UnownedWindow {
let maximized = win_attribs.maximized; let maximized = win_attribs.maximized;
let visible = win_attribs.visible; let visible = win_attribs.visible;
let decorations = win_attribs.decorations; let decorations = win_attribs.decorations;
let inner_rect = win_attribs
.inner_size
.map(|size| size.to_physical(dpi_factor));
let window = Arc::new(UnownedWindow { let window = Arc::new(UnownedWindow {
ns_view, ns_view,
@ -369,6 +383,7 @@ impl UnownedWindow {
shared_state: Arc::new(Mutex::new(win_attribs.into())), shared_state: Arc::new(Mutex::new(win_attribs.into())),
decorations: AtomicBool::new(decorations), decorations: AtomicBool::new(decorations),
cursor_state, cursor_state,
inner_rect,
}); });
let delegate = new_delegate(&window, fullscreen.is_some()); let delegate = new_delegate(&window, fullscreen.is_some());
@ -425,27 +440,31 @@ impl UnownedWindow {
AppState::queue_redraw(RootWindowId(self.id())); AppState::queue_redraw(RootWindowId(self.id()));
} }
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> { pub fn outer_position(&self) -> Result<PhysicalPosition, NotSupportedError> {
let frame_rect = unsafe { NSWindow::frame(*self.ns_window) }; let frame_rect = unsafe { NSWindow::frame(*self.ns_window) };
Ok(( let position = LogicalPosition::new(
frame_rect.origin.x as f64, frame_rect.origin.x as f64,
util::bottom_left_to_top_left(frame_rect), 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<LogicalPosition, NotSupportedError> { pub fn inner_position(&self) -> Result<PhysicalPosition, NotSupportedError> {
let content_rect = unsafe { let content_rect = unsafe {
NSWindow::contentRectForFrameRect_(*self.ns_window, NSWindow::frame(*self.ns_window)) NSWindow::contentRectForFrameRect_(*self.ns_window, NSWindow::frame(*self.ns_window))
}; };
Ok(( let position = LogicalPosition::new(
content_rect.origin.x as f64, content_rect.origin.x as f64,
util::bottom_left_to_top_left(content_rect), 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( let dummy = NSRect::new(
NSPoint::new( NSPoint::new(
position.x, position.x,
@ -461,35 +480,50 @@ impl UnownedWindow {
} }
#[inline] #[inline]
pub fn inner_size(&self) -> LogicalSize { pub fn inner_size(&self) -> PhysicalSize {
let view_frame = unsafe { NSView::frame(*self.ns_view) }; 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] #[inline]
pub fn outer_size(&self) -> LogicalSize { pub fn outer_size(&self) -> PhysicalSize {
let view_frame = unsafe { NSWindow::frame(*self.ns_window) }; 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] #[inline]
pub fn set_inner_size(&self, size: LogicalSize) { pub fn set_inner_size(&self, size: Size) {
unsafe { 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<LogicalSize>) { pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
unsafe { unsafe {
let dimensions = dimensions.unwrap_or_else(|| (0, 0).into()); let dimensions = dimensions.unwrap_or(Logical(LogicalSize {
set_min_inner_size(*self.ns_window, dimensions); 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<LogicalSize>) { pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
unsafe { unsafe {
let dimensions = dimensions.unwrap_or_else(|| (!0, !0).into()); let dimensions = dimensions.unwrap_or(Logical(LogicalSize {
set_max_inner_size(*self.ns_window, dimensions); 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] #[inline]
pub fn set_cursor_position( pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), ExternalError> {
&self, let physical_window_position = self.inner_position().unwrap();
cursor_position: LogicalPosition, let dpi_factor = self.hidpi_factor();
) -> Result<(), ExternalError> { let window_position = physical_window_position.to_logical(dpi_factor);
let window_position = self.inner_position().unwrap(); let logical_cursor_position = cursor_position.to_logical(dpi_factor);
let point = appkit::CGPoint { let point = appkit::CGPoint {
x: (cursor_position.x + window_position.x) as CGFloat, x: (logical_cursor_position.x + window_position.x) as CGFloat,
y: (cursor_position.y + window_position.y) as CGFloat, y: (logical_cursor_position.y + window_position.y) as CGFloat,
}; };
CGDisplay::warp_mouse_cursor_position(point) CGDisplay::warp_mouse_cursor_position(point)
.map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?; .map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?;
@ -894,7 +928,9 @@ impl UnownedWindow {
} }
#[inline] #[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 { unsafe {
view::set_ime_position( view::set_ime_position(
*self.ns_view, *self.ns_view,

View file

@ -19,6 +19,7 @@ use crate::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
platform_impl::platform::{ platform_impl::platform::{
app_state::AppState, app_state::AppState,
event::{EventProxy, EventWrapper},
util::{self, IdRef}, util::{self, IdRef},
window::{get_window_id, UnownedWindow}, window::{get_window_id, UnownedWindow},
}, },
@ -47,20 +48,18 @@ pub struct WindowDelegateState {
impl WindowDelegateState { impl WindowDelegateState {
pub fn new(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Self { pub fn new(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Self {
let dpi_factor = window.hidpi_factor(); let hidpi_factor = window.hidpi_factor();
let mut delegate_state = WindowDelegateState { let mut delegate_state = WindowDelegateState {
ns_window: window.ns_window.clone(), ns_window: window.ns_window.clone(),
ns_view: window.ns_view.clone(), ns_view: window.ns_view.clone(),
window: Arc::downgrade(&window), window: Arc::downgrade(&window),
initial_fullscreen, initial_fullscreen,
previous_position: None, previous_position: None,
previous_dpi_factor: dpi_factor, previous_dpi_factor: hidpi_factor,
}; };
if dpi_factor != 1.0 { if hidpi_factor != 1.0 {
delegate_state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor)); delegate_state.emit_static_hidpi_factor_changed_event();
delegate_state.emit_resize_event();
} }
delegate_state delegate_state
@ -73,17 +72,34 @@ impl WindowDelegateState {
self.window.upgrade().map(|ref window| callback(window)) 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 { let event = Event::WindowEvent {
window_id: WindowId(get_window_id(*self.ns_window)), window_id: WindowId(get_window_id(*self.ns_window)),
event, 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) { pub fn emit_resize_event(&mut self) {
let rect = unsafe { NSView::frame(*self.ns_view) }; 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)); self.emit_event(WindowEvent::Resized(size));
} }
@ -97,6 +113,15 @@ impl WindowDelegateState {
self.emit_event(WindowEvent::Moved((x, y).into())); 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<UnownedWindow>, initial_fullscreen: bool) -> IdRef { pub fn new_delegate(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> IdRef {
@ -140,10 +165,6 @@ lazy_static! {
sel!(windowDidMove:), sel!(windowDidMove:),
window_did_move as extern "C" fn(&Object, Sel, id), 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( decl.add_method(
sel!(windowDidChangeBackingProperties:), sel!(windowDidChangeBackingProperties:),
window_did_change_backing_properties as extern "C" fn(&Object, Sel, id), 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:`"); 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) { extern "C" fn window_did_change_backing_properties(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidChangeBackingProperties:`"); trace!("Triggered `windowDidChangeBackingProperties:`");
with_state(this, |state| { with_state(this, |state| {
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*state.ns_window) } as f64; state.emit_static_hidpi_factor_changed_event();
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 `windowDidChangeBackingProperties:`"); trace!("Completed `windowDidChangeBackingProperties:`");
} }