Add exclusive fullscreen mode (#925)
* Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
This commit is contained in:
parent
131e67ddc1
commit
5bc3cf18d9
31 changed files with 1452 additions and 605 deletions
|
|
@ -79,7 +79,7 @@ use std::fmt;
|
|||
|
||||
pub use self::{
|
||||
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
|
||||
monitor::MonitorHandle,
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,44 @@
|
|||
use std::{
|
||||
collections::{HashSet, VecDeque},
|
||||
collections::{BTreeSet, VecDeque},
|
||||
fmt,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
monitor::VideoMode,
|
||||
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
||||
platform_impl::platform::ffi::{id, nil, CGFloat, CGRect, CGSize, NSInteger, NSUInteger},
|
||||
};
|
||||
|
||||
use crate::platform_impl::platform::ffi::{
|
||||
id, nil, CGFloat, CGRect, CGSize, NSInteger, NSUInteger,
|
||||
};
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VideoMode {
|
||||
pub(crate) size: (u32, u32),
|
||||
pub(crate) bit_depth: u16,
|
||||
pub(crate) refresh_rate: u16,
|
||||
pub(crate) monitor: MonitorHandle,
|
||||
}
|
||||
|
||||
impl VideoMode {
|
||||
pub fn size(&self) -> PhysicalSize {
|
||||
self.size.into()
|
||||
}
|
||||
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
self.bit_depth
|
||||
}
|
||||
|
||||
pub fn refresh_rate(&self) -> u16 {
|
||||
self.refresh_rate
|
||||
}
|
||||
|
||||
pub fn monitor(&self) -> RootMonitorHandle {
|
||||
RootMonitorHandle {
|
||||
inner: self.monitor.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Inner {
|
||||
uiscreen: id,
|
||||
}
|
||||
|
|
@ -25,6 +51,7 @@ impl Drop for Inner {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct MonitorHandle {
|
||||
inner: Inner,
|
||||
}
|
||||
|
|
@ -140,21 +167,24 @@ impl Inner {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
|
||||
let refresh_rate: NSInteger = unsafe { msg_send![self.uiscreen, maximumFramesPerSecond] };
|
||||
|
||||
let available_modes: id = unsafe { msg_send![self.uiscreen, availableModes] };
|
||||
let available_mode_count: NSUInteger = unsafe { msg_send![available_modes, count] };
|
||||
|
||||
let mut modes = HashSet::with_capacity(available_mode_count);
|
||||
let mut modes = BTreeSet::new();
|
||||
|
||||
for i in 0..available_mode_count {
|
||||
let mode: id = unsafe { msg_send![available_modes, objectAtIndex: i] };
|
||||
let size: CGSize = unsafe { msg_send![mode, size] };
|
||||
modes.insert(VideoMode {
|
||||
size: (size.width as u32, size.height as u32),
|
||||
bit_depth: 32,
|
||||
refresh_rate: refresh_rate as u16,
|
||||
modes.insert(RootVideoMode {
|
||||
video_mode: VideoMode {
|
||||
size: (size.width as u32, size.height as u32),
|
||||
bit_depth: 32,
|
||||
refresh_rate: refresh_rate as u16,
|
||||
monitor: MonitorHandle::retained_new(self.uiscreen),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,14 @@ use objc::{
|
|||
use crate::{
|
||||
event::{DeviceId as RootDeviceId, Event, Touch, TouchPhase, WindowEvent},
|
||||
platform::ios::MonitorHandleExtIOS,
|
||||
window::{WindowAttributes, WindowId as RootWindowId},
|
||||
};
|
||||
|
||||
use crate::platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
event_loop,
|
||||
ffi::{id, nil, CGFloat, CGPoint, CGRect, UIInterfaceOrientationMask, UITouchPhase},
|
||||
window::PlatformSpecificWindowBuilderAttributes,
|
||||
DeviceId,
|
||||
platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
event_loop,
|
||||
ffi::{id, nil, CGFloat, CGPoint, CGRect, UIInterfaceOrientationMask, UITouchPhase},
|
||||
window::PlatformSpecificWindowBuilderAttributes,
|
||||
DeviceId,
|
||||
},
|
||||
window::{Fullscreen, WindowAttributes, WindowId as RootWindowId},
|
||||
};
|
||||
|
||||
// requires main thread
|
||||
|
|
@ -366,8 +365,12 @@ pub unsafe fn create_window(
|
|||
if let Some(hidpi_factor) = platform_attributes.hidpi_factor {
|
||||
let () = msg_send![window, setContentScaleFactor: hidpi_factor as CGFloat];
|
||||
}
|
||||
if let &Some(ref monitor) = &window_attributes.fullscreen {
|
||||
let () = msg_send![window, setScreen:monitor.ui_screen()];
|
||||
match window_attributes.fullscreen {
|
||||
Some(Fullscreen::Exclusive(_)) => unimplemented!(),
|
||||
Some(Fullscreen::Borderless(ref monitor)) => {
|
||||
msg_send![window, setScreen:monitor.ui_screen()]
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
|
||||
window
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
ffi::{id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask},
|
||||
monitor, view, EventLoopWindowTarget, MonitorHandle,
|
||||
},
|
||||
window::{CursorIcon, WindowAttributes},
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
||||
};
|
||||
|
||||
pub struct Inner {
|
||||
|
|
@ -157,10 +157,11 @@ impl Inner {
|
|||
warn!("`Window::set_maximized` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorHandle>) {
|
||||
pub fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
|
||||
unsafe {
|
||||
match monitor {
|
||||
Some(monitor) => {
|
||||
Some(Fullscreen::Exclusive(_)) => unimplemented!("exclusive fullscreen on iOS"), // TODO
|
||||
Some(Fullscreen::Borderless(monitor)) => {
|
||||
let uiscreen = monitor.ui_screen() as id;
|
||||
let current: id = msg_send![self.window, screen];
|
||||
let bounds: CGRect = msg_send![uiscreen, bounds];
|
||||
|
|
@ -176,7 +177,7 @@ impl Inner {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fullscreen(&self) -> Option<RootMonitorHandle> {
|
||||
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
unsafe {
|
||||
let monitor = self.current_monitor();
|
||||
let uiscreen = monitor.inner.ui_screen();
|
||||
|
|
@ -189,7 +190,7 @@ impl Inner {
|
|||
&& screen_space_bounds.size.width == screen_bounds.size.width
|
||||
&& screen_space_bounds.size.height == screen_bounds.size.height
|
||||
{
|
||||
Some(monitor)
|
||||
Some(Fullscreen::Borderless(monitor))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -293,11 +294,12 @@ impl Window {
|
|||
// TODO: transparency, visible
|
||||
|
||||
unsafe {
|
||||
let screen = window_attributes
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.map(|screen| screen.ui_screen() as _)
|
||||
.unwrap_or_else(|| monitor::main_uiscreen().ui_screen());
|
||||
let screen = match window_attributes.fullscreen {
|
||||
Some(Fullscreen::Exclusive(_)) => unimplemented!("exclusive fullscreen on iOS"), // TODO: do we set the frame to video mode bounds instead of screen bounds?
|
||||
Some(Fullscreen::Borderless(ref monitor)) => monitor.ui_screen() as id,
|
||||
None => monitor::main_uiscreen().ui_screen(),
|
||||
};
|
||||
|
||||
let screen_bounds: CGRect = msg_send![screen, bounds];
|
||||
|
||||
let frame = match window_attributes.inner_size {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue