mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-02-02 14:56:34 +11:00
[ios] Groundwork for new Redraw API, refactoring AppState, and bugfixes (#1133)
* fix #1087. the CFRunLoopTimer was never started if the user never changed the controlflow. * RedrawRequested ordering matches the new redraw api consistent asserts lots of appstate refactoring to rely less on unsafe, and hopefully make it easier to maintain * ios: dpi bugfix. inputs to setContentScaleFactor are not to be trusted as iOS uses 0.0 as a sentinel value for "default device dpi". the fix is to always go through the getter. * move touch handling onto uiview * update changelog * rustfmt weirdness * fix use option around nullable function pointers in ffi * Document why gl and metal views don't use setNeedsDisplay * change main events cleared observer priority to 0 instead of magic number log when processing non-redraw events when we expect to only be processing redraw events
This commit is contained in:
parent
c99bba1655
commit
bfcd85ab15
6 changed files with 880 additions and 551 deletions
|
@ -33,6 +33,10 @@
|
||||||
- On iOS, add touch pressure information for touch events.
|
- On iOS, add touch pressure information for touch events.
|
||||||
- Implement `raw_window_handle::HasRawWindowHandle` for `Window` type on all supported platforms.
|
- Implement `raw_window_handle::HasRawWindowHandle` for `Window` type on all supported platforms.
|
||||||
- On macOS, fix the signature of `-[NSView drawRect:]`.
|
- On macOS, fix the signature of `-[NSView drawRect:]`.
|
||||||
|
- On iOS, fix the behavior of `ControlFlow::Poll`. It wasn't polling if that was the only mode ever used by the application.
|
||||||
|
- On iOS, fix DPI sent out by views on creation was `0.0` - now it gives a reasonable number.
|
||||||
|
- On iOS, RedrawRequested now works for gl/metal backed views.
|
||||||
|
- On iOS, RedrawRequested is generally ordered after EventsCleared.
|
||||||
|
|
||||||
# 0.20.0 Alpha 2 (2019-07-09)
|
# 0.20.0 Alpha 2 (2019-07-09)
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,7 +16,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::platform_impl::platform::{
|
use crate::platform_impl::platform::{
|
||||||
app_state::AppState,
|
app_state,
|
||||||
ffi::{
|
ffi::{
|
||||||
id, kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes,
|
id, kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes,
|
||||||
kCFRunLoopDefaultMode, kCFRunLoopEntry, kCFRunLoopExit, nil, CFIndex, CFRelease,
|
kCFRunLoopDefaultMode, kCFRunLoopEntry, kCFRunLoopExit, nil, CFIndex, CFRelease,
|
||||||
|
@ -80,7 +80,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
|
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
|
||||||
Note: `EventLoop::run` calls `UIApplicationMain` on iOS"
|
Note: `EventLoop::run` calls `UIApplicationMain` on iOS"
|
||||||
);
|
);
|
||||||
AppState::will_launch(Box::new(EventLoopHandler {
|
app_state::will_launch(Box::new(EventLoopHandler {
|
||||||
f: event_handler,
|
f: event_handler,
|
||||||
event_loop: self.window_target,
|
event_loop: self.window_target,
|
||||||
}));
|
}));
|
||||||
|
@ -155,7 +155,7 @@ impl<T> EventLoopProxy<T> {
|
||||||
let rl = CFRunLoopGetMain();
|
let rl = CFRunLoopGetMain();
|
||||||
// we want all the members of context to be zero/null, except one
|
// we want all the members of context to be zero/null, except one
|
||||||
let mut context: CFRunLoopSourceContext = mem::zeroed();
|
let mut context: CFRunLoopSourceContext = mem::zeroed();
|
||||||
context.perform = event_loop_proxy_handler;
|
context.perform = Some(event_loop_proxy_handler);
|
||||||
let source =
|
let source =
|
||||||
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
|
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
|
||||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||||
|
@ -188,15 +188,40 @@ fn setup_control_flow_observers() {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
match activity {
|
match activity {
|
||||||
kCFRunLoopAfterWaiting => AppState::handle_wakeup_transition(),
|
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(),
|
||||||
kCFRunLoopEntry => unimplemented!(), // not expected to ever happen
|
kCFRunLoopEntry => unimplemented!(), // not expected to ever happen
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
||||||
|
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end
|
||||||
|
// priority to be 0, in order to send EventsCleared before RedrawRequested. This value was
|
||||||
|
// chosen conservatively to guard against apple using different priorities for their redraw
|
||||||
|
// observers in different OS's or on different devices. If it so happens that it's too
|
||||||
|
// conservative, the main symptom would be non-redraw events coming in after `EventsCleared`.
|
||||||
|
//
|
||||||
|
// The value of `0x1e8480` was determined by inspecting stack traces and the associated
|
||||||
|
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
|
||||||
|
//
|
||||||
|
// Also tested to be `0x1e8480` on iPhone 8, iOS 13 beta 4.
|
||||||
|
extern "C" fn control_flow_main_end_handler(
|
||||||
|
_: CFRunLoopObserverRef,
|
||||||
|
activity: CFRunLoopActivity,
|
||||||
|
_: *mut c_void,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
match activity {
|
||||||
|
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(),
|
||||||
|
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||||
|
_ => 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
|
||||||
// without that, LoopDestroyed will get sent after EventsCleared
|
|
||||||
extern "C" fn control_flow_end_handler(
|
extern "C" fn control_flow_end_handler(
|
||||||
_: CFRunLoopObserverRef,
|
_: CFRunLoopObserverRef,
|
||||||
activity: CFRunLoopActivity,
|
activity: CFRunLoopActivity,
|
||||||
|
@ -205,7 +230,7 @@ fn setup_control_flow_observers() {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
match activity {
|
match activity {
|
||||||
kCFRunLoopBeforeWaiting => AppState::handle_events_cleared(),
|
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(),
|
||||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -213,6 +238,7 @@ fn setup_control_flow_observers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let main_loop = CFRunLoopGetMain();
|
let main_loop = CFRunLoopGetMain();
|
||||||
|
|
||||||
let begin_observer = CFRunLoopObserverCreate(
|
let begin_observer = CFRunLoopObserverCreate(
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
kCFRunLoopEntry | kCFRunLoopAfterWaiting,
|
kCFRunLoopEntry | kCFRunLoopAfterWaiting,
|
||||||
|
@ -222,6 +248,17 @@ fn setup_control_flow_observers() {
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
);
|
);
|
||||||
CFRunLoopAddObserver(main_loop, begin_observer, kCFRunLoopDefaultMode);
|
CFRunLoopAddObserver(main_loop, begin_observer, kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
|
let main_end_observer = CFRunLoopObserverCreate(
|
||||||
|
ptr::null_mut(),
|
||||||
|
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
||||||
|
1, // repeat = true
|
||||||
|
0, // see comment on `control_flow_main_end_handler`
|
||||||
|
control_flow_main_end_handler,
|
||||||
|
ptr::null_mut(),
|
||||||
|
);
|
||||||
|
CFRunLoopAddObserver(main_loop, main_end_observer, kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
let end_observer = CFRunLoopObserverCreate(
|
let end_observer = CFRunLoopObserverCreate(
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
||||||
|
|
|
@ -331,14 +331,14 @@ pub enum CFRunLoopTimerContext {}
|
||||||
pub struct CFRunLoopSourceContext {
|
pub struct CFRunLoopSourceContext {
|
||||||
pub version: CFIndex,
|
pub version: CFIndex,
|
||||||
pub info: *mut c_void,
|
pub info: *mut c_void,
|
||||||
pub retain: extern "C" fn(*const c_void) -> *const c_void,
|
pub retain: Option<extern "C" fn(*const c_void) -> *const c_void>,
|
||||||
pub release: extern "C" fn(*const c_void),
|
pub release: Option<extern "C" fn(*const c_void)>,
|
||||||
pub copyDescription: extern "C" fn(*const c_void) -> CFStringRef,
|
pub copyDescription: Option<extern "C" fn(*const c_void) -> CFStringRef>,
|
||||||
pub equal: extern "C" fn(*const c_void, *const c_void) -> Boolean,
|
pub equal: Option<extern "C" fn(*const c_void, *const c_void) -> Boolean>,
|
||||||
pub hash: extern "C" fn(*const c_void) -> CFHashCode,
|
pub hash: Option<extern "C" fn(*const c_void) -> CFHashCode>,
|
||||||
pub schedule: extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode),
|
pub schedule: Option<extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode)>,
|
||||||
pub cancel: extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode),
|
pub cancel: Option<extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode)>,
|
||||||
pub perform: extern "C" fn(*mut c_void),
|
pub perform: Option<extern "C" fn(*mut c_void)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NSString: Sized {
|
pub trait NSString: Sized {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
|
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
|
||||||
platform::ios::MonitorHandleExtIOS,
|
platform::ios::MonitorHandleExtIOS,
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
app_state::{self, AppState, OSCapabilities},
|
app_state::{self, OSCapabilities},
|
||||||
event_loop,
|
event_loop,
|
||||||
ffi::{
|
ffi::{
|
||||||
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
|
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
|
||||||
|
@ -101,7 +101,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||||
extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
|
extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let window: id = msg_send![object, window];
|
let window: id = msg_send![object, window];
|
||||||
AppState::handle_nonuser_event(Event::WindowEvent {
|
app_state::handle_nonuser_event(Event::WindowEvent {
|
||||||
window_id: RootWindowId(window.into()),
|
window_id: RootWindowId(window.into()),
|
||||||
event: WindowEvent::RedrawRequested,
|
event: WindowEvent::RedrawRequested,
|
||||||
});
|
});
|
||||||
|
@ -112,6 +112,9 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||||
|
|
||||||
extern "C" fn layout_subviews(object: &Object, _: Sel) {
|
extern "C" fn layout_subviews(object: &Object, _: Sel) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let superclass: &'static Class = msg_send![object, superclass];
|
||||||
|
let () = msg_send![super(object, superclass), layoutSubviews];
|
||||||
|
|
||||||
let window: id = msg_send![object, window];
|
let window: id = msg_send![object, window];
|
||||||
let bounds: CGRect = msg_send![window, bounds];
|
let bounds: CGRect = msg_send![window, bounds];
|
||||||
let screen: id = msg_send![window, screen];
|
let screen: id = msg_send![window, screen];
|
||||||
|
@ -122,12 +125,127 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||||
width: screen_frame.size.width as _,
|
width: screen_frame.size.width as _,
|
||||||
height: screen_frame.size.height as _,
|
height: screen_frame.size.height as _,
|
||||||
};
|
};
|
||||||
AppState::handle_nonuser_event(Event::WindowEvent {
|
app_state::handle_nonuser_event(Event::WindowEvent {
|
||||||
window_id: RootWindowId(window.into()),
|
window_id: RootWindowId(window.into()),
|
||||||
event: WindowEvent::Resized(size),
|
event: WindowEvent::Resized(size),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn set_content_scale_factor(
|
||||||
|
object: &mut Object,
|
||||||
|
_: Sel,
|
||||||
|
untrusted_hidpi_factor: CGFloat,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
let superclass: &'static Class = msg_send![object, superclass];
|
let superclass: &'static Class = msg_send![object, superclass];
|
||||||
let () = msg_send![super(object, superclass), layoutSubviews];
|
let () = msg_send![
|
||||||
|
super(object, superclass),
|
||||||
|
setContentScaleFactor: untrusted_hidpi_factor
|
||||||
|
];
|
||||||
|
|
||||||
|
// On launch, iOS sets the contentScaleFactor to 0.0. This is a sentinel value that
|
||||||
|
// iOS appears to use to "reset" the contentScaleFactor to the device specific
|
||||||
|
// default value.
|
||||||
|
//
|
||||||
|
// The workaround is to not trust the value received by this function, and always
|
||||||
|
// go through the getter.
|
||||||
|
let hidpi_factor: CGFloat = msg_send![object, contentScaleFactor];
|
||||||
|
assert!(
|
||||||
|
!hidpi_factor.is_nan()
|
||||||
|
&& hidpi_factor.is_finite()
|
||||||
|
&& hidpi_factor.is_sign_positive()
|
||||||
|
&& hidpi_factor > 0.0,
|
||||||
|
"invalid hidpi_factor set on UIWindow",
|
||||||
|
);
|
||||||
|
|
||||||
|
let window: id = msg_send![object, window];
|
||||||
|
|
||||||
|
let bounds: CGRect = msg_send![object, bounds];
|
||||||
|
let screen: id = msg_send![window, screen];
|
||||||
|
let screen_space: id = msg_send![screen, coordinateSpace];
|
||||||
|
let screen_frame: CGRect =
|
||||||
|
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
|
||||||
|
let size = crate::dpi::LogicalSize {
|
||||||
|
width: screen_frame.size.width as _,
|
||||||
|
height: screen_frame.size.height as _,
|
||||||
|
};
|
||||||
|
app_state::handle_nonuser_events(
|
||||||
|
std::iter::once(Event::WindowEvent {
|
||||||
|
window_id: RootWindowId(window.into()),
|
||||||
|
event: WindowEvent::HiDpiFactorChanged(hidpi_factor as _),
|
||||||
|
})
|
||||||
|
.chain(std::iter::once(Event::WindowEvent {
|
||||||
|
window_id: RootWindowId(window.into()),
|
||||||
|
event: WindowEvent::Resized(size),
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn handle_touches(object: &Object, _: Sel, touches: id, _: id) {
|
||||||
|
unsafe {
|
||||||
|
let window: id = msg_send![object, window];
|
||||||
|
let uiscreen: id = msg_send![window, screen];
|
||||||
|
let touches_enum: id = msg_send![touches, objectEnumerator];
|
||||||
|
let mut touch_events = Vec::new();
|
||||||
|
let os_supports_force = app_state::os_capabilities().force_touch;
|
||||||
|
loop {
|
||||||
|
let touch: id = msg_send![touches_enum, nextObject];
|
||||||
|
if touch == nil {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let location: CGPoint = msg_send![touch, locationInView: nil];
|
||||||
|
let touch_type: UITouchType = msg_send![touch, type];
|
||||||
|
let force = if os_supports_force {
|
||||||
|
let trait_collection: id = msg_send![object, traitCollection];
|
||||||
|
let touch_capability: UIForceTouchCapability =
|
||||||
|
msg_send![trait_collection, forceTouchCapability];
|
||||||
|
// Both the OS _and_ the device need to be checked for force touch support.
|
||||||
|
if touch_capability == UIForceTouchCapability::Available {
|
||||||
|
let force: CGFloat = msg_send![touch, force];
|
||||||
|
let max_possible_force: CGFloat =
|
||||||
|
msg_send![touch, maximumPossibleForce];
|
||||||
|
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
|
||||||
|
let angle: CGFloat = msg_send![touch, altitudeAngle];
|
||||||
|
Some(angle as _)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Some(Force::Calibrated {
|
||||||
|
force: force as _,
|
||||||
|
max_possible_force: max_possible_force as _,
|
||||||
|
altitude_angle,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let touch_id = touch as u64;
|
||||||
|
let phase: UITouchPhase = msg_send![touch, phase];
|
||||||
|
let phase = match phase {
|
||||||
|
UITouchPhase::Began => TouchPhase::Started,
|
||||||
|
UITouchPhase::Moved => TouchPhase::Moved,
|
||||||
|
// 2 is UITouchPhase::Stationary and is not expected here
|
||||||
|
UITouchPhase::Ended => TouchPhase::Ended,
|
||||||
|
UITouchPhase::Cancelled => TouchPhase::Cancelled,
|
||||||
|
_ => panic!("unexpected touch phase: {:?}", phase as i32),
|
||||||
|
};
|
||||||
|
|
||||||
|
touch_events.push(Event::WindowEvent {
|
||||||
|
window_id: RootWindowId(window.into()),
|
||||||
|
event: WindowEvent::Touch(Touch {
|
||||||
|
device_id: RootDeviceId(DeviceId { uiscreen }),
|
||||||
|
id: touch_id,
|
||||||
|
location: (location.x as f64, location.y as f64).into(),
|
||||||
|
force,
|
||||||
|
phase,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_state::handle_nonuser_events(touch_events);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +260,28 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||||
sel!(layoutSubviews),
|
sel!(layoutSubviews),
|
||||||
layout_subviews as extern "C" fn(&Object, Sel),
|
layout_subviews as extern "C" fn(&Object, Sel),
|
||||||
);
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(setContentScaleFactor:),
|
||||||
|
set_content_scale_factor as extern "C" fn(&mut Object, Sel, CGFloat),
|
||||||
|
);
|
||||||
|
|
||||||
|
decl.add_method(
|
||||||
|
sel!(touchesBegan:withEvent:),
|
||||||
|
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(touchesMoved:withEvent:),
|
||||||
|
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(touchesEnded:withEvent:),
|
||||||
|
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(touchesCancelled:withEvent:),
|
||||||
|
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
||||||
|
);
|
||||||
|
|
||||||
decl.register()
|
decl.register()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -223,7 +363,7 @@ unsafe fn get_window_class() -> &'static Class {
|
||||||
|
|
||||||
extern "C" fn become_key_window(object: &Object, _: Sel) {
|
extern "C" fn become_key_window(object: &Object, _: Sel) {
|
||||||
unsafe {
|
unsafe {
|
||||||
AppState::handle_nonuser_event(Event::WindowEvent {
|
app_state::handle_nonuser_event(Event::WindowEvent {
|
||||||
window_id: RootWindowId(object.into()),
|
window_id: RootWindowId(object.into()),
|
||||||
event: WindowEvent::Focused(true),
|
event: WindowEvent::Focused(true),
|
||||||
});
|
});
|
||||||
|
@ -233,7 +373,7 @@ unsafe fn get_window_class() -> &'static Class {
|
||||||
|
|
||||||
extern "C" fn resign_key_window(object: &Object, _: Sel) {
|
extern "C" fn resign_key_window(object: &Object, _: Sel) {
|
||||||
unsafe {
|
unsafe {
|
||||||
AppState::handle_nonuser_event(Event::WindowEvent {
|
app_state::handle_nonuser_event(Event::WindowEvent {
|
||||||
window_id: RootWindowId(object.into()),
|
window_id: RootWindowId(object.into()),
|
||||||
event: WindowEvent::Focused(false),
|
event: WindowEvent::Focused(false),
|
||||||
});
|
});
|
||||||
|
@ -241,102 +381,6 @@ unsafe fn get_window_class() -> &'static Class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn handle_touches(object: &Object, _: Sel, touches: id, _: id) {
|
|
||||||
unsafe {
|
|
||||||
let uiscreen = msg_send![object, screen];
|
|
||||||
let touches_enum: id = msg_send![touches, objectEnumerator];
|
|
||||||
let mut touch_events = Vec::new();
|
|
||||||
let os_supports_force = app_state::os_capabilities().force_touch;
|
|
||||||
loop {
|
|
||||||
let touch: id = msg_send![touches_enum, nextObject];
|
|
||||||
if touch == nil {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let location: CGPoint = msg_send![touch, locationInView: nil];
|
|
||||||
let touch_type: UITouchType = msg_send![touch, type];
|
|
||||||
let force = if os_supports_force {
|
|
||||||
let trait_collection: id = msg_send![object, traitCollection];
|
|
||||||
let touch_capability: UIForceTouchCapability =
|
|
||||||
msg_send![trait_collection, forceTouchCapability];
|
|
||||||
// Both the OS _and_ the device need to be checked for force touch support.
|
|
||||||
if touch_capability == UIForceTouchCapability::Available {
|
|
||||||
let force: CGFloat = msg_send![touch, force];
|
|
||||||
let max_possible_force: CGFloat =
|
|
||||||
msg_send![touch, maximumPossibleForce];
|
|
||||||
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
|
|
||||||
let angle: CGFloat = msg_send![touch, altitudeAngle];
|
|
||||||
Some(angle as _)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
Some(Force::Calibrated {
|
|
||||||
force: force as _,
|
|
||||||
max_possible_force: max_possible_force as _,
|
|
||||||
altitude_angle,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let touch_id = touch as u64;
|
|
||||||
let phase: UITouchPhase = msg_send![touch, phase];
|
|
||||||
let phase = match phase {
|
|
||||||
UITouchPhase::Began => TouchPhase::Started,
|
|
||||||
UITouchPhase::Moved => TouchPhase::Moved,
|
|
||||||
// 2 is UITouchPhase::Stationary and is not expected here
|
|
||||||
UITouchPhase::Ended => TouchPhase::Ended,
|
|
||||||
UITouchPhase::Cancelled => TouchPhase::Cancelled,
|
|
||||||
_ => panic!("unexpected touch phase: {:?}", phase as i32),
|
|
||||||
};
|
|
||||||
|
|
||||||
touch_events.push(Event::WindowEvent {
|
|
||||||
window_id: RootWindowId(object.into()),
|
|
||||||
event: WindowEvent::Touch(Touch {
|
|
||||||
device_id: RootDeviceId(DeviceId { uiscreen }),
|
|
||||||
id: touch_id,
|
|
||||||
location: (location.x as f64, location.y as f64).into(),
|
|
||||||
force,
|
|
||||||
phase,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
AppState::handle_nonuser_events(touch_events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn set_content_scale_factor(object: &mut Object, _: Sel, hidpi_factor: CGFloat) {
|
|
||||||
unsafe {
|
|
||||||
let () = msg_send![
|
|
||||||
super(object, class!(UIWindow)),
|
|
||||||
setContentScaleFactor: hidpi_factor
|
|
||||||
];
|
|
||||||
let view_controller: id = msg_send![object, rootViewController];
|
|
||||||
let view: id = msg_send![view_controller, view];
|
|
||||||
let () = msg_send![view, setContentScaleFactor: hidpi_factor];
|
|
||||||
let bounds: CGRect = msg_send![object, bounds];
|
|
||||||
let screen: id = msg_send![object, screen];
|
|
||||||
let screen_space: id = msg_send![screen, coordinateSpace];
|
|
||||||
let screen_frame: CGRect =
|
|
||||||
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
|
|
||||||
let size = crate::dpi::LogicalSize {
|
|
||||||
width: screen_frame.size.width as _,
|
|
||||||
height: screen_frame.size.height as _,
|
|
||||||
};
|
|
||||||
AppState::handle_nonuser_events(
|
|
||||||
std::iter::once(Event::WindowEvent {
|
|
||||||
window_id: RootWindowId(object.into()),
|
|
||||||
event: WindowEvent::HiDpiFactorChanged(hidpi_factor as _),
|
|
||||||
})
|
|
||||||
.chain(std::iter::once(Event::WindowEvent {
|
|
||||||
window_id: RootWindowId(object.into()),
|
|
||||||
event: WindowEvent::Resized(size),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut decl = ClassDecl::new("WinitUIWindow", uiwindow_class)
|
let mut decl = ClassDecl::new("WinitUIWindow", uiwindow_class)
|
||||||
.expect("Failed to declare class `WinitUIWindow`");
|
.expect("Failed to declare class `WinitUIWindow`");
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
|
@ -348,28 +392,6 @@ unsafe fn get_window_class() -> &'static Class {
|
||||||
resign_key_window as extern "C" fn(&Object, Sel),
|
resign_key_window as extern "C" fn(&Object, Sel),
|
||||||
);
|
);
|
||||||
|
|
||||||
decl.add_method(
|
|
||||||
sel!(touchesBegan:withEvent:),
|
|
||||||
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
|
||||||
);
|
|
||||||
decl.add_method(
|
|
||||||
sel!(touchesMoved:withEvent:),
|
|
||||||
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
|
||||||
);
|
|
||||||
decl.add_method(
|
|
||||||
sel!(touchesEnded:withEvent:),
|
|
||||||
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
|
||||||
);
|
|
||||||
decl.add_method(
|
|
||||||
sel!(touchesCancelled:withEvent:),
|
|
||||||
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
|
||||||
);
|
|
||||||
|
|
||||||
decl.add_method(
|
|
||||||
sel!(setContentScaleFactor:),
|
|
||||||
set_content_scale_factor as extern "C" fn(&mut Object, Sel, CGFloat),
|
|
||||||
);
|
|
||||||
|
|
||||||
CLASS = Some(decl.register());
|
CLASS = Some(decl.register());
|
||||||
}
|
}
|
||||||
CLASS.unwrap()
|
CLASS.unwrap()
|
||||||
|
@ -388,6 +410,9 @@ pub unsafe fn create_view(
|
||||||
let view: id = msg_send![view, initWithFrame: frame];
|
let view: id = msg_send![view, initWithFrame: frame];
|
||||||
assert!(!view.is_null(), "Failed to initialize `UIView` instance");
|
assert!(!view.is_null(), "Failed to initialize `UIView` instance");
|
||||||
let () = msg_send![view, setMultipleTouchEnabled: YES];
|
let () = msg_send![view, setMultipleTouchEnabled: YES];
|
||||||
|
if let Some(hidpi_factor) = platform_attributes.hidpi_factor {
|
||||||
|
let () = msg_send![view, setContentScaleFactor: hidpi_factor as CGFloat];
|
||||||
|
}
|
||||||
|
|
||||||
view
|
view
|
||||||
}
|
}
|
||||||
|
@ -451,7 +476,7 @@ pub unsafe fn create_view_controller(
|
||||||
// requires main thread
|
// requires main thread
|
||||||
pub unsafe fn create_window(
|
pub unsafe fn create_window(
|
||||||
window_attributes: &WindowAttributes,
|
window_attributes: &WindowAttributes,
|
||||||
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
||||||
frame: CGRect,
|
frame: CGRect,
|
||||||
view_controller: id,
|
view_controller: id,
|
||||||
) -> id {
|
) -> id {
|
||||||
|
@ -465,9 +490,6 @@ pub unsafe fn create_window(
|
||||||
"Failed to initialize `UIWindow` instance"
|
"Failed to initialize `UIWindow` instance"
|
||||||
);
|
);
|
||||||
let () = msg_send![window, setRootViewController: view_controller];
|
let () = msg_send![window, setRootViewController: view_controller];
|
||||||
if let Some(hidpi_factor) = platform_attributes.hidpi_factor {
|
|
||||||
let () = msg_send![window, setContentScaleFactor: hidpi_factor as CGFloat];
|
|
||||||
}
|
|
||||||
match window_attributes.fullscreen {
|
match window_attributes.fullscreen {
|
||||||
Some(Fullscreen::Exclusive(ref video_mode)) => {
|
Some(Fullscreen::Exclusive(ref video_mode)) => {
|
||||||
let uiscreen = video_mode.monitor().ui_screen() as id;
|
let uiscreen = video_mode.monitor().ui_screen() as id;
|
||||||
|
@ -486,17 +508,17 @@ pub unsafe fn create_window(
|
||||||
pub fn create_delegate_class() {
|
pub fn create_delegate_class() {
|
||||||
extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
||||||
unsafe {
|
unsafe {
|
||||||
AppState::did_finish_launching();
|
app_state::did_finish_launching();
|
||||||
}
|
}
|
||||||
YES
|
YES
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
|
extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
|
||||||
unsafe { AppState::handle_nonuser_event(Event::Resumed) }
|
unsafe { app_state::handle_nonuser_event(Event::Resumed) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
|
extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
|
||||||
unsafe { AppState::handle_nonuser_event(Event::Suspended) }
|
unsafe { app_state::handle_nonuser_event(Event::Suspended) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {}
|
extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {}
|
||||||
|
@ -521,8 +543,8 @@ pub fn create_delegate_class() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AppState::handle_nonuser_events(events);
|
app_state::handle_nonuser_events(events);
|
||||||
AppState::terminated();
|
app_state::terminated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
};
|
};
|
||||||
|
|
||||||
use objc::runtime::{Class, Object, NO, YES};
|
use objc::runtime::{Class, Object, BOOL, NO, YES};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{self, LogicalPosition, LogicalSize},
|
dpi::{self, LogicalPosition, LogicalSize},
|
||||||
|
@ -13,8 +13,7 @@ use crate::{
|
||||||
monitor::MonitorHandle as RootMonitorHandle,
|
monitor::MonitorHandle as RootMonitorHandle,
|
||||||
platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations},
|
platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations},
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
app_state::{self, AppState},
|
app_state, event_loop,
|
||||||
event_loop,
|
|
||||||
ffi::{
|
ffi::{
|
||||||
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask,
|
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask,
|
||||||
UIRectEdge, UIScreenOverscanCompensation,
|
UIRectEdge, UIScreenOverscanCompensation,
|
||||||
|
@ -28,6 +27,7 @@ pub struct Inner {
|
||||||
pub window: id,
|
pub window: id,
|
||||||
pub view_controller: id,
|
pub view_controller: id,
|
||||||
pub view: id,
|
pub view: id,
|
||||||
|
gl_or_metal_backed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Inner {
|
impl Drop for Inner {
|
||||||
|
@ -58,7 +58,19 @@ impl Inner {
|
||||||
|
|
||||||
pub fn request_redraw(&self) {
|
pub fn request_redraw(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let () = msg_send![self.view, setNeedsDisplay];
|
if self.gl_or_metal_backed {
|
||||||
|
// `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
|
||||||
|
// raw or gl/metal for drawing this work is completely avoided.
|
||||||
|
//
|
||||||
|
// The docs for `setNeedsDisplay` don't mention `CAMetalLayer`; however, this has been confirmed via
|
||||||
|
// testing.
|
||||||
|
//
|
||||||
|
// https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc
|
||||||
|
app_state::queue_gl_or_metal_redraw(self.window);
|
||||||
|
} else {
|
||||||
|
let () = msg_send![self.view, setNeedsDisplay];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,6 +349,16 @@ impl Window {
|
||||||
};
|
};
|
||||||
|
|
||||||
let view = view::create_view(&window_attributes, &platform_attributes, frame.clone());
|
let view = view::create_view(&window_attributes, &platform_attributes, frame.clone());
|
||||||
|
|
||||||
|
let gl_or_metal_backed = {
|
||||||
|
let view_class: id = msg_send![view, class];
|
||||||
|
let layer_class: id = msg_send![view_class, layerClass];
|
||||||
|
let is_metal: BOOL =
|
||||||
|
msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
|
||||||
|
let is_gl: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
|
||||||
|
is_metal == YES || is_gl == YES
|
||||||
|
};
|
||||||
|
|
||||||
let view_controller =
|
let view_controller =
|
||||||
view::create_view_controller(&window_attributes, &platform_attributes, view);
|
view::create_view_controller(&window_attributes, &platform_attributes, view);
|
||||||
let window = view::create_window(
|
let window = view::create_window(
|
||||||
|
@ -351,9 +373,10 @@ impl Window {
|
||||||
window,
|
window,
|
||||||
view_controller,
|
view_controller,
|
||||||
view,
|
view,
|
||||||
|
gl_or_metal_backed,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
AppState::set_key_window(window);
|
app_state::set_key_window(window);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue