2019-06-21 11:33:15 -04:00
|
|
|
use std::{
|
|
|
|
|
f64,
|
|
|
|
|
os::raw::c_void,
|
2020-06-09 23:46:33 +02:00
|
|
|
sync::{atomic::Ordering, Arc, Weak},
|
2019-06-21 11:33:15 -04:00
|
|
|
};
|
2019-05-01 17:03:30 -06:00
|
|
|
|
|
|
|
|
use cocoa::{
|
2019-07-29 21:16:14 +03:00
|
|
|
appkit::{self, NSApplicationPresentationOptions, NSView, NSWindow},
|
2019-06-21 11:33:15 -04:00
|
|
|
base::{id, nil},
|
2019-07-29 21:16:14 +03:00
|
|
|
foundation::{NSAutoreleasePool, NSUInteger},
|
2019-05-01 17:03:30 -06:00
|
|
|
};
|
2019-06-21 11:33:15 -04:00
|
|
|
use objc::{
|
|
|
|
|
declare::ClassDecl,
|
|
|
|
|
runtime::{Class, Object, Sel, BOOL, NO, YES},
|
|
|
|
|
};
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::{
|
2020-09-23 18:54:53 +08:00
|
|
|
dpi::{LogicalPosition, LogicalSize},
|
Move `ModifiersChanged` variant to `WindowEvent` (#1381)
* Move `ModifiersChanged` variant to `WindowEvent`
* macos: Fix flags_changed for ModifiersChanged variant move
I haven't look too deep at what this does internally, but at least
cargo-check is fully happy now. :)
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* macos: Fire a ModifiersChanged event on window_did_resign_key
From debugging, I determined that macOS' emission of a flagsChanged
around window switching is inconsistent. It is fair to assume, I think,
that when the user switches windows, they do not expect their former
modifiers state to remain effective; so I think it's best to clear that
state by sending a ModifiersChanged(ModifiersState::empty()).
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* windows: Fix build
I don't know enough about the code to implement the fix as it is done on
this branch, but this commit at least fixes the build.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* windows: Send ModifiersChanged(ModifiersState::empty) on KILLFOCUS
Very similar to the changes made in [1], as focus is lost, send an event
to the window indicating that the modifiers have been released.
It's unclear to me (without a Windows device to test this on) whether
this is necessary, but it certainly ensures that unfocused windows will
have at least received this event, which is an improvement.
[1]: f79f21641a31da3e4039d41be89047cdcc6028f7
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* macos: Add a hook to update stale modifiers
Sometimes, `ViewState` and `event` might have different values for their
stored `modifiers` flags. These are internally stored as a bitmask in
the latter and an enum in the former.
We can check to see if they differ, and if they do, automatically
dispatch an event to update consumers of modifier state as well as the
stored `state.modifiers`. That's what the hook does.
This hook is then called in the key_down, mouse_entered, mouse_exited,
mouse_click, scroll_wheel, and pressure_change_with_event callbacks,
which each will contain updated modifiers.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* Only call event_mods once when determining whether to update state
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* flags_changed: Memoize window_id collection
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* window_did_resign_key: Remove synthetic ModifiersChanged event
We no longer need to emit this event, since we are checking the state of
our modifiers before emitting most other events.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* mouse_motion: Add a call to update_potentially_stale_modifiers
Now, cover all events (that I can think of, at least) where stale
modifiers might affect how user programs behave. Effectively, every
human-interface event (keypress, mouse click, keydown, etc.) will cause
a ModifiersChanged event to be fired if something has changed.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* key_up: Add a call to update_potentially_stale_modifiers
We also want to make sure modifiers state is synchronized here, too.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* mouse_motion: Remove update_potentially_stale_modifiers invocation
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* Retry CI
* ViewState: Promote visibility of modifiers to the macos impl
This is so that we can interact with the ViewState directly from the
WindowDelegate.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* window_delegate: Synthetically set modifiers state to empty on resignKey
This logic is implemented similarly on other platforms, so we wish to
regain parity here. Originally this behavior was implemented to always
fire an event with ModifiersState::empty(), but that was not the best as
it was not necessarily correct and could be a duplicate event.
This solution is perhaps the most elegant possible to implement the
desired behavior of sending a synthetic empty modifiers event when a
window loses focus, trading some safety for interoperation between the
NSWindowDelegate and the NSView (as the objc runtime must now be
consulted in order to acquire access to the ViewState which is "owned"
by the NSView).
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* Check for modifiers change in window events
* Fix modifier changed on macOS
Since the `mouse_entered` function was generating a mouse motion, which
updates the modifier state, a modifiers changed event was incorrectly
generated.
The updating of the modifier state has also been changed to make sure it
consistently happens before events that have a modifier state attached
to it, without happening on any other event.
This of course means that no `CursorMoved` event is generated anymore
when the user enters the window without it being focused, however I'd
say that is consistent with how winit should behave.
* Fix unused variable warning
* Move changelog entry into `Unreleased` section
Co-authored-by: Freya Gentz <zegentzy@protonmail.com>
Co-authored-by: Kristofer Rye <kristofer.rye@gmail.com>
Co-authored-by: Christian Duerr <contact@christianduerr.com>
2020-03-06 15:43:55 -07:00
|
|
|
event::{Event, ModifiersState, WindowEvent},
|
2019-06-21 11:33:15 -04:00
|
|
|
platform_impl::platform::{
|
|
|
|
|
app_state::AppState,
|
2020-06-09 23:46:33 +02:00
|
|
|
app_state::INTERRUPT_EVENT_LOOP_EXIT,
|
2020-01-04 01:32:34 -05:00
|
|
|
event::{EventProxy, EventWrapper},
|
2019-06-21 11:33:15 -04:00
|
|
|
util::{self, IdRef},
|
Move `ModifiersChanged` variant to `WindowEvent` (#1381)
* Move `ModifiersChanged` variant to `WindowEvent`
* macos: Fix flags_changed for ModifiersChanged variant move
I haven't look too deep at what this does internally, but at least
cargo-check is fully happy now. :)
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* macos: Fire a ModifiersChanged event on window_did_resign_key
From debugging, I determined that macOS' emission of a flagsChanged
around window switching is inconsistent. It is fair to assume, I think,
that when the user switches windows, they do not expect their former
modifiers state to remain effective; so I think it's best to clear that
state by sending a ModifiersChanged(ModifiersState::empty()).
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* windows: Fix build
I don't know enough about the code to implement the fix as it is done on
this branch, but this commit at least fixes the build.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* windows: Send ModifiersChanged(ModifiersState::empty) on KILLFOCUS
Very similar to the changes made in [1], as focus is lost, send an event
to the window indicating that the modifiers have been released.
It's unclear to me (without a Windows device to test this on) whether
this is necessary, but it certainly ensures that unfocused windows will
have at least received this event, which is an improvement.
[1]: f79f21641a31da3e4039d41be89047cdcc6028f7
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* macos: Add a hook to update stale modifiers
Sometimes, `ViewState` and `event` might have different values for their
stored `modifiers` flags. These are internally stored as a bitmask in
the latter and an enum in the former.
We can check to see if they differ, and if they do, automatically
dispatch an event to update consumers of modifier state as well as the
stored `state.modifiers`. That's what the hook does.
This hook is then called in the key_down, mouse_entered, mouse_exited,
mouse_click, scroll_wheel, and pressure_change_with_event callbacks,
which each will contain updated modifiers.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* Only call event_mods once when determining whether to update state
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* flags_changed: Memoize window_id collection
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* window_did_resign_key: Remove synthetic ModifiersChanged event
We no longer need to emit this event, since we are checking the state of
our modifiers before emitting most other events.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* mouse_motion: Add a call to update_potentially_stale_modifiers
Now, cover all events (that I can think of, at least) where stale
modifiers might affect how user programs behave. Effectively, every
human-interface event (keypress, mouse click, keydown, etc.) will cause
a ModifiersChanged event to be fired if something has changed.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* key_up: Add a call to update_potentially_stale_modifiers
We also want to make sure modifiers state is synchronized here, too.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* mouse_motion: Remove update_potentially_stale_modifiers invocation
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* Retry CI
* ViewState: Promote visibility of modifiers to the macos impl
This is so that we can interact with the ViewState directly from the
WindowDelegate.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* window_delegate: Synthetically set modifiers state to empty on resignKey
This logic is implemented similarly on other platforms, so we wish to
regain parity here. Originally this behavior was implemented to always
fire an event with ModifiersState::empty(), but that was not the best as
it was not necessarily correct and could be a duplicate event.
This solution is perhaps the most elegant possible to implement the
desired behavior of sending a synthetic empty modifiers event when a
window loses focus, trading some safety for interoperation between the
NSWindowDelegate and the NSView (as the objc runtime must now be
consulted in order to acquire access to the ViewState which is "owned"
by the NSView).
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* Check for modifiers change in window events
* Fix modifier changed on macOS
Since the `mouse_entered` function was generating a mouse motion, which
updates the modifier state, a modifiers changed event was incorrectly
generated.
The updating of the modifier state has also been changed to make sure it
consistently happens before events that have a modifier state attached
to it, without happening on any other event.
This of course means that no `CursorMoved` event is generated anymore
when the user enters the window without it being focused, however I'd
say that is consistent with how winit should behave.
* Fix unused variable warning
* Move changelog entry into `Unreleased` section
Co-authored-by: Freya Gentz <zegentzy@protonmail.com>
Co-authored-by: Kristofer Rye <kristofer.rye@gmail.com>
Co-authored-by: Christian Duerr <contact@christianduerr.com>
2020-03-06 15:43:55 -07:00
|
|
|
view::ViewState,
|
2019-06-21 11:33:15 -04:00
|
|
|
window::{get_window_id, UnownedWindow},
|
|
|
|
|
},
|
2019-07-29 21:16:14 +03:00
|
|
|
window::{Fullscreen, WindowId},
|
2019-05-01 17:03:30 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub struct WindowDelegateState {
|
2019-06-11 01:09:38 +02:00
|
|
|
ns_window: IdRef, // never changes
|
2019-06-21 11:33:15 -04:00
|
|
|
ns_view: IdRef, // never changes
|
2019-05-01 17:03:30 -06:00
|
|
|
|
|
|
|
|
window: Weak<UnownedWindow>,
|
|
|
|
|
|
|
|
|
|
// TODO: It's possible for delegate methods to be called asynchronously,
|
|
|
|
|
// causing data races / `RefCell` panics.
|
|
|
|
|
|
|
|
|
|
// This is set when WindowBuilder::with_fullscreen was set,
|
|
|
|
|
// see comments of `window_did_fail_to_enter_fullscreen`
|
|
|
|
|
initial_fullscreen: bool,
|
|
|
|
|
|
|
|
|
|
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
|
|
|
|
previous_position: Option<(f64, f64)>,
|
|
|
|
|
|
|
|
|
|
// Used to prevent redundant events.
|
2020-02-13 20:41:41 +01:00
|
|
|
previous_scale_factor: f64,
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WindowDelegateState {
|
2019-06-21 11:33:15 -04:00
|
|
|
pub fn new(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Self {
|
2020-01-03 14:52:27 -05:00
|
|
|
let scale_factor = window.scale_factor();
|
2019-05-01 17:03:30 -06:00
|
|
|
let mut delegate_state = WindowDelegateState {
|
2019-06-11 01:09:38 +02:00
|
|
|
ns_window: window.ns_window.clone(),
|
|
|
|
|
ns_view: window.ns_view.clone(),
|
2019-05-01 17:03:30 -06:00
|
|
|
window: Arc::downgrade(&window),
|
|
|
|
|
initial_fullscreen,
|
|
|
|
|
previous_position: None,
|
2020-02-13 20:41:41 +01:00
|
|
|
previous_scale_factor: scale_factor,
|
2019-05-01 17:03:30 -06:00
|
|
|
};
|
|
|
|
|
|
2020-01-03 14:52:27 -05:00
|
|
|
if scale_factor != 1.0 {
|
|
|
|
|
delegate_state.emit_static_scale_factor_changed_event();
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delegate_state
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn with_window<F, T>(&mut self, callback: F) -> Option<T>
|
2019-06-21 11:33:15 -04:00
|
|
|
where
|
|
|
|
|
F: FnOnce(&UnownedWindow) -> T,
|
2019-05-01 17:03:30 -06:00
|
|
|
{
|
2019-06-21 11:33:15 -04:00
|
|
|
self.window.upgrade().map(|ref window| callback(window))
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2020-01-04 01:32:34 -05:00
|
|
|
pub fn emit_event(&mut self, event: WindowEvent<'static>) {
|
2019-05-01 17:03:30 -06:00
|
|
|
let event = Event::WindowEvent {
|
2019-06-11 01:09:38 +02:00
|
|
|
window_id: WindowId(get_window_id(*self.ns_window)),
|
2019-05-01 17:03:30 -06:00
|
|
|
event,
|
|
|
|
|
};
|
2020-01-04 01:32:34 -05:00
|
|
|
AppState::queue_event(EventWrapper::StaticEvent(event));
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-03 14:52:27 -05:00
|
|
|
pub fn emit_static_scale_factor_changed_event(&mut self) {
|
|
|
|
|
let scale_factor = self.get_scale_factor();
|
2020-02-13 20:41:41 +01:00
|
|
|
if scale_factor == self.previous_scale_factor {
|
2020-01-04 01:32:34 -05:00
|
|
|
return ();
|
|
|
|
|
};
|
|
|
|
|
|
2020-02-13 20:41:41 +01:00
|
|
|
self.previous_scale_factor = scale_factor;
|
2020-01-03 14:52:27 -05:00
|
|
|
let wrapper = EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
2020-01-04 01:32:34 -05:00
|
|
|
ns_window: IdRef::retain(*self.ns_window),
|
|
|
|
|
suggested_size: self.view_size(),
|
2020-01-03 14:52:27 -05:00
|
|
|
scale_factor,
|
2020-01-04 01:32:34 -05:00
|
|
|
});
|
|
|
|
|
AppState::queue_event(wrapper);
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn emit_resize_event(&mut self) {
|
2019-06-11 01:09:38 +02:00
|
|
|
let rect = unsafe { NSView::frame(*self.ns_view) };
|
2020-01-03 14:52:27 -05:00
|
|
|
let scale_factor = self.get_scale_factor();
|
2020-01-04 01:32:34 -05:00
|
|
|
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
2020-01-03 14:52:27 -05:00
|
|
|
let size = logical_size.to_physical(scale_factor);
|
2019-09-18 05:49:29 +03:00
|
|
|
self.emit_event(WindowEvent::Resized(size));
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn emit_move_event(&mut self) {
|
2019-06-11 01:09:38 +02:00
|
|
|
let rect = unsafe { NSWindow::frame(*self.ns_window) };
|
2019-05-01 17:03:30 -06:00
|
|
|
let x = rect.origin.x as f64;
|
|
|
|
|
let y = util::bottom_left_to_top_left(rect);
|
|
|
|
|
let moved = self.previous_position != Some((x, y));
|
|
|
|
|
if moved {
|
|
|
|
|
self.previous_position = Some((x, y));
|
2020-09-23 18:54:53 +08:00
|
|
|
let scale_factor = self.get_scale_factor();
|
|
|
|
|
let physical_pos = LogicalPosition::<f64>::from((x, y)).to_physical(scale_factor);
|
|
|
|
|
self.emit_event(WindowEvent::Moved(physical_pos));
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
}
|
2020-01-04 01:32:34 -05:00
|
|
|
|
2020-01-03 14:52:27 -05:00
|
|
|
fn get_scale_factor(&self) -> f64 {
|
2020-01-04 01:32:34 -05:00
|
|
|
(unsafe { NSWindow::backingScaleFactor(*self.ns_window) }) as f64
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-04 01:33:07 -05:00
|
|
|
fn view_size(&self) -> LogicalSize<f64> {
|
2020-01-04 01:32:34 -05:00
|
|
|
let ns_size = unsafe { NSView::frame(*self.ns_view).size };
|
|
|
|
|
LogicalSize::new(ns_size.width as f64, ns_size.height as f64)
|
|
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn new_delegate(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> IdRef {
|
|
|
|
|
let state = WindowDelegateState::new(window, initial_fullscreen);
|
|
|
|
|
unsafe {
|
|
|
|
|
// This is free'd in `dealloc`
|
|
|
|
|
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
|
|
|
|
|
let delegate: id = msg_send![WINDOW_DELEGATE_CLASS.0, alloc];
|
2019-06-21 11:33:15 -04:00
|
|
|
IdRef::new(msg_send![delegate, initWithWinit: state_ptr])
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct WindowDelegateClass(*const Class);
|
|
|
|
|
unsafe impl Send for WindowDelegateClass {}
|
|
|
|
|
unsafe impl Sync for WindowDelegateClass {}
|
|
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
|
static ref WINDOW_DELEGATE_CLASS: WindowDelegateClass = unsafe {
|
|
|
|
|
let superclass = class!(NSResponder);
|
|
|
|
|
let mut decl = ClassDecl::new("WinitWindowDelegate", superclass).unwrap();
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
|
2019-05-01 17:03:30 -06:00
|
|
|
decl.add_method(
|
|
|
|
|
sel!(initWithWinit:),
|
2019-06-21 11:33:15 -04:00
|
|
|
init_with_winit as extern "C" fn(&Object, Sel, *mut c_void) -> id,
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowShouldClose:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowWillClose:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_will_close as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowDidResize:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_did_resize as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowDidMove:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_did_move as extern "C" fn(&Object, Sel, id),
|
|
|
|
|
);
|
2019-05-01 17:03:30 -06:00
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowDidChangeBackingProperties:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowDidBecomeKey:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_did_become_key as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowDidResignKey:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_did_resign_key as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(draggingEntered:),
|
2019-06-21 11:33:15 -04:00
|
|
|
dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL,
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(prepareForDragOperation:),
|
2019-06-21 11:33:15 -04:00
|
|
|
prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(performDragOperation:),
|
2019-06-21 11:33:15 -04:00
|
|
|
perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(concludeDragOperation:),
|
2019-06-21 11:33:15 -04:00
|
|
|
conclude_drag_operation as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(draggingExited:),
|
2019-06-21 11:33:15 -04:00
|
|
|
dragging_exited as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
decl.add_method(
|
|
|
|
|
sel!(window:willUseFullScreenPresentationOptions:),
|
|
|
|
|
window_will_use_fullscreen_presentation_options
|
|
|
|
|
as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger,
|
|
|
|
|
);
|
2019-05-01 17:03:30 -06:00
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowDidEnterFullScreen:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_did_enter_fullscreen as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowWillEnterFullScreen:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowDidExitFullScreen:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_did_exit_fullscreen as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
2019-12-25 03:56:56 +09:00
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowWillExitFullScreen:),
|
|
|
|
|
window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
|
|
|
|
|
);
|
2019-05-01 17:03:30 -06:00
|
|
|
decl.add_method(
|
|
|
|
|
sel!(windowDidFailToEnterFullScreen:),
|
2019-06-21 11:33:15 -04:00
|
|
|
window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id),
|
2019-05-01 17:03:30 -06:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
decl.add_ivar::<*mut c_void>("winitState");
|
|
|
|
|
WindowDelegateClass(decl.register())
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function is definitely unsafe, but labeling that would increase
|
|
|
|
|
// boilerplate and wouldn't really clarify anything...
|
|
|
|
|
fn with_state<F: FnOnce(&mut WindowDelegateState) -> T, T>(this: &Object, callback: F) {
|
|
|
|
|
let state_ptr = unsafe {
|
|
|
|
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
|
|
|
|
&mut *(state_ptr as *mut WindowDelegateState)
|
|
|
|
|
};
|
|
|
|
|
callback(state_ptr);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn dealloc(this: &Object, _sel: Sel) {
|
2019-05-01 17:03:30 -06:00
|
|
|
with_state(this, |state| unsafe {
|
|
|
|
|
Box::from_raw(state as *mut WindowDelegateState);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
|
2019-05-01 17:03:30 -06:00
|
|
|
unsafe {
|
|
|
|
|
let this: id = msg_send![this, init];
|
|
|
|
|
if this != nil {
|
|
|
|
|
(*this).set_ivar("winitState", state);
|
|
|
|
|
with_state(&*this, |state| {
|
2019-06-21 11:33:15 -04:00
|
|
|
let () = msg_send![*state.ns_window, setDelegate: this];
|
2019-05-01 17:03:30 -06:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
this
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowShouldClose:`");
|
|
|
|
|
with_state(this, |state| state.emit_event(WindowEvent::CloseRequested));
|
|
|
|
|
trace!("Completed `windowShouldClose:`");
|
|
|
|
|
NO
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_will_close(this: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowWillClose:`");
|
|
|
|
|
with_state(this, |state| unsafe {
|
|
|
|
|
// `setDelegate:` retains the previous value and then autoreleases it
|
|
|
|
|
let pool = NSAutoreleasePool::new(nil);
|
|
|
|
|
// Since El Capitan, we need to be careful that delegate methods can't
|
|
|
|
|
// be called after the window closes.
|
2019-06-21 11:33:15 -04:00
|
|
|
let () = msg_send![*state.ns_window, setDelegate: nil];
|
2019-05-01 17:03:30 -06:00
|
|
|
pool.drain();
|
|
|
|
|
state.emit_event(WindowEvent::Destroyed);
|
|
|
|
|
});
|
|
|
|
|
trace!("Completed `windowWillClose:`");
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowDidResize:`");
|
|
|
|
|
with_state(this, |state| {
|
|
|
|
|
state.emit_resize_event();
|
|
|
|
|
state.emit_move_event();
|
|
|
|
|
});
|
|
|
|
|
trace!("Completed `windowDidResize:`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This won't be triggered if the move was part of a resize.
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowDidMove:`");
|
|
|
|
|
with_state(this, |state| {
|
|
|
|
|
state.emit_move_event();
|
|
|
|
|
});
|
|
|
|
|
trace!("Completed `windowDidMove:`");
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_did_change_backing_properties(this: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowDidChangeBackingProperties:`");
|
|
|
|
|
with_state(this, |state| {
|
2020-01-03 14:52:27 -05:00
|
|
|
state.emit_static_scale_factor_changed_event();
|
2019-05-01 17:03:30 -06:00
|
|
|
});
|
|
|
|
|
trace!("Completed `windowDidChangeBackingProperties:`");
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_did_become_key(this: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowDidBecomeKey:`");
|
|
|
|
|
with_state(this, |state| {
|
|
|
|
|
// TODO: center the cursor if the window had mouse grab when it
|
|
|
|
|
// lost focus
|
|
|
|
|
state.emit_event(WindowEvent::Focused(true));
|
|
|
|
|
});
|
|
|
|
|
trace!("Completed `windowDidBecomeKey:`");
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowDidResignKey:`");
|
|
|
|
|
with_state(this, |state| {
|
Move `ModifiersChanged` variant to `WindowEvent` (#1381)
* Move `ModifiersChanged` variant to `WindowEvent`
* macos: Fix flags_changed for ModifiersChanged variant move
I haven't look too deep at what this does internally, but at least
cargo-check is fully happy now. :)
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* macos: Fire a ModifiersChanged event on window_did_resign_key
From debugging, I determined that macOS' emission of a flagsChanged
around window switching is inconsistent. It is fair to assume, I think,
that when the user switches windows, they do not expect their former
modifiers state to remain effective; so I think it's best to clear that
state by sending a ModifiersChanged(ModifiersState::empty()).
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* windows: Fix build
I don't know enough about the code to implement the fix as it is done on
this branch, but this commit at least fixes the build.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* windows: Send ModifiersChanged(ModifiersState::empty) on KILLFOCUS
Very similar to the changes made in [1], as focus is lost, send an event
to the window indicating that the modifiers have been released.
It's unclear to me (without a Windows device to test this on) whether
this is necessary, but it certainly ensures that unfocused windows will
have at least received this event, which is an improvement.
[1]: f79f21641a31da3e4039d41be89047cdcc6028f7
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* macos: Add a hook to update stale modifiers
Sometimes, `ViewState` and `event` might have different values for their
stored `modifiers` flags. These are internally stored as a bitmask in
the latter and an enum in the former.
We can check to see if they differ, and if they do, automatically
dispatch an event to update consumers of modifier state as well as the
stored `state.modifiers`. That's what the hook does.
This hook is then called in the key_down, mouse_entered, mouse_exited,
mouse_click, scroll_wheel, and pressure_change_with_event callbacks,
which each will contain updated modifiers.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* Only call event_mods once when determining whether to update state
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* flags_changed: Memoize window_id collection
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* window_did_resign_key: Remove synthetic ModifiersChanged event
We no longer need to emit this event, since we are checking the state of
our modifiers before emitting most other events.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* mouse_motion: Add a call to update_potentially_stale_modifiers
Now, cover all events (that I can think of, at least) where stale
modifiers might affect how user programs behave. Effectively, every
human-interface event (keypress, mouse click, keydown, etc.) will cause
a ModifiersChanged event to be fired if something has changed.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* key_up: Add a call to update_potentially_stale_modifiers
We also want to make sure modifiers state is synchronized here, too.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* mouse_motion: Remove update_potentially_stale_modifiers invocation
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* Retry CI
* ViewState: Promote visibility of modifiers to the macos impl
This is so that we can interact with the ViewState directly from the
WindowDelegate.
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* window_delegate: Synthetically set modifiers state to empty on resignKey
This logic is implemented similarly on other platforms, so we wish to
regain parity here. Originally this behavior was implemented to always
fire an event with ModifiersState::empty(), but that was not the best as
it was not necessarily correct and could be a duplicate event.
This solution is perhaps the most elegant possible to implement the
desired behavior of sending a synthetic empty modifiers event when a
window loses focus, trading some safety for interoperation between the
NSWindowDelegate and the NSView (as the objc runtime must now be
consulted in order to acquire access to the ViewState which is "owned"
by the NSView).
Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
* Check for modifiers change in window events
* Fix modifier changed on macOS
Since the `mouse_entered` function was generating a mouse motion, which
updates the modifier state, a modifiers changed event was incorrectly
generated.
The updating of the modifier state has also been changed to make sure it
consistently happens before events that have a modifier state attached
to it, without happening on any other event.
This of course means that no `CursorMoved` event is generated anymore
when the user enters the window without it being focused, however I'd
say that is consistent with how winit should behave.
* Fix unused variable warning
* Move changelog entry into `Unreleased` section
Co-authored-by: Freya Gentz <zegentzy@protonmail.com>
Co-authored-by: Kristofer Rye <kristofer.rye@gmail.com>
Co-authored-by: Christian Duerr <contact@christianduerr.com>
2020-03-06 15:43:55 -07:00
|
|
|
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
|
|
|
|
|
// NSWindowDelegate will receive a didResignKey event despite no event
|
|
|
|
|
// being received when the modifiers are released. This is because
|
|
|
|
|
// flagsChanged events are received by the NSView instead of the
|
|
|
|
|
// NSWindowDelegate, and as a result a tracked modifiers state can quite
|
|
|
|
|
// easily fall out of synchrony with reality. This requires us to emit
|
|
|
|
|
// a synthetic ModifiersChanged event when we lose focus.
|
|
|
|
|
//
|
|
|
|
|
// Here we (very unsafely) acquire the winitState (a ViewState) from the
|
|
|
|
|
// Object referenced by state.ns_view (an IdRef, which is dereferenced
|
|
|
|
|
// to an id)
|
|
|
|
|
let view_state: &mut ViewState = unsafe {
|
|
|
|
|
let ns_view: &Object = (*state.ns_view).as_ref().expect("failed to deref");
|
|
|
|
|
let state_ptr: *mut c_void = *ns_view.get_ivar("winitState");
|
|
|
|
|
&mut *(state_ptr as *mut ViewState)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Both update the state and emit a ModifiersChanged event.
|
|
|
|
|
if !view_state.modifiers.is_empty() {
|
|
|
|
|
view_state.modifiers = ModifiersState::empty();
|
|
|
|
|
state.emit_event(WindowEvent::ModifiersChanged(view_state.modifiers));
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
state.emit_event(WindowEvent::Focused(false));
|
|
|
|
|
});
|
|
|
|
|
trace!("Completed `windowDidResignKey:`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Invoked when the dragged image enters destination bounds or frame
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `draggingEntered:`");
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
|
2019-05-01 17:03:30 -06:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
|
|
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
|
|
|
|
|
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
|
|
|
|
|
|
|
|
|
|
for file in unsafe { filenames.iter() } {
|
|
|
|
|
use cocoa::foundation::NSString;
|
|
|
|
|
use std::ffi::CStr;
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
let f = NSString::UTF8String(file);
|
|
|
|
|
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
|
|
|
|
|
|
|
|
|
with_state(this, |state| {
|
|
|
|
|
state.emit_event(WindowEvent::HoveredFile(PathBuf::from(path)));
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-06-21 11:33:15 -04:00
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
|
|
|
|
|
trace!("Completed `draggingEntered:`");
|
|
|
|
|
YES
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Invoked when the image is released
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> BOOL {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `prepareForDragOperation:`");
|
|
|
|
|
trace!("Completed `prepareForDragOperation:`");
|
|
|
|
|
YES
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Invoked after the released image has been removed from the screen
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `performDragOperation:`");
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
|
2019-05-01 17:03:30 -06:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
|
|
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
|
|
|
|
|
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
|
|
|
|
|
|
|
|
|
|
for file in unsafe { filenames.iter() } {
|
|
|
|
|
use cocoa::foundation::NSString;
|
|
|
|
|
use std::ffi::CStr;
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
let f = NSString::UTF8String(file);
|
|
|
|
|
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
|
|
|
|
|
|
|
|
|
with_state(this, |state| {
|
|
|
|
|
state.emit_event(WindowEvent::DroppedFile(PathBuf::from(path)));
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-06-21 11:33:15 -04:00
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
|
|
|
|
|
trace!("Completed `performDragOperation:`");
|
|
|
|
|
YES
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Invoked when the dragging operation is complete
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn conclude_drag_operation(_: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `concludeDragOperation:`");
|
|
|
|
|
trace!("Completed `concludeDragOperation:`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Invoked when the dragging operation is cancelled
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `draggingExited:`");
|
2019-06-21 11:33:15 -04:00
|
|
|
with_state(this, |state| {
|
|
|
|
|
state.emit_event(WindowEvent::HoveredFileCancelled)
|
|
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Completed `draggingExited:`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Invoked when before enter fullscreen
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowWillEnterFullscreen:`");
|
2020-06-09 23:46:33 +02:00
|
|
|
|
|
|
|
|
INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst);
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
with_state(this, |state| {
|
|
|
|
|
state.with_window(|window| {
|
|
|
|
|
trace!("Locked shared state in `window_will_enter_fullscreen`");
|
2019-10-05 23:23:06 +03:00
|
|
|
let mut shared_state = window.shared_state.lock().unwrap();
|
|
|
|
|
shared_state.maximized = window.is_zoomed();
|
|
|
|
|
match shared_state.fullscreen {
|
|
|
|
|
// Exclusive mode sets the state in `set_fullscreen` as the user
|
|
|
|
|
// can't enter exclusive mode by other means (like the
|
|
|
|
|
// fullscreen button on the window decorations)
|
|
|
|
|
Some(Fullscreen::Exclusive(_)) => (),
|
|
|
|
|
// `window_will_enter_fullscreen` was triggered and we're already
|
|
|
|
|
// in fullscreen, so we must've reached here by `set_fullscreen`
|
|
|
|
|
// as it updates the state
|
|
|
|
|
Some(Fullscreen::Borderless(_)) => (),
|
|
|
|
|
// Otherwise, we must've reached fullscreen by the user clicking
|
|
|
|
|
// on the green fullscreen button. Update state!
|
|
|
|
|
None => {
|
2020-09-22 04:54:47 +03:00
|
|
|
let current_monitor = Some(window.current_monitor_inner());
|
2020-09-07 20:09:24 +03:00
|
|
|
shared_state.fullscreen = Some(Fullscreen::Borderless(current_monitor))
|
2019-10-05 23:23:06 +03:00
|
|
|
}
|
|
|
|
|
}
|
2019-12-25 03:56:56 +09:00
|
|
|
shared_state.in_fullscreen_transition = true;
|
2019-06-21 11:33:15 -04:00
|
|
|
trace!("Unlocked shared state in `window_will_enter_fullscreen`");
|
|
|
|
|
})
|
|
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Completed `windowWillEnterFullscreen:`");
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-25 03:56:56 +09:00
|
|
|
/// Invoked when before exit fullscreen
|
|
|
|
|
extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
|
|
|
|
trace!("Triggered `windowWillExitFullScreen:`");
|
2020-06-09 23:46:33 +02:00
|
|
|
|
|
|
|
|
INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst);
|
|
|
|
|
|
2019-12-25 03:56:56 +09:00
|
|
|
with_state(this, |state| {
|
|
|
|
|
state.with_window(|window| {
|
|
|
|
|
trace!("Locked shared state in `window_will_exit_fullscreen`");
|
|
|
|
|
let mut shared_state = window.shared_state.lock().unwrap();
|
|
|
|
|
shared_state.in_fullscreen_transition = true;
|
|
|
|
|
trace!("Unlocked shared state in `window_will_exit_fullscreen`");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
trace!("Completed `windowWillExitFullScreen:`");
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
extern "C" fn window_will_use_fullscreen_presentation_options(
|
|
|
|
|
_this: &Object,
|
|
|
|
|
_: Sel,
|
|
|
|
|
_: id,
|
|
|
|
|
_proposed_options: NSUInteger,
|
|
|
|
|
) -> NSUInteger {
|
|
|
|
|
// Generally, games will want to disable the menu bar and the dock. Ideally,
|
|
|
|
|
// this would be configurable by the user. Unfortunately because of our
|
|
|
|
|
// `CGShieldingWindowLevel() + 1` hack (see `set_fullscreen`), our window is
|
|
|
|
|
// placed on top of the menu bar in exclusive fullscreen mode. This looks
|
|
|
|
|
// broken so we always disable the menu bar in exclusive fullscreen. We may
|
|
|
|
|
// still want to make this configurable for borderless fullscreen. Right now
|
|
|
|
|
// we don't, for consistency. If we do, it should be documented that the
|
|
|
|
|
// user-provided options are ignored in exclusive fullscreen.
|
|
|
|
|
(NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
|
|
|
|
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
|
|
|
|
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar)
|
|
|
|
|
.bits()
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
/// Invoked when entered fullscreen
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
2020-06-09 23:46:33 +02:00
|
|
|
INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst);
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowDidEnterFullscreen:`");
|
|
|
|
|
with_state(this, |state| {
|
|
|
|
|
state.initial_fullscreen = false;
|
2019-12-25 03:56:56 +09:00
|
|
|
state.with_window(|window| {
|
|
|
|
|
trace!("Locked shared state in `window_did_enter_fullscreen`");
|
|
|
|
|
let mut shared_state = window.shared_state.lock().unwrap();
|
|
|
|
|
shared_state.in_fullscreen_transition = false;
|
|
|
|
|
let target_fullscreen = shared_state.target_fullscreen.take();
|
|
|
|
|
trace!("Unlocked shared state in `window_did_enter_fullscreen`");
|
|
|
|
|
drop(shared_state);
|
|
|
|
|
if let Some(target_fullscreen) = target_fullscreen {
|
|
|
|
|
window.set_fullscreen(target_fullscreen);
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
});
|
|
|
|
|
trace!("Completed `windowDidEnterFullscreen:`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Invoked when exited fullscreen
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
2020-06-09 23:46:33 +02:00
|
|
|
INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst);
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowDidExitFullscreen:`");
|
2019-06-21 11:33:15 -04:00
|
|
|
with_state(this, |state| {
|
|
|
|
|
state.with_window(|window| {
|
|
|
|
|
window.restore_state_from_fullscreen();
|
2019-12-25 03:56:56 +09:00
|
|
|
trace!("Locked shared state in `window_did_exit_fullscreen`");
|
|
|
|
|
let mut shared_state = window.shared_state.lock().unwrap();
|
|
|
|
|
shared_state.in_fullscreen_transition = false;
|
|
|
|
|
let target_fullscreen = shared_state.target_fullscreen.take();
|
|
|
|
|
trace!("Unlocked shared state in `window_did_exit_fullscreen`");
|
|
|
|
|
drop(shared_state);
|
|
|
|
|
if let Some(target_fullscreen) = target_fullscreen {
|
|
|
|
|
window.set_fullscreen(target_fullscreen);
|
|
|
|
|
}
|
2019-06-21 11:33:15 -04:00
|
|
|
})
|
|
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Completed `windowDidExitFullscreen:`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Invoked when fail to enter fullscreen
|
|
|
|
|
///
|
|
|
|
|
/// When this window launch from a fullscreen app (e.g. launch from VS Code
|
|
|
|
|
/// terminal), it creates a new virtual destkop and a transition animation.
|
|
|
|
|
/// This animation takes one second and cannot be disable without
|
|
|
|
|
/// elevated privileges. In this animation time, all toggleFullscreen events
|
|
|
|
|
/// will be failed. In this implementation, we will try again by using
|
|
|
|
|
/// performSelector:withObject:afterDelay: until window_did_enter_fullscreen.
|
|
|
|
|
/// It should be fine as we only do this at initialzation (i.e with_fullscreen
|
|
|
|
|
/// was set).
|
|
|
|
|
///
|
|
|
|
|
/// From Apple doc:
|
|
|
|
|
/// In some cases, the transition to enter full-screen mode can fail,
|
|
|
|
|
/// due to being in the midst of handling some other animation or user gesture.
|
|
|
|
|
/// This method indicates that there was an error, and you should clean up any
|
|
|
|
|
/// work you may have done to prepare to enter full-screen mode.
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
2019-05-01 17:03:30 -06:00
|
|
|
trace!("Triggered `windowDidFailToEnterFullscreen:`");
|
|
|
|
|
with_state(this, |state| {
|
2019-12-25 03:56:56 +09:00
|
|
|
state.with_window(|window| {
|
|
|
|
|
trace!("Locked shared state in `window_did_fail_to_enter_fullscreen`");
|
|
|
|
|
let mut shared_state = window.shared_state.lock().unwrap();
|
|
|
|
|
shared_state.in_fullscreen_transition = false;
|
|
|
|
|
shared_state.target_fullscreen = None;
|
|
|
|
|
trace!("Unlocked shared state in `window_did_fail_to_enter_fullscreen`");
|
|
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
if state.initial_fullscreen {
|
2019-06-21 11:33:15 -04:00
|
|
|
let _: () = unsafe {
|
|
|
|
|
msg_send![*state.ns_window,
|
|
|
|
|
performSelector:sel!(toggleFullScreen:)
|
|
|
|
|
withObject:nil
|
|
|
|
|
afterDelay: 0.5
|
|
|
|
|
]
|
|
|
|
|
};
|
2019-05-01 17:03:30 -06:00
|
|
|
} else {
|
|
|
|
|
state.with_window(|window| window.restore_state_from_fullscreen());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
trace!("Completed `windowDidFailToEnterFullscreen:`");
|
|
|
|
|
}
|