winit-sonoma-fix/src/platform/ios/mod.rs

683 lines
20 KiB
Rust
Raw Normal View History

2016-12-14 00:28:30 +11:00
//! iOS support
//!
//! # Building app
//! To build ios app you will need rustc built for this targets:
//!
//! - armv7-apple-ios
//! - armv7s-apple-ios
//! - i386-apple-ios
//! - aarch64-apple-ios
//! - x86_64-apple-ios
//!
//! Then
//!
//! ```
//! cargo build --target=...
//! ```
//! The simplest way to integrate your app into xcode environment is to build it
//! as a static library. Wrap your main function and export it.
//!
//! ```rust, ignore
//! #[no_mangle]
2018-06-15 09:42:18 +10:00
//! pub extern fn start_winit_app() {
2016-12-14 00:28:30 +11:00
//! start_inner()
//! }
//!
//! fn start_inner() {
//! ...
//! }
//!
//! ```
//!
2018-06-15 09:42:18 +10:00
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
2016-12-14 00:28:30 +11:00
//!
2016-12-14 00:37:13 +11:00
//! ```ignore
2018-06-15 09:42:18 +10:00
//! void start_winit_app();
2016-12-14 00:28:30 +11:00
//! ```
//!
2018-06-15 09:42:18 +10:00
//! Use start_winit_app inside your xcode's main function.
2016-12-14 00:28:30 +11:00
//!
//!
//! # App lifecycle and events
//!
//! iOS environment is very different from other platforms and you must be very
//! careful with it's events. Familiarize yourself with
//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
//!
//!
2018-06-15 09:42:18 +10:00
//! This is how those event are represented in winit:
2016-12-14 00:28:30 +11:00
//!
//! - applicationDidBecomeActive is Focused(true)
//! - applicationWillResignActive is Focused(false)
//! - applicationDidEnterBackground is Suspended(true)
//! - applicationWillEnterForeground is Suspended(false)
//! - applicationWillTerminate is Destroyed
2016-12-14 00:28:30 +11:00
//!
//! Keep in mind that after Destroyed event is received every attempt to draw with
2016-12-14 00:28:30 +11:00
//! opengl will result in segfault.
//!
//! Also note that app will not receive Destroyed event if suspended, it will be SIGKILL'ed
2016-12-14 00:28:30 +11:00
2015-06-05 23:38:21 +10:00
#![cfg(target_os = "ios")]
2018-06-15 09:42:18 +10:00
use std::{fmt, mem, ptr};
2016-12-14 00:28:30 +11:00
use std::collections::VecDeque;
2018-06-15 09:42:18 +10:00
use std::os::raw::*;
use objc::declare::ClassDecl;
use objc::runtime::{BOOL, Class, Object, Sel, YES};
use {
CreationError,
CursorState,
Event,
LogicalPosition,
LogicalSize,
MouseCursor,
PhysicalPosition,
PhysicalSize,
WindowAttributes,
WindowEvent,
WindowId as RootEventId,
};
use events::{Touch, TouchPhase};
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
use window::MonitorId as RootMonitorId;
2016-12-14 00:28:30 +11:00
mod ffi;
use self::ffi::{
CFTimeInterval,
CFRunLoopRunInMode,
2018-06-15 09:42:18 +10:00
CGFloat,
CGPoint,
CGRect,
id,
2016-12-14 00:28:30 +11:00
kCFRunLoopDefaultMode,
kCFRunLoopRunHandledSource,
2018-06-15 09:42:18 +10:00
longjmp,
2016-12-14 00:28:30 +11:00
nil,
NSString,
2018-06-15 09:42:18 +10:00
setjmp,
UIApplicationMain,
UIViewAutoresizingFlexibleWidth,
UIViewAutoresizingFlexibleHeight,
2016-12-14 00:28:30 +11:00
};
2018-06-15 09:42:18 +10:00
static mut JMPBUF: [c_int; 27] = [0; 27];
2016-12-14 00:28:30 +11:00
pub struct Window {
2018-06-15 09:42:18 +10:00
delegate_state: *mut DelegateState,
2016-12-14 00:28:30 +11:00
}
2018-06-15 09:42:18 +10:00
unsafe impl Send for Window {}
unsafe impl Sync for Window {}
2016-12-14 00:28:30 +11:00
#[derive(Debug)]
struct DelegateState {
events_queue: VecDeque<Event>,
2016-12-14 00:28:30 +11:00
window: id,
controller: id,
2018-06-15 09:42:18 +10:00
view: id,
size: LogicalSize,
scale: f64,
2016-12-14 00:28:30 +11:00
}
impl DelegateState {
2018-06-15 09:42:18 +10:00
fn new(window: id, controller: id, view: id, size: LogicalSize, scale: f64) -> DelegateState {
2016-12-14 00:28:30 +11:00
DelegateState {
events_queue: VecDeque::new(),
2018-06-15 09:42:18 +10:00
window,
controller,
view,
size,
scale,
2016-12-14 00:28:30 +11:00
}
}
}
2018-06-15 09:42:18 +10:00
impl Drop for DelegateState {
fn drop(&mut self) {
unsafe {
let _: () = msg_send![self.window, release];
let _: () = msg_send![self.controller, release];
let _: () = msg_send![self.view, release];
}
}
}
#[derive(Clone)]
pub struct MonitorId;
impl fmt::Debug for MonitorId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[derive(Debug)]
struct MonitorId {
name: Option<String>,
dimensions: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: f64,
}
let monitor_id_proxy = MonitorId {
name: self.get_name(),
dimensions: self.get_dimensions(),
position: self.get_position(),
hidpi_factor: self.get_hidpi_factor(),
};
monitor_id_proxy.fmt(f)
}
}
2016-12-14 00:28:30 +11:00
impl MonitorId {
2018-06-15 09:42:18 +10:00
#[inline]
pub fn get_uiscreen(&self) -> id {
let class = Class::get("UIScreen").expect("Failed to get class `UIScreen`");
unsafe { msg_send![class, mainScreen] }
}
2016-12-14 00:28:30 +11:00
#[inline]
pub fn get_name(&self) -> Option<String> {
Some("Primary".to_string())
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_dimensions(&self) -> PhysicalSize {
let bounds: CGRect = unsafe { msg_send![self.get_uiscreen(), nativeBounds] };
(bounds.size.width as f64, bounds.size.height as f64).into()
2016-12-14 00:28:30 +11:00
}
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_position(&self) -> PhysicalPosition {
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
// iOS assumes single screen
2018-06-15 09:42:18 +10:00
(0, 0).into()
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_hidpi_factor(&self) -> f64 {
let scale: CGFloat = unsafe { msg_send![self.get_uiscreen(), nativeScale] };
scale as f64
}
2016-12-14 00:28:30 +11:00
}
2015-06-05 23:38:21 +10:00
pub struct EventsLoop {
2018-06-15 09:42:18 +10:00
delegate_state: *mut DelegateState,
}
2016-01-08 02:01:18 +11:00
2017-10-26 05:03:57 +11:00
#[derive(Clone)]
pub struct EventsLoopProxy;
2015-06-05 23:38:21 +10:00
impl EventsLoop {
pub fn new() -> EventsLoop {
2016-12-14 00:28:30 +11:00
unsafe {
2018-06-15 09:42:18 +10:00
if setjmp(mem::transmute(&mut JMPBUF)) != 0 {
let app_class = Class::get("UIApplication").expect("Failed to get class `UIApplication`");
let app: id = msg_send![app_class, sharedApplication];
2016-12-14 00:28:30 +11:00
let delegate: id = msg_send![app, delegate];
2018-06-15 09:42:18 +10:00
let state: *mut c_void = *(&*delegate).get_ivar("winitState");
let delegate_state = state as *mut DelegateState;
return EventsLoop { delegate_state };
2016-12-14 00:28:30 +11:00
}
}
create_view_class();
2018-06-15 09:42:18 +10:00
create_delegate_class();
start_app();
2016-12-14 00:28:30 +11:00
2018-06-15 09:42:18 +10:00
panic!("Couldn't create `UIApplication`!")
2015-06-05 23:38:21 +10:00
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
2018-06-15 09:42:18 +10:00
let mut rb = VecDeque::with_capacity(1);
rb.push_back(MonitorId);
rb
}
2016-12-14 00:28:30 +11:00
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
MonitorId
}
2016-12-14 00:28:30 +11:00
pub fn poll_events<F>(&mut self, mut callback: F)
where F: FnMut(::Event)
{
unsafe {
let state = &mut *self.delegate_state;
2016-12-14 00:28:30 +11:00
if let Some(event) = state.events_queue.pop_front() {
callback(event);
return;
2016-12-14 00:28:30 +11:00
}
// jump hack, so we won't quit on willTerminate event before processing it
2018-06-15 09:42:18 +10:00
if setjmp(mem::transmute(&mut JMPBUF)) != 0 {
if let Some(event) = state.events_queue.pop_front() {
callback(event);
return;
}
2016-12-14 00:28:30 +11:00
}
// run runloop
let seconds: CFTimeInterval = 0.000002;
while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {}
2016-12-14 00:28:30 +11:00
if let Some(event) = state.events_queue.pop_front() {
callback(event)
2016-12-14 00:28:30 +11:00
}
}
}
2016-12-14 00:28:30 +11:00
pub fn run_forever<F>(&mut self, mut callback: F)
where F: FnMut(::Event) -> ::ControlFlow,
{
// Yeah that's a very bad implementation.
loop {
let mut control_flow = ::ControlFlow::Continue;
self.poll_events(|e| {
if let ::ControlFlow::Break = callback(e) {
control_flow = ::ControlFlow::Break;
2016-12-14 00:28:30 +11:00
}
});
if let ::ControlFlow::Break = control_flow {
break;
2016-12-14 00:28:30 +11:00
}
::std::thread::sleep(::std::time::Duration::from_millis(5));
2016-12-14 00:28:30 +11:00
}
}
2016-12-14 00:28:30 +11:00
pub fn create_proxy(&self) -> EventsLoopProxy {
EventsLoopProxy
}
}
2016-12-14 00:28:30 +11:00
impl EventsLoopProxy {
pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> {
unimplemented!()
2015-06-05 23:38:21 +10:00
}
}
2015-06-05 23:38:21 +10:00
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId;
2016-12-14 00:28:30 +11:00
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId;
2015-06-05 23:38:21 +10:00
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes;
2018-06-15 09:42:18 +10:00
// TODO: AFAIK transparency is enabled by default on iOS,
// so to be consistent with other platforms we have to change that.
impl Window {
2018-06-15 09:42:18 +10:00
pub fn new(
ev: &EventsLoop,
_attributes: WindowAttributes,
_pl_alltributes: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, CreationError> {
Ok(Window { delegate_state: ev.delegate_state })
2016-12-14 00:28:30 +11:00
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_uiwindow(&self) -> id {
unsafe { (*self.delegate_state).window }
}
#[inline]
pub fn get_uiview(&self) -> id {
unsafe { (*self.delegate_state).view }
}
#[inline]
pub fn set_title(&self, _title: &str) {
// N/A
2016-12-14 00:28:30 +11:00
}
#[inline]
pub fn show(&self) {
2018-06-15 09:42:18 +10:00
// N/A
2016-12-14 00:28:30 +11:00
}
#[inline]
pub fn hide(&self) {
2018-06-15 09:42:18 +10:00
// N/A
2016-12-14 00:28:30 +11:00
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_position(&self) -> Option<LogicalPosition> {
// N/A
2016-12-14 00:28:30 +11:00
None
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
// N/A
None
}
2016-12-14 00:28:30 +11:00
#[inline]
2018-06-15 09:42:18 +10:00
pub fn set_position(&self, _position: LogicalPosition) {
// N/A
2016-12-14 00:28:30 +11:00
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_inner_size(&self) -> Option<LogicalSize> {
2016-12-14 00:28:30 +11:00
unsafe { Some((&*self.delegate_state).size) }
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_outer_size(&self) -> Option<LogicalSize> {
2016-12-14 00:28:30 +11:00
self.get_inner_size()
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn set_inner_size(&self, _size: LogicalSize) {
// N/A
2016-12-14 00:28:30 +11:00
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
// N/A
}
2016-12-14 00:28:30 +11:00
#[inline]
2018-06-15 09:42:18 +10:00
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
// N/A
2015-06-05 23:38:21 +10:00
}
2016-12-14 00:28:30 +11:00
#[inline]
2018-06-15 09:42:18 +10:00
pub fn set_resizable(&self, _resizable: bool) {
// N/A
2016-12-14 00:28:30 +11:00
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn set_cursor(&self, _cursor: MouseCursor) {
// N/A
2015-06-05 23:38:21 +10:00
}
2016-12-14 00:28:30 +11:00
#[inline]
2018-06-15 09:42:18 +10:00
pub fn set_cursor_state(&self, _cursor_state: CursorState) -> Result<(), String> {
// N/A
2016-12-14 00:28:30 +11:00
Ok(())
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_hidpi_factor(&self) -> f64 {
2016-12-14 00:28:30 +11:00
unsafe { (&*self.delegate_state) }.scale
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), ()> {
// N/A
Ok(())
2016-12-14 00:28:30 +11:00
}
#[inline]
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
pub fn set_maximized(&self, _maximized: bool) {
2018-06-15 09:42:18 +10:00
// N/A
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
// iOS has single screen maximized apps so nothing to do
}
#[inline]
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
2018-06-15 09:42:18 +10:00
// N/A
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
// iOS has single screen maximized apps so nothing to do
}
#[inline]
pub fn set_decorations(&self, _decorations: bool) {
// N/A
}
#[inline]
pub fn set_always_on_top(&self, _always_on_top: bool) {
// N/A
}
2018-05-08 07:36:21 +10:00
#[inline]
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
// N/A
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn set_ime_spot(&self, _logical_spot: LogicalPosition) {
// N/A
}
#[inline]
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
pub fn get_current_monitor(&self) -> RootMonitorId {
2018-06-15 09:42:18 +10:00
RootMonitorId { inner: MonitorId }
}
2016-12-14 00:28:30 +11:00
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
let mut rb = VecDeque::with_capacity(1);
rb.push_back(MonitorId);
rb
}
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
MonitorId
}
2016-12-14 00:28:30 +11:00
#[inline]
pub fn id(&self) -> WindowId {
WindowId
2016-12-14 00:28:30 +11:00
}
}
fn create_delegate_class() {
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
2018-06-15 09:42:18 +10:00
let screen_class = Class::get("UIScreen").expect("Failed to get class `UIScreen`");
let window_class = Class::get("UIWindow").expect("Failed to get class `UIWindow`");
let controller_class = Class::get("MainViewController").expect("Failed to get class `MainViewController`");
let view_class = Class::get("MainView").expect("Failed to get class `MainView`");
unsafe {
2018-06-15 09:42:18 +10:00
let main_screen: id = msg_send![screen_class, mainScreen];
let bounds: CGRect = msg_send![main_screen, bounds];
let scale: CGFloat = msg_send![main_screen, nativeScale];
2016-12-14 00:28:30 +11:00
2018-06-15 09:42:18 +10:00
let window: id = msg_send![window_class, alloc];
let window: id = msg_send![window, initWithFrame:bounds.clone()];
2016-12-14 00:28:30 +11:00
2018-06-15 09:42:18 +10:00
let size = (bounds.size.width as f64, bounds.size.height as f64).into();
2018-06-15 09:42:18 +10:00
let view_controller: id = msg_send![controller_class, alloc];
let view_controller: id = msg_send![view_controller, init];
2018-06-15 09:42:18 +10:00
let view: id = msg_send![view_class, alloc];
let view: id = msg_send![view, initForGl:&bounds];
let _: () = msg_send![window, setRootViewController:view_controller];
let _: () = msg_send![window, makeKeyAndVisible];
2018-06-15 09:42:18 +10:00
let state = Box::new(DelegateState::new(window, view_controller, view, size, scale as f64));
let state_ptr: *mut DelegateState = mem::transmute(state);
2018-06-15 09:42:18 +10:00
this.set_ivar("winitState", state_ptr as *mut c_void);
let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
2016-12-14 00:28:30 +11:00
}
YES
2016-12-14 00:28:30 +11:00
}
2015-06-05 23:38:21 +10:00
extern fn post_launch(_: &Object, _: Sel, _: id) {
2018-06-15 09:42:18 +10:00
unsafe { longjmp(mem::transmute(&mut JMPBUF),1); }
}
2016-12-14 00:28:30 +11:00
extern fn did_become_active(this: &Object, _: Sel, _: id) {
2016-12-14 00:28:30 +11:00
unsafe {
2018-06-15 09:42:18 +10:00
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::WindowEvent {
window_id: RootEventId(WindowId),
event: WindowEvent::Focused(true),
});
}
}
2016-12-14 00:28:30 +11:00
extern fn will_resign_active(this: &Object, _: Sel, _: id) {
unsafe {
2018-06-15 09:42:18 +10:00
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::WindowEvent {
window_id: RootEventId(WindowId),
event: WindowEvent::Focused(false),
});
}
}
2016-12-14 00:28:30 +11:00
extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
unsafe {
2018-06-15 09:42:18 +10:00
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::Suspended(false));
}
}
2016-12-14 00:28:30 +11:00
extern fn did_enter_background(this: &Object, _: Sel, _: id) {
unsafe {
2018-06-15 09:42:18 +10:00
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::Suspended(true));
}
}
extern fn will_terminate(this: &Object, _: Sel, _: id) {
unsafe {
2018-06-15 09:42:18 +10:00
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
// push event to the front to garantee that we'll process it
// immidiatly after jump
state.events_queue.push_front(Event::WindowEvent {
window_id: RootEventId(WindowId),
event: WindowEvent::Destroyed,
});
2018-06-15 09:42:18 +10:00
longjmp(mem::transmute(&mut JMPBUF),1);
}
}
extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
unsafe {
2018-06-15 09:42:18 +10:00
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
2016-12-14 00:28:30 +11:00
let touches_enum: id = msg_send![touches, objectEnumerator];
loop {
let touch: id = msg_send![touches_enum, nextObject];
if touch == nil {
break
}
let location: CGPoint = msg_send![touch, locationInView:nil];
let touch_id = touch as u64;
let phase: i32 = msg_send![touch, phase];
state.events_queue.push_back(Event::WindowEvent {
window_id: RootEventId(WindowId),
event: WindowEvent::Touch(Touch {
device_id: DEVICE_ID,
id: touch_id,
2018-06-15 09:42:18 +10:00
location: (location.x as f64, location.y as f64).into(),
phase: match phase {
0 => TouchPhase::Started,
1 => TouchPhase::Moved,
// 2 is UITouchPhaseStationary and is not expected here
3 => TouchPhase::Ended,
4 => TouchPhase::Cancelled,
_ => panic!("unexpected touch phase: {:?}", phase)
}
}),
});
}
2016-12-14 00:28:30 +11:00
}
}
2018-06-15 09:42:18 +10:00
let ui_responder = Class::get("UIResponder").expect("Failed to get class `UIResponder`");
let mut decl = ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
unsafe {
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL);
decl.add_method(sel!(applicationDidBecomeActive:),
did_become_active as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationWillResignActive:),
will_resign_active as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationWillEnterForeground:),
will_enter_foreground as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationDidEnterBackground:),
did_enter_background as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationWillTerminate:),
will_terminate as extern fn(&Object, Sel, id));
decl.add_method(sel!(touchesBegan:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(touchesMoved:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(touchesEnded:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(touchesCancelled:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(postLaunch:),
post_launch as extern fn(&Object, Sel, id));
2018-06-15 09:42:18 +10:00
decl.add_ivar::<*mut c_void>("winitState");
decl.register();
}
}
2018-06-15 09:42:18 +10:00
// TODO: winit shouldn't contain GL-specfiic code
pub fn create_view_class() {
let superclass = Class::get("UIViewController").expect("Failed to get class `UIViewController`");
let decl = ClassDecl::new("MainViewController", superclass).expect("Failed to declare class `MainViewController`");
decl.register();
2018-06-15 09:42:18 +10:00
extern fn init_for_gl(this: &Object, _: Sel, frame: *const c_void) -> id {
unsafe {
let bounds = frame as *const CGRect;
let view: id = msg_send![this, initWithFrame:(*bounds).clone()];
let mask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
let _: () = msg_send![view, setAutoresizingMask:mask];
let _: () = msg_send![view, setAutoresizesSubviews:YES];
let layer: id = msg_send![view, layer];
let _ : () = msg_send![layer, setOpaque:YES];
view
}
}
extern fn layer_class(_: &Class, _: Sel) -> *const Class {
unsafe { mem::transmute(Class::get("CAEAGLLayer").expect("Failed to get class `CAEAGLLayer`")) }
}
let superclass = Class::get("GLKView").expect("Failed to get class `GLKView`");
let mut decl = ClassDecl::new("MainView", superclass).expect("Failed to declare class `MainView`");
unsafe {
decl.add_method(sel!(initForGl:), init_for_gl as extern fn(&Object, Sel, *const c_void) -> id);
decl.add_class_method(sel!(layerClass), layer_class as extern fn(&Class, Sel) -> *const Class);
decl.register();
}
}
#[inline]
fn start_app() {
unsafe {
UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate"));
}
2016-12-14 00:28:30 +11:00
}
// Constant device ID, to be removed when this backend is updated to report real device IDs.
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);