mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 13:51:30 +11:00
Make iOS fully thread safe (#3045)
* macOS & iOS: Refactor EventWrapper * macOS & iOS: Make EventLoopWindowTarget independent of the user event * iOS: Use MainThreadMarker instead of marking functions unsafe * Make iOS thread safe
This commit is contained in:
parent
d9f04780cc
commit
86baa1c99a
|
@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||||
|
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
|
- Make iOS `MonitorHandle` and `VideoMode` usable from other threads.
|
||||||
- Fix window size sometimes being invalid when resizing on macOS.
|
- Fix window size sometimes being invalid when resizing on macOS.
|
||||||
- On Web, `ControlFlow::Poll` and `ControlFlow::WaitUntil` are now using the Prioritized Task Scheduling API. `setTimeout()` with a trick to circumvent throttling to 4ms is used as a fallback.
|
- On Web, `ControlFlow::Poll` and `ControlFlow::WaitUntil` are now using the Prioritized Task Scheduling API. `setTimeout()` with a trick to circumvent throttling to 4ms is used as a fallback.
|
||||||
- On Web, never return a `MonitorHandle`.
|
- On Web, never return a `MonitorHandle`.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
|
use icrate::Foundation::MainThreadMarker;
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Id;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -210,7 +211,9 @@ pub trait MonitorHandleExtIOS {
|
||||||
impl MonitorHandleExtIOS for MonitorHandle {
|
impl MonitorHandleExtIOS for MonitorHandle {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ui_screen(&self) -> *mut c_void {
|
fn ui_screen(&self) -> *mut c_void {
|
||||||
Id::as_ptr(self.inner.ui_screen()) as *mut c_void
|
// SAFETY: The marker is only used to get the pointer of the screen
|
||||||
|
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||||
|
Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -16,19 +16,21 @@ use core_foundation::runloop::{
|
||||||
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
||||||
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
||||||
};
|
};
|
||||||
use icrate::Foundation::{CGRect, CGSize, NSInteger, NSOperatingSystemVersion, NSProcessInfo};
|
use icrate::Foundation::{
|
||||||
|
CGRect, CGSize, MainThreadMarker, NSInteger, NSOperatingSystemVersion, NSProcessInfo,
|
||||||
|
};
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Id;
|
||||||
use objc2::runtime::AnyObject;
|
use objc2::runtime::AnyObject;
|
||||||
use objc2::{msg_send, sel};
|
use objc2::{msg_send, sel};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
use super::event_loop::{EventHandler, Never};
|
||||||
use super::uikit::UIView;
|
use super::uikit::UIView;
|
||||||
use super::view::WinitUIWindow;
|
use super::view::WinitUIWindow;
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::LogicalSize,
|
dpi::PhysicalSize,
|
||||||
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
||||||
event_loop::ControlFlow,
|
event_loop::ControlFlow,
|
||||||
platform_impl::platform::event_loop::{EventHandler, EventProxy, EventWrapper, Never},
|
|
||||||
window::WindowId as RootWindowId,
|
window::WindowId as RootWindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,6 +46,19 @@ macro_rules! bug_assert {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EventWrapper {
|
||||||
|
StaticEvent(Event<Never>),
|
||||||
|
ScaleFactorChanged(ScaleFactorChanged),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ScaleFactorChanged {
|
||||||
|
pub(super) window: Id<WinitUIWindow>,
|
||||||
|
pub(super) suggested_size: PhysicalSize<u32>,
|
||||||
|
pub(super) scale_factor: f64,
|
||||||
|
}
|
||||||
|
|
||||||
enum UserCallbackTransitionResult<'a> {
|
enum UserCallbackTransitionResult<'a> {
|
||||||
Success {
|
Success {
|
||||||
event_handler: Box<dyn EventHandler>,
|
event_handler: Box<dyn EventHandler>,
|
||||||
|
@ -114,24 +129,18 @@ struct AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
// requires main thread
|
fn get_mut(_mtm: MainThreadMarker) -> RefMut<'static, AppState> {
|
||||||
unsafe fn get_mut() -> RefMut<'static, AppState> {
|
|
||||||
// basically everything in UIKit requires the main thread, so it's pointless to use the
|
// basically everything in UIKit requires the main thread, so it's pointless to use the
|
||||||
// std::sync APIs.
|
// std::sync APIs.
|
||||||
// must be mut because plain `static` requires `Sync`
|
// must be mut because plain `static` requires `Sync`
|
||||||
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
|
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
let mut guard = unsafe { APP_STATE.borrow_mut() };
|
||||||
assert_main_thread!(
|
|
||||||
"bug in winit: `AppState::get_mut()` can only be called on the main thread"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let mut guard = APP_STATE.borrow_mut();
|
|
||||||
if guard.is_none() {
|
if guard.is_none() {
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[cold]
|
#[cold]
|
||||||
unsafe fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
|
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
|
||||||
let waker = EventLoopWaker::new(CFRunLoopGetMain());
|
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() });
|
||||||
**guard = Some(AppState {
|
**guard = Some(AppState {
|
||||||
app_state: Some(AppStateImpl::NotLaunched {
|
app_state: Some(AppStateImpl::NotLaunched {
|
||||||
queued_windows: Vec::new(),
|
queued_windows: Vec::new(),
|
||||||
|
@ -142,7 +151,7 @@ impl AppState {
|
||||||
waker,
|
waker,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
init_guard(&mut guard)
|
init_guard(&mut guard);
|
||||||
}
|
}
|
||||||
RefMut::map(guard, |state| state.as_mut().unwrap())
|
RefMut::map(guard, |state| state.as_mut().unwrap())
|
||||||
}
|
}
|
||||||
|
@ -451,10 +460,8 @@ impl AppState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread and window is a UIWindow
|
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id<WinitUIWindow>) {
|
||||||
// retains window
|
let mut this = AppState::get_mut(mtm);
|
||||||
pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow>) {
|
|
||||||
let mut this = AppState::get_mut();
|
|
||||||
match this.state_mut() {
|
match this.state_mut() {
|
||||||
&mut AppStateImpl::NotLaunched {
|
&mut AppStateImpl::NotLaunched {
|
||||||
ref mut queued_windows,
|
ref mut queued_windows,
|
||||||
|
@ -474,10 +481,8 @@ pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow>) {
|
||||||
window.makeKeyAndVisible();
|
window.makeKeyAndVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread and window is a UIWindow
|
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUIWindow>) {
|
||||||
// retains window
|
let mut this = AppState::get_mut(mtm);
|
||||||
pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id<WinitUIWindow>) {
|
|
||||||
let mut this = AppState::get_mut();
|
|
||||||
match this.state_mut() {
|
match this.state_mut() {
|
||||||
&mut AppStateImpl::NotLaunched {
|
&mut AppStateImpl::NotLaunched {
|
||||||
ref mut queued_gpu_redraws,
|
ref mut queued_gpu_redraws,
|
||||||
|
@ -506,14 +511,12 @@ pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id<WinitUIWindow>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
pub fn will_launch(mtm: MainThreadMarker, queued_event_handler: Box<dyn EventHandler>) {
|
||||||
pub unsafe fn will_launch(queued_event_handler: Box<dyn EventHandler>) {
|
AppState::get_mut(mtm).will_launch_transition(queued_event_handler)
|
||||||
AppState::get_mut().will_launch_transition(queued_event_handler)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
||||||
pub unsafe fn did_finish_launching() {
|
let mut this = AppState::get_mut(mtm);
|
||||||
let mut this = AppState::get_mut();
|
|
||||||
let windows = match this.state_mut() {
|
let windows = match this.state_mut() {
|
||||||
AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows),
|
AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows),
|
||||||
s => bug!("unexpected state {:?}", s),
|
s => bug!("unexpected state {:?}", s),
|
||||||
|
@ -541,7 +544,7 @@ pub unsafe fn did_finish_launching() {
|
||||||
// completed. This may result in incorrect visual appearance.
|
// completed. This may result in incorrect visual appearance.
|
||||||
// ```
|
// ```
|
||||||
let screen = window.screen();
|
let screen = window.screen();
|
||||||
let _: () = msg_send![&window, setScreen: ptr::null::<AnyObject>()];
|
let _: () = unsafe { msg_send![&window, setScreen: ptr::null::<AnyObject>()] };
|
||||||
window.setScreen(&screen);
|
window.setScreen(&screen);
|
||||||
|
|
||||||
let controller = window.rootViewController();
|
let controller = window.rootViewController();
|
||||||
|
@ -551,13 +554,13 @@ pub unsafe fn did_finish_launching() {
|
||||||
window.makeKeyAndVisible();
|
window.makeKeyAndVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (windows, events) = AppState::get_mut().did_finish_launching_transition();
|
let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition();
|
||||||
|
|
||||||
let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents(
|
let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents(
|
||||||
StartCause::Init,
|
StartCause::Init,
|
||||||
)))
|
)))
|
||||||
.chain(events);
|
.chain(events);
|
||||||
handle_nonuser_events(events);
|
handle_nonuser_events(mtm, events);
|
||||||
|
|
||||||
// the above window dance hack, could possibly trigger new windows to be created.
|
// the above window dance hack, could possibly trigger new windows to be created.
|
||||||
// we can just set those windows up normally, as they were created after didFinishLaunching
|
// we can just set those windows up normally, as they were created after didFinishLaunching
|
||||||
|
@ -566,27 +569,27 @@ pub unsafe fn did_finish_launching() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
|
||||||
// AppState::did_finish_launching handles the special transition `Init`
|
// AppState::did_finish_launching handles the special transition `Init`
|
||||||
pub unsafe fn handle_wakeup_transition() {
|
pub fn handle_wakeup_transition(mtm: MainThreadMarker) {
|
||||||
let mut this = AppState::get_mut();
|
let mut this = AppState::get_mut(mtm);
|
||||||
let wakeup_event = match this.wakeup_transition() {
|
let wakeup_event = match this.wakeup_transition() {
|
||||||
None => return,
|
None => return,
|
||||||
Some(wakeup_event) => wakeup_event,
|
Some(wakeup_event) => wakeup_event,
|
||||||
};
|
};
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
handle_nonuser_event(wakeup_event)
|
handle_nonuser_event(mtm, wakeup_event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) {
|
||||||
pub(crate) unsafe fn handle_nonuser_event(event: EventWrapper) {
|
handle_nonuser_events(mtm, std::iter::once(event))
|
||||||
handle_nonuser_events(std::iter::once(event))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||||
pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(events: I) {
|
mtm: MainThreadMarker,
|
||||||
let mut this = AppState::get_mut();
|
events: I,
|
||||||
|
) {
|
||||||
|
let mut this = AppState::get_mut(mtm);
|
||||||
let (mut event_handler, active_control_flow, processing_redraws) =
|
let (mut event_handler, active_control_flow, processing_redraws) =
|
||||||
match this.try_user_callback_transition() {
|
match this.try_user_callback_transition() {
|
||||||
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
||||||
|
@ -615,14 +618,14 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
|
||||||
}
|
}
|
||||||
event_handler.handle_nonuser_event(event, &mut control_flow)
|
event_handler.handle_nonuser_event(event, &mut control_flow)
|
||||||
}
|
}
|
||||||
EventWrapper::EventProxy(proxy) => {
|
EventWrapper::ScaleFactorChanged(event) => {
|
||||||
handle_event_proxy(&mut event_handler, control_flow, proxy)
|
handle_hidpi_proxy(&mut event_handler, control_flow, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut this = AppState::get_mut();
|
let mut this = AppState::get_mut(mtm);
|
||||||
let queued_events = match this.state_mut() {
|
let queued_events = match this.state_mut() {
|
||||||
&mut AppStateImpl::InUserCallback {
|
&mut AppStateImpl::InUserCallback {
|
||||||
ref mut queued_events,
|
ref mut queued_events,
|
||||||
|
@ -672,17 +675,16 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
|
||||||
}
|
}
|
||||||
event_handler.handle_nonuser_event(event, &mut control_flow)
|
event_handler.handle_nonuser_event(event, &mut control_flow)
|
||||||
}
|
}
|
||||||
EventWrapper::EventProxy(proxy) => {
|
EventWrapper::ScaleFactorChanged(event) => {
|
||||||
handle_event_proxy(&mut event_handler, control_flow, proxy)
|
handle_hidpi_proxy(&mut event_handler, control_flow, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
fn handle_user_events(mtm: MainThreadMarker) {
|
||||||
unsafe fn handle_user_events() {
|
let mut this = AppState::get_mut(mtm);
|
||||||
let mut this = AppState::get_mut();
|
|
||||||
let mut control_flow = this.control_flow;
|
let mut control_flow = this.control_flow;
|
||||||
let (mut event_handler, active_control_flow, processing_redraws) =
|
let (mut event_handler, active_control_flow, processing_redraws) =
|
||||||
match this.try_user_callback_transition() {
|
match this.try_user_callback_transition() {
|
||||||
|
@ -703,7 +705,7 @@ unsafe fn handle_user_events() {
|
||||||
event_handler.handle_user_events(&mut control_flow);
|
event_handler.handle_user_events(&mut control_flow);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut this = AppState::get_mut();
|
let mut this = AppState::get_mut(mtm);
|
||||||
let queued_events = match this.state_mut() {
|
let queued_events = match this.state_mut() {
|
||||||
&mut AppStateImpl::InUserCallback {
|
&mut AppStateImpl::InUserCallback {
|
||||||
ref mut queued_events,
|
ref mut queued_events,
|
||||||
|
@ -734,8 +736,8 @@ unsafe fn handle_user_events() {
|
||||||
EventWrapper::StaticEvent(event) => {
|
EventWrapper::StaticEvent(event) => {
|
||||||
event_handler.handle_nonuser_event(event, &mut control_flow)
|
event_handler.handle_nonuser_event(event, &mut control_flow)
|
||||||
}
|
}
|
||||||
EventWrapper::EventProxy(proxy) => {
|
EventWrapper::ScaleFactorChanged(event) => {
|
||||||
handle_event_proxy(&mut event_handler, control_flow, proxy)
|
handle_hidpi_proxy(&mut event_handler, control_flow, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -743,9 +745,8 @@ unsafe fn handle_user_events() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
||||||
pub unsafe fn handle_main_events_cleared() {
|
let mut this = AppState::get_mut(mtm);
|
||||||
let mut this = AppState::get_mut();
|
|
||||||
if !this.has_launched() {
|
if !this.has_launched() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -755,9 +756,9 @@ pub unsafe fn handle_main_events_cleared() {
|
||||||
};
|
};
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
handle_user_events();
|
handle_user_events(mtm);
|
||||||
|
|
||||||
let mut this = AppState::get_mut();
|
let mut this = AppState::get_mut(mtm);
|
||||||
let redraw_events: Vec<EventWrapper> = this
|
let redraw_events: Vec<EventWrapper> = this
|
||||||
.main_events_cleared_transition()
|
.main_events_cleared_transition()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -770,18 +771,16 @@ pub unsafe fn handle_main_events_cleared() {
|
||||||
.collect();
|
.collect();
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
handle_nonuser_events(redraw_events);
|
handle_nonuser_events(mtm, redraw_events);
|
||||||
handle_nonuser_event(EventWrapper::StaticEvent(Event::AboutToWait));
|
handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::AboutToWait));
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
pub fn handle_events_cleared(mtm: MainThreadMarker) {
|
||||||
pub unsafe fn handle_events_cleared() {
|
AppState::get_mut(mtm).events_cleared_transition();
|
||||||
AppState::get_mut().events_cleared_transition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
pub fn terminated(mtm: MainThreadMarker) {
|
||||||
pub unsafe fn terminated() {
|
let mut this = AppState::get_mut(mtm);
|
||||||
let mut this = AppState::get_mut();
|
|
||||||
let mut event_handler = this.terminated_transition();
|
let mut event_handler = this.terminated_transition();
|
||||||
let mut control_flow = this.control_flow;
|
let mut control_flow = this.control_flow;
|
||||||
drop(this);
|
drop(this);
|
||||||
|
@ -789,34 +788,17 @@ pub unsafe fn terminated() {
|
||||||
event_handler.handle_nonuser_event(Event::LoopExiting, &mut control_flow)
|
event_handler.handle_nonuser_event(Event::LoopExiting, &mut control_flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_event_proxy(
|
|
||||||
event_handler: &mut Box<dyn EventHandler>,
|
|
||||||
control_flow: ControlFlow,
|
|
||||||
proxy: EventProxy,
|
|
||||||
) {
|
|
||||||
match proxy {
|
|
||||||
EventProxy::DpiChangedProxy {
|
|
||||||
suggested_size,
|
|
||||||
scale_factor,
|
|
||||||
window,
|
|
||||||
} => handle_hidpi_proxy(
|
|
||||||
event_handler,
|
|
||||||
control_flow,
|
|
||||||
suggested_size,
|
|
||||||
scale_factor,
|
|
||||||
window,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_hidpi_proxy(
|
fn handle_hidpi_proxy(
|
||||||
event_handler: &mut Box<dyn EventHandler>,
|
event_handler: &mut Box<dyn EventHandler>,
|
||||||
mut control_flow: ControlFlow,
|
mut control_flow: ControlFlow,
|
||||||
suggested_size: LogicalSize<f64>,
|
event: ScaleFactorChanged,
|
||||||
scale_factor: f64,
|
|
||||||
window: Id<WinitUIWindow>,
|
|
||||||
) {
|
) {
|
||||||
let new_inner_size = Arc::new(Mutex::new(suggested_size.to_physical(scale_factor)));
|
let ScaleFactorChanged {
|
||||||
|
suggested_size,
|
||||||
|
scale_factor,
|
||||||
|
window,
|
||||||
|
} = event;
|
||||||
|
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||||
let event = Event::WindowEvent {
|
let event = Event::WindowEvent {
|
||||||
window_id: RootWindowId(window.id()),
|
window_id: RootWindowId(window.id()),
|
||||||
event: WindowEvent::ScaleFactorChanged {
|
event: WindowEvent::ScaleFactorChanged {
|
||||||
|
|
|
@ -15,12 +15,10 @@ use core_foundation::runloop::{
|
||||||
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||||
};
|
};
|
||||||
use icrate::Foundation::{MainThreadMarker, NSString};
|
use icrate::Foundation::{MainThreadMarker, NSString};
|
||||||
use objc2::rc::Id;
|
|
||||||
use objc2::ClassType;
|
use objc2::ClassType;
|
||||||
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
|
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::LogicalSize,
|
|
||||||
error::EventLoopError,
|
error::EventLoopError,
|
||||||
event::Event,
|
event::Event,
|
||||||
event_loop::{
|
event_loop::{
|
||||||
|
@ -30,38 +28,21 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen};
|
use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen};
|
||||||
use super::view::WinitUIWindow;
|
|
||||||
use super::{app_state, monitor, view, MonitorHandle};
|
use super::{app_state, monitor, view, MonitorHandle};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum EventWrapper {
|
|
||||||
StaticEvent(Event<Never>),
|
|
||||||
EventProxy(EventProxy),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub(crate) enum EventProxy {
|
|
||||||
DpiChangedProxy {
|
|
||||||
window: Id<WinitUIWindow>,
|
|
||||||
suggested_size: LogicalSize<f64>,
|
|
||||||
scale_factor: f64,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EventLoopWindowTarget<T: 'static> {
|
pub struct EventLoopWindowTarget<T: 'static> {
|
||||||
receiver: Receiver<T>,
|
pub(super) mtm: MainThreadMarker,
|
||||||
sender_to_clone: Sender<T>,
|
p: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> EventLoopWindowTarget<T> {
|
impl<T: 'static> EventLoopWindowTarget<T> {
|
||||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||||
monitor::uiscreens(MainThreadMarker::new().unwrap())
|
monitor::uiscreens(self.mtm)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||||
Some(MonitorHandle::new(UIScreen::main(
|
Some(MonitorHandle::new(UIScreen::main(self.mtm)))
|
||||||
MainThreadMarker::new().unwrap(),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||||
|
@ -70,6 +51,9 @@ impl<T: 'static> EventLoopWindowTarget<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventLoop<T: 'static> {
|
pub struct EventLoop<T: 'static> {
|
||||||
|
mtm: MainThreadMarker,
|
||||||
|
sender: Sender<T>,
|
||||||
|
receiver: Receiver<T>,
|
||||||
window_target: RootEventLoopWindowTarget<T>,
|
window_target: RootEventLoopWindowTarget<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +64,8 @@ impl<T: 'static> EventLoop<T> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
_: &PlatformSpecificEventLoopAttributes,
|
_: &PlatformSpecificEventLoopAttributes,
|
||||||
) -> Result<EventLoop<T>, EventLoopError> {
|
) -> Result<EventLoop<T>, EventLoopError> {
|
||||||
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
|
let mtm = MainThreadMarker::new()
|
||||||
|
.expect("On iOS, `EventLoop` must be created on the main thread");
|
||||||
|
|
||||||
static mut SINGLETON_INIT: bool = false;
|
static mut SINGLETON_INIT: bool = false;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -92,16 +77,19 @@ impl<T: 'static> EventLoop<T> {
|
||||||
SINGLETON_INIT = true;
|
SINGLETON_INIT = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (sender_to_clone, receiver) = mpsc::channel();
|
let (sender, receiver) = mpsc::channel();
|
||||||
|
|
||||||
// this line sets up the main run loop before `UIApplicationMain`
|
// this line sets up the main run loop before `UIApplicationMain`
|
||||||
setup_control_flow_observers();
|
setup_control_flow_observers();
|
||||||
|
|
||||||
Ok(EventLoop {
|
Ok(EventLoop {
|
||||||
|
mtm,
|
||||||
|
sender,
|
||||||
|
receiver,
|
||||||
window_target: RootEventLoopWindowTarget {
|
window_target: RootEventLoopWindowTarget {
|
||||||
p: EventLoopWindowTarget {
|
p: EventLoopWindowTarget {
|
||||||
receiver,
|
mtm,
|
||||||
sender_to_clone,
|
p: PhantomData,
|
||||||
},
|
},
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
},
|
},
|
||||||
|
@ -113,7 +101,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let application = UIApplication::shared(MainThreadMarker::new().unwrap());
|
let application = UIApplication::shared(self.mtm);
|
||||||
assert!(
|
assert!(
|
||||||
application.is_none(),
|
application.is_none(),
|
||||||
"\
|
"\
|
||||||
|
@ -128,10 +116,11 @@ impl<T: 'static> EventLoop<T> {
|
||||||
|
|
||||||
let handler = EventLoopHandler {
|
let handler = EventLoopHandler {
|
||||||
f: event_handler,
|
f: event_handler,
|
||||||
|
receiver: self.receiver,
|
||||||
event_loop: self.window_target,
|
event_loop: self.window_target,
|
||||||
};
|
};
|
||||||
|
|
||||||
app_state::will_launch(Box::new(handler));
|
app_state::will_launch(self.mtm, Box::new(handler));
|
||||||
|
|
||||||
// Ensure application delegate is initialized
|
// Ensure application delegate is initialized
|
||||||
view::WinitApplicationDelegate::class();
|
view::WinitApplicationDelegate::class();
|
||||||
|
@ -147,7 +136,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||||
EventLoopProxy::new(self.window_target.p.sender_to_clone.clone())
|
EventLoopProxy::new(self.sender.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
|
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
|
||||||
|
@ -158,9 +147,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
// EventLoopExtIOS
|
// EventLoopExtIOS
|
||||||
impl<T: 'static> EventLoop<T> {
|
impl<T: 'static> EventLoop<T> {
|
||||||
pub fn idiom(&self) -> Idiom {
|
pub fn idiom(&self) -> Idiom {
|
||||||
UIDevice::current(MainThreadMarker::new().unwrap())
|
UIDevice::current(self.mtm).userInterfaceIdiom().into()
|
||||||
.userInterfaceIdiom()
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,14 +225,13 @@ fn setup_control_flow_observers() {
|
||||||
activity: CFRunLoopActivity,
|
activity: CFRunLoopActivity,
|
||||||
_: *mut c_void,
|
_: *mut c_void,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
match activity {
|
match activity {
|
||||||
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(),
|
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(mtm),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
||||||
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end
|
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end
|
||||||
|
@ -263,15 +249,14 @@ fn setup_control_flow_observers() {
|
||||||
activity: CFRunLoopActivity,
|
activity: CFRunLoopActivity,
|
||||||
_: *mut c_void,
|
_: *mut c_void,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
match activity {
|
match activity {
|
||||||
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(),
|
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm),
|
||||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// end is queued with the lowest priority to ensure it is processed after other observers
|
// end is queued with the lowest priority to ensure it is processed after other observers
|
||||||
extern "C" fn control_flow_end_handler(
|
extern "C" fn control_flow_end_handler(
|
||||||
|
@ -279,15 +264,14 @@ fn setup_control_flow_observers() {
|
||||||
activity: CFRunLoopActivity,
|
activity: CFRunLoopActivity,
|
||||||
_: *mut c_void,
|
_: *mut c_void,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
match activity {
|
match activity {
|
||||||
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(),
|
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm),
|
||||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let main_loop = CFRunLoopGetMain();
|
let main_loop = CFRunLoopGetMain();
|
||||||
|
|
||||||
|
@ -336,6 +320,7 @@ pub trait EventHandler: Debug {
|
||||||
|
|
||||||
struct EventLoopHandler<T: 'static> {
|
struct EventLoopHandler<T: 'static> {
|
||||||
f: Box<EventHandlerCallback<T>>,
|
f: Box<EventHandlerCallback<T>>,
|
||||||
|
receiver: Receiver<T>,
|
||||||
event_loop: RootEventLoopWindowTarget<T>,
|
event_loop: RootEventLoopWindowTarget<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +342,7 @@ impl<T: 'static> EventHandler for EventLoopHandler<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
|
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
|
||||||
for event in self.event_loop.p.receiver.try_iter() {
|
for event in self.receiver.try_iter() {
|
||||||
(self.f)(Event::UserEvent(event), &self.event_loop, control_flow);
|
(self.f)(Event::UserEvent(event), &self.event_loop, control_flow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,17 +58,6 @@
|
||||||
#![cfg(ios_platform)]
|
#![cfg(ios_platform)]
|
||||||
#![allow(clippy::let_unit_value)]
|
#![allow(clippy::let_unit_value)]
|
||||||
|
|
||||||
// TODO: (mtak-) UIKit requires main thread for virtually all function/method calls. This could be
|
|
||||||
// worked around in the future by using GCD (grand central dispatch) and/or caching of values like
|
|
||||||
// window size/position.
|
|
||||||
macro_rules! assert_main_thread {
|
|
||||||
($($t:tt)*) => {
|
|
||||||
if !::icrate::Foundation::is_main_thread() {
|
|
||||||
panic!($($t)*);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
mod app_state;
|
mod app_state;
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod ffi;
|
mod ffi;
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeSet, VecDeque},
|
collections::{BTreeSet, VecDeque},
|
||||||
fmt,
|
fmt, hash, ptr,
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use icrate::Foundation::{MainThreadMarker, NSInteger};
|
use icrate::Foundation::{MainThreadBound, MainThreadMarker, NSInteger};
|
||||||
|
use objc2::mutability::IsRetainable;
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Id;
|
||||||
|
|
||||||
use super::uikit::{UIScreen, UIScreenMode};
|
use super::uikit::{UIScreen, UIScreenMode};
|
||||||
|
@ -16,32 +16,59 @@ use crate::{
|
||||||
platform_impl::platform::app_state,
|
platform_impl::platform::app_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(madsmtm): Remove or refactor this
|
// Workaround for `MainThreadBound` implementing almost no traits
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ScreenModeSendSync(pub(crate) Id<UIScreenMode>);
|
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Id<T>>);
|
||||||
|
|
||||||
unsafe impl Send for ScreenModeSendSync {}
|
impl<T: IsRetainable> Clone for MainThreadBoundDelegateImpls<T> {
|
||||||
unsafe impl Sync for ScreenModeSendSync {}
|
fn clone(&self) -> Self {
|
||||||
|
Self(
|
||||||
|
self.0
|
||||||
|
.get_on_main(|inner, mtm| MainThreadBound::new(Id::clone(inner), mtm)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: IsRetainable> hash::Hash for MainThreadBoundDelegateImpls<T> {
|
||||||
|
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||||
|
// SAFETY: Marker only used to get the pointer
|
||||||
|
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||||
|
Id::as_ptr(self.0.get(mtm)).hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: IsRetainable> PartialEq for MainThreadBoundDelegateImpls<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
// SAFETY: Marker only used to get the pointer
|
||||||
|
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||||
|
Id::as_ptr(self.0.get(mtm)) == Id::as_ptr(other.0.get(mtm))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: IsRetainable> Eq for MainThreadBoundDelegateImpls<T> {}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
pub struct VideoMode {
|
pub struct VideoMode {
|
||||||
pub(crate) size: (u32, u32),
|
pub(crate) size: (u32, u32),
|
||||||
pub(crate) bit_depth: u16,
|
pub(crate) bit_depth: u16,
|
||||||
pub(crate) refresh_rate_millihertz: u32,
|
pub(crate) refresh_rate_millihertz: u32,
|
||||||
pub(crate) screen_mode: ScreenModeSendSync,
|
screen_mode: MainThreadBoundDelegateImpls<UIScreenMode>,
|
||||||
pub(crate) monitor: MonitorHandle,
|
pub(crate) monitor: MonitorHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VideoMode {
|
impl VideoMode {
|
||||||
fn new(uiscreen: Id<UIScreen>, screen_mode: Id<UIScreenMode>) -> VideoMode {
|
fn new(
|
||||||
assert_main_thread!("`VideoMode` can only be created on the main thread on iOS");
|
uiscreen: Id<UIScreen>,
|
||||||
|
screen_mode: Id<UIScreenMode>,
|
||||||
|
mtm: MainThreadMarker,
|
||||||
|
) -> VideoMode {
|
||||||
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
|
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
|
||||||
let size = screen_mode.size();
|
let size = screen_mode.size();
|
||||||
VideoMode {
|
VideoMode {
|
||||||
size: (size.width as u32, size.height as u32),
|
size: (size.width as u32, size.height as u32),
|
||||||
bit_depth: 32,
|
bit_depth: 32,
|
||||||
refresh_rate_millihertz,
|
refresh_rate_millihertz,
|
||||||
screen_mode: ScreenModeSendSync(screen_mode),
|
screen_mode: MainThreadBoundDelegateImpls(MainThreadBound::new(screen_mode, mtm)),
|
||||||
monitor: MonitorHandle::new(uiscreen),
|
monitor: MonitorHandle::new(uiscreen),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,18 +88,40 @@ impl VideoMode {
|
||||||
pub fn monitor(&self) -> MonitorHandle {
|
pub fn monitor(&self) -> MonitorHandle {
|
||||||
self.monitor.clone()
|
self.monitor.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Id<UIScreenMode> {
|
||||||
|
self.screen_mode.0.get(mtm)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Inner {
|
|
||||||
uiscreen: Id<UIScreen>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct MonitorHandle {
|
pub struct MonitorHandle {
|
||||||
inner: Inner,
|
ui_screen: MainThreadBound<Id<UIScreen>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for MonitorHandle {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
ui_screen: self
|
||||||
|
.ui_screen
|
||||||
|
.get_on_main(|inner, mtm| MainThreadBound::new(inner.clone(), mtm)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl hash::Hash for MonitorHandle {
|
||||||
|
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||||
|
(self as *const Self).hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for MonitorHandle {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
ptr::eq(self, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for MonitorHandle {}
|
||||||
|
|
||||||
impl PartialOrd for MonitorHandle {
|
impl PartialOrd for MonitorHandle {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
|
@ -86,31 +135,6 @@ impl Ord for MonitorHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for MonitorHandle {
|
|
||||||
type Target = Inner;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Inner {
|
|
||||||
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS");
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for MonitorHandle {
|
|
||||||
fn deref_mut(&mut self) -> &mut Inner {
|
|
||||||
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS");
|
|
||||||
&mut self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for MonitorHandle {}
|
|
||||||
unsafe impl Sync for MonitorHandle {}
|
|
||||||
|
|
||||||
impl Drop for MonitorHandle {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
assert_main_thread!("`MonitorHandle` can only be dropped on the main thread on iOS");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for MonitorHandle {
|
impl fmt::Debug for MonitorHandle {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
// TODO: Do this using the proper fmt API
|
// TODO: Do this using the proper fmt API
|
||||||
|
@ -135,59 +159,80 @@ impl fmt::Debug for MonitorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonitorHandle {
|
impl MonitorHandle {
|
||||||
pub(crate) fn new(uiscreen: Id<UIScreen>) -> Self {
|
pub(crate) fn new(ui_screen: Id<UIScreen>) -> Self {
|
||||||
assert_main_thread!("`MonitorHandle` can only be created on the main thread on iOS");
|
// Holding `Id<UIScreen>` implies we're on the main thread.
|
||||||
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
Self {
|
Self {
|
||||||
inner: Inner { uiscreen },
|
ui_screen: MainThreadBound::new(ui_screen, mtm),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Inner {
|
|
||||||
pub fn name(&self) -> Option<String> {
|
pub fn name(&self) -> Option<String> {
|
||||||
let main = UIScreen::main(MainThreadMarker::new().unwrap());
|
self.ui_screen.get_on_main(|ui_screen, mtm| {
|
||||||
if self.uiscreen == main {
|
let main = UIScreen::main(mtm);
|
||||||
|
if *ui_screen == main {
|
||||||
Some("Primary".to_string())
|
Some("Primary".to_string())
|
||||||
} else if self.uiscreen == main.mirroredScreen() {
|
} else if *ui_screen == main.mirroredScreen() {
|
||||||
Some("Mirrored".to_string())
|
Some("Mirrored".to_string())
|
||||||
} else {
|
} else {
|
||||||
UIScreen::screens(MainThreadMarker::new().unwrap())
|
UIScreen::screens(mtm)
|
||||||
.iter()
|
.iter()
|
||||||
.position(|rhs| rhs == &*self.uiscreen)
|
.position(|rhs| rhs == &**ui_screen)
|
||||||
.map(|idx| idx.to_string())
|
.map(|idx| idx.to_string())
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> PhysicalSize<u32> {
|
pub fn size(&self) -> PhysicalSize<u32> {
|
||||||
let bounds = self.uiscreen.nativeBounds();
|
let bounds = self
|
||||||
|
.ui_screen
|
||||||
|
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
|
||||||
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
|
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||||
let bounds = self.uiscreen.nativeBounds();
|
let bounds = self
|
||||||
|
.ui_screen
|
||||||
|
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
|
||||||
(bounds.origin.x as f64, bounds.origin.y as f64).into()
|
(bounds.origin.x as f64, bounds.origin.y as f64).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scale_factor(&self) -> f64 {
|
pub fn scale_factor(&self) -> f64 {
|
||||||
self.uiscreen.nativeScale() as f64
|
self.ui_screen
|
||||||
|
.get_on_main(|ui_screen, _| ui_screen.nativeScale()) as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||||
Some(refresh_rate_millihertz(&self.uiscreen))
|
Some(
|
||||||
|
self.ui_screen
|
||||||
|
.get_on_main(|ui_screen, _| refresh_rate_millihertz(ui_screen)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
||||||
|
self.ui_screen.get_on_main(|ui_screen, mtm| {
|
||||||
// Use Ord impl of RootVideoMode
|
// Use Ord impl of RootVideoMode
|
||||||
let modes: BTreeSet<_> = self
|
|
||||||
.uiscreen
|
let modes: BTreeSet<_> = ui_screen
|
||||||
.availableModes()
|
.availableModes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mode| RootVideoMode {
|
.map(|mode| RootVideoMode {
|
||||||
video_mode: VideoMode::new(self.uiscreen.clone(), mode),
|
video_mode: VideoMode::new(ui_screen.clone(), mode, mtm),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
modes.into_iter().map(|mode| mode.video_mode)
|
modes.into_iter().map(|mode| mode.video_mode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Id<UIScreen> {
|
||||||
|
self.ui_screen.get(mtm)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn preferred_video_mode(&self) -> VideoMode {
|
||||||
|
self.ui_screen.get_on_main(|ui_screen, mtm| {
|
||||||
|
VideoMode::new(ui_screen.clone(), ui_screen.preferredMode().unwrap(), mtm)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,20 +260,6 @@ fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 {
|
||||||
refresh_rate_millihertz as u32 * 1000
|
refresh_rate_millihertz as u32 * 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonitorHandleExtIOS
|
|
||||||
impl Inner {
|
|
||||||
pub(crate) fn ui_screen(&self) -> &Id<UIScreen> {
|
|
||||||
&self.uiscreen
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn preferred_video_mode(&self) -> VideoMode {
|
|
||||||
VideoMode::new(
|
|
||||||
self.uiscreen.clone(),
|
|
||||||
self.uiscreen.preferredMode().unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
||||||
UIScreen::screens(mtm)
|
UIScreen::screens(mtm)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -8,6 +8,7 @@ use objc2::rc::Id;
|
||||||
use objc2::runtime::AnyClass;
|
use objc2::runtime::AnyClass;
|
||||||
use objc2::{declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType};
|
use objc2::{declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType};
|
||||||
|
|
||||||
|
use super::app_state::{self, EventWrapper};
|
||||||
use super::uikit::{
|
use super::uikit::{
|
||||||
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIInterfaceOrientationMask,
|
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIInterfaceOrientationMask,
|
||||||
UIResponder, UITouch, UITouchPhase, UITouchType, UITraitCollection, UIView, UIViewController,
|
UIResponder, UITouch, UITouchPhase, UITouchType, UITraitCollection, UIView, UIViewController,
|
||||||
|
@ -19,8 +20,6 @@ use crate::{
|
||||||
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
|
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
|
||||||
platform::ios::ValidOrientations,
|
platform::ios::ValidOrientations,
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
app_state,
|
|
||||||
event_loop::{EventProxy, EventWrapper},
|
|
||||||
ffi::{UIRectEdge, UIUserInterfaceIdiom},
|
ffi::{UIRectEdge, UIUserInterfaceIdiom},
|
||||||
window::PlatformSpecificWindowBuilderAttributes,
|
window::PlatformSpecificWindowBuilderAttributes,
|
||||||
DeviceId, Fullscreen,
|
DeviceId, Fullscreen,
|
||||||
|
@ -41,18 +40,21 @@ declare_class!(
|
||||||
unsafe impl WinitView {
|
unsafe impl WinitView {
|
||||||
#[method(drawRect:)]
|
#[method(drawRect:)]
|
||||||
fn draw_rect(&self, rect: CGRect) {
|
fn draw_rect(&self, rect: CGRect) {
|
||||||
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
unsafe {
|
app_state::handle_nonuser_event(
|
||||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
mtm,
|
||||||
|
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
window_id: RootWindowId(window.id()),
|
window_id: RootWindowId(window.id()),
|
||||||
event: WindowEvent::RedrawRequested,
|
event: WindowEvent::RedrawRequested,
|
||||||
}));
|
}),
|
||||||
}
|
);
|
||||||
let _: () = unsafe { msg_send![super(self), drawRect: rect] };
|
let _: () = unsafe { msg_send![super(self), drawRect: rect] };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(layoutSubviews)]
|
#[method(layoutSubviews)]
|
||||||
fn layout_subviews(&self) {
|
fn layout_subviews(&self) {
|
||||||
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
|
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
|
||||||
|
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
|
@ -75,16 +77,18 @@ declare_class!(
|
||||||
self.setFrame(window_bounds);
|
self.setFrame(window_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
app_state::handle_nonuser_event(
|
||||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
mtm,
|
||||||
|
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
window_id: RootWindowId(window.id()),
|
window_id: RootWindowId(window.id()),
|
||||||
event: WindowEvent::Resized(size),
|
event: WindowEvent::Resized(size),
|
||||||
}));
|
}),
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(setContentScaleFactor:)]
|
#[method(setContentScaleFactor:)]
|
||||||
fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) {
|
fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) {
|
||||||
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
let _: () =
|
let _: () =
|
||||||
unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] };
|
unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] };
|
||||||
|
|
||||||
|
@ -112,17 +116,19 @@ declare_class!(
|
||||||
let screen_space = screen.coordinateSpace();
|
let screen_space = screen.coordinateSpace();
|
||||||
let screen_frame = self.convertRect_toCoordinateSpace(bounds, &screen_space);
|
let screen_frame = self.convertRect_toCoordinateSpace(bounds, &screen_space);
|
||||||
let size = crate::dpi::LogicalSize {
|
let size = crate::dpi::LogicalSize {
|
||||||
width: screen_frame.size.width as _,
|
width: screen_frame.size.width as f64,
|
||||||
height: screen_frame.size.height as _,
|
height: screen_frame.size.height as f64,
|
||||||
};
|
};
|
||||||
let window_id = RootWindowId(window.id());
|
let window_id = RootWindowId(window.id());
|
||||||
unsafe {
|
|
||||||
app_state::handle_nonuser_events(
|
app_state::handle_nonuser_events(
|
||||||
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
mtm,
|
||||||
|
std::iter::once(EventWrapper::ScaleFactorChanged(
|
||||||
|
app_state::ScaleFactorChanged {
|
||||||
window,
|
window,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
suggested_size: size,
|
suggested_size: size.to_physical(scale_factor),
|
||||||
}))
|
},
|
||||||
|
))
|
||||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||||
Event::WindowEvent {
|
Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
|
@ -131,7 +137,6 @@ declare_class!(
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[method(touchesBegan:withEvent:)]
|
#[method(touchesBegan:withEvent:)]
|
||||||
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||||
|
@ -255,9 +260,8 @@ impl WinitView {
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
unsafe {
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
app_state::handle_nonuser_events(touch_events);
|
app_state::handle_nonuser_events(mtm, touch_events);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,23 +435,27 @@ declare_class!(
|
||||||
unsafe impl WinitUIWindow {
|
unsafe impl WinitUIWindow {
|
||||||
#[method(becomeKeyWindow)]
|
#[method(becomeKeyWindow)]
|
||||||
fn become_key_window(&self) {
|
fn become_key_window(&self) {
|
||||||
unsafe {
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
app_state::handle_nonuser_event(
|
||||||
|
mtm,
|
||||||
|
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
window_id: RootWindowId(self.id()),
|
window_id: RootWindowId(self.id()),
|
||||||
event: WindowEvent::Focused(true),
|
event: WindowEvent::Focused(true),
|
||||||
}));
|
}),
|
||||||
}
|
);
|
||||||
let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
|
let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(resignKeyWindow)]
|
#[method(resignKeyWindow)]
|
||||||
fn resign_key_window(&self) {
|
fn resign_key_window(&self) {
|
||||||
unsafe {
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
app_state::handle_nonuser_event(
|
||||||
|
mtm,
|
||||||
|
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
window_id: RootWindowId(self.id()),
|
window_id: RootWindowId(self.id()),
|
||||||
event: WindowEvent::Focused(false),
|
event: WindowEvent::Focused(false),
|
||||||
}));
|
}),
|
||||||
}
|
);
|
||||||
let _: () = unsafe { msg_send![super(self), resignKeyWindow] };
|
let _: () = unsafe { msg_send![super(self), resignKeyWindow] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -455,7 +463,7 @@ declare_class!(
|
||||||
|
|
||||||
impl WinitUIWindow {
|
impl WinitUIWindow {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
_mtm: MainThreadMarker,
|
mtm: MainThreadMarker,
|
||||||
window_attributes: &WindowAttributes,
|
window_attributes: &WindowAttributes,
|
||||||
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
||||||
frame: CGRect,
|
frame: CGRect,
|
||||||
|
@ -468,12 +476,12 @@ impl WinitUIWindow {
|
||||||
match window_attributes.fullscreen.clone().map(Into::into) {
|
match window_attributes.fullscreen.clone().map(Into::into) {
|
||||||
Some(Fullscreen::Exclusive(ref video_mode)) => {
|
Some(Fullscreen::Exclusive(ref video_mode)) => {
|
||||||
let monitor = video_mode.monitor();
|
let monitor = video_mode.monitor();
|
||||||
let screen = monitor.ui_screen();
|
let screen = monitor.ui_screen(mtm);
|
||||||
screen.setCurrentMode(Some(&video_mode.screen_mode.0));
|
screen.setCurrentMode(Some(video_mode.screen_mode(mtm)));
|
||||||
this.setScreen(screen);
|
this.setScreen(screen);
|
||||||
}
|
}
|
||||||
Some(Fullscreen::Borderless(Some(ref monitor))) => {
|
Some(Fullscreen::Borderless(Some(ref monitor))) => {
|
||||||
let screen = monitor.ui_screen();
|
let screen = monitor.ui_screen(mtm);
|
||||||
this.setScreen(screen);
|
this.setScreen(screen);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -500,20 +508,20 @@ declare_class!(
|
||||||
unsafe impl WinitApplicationDelegate {
|
unsafe impl WinitApplicationDelegate {
|
||||||
#[method(application:didFinishLaunchingWithOptions:)]
|
#[method(application:didFinishLaunchingWithOptions:)]
|
||||||
fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
|
fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
|
||||||
unsafe {
|
app_state::did_finish_launching(MainThreadMarker::new().unwrap());
|
||||||
app_state::did_finish_launching();
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(applicationDidBecomeActive:)]
|
#[method(applicationDidBecomeActive:)]
|
||||||
fn did_become_active(&self, _application: &UIApplication) {
|
fn did_become_active(&self, _application: &UIApplication) {
|
||||||
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
|
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(applicationWillResignActive:)]
|
#[method(applicationWillResignActive:)]
|
||||||
fn will_resign_active(&self, _application: &UIApplication) {
|
fn will_resign_active(&self, _application: &UIApplication) {
|
||||||
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
|
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(applicationWillEnterForeground:)]
|
#[method(applicationWillEnterForeground:)]
|
||||||
|
@ -539,10 +547,9 @@ declare_class!(
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe {
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
app_state::handle_nonuser_events(events);
|
app_state::handle_nonuser_events(mtm, events);
|
||||||
app_state::terminated();
|
app_state::terminated(mtm);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,6 +8,7 @@ use objc2::runtime::AnyObject;
|
||||||
use objc2::{class, msg_send};
|
use objc2::{class, msg_send};
|
||||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, UiKitDisplayHandle, UiKitWindowHandle};
|
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, UiKitDisplayHandle, UiKitWindowHandle};
|
||||||
|
|
||||||
|
use super::app_state::EventWrapper;
|
||||||
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
|
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
|
||||||
use super::view::{WinitUIWindow, WinitView, WinitViewController};
|
use super::view::{WinitUIWindow, WinitView, WinitViewController};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -17,9 +18,7 @@ use crate::{
|
||||||
icon::Icon,
|
icon::Icon,
|
||||||
platform::ios::{ScreenEdge, ValidOrientations},
|
platform::ios::{ScreenEdge, ValidOrientations},
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
app_state,
|
app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
|
||||||
event_loop::{EventProxy, EventWrapper},
|
|
||||||
monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
|
|
||||||
},
|
},
|
||||||
window::{
|
window::{
|
||||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||||
|
@ -53,8 +52,8 @@ impl Inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_redraw(&self) {
|
pub fn request_redraw(&self) {
|
||||||
unsafe {
|
|
||||||
if self.gl_or_metal_backed {
|
if self.gl_or_metal_backed {
|
||||||
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
// `setNeedsDisplay` does nothing on UIViews which are directly backed by CAEAGLLayer or CAMetalLayer.
|
// `setNeedsDisplay` does nothing on UIViews which are directly backed by CAEAGLLayer or CAMetalLayer.
|
||||||
// Ordinarily the OS sets up a bunch of UIKit state before calling drawRect: on a UIView, but when using
|
// Ordinarily the OS sets up a bunch of UIKit state before calling drawRect: on a UIView, but when using
|
||||||
// raw or gl/metal for drawing this work is completely avoided.
|
// raw or gl/metal for drawing this work is completely avoided.
|
||||||
|
@ -63,12 +62,11 @@ impl Inner {
|
||||||
// testing.
|
// testing.
|
||||||
//
|
//
|
||||||
// https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc
|
// https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc
|
||||||
app_state::queue_gl_or_metal_redraw(self.window.clone());
|
app_state::queue_gl_or_metal_redraw(mtm, self.window.clone());
|
||||||
} else {
|
} else {
|
||||||
self.view.setNeedsDisplay();
|
self.view.setNeedsDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pre_present_notify(&self) {}
|
pub fn pre_present_notify(&self) {}
|
||||||
|
|
||||||
|
@ -219,14 +217,17 @@ impl Inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
|
pub(crate) fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
|
||||||
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
let uiscreen = match &monitor {
|
let uiscreen = match &monitor {
|
||||||
Some(Fullscreen::Exclusive(video_mode)) => {
|
Some(Fullscreen::Exclusive(video_mode)) => {
|
||||||
let uiscreen = video_mode.monitor.ui_screen();
|
let uiscreen = video_mode.monitor.ui_screen(mtm);
|
||||||
uiscreen.setCurrentMode(Some(&video_mode.screen_mode.0));
|
uiscreen.setCurrentMode(Some(video_mode.screen_mode(mtm)));
|
||||||
uiscreen.clone()
|
uiscreen.clone()
|
||||||
}
|
}
|
||||||
Some(Fullscreen::Borderless(Some(monitor))) => monitor.ui_screen().clone(),
|
Some(Fullscreen::Borderless(Some(monitor))) => monitor.ui_screen(mtm).clone(),
|
||||||
Some(Fullscreen::Borderless(None)) => self.current_monitor_inner().ui_screen().clone(),
|
Some(Fullscreen::Borderless(None)) => {
|
||||||
|
self.current_monitor_inner().ui_screen(mtm).clone()
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
warn!("`Window::set_fullscreen(None)` ignored on iOS");
|
warn!("`Window::set_fullscreen(None)` ignored on iOS");
|
||||||
return;
|
return;
|
||||||
|
@ -249,8 +250,9 @@ impl Inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||||
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
let monitor = self.current_monitor_inner();
|
let monitor = self.current_monitor_inner();
|
||||||
let uiscreen = monitor.ui_screen();
|
let uiscreen = monitor.ui_screen(mtm);
|
||||||
let screen_space_bounds = self.screen_frame();
|
let screen_space_bounds = self.screen_frame();
|
||||||
let screen_bounds = uiscreen.bounds();
|
let screen_bounds = uiscreen.bounds();
|
||||||
|
|
||||||
|
@ -365,11 +367,11 @@ pub struct Window {
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub(crate) fn new<T>(
|
pub(crate) fn new<T>(
|
||||||
_event_loop: &EventLoopWindowTarget<T>,
|
event_loop: &EventLoopWindowTarget<T>,
|
||||||
window_attributes: WindowAttributes,
|
window_attributes: WindowAttributes,
|
||||||
platform_attributes: PlatformSpecificWindowBuilderAttributes,
|
platform_attributes: PlatformSpecificWindowBuilderAttributes,
|
||||||
) -> Result<Window, RootOsError> {
|
) -> Result<Window, RootOsError> {
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
let mtm = event_loop.mtm;
|
||||||
|
|
||||||
if window_attributes.min_inner_size.is_some() {
|
if window_attributes.min_inner_size.is_some() {
|
||||||
warn!("`WindowAttributes::min_inner_size` is ignored on iOS");
|
warn!("`WindowAttributes::min_inner_size` is ignored on iOS");
|
||||||
|
@ -383,8 +385,8 @@ impl Window {
|
||||||
let main_screen = UIScreen::main(mtm);
|
let main_screen = UIScreen::main(mtm);
|
||||||
let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
|
let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
|
||||||
let screen = match fullscreen {
|
let screen = match fullscreen {
|
||||||
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(),
|
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm),
|
||||||
Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(),
|
Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm),
|
||||||
Some(Fullscreen::Borderless(None)) | None => &main_screen,
|
Some(Fullscreen::Borderless(None)) | None => &main_screen,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -424,7 +426,7 @@ impl Window {
|
||||||
&view_controller,
|
&view_controller,
|
||||||
);
|
);
|
||||||
|
|
||||||
unsafe { app_state::set_key_window(&window) };
|
app_state::set_key_window(mtm, &window);
|
||||||
|
|
||||||
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
|
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
|
||||||
// event on window creation if the DPI factor != 1.0
|
// event on window creation if the DPI factor != 1.0
|
||||||
|
@ -436,17 +438,19 @@ impl Window {
|
||||||
let screen_space = screen.coordinateSpace();
|
let screen_space = screen.coordinateSpace();
|
||||||
let screen_frame = view.convertRect_toCoordinateSpace(bounds, &screen_space);
|
let screen_frame = view.convertRect_toCoordinateSpace(bounds, &screen_space);
|
||||||
let size = crate::dpi::LogicalSize {
|
let size = crate::dpi::LogicalSize {
|
||||||
width: screen_frame.size.width as _,
|
width: screen_frame.size.width as f64,
|
||||||
height: screen_frame.size.height as _,
|
height: screen_frame.size.height as f64,
|
||||||
};
|
};
|
||||||
let window_id = RootWindowId(window.id());
|
let window_id = RootWindowId(window.id());
|
||||||
unsafe {
|
|
||||||
app_state::handle_nonuser_events(
|
app_state::handle_nonuser_events(
|
||||||
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
mtm,
|
||||||
|
std::iter::once(EventWrapper::ScaleFactorChanged(
|
||||||
|
app_state::ScaleFactorChanged {
|
||||||
window: window.clone(),
|
window: window.clone(),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
suggested_size: size,
|
suggested_size: size.to_physical(scale_factor),
|
||||||
}))
|
},
|
||||||
|
))
|
||||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||||
Event::WindowEvent {
|
Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
|
@ -455,7 +459,6 @@ impl Window {
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let inner = Inner {
|
let inner = Inner {
|
||||||
window,
|
window,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use icrate::Foundation::NSObject;
|
||||||
use objc2::{declare_class, msg_send, mutability, ClassType};
|
use objc2::{declare_class, msg_send, mutability, ClassType};
|
||||||
|
|
||||||
use super::appkit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
use super::appkit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
||||||
use super::{app_state::AppState, event::EventWrapper, DEVICE_ID};
|
use super::{app_state::AppState, DEVICE_ID};
|
||||||
use crate::event::{DeviceEvent, ElementState, Event};
|
use crate::event::{DeviceEvent, ElementState, Event};
|
||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
|
@ -96,5 +96,5 @@ fn queue_device_event(event: DeviceEvent) {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
event,
|
event,
|
||||||
};
|
};
|
||||||
AppState::queue_event(EventWrapper::StaticEvent(event));
|
AppState::queue_event(event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,29 +6,24 @@ use std::{
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
Arc, Mutex, MutexGuard,
|
mpsc, Arc, Mutex, MutexGuard,
|
||||||
},
|
},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp};
|
use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp};
|
||||||
use icrate::Foundation::{is_main_thread, NSSize};
|
use icrate::Foundation::{is_main_thread, NSSize};
|
||||||
use objc2::rc::autoreleasepool;
|
use objc2::rc::{autoreleasepool, Id};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent};
|
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent};
|
||||||
|
use super::{
|
||||||
|
event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never, window::WinitWindow,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::LogicalSize,
|
dpi::PhysicalSize,
|
||||||
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
|
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
|
||||||
platform_impl::platform::{
|
|
||||||
event::{EventProxy, EventWrapper},
|
|
||||||
event_loop::PanicInfo,
|
|
||||||
menu,
|
|
||||||
observer::EventLoopWaker,
|
|
||||||
util::Never,
|
|
||||||
window::WinitWindow,
|
|
||||||
},
|
|
||||||
window::WindowId,
|
window::WindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,6 +49,7 @@ pub(crate) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>,
|
||||||
struct EventLoopHandler<T: 'static> {
|
struct EventLoopHandler<T: 'static> {
|
||||||
callback: Weak<Callback<T>>,
|
callback: Weak<Callback<T>>,
|
||||||
window_target: Rc<RootWindowTarget<T>>,
|
window_target: Rc<RootWindowTarget<T>>,
|
||||||
|
receiver: Rc<mpsc::Receiver<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> EventLoopHandler<T> {
|
impl<T> EventLoopHandler<T> {
|
||||||
|
@ -103,7 +99,7 @@ impl<T> EventHandler for EventLoopHandler<T> {
|
||||||
|
|
||||||
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
|
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
|
||||||
self.with_callback(|this, mut callback| {
|
self.with_callback(|this, mut callback| {
|
||||||
for event in this.window_target.p.receiver.try_iter() {
|
for event in this.receiver.try_iter() {
|
||||||
if let ControlFlow::ExitWithCode(code) = *control_flow {
|
if let ControlFlow::ExitWithCode(code) = *control_flow {
|
||||||
// XXX: why isn't event dispatching simply skipped after control_flow = ExitWithCode?
|
// XXX: why isn't event dispatching simply skipped after control_flow = ExitWithCode?
|
||||||
let dummy = &mut ControlFlow::ExitWithCode(code);
|
let dummy = &mut ControlFlow::ExitWithCode(code);
|
||||||
|
@ -116,6 +112,16 @@ impl<T> EventHandler for EventLoopHandler<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum EventWrapper {
|
||||||
|
StaticEvent(Event<Never>),
|
||||||
|
ScaleFactorChanged {
|
||||||
|
window: Id<WinitWindow>,
|
||||||
|
suggested_size: PhysicalSize<u32>,
|
||||||
|
scale_factor: f64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Handler {
|
struct Handler {
|
||||||
stop_app_on_launch: AtomicBool,
|
stop_app_on_launch: AtomicBool,
|
||||||
|
@ -313,15 +319,10 @@ impl Handler {
|
||||||
self.callback.lock().unwrap().is_some()
|
self.callback.lock().unwrap().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_nonuser_event(&self, wrapper: EventWrapper) {
|
fn handle_nonuser_event(&self, event: Event<Never>) {
|
||||||
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||||
match wrapper {
|
|
||||||
EventWrapper::StaticEvent(event) => {
|
|
||||||
callback.handle_nonuser_event(event, &mut self.control_flow.lock().unwrap())
|
callback.handle_nonuser_event(event, &mut self.control_flow.lock().unwrap())
|
||||||
}
|
}
|
||||||
EventWrapper::EventProxy(proxy) => self.handle_proxy(proxy, callback),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_user_events(&self) {
|
fn handle_user_events(&self) {
|
||||||
|
@ -332,12 +333,12 @@ impl Handler {
|
||||||
|
|
||||||
fn handle_scale_factor_changed_event(
|
fn handle_scale_factor_changed_event(
|
||||||
&self,
|
&self,
|
||||||
callback: &mut Box<dyn EventHandler + 'static>,
|
|
||||||
window: &WinitWindow,
|
window: &WinitWindow,
|
||||||
suggested_size: LogicalSize<f64>,
|
suggested_size: PhysicalSize<u32>,
|
||||||
scale_factor: f64,
|
scale_factor: f64,
|
||||||
) {
|
) {
|
||||||
let new_inner_size = Arc::new(Mutex::new(suggested_size.to_physical(scale_factor)));
|
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||||
|
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||||
let event = Event::WindowEvent {
|
let event = Event::WindowEvent {
|
||||||
window_id: WindowId(window.id()),
|
window_id: WindowId(window.id()),
|
||||||
event: WindowEvent::ScaleFactorChanged {
|
event: WindowEvent::ScaleFactorChanged {
|
||||||
|
@ -354,20 +355,6 @@ impl Handler {
|
||||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||||
window.setContentSize(size);
|
window.setContentSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_proxy(&self, proxy: EventProxy, callback: &mut Box<dyn EventHandler + 'static>) {
|
|
||||||
match proxy {
|
|
||||||
EventProxy::DpiChangedProxy {
|
|
||||||
window,
|
|
||||||
suggested_size,
|
|
||||||
scale_factor,
|
|
||||||
} => self.handle_scale_factor_changed_event(
|
|
||||||
callback,
|
|
||||||
&window,
|
|
||||||
suggested_size,
|
|
||||||
scale_factor,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,10 +374,12 @@ impl AppState {
|
||||||
pub unsafe fn set_callback<T>(
|
pub unsafe fn set_callback<T>(
|
||||||
callback: Weak<Callback<T>>,
|
callback: Weak<Callback<T>>,
|
||||||
window_target: Rc<RootWindowTarget<T>>,
|
window_target: Rc<RootWindowTarget<T>>,
|
||||||
|
receiver: Rc<mpsc::Receiver<T>>,
|
||||||
) {
|
) {
|
||||||
*HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler {
|
*HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler {
|
||||||
callback,
|
callback,
|
||||||
window_target,
|
window_target,
|
||||||
|
receiver,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +424,7 @@ impl AppState {
|
||||||
|
|
||||||
pub fn exit() -> i32 {
|
pub fn exit() -> i32 {
|
||||||
HANDLER.set_in_callback(true);
|
HANDLER.set_in_callback(true);
|
||||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::LoopExiting));
|
HANDLER.handle_nonuser_event(Event::LoopExiting);
|
||||||
HANDLER.set_in_callback(false);
|
HANDLER.set_in_callback(false);
|
||||||
HANDLER.exit();
|
HANDLER.exit();
|
||||||
Self::clear_callback();
|
Self::clear_callback();
|
||||||
|
@ -448,12 +437,10 @@ impl AppState {
|
||||||
|
|
||||||
pub fn dispatch_init_events() {
|
pub fn dispatch_init_events() {
|
||||||
HANDLER.set_in_callback(true);
|
HANDLER.set_in_callback(true);
|
||||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents(
|
HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init));
|
||||||
StartCause::Init,
|
|
||||||
)));
|
|
||||||
// NB: For consistency all platforms must emit a 'resumed' event even though macOS
|
// NB: For consistency all platforms must emit a 'resumed' event even though macOS
|
||||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed));
|
HANDLER.handle_nonuser_event(Event::Resumed);
|
||||||
HANDLER.set_in_callback(false);
|
HANDLER.set_in_callback(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,7 +531,7 @@ impl AppState {
|
||||||
ControlFlow::ExitWithCode(_) => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"),
|
ControlFlow::ExitWithCode(_) => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"),
|
||||||
};
|
};
|
||||||
HANDLER.set_in_callback(true);
|
HANDLER.set_in_callback(true);
|
||||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents(cause)));
|
HANDLER.handle_nonuser_event(Event::NewEvents(cause));
|
||||||
HANDLER.set_in_callback(false);
|
HANDLER.set_in_callback(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,10 +551,10 @@ impl AppState {
|
||||||
// Redraw request might come out of order from the OS.
|
// Redraw request might come out of order from the OS.
|
||||||
// -> Don't go back into the callback when our callstack originates from there
|
// -> Don't go back into the callback when our callstack originates from there
|
||||||
if !HANDLER.in_callback.swap(true, Ordering::AcqRel) {
|
if !HANDLER.in_callback.swap(true, Ordering::AcqRel) {
|
||||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
HANDLER.handle_nonuser_event(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::RedrawRequested,
|
event: WindowEvent::RedrawRequested,
|
||||||
}));
|
});
|
||||||
HANDLER.set_in_callback(false);
|
HANDLER.set_in_callback(false);
|
||||||
|
|
||||||
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested events
|
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested events
|
||||||
|
@ -578,11 +565,25 @@ impl AppState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn queue_event(wrapper: EventWrapper) {
|
pub fn queue_event(event: Event<Never>) {
|
||||||
if !is_main_thread() {
|
if !is_main_thread() {
|
||||||
panic!("Event queued from different thread: {wrapper:#?}");
|
panic!("Event queued from different thread: {event:#?}");
|
||||||
}
|
}
|
||||||
HANDLER.events().push_back(wrapper);
|
HANDLER.events().push_back(EventWrapper::StaticEvent(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_static_scale_factor_changed_event(
|
||||||
|
window: Id<WinitWindow>,
|
||||||
|
suggested_size: PhysicalSize<u32>,
|
||||||
|
scale_factor: f64,
|
||||||
|
) {
|
||||||
|
HANDLER
|
||||||
|
.events()
|
||||||
|
.push_back(EventWrapper::ScaleFactorChanged {
|
||||||
|
window,
|
||||||
|
suggested_size,
|
||||||
|
scale_factor,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop() {
|
pub fn stop() {
|
||||||
|
@ -614,17 +615,32 @@ impl AppState {
|
||||||
HANDLER.set_in_callback(true);
|
HANDLER.set_in_callback(true);
|
||||||
HANDLER.handle_user_events();
|
HANDLER.handle_user_events();
|
||||||
for event in HANDLER.take_events() {
|
for event in HANDLER.take_events() {
|
||||||
|
match event {
|
||||||
|
EventWrapper::StaticEvent(event) => {
|
||||||
HANDLER.handle_nonuser_event(event);
|
HANDLER.handle_nonuser_event(event);
|
||||||
}
|
}
|
||||||
|
EventWrapper::ScaleFactorChanged {
|
||||||
|
window,
|
||||||
|
suggested_size,
|
||||||
|
scale_factor,
|
||||||
|
} => {
|
||||||
|
HANDLER.handle_scale_factor_changed_event(
|
||||||
|
&window,
|
||||||
|
suggested_size,
|
||||||
|
scale_factor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for window_id in HANDLER.should_redraw() {
|
for window_id in HANDLER.should_redraw() {
|
||||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
HANDLER.handle_nonuser_event(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::RedrawRequested,
|
event: WindowEvent::RedrawRequested,
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::AboutToWait));
|
HANDLER.handle_nonuser_event(Event::AboutToWait);
|
||||||
HANDLER.set_in_callback(false);
|
HANDLER.set_in_callback(false);
|
||||||
|
|
||||||
if HANDLER.should_exit() {
|
if HANDLER.should_exit() {
|
||||||
|
|
|
@ -5,15 +5,11 @@ use core_foundation::{
|
||||||
data::{CFDataGetBytePtr, CFDataRef},
|
data::{CFDataGetBytePtr, CFDataRef},
|
||||||
};
|
};
|
||||||
use icrate::Foundation::MainThreadMarker;
|
use icrate::Foundation::MainThreadMarker;
|
||||||
use objc2::rc::Id;
|
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
use super::appkit::{NSEvent, NSEventModifierFlags};
|
use super::appkit::{NSEvent, NSEventModifierFlags};
|
||||||
use super::util::Never;
|
|
||||||
use super::window::WinitWindow;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::LogicalSize,
|
event::{ElementState, KeyEvent, Modifiers},
|
||||||
event::{ElementState, Event, KeyEvent, Modifiers},
|
|
||||||
keyboard::{
|
keyboard::{
|
||||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NativeKey, NativeKeyCode,
|
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NativeKey, NativeKeyCode,
|
||||||
},
|
},
|
||||||
|
@ -21,21 +17,6 @@ use crate::{
|
||||||
platform_impl::platform::ffi,
|
platform_impl::platform::ffi,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) enum EventWrapper {
|
|
||||||
StaticEvent(Event<Never>),
|
|
||||||
EventProxy(EventProxy),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) enum EventProxy {
|
|
||||||
DpiChangedProxy {
|
|
||||||
window: Id<WinitWindow>,
|
|
||||||
suggested_size: LogicalSize<f64>,
|
|
||||||
scale_factor: f64,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct KeyEventExtra {
|
pub struct KeyEventExtra {
|
||||||
pub text_with_all_modifiers: Option<SmolStr>,
|
pub text_with_all_modifiers: Option<SmolStr>,
|
||||||
|
|
|
@ -65,9 +65,10 @@ impl PanicInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct EventLoopWindowTarget<T: 'static> {
|
pub struct EventLoopWindowTarget<T: 'static> {
|
||||||
pub receiver: mpsc::Receiver<T>,
|
|
||||||
mtm: MainThreadMarker,
|
mtm: MainThreadMarker,
|
||||||
|
p: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> EventLoopWindowTarget<T> {
|
impl<T: 'static> EventLoopWindowTarget<T> {
|
||||||
|
@ -107,14 +108,18 @@ impl<T> EventLoopWindowTarget<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventLoop<T: 'static> {
|
pub struct EventLoop<T: 'static> {
|
||||||
|
/// Store a reference to the application for convenience.
|
||||||
|
app: Id<WinitApplication>,
|
||||||
/// The delegate is only weakly referenced by NSApplication, so we keep
|
/// The delegate is only weakly referenced by NSApplication, so we keep
|
||||||
/// it around here as well.
|
/// it around here as well.
|
||||||
_delegate: Id<ApplicationDelegate>,
|
_delegate: Id<ApplicationDelegate>,
|
||||||
|
|
||||||
|
// Event sender and receiver, used for EventLoopProxy.
|
||||||
sender: mpsc::Sender<T>,
|
sender: mpsc::Sender<T>,
|
||||||
|
receiver: Rc<mpsc::Receiver<T>>,
|
||||||
|
|
||||||
window_target: Rc<RootWindowTarget<T>>,
|
window_target: Rc<RootWindowTarget<T>>,
|
||||||
panic_info: Rc<PanicInfo>,
|
panic_info: Rc<PanicInfo>,
|
||||||
mtm: MainThreadMarker,
|
|
||||||
|
|
||||||
/// We make sure that the callback closure is dropped during a panic
|
/// We make sure that the callback closure is dropped during a panic
|
||||||
/// by making the event loop own it.
|
/// by making the event loop own it.
|
||||||
|
@ -177,13 +182,17 @@ impl<T> EventLoop<T> {
|
||||||
|
|
||||||
let (sender, receiver) = mpsc::channel();
|
let (sender, receiver) = mpsc::channel();
|
||||||
Ok(EventLoop {
|
Ok(EventLoop {
|
||||||
|
app,
|
||||||
_delegate: delegate,
|
_delegate: delegate,
|
||||||
sender,
|
sender,
|
||||||
|
receiver: Rc::new(receiver),
|
||||||
window_target: Rc::new(RootWindowTarget {
|
window_target: Rc::new(RootWindowTarget {
|
||||||
p: EventLoopWindowTarget { receiver, mtm },
|
p: EventLoopWindowTarget {
|
||||||
|
mtm,
|
||||||
|
p: PhantomData,
|
||||||
|
},
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
}),
|
}),
|
||||||
mtm,
|
|
||||||
panic_info,
|
panic_info,
|
||||||
_callback: None,
|
_callback: None,
|
||||||
})
|
})
|
||||||
|
@ -231,8 +240,6 @@ impl<T> EventLoop<T> {
|
||||||
self._callback = Some(Rc::clone(&callback));
|
self._callback = Some(Rc::clone(&callback));
|
||||||
|
|
||||||
let exit_code = autoreleasepool(|_| {
|
let exit_code = autoreleasepool(|_| {
|
||||||
let app = NSApplication::shared(self.mtm);
|
|
||||||
|
|
||||||
// A bit of juggling with the callback references to make sure
|
// A bit of juggling with the callback references to make sure
|
||||||
// that `self.callback` is the only owner of the callback.
|
// that `self.callback` is the only owner of the callback.
|
||||||
let weak_cb: Weak<_> = Rc::downgrade(&callback);
|
let weak_cb: Weak<_> = Rc::downgrade(&callback);
|
||||||
|
@ -241,7 +248,11 @@ impl<T> EventLoop<T> {
|
||||||
// # Safety
|
// # Safety
|
||||||
// We make sure to call `AppState::clear_callback` before returning
|
// We make sure to call `AppState::clear_callback` before returning
|
||||||
unsafe {
|
unsafe {
|
||||||
AppState::set_callback(weak_cb, Rc::clone(&self.window_target));
|
AppState::set_callback(
|
||||||
|
weak_cb,
|
||||||
|
Rc::clone(&self.window_target),
|
||||||
|
Rc::clone(&self.receiver),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// catch panics to make sure we can't unwind without clearing the set callback
|
// catch panics to make sure we can't unwind without clearing the set callback
|
||||||
|
@ -257,7 +268,7 @@ impl<T> EventLoop<T> {
|
||||||
debug_assert!(!AppState::is_running());
|
debug_assert!(!AppState::is_running());
|
||||||
AppState::start_running(); // Set is_running = true + dispatch `NewEvents(Init)` + `Resumed`
|
AppState::start_running(); // Set is_running = true + dispatch `NewEvents(Init)` + `Resumed`
|
||||||
}
|
}
|
||||||
unsafe { app.run() };
|
unsafe { self.app.run() };
|
||||||
|
|
||||||
// While the app is running it's possible that we catch a panic
|
// While the app is running it's possible that we catch a panic
|
||||||
// to avoid unwinding across an objective-c ffi boundary, which
|
// to avoid unwinding across an objective-c ffi boundary, which
|
||||||
|
@ -326,7 +337,11 @@ impl<T> EventLoop<T> {
|
||||||
// to ensure that we don't hold on to the callback beyond its (erased)
|
// to ensure that we don't hold on to the callback beyond its (erased)
|
||||||
// lifetime
|
// lifetime
|
||||||
unsafe {
|
unsafe {
|
||||||
AppState::set_callback(weak_cb, Rc::clone(&self.window_target));
|
AppState::set_callback(
|
||||||
|
weak_cb,
|
||||||
|
Rc::clone(&self.window_target),
|
||||||
|
Rc::clone(&self.receiver),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// catch panics to make sure we can't unwind without clearing the set callback
|
// catch panics to make sure we can't unwind without clearing the set callback
|
||||||
|
|
|
@ -31,7 +31,7 @@ use crate::{
|
||||||
platform::scancode::KeyCodeExtScancode,
|
platform::scancode::KeyCodeExtScancode,
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
app_state::AppState,
|
app_state::AppState,
|
||||||
event::{create_key_event, event_mods, EventWrapper},
|
event::{create_key_event, event_mods},
|
||||||
util,
|
util,
|
||||||
window::WinitWindow,
|
window::WinitWindow,
|
||||||
DEVICE_ID,
|
DEVICE_ID,
|
||||||
|
@ -826,7 +826,7 @@ impl WinitView {
|
||||||
window_id: self.window_id(),
|
window_id: self.window_id(),
|
||||||
event,
|
event,
|
||||||
};
|
};
|
||||||
AppState::queue_event(EventWrapper::StaticEvent(event));
|
AppState::queue_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_device_event(&self, event: DeviceEvent) {
|
fn queue_device_event(&self, event: DeviceEvent) {
|
||||||
|
@ -834,7 +834,7 @@ impl WinitView {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
event,
|
event,
|
||||||
};
|
};
|
||||||
AppState::queue_event(EventWrapper::StaticEvent(event));
|
AppState::queue_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale_factor(&self) -> f64 {
|
fn scale_factor(&self) -> f64 {
|
||||||
|
|
|
@ -11,16 +11,15 @@ use objc2::{class, declare_class, msg_send, msg_send_id, mutability, sel, ClassT
|
||||||
use super::appkit::{
|
use super::appkit::{
|
||||||
NSApplicationPresentationOptions, NSFilenamesPboardType, NSPasteboard, NSWindowOcclusionState,
|
NSApplicationPresentationOptions, NSFilenamesPboardType, NSPasteboard, NSWindowOcclusionState,
|
||||||
};
|
};
|
||||||
use crate::{
|
use super::{
|
||||||
dpi::{LogicalPosition, LogicalSize},
|
|
||||||
event::{Event, WindowEvent},
|
|
||||||
platform_impl::platform::{
|
|
||||||
app_state::AppState,
|
app_state::AppState,
|
||||||
event::{EventProxy, EventWrapper},
|
|
||||||
util,
|
util,
|
||||||
window::{get_ns_theme, WinitWindow},
|
window::{get_ns_theme, WinitWindow},
|
||||||
Fullscreen,
|
Fullscreen,
|
||||||
},
|
};
|
||||||
|
use crate::{
|
||||||
|
dpi::{LogicalPosition, LogicalSize},
|
||||||
|
event::{Event, WindowEvent},
|
||||||
window::WindowId,
|
window::WindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -447,7 +446,7 @@ impl WinitWindowDelegate {
|
||||||
window_id: WindowId(self.window.id()),
|
window_id: WindowId(self.window.id()),
|
||||||
event,
|
event,
|
||||||
};
|
};
|
||||||
AppState::queue_event(EventWrapper::StaticEvent(event));
|
AppState::queue_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_static_scale_factor_changed_event(&self) {
|
fn queue_static_scale_factor_changed_event(&self) {
|
||||||
|
@ -457,12 +456,12 @@ impl WinitWindowDelegate {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.state.previous_scale_factor.set(scale_factor);
|
self.state.previous_scale_factor.set(scale_factor);
|
||||||
let wrapper = EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
let suggested_size = self.view_size();
|
||||||
window: self.window.clone(),
|
AppState::queue_static_scale_factor_changed_event(
|
||||||
suggested_size: self.view_size(),
|
self.window.clone(),
|
||||||
|
suggested_size.to_physical(scale_factor),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
});
|
);
|
||||||
AppState::queue_event(wrapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_move_event(&self) {
|
fn emit_move_event(&self) {
|
||||||
|
|
Loading…
Reference in a new issue