mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
1 Commits
madsmtm/do
...
kchibisov/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ea7a54130 |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -68,8 +68,7 @@ jobs:
|
||||
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
|
||||
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
- { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, }
|
||||
- { name: 'macOS x86_64', target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { name: 'macOS Aarch64', target: aarch64-apple-darwin, os: macos-latest, }
|
||||
- { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, }
|
||||
- { name: 'iOS Aarch64', target: aarch64-apple-ios, os: macos-latest, }
|
||||
- { name: 'Web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
|
||||
@@ -244,11 +243,10 @@ jobs:
|
||||
- { name: 'Android', target: aarch64-linux-android }
|
||||
- { name: 'iOS', target: aarch64-apple-ios }
|
||||
- { name: 'Linux', target: x86_64-unknown-linux-gnu }
|
||||
- { name: 'macOS', target: aarch64-apple-darwin }
|
||||
- { name: 'macOS', target: x86_64-apple-darwin }
|
||||
- { name: 'Redox OS', target: x86_64-unknown-redox }
|
||||
- { name: 'Web', target: wasm32-unknown-unknown }
|
||||
- { name: 'Windows GNU', target: x86_64-pc-windows-gnu }
|
||||
- { name: 'Windows MSVC', target: x86_64-pc-windows-msvc }
|
||||
- { name: 'Windows', target: x86_64-pc-windows-gnu }
|
||||
|
||||
steps:
|
||||
- uses: taiki-e/checkout-action@v1
|
||||
|
||||
21
Cargo.toml
21
Cargo.toml
@@ -21,7 +21,7 @@ name = "winit"
|
||||
readme = "README.md"
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
version = "0.30.5"
|
||||
version = "0.30.4"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [
|
||||
@@ -38,13 +38,12 @@ targets = [
|
||||
"i686-pc-windows-msvc",
|
||||
"x86_64-pc-windows-msvc",
|
||||
# macOS
|
||||
"aarch64-apple-darwin",
|
||||
"x86_64-apple-darwin",
|
||||
# Unix (X11 & Wayland)
|
||||
"i686-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
# iOS
|
||||
"aarch64-apple-ios",
|
||||
"x86_64-apple-ios",
|
||||
# Android
|
||||
"aarch64-linux-android",
|
||||
# Web
|
||||
@@ -106,12 +105,12 @@ ndk = { version = "0.9.0", default-features = false }
|
||||
|
||||
# AppKit or UIKit
|
||||
[target.'cfg(target_vendor = "apple")'.dependencies]
|
||||
block2 = "0.5.1"
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = "0.5.2"
|
||||
|
||||
# AppKit
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
block2 = "0.5.1"
|
||||
core-graphics = "0.23.1"
|
||||
objc2-app-kit = { version = "0.2.2", features = [
|
||||
"NSAppearance",
|
||||
@@ -153,7 +152,6 @@ objc2-foundation = { version = "0.2.2", features = [
|
||||
"NSKeyValueObserving",
|
||||
"NSNotification",
|
||||
"NSObjCRuntime",
|
||||
"NSOperation",
|
||||
"NSPathUtilities",
|
||||
"NSProcessInfo",
|
||||
"NSRunLoop",
|
||||
@@ -165,13 +163,11 @@ objc2-foundation = { version = "0.2.2", features = [
|
||||
# UIKit
|
||||
[target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dependencies]
|
||||
objc2-foundation = { version = "0.2.2", features = [
|
||||
"block2",
|
||||
"dispatch",
|
||||
"NSArray",
|
||||
"NSEnumerator",
|
||||
"NSGeometry",
|
||||
"NSObjCRuntime",
|
||||
"NSOperation",
|
||||
"NSString",
|
||||
"NSProcessInfo",
|
||||
"NSThread",
|
||||
@@ -183,8 +179,6 @@ objc2-ui-kit = { version = "0.2.2", features = [
|
||||
"UIEvent",
|
||||
"UIGeometry",
|
||||
"UIGestureRecognizer",
|
||||
"UITextInput",
|
||||
"UITextInputTraits",
|
||||
"UIOrientation",
|
||||
"UIPanGestureRecognizer",
|
||||
"UIPinchGestureRecognizer",
|
||||
@@ -257,7 +251,6 @@ wayland-protocols-plasma = { version = "0.3.2", features = ["client"], optional
|
||||
x11-dl = { version = "2.19.1", optional = true }
|
||||
x11rb = { version = "0.13.0", default-features = false, features = [
|
||||
"allow-unsafe-code",
|
||||
"cursor",
|
||||
"dl-libxcb",
|
||||
"randr",
|
||||
"resource_manager",
|
||||
@@ -274,12 +267,12 @@ redox_syscall = "0.4.1"
|
||||
|
||||
# Web
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
js-sys = "0.3.70"
|
||||
js-sys = "0.3.64"
|
||||
pin-project = "1"
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-futures = "0.4.43"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-time = "1"
|
||||
web_sys = { package = "web-sys", version = "0.3.70", features = [
|
||||
web_sys = { package = "web-sys", version = "0.3.64", features = [
|
||||
"AbortController",
|
||||
"AbortSignal",
|
||||
"Blob",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.30.5"
|
||||
winit = "0.30.4"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
14
deny.toml
14
deny.toml
@@ -7,7 +7,6 @@
|
||||
all-features = true
|
||||
exclude-dev = true
|
||||
targets = [
|
||||
{ triple = "aarch64-apple-darwin" },
|
||||
{ triple = "aarch64-apple-ios" },
|
||||
{ triple = "aarch64-linux-android" },
|
||||
{ triple = "i686-pc-windows-gnu" },
|
||||
@@ -55,6 +54,19 @@ crate = "android-activity"
|
||||
allow-globs = ["freetype2/*"]
|
||||
crate = "freetype-sys"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow = [
|
||||
{ path = "releases/friends.sh", checksum = "f896ccdcb8445d29ed6dd0d9a360f94d4f33af2f1cc9965e7bb38b156c45949d" },
|
||||
]
|
||||
crate = "wasm-bindgen"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow = [
|
||||
{ path = "ui-tests/update-all-references.sh", checksum = "8b8dbf31e7ada1314956db7a20ab14b13af3ae246a6295afdc7dc96af8ec3773" },
|
||||
{ path = "ui-tests/update-references.sh", checksum = "65375c25981646e08e8589449a06be4505b1a2c9e10d35f650be4b1b495dff22" },
|
||||
]
|
||||
crate = "wasm-bindgen-macro"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["lib/*.a"]
|
||||
crate = "windows_i686_gnu"
|
||||
|
||||
@@ -26,13 +26,12 @@ targets = [
|
||||
"i686-pc-windows-msvc",
|
||||
"x86_64-pc-windows-msvc",
|
||||
# macOS
|
||||
"aarch64-apple-darwin",
|
||||
"x86_64-apple-darwin",
|
||||
# Unix (X11 & Wayland)
|
||||
"i686-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
# iOS
|
||||
"aarch64-apple-ios",
|
||||
"x86_64-apple-ios",
|
||||
# Android
|
||||
"aarch64-linux-android",
|
||||
# Web
|
||||
|
||||
@@ -8,7 +8,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::raw_window_handle::HasRawWindowHandle;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
@@ -16,15 +16,15 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
#[derive(Default)]
|
||||
struct Application {
|
||||
parent_window_id: Option<WindowId>,
|
||||
windows: HashMap<WindowId, Box<dyn Window>>,
|
||||
windows: HashMap<WindowId, Window>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for Application {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let attributes = WindowAttributes::default()
|
||||
let attributes = Window::default_attributes()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||
let window = event_loop.create_window(attributes).unwrap();
|
||||
|
||||
println!("Parent window id: {:?})", window.id());
|
||||
@@ -57,14 +57,14 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
..
|
||||
} => {
|
||||
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap();
|
||||
let child_window = spawn_child_window(parent_window.as_ref(), event_loop);
|
||||
let child_window = spawn_child_window(parent_window, event_loop);
|
||||
let child_id = child_window.id();
|
||||
println!("Child window created with id: {child_id:?}");
|
||||
self.windows.insert(child_id, child_window);
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = self.windows.get(&window_id) {
|
||||
fill::fill_window(window.as_ref());
|
||||
fill::fill_window(window);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
@@ -72,14 +72,11 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_child_window(
|
||||
parent: &dyn Window,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
) -> Box<dyn Window> {
|
||||
fn spawn_child_window(parent: &Window, event_loop: &dyn ActiveEventLoop) -> Window {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let mut window_attributes = WindowAttributes::default()
|
||||
let mut window_attributes = Window::default_attributes()
|
||||
.with_title("child window")
|
||||
.with_surface_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_visible(true);
|
||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||
|
||||
@@ -11,7 +11,7 @@ use winit::application::ApplicationHandler;
|
||||
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
@@ -52,7 +52,7 @@ struct ControlFlowDemo {
|
||||
request_redraw: bool,
|
||||
wait_cancelled: bool,
|
||||
close_requested: bool,
|
||||
window: Option<Box<dyn Window>>,
|
||||
window: Option<Window>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for ControlFlowDemo {
|
||||
@@ -66,7 +66,7 @@ impl ApplicationHandler for ControlFlowDemo {
|
||||
}
|
||||
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default().with_title(
|
||||
let window_attributes = Window::default_attributes().with_title(
|
||||
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
|
||||
);
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
@@ -114,7 +114,7 @@ impl ApplicationHandler for ControlFlowDemo {
|
||||
WindowEvent::RedrawRequested => {
|
||||
let window = self.window.as_ref().unwrap();
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window.as_ref());
|
||||
fill::fill_window(window);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -11,19 +11,19 @@ fn main() -> std::process::ExitCode {
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Default)]
|
||||
struct PumpDemo {
|
||||
window: Option<Box<dyn Window>>,
|
||||
window: Option<Window>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for PumpDemo {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default().with_title("A fantastic window!");
|
||||
let window_attributes = Window::default_attributes().with_title("A fantastic window!");
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ fn main() -> std::process::ExitCode {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(window.as_ref());
|
||||
fill::fill_window(window);
|
||||
window.request_redraw();
|
||||
},
|
||||
_ => (),
|
||||
|
||||
@@ -9,7 +9,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
@@ -18,7 +18,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
struct App {
|
||||
idx: usize,
|
||||
window_id: Option<WindowId>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
window: Option<Window>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
@@ -29,9 +29,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default()
|
||||
let window_attributes = Window::default_attributes()
|
||||
.with_title("Fantastic window number one!")
|
||||
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0));
|
||||
let window = event_loop.create_window(window_attributes).unwrap();
|
||||
self.window_id = Some(window.id());
|
||||
self.window = Some(window);
|
||||
@@ -65,11 +65,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
CloseRequested",
|
||||
self.idx
|
||||
);
|
||||
fill::cleanup_window(window.as_ref());
|
||||
fill::cleanup_window(window);
|
||||
self.window = None;
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(window.as_ref());
|
||||
fill::fill_window(window);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -34,20 +34,18 @@ mod platform {
|
||||
/// The graphics context used to draw to a window.
|
||||
struct GraphicsContext {
|
||||
/// The global softbuffer context.
|
||||
context: RefCell<Context<&'static dyn Window>>,
|
||||
context: RefCell<Context<&'static Window>>,
|
||||
|
||||
/// The hash map of window IDs to surfaces.
|
||||
surfaces: HashMap<WindowId, Surface<&'static dyn Window, &'static dyn Window>>,
|
||||
surfaces: HashMap<WindowId, Surface<&'static Window, &'static Window>>,
|
||||
}
|
||||
|
||||
impl GraphicsContext {
|
||||
fn new(w: &dyn Window) -> Self {
|
||||
fn new(w: &Window) -> Self {
|
||||
Self {
|
||||
context: RefCell::new(
|
||||
Context::new(unsafe {
|
||||
mem::transmute::<&'_ dyn Window, &'static dyn Window>(w)
|
||||
})
|
||||
.expect("Failed to create a softbuffer context"),
|
||||
Context::new(unsafe { mem::transmute::<&'_ Window, &'static Window>(w) })
|
||||
.expect("Failed to create a softbuffer context"),
|
||||
),
|
||||
surfaces: HashMap::new(),
|
||||
}
|
||||
@@ -55,24 +53,24 @@ mod platform {
|
||||
|
||||
fn create_surface(
|
||||
&mut self,
|
||||
window: &dyn Window,
|
||||
) -> &mut Surface<&'static dyn Window, &'static dyn Window> {
|
||||
window: &Window,
|
||||
) -> &mut Surface<&'static Window, &'static Window> {
|
||||
self.surfaces.entry(window.id()).or_insert_with(|| {
|
||||
Surface::new(&self.context.borrow(), unsafe {
|
||||
mem::transmute::<&'_ dyn Window, &'static dyn Window>(window)
|
||||
mem::transmute::<&'_ Window, &'static Window>(window)
|
||||
})
|
||||
.expect("Failed to create a softbuffer surface")
|
||||
})
|
||||
}
|
||||
|
||||
fn destroy_surface(&mut self, window: &dyn Window) {
|
||||
fn destroy_surface(&mut self, window: &Window) {
|
||||
self.surfaces.remove(&window.id());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_window(window: &dyn Window) {
|
||||
pub fn fill_window(window: &Window) {
|
||||
GC.with(|gc| {
|
||||
let size = window.surface_size();
|
||||
let size = window.inner_size();
|
||||
let (Some(width), Some(height)) =
|
||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
else {
|
||||
@@ -96,7 +94,7 @@ mod platform {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(window: &dyn Window) {
|
||||
pub fn cleanup_window(window: &Window) {
|
||||
GC.with(|gc| {
|
||||
let mut gc = gc.borrow_mut();
|
||||
if let Some(context) = gc.as_mut() {
|
||||
@@ -108,12 +106,12 @@ mod platform {
|
||||
|
||||
#[cfg(not(all(feature = "rwh_06", not(any(target_os = "android", target_os = "ios")))))]
|
||||
mod platform {
|
||||
pub fn fill_window(_window: &dyn winit::window::Window) {
|
||||
pub fn fill_window(_window: &winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(_window: &dyn winit::window::Window) {
|
||||
pub fn cleanup_window(_window: &winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use rwh_06::{DisplayHandle, HasDisplayHandle};
|
||||
use softbuffer::{Context, Surface};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
|
||||
use winit::error::RequestError;
|
||||
use winit::error::ExternalError;
|
||||
use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, ModifiersState};
|
||||
@@ -27,11 +27,12 @@ use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMac
|
||||
use winit::platform::startup_notify::{
|
||||
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
|
||||
};
|
||||
use winit::platform::wayland::{EventLoopExtWayland, WaylandApplicationHandler};
|
||||
#[cfg(web_platform)]
|
||||
use winit::platform::web::{ActiveEventLoopExtWeb, CustomCursorExtWeb, WindowAttributesExtWeb};
|
||||
use winit::window::{
|
||||
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection,
|
||||
Theme, Window, WindowAttributes, WindowId,
|
||||
Theme, Window, WindowId,
|
||||
};
|
||||
|
||||
#[path = "util/tracing.rs"]
|
||||
@@ -46,7 +47,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
tracing::init();
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
let mut event_loop = EventLoop::new()?;
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
||||
// Wire the user event from another thread.
|
||||
@@ -67,6 +68,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
|
||||
let app = Application::new(&event_loop, receiver, sender);
|
||||
event_loop.register_wayland_callback::<Application>();
|
||||
Ok(event_loop.run_app(app)?)
|
||||
}
|
||||
|
||||
@@ -76,7 +78,7 @@ struct Application {
|
||||
receiver: Receiver<Action>,
|
||||
sender: Sender<Action>,
|
||||
/// Custom cursors assets.
|
||||
custom_cursors: Result<Vec<CustomCursor>, RequestError>,
|
||||
custom_cursors: Result<Vec<CustomCursor>, ExternalError>,
|
||||
/// Application icon.
|
||||
icon: Icon,
|
||||
windows: HashMap<WindowId, WindowState>,
|
||||
@@ -135,7 +137,7 @@ impl Application {
|
||||
// TODO read-out activation token.
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut window_attributes = WindowAttributes::default()
|
||||
let mut window_attributes = Window::default_attributes()
|
||||
.with_title("Winit window")
|
||||
.with_transparent(true)
|
||||
.with_window_icon(Some(self.icon.clone()));
|
||||
@@ -371,6 +373,11 @@ impl Application {
|
||||
}
|
||||
|
||||
impl ApplicationHandler for Application {
|
||||
fn as_any(&mut self) -> Option<&mut dyn std::any::Any> {
|
||||
println!("Called");
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
while let Ok(action) = self.receiver.try_recv() {
|
||||
self.handle_action_from_proxy(event_loop, action)
|
||||
@@ -389,7 +396,7 @@ impl ApplicationHandler for Application {
|
||||
};
|
||||
|
||||
match event {
|
||||
WindowEvent::SurfaceResized(size) => {
|
||||
WindowEvent::Resized(size) => {
|
||||
window.resize(size);
|
||||
},
|
||||
WindowEvent::Focused(focused) => {
|
||||
@@ -552,6 +559,12 @@ impl ApplicationHandler for Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandApplicationHandler for Application {
|
||||
fn wayland_callback(&mut self) {
|
||||
println!("Wayland callback!");
|
||||
}
|
||||
}
|
||||
|
||||
/// State of the window.
|
||||
struct WindowState {
|
||||
/// IME input.
|
||||
@@ -560,9 +573,9 @@ struct WindowState {
|
||||
///
|
||||
/// NOTE: This surface must be dropped before the `Window`.
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
surface: Surface<DisplayHandle<'static>, Arc<dyn Window>>,
|
||||
surface: Surface<DisplayHandle<'static>, Arc<Window>>,
|
||||
/// The actual winit Window.
|
||||
window: Arc<dyn Window>,
|
||||
window: Arc<Window>,
|
||||
/// The window theme we're drawing with.
|
||||
theme: Theme,
|
||||
/// Cursor position over the window.
|
||||
@@ -590,8 +603,8 @@ struct WindowState {
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> {
|
||||
let window: Arc<dyn Window> = Arc::from(window);
|
||||
fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
|
||||
let window = Arc::new(window);
|
||||
|
||||
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
|
||||
// it doesn't outlive it.
|
||||
@@ -601,13 +614,13 @@ impl WindowState {
|
||||
let theme = window.theme().unwrap_or(Theme::Dark);
|
||||
info!("Theme: {theme:?}");
|
||||
let named_idx = 0;
|
||||
window.set_cursor(CURSORS[named_idx].into());
|
||||
window.set_cursor(CURSORS[named_idx]);
|
||||
|
||||
// Allow IME out of the box.
|
||||
let ime = true;
|
||||
window.set_ime_allowed(ime);
|
||||
|
||||
let size = window.surface_size();
|
||||
let size = window.inner_size();
|
||||
let mut state = Self {
|
||||
#[cfg(macos_platform)]
|
||||
option_as_alt: window.option_as_alt(),
|
||||
@@ -636,7 +649,7 @@ impl WindowState {
|
||||
self.ime = !self.ime;
|
||||
self.window.set_ime_allowed(self.ime);
|
||||
if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
|
||||
self.window.set_ime_cursor_area(position.into(), PhysicalSize::new(20, 20).into());
|
||||
self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,7 +660,7 @@ impl WindowState {
|
||||
pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
|
||||
self.cursor_position = Some(position);
|
||||
if self.ime {
|
||||
self.window.set_ime_cursor_area(position.into(), PhysicalSize::new(20, 20).into());
|
||||
self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,12 +694,12 @@ impl WindowState {
|
||||
|
||||
/// Toggle resize increments on a window.
|
||||
fn toggle_resize_increments(&mut self) {
|
||||
let new_increments = match self.window.surface_resize_increments() {
|
||||
let new_increments = match self.window.resize_increments() {
|
||||
Some(_) => None,
|
||||
None => Some(LogicalSize::new(25.0, 25.0).into()),
|
||||
None => Some(LogicalSize::new(25.0, 25.0)),
|
||||
};
|
||||
info!("Had increments: {}", new_increments.is_none());
|
||||
self.window.set_surface_resize_increments(new_increments);
|
||||
self.window.set_resize_increments(new_increments);
|
||||
}
|
||||
|
||||
/// Toggle fullscreen.
|
||||
@@ -725,22 +738,22 @@ impl WindowState {
|
||||
self.window.set_option_as_alt(self.option_as_alt);
|
||||
}
|
||||
|
||||
/// Swap the window dimensions with `request_surface_size`.
|
||||
/// Swap the window dimensions with `request_inner_size`.
|
||||
fn swap_dimensions(&mut self) {
|
||||
let old_surface_size = self.window.surface_size();
|
||||
let mut surface_size = old_surface_size;
|
||||
let old_inner_size = self.window.inner_size();
|
||||
let mut inner_size = old_inner_size;
|
||||
|
||||
mem::swap(&mut surface_size.width, &mut surface_size.height);
|
||||
info!("Requesting resize from {old_surface_size:?} to {surface_size:?}");
|
||||
mem::swap(&mut inner_size.width, &mut inner_size.height);
|
||||
info!("Requesting resize from {old_inner_size:?} to {inner_size:?}");
|
||||
|
||||
if let Some(new_surface_size) = self.window.request_surface_size(surface_size.into()) {
|
||||
if old_surface_size == new_surface_size {
|
||||
if let Some(new_inner_size) = self.window.request_inner_size(inner_size) {
|
||||
if old_inner_size == new_inner_size {
|
||||
info!("Inner size change got ignored");
|
||||
} else {
|
||||
self.resize(new_surface_size);
|
||||
self.resize(new_inner_size);
|
||||
}
|
||||
} else {
|
||||
info!("Requesting surface size is asynchronous");
|
||||
info!("Request inner size is asynchronous");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,7 +779,7 @@ impl WindowState {
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let cursor = event_loop.create_custom_cursor(url_custom_cursor())?;
|
||||
|
||||
self.window.set_cursor(cursor.into());
|
||||
self.window.set_cursor(cursor);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -788,14 +801,14 @@ impl WindowState {
|
||||
let cursor = CustomCursor::from_animation(Duration::from_secs(3), cursors).unwrap();
|
||||
let cursor = event_loop.create_custom_cursor(cursor)?;
|
||||
|
||||
self.window.set_cursor(cursor.into());
|
||||
self.window.set_cursor(cursor);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resize the surface to the new size.
|
||||
/// Resize the window to the new size.
|
||||
fn resize(&mut self, size: PhysicalSize<u32>) {
|
||||
info!("Surface resized to {size:?}");
|
||||
info!("Resized to {size:?}");
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
{
|
||||
let (width, height) = match (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
@@ -817,7 +830,7 @@ impl WindowState {
|
||||
/// Show window menu.
|
||||
fn show_menu(&self) {
|
||||
if let Some(position) = self.cursor_position {
|
||||
self.window.show_window_menu(position.into());
|
||||
self.window.show_window_menu(position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,7 +853,7 @@ impl WindowState {
|
||||
},
|
||||
};
|
||||
|
||||
let win_size = self.window.surface_size();
|
||||
let win_size = self.window.inner_size();
|
||||
let border_size = BORDER_SIZE * self.window.scale_factor();
|
||||
|
||||
let x_direction = if position.x < border_size {
|
||||
|
||||
@@ -7,21 +7,21 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::platform::x11::WindowAttributesExtX11;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
pub struct XEmbedDemo {
|
||||
parent_window_id: u32,
|
||||
window: Option<Box<dyn Window>>,
|
||||
window: Option<Window>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for XEmbedDemo {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default()
|
||||
let window_attributes = Window::default_attributes()
|
||||
.with_title("An embedded window!")
|
||||
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.with_embed_parent_window(self.parent_window_id);
|
||||
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
@@ -38,7 +38,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window.as_ref());
|
||||
fill::fill_window(window);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! End user application handling.
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
|
||||
use crate::event_loop::ActiveEventLoop;
|
||||
use crate::window::WindowId;
|
||||
@@ -325,10 +327,23 @@ pub trait ApplicationHandler {
|
||||
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
|
||||
/// Get the [`ApplicationHandler`] as [`Any`].
|
||||
///
|
||||
/// This is useful for downcasting to a concrete application type.
|
||||
#[inline(always)]
|
||||
fn as_any(&mut self) -> Option<&mut dyn Any> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[deny(clippy::missing_trait_methods)]
|
||||
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
||||
#[inline(always)]
|
||||
fn as_any(&mut self) -> Option<&mut dyn Any> {
|
||||
(**self).as_any()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
|
||||
(**self).new_events(event_loop, cause);
|
||||
@@ -397,6 +412,11 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
||||
|
||||
#[deny(clippy::missing_trait_methods)]
|
||||
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
|
||||
#[inline(always)]
|
||||
fn as_any(&mut self) -> Option<&mut dyn Any> {
|
||||
(**self).as_any()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
|
||||
(**self).new_events(event_loop, cause);
|
||||
|
||||
@@ -43,34 +43,29 @@ changelog entry.
|
||||
### Added
|
||||
|
||||
- Add `ActiveEventLoop::create_proxy()`.
|
||||
- On Web, implement `Error` for `platform::web::CustomCursorError`.
|
||||
- On Web, add `ActiveEventLoopExtWeb::is_cursor_lock_raw()` to determine if
|
||||
`DeviceEvent::MouseMotion` is returning raw data, not OS accelerated, when using
|
||||
`CursorGrabMode::Locked`.
|
||||
- On Web, implement `MonitorHandle` and `VideoModeHandle`.
|
||||
|
||||
|
||||
Without prompting the user for permission, only the current monitor is returned. But when
|
||||
prompting and being granted permission through
|
||||
`ActiveEventLoop::request_detailed_monitor_permission()`, access to all monitors and their
|
||||
details is available. Handles created with "detailed monitor permissions" can be used in
|
||||
`Window::set_fullscreen()` as well.
|
||||
|
||||
Keep in mind that handles do not auto-upgrade after permissions are granted and have to be
|
||||
re-created to make full use of this feature.
|
||||
information is available. This "detailed monitors" can be used in `Window::set_fullscreen()` as
|
||||
well.
|
||||
- On Android, add `{Active,}EventLoopExtAndroid::android_app()` to access the app used to create the loop.
|
||||
- Add `ActiveEventLoop::system_theme()`, returning the current system theme.
|
||||
- Add `Touch::finger_id` with a new type `FingerId`.
|
||||
- On Web and Windows, add `FingerIdExt*::is_primary()`, exposing a way to determine
|
||||
the primary finger in a multi-touch interaction.
|
||||
- Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd`
|
||||
and `Serialize` on many types.
|
||||
- Add `MonitorHandle::current_video_mode()`.
|
||||
- Add basic iOS IME support. The soft keyboard can now be shown using `Window::set_ime_allowed`.
|
||||
- On macOS, add `WindowExtMacOS::set_borderless_game` and `WindowAttributesExtMacOS::with_borderless_game`
|
||||
to fully disable the menu bar and dock in Borderless Fullscreen as commonly done in games.
|
||||
|
||||
### Changed
|
||||
|
||||
- Change `ActiveEventLoop` to be a trait.
|
||||
- Change `Window` to be a trait.
|
||||
- `ActiveEventLoop::create_window` now returns `Box<dyn Window>`.
|
||||
- `ApplicationHandler` now uses `dyn ActiveEventLoop`.
|
||||
- On Web, let events wake up event loop immediately when using `ControlFlow::Poll`.
|
||||
- Bump MSRV from `1.70` to `1.73`.
|
||||
@@ -102,28 +97,6 @@ changelog entry.
|
||||
- Changed how `ModifiersState` is serialized by Serde.
|
||||
- `VideoModeHandle::refresh_rate_millihertz()` and `bit_depth()` now return a `Option<NonZero*>`.
|
||||
- `MonitorHandle::position()` now returns an `Option`.
|
||||
- On iOS and macOS, remove custom application delegates. You are now allowed to override the
|
||||
application delegate yourself.
|
||||
- On iOS, no longer act as-if the application successfully open all URLs. Override
|
||||
`application:didFinishLaunchingWithOptions:` and provide the desired behaviour yourself.
|
||||
- On X11, remove our dependency on libXcursor. (#3749)
|
||||
- Renamed the following APIs to make it clearer that the sizes apply to the underlying surface:
|
||||
- `WindowEvent::Resized` to `SurfaceResized`.
|
||||
- `InnerSizeWriter` to `SurfaceSizeWriter`.
|
||||
- `WindowAttributes.inner_size` to `surface_size`.
|
||||
- `WindowAttributes.min_inner_size` to `min_surface_size`.
|
||||
- `WindowAttributes.max_inner_size` to `max_surface_size`.
|
||||
- `WindowAttributes.resize_increments` to `surface_resize_increments`.
|
||||
- `WindowAttributes::with_inner_size` to `with_surface_size`.
|
||||
- `WindowAttributes::with_min_inner_size` to `with_min_surface_size`.
|
||||
- `WindowAttributes::with_max_inner_size` to `with_max_surface_size`.
|
||||
- `WindowAttributes::with_resize_increments` to `with_surface_resize_increments`.
|
||||
- `Window::inner_size` to `surface_size`.
|
||||
- `Window::request_inner_size` to `request_surface_size`.
|
||||
- `Window::set_min_inner_size` to `set_min_surface_size`.
|
||||
- `Window::set_max_inner_size` to `set_max_surface_size`.
|
||||
|
||||
To migrate, you can probably just replace all instances of `inner_size` with `surface_size` in your codebase.
|
||||
|
||||
### Removed
|
||||
|
||||
@@ -153,4 +126,7 @@ changelog entry.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On Web, pen events are now routed through to `WindowEvent::Cursor*`.
|
||||
- On macOS, fix panic when releasing not available monitor.
|
||||
- On MacOS, return the system theme in `Window::theme()` if no theme override is set.
|
||||
- On Orbital, `MonitorHandle::name()` now returns `None` instead of a dummy name.
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
## 0.30.5
|
||||
|
||||
### Added
|
||||
|
||||
- Add `ActiveEventLoop::system_theme()`, returning the current system theme.
|
||||
- On Web, implement `Error` for `platform::web::CustomCursorError`.
|
||||
- On Android, add `{Active,}EventLoopExtAndroid::android_app()` to access the app used to create the loop.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On MacOS, fix building with `feature = "rwh_04"`.
|
||||
- On Web, pen events are now routed through to `WindowEvent::Cursor*`.
|
||||
- On macOS, fix panic when releasing not available monitor.
|
||||
- On MacOS, return the system theme in `Window::theme()` if no theme override is set.
|
||||
|
||||
## 0.30.4
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -50,7 +50,7 @@ impl From<CustomCursor> for Cursor {
|
||||
/// ```no_run
|
||||
/// # use winit::event_loop::ActiveEventLoop;
|
||||
/// # use winit::window::Window;
|
||||
/// # fn scope(event_loop: &dyn ActiveEventLoop, window: &dyn Window) {
|
||||
/// # fn scope(event_loop: &dyn ActiveEventLoop, window: &Window) {
|
||||
/// use winit::window::CustomCursor;
|
||||
///
|
||||
/// let w = 10;
|
||||
@@ -67,7 +67,7 @@ impl From<CustomCursor> for Cursor {
|
||||
/// };
|
||||
///
|
||||
/// if let Ok(custom_cursor) = event_loop.create_custom_cursor(source) {
|
||||
/// window.set_cursor(custom_cursor.clone().into());
|
||||
/// window.set_cursor(custom_cursor.clone());
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
|
||||
222
src/error.rs
222
src/error.rs
@@ -1,40 +1,45 @@
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Display};
|
||||
use std::{error, fmt};
|
||||
|
||||
/// A general error that may occur while running or creating
|
||||
/// the event loop.
|
||||
use crate::platform_impl;
|
||||
|
||||
// TODO: Rename
|
||||
/// An error that may be generated when requesting Winit state
|
||||
#[derive(Debug)]
|
||||
pub enum ExternalError {
|
||||
/// The operation is not supported by the backend.
|
||||
NotSupported(NotSupportedError),
|
||||
/// The operation was ignored.
|
||||
Ignored,
|
||||
/// The OS cannot perform the operation.
|
||||
Os(OsError),
|
||||
}
|
||||
|
||||
/// The error type for when the requested operation is not supported by the backend.
|
||||
#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct NotSupportedError {
|
||||
_marker: (),
|
||||
}
|
||||
|
||||
/// The error type for when the OS cannot perform the requested operation.
|
||||
#[derive(Debug)]
|
||||
pub struct OsError {
|
||||
line: u32,
|
||||
file: &'static str,
|
||||
error: platform_impl::OsError,
|
||||
}
|
||||
|
||||
/// A general error that may occur while running the Winit event loop
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum EventLoopError {
|
||||
/// The operation is not supported by the backend.
|
||||
NotSupported(NotSupportedError),
|
||||
/// The OS cannot perform the operation.
|
||||
Os(OsError),
|
||||
/// The event loop can't be re-created.
|
||||
RecreationAttempt,
|
||||
/// Application has exit with an error status.
|
||||
ExitFailure(i32),
|
||||
/// Got unspecified OS-specific error during the request.
|
||||
Os(OsError),
|
||||
/// Creating the event loop with the requested configuration is not supported.
|
||||
NotSupported(NotSupportedError),
|
||||
}
|
||||
|
||||
impl fmt::Display for EventLoopError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
|
||||
Self::Os(err) => err.fmt(f),
|
||||
Self::ExitFailure(status) => write!(f, "Exit Failure: {status}"),
|
||||
Self::NotSupported(err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for EventLoopError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
if let Self::Os(err) = self {
|
||||
err.source()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OsError> for EventLoopError {
|
||||
@@ -43,102 +48,18 @@ impl From<OsError> for EventLoopError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NotSupportedError> for EventLoopError {
|
||||
fn from(value: NotSupportedError) -> Self {
|
||||
Self::NotSupported(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// A general error that may occur during a request to the windowing system.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum RequestError {
|
||||
/// The request is not supported.
|
||||
NotSupported(NotSupportedError),
|
||||
/// The request was ignored by the operating system.
|
||||
Ignored,
|
||||
/// Got unspecified OS specific error during the request.
|
||||
Os(OsError),
|
||||
}
|
||||
|
||||
impl Display for RequestError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::NotSupported(err) => err.fmt(f),
|
||||
Self::Ignored => write!(f, "The request was ignored"),
|
||||
Self::Os(err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Error for RequestError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
if let Self::Os(err) = self {
|
||||
err.source()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NotSupportedError> for RequestError {
|
||||
fn from(value: NotSupportedError) -> Self {
|
||||
Self::NotSupported(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OsError> for RequestError {
|
||||
fn from(value: OsError) -> Self {
|
||||
Self::Os(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// The requested operation is not supported.
|
||||
#[derive(Debug)]
|
||||
pub struct NotSupportedError {
|
||||
/// The reason why a certain operation is not supported.
|
||||
reason: &'static str,
|
||||
}
|
||||
|
||||
impl NotSupportedError {
|
||||
pub(crate) fn new(reason: &'static str) -> Self {
|
||||
Self { reason }
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new() -> NotSupportedError {
|
||||
NotSupportedError { _marker: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NotSupportedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Operation is not supported: {}", self.reason)
|
||||
}
|
||||
}
|
||||
impl Error for NotSupportedError {}
|
||||
|
||||
/// Unclassified error from the OS.
|
||||
#[derive(Debug)]
|
||||
pub struct OsError {
|
||||
line: u32,
|
||||
file: &'static str,
|
||||
error: Box<dyn Error + Send + Sync + 'static>,
|
||||
}
|
||||
|
||||
impl OsError {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new(
|
||||
line: u32,
|
||||
file: &'static str,
|
||||
error: impl Into<Box<dyn Error + Send + Sync + 'static>>,
|
||||
) -> Self {
|
||||
Self { line, file, error: error.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for OsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad(&format!("os error at {}:{}: {}", self.file, self.line, self.error))
|
||||
}
|
||||
}
|
||||
impl Error for OsError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(self.error.as_ref())
|
||||
pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError {
|
||||
OsError { line, file, error }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,3 +69,64 @@ macro_rules! os_error {
|
||||
crate::error::OsError::new(line!(), file!(), $error)
|
||||
}};
|
||||
}
|
||||
|
||||
impl fmt::Display for OsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
f.pad(&format!("os error at {}:{}: {}", self.file, self.line, self.error))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExternalError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
ExternalError::NotSupported(e) => e.fmt(f),
|
||||
ExternalError::Ignored => write!(f, "Operation was ignored"),
|
||||
ExternalError::Os(e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NotSupportedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
f.debug_struct("NotSupportedError").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NotSupportedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
f.pad("the requested operation is not supported by Winit")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EventLoopError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
|
||||
EventLoopError::NotSupported(e) => e.fmt(f),
|
||||
EventLoopError::Os(e) => e.fmt(f),
|
||||
EventLoopError::ExitFailure(status) => write!(f, "Exit Failure: {status}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for OsError {}
|
||||
impl error::Error for ExternalError {}
|
||||
impl error::Error for NotSupportedError {}
|
||||
impl error::Error for EventLoopError {}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::redundant_clone)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Eat attributes for testing
|
||||
#[test]
|
||||
fn ensure_fmt_does_not_panic() {
|
||||
let _ = format!("{:?}, {}", NotSupportedError::new(), NotSupportedError::new().clone());
|
||||
let _ = format!(
|
||||
"{:?}, {}",
|
||||
ExternalError::NotSupported(NotSupportedError::new()),
|
||||
ExternalError::NotSupported(NotSupportedError::new())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
54
src/event.rs
54
src/event.rs
@@ -46,7 +46,7 @@ use smol_str::SmolStr;
|
||||
use web_time::Instant;
|
||||
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::error::RequestError;
|
||||
use crate::error::ExternalError;
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState};
|
||||
use crate::platform_impl;
|
||||
@@ -148,13 +148,8 @@ pub enum WindowEvent {
|
||||
/// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token
|
||||
ActivationTokenDone { serial: AsyncRequestSerial, token: ActivationToken },
|
||||
|
||||
/// The size of the window's surface has changed.
|
||||
///
|
||||
/// Contains the new dimensions of the surface (can also be retrieved with
|
||||
/// [`Window::surface_size`]).
|
||||
///
|
||||
/// [`Window::surface_size`]: crate::window::Window::surface_size
|
||||
SurfaceResized(PhysicalSize<u32>),
|
||||
/// The size of the window has changed. Contains the client area's new dimensions.
|
||||
Resized(PhysicalSize<u32>),
|
||||
|
||||
/// The position of the window has changed. Contains the window's new position.
|
||||
///
|
||||
@@ -365,16 +360,16 @@ pub enum WindowEvent {
|
||||
/// * Changing the display's scale factor (e.g. in Control Panel on Windows).
|
||||
/// * Moving the window to a display with a different scale factor.
|
||||
///
|
||||
/// To update the window size, use the provided [`SurfaceSizeWriter`] handle. By default, the
|
||||
/// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the
|
||||
/// window is resized to the value suggested by the OS, but it can be changed to any value.
|
||||
///
|
||||
/// For more information about DPI in general, see the [`dpi`] crate.
|
||||
ScaleFactorChanged {
|
||||
scale_factor: f64,
|
||||
/// Handle to update surface size during scale changes.
|
||||
/// Handle to update inner size during scale changes.
|
||||
///
|
||||
/// See [`SurfaceSizeWriter`] docs for more details.
|
||||
surface_size_writer: SurfaceSizeWriter,
|
||||
/// See [`InnerSizeWriter`] docs for more details.
|
||||
inner_size_writer: InnerSizeWriter,
|
||||
},
|
||||
|
||||
/// The system window theme has changed.
|
||||
@@ -1000,39 +995,40 @@ pub enum MouseScrollDelta {
|
||||
PixelDelta(PhysicalPosition<f64>),
|
||||
}
|
||||
|
||||
/// Handle to synchronously change the size of the window from the [`WindowEvent`].
|
||||
/// Handle to synchronously change the size of the window from the
|
||||
/// [`WindowEvent`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SurfaceSizeWriter {
|
||||
pub(crate) new_surface_size: Weak<Mutex<PhysicalSize<u32>>>,
|
||||
pub struct InnerSizeWriter {
|
||||
pub(crate) new_inner_size: Weak<Mutex<PhysicalSize<u32>>>,
|
||||
}
|
||||
|
||||
impl SurfaceSizeWriter {
|
||||
impl InnerSizeWriter {
|
||||
#[cfg(not(orbital_platform))]
|
||||
pub(crate) fn new(new_surface_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
|
||||
Self { new_surface_size }
|
||||
pub(crate) fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
|
||||
Self { new_inner_size }
|
||||
}
|
||||
|
||||
/// Try to request surface size which will be set synchronously on the window.
|
||||
pub fn request_surface_size(
|
||||
/// Try to request inner size which will be set synchronously on the window.
|
||||
pub fn request_inner_size(
|
||||
&mut self,
|
||||
new_surface_size: PhysicalSize<u32>,
|
||||
) -> Result<(), RequestError> {
|
||||
if let Some(inner) = self.new_surface_size.upgrade() {
|
||||
*inner.lock().unwrap() = new_surface_size;
|
||||
new_inner_size: PhysicalSize<u32>,
|
||||
) -> Result<(), ExternalError> {
|
||||
if let Some(inner) = self.new_inner_size.upgrade() {
|
||||
*inner.lock().unwrap() = new_inner_size;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RequestError::Ignored)
|
||||
Err(ExternalError::Ignored)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SurfaceSizeWriter {
|
||||
impl PartialEq for InnerSizeWriter {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.new_surface_size.as_ptr() == other.new_surface_size.as_ptr()
|
||||
self.new_inner_size.as_ptr() == other.new_inner_size.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SurfaceSizeWriter {}
|
||||
impl Eq for InnerSizeWriter {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -1070,7 +1066,7 @@ mod tests {
|
||||
with_window_event(Destroyed);
|
||||
with_window_event(Focused(true));
|
||||
with_window_event(Moved((0, 0).into()));
|
||||
with_window_event(SurfaceResized((0, 0).into()));
|
||||
with_window_event(Resized((0, 0).into()));
|
||||
with_window_event(DroppedFile("x.txt".into()));
|
||||
with_window_event(HoveredFile("x.txt".into()));
|
||||
with_window_event(HoveredFileCancelled);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
//!
|
||||
//! See the root-level documentation for information on how to create and use an event loop to
|
||||
//! handle events.
|
||||
use std::any::Any;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
@@ -20,10 +21,9 @@ use std::time::{Duration, Instant};
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, RequestError};
|
||||
use crate::error::{EventLoopError, ExternalError, OsError};
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::platform_impl;
|
||||
use crate::utils::AsAny;
|
||||
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes};
|
||||
|
||||
/// Provides a way to retrieve events from the system and from the windows that were registered to
|
||||
@@ -148,12 +148,11 @@ pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, suspend the thread until either another event
|
||||
/// arrives or the given time is reached.
|
||||
///
|
||||
/// Useful for implementing efficient timers, but should not be relied on for real-time needs
|
||||
/// like rendering (VSync) or audio. Applications which want to render at the display's native
|
||||
/// refresh rate should instead issue a [`Window::request_redraw`] at the end of
|
||||
/// [`WindowEvent::RedrawRequested`].
|
||||
/// Useful for implementing efficient timers. Applications which want to render at the
|
||||
/// display's native refresh rate should instead use [`Poll`] and the VSync functionality
|
||||
/// of a graphics API to reduce odds of missed frames.
|
||||
///
|
||||
/// [`WindowEvent::RedrawRequested`]: crate::event::WindowEvent::RedrawRequested
|
||||
/// [`Poll`]: Self::Poll
|
||||
WaitUntil(Instant),
|
||||
}
|
||||
|
||||
@@ -269,7 +268,7 @@ impl EventLoop {
|
||||
pub fn create_custom_cursor(
|
||||
&self,
|
||||
custom_cursor: CustomCursorSource,
|
||||
) -> Result<CustomCursor, RequestError> {
|
||||
) -> Result<CustomCursor, ExternalError> {
|
||||
self.event_loop.window_target().create_custom_cursor(custom_cursor)
|
||||
}
|
||||
}
|
||||
@@ -309,7 +308,7 @@ impl AsRawFd for EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ActiveEventLoop: AsAny {
|
||||
pub trait ActiveEventLoop {
|
||||
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
|
||||
/// to the main event loop, possibly from another thread.
|
||||
fn create_proxy(&self) -> EventLoopProxy;
|
||||
@@ -322,10 +321,7 @@ pub trait ActiveEventLoop: AsAny {
|
||||
///
|
||||
/// - **Web:** The window is created but not inserted into the Web page automatically. Please
|
||||
/// see the Web platform module for more information.
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: WindowAttributes,
|
||||
) -> Result<Box<dyn Window>, RequestError>;
|
||||
fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError>;
|
||||
|
||||
/// Create custom cursor.
|
||||
///
|
||||
@@ -335,7 +331,7 @@ pub trait ActiveEventLoop: AsAny {
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
custom_cursor: CustomCursorSource,
|
||||
) -> Result<CustomCursor, RequestError>;
|
||||
) -> Result<CustomCursor, ExternalError>;
|
||||
|
||||
/// Returns the list of all the monitors available on the system.
|
||||
///
|
||||
@@ -407,18 +403,16 @@ pub trait ActiveEventLoop: AsAny {
|
||||
/// See the [`OwnedDisplayHandle`] type for more information.
|
||||
fn owned_display_handle(&self) -> OwnedDisplayHandle;
|
||||
|
||||
/// Get the [`ActiveEventLoop`] as [`Any`].
|
||||
///
|
||||
/// This is useful for downcasting to a concrete event loop type.
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// Get the raw-window-handle handle.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
self.rwh_06_handle().display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
/// A proxy for the underlying display handle.
|
||||
///
|
||||
/// The purpose of this type is to provide a cheaply clonable handle to the underlying
|
||||
|
||||
@@ -44,16 +44,16 @@
|
||||
//! use winit::application::ApplicationHandler;
|
||||
//! use winit::event::WindowEvent;
|
||||
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
//! use winit::window::{Window, WindowId, WindowAttributes};
|
||||
//! use winit::window::{Window, WindowId};
|
||||
//!
|
||||
//! #[derive(Default)]
|
||||
//! struct App {
|
||||
//! window: Option<Box<dyn Window>>,
|
||||
//! window: Option<Window>,
|
||||
//! }
|
||||
//!
|
||||
//! impl ApplicationHandler for App {
|
||||
//! fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
//! self.window = Some(event_loop.create_window(WindowAttributes::default()).unwrap());
|
||||
//! self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
|
||||
//! }
|
||||
//!
|
||||
//! fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, id: WindowId, event: WindowEvent) {
|
||||
|
||||
@@ -47,9 +47,9 @@ impl Ord for VideoModeHandle {
|
||||
|
||||
impl VideoModeHandle {
|
||||
/// Returns the resolution of this video mode. This **must not** be used to create your
|
||||
/// rendering surface. Use [`Window::surface_size()`] instead.
|
||||
/// rendering surface. Use [`Window::inner_size()`] instead.
|
||||
///
|
||||
/// [`Window::surface_size()`]: crate::window::Window::surface_size
|
||||
/// [`Window::inner_size()`]: crate::window::Window::inner_size
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.video_mode.size()
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building
|
||||
//! with `cargo apk`, then the minimal changes would be:
|
||||
//! 1. Remove `ndk-glue` from your `Cargo.toml`
|
||||
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.5",
|
||||
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.4",
|
||||
//! features = [ "android-native-activity" ] }`
|
||||
//! 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc
|
||||
//! macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize
|
||||
@@ -99,19 +99,17 @@ pub trait WindowExtAndroid {
|
||||
fn config(&self) -> ConfigurationRef;
|
||||
}
|
||||
|
||||
impl WindowExtAndroid for dyn Window + '_ {
|
||||
impl WindowExtAndroid for Window {
|
||||
fn content_rect(&self) -> Rect {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.content_rect()
|
||||
self.window.content_rect()
|
||||
}
|
||||
|
||||
fn config(&self) -> ConfigurationRef {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.config()
|
||||
self.window.config()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtAndroid for dyn ActiveEventLoop + '_ {
|
||||
impl ActiveEventLoopExtAndroid for &dyn ActiveEventLoop {
|
||||
fn android_app(&self) -> &AndroidApp {
|
||||
let event_loop =
|
||||
self.as_any().downcast_ref::<crate::platform_impl::ActiveEventLoop>().unwrap();
|
||||
|
||||
@@ -1,79 +1,51 @@
|
||||
//! # iOS / UIKit
|
||||
//!
|
||||
//! Winit has [the same iOS version requirements as `rustc`][rustc-ios-version], although it's
|
||||
//! frequently only tested on newer iOS versions.
|
||||
//! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on
|
||||
//! iOS 9.3.
|
||||
//!
|
||||
//! [rustc-ios-version]: https://doc.rust-lang.org/rustc/platform-support/apple-ios.html#os-version
|
||||
//! iOS's main `UIApplicationMain` does some init work that's required by all
|
||||
//! UI-related code (see issue [#1705]). It is best to create your windows
|
||||
//! inside `Event::Resumed`.
|
||||
//!
|
||||
//! ## Running on Mac Catalyst
|
||||
//! [#1705]: https://github.com/rust-windowing/winit/issues/1705
|
||||
//!
|
||||
//! Mac Catalyst allows running applications using UIKit on macOS, which can be very useful for
|
||||
//! testing. See [`rustc`'s documentation on Mac Catalyst][rustc-mac-catalyst] for details on how to
|
||||
//! use these targets. To use these with Winit, you'll need to bundle your application before
|
||||
//! running it, otherwise UIKit will exit with an error.
|
||||
//! ## Building app
|
||||
//!
|
||||
//! To run e.g. the `window` example in the Winit repository, you can use [`cargo-bundle`] as
|
||||
//! follows:
|
||||
//! To build ios app you will need rustc built for this targets:
|
||||
//!
|
||||
//! ```console
|
||||
//! $ cargo +nightly bundle --format=ios --target=aarch64-apple-ios-macabi --example=window
|
||||
//! $ ./target/aarch64-apple-ios-macabi/debug/examples/bundle/ios/winit.app/window
|
||||
//! - 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]
|
||||
//! pub extern fn start_winit_app() {
|
||||
//! start_inner()
|
||||
//! }
|
||||
//!
|
||||
//! fn start_inner() {
|
||||
//! ...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [rustc-mac-catalyst]: https://doc.rust-lang.org/rustc/platform-support/apple-ios-macabi.html
|
||||
//! [`cargo-bundle`]: https://github.com/burtonageo/cargo-bundle
|
||||
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
|
||||
//!
|
||||
//! ## Introduction to building an app
|
||||
//!
|
||||
//! Building and running your application in the iOS simulator, or on a real device, is a bit more
|
||||
//! complicated than Mac Catalyst - fundamentally, you must use Xcode, since the binary needs to be
|
||||
//! bundled, signed, notarized and uploaded to the device (there is [an open source work-in-progress
|
||||
//! on re-implementing parts of this][apple-platform-rs], but the user-story around it is not yet
|
||||
//! clear).
|
||||
//!
|
||||
//! This means that you're left with effectively two options: Use a tool that manages the Xcode
|
||||
//! configuration for you, or use Xcode directly. [`cargo-dinghy`] and [`cargo-mobile2`] are notable
|
||||
//! projects in the ecosystem that attempt the former, and [`cargo-xcode`] is an excellent project
|
||||
//! that attempts the latter. We will also attempt to describe here how you would go about using
|
||||
//! Xcode directly:
|
||||
//!
|
||||
//! First off, you'll need the correct Rust targets, see [`rustc`'s documentation on iOS][rustc-ios]
|
||||
//! for details. Nowadays, the correct targets are usually `aarch64-apple-ios-sim` for the
|
||||
//! simulator, and `aarch64-apple-ios` for the actual device.
|
||||
//!
|
||||
//! Next, create a new Xcode project using the "App" template. The exact configuration does not
|
||||
//! really matter, as we're going to delete most of it, since it's tailored for Objective-C and/or
|
||||
//! Swift, and Rust/Winit is neither. Specifically, we need to delete:
|
||||
//! - Everything relating to storyboards (unless you want to use e.g. a launch screen). This
|
||||
//! includes the relevant keys in `Info.plist`.
|
||||
//! - All the generated C header, Objective-C and/or Swift files.
|
||||
//!
|
||||
//! Now that we have a fairly clean slate that we can build upon, you can add a "run script" build
|
||||
//! phase to your Xcode target, which will get invoked instead of the "compile sources" and "link
|
||||
//! binary" steps. The basic script should look something like:
|
||||
//!
|
||||
//! ```sh
|
||||
//! # Build desired targets based on `ARCHS` environment variable
|
||||
//! cargo build --target=aarch64-apple-ios --target=armv7s-apple-ios
|
||||
//! # Merge these with `lipo`, and place the result in "$TARGET_BUILD_DIR/$EXECUTABLE_PATH", which
|
||||
//! # is understood by Xcode
|
||||
//! lipo "$TARGET_BUILD_DIR/$EXECUTABLE_PATH" target/aarch64-apple-ios/debug/my_app target/armv7s-apple-ios/debug/my_app
|
||||
//! ```ignore
|
||||
//! void start_winit_app();
|
||||
//! ```
|
||||
//!
|
||||
//! Note that this is very much the overall idea; the script needs to be much more involved to
|
||||
//! properly deal with different target architectures, invoking `lipo` when needed, incremental
|
||||
//! rebuild change detection, and so on. `cargo-xcode` has a script [here][cargo-xcode-script] that
|
||||
//! handles most of this complexity, you might be able to build upon that.
|
||||
//! Use start_winit_app inside your xcode's main function.
|
||||
//!
|
||||
//! Apologies that we're not able to provide you with more than this; work is in-progress on
|
||||
//! improving the situation, but it's slow-going.
|
||||
//!
|
||||
//! [apple-platform-rs]: https://github.com/indygreg/apple-platform-rs
|
||||
//! [`cargo-dinghy`]: https://github.com/sonos/dinghy
|
||||
//! [`cargo-mobile2`]: https://github.com/tauri-apps/cargo-mobile2
|
||||
//! [`cargo-xcode`]: https://crates.io/crates/cargo-xcode
|
||||
//! [rustc-ios]: https://doc.rust-lang.org/rustc/platform-support/apple-ios.html
|
||||
//! [cargo-xcode-script]: https://gitlab.com/kornelski/cargo-xcode/-/blob/main/src/xcodebuild.sh
|
||||
//!
|
||||
//! ## App lifecycle and events
|
||||
//!
|
||||
@@ -91,16 +63,6 @@
|
||||
//! opengl will result in segfault.
|
||||
//!
|
||||
//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
|
||||
//!
|
||||
//! ## Custom `UIApplicationDelegate`
|
||||
//!
|
||||
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
|
||||
//! though, you might want to access some of the more niche stuff that [the application
|
||||
//! delegate][app-delegate] provides. This functionality is not exposed directly in Winit, since it
|
||||
//! would increase the API surface by quite a lot. Instead, Winit guarantees that it will not
|
||||
//! register an application delegate, so you can set up a custom one in a nib file instead.
|
||||
//!
|
||||
//! [app-delegate]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate?language=objc
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
@@ -209,49 +171,42 @@ pub trait WindowExtIOS {
|
||||
fn recognize_rotation_gesture(&self, should_recognize: bool);
|
||||
}
|
||||
|
||||
impl WindowExtIOS for dyn Window + '_ {
|
||||
impl WindowExtIOS for Window {
|
||||
#[inline]
|
||||
fn set_scale_factor(&self, scale_factor: f64) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_scale_factor(scale_factor));
|
||||
self.window.maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_valid_orientations(valid_orientations));
|
||||
self.window.maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden));
|
||||
self.window.maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| {
|
||||
self.window.maybe_queue_on_main(move |w| {
|
||||
w.set_preferred_screen_edges_deferring_system_gestures(edges)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_prefers_status_bar_hidden(hidden));
|
||||
self.window.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
|
||||
self.window.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn recognize_pinch_gesture(&self, should_recognize: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
|
||||
self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -261,8 +216,7 @@ impl WindowExtIOS for dyn Window + '_ {
|
||||
minimum_number_of_touches: u8,
|
||||
maximum_number_of_touches: u8,
|
||||
) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| {
|
||||
self.window.maybe_queue_on_main(move |w| {
|
||||
w.recognize_pan_gesture(
|
||||
should_recognize,
|
||||
minimum_number_of_touches,
|
||||
@@ -273,14 +227,12 @@ impl WindowExtIOS for dyn Window + '_ {
|
||||
|
||||
#[inline]
|
||||
fn recognize_doubletap_gesture(&self, should_recognize: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
|
||||
self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn recognize_rotation_gesture(&self, should_recognize: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
|
||||
self.window.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,74 +1,18 @@
|
||||
//! # macOS / AppKit
|
||||
//!
|
||||
//! Winit has [the same macOS version requirements as `rustc`][rustc-macos-version], and is tested
|
||||
//! once in a while on as low as macOS 10.14.
|
||||
//! Winit has an OS requirement of macOS 10.11 or higher (same as Rust
|
||||
//! itself), and is regularly tested on macOS 10.14.
|
||||
//!
|
||||
//! [rustc-macos-version]: https://doc.rust-lang.org/rustc/platform-support/apple-darwin.html#os-version
|
||||
//! A lot of functionality expects the application to be ready before you
|
||||
//! start doing anything; this includes creating windows, fetching monitors,
|
||||
//! drawing, and so on, see issues [#2238], [#2051] and [#2087].
|
||||
//!
|
||||
//! ## Custom `NSApplicationDelegate`
|
||||
//! If you encounter problems, you should try doing your initialization inside
|
||||
//! `Event::Resumed`.
|
||||
//!
|
||||
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
|
||||
//! though, you might want to do more niche stuff, such as [handle when the user re-activates the
|
||||
//! application][reopen]. Such functionality is not exposed directly in Winit, since it would
|
||||
//! increase the API surface by quite a lot.
|
||||
//!
|
||||
//! [reopen]: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc
|
||||
//!
|
||||
//! Instead, Winit guarantees that it will not register an application delegate, so the solution is
|
||||
//! to register your own application delegate, as outlined in the following example (see
|
||||
//! `objc2-app-kit` for more detailed information).
|
||||
#![cfg_attr(target_os = "macos", doc = "```")]
|
||||
#![cfg_attr(not(target_os = "macos"), doc = "```ignore")]
|
||||
//! use objc2::rc::Retained;
|
||||
//! use objc2::runtime::ProtocolObject;
|
||||
//! use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||
//! use objc2_app_kit::{NSApplication, NSApplicationDelegate};
|
||||
//! use objc2_foundation::{NSArray, NSURL, MainThreadMarker, NSObject, NSObjectProtocol};
|
||||
//! use winit::event_loop::EventLoop;
|
||||
//!
|
||||
//! declare_class!(
|
||||
//! struct AppDelegate;
|
||||
//!
|
||||
//! unsafe impl ClassType for AppDelegate {
|
||||
//! type Super = NSObject;
|
||||
//! type Mutability = mutability::MainThreadOnly;
|
||||
//! const NAME: &'static str = "MyAppDelegate";
|
||||
//! }
|
||||
//!
|
||||
//! impl DeclaredClass for AppDelegate {}
|
||||
//!
|
||||
//! unsafe impl NSObjectProtocol for AppDelegate {}
|
||||
//!
|
||||
//! unsafe impl NSApplicationDelegate for AppDelegate {
|
||||
//! #[method(application:openURLs:)]
|
||||
//! fn application_openURLs(&self, application: &NSApplication, urls: &NSArray<NSURL>) {
|
||||
//! // Note: To specifically get `application:openURLs:` to work, you _might_
|
||||
//! // have to bundle your application. This is not done in this example.
|
||||
//! println!("open urls: {application:?}, {urls:?}");
|
||||
//! }
|
||||
//! }
|
||||
//! );
|
||||
//!
|
||||
//! impl AppDelegate {
|
||||
//! fn new(mtm: MainThreadMarker) -> Retained<Self> {
|
||||
//! unsafe { msg_send_id![super(mtm.alloc().set_ivars(())), init] }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let event_loop = EventLoop::new()?;
|
||||
//!
|
||||
//! let mtm = MainThreadMarker::new().unwrap();
|
||||
//! let delegate = AppDelegate::new(mtm);
|
||||
//! // Important: Call `sharedApplication` after `EventLoop::new`,
|
||||
//! // doing it before is not yet supported.
|
||||
//! let app = NSApplication::sharedApplication(mtm);
|
||||
//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
//!
|
||||
//! // event_loop.run_app(&mut my_app);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//! [#2238]: https://github.com/rust-windowing/winit/issues/2238
|
||||
//! [#2051]: https://github.com/rust-windowing/winit/issues/2051
|
||||
//! [#2087]: https://github.com/rust-windowing/winit/issues/2087
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
@@ -150,109 +94,77 @@ pub trait WindowExtMacOS {
|
||||
|
||||
/// Getter for the [`WindowExtMacOS::set_option_as_alt`].
|
||||
fn option_as_alt(&self) -> OptionAsAlt;
|
||||
|
||||
/// Disable the Menu Bar and Dock in Borderless Fullscreen mode. Useful for games.
|
||||
fn set_borderless_game(&self, borderless_game: bool);
|
||||
|
||||
/// Getter for the [`WindowExtMacOS::set_borderless_game`].
|
||||
fn is_borderless_game(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WindowExtMacOS for dyn Window + '_ {
|
||||
impl WindowExtMacOS for Window {
|
||||
#[inline]
|
||||
fn simple_fullscreen(&self) -> bool {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.simple_fullscreen())
|
||||
self.window.maybe_wait_on_main(|w| w.simple_fullscreen())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
|
||||
self.window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_shadow(&self) -> bool {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.has_shadow())
|
||||
self.window.maybe_wait_on_main(|w| w.has_shadow())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_has_shadow(&self, has_shadow: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_has_shadow(has_shadow));
|
||||
self.window.maybe_queue_on_main(move |w| w.set_has_shadow(has_shadow))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_tabbing_identifier(&self, identifier: &str) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
|
||||
self.window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn tabbing_identifier(&self) -> String {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.tabbing_identifier())
|
||||
self.window.maybe_wait_on_main(|w| w.tabbing_identifier())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn select_next_tab(&self) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.select_next_tab());
|
||||
self.window.maybe_queue_on_main(|w| w.select_next_tab())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn select_previous_tab(&self) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.select_previous_tab());
|
||||
self.window.maybe_queue_on_main(|w| w.select_previous_tab())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn select_tab_at_index(&self, index: usize) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.select_tab_at_index(index));
|
||||
self.window.maybe_queue_on_main(move |w| w.select_tab_at_index(index))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn num_tabs(&self) -> usize {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.num_tabs())
|
||||
self.window.maybe_wait_on_main(|w| w.num_tabs())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_document_edited(&self) -> bool {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.is_document_edited())
|
||||
self.window.maybe_wait_on_main(|w| w.is_document_edited())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_document_edited(&self, edited: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_document_edited(edited));
|
||||
self.window.maybe_queue_on_main(move |w| w.set_document_edited(edited))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_option_as_alt(option_as_alt));
|
||||
self.window.maybe_queue_on_main(move |w| w.set_option_as_alt(option_as_alt))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn option_as_alt(&self) -> OptionAsAlt {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.option_as_alt())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_borderless_game(&self, borderless_game: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_borderless_game(&self) -> bool {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.is_borderless_game())
|
||||
self.window.maybe_wait_on_main(|w| w.option_as_alt())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,8 +217,6 @@ pub trait WindowAttributesExtMacOS {
|
||||
///
|
||||
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
|
||||
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
|
||||
/// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
|
||||
fn with_borderless_game(self, borderless_game: bool) -> Self;
|
||||
}
|
||||
|
||||
impl WindowAttributesExtMacOS for WindowAttributes {
|
||||
@@ -375,12 +285,6 @@ impl WindowAttributesExtMacOS for WindowAttributes {
|
||||
self.platform_specific.option_as_alt = option_as_alt;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_borderless_game(mut self, borderless_game: bool) -> Self {
|
||||
self.platform_specific.borderless_game = borderless_game;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EventLoopBuilderExtMacOS {
|
||||
@@ -393,11 +297,11 @@ pub trait EventLoopBuilderExtMacOS {
|
||||
/// Set the activation policy to "accessory".
|
||||
///
|
||||
/// ```
|
||||
/// use winit::event_loop::EventLoop;
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS};
|
||||
///
|
||||
/// let mut builder = EventLoop::builder();
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// builder.with_activation_policy(ActivationPolicy::Accessory);
|
||||
/// # if false { // We can't test this part
|
||||
@@ -415,11 +319,11 @@ pub trait EventLoopBuilderExtMacOS {
|
||||
/// Disable creating a default menubar.
|
||||
///
|
||||
/// ```
|
||||
/// use winit::event_loop::EventLoop;
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// use winit::platform::macos::EventLoopBuilderExtMacOS;
|
||||
///
|
||||
/// let mut builder = EventLoop::builder();
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// builder.with_default_menu(false);
|
||||
/// # if false { // We can't test this part
|
||||
@@ -492,7 +396,7 @@ pub trait ActiveEventLoopExtMacOS {
|
||||
fn allows_automatic_window_tabbing(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtMacOS for dyn ActiveEventLoop + '_ {
|
||||
impl ActiveEventLoopExtMacOS for &dyn ActiveEventLoop {
|
||||
fn hide_application(&self) {
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
use std::env;
|
||||
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::error::NotSupportedError;
|
||||
use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial};
|
||||
#[cfg(wayland_platform)]
|
||||
use crate::platform::wayland::ActiveEventLoopExtWayland;
|
||||
@@ -46,7 +46,7 @@ pub trait WindowExtStartupNotify {
|
||||
/// Request a new activation token.
|
||||
///
|
||||
/// The token will be delivered inside
|
||||
fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError>;
|
||||
fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError>;
|
||||
}
|
||||
|
||||
pub trait WindowAttributesExtStartupNotify {
|
||||
@@ -57,7 +57,7 @@ pub trait WindowAttributesExtStartupNotify {
|
||||
fn with_activation_token(self, token: ActivationToken) -> Self;
|
||||
}
|
||||
|
||||
impl EventLoopExtStartupNotify for dyn ActiveEventLoop + '_ {
|
||||
impl EventLoopExtStartupNotify for &dyn ActiveEventLoop {
|
||||
fn read_token_from_env(&self) -> Option<ActivationToken> {
|
||||
#[cfg(x11_platform)]
|
||||
let _is_wayland = false;
|
||||
@@ -72,22 +72,9 @@ impl EventLoopExtStartupNotify for dyn ActiveEventLoop + '_ {
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowExtStartupNotify for dyn Window + '_ {
|
||||
fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
|
||||
#[cfg(wayland_platform)]
|
||||
if let Some(window) = self.as_any().downcast_ref::<crate::platform_impl::wayland::Window>()
|
||||
{
|
||||
return window.request_activation_token();
|
||||
}
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
if let Some(window) =
|
||||
self.as_any().downcast_ref::<crate::platform_impl::x11::window::Window>()
|
||||
{
|
||||
return window.request_activation_token();
|
||||
}
|
||||
|
||||
Err(NotSupportedError::new("startup notify is not supported").into())
|
||||
impl WindowExtStartupNotify for Window {
|
||||
fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||
self.window.request_activation_token()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
//! * `wayland-csd-adwaita` (default).
|
||||
//! * `wayland-csd-adwaita-crossfont`.
|
||||
//! * `wayland-csd-adwaita-notitle`.
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
|
||||
use crate::monitor::MonitorHandle;
|
||||
pub use crate::window::Theme;
|
||||
use crate::window::{Window as CoreWindow, WindowAttributes};
|
||||
use crate::window::{Window, WindowAttributes};
|
||||
|
||||
/// Additional methods on [`ActiveEventLoop`] that are specific to Wayland.
|
||||
pub trait ActiveEventLoopExtWayland {
|
||||
@@ -24,7 +25,11 @@ pub trait ActiveEventLoopExtWayland {
|
||||
fn is_wayland(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtWayland for dyn ActiveEventLoop + '_ {
|
||||
pub trait WaylandApplicationHandler: ApplicationHandler + 'static {
|
||||
fn wayland_callback(&mut self);
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtWayland for &dyn ActiveEventLoop {
|
||||
#[inline]
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.as_any().downcast_ref::<crate::platform_impl::wayland::ActiveEventLoop>().is_some()
|
||||
@@ -35,6 +40,8 @@ impl ActiveEventLoopExtWayland for dyn ActiveEventLoop + '_ {
|
||||
pub trait EventLoopExtWayland {
|
||||
/// True if the [`EventLoop`] uses Wayland.
|
||||
fn is_wayland(&self) -> bool;
|
||||
|
||||
fn register_wayland_callback<T: WaylandApplicationHandler>(&mut self);
|
||||
}
|
||||
|
||||
impl EventLoopExtWayland for EventLoop {
|
||||
@@ -42,6 +49,22 @@ impl EventLoopExtWayland for EventLoop {
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.event_loop.is_wayland()
|
||||
}
|
||||
|
||||
fn register_wayland_callback<T: WaylandApplicationHandler>(&mut self) {
|
||||
let event_loop = match &self.event_loop {
|
||||
crate::platform_impl::EventLoop::Wayland(event_loop) => &event_loop.active_event_loop,
|
||||
#[cfg(x11_platform)]
|
||||
crate::platform_impl::EventLoop::X(_) => return,
|
||||
};
|
||||
|
||||
event_loop.wayland_callback.set(Some(|app: &mut dyn ApplicationHandler| {
|
||||
app.as_any()
|
||||
.expect("as_any_mut is not implemented")
|
||||
.downcast_mut::<T>()
|
||||
.unwrap()
|
||||
.wayland_callback()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`EventLoopBuilder`] that are specific to Wayland.
|
||||
@@ -71,11 +94,9 @@ impl EventLoopBuilderExtWayland for EventLoopBuilder {
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to Wayland.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub trait WindowExtWayland {}
|
||||
|
||||
impl WindowExtWayland for dyn CoreWindow + '_ {}
|
||||
impl WindowExtWayland for Window {}
|
||||
|
||||
/// Additional methods on [`WindowAttributes`] that are specific to Wayland.
|
||||
pub trait WindowAttributesExtWayland {
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
//! - [`padding`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding)
|
||||
//!
|
||||
//! The following APIs can't take them into account and will therefore provide inaccurate results:
|
||||
//! - [`WindowEvent::SurfaceResized`] and [`Window::(set_)surface_size()`]
|
||||
//! - [`WindowEvent::Resized`] and [`Window::(set_)inner_size()`]
|
||||
//! - [`WindowEvent::Occluded`]
|
||||
//! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`], and
|
||||
//! [`WindowEvent::Touch`].
|
||||
//! - [`Window::set_outer_position()`]
|
||||
//!
|
||||
//! [`WindowEvent::SurfaceResized`]: crate::event::WindowEvent::SurfaceResized
|
||||
//! [`Window::(set_)surface_size()`]: crate::window::Window::surface_size
|
||||
//! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
|
||||
//! [`Window::(set_)inner_size()`]: crate::window::Window::inner_size
|
||||
//! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded
|
||||
//! [`WindowEvent::CursorMoved`]: crate::event::WindowEvent::CursorMoved
|
||||
//! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered
|
||||
@@ -82,7 +82,7 @@ pub trait WindowExtWeb {
|
||||
|
||||
/// Returns [`true`] if calling `event.preventDefault()` is enabled.
|
||||
///
|
||||
/// See [`WindowExtWeb::set_prevent_default()`] for more details.
|
||||
/// See [`Window::set_prevent_default()`] for more details.
|
||||
fn prevent_default(&self) -> bool;
|
||||
|
||||
/// Sets whether `event.preventDefault()` should be called on events on the
|
||||
@@ -104,34 +104,22 @@ pub trait WindowExtWeb {
|
||||
fn is_cursor_lock_raw(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WindowExtWeb for dyn Window + '_ {
|
||||
impl WindowExtWeb for Window {
|
||||
#[inline]
|
||||
fn canvas(&self) -> Option<Ref<'_, HtmlCanvasElement>> {
|
||||
self.as_any()
|
||||
.downcast_ref::<crate::platform_impl::Window>()
|
||||
.expect("non Web window on Web")
|
||||
.canvas()
|
||||
self.window.canvas()
|
||||
}
|
||||
|
||||
fn prevent_default(&self) -> bool {
|
||||
self.as_any()
|
||||
.downcast_ref::<crate::platform_impl::Window>()
|
||||
.expect("non Web window on Web")
|
||||
.prevent_default()
|
||||
self.window.prevent_default()
|
||||
}
|
||||
|
||||
fn set_prevent_default(&self, prevent_default: bool) {
|
||||
self.as_any()
|
||||
.downcast_ref::<crate::platform_impl::Window>()
|
||||
.expect("non Web window on Web")
|
||||
.set_prevent_default(prevent_default)
|
||||
self.window.set_prevent_default(prevent_default)
|
||||
}
|
||||
|
||||
fn is_cursor_lock_raw(&self) -> bool {
|
||||
self.as_any()
|
||||
.downcast_ref::<crate::platform_impl::Window>()
|
||||
.expect("non Web window on Web")
|
||||
.is_cursor_lock_raw()
|
||||
self.window.is_cursor_lock_raw()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +136,7 @@ pub trait WindowAttributesExtWeb {
|
||||
/// Sets whether `event.preventDefault()` should be called on events on the
|
||||
/// canvas that have side effects.
|
||||
///
|
||||
/// See [`WindowExtWeb::set_prevent_default()`] for more details.
|
||||
/// See [`Window::set_prevent_default()`] for more details.
|
||||
///
|
||||
/// Enabled by default.
|
||||
fn with_prevent_default(self, prevent_default: bool) -> Self;
|
||||
@@ -359,7 +347,7 @@ pub trait ActiveEventLoopExtWeb {
|
||||
fn has_detailed_monitor_permission(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ {
|
||||
impl ActiveEventLoopExtWeb for &dyn ActiveEventLoop {
|
||||
#[inline]
|
||||
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture {
|
||||
let event_loop = self
|
||||
@@ -711,9 +699,10 @@ pub struct OrientationData {
|
||||
pub orientation: Orientation,
|
||||
/// [`true`] if the [`orientation`](Self::orientation) is flipped upside down.
|
||||
pub flipped: bool,
|
||||
/// The most natural orientation for the screen. Computer monitors are commonly naturally
|
||||
/// landscape mode, while mobile phones are commonly naturally portrait mode.
|
||||
pub natural: Orientation,
|
||||
/// [`true`] if the [`Orientation`] is the most natural one for the screen regardless of being
|
||||
/// flipped. Computer monitors are commonly naturally landscape mode, while mobile phones
|
||||
/// are commonly naturally portrait mode.
|
||||
pub natural: bool,
|
||||
}
|
||||
|
||||
/// Screen orientation.
|
||||
@@ -737,14 +726,14 @@ pub enum OrientationLock {
|
||||
/// User is locked to landscape mode.
|
||||
Landscape {
|
||||
/// - [`None`]: User is locked to both upright or upside down landscape mode.
|
||||
/// - [`true`]: User is locked to upright landscape mode.
|
||||
/// - [`false`]: User is locked to upright landscape mode.
|
||||
/// - [`false`]: User is locked to upside down landscape mode.
|
||||
flipped: Option<bool>,
|
||||
},
|
||||
/// User is locked to portrait mode.
|
||||
Portrait {
|
||||
/// - [`None`]: User is locked to both upright or upside down portrait mode.
|
||||
/// - [`true`]: User is locked to upright portrait mode.
|
||||
/// - [`false`]: User is locked to upright portrait mode.
|
||||
/// - [`false`]: User is locked to upside down portrait mode.
|
||||
flipped: Option<bool>,
|
||||
},
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
//! tested regularly.
|
||||
use std::borrow::Borrow;
|
||||
use std::ffi::c_void;
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -116,25 +115,50 @@ pub enum CornerPreference {
|
||||
///
|
||||
/// See [`WindowBorrowExtWindows::any_thread`] for more information.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnyThread<W: Window>(W);
|
||||
pub struct AnyThread<W>(W);
|
||||
|
||||
impl<W: Window> AnyThread<W> {
|
||||
impl<W: Borrow<Window>> AnyThread<W> {
|
||||
/// Get a reference to the inner window.
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> &dyn Window {
|
||||
pub fn get_ref(&self) -> &Window {
|
||||
self.0.borrow()
|
||||
}
|
||||
|
||||
/// Get a reference to the inner object.
|
||||
#[inline]
|
||||
pub fn inner(&self) -> &W {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Unwrap and get the inner window.
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> W {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Window> Deref for AnyThread<W> {
|
||||
type Target = W;
|
||||
impl<W: Borrow<Window>> AsRef<Window> for AnyThread<W> {
|
||||
fn as_ref(&self) -> &Window {
|
||||
self.get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Borrow<Window>> Borrow<Window> for AnyThread<W> {
|
||||
fn borrow(&self) -> &Window {
|
||||
self.get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Borrow<Window>> std::ops::Deref for AnyThread<W> {
|
||||
type Target = Window;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
self.get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl<W: Window> rwh_06::HasWindowHandle for AnyThread<W> {
|
||||
impl<W: Borrow<Window>> rwh_06::HasWindowHandle for AnyThread<W> {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
// SAFETY: The top level user has asserted this is only used safely.
|
||||
unsafe { self.get_ref().window_handle_any_thread() }
|
||||
@@ -317,7 +341,7 @@ pub trait WindowExtWindows {
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use winit::window::Window;
|
||||
/// # fn scope(window: Box<dyn Window>) {
|
||||
/// # fn scope(window: Window) {
|
||||
/// use std::thread;
|
||||
///
|
||||
/// use winit::platform::windows::WindowExtWindows;
|
||||
@@ -341,41 +365,35 @@ pub trait WindowExtWindows {
|
||||
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>;
|
||||
}
|
||||
|
||||
impl WindowExtWindows for dyn Window + '_ {
|
||||
impl WindowExtWindows for Window {
|
||||
#[inline]
|
||||
fn set_enable(&self, enabled: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_enable(enabled)
|
||||
self.window.set_enable(enabled)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_taskbar_icon(taskbar_icon)
|
||||
self.window.set_taskbar_icon(taskbar_icon)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_skip_taskbar(&self, skip: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_skip_taskbar(skip)
|
||||
self.window.set_skip_taskbar(skip)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_undecorated_shadow(&self, shadow: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_undecorated_shadow(shadow)
|
||||
self.window.set_undecorated_shadow(shadow)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_system_backdrop(&self, backdrop_type: BackdropType) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_system_backdrop(backdrop_type)
|
||||
self.window.set_system_backdrop(backdrop_type)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_border_color(&self, color: Option<Color>) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_border_color(color.unwrap_or(Color::NONE))
|
||||
self.window.set_border_color(color.unwrap_or(Color::NONE))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -383,29 +401,25 @@ impl WindowExtWindows for dyn Window + '_ {
|
||||
// The windows docs don't mention NONE as a valid options but it works in practice and is
|
||||
// useful to circumvent the Windows option "Show accent color on title bars and
|
||||
// window borders"
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_title_background_color(color.unwrap_or(Color::NONE))
|
||||
self.window.set_title_background_color(color.unwrap_or(Color::NONE))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_title_text_color(&self, color: Color) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_title_text_color(color)
|
||||
self.window.set_title_text_color(color)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_corner_preference(&self, preference: CornerPreference) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_corner_preference(preference)
|
||||
self.window.set_corner_preference(preference)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
unsafe fn window_handle_any_thread(
|
||||
&self,
|
||||
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
unsafe {
|
||||
let handle = window.rwh_06_no_thread_check()?;
|
||||
let handle = self.window.rwh_06_no_thread_check()?;
|
||||
|
||||
// SAFETY: The handle is valid in this context.
|
||||
Ok(rwh_06::WindowHandle::borrow_raw(handle))
|
||||
@@ -416,7 +430,7 @@ impl WindowExtWindows for dyn Window + '_ {
|
||||
/// Additional methods for anything that dereference to [`Window`].
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub trait WindowBorrowExtWindows: Borrow<dyn Window> + Sized {
|
||||
pub trait WindowBorrowExtWindows: Borrow<Window> + Sized {
|
||||
/// Create an object that allows accessing the inner window handle in a thread-unsafe way.
|
||||
///
|
||||
/// It is possible to call [`window_handle_any_thread`] to get around Windows's thread
|
||||
@@ -443,15 +457,12 @@ pub trait WindowBorrowExtWindows: Borrow<dyn Window> + Sized {
|
||||
doc = "[`HasWindowHandle`]: #only-available-with-rwh_06",
|
||||
doc = "[`window_handle_any_thread`]: #only-available-with-rwh_06"
|
||||
)]
|
||||
unsafe fn any_thread(self) -> AnyThread<Self>
|
||||
where
|
||||
Self: Window,
|
||||
{
|
||||
unsafe fn any_thread(self) -> AnyThread<Self> {
|
||||
AnyThread(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Borrow<dyn Window> + Sized> WindowBorrowExtWindows for W {}
|
||||
impl<W: Borrow<Window> + Sized> WindowBorrowExtWindows for W {}
|
||||
|
||||
/// Additional methods on `WindowAttributes` that are specific to Windows.
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::dpi::Size;
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::window::{Window as CoreWindow, WindowAttributes};
|
||||
use crate::window::{Window, WindowAttributes};
|
||||
|
||||
/// X window type. Maps directly to
|
||||
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
|
||||
@@ -91,7 +91,7 @@ pub trait ActiveEventLoopExtX11 {
|
||||
fn is_x11(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtX11 for dyn ActiveEventLoop + '_ {
|
||||
impl ActiveEventLoopExtX11 for &dyn ActiveEventLoop {
|
||||
#[inline]
|
||||
fn is_x11(&self) -> bool {
|
||||
self.as_any().downcast_ref::<crate::platform_impl::x11::ActiveEventLoop>().is_some()
|
||||
@@ -138,11 +138,9 @@ impl EventLoopBuilderExtX11 for EventLoopBuilder {
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to X11.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub trait WindowExtX11 {}
|
||||
|
||||
impl WindowExtX11 for dyn CoreWindow {}
|
||||
impl WindowExtX11 for Window {}
|
||||
|
||||
/// Additional methods on [`WindowAttributes`] that are specific to X11.
|
||||
pub trait WindowAttributesExtX11 {
|
||||
@@ -171,13 +169,13 @@ pub trait WindowAttributesExtX11 {
|
||||
///
|
||||
/// ```
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::window::{Window, WindowAttributes};
|
||||
/// # use winit::window::Window;
|
||||
/// # use winit::platform::x11::WindowAttributesExtX11;
|
||||
/// // Specify the size in logical dimensions like this:
|
||||
/// WindowAttributes::default().with_base_size(LogicalSize::new(400.0, 200.0));
|
||||
/// Window::default_attributes().with_base_size(LogicalSize::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the size in physical dimensions like this:
|
||||
/// WindowAttributes::default().with_base_size(PhysicalSize::new(400, 200));
|
||||
/// Window::default_attributes().with_base_size(PhysicalSize::new(400, 200));
|
||||
/// ```
|
||||
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
|
||||
|
||||
@@ -186,12 +184,12 @@ pub trait WindowAttributesExtX11 {
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winit::window::{Window, WindowAttributes};
|
||||
/// use winit::window::Window;
|
||||
/// use winit::event_loop::ActiveEventLoop;
|
||||
/// use winit::platform::x11::{XWindow, WindowAttributesExtX11};
|
||||
/// # fn create_window(event_loop: &dyn ActiveEventLoop) -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?;
|
||||
/// let window_attributes = WindowAttributes::default().with_embed_parent_window(parent_window_id);
|
||||
/// let window_attributes = Window::default_attributes().with_embed_parent_window(parent_window_id);
|
||||
/// let window = event_loop.create_window(window_attributes)?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::Any;
|
||||
use std::cell::Cell;
|
||||
use std::hash::Hash;
|
||||
use std::num::{NonZeroU16, NonZeroU32};
|
||||
@@ -14,17 +15,18 @@ use tracing::{debug, trace, warn};
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use crate::event::{self, Force, StartCause, SurfaceSizeWriter};
|
||||
use crate::error::{self, EventLoopError, ExternalError, NotSupportedError};
|
||||
use crate::event::{self, Force, InnerSizeWriter, StartCause};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::Fullscreen;
|
||||
use crate::window::{
|
||||
self, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, ImePurpose,
|
||||
ResizeDirection, Theme, Window as CoreWindow, WindowAttributes, WindowButtons, WindowLevel,
|
||||
self, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose, ResizeDirection, Theme,
|
||||
Window as RootWindow, WindowAttributes, WindowButtons, WindowLevel,
|
||||
};
|
||||
|
||||
mod keycodes;
|
||||
@@ -201,11 +203,11 @@ impl EventLoop {
|
||||
let old_scale_factor = scale_factor(&self.android_app);
|
||||
let scale_factor = scale_factor(&self.android_app);
|
||||
if (scale_factor - old_scale_factor).abs() < f64::EPSILON {
|
||||
let new_surface_size = Arc::new(Mutex::new(screen_size(&self.android_app)));
|
||||
let new_inner_size = Arc::new(Mutex::new(screen_size(&self.android_app)));
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::ScaleFactorChanged {
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(
|
||||
&new_surface_size,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&new_inner_size,
|
||||
)),
|
||||
scale_factor,
|
||||
};
|
||||
@@ -287,7 +289,7 @@ impl EventLoop {
|
||||
PhysicalSize::new(0, 0)
|
||||
};
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::SurfaceResized(size);
|
||||
let event = event::WindowEvent::Resized(size);
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
}
|
||||
|
||||
@@ -592,15 +594,16 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: WindowAttributes,
|
||||
) -> Result<Box<dyn CoreWindow>, RequestError> {
|
||||
Ok(Box::new(Window::new(self, window_attributes)?))
|
||||
) -> Result<RootWindow, error::OsError> {
|
||||
let window = Window::new(self, window_attributes)?;
|
||||
Ok(RootWindow { window })
|
||||
}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
_source: CustomCursorSource,
|
||||
) -> Result<CustomCursor, RequestError> {
|
||||
Err(NotSupportedError::new("create_custom_cursor is not supported").into())
|
||||
) -> Result<CustomCursor, ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
|
||||
@@ -637,6 +640,10 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
@@ -715,24 +722,178 @@ impl Window {
|
||||
pub(crate) fn new(
|
||||
el: &ActiveEventLoop,
|
||||
_window_attrs: window::WindowAttributes,
|
||||
) -> Result<Self, RequestError> {
|
||||
) -> Result<Self, error::OsError> {
|
||||
// FIXME this ignores requested window attributes
|
||||
|
||||
Ok(Self { app: el.app.clone(), redraw_requester: el.redraw_requester.clone() })
|
||||
}
|
||||
|
||||
pub fn config(&self) -> ConfigurationRef {
|
||||
self.app.config()
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) {
|
||||
f(self)
|
||||
}
|
||||
|
||||
pub fn content_rect(&self) -> Rect {
|
||||
self.app.content_rect()
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Self) -> R + Send) -> R {
|
||||
f(self)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> Option<MonitorHandle> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
scale_factor(&self.app)
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
self.redraw_requester.request_redraw()
|
||||
}
|
||||
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
|
||||
Err(error::NotSupportedError::new())
|
||||
}
|
||||
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
|
||||
Err(error::NotSupportedError::new())
|
||||
}
|
||||
|
||||
pub fn set_outer_position(&self, _position: Position) {
|
||||
// no effect
|
||||
}
|
||||
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
self.outer_size()
|
||||
}
|
||||
|
||||
pub fn request_inner_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
|
||||
Some(self.inner_size())
|
||||
}
|
||||
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
screen_size(&self.app)
|
||||
}
|
||||
|
||||
pub fn set_min_inner_size(&self, _: Option<Size>) {}
|
||||
|
||||
pub fn set_max_inner_size(&self, _: Option<Size>) {}
|
||||
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_resize_increments(&self, _increments: Option<Size>) {}
|
||||
|
||||
pub fn set_title(&self, _title: &str) {}
|
||||
|
||||
pub fn set_transparent(&self, _transparent: bool) {}
|
||||
|
||||
pub fn set_blur(&self, _blur: bool) {}
|
||||
|
||||
pub fn set_visible(&self, _visibility: bool) {}
|
||||
|
||||
pub fn is_visible(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_resizable(&self, _resizeable: bool) {}
|
||||
|
||||
pub fn is_resizable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
|
||||
|
||||
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||
WindowButtons::all()
|
||||
}
|
||||
|
||||
pub fn set_minimized(&self, _minimized: bool) {}
|
||||
|
||||
pub fn is_minimized(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_maximized(&self, _maximized: bool) {}
|
||||
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn set_fullscreen(&self, _monitor: Option<Fullscreen>) {
|
||||
warn!("Cannot set fullscreen on Android");
|
||||
}
|
||||
|
||||
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_decorations(&self, _decorations: bool) {}
|
||||
|
||||
pub fn is_decorated(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn set_window_level(&self, _level: WindowLevel) {}
|
||||
|
||||
pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
|
||||
|
||||
pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
|
||||
|
||||
pub fn set_ime_allowed(&self, _allowed: bool) {}
|
||||
|
||||
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}
|
||||
|
||||
pub fn focus_window(&self) {}
|
||||
|
||||
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
|
||||
|
||||
pub fn set_cursor(&self, _: Cursor) {}
|
||||
|
||||
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(error::NotSupportedError::new()))
|
||||
}
|
||||
|
||||
pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(error::NotSupportedError::new()))
|
||||
}
|
||||
|
||||
pub fn set_cursor_visible(&self, _: bool) {}
|
||||
|
||||
pub fn drag_window(&self) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(error::NotSupportedError::new()))
|
||||
}
|
||||
|
||||
pub fn drag_resize_window(
|
||||
&self,
|
||||
_direction: ResizeDirection,
|
||||
) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(error::NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(error::NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
// Allow the usage of HasRawWindowHandle inside this function
|
||||
#[allow(deprecated)]
|
||||
fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
use rwh_06::HasRawWindowHandle;
|
||||
|
||||
if let Some(native_window) = self.app.native_window().as_ref() {
|
||||
@@ -748,206 +909,37 @@ impl Window {
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::Android(rwh_06::AndroidDisplayHandle::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> window::WindowId {
|
||||
window::WindowId(WindowId)
|
||||
pub fn config(&self) -> ConfigurationRef {
|
||||
self.app.config()
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<RootMonitorHandle> {
|
||||
pub fn content_rect(&self) -> Rect {
|
||||
self.app.content_rect()
|
||||
}
|
||||
|
||||
pub fn set_theme(&self, _theme: Option<Theme>) {}
|
||||
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
|
||||
Box::new(std::iter::empty())
|
||||
}
|
||||
pub fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
fn current_monitor(&self) -> Option<RootMonitorHandle> {
|
||||
None
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
scale_factor(&self.app)
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
self.redraw_requester.request_redraw()
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Err(NotSupportedError::new("inner_position is not supported").into())
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Err(NotSupportedError::new("outer_position is not supported").into())
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, _position: Position) {
|
||||
// no effect
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
self.outer_size()
|
||||
}
|
||||
|
||||
fn request_surface_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
|
||||
Some(self.surface_size())
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
screen_size(&self.app)
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, _: Option<Size>) {}
|
||||
|
||||
fn set_max_surface_size(&self, _: Option<Size>) {}
|
||||
|
||||
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_surface_resize_increments(&self, _increments: Option<Size>) {}
|
||||
|
||||
fn set_title(&self, _title: &str) {}
|
||||
|
||||
fn set_transparent(&self, _transparent: bool) {}
|
||||
|
||||
fn set_blur(&self, _blur: bool) {}
|
||||
|
||||
fn set_visible(&self, _visibility: bool) {}
|
||||
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_resizable(&self, _resizeable: bool) {}
|
||||
|
||||
fn is_resizable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
|
||||
|
||||
fn enabled_buttons(&self) -> WindowButtons {
|
||||
WindowButtons::all()
|
||||
}
|
||||
|
||||
fn set_minimized(&self, _minimized: bool) {}
|
||||
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_maximized(&self, _maximized: bool) {}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, _monitor: Option<Fullscreen>) {
|
||||
warn!("Cannot set fullscreen on Android");
|
||||
}
|
||||
|
||||
fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_decorations(&self, _decorations: bool) {}
|
||||
|
||||
fn is_decorated(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_window_level(&self, _level: WindowLevel) {}
|
||||
|
||||
fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
|
||||
|
||||
fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
|
||||
|
||||
fn set_ime_allowed(&self, _allowed: bool) {}
|
||||
|
||||
fn set_ime_purpose(&self, _purpose: ImePurpose) {}
|
||||
|
||||
fn focus_window(&self) {}
|
||||
|
||||
fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
|
||||
|
||||
fn set_cursor(&self, _: Cursor) {}
|
||||
|
||||
fn set_cursor_position(&self, _: Position) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("set_cursor_position is not supported").into())
|
||||
}
|
||||
|
||||
fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("set_cursor_grab is not supported").into())
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, _: bool) {}
|
||||
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("drag_window is not supported").into())
|
||||
}
|
||||
|
||||
fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("drag_resize_window").into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("set_cursor_hittest is not supported").into())
|
||||
}
|
||||
|
||||
fn set_theme(&self, _theme: Option<Theme>) {}
|
||||
|
||||
fn theme(&self) -> Option<Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
pub fn has_focus(&self) -> bool {
|
||||
HAS_FOCUS.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
pub fn title(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
|
||||
fn reset_dead_keys(&self) {}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
pub fn reset_dead_keys(&self) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||
|
||||
use super::app_state::AppState;
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::DEVICE_ID;
|
||||
use crate::event::{DeviceEvent, ElementState};
|
||||
|
||||
@@ -40,15 +38,15 @@ declare_class!(
|
||||
key_window.sendEvent(event);
|
||||
}
|
||||
} else {
|
||||
let app_state = AppState::get(MainThreadMarker::from(self));
|
||||
maybe_dispatch_device_event(&app_state, event);
|
||||
let delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
|
||||
maybe_dispatch_device_event(&delegate, event);
|
||||
unsafe { msg_send![super(self), sendEvent: event] }
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||
fn maybe_dispatch_device_event(delegate: &ApplicationDelegate, event: &NSEvent) {
|
||||
let event_type = unsafe { event.r#type() };
|
||||
#[allow(non_upper_case_globals)]
|
||||
match event_type {
|
||||
@@ -60,7 +58,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||
let delta_y = unsafe { event.deltaY() } as f64;
|
||||
|
||||
if delta_x != 0.0 || delta_y != 0.0 {
|
||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseMotion {
|
||||
delta: (delta_x, delta_y),
|
||||
});
|
||||
@@ -69,7 +67,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||
},
|
||||
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
|
||||
let button = unsafe { event.buttonNumber() } as u32;
|
||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
|
||||
button,
|
||||
state: ElementState::Pressed,
|
||||
@@ -78,7 +76,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||
},
|
||||
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
||||
let button = unsafe { event.buttonNumber() } as u32;
|
||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
|
||||
button,
|
||||
state: ElementState::Released,
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use std::cell::{Cell, OnceCell, RefCell};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::mem;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::rc::Weak;
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotification};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol};
|
||||
|
||||
use super::super::event_handler::EventHandler;
|
||||
use super::event_handler::EventHandler;
|
||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
||||
use super::observer::{EventLoopWaker, RunLoop};
|
||||
use super::{menu, WindowId};
|
||||
@@ -19,7 +21,6 @@ use crate::window::WindowId as RootWindowId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct AppState {
|
||||
mtm: MainThreadMarker,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
@@ -45,32 +46,43 @@ pub(super) struct AppState {
|
||||
// as such should be careful to not add fields that, in turn, strongly reference those.
|
||||
}
|
||||
|
||||
// TODO(madsmtm): Use `MainThreadBound` once that is possible in `static`s.
|
||||
struct StaticMainThreadBound<T>(T);
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ApplicationDelegate;
|
||||
|
||||
impl<T> StaticMainThreadBound<T> {
|
||||
const fn get(&self, _mtm: MainThreadMarker) -> &T {
|
||||
&self.0
|
||||
unsafe impl ClassType for ApplicationDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitApplicationDelegate";
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for StaticMainThreadBound<T> {}
|
||||
unsafe impl<T> Sync for StaticMainThreadBound<T> {}
|
||||
impl DeclaredClass for ApplicationDelegate {
|
||||
type Ivars = AppState;
|
||||
}
|
||||
|
||||
// SAFETY: Creating `StaticMainThreadBound` in a `const` context, where there is no concept of the
|
||||
// main thread.
|
||||
static GLOBAL: StaticMainThreadBound<OnceCell<Rc<AppState>>> =
|
||||
StaticMainThreadBound(OnceCell::new());
|
||||
unsafe impl NSObjectProtocol for ApplicationDelegate {}
|
||||
|
||||
impl AppState {
|
||||
pub(super) fn setup_global(
|
||||
unsafe impl NSApplicationDelegate for ApplicationDelegate {
|
||||
#[method(applicationDidFinishLaunching:)]
|
||||
fn app_did_finish_launching(&self, notification: &NSNotification) {
|
||||
self.did_finish_launching(notification)
|
||||
}
|
||||
|
||||
#[method(applicationWillTerminate:)]
|
||||
fn app_will_terminate(&self, notification: &NSNotification) {
|
||||
self.will_terminate(notification)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl ApplicationDelegate {
|
||||
pub(super) fn new(
|
||||
mtm: MainThreadMarker,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) -> Rc<Self> {
|
||||
let this = Rc::new(AppState {
|
||||
mtm,
|
||||
) -> Retained<Self> {
|
||||
let this = mtm.alloc().set_ivars(AppState {
|
||||
activation_policy,
|
||||
proxy_wake_up: Arc::new(AtomicBool::new(false)),
|
||||
default_menu,
|
||||
@@ -90,41 +102,33 @@ impl AppState {
|
||||
wait_timeout: Cell::new(None),
|
||||
pending_redraw: RefCell::new(vec![]),
|
||||
});
|
||||
|
||||
GLOBAL.get(mtm).set(this.clone()).expect("application state can only be set once");
|
||||
this
|
||||
unsafe { msg_send_id![super(this), init] }
|
||||
}
|
||||
|
||||
pub fn get(mtm: MainThreadMarker) -> Rc<Self> {
|
||||
GLOBAL
|
||||
.get(mtm)
|
||||
.get()
|
||||
.expect("tried to get application state before it was registered")
|
||||
.clone()
|
||||
}
|
||||
// NOTE: This will, globally, only be run once, no matter how many
|
||||
// `EventLoop`s the user creates.
|
||||
fn did_finish_launching(&self, _notification: &NSNotification) {
|
||||
trace_scope!("applicationDidFinishLaunching:");
|
||||
self.ivars().is_launched.set(true);
|
||||
|
||||
// NOTE: This notification will, globally, only be emitted once,
|
||||
// no matter how many `EventLoop`s the user creates.
|
||||
pub fn did_finish_launching(self: &Rc<Self>, _notification: &NSNotification) {
|
||||
trace_scope!("NSApplicationDidFinishLaunchingNotification");
|
||||
self.is_launched.set(true);
|
||||
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
app.setActivationPolicy(self.activation_policy);
|
||||
app.setActivationPolicy(self.ivars().activation_policy);
|
||||
|
||||
window_activation_hack(&app);
|
||||
#[allow(deprecated)]
|
||||
app.activateIgnoringOtherApps(self.activate_ignoring_other_apps);
|
||||
app.activateIgnoringOtherApps(self.ivars().activate_ignoring_other_apps);
|
||||
|
||||
if self.default_menu {
|
||||
if self.ivars().default_menu {
|
||||
// The menubar initialization should be before the `NewEvents` event, to allow
|
||||
// overriding of the default menu even if it's created
|
||||
menu::initialize(&app);
|
||||
}
|
||||
|
||||
self.waker.borrow_mut().start();
|
||||
self.ivars().waker.borrow_mut().start();
|
||||
|
||||
self.set_is_running(true);
|
||||
self.dispatch_init_events();
|
||||
@@ -134,65 +138,77 @@ impl AppState {
|
||||
//
|
||||
// In this case we still want to consider Winit's `EventLoop` to be "running",
|
||||
// so we call `start_running()` above.
|
||||
if self.stop_on_launch.get() {
|
||||
if self.ivars().stop_on_launch.get() {
|
||||
// NOTE: the original idea had been to only stop the underlying `RunLoop`
|
||||
// for the app but that didn't work as expected (`-[NSApplication run]`
|
||||
// effectively ignored the attempt to stop the RunLoop and re-started it).
|
||||
//
|
||||
// So we return from `pump_events` by stopping the application.
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) {
|
||||
trace_scope!("NSApplicationWillTerminateNotification");
|
||||
fn will_terminate(&self, _notification: &NSNotification) {
|
||||
trace_scope!("applicationWillTerminate:");
|
||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||
self.internal_exit();
|
||||
}
|
||||
|
||||
/// Place the event handler in the application state for the duration
|
||||
pub fn get(mtm: MainThreadMarker) -> Retained<Self> {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
let delegate =
|
||||
unsafe { app.delegate() }.expect("a delegate was not configured on the application");
|
||||
if delegate.is_kind_of::<Self>() {
|
||||
// SAFETY: Just checked that the delegate is an instance of `ApplicationDelegate`
|
||||
unsafe { Retained::cast(delegate) }
|
||||
} else {
|
||||
panic!("tried to get a delegate that was not the one Winit has registered")
|
||||
}
|
||||
}
|
||||
|
||||
/// Place the event handler in the application delegate for the duration
|
||||
/// of the given closure.
|
||||
pub fn set_event_handler<R>(
|
||||
&self,
|
||||
handler: &mut dyn ApplicationHandler,
|
||||
closure: impl FnOnce() -> R,
|
||||
) -> R {
|
||||
self.event_handler.set(handler, closure)
|
||||
self.ivars().event_handler.set(handler, closure)
|
||||
}
|
||||
|
||||
pub fn proxy_wake_up(&self) -> Arc<AtomicBool> {
|
||||
self.proxy_wake_up.clone()
|
||||
self.ivars().proxy_wake_up.clone()
|
||||
}
|
||||
|
||||
/// If `pump_events` is called to progress the event loop then we
|
||||
/// bootstrap the event loop via `-[NSApplication run]` but will use
|
||||
/// `CFRunLoopRunInMode` for subsequent calls to `pump_events`.
|
||||
pub fn set_stop_on_launch(&self) {
|
||||
self.stop_on_launch.set(true);
|
||||
self.ivars().stop_on_launch.set(true);
|
||||
}
|
||||
|
||||
pub fn set_stop_before_wait(&self, value: bool) {
|
||||
self.stop_before_wait.set(value)
|
||||
self.ivars().stop_before_wait.set(value)
|
||||
}
|
||||
|
||||
pub fn set_stop_after_wait(&self, value: bool) {
|
||||
self.stop_after_wait.set(value)
|
||||
self.ivars().stop_after_wait.set(value)
|
||||
}
|
||||
|
||||
pub fn set_stop_on_redraw(&self, value: bool) {
|
||||
self.stop_on_redraw.set(value)
|
||||
self.ivars().stop_on_redraw.set(value)
|
||||
}
|
||||
|
||||
pub fn set_wait_timeout(&self, value: Option<Instant>) {
|
||||
self.wait_timeout.set(value)
|
||||
self.ivars().wait_timeout.set(value)
|
||||
}
|
||||
|
||||
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits.
|
||||
///
|
||||
/// NOTE: that if the `NSApplication` has been launched then that state is preserved,
|
||||
/// and we won't need to re-launch the app if subsequent EventLoops are run.
|
||||
pub fn internal_exit(self: &Rc<Self>) {
|
||||
pub fn internal_exit(&self) {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.exiting(event_loop);
|
||||
});
|
||||
@@ -205,41 +221,42 @@ impl AppState {
|
||||
}
|
||||
|
||||
pub fn is_launched(&self) -> bool {
|
||||
self.is_launched.get()
|
||||
self.ivars().is_launched.get()
|
||||
}
|
||||
|
||||
pub fn set_is_running(&self, value: bool) {
|
||||
self.is_running.set(value)
|
||||
self.ivars().is_running.set(value)
|
||||
}
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.is_running.get()
|
||||
self.ivars().is_running.get()
|
||||
}
|
||||
|
||||
pub fn exit(&self) {
|
||||
self.exit.set(true)
|
||||
self.ivars().exit.set(true)
|
||||
}
|
||||
|
||||
pub fn clear_exit(&self) {
|
||||
self.exit.set(false)
|
||||
self.ivars().exit.set(false)
|
||||
}
|
||||
|
||||
pub fn exiting(&self) -> bool {
|
||||
self.exit.get()
|
||||
self.ivars().exit.get()
|
||||
}
|
||||
|
||||
pub fn set_control_flow(&self, value: ControlFlow) {
|
||||
self.control_flow.set(value)
|
||||
self.ivars().control_flow.set(value)
|
||||
}
|
||||
|
||||
pub fn control_flow(&self) -> ControlFlow {
|
||||
self.control_flow.get()
|
||||
self.ivars().control_flow.get()
|
||||
}
|
||||
|
||||
pub fn handle_redraw(self: &Rc<Self>, window_id: WindowId) {
|
||||
pub fn handle_redraw(&self, window_id: WindowId) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
// Redraw request might come out of order from the OS.
|
||||
// -> Don't go back into the event handler when our callstack originates from there
|
||||
if !self.event_handler.in_use() {
|
||||
if !self.ivars().event_handler.in_use() {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
|
||||
});
|
||||
@@ -247,24 +264,24 @@ impl AppState {
|
||||
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
|
||||
// events as a way to ensure that `pump_events` can't block an external loop
|
||||
// indefinitely
|
||||
if self.stop_on_redraw.get() {
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
if self.ivars().stop_on_redraw.get() {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_redraw(&self, window_id: WindowId) {
|
||||
let mut pending_redraw = self.pending_redraw.borrow_mut();
|
||||
let mut pending_redraw = self.ivars().pending_redraw.borrow_mut();
|
||||
if !pending_redraw.contains(&window_id) {
|
||||
pending_redraw.push(window_id);
|
||||
}
|
||||
self.run_loop.wakeup();
|
||||
self.ivars().run_loop.wakeup();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn maybe_queue_with_handler(
|
||||
self: &Rc<Self>,
|
||||
&self,
|
||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop) + 'static,
|
||||
) {
|
||||
// Most programmer actions in AppKit (e.g. change window fullscreen, set focused, etc.)
|
||||
@@ -273,28 +290,26 @@ impl AppState {
|
||||
// However, it is not documented which actions do this, and which ones are done immediately,
|
||||
// so to make sure that we don't encounter re-entrancy issues, we first check if we're
|
||||
// currently handling another event, and if we are, we queue the event instead.
|
||||
if !self.event_handler.in_use() {
|
||||
if !self.ivars().event_handler.in_use() {
|
||||
self.with_handler(callback);
|
||||
} else {
|
||||
tracing::debug!("had to queue event since another is currently being handled");
|
||||
let this = Rc::clone(self);
|
||||
self.run_loop.queue_closure(move || {
|
||||
let this = self.retain();
|
||||
self.ivars().run_loop.queue_closure(move || {
|
||||
this.with_handler(callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn with_handler(
|
||||
self: &Rc<Self>,
|
||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop),
|
||||
) {
|
||||
let event_loop = ActiveEventLoop { app_state: Rc::clone(self), mtm: self.mtm };
|
||||
self.event_handler.handle(|app| callback(app, &event_loop));
|
||||
fn with_handler(&self, callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop)) {
|
||||
let event_loop =
|
||||
ActiveEventLoop { delegate: self.retain(), mtm: MainThreadMarker::from(self) };
|
||||
self.ivars().event_handler.handle(callback, &event_loop);
|
||||
}
|
||||
|
||||
/// dispatch `NewEvents(Init)` + `Resumed`
|
||||
pub fn dispatch_init_events(self: &Rc<Self>) {
|
||||
pub fn dispatch_init_events(&self) {
|
||||
self.with_handler(|app, event_loop| app.new_events(event_loop, StartCause::Init));
|
||||
// NB: For consistency all platforms must call `can_create_surfaces` even though macOS
|
||||
// applications don't themselves have a formal surface destroy/create lifecycle.
|
||||
@@ -302,22 +317,23 @@ impl AppState {
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver after finishing waiting for new events
|
||||
pub fn wakeup(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
|
||||
pub fn wakeup(&self, panic_info: Weak<PanicInfo>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let panic_info = panic_info
|
||||
.upgrade()
|
||||
.expect("The panic info must exist here. This failure indicates a developer error.");
|
||||
|
||||
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
|
||||
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
|
||||
if panic_info.is_panicking() || !self.ivars().event_handler.ready() || !self.is_running() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.stop_after_wait.get() {
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
if self.ivars().stop_after_wait.get() {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
|
||||
let start = self.start_time.get().unwrap();
|
||||
let start = self.ivars().start_time.get().unwrap();
|
||||
let cause = match self.control_flow() {
|
||||
ControlFlow::Poll => StartCause::Poll,
|
||||
ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
|
||||
@@ -334,7 +350,8 @@ impl AppState {
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver before waiting for new events
|
||||
pub fn cleared(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
|
||||
pub fn cleared(&self, panic_info: Weak<PanicInfo>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let panic_info = panic_info
|
||||
.upgrade()
|
||||
.expect("The panic info must exist here. This failure indicates a developer error.");
|
||||
@@ -342,15 +359,15 @@ impl AppState {
|
||||
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
|
||||
// XXX: how does it make sense that `event_handler.ready()` can ever return `false` here if
|
||||
// we're about to return to the `CFRunLoop` to poll for new events?
|
||||
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
|
||||
if panic_info.is_panicking() || !self.ivars().event_handler.ready() || !self.is_running() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
|
||||
if self.ivars().proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
|
||||
self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
|
||||
}
|
||||
|
||||
let redraw = mem::take(&mut *self.pending_redraw.borrow_mut());
|
||||
let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut());
|
||||
for window_id in redraw {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
|
||||
@@ -361,22 +378,22 @@ impl AppState {
|
||||
});
|
||||
|
||||
if self.exiting() {
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
|
||||
if self.stop_before_wait.get() {
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
if self.ivars().stop_before_wait.get() {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
self.start_time.set(Some(Instant::now()));
|
||||
let wait_timeout = self.wait_timeout.get(); // configured by pump_events
|
||||
self.ivars().start_time.set(Some(Instant::now()));
|
||||
let wait_timeout = self.ivars().wait_timeout.get(); // configured by pump_events
|
||||
let app_timeout = match self.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Instant::now()),
|
||||
ControlFlow::WaitUntil(instant) => Some(instant),
|
||||
};
|
||||
self.waker.borrow_mut().start_at(min_timeout(wait_timeout, app_timeout));
|
||||
self.ivars().waker.borrow_mut().start_at(min_timeout(wait_timeout, app_timeout));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,3 +403,26 @@ impl AppState {
|
||||
fn min_timeout(a: Option<Instant>, b: Option<Instant>) -> Option<Instant> {
|
||||
a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
|
||||
}
|
||||
|
||||
/// A hack to make activation of multiple windows work when creating them before
|
||||
/// `applicationDidFinishLaunching:` / `Event::Event::NewEvents(StartCause::Init)`.
|
||||
///
|
||||
/// Alternative to this would be the user calling `window.set_visible(true)` in
|
||||
/// `StartCause::Init`.
|
||||
///
|
||||
/// If this becomes too bothersome to maintain, it can probably be removed
|
||||
/// without too much damage.
|
||||
fn window_activation_hack(app: &NSApplication) {
|
||||
// TODO: Proper ordering of the windows
|
||||
app.windows().into_iter().for_each(|window| {
|
||||
// Call `makeKeyAndOrderFront` if it was called on the window in `WinitWindow::new`
|
||||
// This way we preserve the user's desired initial visibility status
|
||||
// TODO: Also filter on the type/"level" of the window, and maybe other things?
|
||||
if window.isVisible() {
|
||||
tracing::trace!("Activating visible window");
|
||||
window.makeKeyAndOrderFront(None);
|
||||
} else {
|
||||
tracing::trace!("Skipping activating invisible window");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ use objc2_foundation::{
|
||||
NSString,
|
||||
};
|
||||
|
||||
use super::OsError;
|
||||
use crate::cursor::{CursorImage, OnlyCursorImageSource};
|
||||
use crate::error::RequestError;
|
||||
use crate::error::ExternalError;
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
@@ -24,12 +25,12 @@ unsafe impl Send for CustomCursor {}
|
||||
unsafe impl Sync for CustomCursor {}
|
||||
|
||||
impl CustomCursor {
|
||||
pub(crate) fn new(cursor: OnlyCursorImageSource) -> Result<CustomCursor, RequestError> {
|
||||
pub(crate) fn new(cursor: OnlyCursorImageSource) -> Result<CustomCursor, ExternalError> {
|
||||
cursor_from_image(&cursor.0).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result<Retained<NSCursor>, RequestError> {
|
||||
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result<Retained<NSCursor>, ExternalError> {
|
||||
let width = cursor.width;
|
||||
let height = cursor.height;
|
||||
|
||||
@@ -47,7 +48,7 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result<Retained<NSCurso
|
||||
width as isize * 4,
|
||||
32,
|
||||
)
|
||||
}.ok_or_else(|| os_error!("parent view should be installed in a window"))?;
|
||||
}.ok_or_else(|| ExternalError::Os(os_error!(OsError::CreationError("parent view should be installed in a window"))))?;
|
||||
let bitmap_data = unsafe { slice::from_raw_parts_mut(bitmap.bitmapData(), cursor.rgba.len()) };
|
||||
bitmap_data.copy_from_slice(&cursor.rgba);
|
||||
|
||||
|
||||
@@ -2,9 +2,8 @@ use std::cell::RefCell;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::platform_impl::ActiveEventLoop;
|
||||
|
||||
/// A helper type for storing a reference to `ApplicationHandler`, allowing interior mutable access
|
||||
/// to it within the execution of a closure.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct EventHandler {
|
||||
/// This can be in the following states:
|
||||
@@ -101,17 +100,19 @@ impl EventHandler {
|
||||
// soundness.
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(crate) fn in_use(&self) -> bool {
|
||||
self.inner.try_borrow().is_err()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(crate) fn ready(&self) -> bool {
|
||||
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
|
||||
}
|
||||
|
||||
pub(crate) fn handle(&self, callback: impl FnOnce(&mut dyn ApplicationHandler)) {
|
||||
pub(crate) fn handle(
|
||||
&self,
|
||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop),
|
||||
event_loop: &ActiveEventLoop,
|
||||
) {
|
||||
match self.inner.try_borrow_mut().as_deref_mut() {
|
||||
Ok(Some(user_app)) => {
|
||||
// It is important that we keep the reference borrowed here,
|
||||
@@ -120,10 +121,10 @@ impl EventHandler {
|
||||
//
|
||||
// If the handler unwinds, the `RefMut` will ensure that the
|
||||
// handler is no longer borrowed.
|
||||
callback(*user_app);
|
||||
callback(*user_app, event_loop);
|
||||
},
|
||||
Ok(None) => {
|
||||
// `NSApplication`, our app state and this handler are all
|
||||
// `NSApplication`, our app delegate and this handler are all
|
||||
// global state and so it's not impossible that we could get
|
||||
// an event after the application has exited the `EventLoop`.
|
||||
tracing::error!("tried to run event handler, but no handler was set");
|
||||
@@ -14,22 +14,19 @@ use core_foundation::runloop::{
|
||||
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use objc2::rc::{autoreleasepool, Retained};
|
||||
use objc2::runtime::ProtocolObject;
|
||||
use objc2::{msg_send_id, sel, ClassType};
|
||||
use objc2_app_kit::{
|
||||
NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
|
||||
NSApplicationWillTerminateNotification, NSWindow,
|
||||
};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject, NSObjectProtocol};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSWindow};
|
||||
use objc2_foundation::{MainThreadMarker, NSObjectProtocol};
|
||||
|
||||
use super::super::notification_center::create_observer;
|
||||
use super::app::WinitApplication;
|
||||
use super::app_state::AppState;
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::cursor::CustomCursor;
|
||||
use super::event::dummy_event;
|
||||
use super::monitor;
|
||||
use super::observer::setup_control_flow_observers;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, RequestError};
|
||||
use crate::error::{EventLoopError, ExternalError};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
@@ -38,7 +35,9 @@ use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform::macos::ActivationPolicy;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::Window;
|
||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme};
|
||||
use crate::window::{
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as RootWindow,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PanicInfo {
|
||||
@@ -72,11 +71,15 @@ impl PanicInfo {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ActiveEventLoop {
|
||||
pub(super) app_state: Rc<AppState>,
|
||||
pub(super) delegate: Retained<ApplicationDelegate>,
|
||||
pub(super) mtm: MainThreadMarker,
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
pub(super) fn app_delegate(&self) -> &ApplicationDelegate {
|
||||
&self.delegate
|
||||
}
|
||||
|
||||
pub(crate) fn hide_application(&self) {
|
||||
NSApplication::sharedApplication(self.mtm).hide(None)
|
||||
}
|
||||
@@ -96,21 +99,22 @@ impl ActiveEventLoop {
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> RootEventLoopProxy {
|
||||
let event_loop_proxy = EventLoopProxy::new(self.app_state.proxy_wake_up());
|
||||
let event_loop_proxy = EventLoopProxy::new(self.delegate.proxy_wake_up());
|
||||
RootEventLoopProxy { event_loop_proxy }
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: crate::window::WindowAttributes,
|
||||
) -> Result<Box<dyn crate::window::Window>, RequestError> {
|
||||
Ok(Box::new(Window::new(self, window_attributes)?))
|
||||
) -> Result<crate::window::Window, crate::error::OsError> {
|
||||
let window = Window::new(self, window_attributes)?;
|
||||
Ok(RootWindow { window })
|
||||
}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
source: CustomCursorSource,
|
||||
) -> Result<RootCustomCursor, RequestError> {
|
||||
) -> Result<RootCustomCursor, ExternalError> {
|
||||
Ok(RootCustomCursor { inner: CustomCursor::new(source.inner)? })
|
||||
}
|
||||
|
||||
@@ -136,25 +140,29 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
}
|
||||
|
||||
fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.app_state.set_control_flow(control_flow)
|
||||
self.delegate.set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
self.app_state.control_flow()
|
||||
self.delegate.control_flow()
|
||||
}
|
||||
|
||||
fn exit(&self) {
|
||||
self.app_state.exit()
|
||||
self.delegate.exit()
|
||||
}
|
||||
|
||||
fn exiting(&self) -> bool {
|
||||
self.app_state.exiting()
|
||||
self.delegate.exiting()
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
@@ -175,17 +183,14 @@ pub struct EventLoop {
|
||||
/// We intentionally don't store `WinitApplication` since we want to have
|
||||
/// the possibility of swapping that out at some point.
|
||||
app: Retained<NSApplication>,
|
||||
app_state: Rc<AppState>,
|
||||
/// The application delegate that we've registered.
|
||||
///
|
||||
/// The delegate is only weakly referenced by NSApplication, so we must
|
||||
/// keep it around here as well.
|
||||
delegate: Retained<ApplicationDelegate>,
|
||||
|
||||
window_target: ActiveEventLoop,
|
||||
panic_info: Rc<PanicInfo>,
|
||||
|
||||
// Since macOS 10.11, we no longer need to remove the observers before they are deallocated;
|
||||
// the system instead cleans it up next time it would have posted a notification to it.
|
||||
//
|
||||
// Though we do still need to keep the observers around to prevent them from being deallocated.
|
||||
_did_finish_launching_observer: Retained<NSObject>,
|
||||
_will_terminate_observer: Retained<NSObject>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -228,49 +233,25 @@ impl EventLoop {
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited,
|
||||
};
|
||||
|
||||
let app_state = AppState::setup_global(
|
||||
let delegate = ApplicationDelegate::new(
|
||||
mtm,
|
||||
activation_policy,
|
||||
attributes.default_menu,
|
||||
attributes.activate_ignoring_other_apps,
|
||||
);
|
||||
|
||||
let center = unsafe { NSNotificationCenter::defaultCenter() };
|
||||
|
||||
let weak_app_state = Rc::downgrade(&app_state);
|
||||
let _did_finish_launching_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationDidFinishLaunching:`
|
||||
unsafe { NSApplicationDidFinishLaunchingNotification },
|
||||
move |notification| {
|
||||
if let Some(app_state) = weak_app_state.upgrade() {
|
||||
app_state.did_finish_launching(notification);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let weak_app_state = Rc::downgrade(&app_state);
|
||||
let _will_terminate_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationWillTerminate:`
|
||||
unsafe { NSApplicationWillTerminateNotification },
|
||||
move |notification| {
|
||||
if let Some(app_state) = weak_app_state.upgrade() {
|
||||
app_state.will_terminate(notification);
|
||||
}
|
||||
},
|
||||
);
|
||||
autoreleasepool(|_| {
|
||||
app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
});
|
||||
|
||||
let panic_info: Rc<PanicInfo> = Default::default();
|
||||
setup_control_flow_observers(mtm, Rc::downgrade(&panic_info));
|
||||
|
||||
Ok(EventLoop {
|
||||
app,
|
||||
app_state: app_state.clone(),
|
||||
window_target: ActiveEventLoop { app_state, mtm },
|
||||
delegate: delegate.clone(),
|
||||
window_target: ActiveEventLoop { delegate, mtm },
|
||||
panic_info,
|
||||
_did_finish_launching_observer,
|
||||
_will_terminate_observer,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -290,19 +271,19 @@ impl EventLoop {
|
||||
&mut self,
|
||||
mut app: A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
self.app_state.clear_exit();
|
||||
self.app_state.set_event_handler(&mut app, || {
|
||||
self.delegate.clear_exit();
|
||||
self.delegate.set_event_handler(&mut app, || {
|
||||
autoreleasepool(|_| {
|
||||
// clear / normalize pump_events state
|
||||
self.app_state.set_wait_timeout(None);
|
||||
self.app_state.set_stop_before_wait(false);
|
||||
self.app_state.set_stop_after_wait(false);
|
||||
self.app_state.set_stop_on_redraw(false);
|
||||
self.delegate.set_wait_timeout(None);
|
||||
self.delegate.set_stop_before_wait(false);
|
||||
self.delegate.set_stop_after_wait(false);
|
||||
self.delegate.set_stop_on_redraw(false);
|
||||
|
||||
if self.app_state.is_launched() {
|
||||
debug_assert!(!self.app_state.is_running());
|
||||
self.app_state.set_is_running(true);
|
||||
self.app_state.dispatch_init_events();
|
||||
if self.delegate.is_launched() {
|
||||
debug_assert!(!self.delegate.is_running());
|
||||
self.delegate.set_is_running(true);
|
||||
self.delegate.dispatch_init_events();
|
||||
}
|
||||
|
||||
// SAFETY: We do not run the application re-entrantly
|
||||
@@ -317,7 +298,7 @@ impl EventLoop {
|
||||
resume_unwind(panic);
|
||||
}
|
||||
|
||||
self.app_state.internal_exit()
|
||||
self.delegate.internal_exit()
|
||||
})
|
||||
});
|
||||
|
||||
@@ -329,47 +310,47 @@ impl EventLoop {
|
||||
timeout: Option<Duration>,
|
||||
mut app: A,
|
||||
) -> PumpStatus {
|
||||
self.app_state.set_event_handler(&mut app, || {
|
||||
self.delegate.set_event_handler(&mut app, || {
|
||||
autoreleasepool(|_| {
|
||||
// As a special case, if the application hasn't been launched yet then we at least
|
||||
// run the loop until it has fully launched.
|
||||
if !self.app_state.is_launched() {
|
||||
debug_assert!(!self.app_state.is_running());
|
||||
if !self.delegate.is_launched() {
|
||||
debug_assert!(!self.delegate.is_running());
|
||||
|
||||
self.app_state.set_stop_on_launch();
|
||||
self.delegate.set_stop_on_launch();
|
||||
// SAFETY: We do not run the application re-entrantly
|
||||
unsafe { self.app.run() };
|
||||
|
||||
// Note: we dispatch `NewEvents(Init)` + `Resumed` events after the application
|
||||
// has launched
|
||||
} else if !self.app_state.is_running() {
|
||||
} else if !self.delegate.is_running() {
|
||||
// Even though the application may have been launched, it's possible we aren't
|
||||
// running if the `EventLoop` was run before and has since
|
||||
// exited. This indicates that we just starting to re-run
|
||||
// the same `EventLoop` again.
|
||||
self.app_state.set_is_running(true);
|
||||
self.app_state.dispatch_init_events();
|
||||
self.delegate.set_is_running(true);
|
||||
self.delegate.dispatch_init_events();
|
||||
} else {
|
||||
// Only run for as long as the given `Duration` allows so we don't block the
|
||||
// external loop.
|
||||
match timeout {
|
||||
Some(Duration::ZERO) => {
|
||||
self.app_state.set_wait_timeout(None);
|
||||
self.app_state.set_stop_before_wait(true);
|
||||
self.delegate.set_wait_timeout(None);
|
||||
self.delegate.set_stop_before_wait(true);
|
||||
},
|
||||
Some(duration) => {
|
||||
self.app_state.set_stop_before_wait(false);
|
||||
self.delegate.set_stop_before_wait(false);
|
||||
let timeout = Instant::now() + duration;
|
||||
self.app_state.set_wait_timeout(Some(timeout));
|
||||
self.app_state.set_stop_after_wait(true);
|
||||
self.delegate.set_wait_timeout(Some(timeout));
|
||||
self.delegate.set_stop_after_wait(true);
|
||||
},
|
||||
None => {
|
||||
self.app_state.set_wait_timeout(None);
|
||||
self.app_state.set_stop_before_wait(false);
|
||||
self.app_state.set_stop_after_wait(true);
|
||||
self.delegate.set_wait_timeout(None);
|
||||
self.delegate.set_stop_before_wait(false);
|
||||
self.delegate.set_stop_after_wait(true);
|
||||
},
|
||||
}
|
||||
self.app_state.set_stop_on_redraw(true);
|
||||
self.delegate.set_stop_on_redraw(true);
|
||||
// SAFETY: We do not run the application re-entrantly
|
||||
unsafe { self.app.run() };
|
||||
}
|
||||
@@ -383,8 +364,8 @@ impl EventLoop {
|
||||
resume_unwind(panic);
|
||||
}
|
||||
|
||||
if self.app_state.exiting() {
|
||||
self.app_state.internal_exit();
|
||||
if self.delegate.exiting() {
|
||||
self.delegate.internal_exit();
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
PumpStatus::Continue
|
||||
|
||||
@@ -5,6 +5,7 @@ mod app;
|
||||
mod app_state;
|
||||
mod cursor;
|
||||
mod event;
|
||||
mod event_handler;
|
||||
mod event_loop;
|
||||
mod ffi;
|
||||
mod menu;
|
||||
@@ -14,6 +15,8 @@ mod view;
|
||||
mod window;
|
||||
mod window_delegate;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
|
||||
pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey, KeyEventExtra};
|
||||
pub(crate) use self::event_loop::{
|
||||
@@ -48,3 +51,18 @@ impl FingerId {
|
||||
FingerId
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OsError {
|
||||
CGError(core_graphics::base::CGError),
|
||||
CreationError(&'static str),
|
||||
}
|
||||
|
||||
impl fmt::Display for OsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
OsError::CGError(e) => f.pad(&format!("CGError {e}")),
|
||||
OsError::CreationError(e) => f.pad(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use core_foundation::runloop::{
|
||||
use objc2_foundation::MainThreadMarker;
|
||||
use tracing::error;
|
||||
|
||||
use super::app_state::AppState;
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::event_loop::{stop_app_on_panic, PanicInfo};
|
||||
use super::ffi;
|
||||
|
||||
@@ -59,7 +59,7 @@ extern "C" fn control_flow_begin_handler(
|
||||
match activity {
|
||||
kCFRunLoopAfterWaiting => {
|
||||
// trace!("Triggered `CFRunLoopAfterWaiting`");
|
||||
AppState::get(MainThreadMarker::new().unwrap()).wakeup(panic_info);
|
||||
ApplicationDelegate::get(MainThreadMarker::new().unwrap()).wakeup(panic_info);
|
||||
// trace!("Completed `CFRunLoopAfterWaiting`");
|
||||
},
|
||||
_ => unreachable!(),
|
||||
@@ -81,7 +81,7 @@ extern "C" fn control_flow_end_handler(
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => {
|
||||
// trace!("Triggered `CFRunLoopBeforeWaiting`");
|
||||
AppState::get(MainThreadMarker::new().unwrap()).cleared(panic_info);
|
||||
ApplicationDelegate::get(MainThreadMarker::new().unwrap()).cleared(panic_info);
|
||||
// trace!("Completed `CFRunLoopBeforeWaiting`");
|
||||
},
|
||||
kCFRunLoopExit => (), // unimplemented!(), // not expected to ever happen
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use objc2::rc::{Retained, WeakId};
|
||||
use objc2::runtime::{AnyObject, Sel};
|
||||
@@ -17,7 +16,7 @@ use objc2_foundation::{
|
||||
NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
||||
};
|
||||
|
||||
use super::app_state::AppState;
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::cursor::{default_cursor, invisible_cursor};
|
||||
use super::event::{
|
||||
code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed,
|
||||
@@ -113,7 +112,7 @@ fn get_left_modifier_code(key: &Key) -> KeyCode {
|
||||
#[derive(Debug)]
|
||||
pub struct ViewState {
|
||||
/// Strong reference to the global application state.
|
||||
app_state: Rc<AppState>,
|
||||
app_delegate: Retained<ApplicationDelegate>,
|
||||
|
||||
cursor_state: RefCell<CursorState>,
|
||||
ime_position: Cell<NSPoint>,
|
||||
@@ -198,7 +197,7 @@ declare_class!(
|
||||
// 2. Even when a window resize does occur on a new tabbed window, it contains the wrong size (includes tab height).
|
||||
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
||||
let size = logical_size.to_physical::<u32>(self.scale_factor());
|
||||
self.queue_event(WindowEvent::SurfaceResized(size));
|
||||
self.queue_event(WindowEvent::Resized(size));
|
||||
}
|
||||
|
||||
#[method(drawRect:)]
|
||||
@@ -207,7 +206,7 @@ declare_class!(
|
||||
|
||||
// It's a workaround for https://github.com/rust-windowing/winit/issues/2640, don't replace with `self.window_id()`.
|
||||
if let Some(window) = self.ivars()._ns_window.load() {
|
||||
self.ivars().app_state.handle_redraw(window.id());
|
||||
self.ivars().app_delegate.handle_redraw(window.id());
|
||||
}
|
||||
|
||||
// This is a direct subclass of NSView, no need to call superclass' drawRect:
|
||||
@@ -688,7 +687,7 @@ declare_class!(
|
||||
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop|
|
||||
self.ivars().app_delegate.maybe_queue_with_handler(move |app, event_loop|
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseWheel { delta })
|
||||
);
|
||||
self.queue_event(WindowEvent::MouseWheel {
|
||||
@@ -783,14 +782,14 @@ declare_class!(
|
||||
|
||||
impl WinitView {
|
||||
pub(super) fn new(
|
||||
app_state: &Rc<AppState>,
|
||||
app_delegate: &ApplicationDelegate,
|
||||
window: &WinitWindow,
|
||||
accepts_first_mouse: bool,
|
||||
option_as_alt: OptionAsAlt,
|
||||
) -> Retained<Self> {
|
||||
let mtm = MainThreadMarker::from(window);
|
||||
let this = mtm.alloc().set_ivars(ViewState {
|
||||
app_state: Rc::clone(app_state),
|
||||
app_delegate: app_delegate.retain(),
|
||||
cursor_state: Default::default(),
|
||||
ime_position: Default::default(),
|
||||
ime_size: Default::default(),
|
||||
@@ -835,7 +834,7 @@ impl WinitView {
|
||||
|
||||
fn queue_event(&self, event: WindowEvent) {
|
||||
let window_id = RootWindowId(self.window().id());
|
||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
self.ivars().app_delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.window_event(event_loop, window_id, event);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use dpi::{Position, Size};
|
||||
use objc2::rc::{autoreleasepool, Retained};
|
||||
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSResponder, NSWindow};
|
||||
@@ -8,12 +7,8 @@ use objc2_foundation::{MainThreadBound, MainThreadMarker, NSObject};
|
||||
|
||||
use super::event_loop::ActiveEventLoop;
|
||||
use super::window_delegate::WindowDelegate;
|
||||
use crate::error::RequestError;
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
use crate::window::{
|
||||
Cursor, Fullscreen, Icon, ImePurpose, Theme, UserAttentionType, Window as CoreWindow,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
};
|
||||
use crate::error::OsError as RootOsError;
|
||||
use crate::window::WindowAttributes;
|
||||
|
||||
pub(crate) struct Window {
|
||||
window: MainThreadBound<Retained<WinitWindow>>,
|
||||
@@ -21,20 +16,32 @@ pub(crate) struct Window {
|
||||
delegate: MainThreadBound<Retained<WindowDelegate>>,
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
self.window.get_on_main(|window| autoreleasepool(|_| window.close()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new(
|
||||
window_target: &ActiveEventLoop,
|
||||
attributes: WindowAttributes,
|
||||
) -> Result<Self, RequestError> {
|
||||
) -> Result<Self, RootOsError> {
|
||||
let mtm = window_target.mtm;
|
||||
let delegate =
|
||||
autoreleasepool(|_| WindowDelegate::new(&window_target.app_state, attributes, mtm))?;
|
||||
let delegate = autoreleasepool(|_| {
|
||||
WindowDelegate::new(window_target.app_delegate(), attributes, mtm)
|
||||
})?;
|
||||
Ok(Window {
|
||||
window: MainThreadBound::new(delegate.window().retain(), mtm),
|
||||
delegate: MainThreadBound::new(delegate, mtm),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&WindowDelegate) + Send + 'static) {
|
||||
// For now, don't actually do queuing, since it may be less predictable
|
||||
self.maybe_wait_on_main(f)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(
|
||||
&self,
|
||||
f: impl FnOnce(&WindowDelegate) -> R + Send,
|
||||
@@ -63,278 +70,6 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
// Restore the video mode.
|
||||
if matches!(self.fullscreen(), Some(Fullscreen::Exclusive(_))) {
|
||||
self.set_fullscreen(None);
|
||||
}
|
||||
|
||||
self.window.get_on_main(|window| autoreleasepool(|_| window.close()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> crate::window::WindowId {
|
||||
self.maybe_wait_on_main(|delegate| crate::window::WindowId(delegate.id()))
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
self.maybe_wait_on_main(|delegate| delegate.scale_factor())
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_redraw());
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.pre_present_notify());
|
||||
}
|
||||
|
||||
fn reset_dead_keys(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_outer_position(position));
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> dpi::PhysicalSize<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_size())
|
||||
}
|
||||
|
||||
fn request_surface_size(&self, size: Size) -> Option<dpi::PhysicalSize<u32>> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_surface_size(size))
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> dpi::PhysicalSize<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.outer_size())
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size))
|
||||
}
|
||||
|
||||
fn set_max_surface_size(&self, max_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_max_surface_size(max_size));
|
||||
}
|
||||
|
||||
fn surface_resize_increments(&self) -> Option<dpi::PhysicalSize<u32>> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_resize_increments())
|
||||
}
|
||||
|
||||
fn set_surface_resize_increments(&self, increments: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_surface_resize_increments(increments));
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_title(title));
|
||||
}
|
||||
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_transparent(transparent));
|
||||
}
|
||||
|
||||
fn set_blur(&self, blur: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_blur(blur));
|
||||
}
|
||||
|
||||
fn set_visible(&self, visible: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_visible(visible));
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_visible())
|
||||
}
|
||||
|
||||
fn set_resizable(&self, resizable: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_resizable(resizable))
|
||||
}
|
||||
|
||||
fn is_resizable(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_resizable())
|
||||
}
|
||||
|
||||
fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_enabled_buttons(buttons))
|
||||
}
|
||||
|
||||
fn enabled_buttons(&self) -> WindowButtons {
|
||||
self.maybe_wait_on_main(|delegate| delegate.enabled_buttons())
|
||||
}
|
||||
|
||||
fn set_minimized(&self, minimized: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_minimized(minimized));
|
||||
}
|
||||
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_minimized())
|
||||
}
|
||||
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_maximized(maximized));
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_maximized())
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen.map(Into::into)))
|
||||
}
|
||||
|
||||
fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.fullscreen().map(Into::into))
|
||||
}
|
||||
|
||||
fn set_decorations(&self, decorations: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_decorations(decorations));
|
||||
}
|
||||
|
||||
fn is_decorated(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_decorated())
|
||||
}
|
||||
|
||||
fn set_window_level(&self, level: WindowLevel) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_window_level(level));
|
||||
}
|
||||
|
||||
fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_window_icon(window_icon));
|
||||
}
|
||||
|
||||
fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_cursor_area(position, size));
|
||||
}
|
||||
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_allowed(allowed));
|
||||
}
|
||||
|
||||
fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_purpose(purpose));
|
||||
}
|
||||
|
||||
fn focus_window(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.focus_window());
|
||||
}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.has_focus())
|
||||
}
|
||||
|
||||
fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_user_attention(request_type));
|
||||
}
|
||||
|
||||
fn set_theme(&self, theme: Option<Theme>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_theme(theme));
|
||||
}
|
||||
|
||||
fn theme(&self) -> Option<Theme> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.theme())
|
||||
}
|
||||
|
||||
fn set_content_protected(&self, protected: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_content_protected(protected));
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
self.maybe_wait_on_main(|delegate| delegate.title())
|
||||
}
|
||||
|
||||
fn set_cursor(&self, cursor: Cursor) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor(cursor));
|
||||
}
|
||||
|
||||
fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_position(position))
|
||||
}
|
||||
|
||||
fn set_cursor_grab(&self, mode: crate::window::CursorGrabMode) -> Result<(), RequestError> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_grab(mode))
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_visible(visible))
|
||||
}
|
||||
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.drag_window());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn drag_resize_window(
|
||||
&self,
|
||||
direction: crate::window::ResizeDirection,
|
||||
) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.drag_resize_window(direction))?)
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, position: Position) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.show_window_menu(position))
|
||||
}
|
||||
|
||||
fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_hittest(hittest));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
delegate.current_monitor().map(|inner| CoreMonitorHandle { inner })
|
||||
})
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
Box::new(
|
||||
delegate.available_monitors().into_iter().map(|inner| CoreMonitorHandle { inner }),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
delegate.primary_monitor().map(|inner| CoreMonitorHandle { inner })
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(pub usize);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::c_void;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use core_graphics::display::{CGDisplay, CGPoint};
|
||||
@@ -27,16 +26,16 @@ use objc2_foundation::{
|
||||
};
|
||||
use tracing::{trace, warn};
|
||||
|
||||
use super::app_state::AppState;
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::cursor::cursor_from_icon;
|
||||
use super::monitor::{self, flip_window_screen_coordinates, get_display_id};
|
||||
use super::observer::RunLoop;
|
||||
use super::view::WinitView;
|
||||
use super::window::WinitWindow;
|
||||
use super::{ffi, Fullscreen, MonitorHandle, WindowId};
|
||||
use super::{ffi, Fullscreen, MonitorHandle, OsError, WindowId};
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{SurfaceSizeWriter, WindowEvent};
|
||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event::{InnerSizeWriter, WindowEvent};
|
||||
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||
use crate::window::{
|
||||
Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
@@ -56,7 +55,6 @@ pub struct PlatformSpecificWindowAttributes {
|
||||
pub accepts_first_mouse: bool,
|
||||
pub tabbing_identifier: Option<String>,
|
||||
pub option_as_alt: OptionAsAlt,
|
||||
pub borderless_game: bool,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificWindowAttributes {
|
||||
@@ -74,7 +72,6 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
accepts_first_mouse: true,
|
||||
tabbing_identifier: None,
|
||||
option_as_alt: Default::default(),
|
||||
borderless_game: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +79,7 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct State {
|
||||
/// Strong reference to the global application state.
|
||||
app_state: Rc<AppState>,
|
||||
app_delegate: Retained<ApplicationDelegate>,
|
||||
|
||||
window: Retained<WinitWindow>,
|
||||
|
||||
@@ -95,7 +92,7 @@ pub(crate) struct State {
|
||||
previous_scale_factor: Cell<f64>,
|
||||
|
||||
/// The current resize increments for the window content.
|
||||
surface_resize_increments: Cell<NSSize>,
|
||||
resize_increments: Cell<NSSize>,
|
||||
/// Whether the window is showing decorations.
|
||||
decorations: Cell<bool>,
|
||||
resizable: Cell<bool>,
|
||||
@@ -123,7 +120,6 @@ pub(crate) struct State {
|
||||
standard_frame: Cell<Option<NSRect>>,
|
||||
is_simple_fullscreen: Cell<bool>,
|
||||
saved_style: Cell<Option<NSWindowStyleMask>>,
|
||||
is_borderless_game: Cell<bool>,
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
@@ -164,7 +160,7 @@ declare_class!(
|
||||
#[method(windowDidResize:)]
|
||||
fn window_did_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidResize:");
|
||||
// NOTE: WindowEvent::SurfaceResized is reported in frameDidChange.
|
||||
// NOTE: WindowEvent::Resized is reported in frameDidChange.
|
||||
self.emit_move_event();
|
||||
}
|
||||
|
||||
@@ -172,7 +168,7 @@ declare_class!(
|
||||
fn window_will_start_live_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillStartLiveResize:");
|
||||
|
||||
let increments = self.ivars().surface_resize_increments.get();
|
||||
let increments = self.ivars().resize_increments.get();
|
||||
self.set_resize_increments_inner(increments);
|
||||
}
|
||||
|
||||
@@ -486,7 +482,7 @@ impl Drop for WindowDelegate {
|
||||
}
|
||||
|
||||
fn new_window(
|
||||
app_state: &Rc<AppState>,
|
||||
app_delegate: &ApplicationDelegate,
|
||||
attrs: &WindowAttributes,
|
||||
mtm: MainThreadMarker,
|
||||
) -> Option<Retained<WinitWindow>> {
|
||||
@@ -505,7 +501,7 @@ fn new_window(
|
||||
let scale_factor = NSScreen::mainScreen(mtm)
|
||||
.map(|screen| screen.backingScaleFactor() as f64)
|
||||
.unwrap_or(1.0);
|
||||
let size = match attrs.surface_size {
|
||||
let size = match attrs.inner_size {
|
||||
Some(size) => {
|
||||
let size = size.to_logical(scale_factor);
|
||||
NSSize::new(size.width, size.height)
|
||||
@@ -561,11 +557,6 @@ fn new_window(
|
||||
masks |= NSWindowStyleMask::FullSizeContentView;
|
||||
}
|
||||
|
||||
// NOTE: This should only be created after the application has started launching,
|
||||
// (`applicationWillFinishLaunching:` at the earliest), otherwise you'll run into very
|
||||
// confusing issues with the window not being properly activated.
|
||||
//
|
||||
// Winit ensures this by not allowing access to `ActiveEventLoop` before handling events.
|
||||
let window: Option<Retained<WinitWindow>> = unsafe {
|
||||
msg_send_id![
|
||||
super(mtm.alloc().set_ivars(())),
|
||||
@@ -631,7 +622,7 @@ fn new_window(
|
||||
}
|
||||
|
||||
let view = WinitView::new(
|
||||
app_state,
|
||||
app_delegate,
|
||||
&window,
|
||||
attrs.platform_specific.accepts_first_mouse,
|
||||
attrs.platform_specific.option_as_alt,
|
||||
@@ -674,12 +665,12 @@ fn new_window(
|
||||
|
||||
impl WindowDelegate {
|
||||
pub(super) fn new(
|
||||
app_state: &Rc<AppState>,
|
||||
app_delegate: &ApplicationDelegate,
|
||||
attrs: WindowAttributes,
|
||||
mtm: MainThreadMarker,
|
||||
) -> Result<Retained<Self>, RequestError> {
|
||||
let window = new_window(app_state, &attrs, mtm)
|
||||
.ok_or_else(|| os_error!("couldn't create `NSWindow`"))?;
|
||||
) -> Result<Retained<Self>, RootOsError> {
|
||||
let window = new_window(app_delegate, &attrs, mtm)
|
||||
.ok_or_else(|| os_error!(OsError::CreationError("couldn't create `NSWindow`")))?;
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
match attrs.parent_window.map(|handle| handle.0) {
|
||||
@@ -688,9 +679,9 @@ impl WindowDelegate {
|
||||
// Unwrap is fine, since the pointer comes from `NonNull`.
|
||||
let parent_view: Retained<NSView> =
|
||||
unsafe { Retained::retain(handle.ns_view.as_ptr().cast()) }.unwrap();
|
||||
let parent = parent_view
|
||||
.window()
|
||||
.ok_or_else(|| os_error!("parent view should be installed in a window"))?;
|
||||
let parent = parent_view.window().ok_or_else(|| {
|
||||
os_error!(OsError::CreationError("parent view should be installed in a window"))
|
||||
})?;
|
||||
|
||||
// SAFETY: We know that there are no parent -> child -> parent cycles since the only
|
||||
// place in `winit` where we allow making a window a child window is
|
||||
@@ -703,15 +694,13 @@ impl WindowDelegate {
|
||||
None => (),
|
||||
}
|
||||
|
||||
let surface_resize_increments = match attrs
|
||||
.surface_resize_increments
|
||||
.map(|i| i.to_logical(window.backingScaleFactor() as _))
|
||||
{
|
||||
Some(LogicalSize { width, height }) if width >= 1. && height >= 1. => {
|
||||
NSSize::new(width, height)
|
||||
},
|
||||
_ => NSSize::new(1., 1.),
|
||||
};
|
||||
let resize_increments =
|
||||
match attrs.resize_increments.map(|i| i.to_logical(window.backingScaleFactor() as _)) {
|
||||
Some(LogicalSize { width, height }) if width >= 1. && height >= 1. => {
|
||||
NSSize::new(width, height)
|
||||
},
|
||||
_ => NSSize::new(1., 1.),
|
||||
};
|
||||
|
||||
let scale_factor = window.backingScaleFactor() as _;
|
||||
|
||||
@@ -720,11 +709,11 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
let delegate = mtm.alloc().set_ivars(State {
|
||||
app_state: Rc::clone(app_state),
|
||||
app_delegate: app_delegate.retain(),
|
||||
window: window.retain(),
|
||||
previous_position: Cell::new(None),
|
||||
previous_scale_factor: Cell::new(scale_factor),
|
||||
surface_resize_increments: Cell::new(surface_resize_increments),
|
||||
resize_increments: Cell::new(resize_increments),
|
||||
decorations: Cell::new(attrs.decorations),
|
||||
resizable: Cell::new(attrs.resizable),
|
||||
maximized: Cell::new(attrs.maximized),
|
||||
@@ -736,7 +725,6 @@ impl WindowDelegate {
|
||||
standard_frame: Cell::new(None),
|
||||
is_simple_fullscreen: Cell::new(false),
|
||||
saved_style: Cell::new(None),
|
||||
is_borderless_game: Cell::new(attrs.platform_specific.borderless_game),
|
||||
});
|
||||
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
|
||||
|
||||
@@ -765,11 +753,11 @@ impl WindowDelegate {
|
||||
delegate.set_blur(attrs.blur);
|
||||
}
|
||||
|
||||
if let Some(dim) = attrs.min_surface_size {
|
||||
delegate.set_min_surface_size(Some(dim));
|
||||
if let Some(dim) = attrs.min_inner_size {
|
||||
delegate.set_min_inner_size(Some(dim));
|
||||
}
|
||||
if let Some(dim) = attrs.max_surface_size {
|
||||
delegate.set_max_surface_size(Some(dim));
|
||||
if let Some(dim) = attrs.max_inner_size {
|
||||
delegate.set_max_inner_size(Some(dim));
|
||||
}
|
||||
|
||||
delegate.set_window_level(attrs.window_level);
|
||||
@@ -820,7 +808,7 @@ impl WindowDelegate {
|
||||
|
||||
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
||||
let window_id = RootWindowId(self.window().id());
|
||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
self.ivars().app_delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.window_event(event_loop, window_id, event);
|
||||
});
|
||||
}
|
||||
@@ -832,20 +820,20 @@ impl WindowDelegate {
|
||||
let content_size = LogicalSize::new(content_size.width, content_size.height);
|
||||
|
||||
let suggested_size = content_size.to_physical(scale_factor);
|
||||
let new_surface_size = Arc::new(Mutex::new(suggested_size));
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
self.queue_event(WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
});
|
||||
let physical_size = *new_surface_size.lock().unwrap();
|
||||
drop(new_surface_size);
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
|
||||
if physical_size != suggested_size {
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
}
|
||||
self.queue_event(WindowEvent::SurfaceResized(physical_size));
|
||||
self.queue_event(WindowEvent::Resized(physical_size));
|
||||
}
|
||||
|
||||
fn emit_move_event(&self) {
|
||||
@@ -919,21 +907,21 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
self.ivars().app_state.queue_redraw(self.window().id());
|
||||
self.ivars().app_delegate.queue_redraw(self.window().id());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
pub fn outer_position(&self) -> PhysicalPosition<i32> {
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
let position = flip_window_screen_coordinates(self.window().frame());
|
||||
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
|
||||
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
|
||||
}
|
||||
|
||||
pub fn inner_position(&self) -> PhysicalPosition<i32> {
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
|
||||
let position = flip_window_screen_coordinates(content_rect);
|
||||
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
|
||||
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
|
||||
}
|
||||
|
||||
pub fn set_outer_position(&self, position: Position) {
|
||||
@@ -946,7 +934,7 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
|
||||
let logical = LogicalSize::new(content_rect.size.width, content_rect.size.height);
|
||||
logical.to_physical(self.scale_factor())
|
||||
@@ -960,14 +948,14 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let size = size.to_logical(scale_factor);
|
||||
self.window().setContentSize(NSSize::new(size.width, size.height));
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_min_surface_size(&self, dimensions: Option<Size>) {
|
||||
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
||||
let dimensions =
|
||||
dimensions.unwrap_or(Size::Logical(LogicalSize { width: 0.0, height: 0.0 }));
|
||||
let min_size = dimensions.to_logical::<CGFloat>(self.scale_factor());
|
||||
@@ -986,7 +974,7 @@ impl WindowDelegate {
|
||||
self.window().setContentSize(current_size);
|
||||
}
|
||||
|
||||
pub fn set_max_surface_size(&self, dimensions: Option<Size>) {
|
||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||
let dimensions = dimensions.unwrap_or(Size::Logical(LogicalSize {
|
||||
width: f32::MAX as f64,
|
||||
height: f32::MAX as f64,
|
||||
@@ -1008,8 +996,8 @@ impl WindowDelegate {
|
||||
self.window().setContentSize(current_size);
|
||||
}
|
||||
|
||||
pub fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
let increments = self.ivars().surface_resize_increments.get();
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
let increments = self.ivars().resize_increments.get();
|
||||
let (w, h) = (increments.width, increments.height);
|
||||
if w > 1.0 || h > 1.0 {
|
||||
Some(LogicalSize::new(w, h).to_physical(self.scale_factor()))
|
||||
@@ -1018,9 +1006,9 @@ impl WindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_surface_resize_increments(&self, increments: Option<Size>) {
|
||||
pub fn set_resize_increments(&self, increments: Option<Size>) {
|
||||
// XXX the resize increments are only used during live resizes.
|
||||
self.ivars().surface_resize_increments.set(
|
||||
self.ivars().resize_increments.set(
|
||||
increments
|
||||
.map(|increments| {
|
||||
let logical = increments.to_logical::<f64>(self.scale_factor());
|
||||
@@ -1125,18 +1113,18 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
let associate_mouse_cursor = match mode {
|
||||
CursorGrabMode::Locked => false,
|
||||
CursorGrabMode::None => true,
|
||||
CursorGrabMode::Confined => {
|
||||
return Err(NotSupportedError::new("confined cursor is not supported").into())
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
|
||||
CGDisplay::associate_mouse_and_mouse_cursor_position(associate_mouse_cursor)
|
||||
.map_err(|status| os_error!(format!("CGError {status}")).into())
|
||||
.map_err(|status| ExternalError::Os(os_error!(OsError::CGError(status))))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1154,8 +1142,8 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), RequestError> {
|
||||
let physical_window_position = self.inner_position();
|
||||
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), ExternalError> {
|
||||
let physical_window_position = self.inner_position().unwrap();
|
||||
let scale_factor = self.scale_factor();
|
||||
let window_position = physical_window_position.to_logical::<CGFloat>(scale_factor);
|
||||
let logical_cursor_position = cursor_position.to_logical::<CGFloat>(scale_factor);
|
||||
@@ -1164,31 +1152,33 @@ impl WindowDelegate {
|
||||
y: logical_cursor_position.y + window_position.y,
|
||||
};
|
||||
CGDisplay::warp_mouse_cursor_position(point)
|
||||
.map_err(|status| os_error!(format!("CGError {status}")))?;
|
||||
.map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?;
|
||||
CGDisplay::associate_mouse_and_mouse_cursor_position(true)
|
||||
.map_err(|status| os_error!(format!("CGError {status}")))?;
|
||||
.map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) {
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let event = NSApplication::sharedApplication(mtm).currentEvent().unwrap();
|
||||
self.window().performWindowDragWithEvent(&event);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("drag_resize_window is not supported"))
|
||||
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) {
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
self.window().setIgnoresMouseEvents(!hittest);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn is_zoomed(&self) -> bool {
|
||||
@@ -1421,7 +1411,7 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
match (old_fullscreen, fullscreen) {
|
||||
(None, Some(fullscreen)) => {
|
||||
(None, Some(_)) => {
|
||||
// `toggleFullScreen` doesn't work if the `StyleMask` is none, so we
|
||||
// set a normal style temporarily. The previous state will be
|
||||
// restored in `WindowDelegate::window_did_exit_fullscreen`.
|
||||
@@ -1431,17 +1421,6 @@ impl WindowDelegate {
|
||||
self.set_style_mask(required);
|
||||
self.ivars().saved_style.set(Some(curr_mask));
|
||||
}
|
||||
|
||||
// In borderless games, we want to disable the dock and menu bar
|
||||
// by setting the presentation options. We do this here rather than in
|
||||
// `window:willUseFullScreenPresentationOptions` because for some reason
|
||||
// the menu bar remains interactable despite being hidden.
|
||||
if self.is_borderless_game() && matches!(fullscreen, Fullscreen::Borderless(_)) {
|
||||
let presentation_options = NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||
app.setPresentationOptions(presentation_options);
|
||||
}
|
||||
|
||||
toggle_fullscreen(self.window());
|
||||
},
|
||||
(Some(Fullscreen::Borderless(_)), None) => {
|
||||
@@ -1828,14 +1807,6 @@ impl WindowExtMacOS for WindowDelegate {
|
||||
fn option_as_alt(&self) -> OptionAsAlt {
|
||||
self.view().option_as_alt()
|
||||
}
|
||||
|
||||
fn set_borderless_game(&self, borderless_game: bool) {
|
||||
self.ivars().is_borderless_game.set(borderless_game);
|
||||
}
|
||||
|
||||
fn is_borderless_game(&self) -> bool {
|
||||
self.ivars().is_borderless_game.get()
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_STANDARD_FRAME: NSRect =
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod appkit;
|
||||
mod event_handler;
|
||||
mod notification_center;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
mod uikit;
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use block2::RcBlock;
|
||||
use objc2::rc::Retained;
|
||||
use objc2_foundation::{NSNotification, NSNotificationCenter, NSNotificationName, NSObject};
|
||||
|
||||
/// Observe the given notification.
|
||||
///
|
||||
/// This is used in Winit as an alternative to declaring an application delegate, as we want to
|
||||
/// give the user full control over those.
|
||||
pub fn create_observer(
|
||||
center: &NSNotificationCenter,
|
||||
name: &NSNotificationName,
|
||||
handler: impl Fn(&NSNotification) + 'static,
|
||||
) -> Retained<NSObject> {
|
||||
let block = RcBlock::new(move |notification: NonNull<NSNotification>| {
|
||||
handler(unsafe { notification.as_ref() });
|
||||
});
|
||||
unsafe {
|
||||
center.addObserverForName_object_queue_usingBlock(
|
||||
Some(name),
|
||||
None, // No sender filter
|
||||
None, // No queue, run on posting thread (i.e. main thread)
|
||||
&block,
|
||||
)
|
||||
}
|
||||
}
|
||||
60
src/platform_impl/apple/uikit/app_delegate.rs
Normal file
60
src/platform_impl/apple/uikit/app_delegate.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||
use objc2_ui_kit::UIApplication;
|
||||
|
||||
use super::app_state::{self, send_occluded_event_for_all_windows, EventWrapper};
|
||||
use crate::event::Event;
|
||||
|
||||
declare_class!(
|
||||
pub struct AppDelegate;
|
||||
|
||||
unsafe impl ClassType for AppDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
const NAME: &'static str = "WinitApplicationDelegate";
|
||||
}
|
||||
|
||||
impl DeclaredClass for AppDelegate {}
|
||||
|
||||
// UIApplicationDelegate protocol
|
||||
unsafe impl AppDelegate {
|
||||
#[method(application:didFinishLaunchingWithOptions:)]
|
||||
fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
|
||||
app_state::did_finish_launching(MainThreadMarker::new().unwrap());
|
||||
true
|
||||
}
|
||||
|
||||
#[method(applicationDidBecomeActive:)]
|
||||
fn did_become_active(&self, _application: &UIApplication) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed))
|
||||
}
|
||||
|
||||
#[method(applicationWillResignActive:)]
|
||||
fn will_resign_active(&self, _application: &UIApplication) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended))
|
||||
}
|
||||
|
||||
#[method(applicationWillEnterForeground:)]
|
||||
fn will_enter_foreground(&self, application: &UIApplication) {
|
||||
send_occluded_event_for_all_windows(application, false);
|
||||
}
|
||||
|
||||
#[method(applicationDidEnterBackground:)]
|
||||
fn did_enter_background(&self, application: &UIApplication) {
|
||||
send_occluded_event_for_all_windows(application, true);
|
||||
}
|
||||
|
||||
#[method(applicationWillTerminate:)]
|
||||
fn will_terminate(&self, application: &UIApplication) {
|
||||
app_state::terminated(application);
|
||||
}
|
||||
|
||||
#[method(applicationDidReceiveMemoryWarning:)]
|
||||
fn did_receive_memory_warning(&self, _application: &UIApplication) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning))
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,12 +1,12 @@
|
||||
#![deny(unused_results)]
|
||||
|
||||
use std::cell::{OnceCell, RefCell, RefMut};
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
use std::time::Instant;
|
||||
use std::{mem, ptr};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use core_foundation::base::CFRelease;
|
||||
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
||||
@@ -15,20 +15,19 @@ use core_foundation::runloop::{
|
||||
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
||||
};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::sel;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{msg_send, sel};
|
||||
use objc2_foundation::{
|
||||
CGRect, CGSize, MainThreadMarker, NSInteger, NSObjectProtocol, NSOperatingSystemVersion,
|
||||
NSProcessInfo,
|
||||
};
|
||||
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
|
||||
|
||||
use super::super::event_handler::EventHandler;
|
||||
use super::window::WinitUIWindow;
|
||||
use super::ActiveEventLoop;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::event_loop::ControlFlow;
|
||||
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
macro_rules! bug {
|
||||
@@ -43,45 +42,25 @@ macro_rules! bug_assert {
|
||||
};
|
||||
}
|
||||
|
||||
/// Get the global event handler for the application.
|
||||
///
|
||||
/// This is stored separately from AppState, since AppState needs to be accessible while the handler
|
||||
/// is executing.
|
||||
fn get_handler(mtm: MainThreadMarker) -> &'static EventHandler {
|
||||
// TODO(madsmtm): Use `MainThreadBound` once that is possible in `static`s.
|
||||
struct StaticMainThreadBound<T>(T);
|
||||
|
||||
impl<T> StaticMainThreadBound<T> {
|
||||
const fn get(&self, _mtm: MainThreadMarker) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for StaticMainThreadBound<T> {}
|
||||
unsafe impl<T> Sync for StaticMainThreadBound<T> {}
|
||||
|
||||
// SAFETY: Creating `StaticMainThreadBound` in a `const` context, where there is no concept
|
||||
// of the main thread.
|
||||
static GLOBAL: StaticMainThreadBound<OnceCell<EventHandler>> =
|
||||
StaticMainThreadBound(OnceCell::new());
|
||||
|
||||
GLOBAL.get(mtm).get_or_init(EventHandler::new)
|
||||
pub(crate) struct EventLoopHandler {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) handler: Box<dyn FnMut(Event, &dyn RootActiveEventLoop)>,
|
||||
pub(crate) event_loop: ActiveEventLoop,
|
||||
}
|
||||
|
||||
fn handle_event(mtm: MainThreadMarker, event: Event) {
|
||||
let event_loop = &ActiveEventLoop { mtm };
|
||||
get_handler(mtm).handle(|app| match event {
|
||||
Event::NewEvents(cause) => app.new_events(event_loop, cause),
|
||||
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
|
||||
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
|
||||
Event::UserWakeUp => app.proxy_wake_up(event_loop),
|
||||
Event::Suspended => app.suspended(event_loop),
|
||||
Event::Resumed => app.resumed(event_loop),
|
||||
Event::CreateSurfaces => app.can_create_surfaces(event_loop),
|
||||
Event::AboutToWait => app.about_to_wait(event_loop),
|
||||
Event::LoopExiting => app.exiting(event_loop),
|
||||
Event::MemoryWarning => app.memory_warning(event_loop),
|
||||
})
|
||||
impl fmt::Debug for EventLoopHandler {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EventLoopHandler")
|
||||
.field("handler", &"...")
|
||||
.field("event_loop", &self.event_loop)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopHandler {
|
||||
fn handle_event(&mut self, event: Event) {
|
||||
(self.handler)(event, &self.event_loop)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -98,8 +77,14 @@ pub struct ScaleFactorChanged {
|
||||
}
|
||||
|
||||
enum UserCallbackTransitionResult<'a> {
|
||||
Success { active_control_flow: ControlFlow, processing_redraws: bool },
|
||||
ReentrancyPrevented { queued_events: &'a mut Vec<EventWrapper> },
|
||||
Success {
|
||||
handler: EventLoopHandler,
|
||||
active_control_flow: ControlFlow,
|
||||
processing_redraws: bool,
|
||||
},
|
||||
ReentrancyPrevented {
|
||||
queued_events: &'a mut Vec<EventWrapper>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Event {
|
||||
@@ -112,11 +97,19 @@ impl Event {
|
||||
#[derive(Debug)]
|
||||
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
|
||||
enum AppStateImpl {
|
||||
Initial {
|
||||
NotLaunched {
|
||||
queued_windows: Vec<Retained<WinitUIWindow>>,
|
||||
queued_events: Vec<EventWrapper>,
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
},
|
||||
Launching {
|
||||
queued_windows: Vec<Retained<WinitUIWindow>>,
|
||||
queued_events: Vec<EventWrapper>,
|
||||
queued_handler: EventLoopHandler,
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
},
|
||||
ProcessingEvents {
|
||||
handler: EventLoopHandler,
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
active_control_flow: ControlFlow,
|
||||
},
|
||||
@@ -126,12 +119,16 @@ enum AppStateImpl {
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
},
|
||||
ProcessingRedraws {
|
||||
handler: EventLoopHandler,
|
||||
active_control_flow: ControlFlow,
|
||||
},
|
||||
Waiting {
|
||||
waiting_handler: EventLoopHandler,
|
||||
start: Instant,
|
||||
},
|
||||
PollFinished,
|
||||
PollFinished {
|
||||
waiting_handler: EventLoopHandler,
|
||||
},
|
||||
Terminated,
|
||||
}
|
||||
|
||||
@@ -157,7 +154,8 @@ impl AppState {
|
||||
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
|
||||
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() });
|
||||
**guard = Some(AppState {
|
||||
app_state: Some(AppStateImpl::Initial {
|
||||
app_state: Some(AppStateImpl::NotLaunched {
|
||||
queued_windows: Vec::new(),
|
||||
queued_events: Vec::new(),
|
||||
queued_gpu_redraws: HashSet::new(),
|
||||
}),
|
||||
@@ -209,25 +207,46 @@ impl AppState {
|
||||
}
|
||||
|
||||
fn has_launched(&self) -> bool {
|
||||
!matches!(self.state(), AppStateImpl::Initial { .. })
|
||||
!matches!(self.state(), AppStateImpl::NotLaunched { .. } | AppStateImpl::Launching { .. })
|
||||
}
|
||||
|
||||
fn has_terminated(&self) -> bool {
|
||||
matches!(self.state(), AppStateImpl::Terminated)
|
||||
}
|
||||
|
||||
fn did_finish_launching_transition(&mut self) -> Vec<EventWrapper> {
|
||||
let (events, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::Initial { queued_events, queued_gpu_redraws } => {
|
||||
(queued_events, queued_gpu_redraws)
|
||||
fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) {
|
||||
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::NotLaunched { queued_windows, queued_events, queued_gpu_redraws } => {
|
||||
(queued_windows, queued_events, queued_gpu_redraws)
|
||||
},
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
self.set_state(AppStateImpl::Launching {
|
||||
queued_windows,
|
||||
queued_events,
|
||||
queued_handler,
|
||||
queued_gpu_redraws,
|
||||
});
|
||||
}
|
||||
|
||||
fn did_finish_launching_transition(
|
||||
&mut self,
|
||||
) -> (Vec<Retained<WinitUIWindow>>, Vec<EventWrapper>) {
|
||||
let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::Launching {
|
||||
queued_windows,
|
||||
queued_events,
|
||||
queued_handler,
|
||||
queued_gpu_redraws,
|
||||
} => (queued_windows, queued_events, queued_handler, queued_gpu_redraws),
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
self.set_state(AppStateImpl::ProcessingEvents {
|
||||
handler,
|
||||
active_control_flow: self.control_flow,
|
||||
queued_gpu_redraws,
|
||||
});
|
||||
events
|
||||
(windows, events)
|
||||
}
|
||||
|
||||
fn wakeup_transition(&mut self) -> Option<EventWrapper> {
|
||||
@@ -237,18 +256,22 @@ impl AppState {
|
||||
return None;
|
||||
}
|
||||
|
||||
let event = match (self.control_flow, self.take_state()) {
|
||||
(ControlFlow::Poll, AppStateImpl::PollFinished) => {
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll))
|
||||
let (handler, event) = match (self.control_flow, self.take_state()) {
|
||||
(ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => {
|
||||
(waiting_handler, EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)))
|
||||
},
|
||||
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
|
||||
(ControlFlow::Wait, AppStateImpl::Waiting { waiting_handler, start }) => (
|
||||
waiting_handler,
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: None,
|
||||
}))
|
||||
},
|
||||
(ControlFlow::WaitUntil(requested_resume), AppStateImpl::Waiting { start }) => {
|
||||
if Instant::now() >= requested_resume {
|
||||
})),
|
||||
),
|
||||
(
|
||||
ControlFlow::WaitUntil(requested_resume),
|
||||
AppStateImpl::Waiting { waiting_handler, start },
|
||||
) => {
|
||||
let event = if Instant::now() >= requested_resume {
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached {
|
||||
start,
|
||||
requested_resume,
|
||||
@@ -258,12 +281,14 @@ impl AppState {
|
||||
start,
|
||||
requested_resume: Some(requested_resume),
|
||||
}))
|
||||
}
|
||||
};
|
||||
(waiting_handler, event)
|
||||
},
|
||||
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
|
||||
};
|
||||
|
||||
self.set_state(AppStateImpl::ProcessingEvents {
|
||||
handler,
|
||||
queued_gpu_redraws: Default::default(),
|
||||
active_control_flow: self.control_flow,
|
||||
});
|
||||
@@ -274,7 +299,8 @@ impl AppState {
|
||||
// If we're not able to process an event due to recursion or `Init` not having been sent out
|
||||
// yet, then queue the events up.
|
||||
match self.state_mut() {
|
||||
&mut AppStateImpl::Initial { ref mut queued_events, .. }
|
||||
&mut AppStateImpl::Launching { ref mut queued_events, .. }
|
||||
| &mut AppStateImpl::NotLaunched { ref mut queued_events, .. }
|
||||
| &mut AppStateImpl::InUserCallback { ref mut queued_events, .. } => {
|
||||
// A lifetime cast: early returns are not currently handled well with NLL, but
|
||||
// polonius handles them well. This transmute is a safe workaround.
|
||||
@@ -298,14 +324,17 @@ impl AppState {
|
||||
},
|
||||
}
|
||||
|
||||
let (queued_gpu_redraws, active_control_flow, processing_redraws) = match self.take_state()
|
||||
let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) = match self
|
||||
.take_state()
|
||||
{
|
||||
AppStateImpl::Initial { .. } | AppStateImpl::InUserCallback { .. } => unreachable!(),
|
||||
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
|
||||
(queued_gpu_redraws, active_control_flow, false)
|
||||
AppStateImpl::Launching { .. }
|
||||
| AppStateImpl::NotLaunched { .. }
|
||||
| AppStateImpl::InUserCallback { .. } => unreachable!(),
|
||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => {
|
||||
(handler, queued_gpu_redraws, active_control_flow, false)
|
||||
},
|
||||
AppStateImpl::ProcessingRedraws { active_control_flow } => {
|
||||
(Default::default(), active_control_flow, true)
|
||||
AppStateImpl::ProcessingRedraws { handler, active_control_flow } => {
|
||||
(handler, Default::default(), active_control_flow, true)
|
||||
},
|
||||
AppStateImpl::PollFinished { .. }
|
||||
| AppStateImpl::Waiting { .. }
|
||||
@@ -315,17 +344,17 @@ impl AppState {
|
||||
queued_events: Vec::new(),
|
||||
queued_gpu_redraws,
|
||||
});
|
||||
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws }
|
||||
UserCallbackTransitionResult::Success { handler, active_control_flow, processing_redraws }
|
||||
}
|
||||
|
||||
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
|
||||
let (queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
||||
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
|
||||
(queued_gpu_redraws, active_control_flow)
|
||||
let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => {
|
||||
(handler, queued_gpu_redraws, active_control_flow)
|
||||
},
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
self.set_state(AppStateImpl::ProcessingRedraws { active_control_flow });
|
||||
self.set_state(AppStateImpl::ProcessingRedraws { handler, active_control_flow });
|
||||
queued_gpu_redraws
|
||||
}
|
||||
|
||||
@@ -333,8 +362,10 @@ impl AppState {
|
||||
if !self.has_launched() || self.has_terminated() {
|
||||
return;
|
||||
}
|
||||
let old = match self.take_state() {
|
||||
AppStateImpl::ProcessingRedraws { active_control_flow } => active_control_flow,
|
||||
let (waiting_handler, old) = match self.take_state() {
|
||||
AppStateImpl::ProcessingRedraws { handler, active_control_flow } => {
|
||||
(handler, active_control_flow)
|
||||
},
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
|
||||
@@ -342,35 +373,35 @@ impl AppState {
|
||||
match (old, new) {
|
||||
(ControlFlow::Wait, ControlFlow::Wait) => {
|
||||
let start = Instant::now();
|
||||
self.set_state(AppStateImpl::Waiting { start });
|
||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
||||
},
|
||||
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
|
||||
if old_instant == new_instant =>
|
||||
{
|
||||
let start = Instant::now();
|
||||
self.set_state(AppStateImpl::Waiting { start });
|
||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
||||
},
|
||||
(_, ControlFlow::Wait) => {
|
||||
let start = Instant::now();
|
||||
self.set_state(AppStateImpl::Waiting { start });
|
||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
||||
self.waker.stop()
|
||||
},
|
||||
(_, ControlFlow::WaitUntil(new_instant)) => {
|
||||
let start = Instant::now();
|
||||
self.set_state(AppStateImpl::Waiting { start });
|
||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
||||
self.waker.start_at(new_instant)
|
||||
},
|
||||
// Unlike on macOS, handle Poll to Poll transition here to call the waker
|
||||
(_, ControlFlow::Poll) => {
|
||||
self.set_state(AppStateImpl::PollFinished);
|
||||
self.set_state(AppStateImpl::PollFinished { waiting_handler });
|
||||
self.waker.start()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn terminated_transition(&mut self) {
|
||||
fn terminated_transition(&mut self) -> EventLoopHandler {
|
||||
match self.replace_state(AppStateImpl::Terminated) {
|
||||
AppStateImpl::ProcessingEvents { .. } => {},
|
||||
AppStateImpl::ProcessingEvents { handler, .. } => handler,
|
||||
s => bug!("`LoopExiting` happened while not processing events {:?}", s),
|
||||
}
|
||||
}
|
||||
@@ -388,10 +419,31 @@ impl AppState {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWindow>) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } => {
|
||||
return queued_windows.push(window.clone())
|
||||
},
|
||||
&mut AppStateImpl::ProcessingEvents { .. }
|
||||
| &mut AppStateImpl::InUserCallback { .. }
|
||||
| &mut AppStateImpl::ProcessingRedraws { .. } => {},
|
||||
s @ &mut AppStateImpl::Launching { .. }
|
||||
| s @ &mut AppStateImpl::Waiting { .. }
|
||||
| s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s),
|
||||
&mut AppStateImpl::Terminated => {
|
||||
panic!("Attempt to create a `Window` after the app has terminated")
|
||||
},
|
||||
}
|
||||
drop(this);
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. }
|
||||
&mut AppStateImpl::NotLaunched { ref mut queued_gpu_redraws, .. }
|
||||
| &mut AppStateImpl::Launching { ref mut queued_gpu_redraws, .. }
|
||||
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. }
|
||||
| &mut AppStateImpl::InUserCallback { ref mut queued_gpu_redraws, .. } => {
|
||||
let _ = queued_gpu_redraws.insert(window);
|
||||
@@ -405,19 +457,45 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<W
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn launch(mtm: MainThreadMarker, app: &mut dyn ApplicationHandler, run: impl FnOnce()) {
|
||||
get_handler(mtm).set(app, run)
|
||||
pub(crate) fn will_launch(mtm: MainThreadMarker, queued_handler: EventLoopHandler) {
|
||||
AppState::get_mut(mtm).will_launch_transition(queued_handler)
|
||||
}
|
||||
|
||||
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let windows = match this.state_mut() {
|
||||
AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows),
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
|
||||
this.waker.start();
|
||||
|
||||
// have to drop RefMut because the window setup code below can trigger new events
|
||||
drop(this);
|
||||
|
||||
let events = AppState::get_mut(mtm).did_finish_launching_transition();
|
||||
for window in windows {
|
||||
// Do a little screen dance here to account for windows being created before
|
||||
// `UIApplicationMain` is called. This fixes visual issues such as being
|
||||
// offcenter and sized incorrectly. Additionally, to fix orientation issues, we
|
||||
// gotta reset the `rootViewController`.
|
||||
//
|
||||
// relevant iOS log:
|
||||
// ```
|
||||
// [ApplicationLifecycle] Windows were created before application initialization
|
||||
// completed. This may result in incorrect visual appearance.
|
||||
// ```
|
||||
let screen = window.screen();
|
||||
let _: () = unsafe { msg_send![&window, setScreen: ptr::null::<AnyObject>()] };
|
||||
window.setScreen(&screen);
|
||||
|
||||
let controller = window.rootViewController();
|
||||
window.setRootViewController(None);
|
||||
window.setRootViewController(controller.as_deref());
|
||||
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition();
|
||||
|
||||
let events = [
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init)),
|
||||
@@ -426,6 +504,12 @@ pub fn did_finish_launching(mtm: MainThreadMarker) {
|
||||
.into_iter()
|
||||
.chain(events);
|
||||
handle_nonuser_events(mtm, events);
|
||||
|
||||
// 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
|
||||
for window in windows {
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
}
|
||||
|
||||
// AppState::did_finish_launching handles the special transition `Init`
|
||||
@@ -453,15 +537,18 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
return;
|
||||
}
|
||||
|
||||
let (active_control_flow, processing_redraws) = match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
||||
queued_events.extend(events);
|
||||
return;
|
||||
},
|
||||
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws } => {
|
||||
(active_control_flow, processing_redraws)
|
||||
},
|
||||
};
|
||||
let (mut handler, active_control_flow, processing_redraws) =
|
||||
match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
||||
queued_events.extend(events);
|
||||
return;
|
||||
},
|
||||
UserCallbackTransitionResult::Success {
|
||||
handler,
|
||||
active_control_flow,
|
||||
processing_redraws,
|
||||
} => (handler, active_control_flow, processing_redraws),
|
||||
};
|
||||
drop(this);
|
||||
|
||||
for wrapper in events {
|
||||
@@ -475,9 +562,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
event
|
||||
);
|
||||
}
|
||||
handle_event(mtm, event)
|
||||
handler.handle_event(event)
|
||||
},
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,9 +588,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
queued_gpu_redraws.is_empty(),
|
||||
"redraw queued while processing redraws"
|
||||
);
|
||||
AppStateImpl::ProcessingRedraws { active_control_flow }
|
||||
AppStateImpl::ProcessingRedraws { handler, active_control_flow }
|
||||
} else {
|
||||
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow }
|
||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow }
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -521,9 +608,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
event
|
||||
);
|
||||
}
|
||||
handle_event(mtm, event)
|
||||
handler.handle_event(event)
|
||||
},
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,23 +618,23 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
|
||||
fn handle_user_events(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let (active_control_flow, processing_redraws) = match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
|
||||
bug!("unexpected attempted to process an event")
|
||||
},
|
||||
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws } => {
|
||||
(active_control_flow, processing_redraws)
|
||||
},
|
||||
};
|
||||
let (mut handler, active_control_flow, processing_redraws) =
|
||||
match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
|
||||
bug!("unexpected attempted to process an event")
|
||||
},
|
||||
UserCallbackTransitionResult::Success {
|
||||
handler,
|
||||
active_control_flow,
|
||||
processing_redraws,
|
||||
} => (handler, active_control_flow, processing_redraws),
|
||||
};
|
||||
if processing_redraws {
|
||||
bug!("user events attempted to be sent out while `ProcessingRedraws`");
|
||||
}
|
||||
let proxy_wake_up = this.proxy_wake_up.clone();
|
||||
drop(this);
|
||||
|
||||
if proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||
handle_event(mtm, Event::UserWakeUp);
|
||||
}
|
||||
handler.handle_event(Event::UserWakeUp);
|
||||
|
||||
loop {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
@@ -564,22 +651,23 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
this.app_state =
|
||||
Some(AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow });
|
||||
this.app_state = Some(AppStateImpl::ProcessingEvents {
|
||||
handler,
|
||||
queued_gpu_redraws,
|
||||
active_control_flow,
|
||||
});
|
||||
break;
|
||||
}
|
||||
drop(this);
|
||||
|
||||
for wrapper in queued_events {
|
||||
match wrapper {
|
||||
EventWrapper::StaticEvent(event) => handle_event(mtm, event),
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||
EventWrapper::StaticEvent(event) => handler.handle_event(event),
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
|
||||
}
|
||||
}
|
||||
|
||||
if proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||
handle_event(mtm, Event::UserWakeUp);
|
||||
}
|
||||
handler.handle_event(Event::UserWakeUp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,26 +749,26 @@ pub(crate) fn terminated(application: &UIApplication) {
|
||||
handle_nonuser_events(mtm, events);
|
||||
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
this.terminated_transition();
|
||||
let mut handler = this.terminated_transition();
|
||||
drop(this);
|
||||
|
||||
handle_event(mtm, Event::LoopExiting)
|
||||
handler.handle_event(Event::LoopExiting)
|
||||
}
|
||||
|
||||
fn handle_hidpi_proxy(mtm: MainThreadMarker, event: ScaleFactorChanged) {
|
||||
fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged) {
|
||||
let ScaleFactorChanged { suggested_size, scale_factor, window } = event;
|
||||
let new_surface_size = Arc::new(Mutex::new(suggested_size));
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
let event = Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
},
|
||||
};
|
||||
handle_event(mtm, event);
|
||||
handler.handle_event(event);
|
||||
let (view, screen_frame) = get_view_and_screen_frame(&window);
|
||||
let physical_size = *new_surface_size.lock().unwrap();
|
||||
drop(new_surface_size);
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = CGSize::new(logical_size.width, logical_size.height);
|
||||
let new_frame: CGRect = CGRect::new(screen_frame.origin, size);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::Any;
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
@@ -12,20 +13,14 @@ use core_foundation::runloop::{
|
||||
};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject};
|
||||
use objc2_ui_kit::{
|
||||
UIApplication, UIApplicationDidBecomeActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification, UIApplicationDidFinishLaunchingNotification,
|
||||
UIApplicationDidReceiveMemoryWarningNotification, UIApplicationMain,
|
||||
UIApplicationWillEnterForegroundNotification, UIApplicationWillResignActiveNotification,
|
||||
UIApplicationWillTerminateNotification, UIScreen,
|
||||
};
|
||||
use objc2_foundation::{MainThreadMarker, NSString};
|
||||
use objc2_ui_kit::{UIApplication, UIApplicationMain, UIScreen};
|
||||
|
||||
use super::super::notification_center::create_observer;
|
||||
use super::app_state::{send_occluded_event_for_all_windows, AppState, EventWrapper};
|
||||
use super::app_delegate::AppDelegate;
|
||||
use super::app_state::{AppState, EventLoopHandler};
|
||||
use super::{app_state, monitor, MonitorHandle};
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError};
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
@@ -33,7 +28,7 @@ use crate::event_loop::{
|
||||
};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform_impl::Window;
|
||||
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window as CoreWindow};
|
||||
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window as RootWindow};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ActiveEventLoop {
|
||||
@@ -49,15 +44,16 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: crate::window::WindowAttributes,
|
||||
) -> Result<Box<dyn CoreWindow>, RequestError> {
|
||||
Ok(Box::new(Window::new(self, window_attributes)?))
|
||||
) -> Result<RootWindow, OsError> {
|
||||
let window = Window::new(self, window_attributes)?;
|
||||
Ok(RootWindow { window })
|
||||
}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
_source: CustomCursorSource,
|
||||
) -> Result<CustomCursor, RequestError> {
|
||||
Err(NotSupportedError::new("create_custom_cursor is not supported").into())
|
||||
) -> Result<CustomCursor, ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
|
||||
@@ -98,6 +94,10 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
@@ -125,21 +125,35 @@ impl OwnedDisplayHandle {
|
||||
}
|
||||
}
|
||||
|
||||
fn map_user_event<'a, A: ApplicationHandler + 'a>(
|
||||
mut app: A,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
) -> impl FnMut(Event, &dyn RootActiveEventLoop) + 'a {
|
||||
move |event, window_target| match event {
|
||||
Event::NewEvents(cause) => app.new_events(window_target, cause),
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(window_target, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(window_target, device_id, event)
|
||||
},
|
||||
Event::UserWakeUp => {
|
||||
if proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
|
||||
app.proxy_wake_up(window_target);
|
||||
}
|
||||
},
|
||||
Event::Suspended => app.suspended(window_target),
|
||||
Event::Resumed => app.resumed(window_target),
|
||||
Event::CreateSurfaces => app.can_create_surfaces(window_target),
|
||||
Event::AboutToWait => app.about_to_wait(window_target),
|
||||
Event::LoopExiting => app.exiting(window_target),
|
||||
Event::MemoryWarning => app.memory_warning(window_target),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop {
|
||||
mtm: MainThreadMarker,
|
||||
window_target: ActiveEventLoop,
|
||||
|
||||
// Since iOS 9.0, we no longer need to remove the observers before they are deallocated; the
|
||||
// system instead cleans it up next time it would have posted a notification to it.
|
||||
//
|
||||
// Though we do still need to keep the observers around to prevent them from being deallocated.
|
||||
_did_finish_launching_observer: Retained<NSObject>,
|
||||
_did_become_active_observer: Retained<NSObject>,
|
||||
_will_resign_active_observer: Retained<NSObject>,
|
||||
_will_enter_foreground_observer: Retained<NSObject>,
|
||||
_did_enter_background_observer: Retained<NSObject>,
|
||||
_will_terminate_observer: Retained<NSObject>,
|
||||
_did_receive_memory_warning_observer: Retained<NSObject>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -164,99 +178,10 @@ impl EventLoop {
|
||||
// this line sets up the main run loop before `UIApplicationMain`
|
||||
setup_control_flow_observers();
|
||||
|
||||
let center = unsafe { NSNotificationCenter::defaultCenter() };
|
||||
|
||||
let _did_finish_launching_observer = create_observer(
|
||||
¢er,
|
||||
// `application:didFinishLaunchingWithOptions:`
|
||||
unsafe { UIApplicationDidFinishLaunchingNotification },
|
||||
move |_| {
|
||||
app_state::did_finish_launching(mtm);
|
||||
},
|
||||
);
|
||||
let _did_become_active_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationDidBecomeActive:`
|
||||
unsafe { UIApplicationDidBecomeActiveNotification },
|
||||
move |_| {
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed));
|
||||
},
|
||||
);
|
||||
let _will_resign_active_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationWillResignActive:`
|
||||
unsafe { UIApplicationWillResignActiveNotification },
|
||||
move |_| {
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended));
|
||||
},
|
||||
);
|
||||
let _will_enter_foreground_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationWillEnterForeground:`
|
||||
unsafe { UIApplicationWillEnterForegroundNotification },
|
||||
move |notification| {
|
||||
let app = unsafe { notification.object() }.expect(
|
||||
"UIApplicationWillEnterForegroundNotification to have application object",
|
||||
);
|
||||
// SAFETY: The `object` in `UIApplicationWillEnterForegroundNotification` is
|
||||
// documented to be `UIApplication`.
|
||||
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
|
||||
send_occluded_event_for_all_windows(&app, false);
|
||||
},
|
||||
);
|
||||
let _did_enter_background_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationDidEnterBackground:`
|
||||
unsafe { UIApplicationDidEnterBackgroundNotification },
|
||||
move |notification| {
|
||||
let app = unsafe { notification.object() }.expect(
|
||||
"UIApplicationDidEnterBackgroundNotification to have application object",
|
||||
);
|
||||
// SAFETY: The `object` in `UIApplicationDidEnterBackgroundNotification` is
|
||||
// documented to be `UIApplication`.
|
||||
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
|
||||
send_occluded_event_for_all_windows(&app, true);
|
||||
},
|
||||
);
|
||||
let _will_terminate_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationWillTerminate:`
|
||||
unsafe { UIApplicationWillTerminateNotification },
|
||||
move |notification| {
|
||||
let app = unsafe { notification.object() }
|
||||
.expect("UIApplicationWillTerminateNotification to have application object");
|
||||
// SAFETY: The `object` in `UIApplicationWillTerminateNotification` is
|
||||
// (somewhat) documented to be `UIApplication`.
|
||||
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
|
||||
app_state::terminated(&app);
|
||||
},
|
||||
);
|
||||
let _did_receive_memory_warning_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationDidReceiveMemoryWarning:`
|
||||
unsafe { UIApplicationDidReceiveMemoryWarningNotification },
|
||||
move |_| {
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::MemoryWarning),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Ok(EventLoop {
|
||||
mtm,
|
||||
window_target: ActiveEventLoop { mtm },
|
||||
_did_finish_launching_observer,
|
||||
_did_become_active_observer,
|
||||
_will_resign_active_observer,
|
||||
_will_enter_foreground_observer,
|
||||
_did_enter_background_observer,
|
||||
_will_terminate_observer,
|
||||
_did_receive_memory_warning_observer,
|
||||
})
|
||||
Ok(EventLoop { mtm, window_target: ActiveEventLoop { mtm } })
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(self, mut app: A) -> ! {
|
||||
pub fn run_app<A: ApplicationHandler>(self, app: A) -> ! {
|
||||
let application: Option<Retained<UIApplication>> =
|
||||
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
|
||||
assert!(
|
||||
@@ -266,23 +191,36 @@ impl EventLoop {
|
||||
`EventLoop::run_app` calls `UIApplicationMain` on iOS",
|
||||
);
|
||||
|
||||
let handler = map_user_event(app, AppState::get_mut(self.mtm).proxy_wake_up());
|
||||
|
||||
let handler = unsafe {
|
||||
std::mem::transmute::<
|
||||
Box<dyn FnMut(Event, &dyn RootActiveEventLoop)>,
|
||||
Box<dyn FnMut(Event, &dyn RootActiveEventLoop)>,
|
||||
>(Box::new(handler))
|
||||
};
|
||||
|
||||
let handler = EventLoopHandler { handler, event_loop: self.window_target };
|
||||
|
||||
app_state::will_launch(self.mtm, handler);
|
||||
|
||||
// Ensure application delegate is initialized
|
||||
let _ = AppDelegate::class();
|
||||
|
||||
extern "C" {
|
||||
// These functions are in crt_externs.h.
|
||||
fn _NSGetArgc() -> *mut c_int;
|
||||
fn _NSGetArgv() -> *mut *mut *mut c_char;
|
||||
}
|
||||
|
||||
app_state::launch(self.mtm, &mut app, || unsafe {
|
||||
unsafe {
|
||||
UIApplicationMain(
|
||||
*_NSGetArgc(),
|
||||
NonNull::new(*_NSGetArgv()).unwrap(),
|
||||
// We intentionally override neither the application nor the delegate, to allow
|
||||
// the user to do so themselves!
|
||||
None,
|
||||
None,
|
||||
);
|
||||
});
|
||||
|
||||
Some(&NSString::from_str(AppDelegate::NAME)),
|
||||
)
|
||||
};
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![allow(clippy::let_unit_value)]
|
||||
|
||||
mod app_delegate;
|
||||
mod app_state;
|
||||
mod event_loop;
|
||||
mod monitor;
|
||||
|
||||
@@ -4,23 +4,19 @@ use std::cell::{Cell, RefCell};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::{NSObjectProtocol, ProtocolObject};
|
||||
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet, NSString};
|
||||
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet};
|
||||
use objc2_ui_kit::{
|
||||
UICoordinateSpace, UIEvent, UIForceTouchCapability, UIGestureRecognizer,
|
||||
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer,
|
||||
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIPanGestureRecognizer,
|
||||
UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer,
|
||||
UITextInputTraits, UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||
UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||
};
|
||||
|
||||
use super::app_state::{self, EventWrapper};
|
||||
use super::window::WinitUIWindow;
|
||||
use super::{FingerId, DEVICE_ID};
|
||||
use crate::dpi::PhysicalPosition;
|
||||
use crate::event::{
|
||||
ElementState, Event, FingerId as RootFingerId, Force, KeyEvent, Touch, TouchPhase, WindowEvent,
|
||||
};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
|
||||
use crate::platform_impl::KeyEventExtra;
|
||||
use crate::event::{Event, FingerId as RootFingerId, Force, Touch, TouchPhase, WindowEvent};
|
||||
use crate::window::{WindowAttributes, WindowId as RootWindowId};
|
||||
|
||||
pub struct WinitViewState {
|
||||
@@ -93,7 +89,7 @@ declare_class!(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::SurfaceResized(size),
|
||||
event: WindowEvent::Resized(size),
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -144,7 +140,7 @@ declare_class!(
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)),
|
||||
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
@@ -318,11 +314,6 @@ declare_class!(
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, gesture_event);
|
||||
}
|
||||
|
||||
#[method(canBecomeFirstResponder)]
|
||||
fn can_become_first_responder(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NSObjectProtocol for WinitView {}
|
||||
@@ -333,26 +324,6 @@ declare_class!(
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl UITextInputTraits for WinitView {
|
||||
}
|
||||
|
||||
unsafe impl UIKeyInput for WinitView {
|
||||
#[method(hasText)]
|
||||
fn has_text(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[method(insertText:)]
|
||||
fn insert_text(&self, text: &NSString) {
|
||||
self.handle_insert_text(text)
|
||||
}
|
||||
|
||||
#[method(deleteBackward)]
|
||||
fn delete_backward(&self) {
|
||||
self.handle_delete_backward()
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl WinitView {
|
||||
@@ -541,69 +512,4 @@ impl WinitView {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(mtm, touch_events);
|
||||
}
|
||||
|
||||
fn handle_insert_text(&self, text: &NSString) {
|
||||
let window = self.window().unwrap();
|
||||
let window_id = RootWindowId(window.id());
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
// send individual events for each character
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
text.to_string().chars().flat_map(|c| {
|
||||
let text = smol_str::SmolStr::from_iter([c]);
|
||||
// Emit both press and release events
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
event: KeyEvent {
|
||||
text: if state == ElementState::Pressed {
|
||||
Some(text.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
state,
|
||||
location: KeyLocation::Standard,
|
||||
repeat: false,
|
||||
logical_key: Key::Character(text.clone()),
|
||||
physical_key: PhysicalKey::Unidentified(
|
||||
NativeKeyCode::Unidentified,
|
||||
),
|
||||
platform_specific: KeyEventExtra {},
|
||||
},
|
||||
is_synthetic: false,
|
||||
device_id: DEVICE_ID,
|
||||
},
|
||||
})
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn handle_delete_backward(&self) {
|
||||
let window = self.window().unwrap();
|
||||
let window_id = RootWindowId(window.id());
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event: KeyEvent {
|
||||
state,
|
||||
logical_key: Key::Named(NamedKey::Backspace),
|
||||
physical_key: PhysicalKey::Code(KeyCode::Backspace),
|
||||
platform_specific: KeyEventExtra {},
|
||||
repeat: false,
|
||||
location: KeyLocation::Standard,
|
||||
text: None,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,13 @@ use super::view_controller::WinitViewController;
|
||||
use super::{app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle};
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event::{Event, WindowEvent};
|
||||
use crate::icon::Icon;
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations};
|
||||
use crate::window::{
|
||||
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
|
||||
WindowAttributes, WindowButtons, WindowId as CoreWindowId, WindowLevel,
|
||||
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
|
||||
WindowButtons, WindowId as RootWindowId, WindowLevel,
|
||||
};
|
||||
|
||||
declare_class!(
|
||||
@@ -50,7 +49,7 @@ declare_class!(
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: CoreWindowId(self.id()),
|
||||
window_id: RootWindowId(self.id()),
|
||||
event: WindowEvent::Focused(true),
|
||||
}),
|
||||
);
|
||||
@@ -63,7 +62,7 @@ declare_class!(
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: CoreWindowId(self.id()),
|
||||
window_id: RootWindowId(self.id()),
|
||||
event: WindowEvent::Focused(false),
|
||||
}),
|
||||
);
|
||||
@@ -79,11 +78,6 @@ impl WinitUIWindow {
|
||||
frame: CGRect,
|
||||
view_controller: &UIViewController,
|
||||
) -> Retained<Self> {
|
||||
// NOTE: This should only be created after the application has started launching,
|
||||
// (`application:willFinishLaunchingWithOptions:` at the earliest), otherwise you'll run
|
||||
// into very confusing issues with the window not being properly activated.
|
||||
//
|
||||
// Winit ensures this by not allowing access to `ActiveEventLoop` before handling events.
|
||||
let this: Retained<Self> = unsafe { msg_send_id![mtm.alloc(), initWithFrame: frame] };
|
||||
|
||||
this.setRootViewController(Some(view_controller));
|
||||
@@ -159,20 +153,20 @@ impl Inner {
|
||||
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
pub fn inner_position(&self) -> PhysicalPosition<i32> {
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
let safe_area = self.safe_area_screen_space();
|
||||
let position =
|
||||
LogicalPosition { x: safe_area.origin.x as f64, y: safe_area.origin.y as f64 };
|
||||
let scale_factor = self.scale_factor();
|
||||
position.to_physical(scale_factor)
|
||||
Ok(position.to_physical(scale_factor))
|
||||
}
|
||||
|
||||
pub fn outer_position(&self) -> PhysicalPosition<i32> {
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
let screen_frame = self.screen_frame();
|
||||
let position =
|
||||
LogicalPosition { x: screen_frame.origin.x as f64, y: screen_frame.origin.y as f64 };
|
||||
let scale_factor = self.scale_factor();
|
||||
position.to_physical(scale_factor)
|
||||
Ok(position.to_physical(scale_factor))
|
||||
}
|
||||
|
||||
pub fn set_outer_position(&self, physical_position: Position) {
|
||||
@@ -187,7 +181,7 @@ impl Inner {
|
||||
self.window.setBounds(bounds);
|
||||
}
|
||||
|
||||
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let safe_area = self.safe_area_screen_space();
|
||||
let size = LogicalSize {
|
||||
@@ -207,25 +201,25 @@ impl Inner {
|
||||
size.to_physical(scale_factor)
|
||||
}
|
||||
|
||||
pub fn request_surface_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
|
||||
Some(self.surface_size())
|
||||
pub fn request_inner_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
|
||||
Some(self.inner_size())
|
||||
}
|
||||
|
||||
pub fn set_min_surface_size(&self, _dimensions: Option<Size>) {
|
||||
warn!("`Window::set_min_surface_size` is ignored on iOS")
|
||||
pub fn set_min_inner_size(&self, _dimensions: Option<Size>) {
|
||||
warn!("`Window::set_min_inner_size` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_max_surface_size(&self, _dimensions: Option<Size>) {
|
||||
warn!("`Window::set_max_surface_size` is ignored on iOS")
|
||||
pub fn set_max_inner_size(&self, _dimensions: Option<Size>) {
|
||||
warn!("`Window::set_max_inner_size` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_surface_resize_increments(&self, _increments: Option<Size>) {
|
||||
warn!("`Window::set_surface_resize_increments` is ignored on iOS")
|
||||
pub fn set_resize_increments(&self, _increments: Option<Size>) {
|
||||
warn!("`Window::set_resize_increments` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
@@ -256,31 +250,31 @@ impl Inner {
|
||||
debug!("`Window::set_cursor` ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, _position: Position) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("set_cursor_position is not supported"))
|
||||
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("set_cursor_grab is not supported"))
|
||||
pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
pub fn set_cursor_visible(&self, _visible: bool) {
|
||||
debug!("`Window::set_cursor_visible` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn drag_window(&self) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("drag_window is not supported"))
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("drag_resize_window is not supported"))
|
||||
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("set_cursor_hittest is not supported"))
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
pub fn set_minimized(&self, _minimized: bool) {
|
||||
@@ -371,24 +365,12 @@ impl Inner {
|
||||
warn!("`Window::set_ime_cursor_area` is ignored on iOS")
|
||||
}
|
||||
|
||||
/// Show / hide the keyboard. To show the keyboard, we call `becomeFirstResponder`,
|
||||
/// requesting focus for the [WinitView]. Since [WinitView] implements
|
||||
/// [objc2_ui_kit::UIKeyInput], the keyboard will be shown.
|
||||
/// <https://developer.apple.com/documentation/uikit/uiresponder/1621113-becomefirstresponder>
|
||||
pub fn set_ime_allowed(&self, allowed: bool) {
|
||||
if allowed {
|
||||
unsafe {
|
||||
self.view.becomeFirstResponder();
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
self.view.resignFirstResponder();
|
||||
}
|
||||
}
|
||||
pub fn set_ime_allowed(&self, _allowed: bool) {
|
||||
warn!("`Window::set_ime_allowed` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {
|
||||
warn!("`Window::set_ime_purpose` is ignored on iOS")
|
||||
warn!("`Window::set_ime_allowed` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn focus_window(&self) {
|
||||
@@ -466,14 +448,14 @@ impl Window {
|
||||
pub(crate) fn new(
|
||||
event_loop: &ActiveEventLoop,
|
||||
window_attributes: WindowAttributes,
|
||||
) -> Result<Window, RequestError> {
|
||||
) -> Result<Window, RootOsError> {
|
||||
let mtm = event_loop.mtm;
|
||||
|
||||
if window_attributes.min_surface_size.is_some() {
|
||||
warn!("`WindowAttributes::min_surface_size` is ignored on iOS");
|
||||
if window_attributes.min_inner_size.is_some() {
|
||||
warn!("`WindowAttributes::min_inner_size` is ignored on iOS");
|
||||
}
|
||||
if window_attributes.max_surface_size.is_some() {
|
||||
warn!("`WindowAttributes::max_surface_size` is ignored on iOS");
|
||||
if window_attributes.max_inner_size.is_some() {
|
||||
warn!("`WindowAttributes::max_inner_size` is ignored on iOS");
|
||||
}
|
||||
|
||||
// TODO: transparency, visible
|
||||
@@ -489,7 +471,7 @@ impl Window {
|
||||
|
||||
let screen_bounds = screen.bounds();
|
||||
|
||||
let frame = match window_attributes.surface_size {
|
||||
let frame = match window_attributes.inner_size {
|
||||
Some(dim) => {
|
||||
let scale_factor = screen.scale();
|
||||
let size = dim.to_logical::<f64>(scale_factor as f64);
|
||||
@@ -508,9 +490,10 @@ impl Window {
|
||||
|
||||
let view_controller = WinitViewController::new(mtm, &window_attributes, &view);
|
||||
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
|
||||
window.makeKeyAndVisible();
|
||||
|
||||
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `SurfaceResized`
|
||||
app_state::set_key_window(mtm, &window);
|
||||
|
||||
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
|
||||
// event on window creation if the DPI factor != 1.0
|
||||
let scale_factor = view.contentScaleFactor();
|
||||
let scale_factor = scale_factor as f64;
|
||||
@@ -523,7 +506,7 @@ impl Window {
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
};
|
||||
let window_id = CoreWindowId(window.id());
|
||||
let window_id = RootWindowId(window.id());
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
std::iter::once(EventWrapper::ScaleFactorChanged(app_state::ScaleFactorChanged {
|
||||
@@ -534,7 +517,7 @@ impl Window {
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)),
|
||||
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
@@ -544,6 +527,11 @@ impl Window {
|
||||
Ok(Window { inner: MainThreadBound::new(inner, mtm) })
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Inner) + Send + 'static) {
|
||||
// For now, don't actually do queuing, since it may be less predictable
|
||||
self.maybe_wait_on_main(f)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Inner) -> R + Send) -> R {
|
||||
self.inner.get_on_main(|inner| f(inner))
|
||||
}
|
||||
@@ -569,265 +557,6 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> crate::window::WindowId {
|
||||
self.maybe_wait_on_main(|delegate| crate::window::WindowId(delegate.id()))
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
self.maybe_wait_on_main(|delegate| delegate.scale_factor())
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_redraw());
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.pre_present_notify());
|
||||
}
|
||||
|
||||
fn reset_dead_keys(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_outer_position(position));
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_size())
|
||||
}
|
||||
|
||||
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_surface_size(size))
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.outer_size())
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size))
|
||||
}
|
||||
|
||||
fn set_max_surface_size(&self, max_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_max_surface_size(max_size));
|
||||
}
|
||||
|
||||
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_resize_increments())
|
||||
}
|
||||
|
||||
fn set_surface_resize_increments(&self, increments: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_surface_resize_increments(increments));
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_title(title));
|
||||
}
|
||||
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_transparent(transparent));
|
||||
}
|
||||
|
||||
fn set_blur(&self, blur: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_blur(blur));
|
||||
}
|
||||
|
||||
fn set_visible(&self, visible: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_visible(visible));
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_visible())
|
||||
}
|
||||
|
||||
fn set_resizable(&self, resizable: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_resizable(resizable))
|
||||
}
|
||||
|
||||
fn is_resizable(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_resizable())
|
||||
}
|
||||
|
||||
fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_enabled_buttons(buttons))
|
||||
}
|
||||
|
||||
fn enabled_buttons(&self) -> WindowButtons {
|
||||
self.maybe_wait_on_main(|delegate| delegate.enabled_buttons())
|
||||
}
|
||||
|
||||
fn set_minimized(&self, minimized: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_minimized(minimized));
|
||||
}
|
||||
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_minimized())
|
||||
}
|
||||
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_maximized(maximized));
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_maximized())
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, fullscreen: Option<crate::window::Fullscreen>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen.map(Into::into)))
|
||||
}
|
||||
|
||||
fn fullscreen(&self) -> Option<crate::window::Fullscreen> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.fullscreen().map(Into::into))
|
||||
}
|
||||
|
||||
fn set_decorations(&self, decorations: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_decorations(decorations));
|
||||
}
|
||||
|
||||
fn is_decorated(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_decorated())
|
||||
}
|
||||
|
||||
fn set_window_level(&self, level: WindowLevel) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_window_level(level));
|
||||
}
|
||||
|
||||
fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_window_icon(window_icon));
|
||||
}
|
||||
|
||||
fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_cursor_area(position, size));
|
||||
}
|
||||
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_allowed(allowed));
|
||||
}
|
||||
|
||||
fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_purpose(purpose));
|
||||
}
|
||||
|
||||
fn focus_window(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.focus_window());
|
||||
}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.has_focus())
|
||||
}
|
||||
|
||||
fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_user_attention(request_type));
|
||||
}
|
||||
|
||||
fn set_theme(&self, theme: Option<Theme>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_theme(theme));
|
||||
}
|
||||
|
||||
fn theme(&self) -> Option<Theme> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.theme())
|
||||
}
|
||||
|
||||
fn set_content_protected(&self, protected: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_content_protected(protected));
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
self.maybe_wait_on_main(|delegate| delegate.title())
|
||||
}
|
||||
|
||||
fn set_cursor(&self, cursor: Cursor) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor(cursor));
|
||||
}
|
||||
|
||||
fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.set_cursor_position(position))?)
|
||||
}
|
||||
|
||||
fn set_cursor_grab(&self, mode: crate::window::CursorGrabMode) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.set_cursor_grab(mode))?)
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_visible(visible))
|
||||
}
|
||||
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.drag_window())?)
|
||||
}
|
||||
|
||||
fn drag_resize_window(
|
||||
&self,
|
||||
direction: crate::window::ResizeDirection,
|
||||
) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.drag_resize_window(direction))?)
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, position: Position) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.show_window_menu(position))
|
||||
}
|
||||
|
||||
fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.set_cursor_hittest(hittest))?)
|
||||
}
|
||||
|
||||
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
delegate.current_monitor().map(|inner| CoreMonitorHandle { inner })
|
||||
})
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
Box::new(
|
||||
delegate.available_monitors().into_iter().map(|inner| CoreMonitorHandle { inner }),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
delegate.primary_monitor().map(|inner| CoreMonitorHandle { inner })
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// WindowExtIOS
|
||||
impl Inner {
|
||||
pub fn set_scale_factor(&self, scale_factor: f64) {
|
||||
|
||||
@@ -3,33 +3,38 @@
|
||||
#[cfg(all(not(x11_platform), not(wayland_platform)))]
|
||||
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
||||
|
||||
use std::env;
|
||||
use std::collections::VecDeque;
|
||||
use std::num::{NonZeroU16, NonZeroU32};
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{env, fmt};
|
||||
#[cfg(x11_platform)]
|
||||
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc, sync::Mutex};
|
||||
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex};
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
||||
pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
|
||||
#[cfg(x11_platform)]
|
||||
use self::x11::{XConnection, XError, XNotSupported};
|
||||
use self::x11::{X11Error, XConnection, XError, XNotSupported};
|
||||
use crate::application::ApplicationHandler;
|
||||
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::dpi::Size;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::error::{EventLoopError, NotSupportedError};
|
||||
use crate::event_loop::ActiveEventLoop;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{EventLoopError, ExternalError, NotSupportedError};
|
||||
use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial};
|
||||
use crate::icon::Icon;
|
||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||
use crate::keyboard::Key;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::utils::Lazy;
|
||||
use crate::window::ActivationToken;
|
||||
use crate::window::{
|
||||
ActivationToken, Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowButtons, WindowLevel,
|
||||
};
|
||||
|
||||
pub(crate) mod common;
|
||||
#[cfg(wayland_platform)]
|
||||
@@ -107,6 +112,34 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported>>> =
|
||||
Lazy::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)));
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OsError {
|
||||
Misc(&'static str),
|
||||
#[cfg(x11_platform)]
|
||||
XError(Arc<X11Error>),
|
||||
#[cfg(wayland_platform)]
|
||||
WaylandError(Arc<wayland::WaylandError>),
|
||||
}
|
||||
|
||||
impl fmt::Display for OsError {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
OsError::Misc(e) => _f.pad(e),
|
||||
#[cfg(x11_platform)]
|
||||
OsError::XError(ref e) => fmt::Display::fmt(e, _f),
|
||||
#[cfg(wayland_platform)]
|
||||
OsError::WaylandError(ref e) => fmt::Display::fmt(e, _f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum Window {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::Window),
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(wayland::Window),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(u64);
|
||||
|
||||
@@ -260,6 +293,316 @@ impl VideoModeHandle {
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) {
|
||||
f(self)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Self) -> R + Send) -> R {
|
||||
f(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
x11_or_wayland!(match self; Window(w) => w.id())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, title: &str) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_title(title));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_transparent(&self, transparent: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_transparent(transparent));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_blur(&self, blur: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_blur(blur));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_visible(visible))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_visible(&self) -> Option<bool> {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_visible())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.outer_position())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.inner_position())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_outer_position(&self, position: Position) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_outer_position(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
x11_or_wayland!(match self; Window(w) => w.inner_size())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
x11_or_wayland!(match self; Window(w) => w.outer_size())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_inner_size(size))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_activation_token())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_min_inner_size(dimensions))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_max_inner_size(dimensions))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
x11_or_wayland!(match self; Window(w) => w.resize_increments())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resize_increments(&self, increments: Option<Size>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_resize_increments(increments))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_resizable(resizable))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_resizable(&self) -> bool {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_resizable())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_enabled_buttons(buttons))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||
x11_or_wayland!(match self; Window(w) => w.enabled_buttons())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: Cursor) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_cursor(cursor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_cursor_grab(mode))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_cursor_visible(visible))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.drag_window())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.drag_resize_window(direction))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, position: Position) {
|
||||
x11_or_wayland!(match self; Window(w) => w.show_window_menu(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_cursor_hittest(hittest))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
x11_or_wayland!(match self; Window(w) => w.scale_factor())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_cursor_position(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_maximized(maximized))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_maximized())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_minimized(minimized))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_minimized(&self) -> Option<bool> {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_minimized())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
x11_or_wayland!(match self; Window(w) => w.fullscreen())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_fullscreen(monitor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_decorations(decorations))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_decorated(&self) -> bool {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_decorated())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_level(&self, level: WindowLevel) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_window_level(level))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_window_icon(window_icon.map(|icon| icon.inner)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_ime_cursor_area(position, size))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reset_dead_keys(&self) {
|
||||
common::xkb::reset_dead_keys()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_allowed(&self, allowed: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_ime_allowed(allowed))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_ime_purpose(purpose))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
x11_or_wayland!(match self; Window(w) => w.focus_window())
|
||||
}
|
||||
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_user_attention(request_type))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_redraw())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pre_present_notify(&self) {
|
||||
x11_or_wayland!(match self; Window(w) => w.pre_present_notify())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(x11_or_wayland!(match self; Window(w) => w.current_monitor()?; as MonitorHandle))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Window::X(ref window) => {
|
||||
window.available_monitors().into_iter().map(MonitorHandle::X).collect()
|
||||
},
|
||||
#[cfg(wayland_platform)]
|
||||
Window::Wayland(ref window) => {
|
||||
window.available_monitors().into_iter().map(MonitorHandle::Wayland).collect()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(x11_or_wayland!(match self; Window(w) => w.primary_monitor()?; as MonitorHandle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.raw_window_handle_rwh_06())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.raw_display_handle_rwh_06())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_theme(&self, theme: Option<Theme>) {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_theme(theme))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
x11_or_wayland!(match self; Window(window) => window.theme())
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, protected: bool) {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_content_protected(protected))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
x11_or_wayland!(match self; Window(window) => window.has_focus())
|
||||
}
|
||||
|
||||
pub fn title(&self) -> String {
|
||||
x11_or_wayland!(match self; Window(window) => window.title())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
pub text_with_all_modifiers: Option<SmolStr>,
|
||||
@@ -283,7 +626,7 @@ unsafe extern "C" fn x_error_callback(
|
||||
display: *mut x11::ffi::Display,
|
||||
event: *mut x11::ffi::XErrorEvent,
|
||||
) -> c_int {
|
||||
let xconn_lock = X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let xconn_lock = X11_BACKEND.lock().unwrap();
|
||||
if let Ok(ref xconn) = *xconn_lock {
|
||||
// Call all the hooks.
|
||||
let mut error_handled = false;
|
||||
@@ -385,7 +728,7 @@ impl EventLoop {
|
||||
} else {
|
||||
"neither WAYLAND_DISPLAY nor WAYLAND_SOCKET nor DISPLAY is set."
|
||||
};
|
||||
return Err(NotSupportedError::new(msg).into());
|
||||
return Err(EventLoopError::Os(os_error!(OsError::Misc(msg))));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -405,9 +748,9 @@ impl EventLoop {
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
fn new_x11_any_thread() -> Result<EventLoop, EventLoopError> {
|
||||
let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
|
||||
let xconn = match X11_BACKEND.lock().unwrap().as_ref() {
|
||||
Ok(xconn) => xconn.clone(),
|
||||
Err(err) => return Err(os_error!(err.clone()).into()),
|
||||
Err(_) => return Err(EventLoopError::NotSupported(NotSupportedError::new())),
|
||||
};
|
||||
|
||||
Ok(EventLoop::X(x11::EventLoop::new(xconn)))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! The event-loop routines.
|
||||
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::io::Result as IOResult;
|
||||
use std::mem;
|
||||
@@ -8,18 +9,19 @@ use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use sctk::reexports::calloop::Error as CalloopError;
|
||||
use sctk::reexports::calloop_wayland_source::WaylandSource;
|
||||
use sctk::reexports::client::{globals, Connection, QueueHandle};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::OnlyCursorImage;
|
||||
use crate::dpi::LogicalSize;
|
||||
use crate::error::{EventLoopError, OsError, RequestError};
|
||||
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::error::{EventLoopError, ExternalError, OsError as RootOsError};
|
||||
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::platform::min_timeout;
|
||||
use crate::platform_impl::PlatformCustomCursor;
|
||||
use crate::platform_impl::{OsError, PlatformCustomCursor};
|
||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme};
|
||||
|
||||
mod proxy;
|
||||
@@ -30,7 +32,7 @@ use sink::EventSink;
|
||||
|
||||
use super::state::{WindowCompositorUpdate, WinitState};
|
||||
use super::window::state::FrameCallbackState;
|
||||
use super::{logical_to_physical_rounded, DeviceId, WindowId};
|
||||
use super::{logical_to_physical_rounded, DeviceId, WaylandError, WindowId};
|
||||
|
||||
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
|
||||
|
||||
@@ -51,7 +53,7 @@ pub struct EventLoop {
|
||||
connection: Connection,
|
||||
|
||||
/// Event loop window target.
|
||||
active_event_loop: ActiveEventLoop,
|
||||
pub(crate) active_event_loop: ActiveEventLoop,
|
||||
|
||||
// XXX drop after everything else, just to be safe.
|
||||
/// Calloop's event loop.
|
||||
@@ -60,20 +62,27 @@ pub struct EventLoop {
|
||||
|
||||
impl EventLoop {
|
||||
pub fn new() -> Result<EventLoop, EventLoopError> {
|
||||
let connection = Connection::connect_to_env().map_err(|err| os_error!(err))?;
|
||||
macro_rules! map_err {
|
||||
($e:expr, $err:expr) => {
|
||||
$e.map_err(|error| os_error!($err(error).into()))
|
||||
};
|
||||
}
|
||||
|
||||
let connection = map_err!(Connection::connect_to_env(), WaylandError::Connection)?;
|
||||
|
||||
let (globals, mut event_queue) =
|
||||
globals::registry_queue_init(&connection).map_err(|err| os_error!(err))?;
|
||||
map_err!(globals::registry_queue_init(&connection), WaylandError::Global)?;
|
||||
let queue_handle = event_queue.handle();
|
||||
|
||||
let event_loop =
|
||||
calloop::EventLoop::<WinitState>::try_new().map_err(|err| os_error!(err))?;
|
||||
map_err!(calloop::EventLoop::<WinitState>::try_new(), WaylandError::Calloop)?;
|
||||
|
||||
let mut winit_state = WinitState::new(&globals, &queue_handle, event_loop.handle())?;
|
||||
let mut winit_state = WinitState::new(&globals, &queue_handle, event_loop.handle())
|
||||
.map_err(|error| os_error!(error))?;
|
||||
|
||||
// NOTE: do a roundtrip after binding the globals to prevent potential
|
||||
// races with the server.
|
||||
event_queue.roundtrip(&mut winit_state).map_err(|err| os_error!(err))?;
|
||||
map_err!(event_queue.roundtrip(&mut winit_state), WaylandError::Dispatch)?;
|
||||
|
||||
// Register Wayland source.
|
||||
let wayland_source = WaylandSource::new(connection.clone(), event_queue);
|
||||
@@ -89,32 +98,37 @@ impl EventLoop {
|
||||
result
|
||||
});
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.register_dispatcher(wayland_dispatcher.clone())
|
||||
.map_err(|err| os_error!(err))?;
|
||||
map_err!(
|
||||
event_loop.handle().register_dispatcher(wayland_dispatcher.clone()),
|
||||
WaylandError::Calloop
|
||||
)?;
|
||||
|
||||
// Setup the user proxy.
|
||||
let (ping, ping_source) = calloop::ping::make_ping().unwrap();
|
||||
event_loop
|
||||
let result = event_loop
|
||||
.handle()
|
||||
.insert_source(ping_source, move |_, _, winit_state: &mut WinitState| {
|
||||
winit_state.dispatched_events = true;
|
||||
winit_state.proxy_wake_up = true;
|
||||
})
|
||||
.map_err(|err| os_error!(err))?;
|
||||
.map_err(|error| error.error);
|
||||
map_err!(result, WaylandError::Calloop)?;
|
||||
|
||||
// An event's loop awakener to wake up for window events from winit's windows.
|
||||
let (event_loop_awakener, event_loop_awakener_source) =
|
||||
calloop::ping::make_ping().map_err(|err| os_error!(err))?;
|
||||
let (event_loop_awakener, event_loop_awakener_source) = map_err!(
|
||||
calloop::ping::make_ping()
|
||||
.map_err(|error| CalloopError::OtherError(Box::new(error).into())),
|
||||
WaylandError::Calloop
|
||||
)?;
|
||||
|
||||
event_loop
|
||||
let result = event_loop
|
||||
.handle()
|
||||
.insert_source(event_loop_awakener_source, move |_, _, winit_state: &mut WinitState| {
|
||||
// Mark that we have something to dispatch.
|
||||
winit_state.dispatched_events = true;
|
||||
})
|
||||
.map_err(|err| os_error!(err))?;
|
||||
.map_err(|error| error.error);
|
||||
map_err!(result, WaylandError::Calloop)?;
|
||||
|
||||
let active_event_loop = ActiveEventLoop {
|
||||
connection: connection.clone(),
|
||||
@@ -125,6 +139,7 @@ impl EventLoop {
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
exit: Cell::new(None),
|
||||
state: RefCell::new(winit_state),
|
||||
wayland_callback: Default::default(),
|
||||
};
|
||||
|
||||
let event_loop = Self {
|
||||
@@ -283,6 +298,10 @@ impl EventLoop {
|
||||
|
||||
app.new_events(&self.active_event_loop, cause);
|
||||
|
||||
if let Some(callback) = self.active_event_loop.wayland_callback.get() {
|
||||
callback(app);
|
||||
}
|
||||
|
||||
// NB: For consistency all platforms must call `can_create_surfaces` even though Wayland
|
||||
// applications don't themselves have a formal surface destroy/create lifecycle.
|
||||
if cause == StartCause::Init {
|
||||
@@ -304,24 +323,24 @@ impl EventLoop {
|
||||
let windows = state.windows.get_mut();
|
||||
let window = windows.get(&window_id).unwrap().lock().unwrap();
|
||||
let scale_factor = window.scale_factor();
|
||||
let size = logical_to_physical_rounded(window.surface_size(), scale_factor);
|
||||
let size = logical_to_physical_rounded(window.inner_size(), scale_factor);
|
||||
(size, scale_factor)
|
||||
});
|
||||
|
||||
// Stash the old window size.
|
||||
let old_physical_size = physical_size;
|
||||
|
||||
let new_surface_size = Arc::new(Mutex::new(physical_size));
|
||||
let new_inner_size = Arc::new(Mutex::new(physical_size));
|
||||
let root_window_id = crate::window::WindowId(window_id);
|
||||
let event = WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
};
|
||||
|
||||
app.window_event(&self.active_event_loop, root_window_id, event);
|
||||
|
||||
let physical_size = *new_surface_size.lock().unwrap();
|
||||
drop(new_surface_size);
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
|
||||
// Resize the window when user altered the size.
|
||||
if old_physical_size != physical_size {
|
||||
@@ -331,7 +350,7 @@ impl EventLoop {
|
||||
|
||||
let new_logical_size: LogicalSize<f64> =
|
||||
physical_size.to_logical(scale_factor);
|
||||
window.request_surface_size(new_logical_size.into());
|
||||
window.request_inner_size(new_logical_size.into());
|
||||
});
|
||||
|
||||
// Make it queue resize.
|
||||
@@ -347,7 +366,7 @@ impl EventLoop {
|
||||
let window = windows.get(&window_id).unwrap().lock().unwrap();
|
||||
|
||||
let scale_factor = window.scale_factor();
|
||||
let size = logical_to_physical_rounded(window.surface_size(), scale_factor);
|
||||
let size = logical_to_physical_rounded(window.inner_size(), scale_factor);
|
||||
|
||||
// Mark the window as needed a redraw.
|
||||
state
|
||||
@@ -362,7 +381,7 @@ impl EventLoop {
|
||||
});
|
||||
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
let event = WindowEvent::SurfaceResized(physical_size);
|
||||
let event = WindowEvent::Resized(physical_size);
|
||||
app.window_event(&self.active_event_loop, window_id, event);
|
||||
}
|
||||
|
||||
@@ -504,12 +523,14 @@ impl EventLoop {
|
||||
})
|
||||
}
|
||||
|
||||
fn roundtrip(&mut self) -> Result<usize, OsError> {
|
||||
fn roundtrip(&mut self) -> Result<usize, RootOsError> {
|
||||
let state = &mut self.active_event_loop.state.get_mut();
|
||||
|
||||
let mut wayland_source = self.wayland_dispatcher.as_source_mut();
|
||||
let event_queue = wayland_source.queue();
|
||||
event_queue.roundtrip(state).map_err(|err| os_error!(err))
|
||||
event_queue.roundtrip(state).map_err(|error| {
|
||||
os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
|
||||
})
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
@@ -566,6 +587,8 @@ pub struct ActiveEventLoop {
|
||||
|
||||
/// Connection to the wayland server.
|
||||
pub connection: Connection,
|
||||
|
||||
pub wayland_callback: Cell<Option<fn(&mut dyn ApplicationHandler)>>,
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
@@ -599,7 +622,7 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
cursor: CustomCursorSource,
|
||||
) -> Result<RootCustomCursor, RequestError> {
|
||||
) -> Result<RootCustomCursor, ExternalError> {
|
||||
Ok(RootCustomCursor {
|
||||
inner: PlatformCustomCursor::Wayland(OnlyCursorImage(Arc::from(cursor.inner.0))),
|
||||
})
|
||||
@@ -613,9 +636,10 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: crate::window::WindowAttributes,
|
||||
) -> Result<Box<dyn crate::window::Window>, RequestError> {
|
||||
) -> Result<crate::window::Window, RootOsError> {
|
||||
let window = crate::platform_impl::wayland::Window::new(self, window_attributes)?;
|
||||
Ok(Box::new(window))
|
||||
let window = crate::platform_impl::Window::Wayland(window);
|
||||
Ok(crate::window::Window { window })
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
|
||||
@@ -641,6 +665,11 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
//! Winit's Wayland backend.
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
|
||||
pub use output::{MonitorHandle, VideoModeHandle};
|
||||
use sctk::reexports::client::globals::{BindError, GlobalError};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::Proxy;
|
||||
use sctk::reexports::client::{self, ConnectError, DispatchError, Proxy};
|
||||
pub use window::Window;
|
||||
|
||||
pub(super) use crate::cursor::OnlyCursorImage as CustomCursor;
|
||||
use crate::dpi::{LogicalSize, PhysicalSize};
|
||||
pub use crate::platform_impl::platform::WindowId;
|
||||
pub use crate::platform_impl::platform::{OsError, WindowId};
|
||||
|
||||
mod event_loop;
|
||||
mod output;
|
||||
@@ -17,6 +21,46 @@ mod state;
|
||||
mod types;
|
||||
mod window;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WaylandError {
|
||||
/// Error connecting to the socket.
|
||||
Connection(ConnectError),
|
||||
|
||||
/// Error binding the global.
|
||||
Global(GlobalError),
|
||||
|
||||
// Bind error.
|
||||
Bind(BindError),
|
||||
|
||||
/// Error during the dispatching the event queue.
|
||||
Dispatch(DispatchError),
|
||||
|
||||
/// Calloop error.
|
||||
Calloop(calloop::Error),
|
||||
|
||||
/// Wayland
|
||||
Wire(client::backend::WaylandError),
|
||||
}
|
||||
|
||||
impl Display for WaylandError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
WaylandError::Connection(error) => error.fmt(f),
|
||||
WaylandError::Global(error) => error.fmt(f),
|
||||
WaylandError::Bind(error) => error.fmt(f),
|
||||
WaylandError::Dispatch(error) => error.fmt(f),
|
||||
WaylandError::Calloop(error) => error.fmt(f),
|
||||
WaylandError::Wire(error) => error.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WaylandError> for OsError {
|
||||
fn from(value: WaylandError) -> Self {
|
||||
Self::WaylandError(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Dummy device id, since Wayland doesn't have device events.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
@@ -21,7 +21,6 @@ use sctk::shm::slot::SlotPool;
|
||||
use sctk::shm::{Shm, ShmHandler};
|
||||
use sctk::subcompositor::SubcompositorState;
|
||||
|
||||
use crate::error::OsError;
|
||||
use crate::platform_impl::wayland::event_loop::sink::EventSink;
|
||||
use crate::platform_impl::wayland::output::MonitorHandle;
|
||||
use crate::platform_impl::wayland::seat::{
|
||||
@@ -33,7 +32,8 @@ use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScali
|
||||
use crate::platform_impl::wayland::types::wp_viewporter::ViewporterState;
|
||||
use crate::platform_impl::wayland::types::xdg_activation::XdgActivationState;
|
||||
use crate::platform_impl::wayland::window::{WindowRequests, WindowState};
|
||||
use crate::platform_impl::wayland::WindowId;
|
||||
use crate::platform_impl::wayland::{WaylandError, WindowId};
|
||||
use crate::platform_impl::OsError;
|
||||
|
||||
/// Winit's Wayland state.
|
||||
pub struct WinitState {
|
||||
@@ -126,7 +126,7 @@ impl WinitState {
|
||||
) -> Result<Self, OsError> {
|
||||
let registry_state = RegistryState::new(globals);
|
||||
let compositor_state =
|
||||
CompositorState::bind(globals, queue_handle).map_err(|err| os_error!(err))?;
|
||||
CompositorState::bind(globals, queue_handle).map_err(WaylandError::Bind)?;
|
||||
let subcompositor_state = match SubcompositorState::bind(
|
||||
compositor_state.wl_compositor().clone(),
|
||||
globals,
|
||||
@@ -156,7 +156,7 @@ impl WinitState {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let shm = Shm::bind(globals, queue_handle).map_err(|err| os_error!(err))?;
|
||||
let shm = Shm::bind(globals, queue_handle).map_err(WaylandError::Bind)?;
|
||||
let custom_cursor_pool = Arc::new(Mutex::new(SlotPool::new(2, &shm).unwrap()));
|
||||
|
||||
Ok(Self {
|
||||
@@ -168,7 +168,7 @@ impl WinitState {
|
||||
shm,
|
||||
custom_cursor_pool,
|
||||
|
||||
xdg_shell: XdgShell::bind(globals, queue_handle).map_err(|err| os_error!(err))?,
|
||||
xdg_shell: XdgShell::bind(globals, queue_handle).map_err(WaylandError::Bind)?,
|
||||
xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(),
|
||||
|
||||
windows: Default::default(),
|
||||
|
||||
@@ -16,17 +16,17 @@ use super::event_loop::sink::EventSink;
|
||||
use super::output::MonitorHandle;
|
||||
use super::state::WinitState;
|
||||
use super::types::xdg_activation::XdgActivationTokenData;
|
||||
use super::{ActiveEventLoop, WindowId};
|
||||
use super::{ActiveEventLoop, WaylandError, WindowId};
|
||||
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event::{Ime, WindowEvent};
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
use crate::platform_impl::{Fullscreen, MonitorHandle as PlatformMonitorHandle};
|
||||
use crate::platform_impl::{
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
|
||||
};
|
||||
use crate::window::{
|
||||
Cursor, CursorGrabMode, Fullscreen as CoreFullscreen, ImePurpose, ResizeDirection, Theme,
|
||||
UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons,
|
||||
WindowId as CoreWindowId, WindowLevel,
|
||||
Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
};
|
||||
|
||||
pub(crate) mod state;
|
||||
@@ -77,7 +77,7 @@ impl Window {
|
||||
pub(crate) fn new(
|
||||
event_loop_window_target: &ActiveEventLoop,
|
||||
attributes: WindowAttributes,
|
||||
) -> Result<Self, RequestError> {
|
||||
) -> Result<Self, RootOsError> {
|
||||
let queue_handle = event_loop_window_target.queue_handle.clone();
|
||||
let mut state = event_loop_window_target.state.borrow_mut();
|
||||
|
||||
@@ -89,7 +89,7 @@ impl Window {
|
||||
state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone());
|
||||
let display = event_loop_window_target.connection.display();
|
||||
|
||||
let size: Size = attributes.surface_size.unwrap_or(LogicalSize::new(800., 600.).into());
|
||||
let size: Size = attributes.inner_size.unwrap_or(LogicalSize::new(800., 600.).into());
|
||||
|
||||
// We prefer server side decorations, however to not have decorations we ask for client
|
||||
// side decorations instead.
|
||||
@@ -129,10 +129,10 @@ impl Window {
|
||||
|
||||
// Set the min and max sizes. We must set the hints upon creating a window, so
|
||||
// we use the default `1.` scaling...
|
||||
let min_size = attributes.min_surface_size.map(|size| size.to_logical(1.));
|
||||
let max_size = attributes.max_surface_size.map(|size| size.to_logical(1.));
|
||||
window_state.set_min_surface_size(min_size);
|
||||
window_state.set_max_surface_size(max_size);
|
||||
let min_size = attributes.min_inner_size.map(|size| size.to_logical(1.));
|
||||
let max_size = attributes.max_inner_size.map(|size| size.to_logical(1.));
|
||||
window_state.set_min_inner_size(min_size);
|
||||
window_state.set_max_inner_size(max_size);
|
||||
|
||||
// Non-resizable implies that the min and max sizes are set to the same value.
|
||||
window_state.set_resizable(attributes.resizable);
|
||||
@@ -190,11 +190,15 @@ impl Window {
|
||||
let event_queue = wayland_source.queue();
|
||||
|
||||
// Do a roundtrip.
|
||||
event_queue.roundtrip(&mut state).map_err(|err| os_error!(err))?;
|
||||
event_queue.roundtrip(&mut state).map_err(|error| {
|
||||
os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
|
||||
})?;
|
||||
|
||||
// XXX Wait for the initial configure to arrive.
|
||||
while !window_state.lock().unwrap().is_configured() {
|
||||
event_queue.blocking_dispatch(&mut state).map_err(|err| os_error!(err))?;
|
||||
event_queue.blocking_dispatch(&mut state).map_err(|error| {
|
||||
os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
|
||||
})?;
|
||||
}
|
||||
|
||||
// Wake-up event loop, so it'll send initial redraw requested.
|
||||
@@ -219,65 +223,51 @@ impl Window {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
|
||||
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||
Some(xdg_activation) => xdg_activation,
|
||||
None => return Err(NotSupportedError::new("xdg_activation_v1 is not available").into()),
|
||||
};
|
||||
|
||||
let serial = AsyncRequestSerial::get();
|
||||
|
||||
let data = XdgActivationTokenData::Obtain((self.window_id, serial));
|
||||
let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
|
||||
xdg_activation_token.set_surface(self.surface());
|
||||
xdg_activation_token.commit();
|
||||
|
||||
Ok(serial)
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
self.window_id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn surface(&self) -> &WlSurface {
|
||||
self.window.wl_surface()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
self.window_requests.closed.store(true, Ordering::Relaxed);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::WaylandWindowHandle::new({
|
||||
let ptr = self.window.wl_surface().id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
|
||||
});
|
||||
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw.into())) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::WaylandDisplayHandle::new({
|
||||
let ptr = self.display.id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
|
||||
});
|
||||
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw.into())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> CoreWindowId {
|
||||
CoreWindowId(self.window_id)
|
||||
pub fn set_title(&self, title: impl ToString) {
|
||||
let new_title = title.to_string();
|
||||
self.window_state.lock().unwrap().set_title(new_title);
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
#[inline]
|
||||
pub fn set_visible(&self, _visible: bool) {
|
||||
// Not possible on Wayland.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_visible(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
Err(NotSupportedError::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
Err(NotSupportedError::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_outer_position(&self, _: Position) {
|
||||
// Not possible on Wayland.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
let scale_factor = window_state.scale_factor();
|
||||
super::logical_to_physical_rounded(window_state.inner_size(), scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
// NOTE: try to not wake up the loop when the event was already scheduled and not yet
|
||||
// processed by the loop, because if at this point the value was `true` it could only
|
||||
// mean that the loop still haven't dispatched the value to the client and will do
|
||||
@@ -293,116 +283,135 @@ impl CoreWindow for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn title(&self) -> String {
|
||||
self.window_state.lock().unwrap().title().to_owned()
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {
|
||||
pub fn pre_present_notify(&self) {
|
||||
self.window_state.lock().unwrap().request_frame_callback();
|
||||
}
|
||||
|
||||
fn reset_dead_keys(&self) {
|
||||
crate::platform_impl::common::xkb::reset_dead_keys()
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Err(NotSupportedError::new("window position information is not available on Wayland")
|
||||
.into())
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Err(NotSupportedError::new("window position information is not available on Wayland")
|
||||
.into())
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, _position: Position) {
|
||||
// Not possible.
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
let scale_factor = window_state.scale_factor();
|
||||
super::logical_to_physical_rounded(window_state.surface_size(), scale_factor)
|
||||
}
|
||||
|
||||
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
let new_size = window_state.request_surface_size(size);
|
||||
self.request_redraw();
|
||||
Some(new_size)
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
#[inline]
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
let scale_factor = window_state.scale_factor();
|
||||
super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
#[inline]
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
let new_size = window_state.request_inner_size(size);
|
||||
self.request_redraw();
|
||||
Some(new_size)
|
||||
}
|
||||
|
||||
/// Set the minimum inner size for the window.
|
||||
#[inline]
|
||||
pub fn set_min_inner_size(&self, min_size: Option<Size>) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let min_size = min_size.map(|size| size.to_logical(scale_factor));
|
||||
self.window_state.lock().unwrap().set_min_surface_size(min_size);
|
||||
self.window_state.lock().unwrap().set_min_inner_size(min_size);
|
||||
// NOTE: Requires commit to be applied.
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
/// Set the maximum surface size for the window.
|
||||
/// Set the maximum inner size for the window.
|
||||
#[inline]
|
||||
fn set_max_surface_size(&self, max_size: Option<Size>) {
|
||||
pub fn set_max_inner_size(&self, max_size: Option<Size>) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let max_size = max_size.map(|size| size.to_logical(scale_factor));
|
||||
self.window_state.lock().unwrap().set_max_surface_size(max_size);
|
||||
self.window_state.lock().unwrap().set_max_inner_size(max_size);
|
||||
// NOTE: Requires commit to be applied.
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
#[inline]
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_surface_resize_increments(&self, _increments: Option<Size>) {
|
||||
warn!("`set_surface_resize_increments` is not implemented for Wayland");
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
let new_title = title.to_string();
|
||||
self.window_state.lock().unwrap().set_title(new_title);
|
||||
#[inline]
|
||||
pub fn set_resize_increments(&self, _increments: Option<Size>) {
|
||||
warn!("`set_resize_increments` is not implemented for Wayland");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
pub fn set_transparent(&self, transparent: bool) {
|
||||
self.window_state.lock().unwrap().set_transparent(transparent);
|
||||
}
|
||||
|
||||
fn set_visible(&self, _visible: bool) {
|
||||
// Not possible on Wayland.
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
self.window_state.lock().unwrap().has_focus()
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
#[inline]
|
||||
pub fn is_minimized(&self) -> Option<bool> {
|
||||
// XXX clients don't know whether they are minimized or not.
|
||||
None
|
||||
}
|
||||
|
||||
fn set_resizable(&self, resizable: bool) {
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, position: Position) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
self.window_state.lock().unwrap().show_window_menu(position);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
self.window_state.lock().unwrap().drag_resize_window(direction)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
if self.window_state.lock().unwrap().set_resizable(resizable) {
|
||||
// NOTE: Requires commit to be applied.
|
||||
self.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
fn is_resizable(&self) -> bool {
|
||||
#[inline]
|
||||
pub fn is_resizable(&self) -> bool {
|
||||
self.window_state.lock().unwrap().resizable()
|
||||
}
|
||||
|
||||
fn set_enabled_buttons(&self, _buttons: WindowButtons) {
|
||||
#[inline]
|
||||
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {
|
||||
// TODO(kchibisov) v5 of the xdg_shell allows that.
|
||||
}
|
||||
|
||||
fn enabled_buttons(&self) -> WindowButtons {
|
||||
#[inline]
|
||||
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||
// TODO(kchibisov) v5 of the xdg_shell allows that.
|
||||
WindowButtons::all()
|
||||
}
|
||||
|
||||
fn set_minimized(&self, minimized: bool) {
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.window_state.lock().unwrap().scale_factor()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_blur(&self, blur: bool) {
|
||||
self.window_state.lock().unwrap().set_blur(blur);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorate: bool) {
|
||||
self.window_state.lock().unwrap().set_decorate(decorate)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_decorated(&self) -> bool {
|
||||
self.window_state.lock().unwrap().is_decorated()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_level(&self, _level: WindowLevel) {}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_window_icon(&self, _window_icon: Option<PlatformIcon>) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
// You can't unminimize the window on Wayland.
|
||||
if !minimized {
|
||||
warn!("Unminimizing is ignored on Wayland.");
|
||||
@@ -412,20 +421,8 @@ impl CoreWindow for Window {
|
||||
self.window.set_minimized();
|
||||
}
|
||||
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
// XXX clients don't know whether they are minimized or not.
|
||||
None
|
||||
}
|
||||
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
if maximized {
|
||||
self.window.set_maximized()
|
||||
} else {
|
||||
self.window.unset_maximized()
|
||||
}
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
#[inline]
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
self.window_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
@@ -435,14 +432,43 @@ impl CoreWindow for Window {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, fullscreen: Option<CoreFullscreen>) {
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
if maximized {
|
||||
self.window.set_maximized()
|
||||
} else {
|
||||
self.window.unset_maximized()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
let is_fullscreen = self
|
||||
.window_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.last_configure
|
||||
.as_ref()
|
||||
.map(|last_configure| last_configure.is_fullscreen())
|
||||
.unwrap_or_default();
|
||||
|
||||
if is_fullscreen {
|
||||
let current_monitor = self.current_monitor().map(PlatformMonitorHandle::Wayland);
|
||||
Some(Fullscreen::Borderless(current_monitor))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
match fullscreen {
|
||||
Some(CoreFullscreen::Exclusive(_)) => {
|
||||
Some(Fullscreen::Exclusive(_)) => {
|
||||
warn!("`Fullscreen::Exclusive` is ignored on Wayland");
|
||||
},
|
||||
#[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
|
||||
Some(CoreFullscreen::Borderless(monitor)) => {
|
||||
let output = monitor.and_then(|monitor| match monitor.inner {
|
||||
Some(Fullscreen::Borderless(monitor)) => {
|
||||
let output = monitor.and_then(|monitor| match monitor {
|
||||
PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
|
||||
#[cfg(x11_platform)]
|
||||
PlatformMonitorHandle::X(_) => None,
|
||||
@@ -454,82 +480,22 @@ impl CoreWindow for Window {
|
||||
}
|
||||
}
|
||||
|
||||
fn fullscreen(&self) -> Option<CoreFullscreen> {
|
||||
let is_fullscreen = self
|
||||
.window_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.last_configure
|
||||
.as_ref()
|
||||
.map(|last_configure| last_configure.is_fullscreen())
|
||||
.unwrap_or_default();
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: Cursor) {
|
||||
let window_state = &mut self.window_state.lock().unwrap();
|
||||
|
||||
if is_fullscreen {
|
||||
let current_monitor = self.current_monitor();
|
||||
Some(CoreFullscreen::Borderless(current_monitor))
|
||||
} else {
|
||||
None
|
||||
match cursor {
|
||||
Cursor::Icon(icon) => window_state.set_cursor(icon),
|
||||
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn scale_factor(&self) -> f64 {
|
||||
self.window_state.lock().unwrap().scale_factor()
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
self.window_state.lock().unwrap().set_cursor_visible(visible);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_blur(&self, blur: bool) {
|
||||
self.window_state.lock().unwrap().set_blur(blur);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_decorations(&self, decorate: bool) {
|
||||
self.window_state.lock().unwrap().set_decorate(decorate)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_decorated(&self) -> bool {
|
||||
self.window_state.lock().unwrap().is_decorated()
|
||||
}
|
||||
|
||||
fn set_window_level(&self, _level: WindowLevel) {}
|
||||
|
||||
fn set_window_icon(&self, _window_icon: Option<crate::window::Icon>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
if window_state.ime_allowed() {
|
||||
let scale_factor = window_state.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
let size = size.to_logical(scale_factor);
|
||||
window_state.set_ime_cursor_area(position, size);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
|
||||
let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
|
||||
self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.window_state.lock().unwrap().set_ime_purpose(purpose);
|
||||
}
|
||||
|
||||
fn focus_window(&self) {}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
self.window_state.lock().unwrap().has_focus()
|
||||
}
|
||||
|
||||
fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||
Some(xdg_activation) => xdg_activation,
|
||||
None => {
|
||||
@@ -555,26 +521,29 @@ impl CoreWindow for Window {
|
||||
xdg_activation_token.commit();
|
||||
}
|
||||
|
||||
fn set_theme(&self, theme: Option<Theme>) {
|
||||
self.window_state.lock().unwrap().set_theme(theme)
|
||||
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||
Some(xdg_activation) => xdg_activation,
|
||||
None => return Err(NotSupportedError::new()),
|
||||
};
|
||||
|
||||
let serial = AsyncRequestSerial::get();
|
||||
|
||||
let data = XdgActivationTokenData::Obtain((self.window_id, serial));
|
||||
let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
|
||||
xdg_activation_token.set_surface(self.surface());
|
||||
xdg_activation_token.commit();
|
||||
|
||||
Ok(serial)
|
||||
}
|
||||
|
||||
fn theme(&self) -> Option<Theme> {
|
||||
self.window_state.lock().unwrap().theme()
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
self.window_state.lock().unwrap().set_cursor_grab(mode)
|
||||
}
|
||||
|
||||
fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
fn set_cursor(&self, cursor: Cursor) {
|
||||
let window_state = &mut self.window_state.lock().unwrap();
|
||||
|
||||
match cursor {
|
||||
Cursor::Icon(icon) => window_state.set_cursor(icon),
|
||||
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
self.window_state
|
||||
@@ -585,78 +554,124 @@ impl CoreWindow for Window {
|
||||
.map(|_| self.request_redraw())
|
||||
}
|
||||
|
||||
fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
self.window_state.lock().unwrap().set_cursor_grab(mode)
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
self.window_state.lock().unwrap().set_cursor_visible(visible);
|
||||
}
|
||||
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
self.window_state.lock().unwrap().drag_window()
|
||||
}
|
||||
|
||||
fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
|
||||
self.window_state.lock().unwrap().drag_resize_window(direction)
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, position: Position) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
self.window_state.lock().unwrap().show_window_menu(position);
|
||||
}
|
||||
|
||||
fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
let surface = self.window.wl_surface();
|
||||
|
||||
if hittest {
|
||||
surface.set_input_region(None);
|
||||
Ok(())
|
||||
} else {
|
||||
let region = Region::new(&*self.compositor).map_err(|err| os_error!(err))?;
|
||||
let region = Region::new(&*self.compositor).map_err(|_| {
|
||||
ExternalError::Os(os_error!(OsError::Misc("failed to set input region.")))
|
||||
})?;
|
||||
region.add(0, 0, 0, 0);
|
||||
surface.set_input_region(Some(region.wl_region()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
#[inline]
|
||||
pub fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
if window_state.ime_allowed() {
|
||||
let scale_factor = window_state.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
let size = size.to_logical(scale_factor);
|
||||
window_state.set_ime_cursor_area(position, size);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_allowed(&self, allowed: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
|
||||
let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
|
||||
self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.window_state.lock().unwrap().set_ime_purpose(purpose);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {}
|
||||
|
||||
#[inline]
|
||||
pub fn surface(&self) -> &WlSurface {
|
||||
self.window.wl_surface()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||
let data = self.window.wl_surface().data::<SurfaceData>()?;
|
||||
data.outputs()
|
||||
.next()
|
||||
.map(MonitorHandle::new)
|
||||
.map(crate::platform_impl::MonitorHandle::Wayland)
|
||||
.map(|inner| CoreMonitorHandle { inner })
|
||||
data.outputs().next().map(MonitorHandle::new)
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
|
||||
Box::new(
|
||||
self.monitors
|
||||
.lock()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(crate::platform_impl::MonitorHandle::Wayland)
|
||||
.map(|inner| CoreMonitorHandle { inner }),
|
||||
)
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
|
||||
self.monitors.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
// NOTE: There's no such concept on Wayland.
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
// XXX there's no such concept on Wayland.
|
||||
None
|
||||
}
|
||||
|
||||
/// Get the raw-window-handle v0.6 display handle.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::WaylandWindowHandle::new({
|
||||
let ptr = self.window.wl_surface().id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
|
||||
})
|
||||
.into())
|
||||
}
|
||||
|
||||
/// Get the raw-window-handle v0.6 window handle.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::WaylandDisplayHandle::new({
|
||||
let ptr = self.display.id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
|
||||
})
|
||||
.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_theme(&self, theme: Option<Theme>) {
|
||||
self.window_state.lock().unwrap().set_theme(theme)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
self.window_state.lock().unwrap().theme()
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
#[inline]
|
||||
pub fn title(&self) -> String {
|
||||
self.window_state.lock().unwrap().title().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
self.window_requests.closed.store(true, Ordering::Relaxed);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
||||
|
||||
use crate::cursor::CustomCursor as RootCustomCursor;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::error::{ExternalError, NotSupportedError};
|
||||
use crate::platform_impl::wayland::logical_to_physical_rounded;
|
||||
use crate::platform_impl::wayland::seat::{
|
||||
PointerConstraintsState, WinitPointerData, WinitPointerDataExt, ZwpTextInputV3Ext,
|
||||
@@ -46,7 +46,7 @@ pub type WinitFrame = sctk_adwaita::AdwaitaFrame<WinitState>;
|
||||
#[cfg(not(feature = "sctk-adwaita"))]
|
||||
pub type WinitFrame = sctk::shell::xdg::fallback_frame::FallbackFrame<WinitState>;
|
||||
|
||||
// Minimum window surface size.
|
||||
// Minimum window inner size.
|
||||
const MIN_WINDOW_SIZE: LogicalSize<u32> = LogicalSize::new(2, 1);
|
||||
|
||||
/// The state of the window which is being updated from the [`WinitState`].
|
||||
@@ -112,7 +112,7 @@ pub struct WindowState {
|
||||
/// The text inputs observed on the window.
|
||||
text_inputs: Vec<ZwpTextInputV3>,
|
||||
|
||||
/// The surface size of the window, as in without client side decorations.
|
||||
/// The inner size of the window, as in without client side decorations.
|
||||
size: LogicalSize<u32>,
|
||||
|
||||
/// Whether the CSD fail to create, so we don't try to create them on each iteration.
|
||||
@@ -122,8 +122,8 @@ pub struct WindowState {
|
||||
decorate: bool,
|
||||
|
||||
/// Min size.
|
||||
min_surface_size: LogicalSize<u32>,
|
||||
max_surface_size: Option<LogicalSize<u32>>,
|
||||
min_inner_size: LogicalSize<u32>,
|
||||
max_inner_size: Option<LogicalSize<u32>>,
|
||||
|
||||
/// The size of the window when no states were applied to it. The primary use for it
|
||||
/// is to fallback to original window size, before it was maximized, if the compositor
|
||||
@@ -197,8 +197,8 @@ impl WindowState {
|
||||
ime_allowed: false,
|
||||
ime_purpose: ImePurpose::Normal,
|
||||
last_configure: None,
|
||||
max_surface_size: None,
|
||||
min_surface_size: MIN_WINDOW_SIZE,
|
||||
max_inner_size: None,
|
||||
min_inner_size: MIN_WINDOW_SIZE,
|
||||
pointer_constraints,
|
||||
pointers: Default::default(),
|
||||
queue_handle: queue_handle.clone(),
|
||||
@@ -328,7 +328,7 @@ impl WindowState {
|
||||
|
||||
// Apply configure bounds only when compositor let the user decide what size to pick.
|
||||
if constrain {
|
||||
let bounds = self.surface_size_bounds(&configure);
|
||||
let bounds = self.inner_size_bounds(&configure);
|
||||
new_size.width =
|
||||
bounds.0.map(|bound_w| new_size.width.min(bound_w.get())).unwrap_or(new_size.width);
|
||||
new_size.height = bounds
|
||||
@@ -353,7 +353,7 @@ impl WindowState {
|
||||
// NOTE: Set the configure before doing a resize, since we query it during it.
|
||||
self.last_configure = Some(configure);
|
||||
|
||||
if state_change_requires_resize || new_size != self.surface_size() {
|
||||
if state_change_requires_resize || new_size != self.inner_size() {
|
||||
self.resize(new_size);
|
||||
true
|
||||
} else {
|
||||
@@ -361,8 +361,8 @@ impl WindowState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the bounds for the surface size of the surface.
|
||||
fn surface_size_bounds(
|
||||
/// Compute the bounds for the inner size of the surface.
|
||||
fn inner_size_bounds(
|
||||
&self,
|
||||
configure: &WindowConfigure,
|
||||
) -> (Option<NonZeroU32>, Option<NonZeroU32>) {
|
||||
@@ -388,7 +388,7 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Start interacting drag resize.
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
let xdg_toplevel = self.window.xdg_toplevel();
|
||||
|
||||
// TODO(kchibisov) handle touch serials.
|
||||
@@ -402,7 +402,7 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Start the window drag.
|
||||
pub fn drag_window(&self) -> Result<(), RequestError> {
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
let xdg_toplevel = self.window.xdg_toplevel();
|
||||
// TODO(kchibisov) handle touch serials.
|
||||
self.apply_on_pointer(|_, data| {
|
||||
@@ -507,8 +507,8 @@ impl WindowState {
|
||||
// Restore min/max sizes of the window.
|
||||
self.reload_min_max_hints();
|
||||
} else {
|
||||
self.set_min_surface_size(Some(self.size));
|
||||
self.set_max_surface_size(Some(self.size));
|
||||
self.set_min_inner_size(Some(self.size));
|
||||
self.set_max_inner_size(Some(self.size));
|
||||
}
|
||||
|
||||
// Reload the state on the frame as well.
|
||||
@@ -533,7 +533,7 @@ impl WindowState {
|
||||
|
||||
/// Get the size of the window.
|
||||
#[inline]
|
||||
pub fn surface_size(&self) -> LogicalSize<u32> {
|
||||
pub fn inner_size(&self) -> LogicalSize<u32> {
|
||||
self.size
|
||||
}
|
||||
|
||||
@@ -628,21 +628,21 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Try to resize the window when the user can do so.
|
||||
pub fn request_surface_size(&mut self, surface_size: Size) -> PhysicalSize<u32> {
|
||||
pub fn request_inner_size(&mut self, inner_size: Size) -> PhysicalSize<u32> {
|
||||
if self.last_configure.as_ref().map(Self::is_stateless).unwrap_or(true) {
|
||||
self.resize(surface_size.to_logical(self.scale_factor()))
|
||||
self.resize(inner_size.to_logical(self.scale_factor()))
|
||||
}
|
||||
|
||||
logical_to_physical_rounded(self.surface_size(), self.scale_factor())
|
||||
logical_to_physical_rounded(self.inner_size(), self.scale_factor())
|
||||
}
|
||||
|
||||
/// Resize the window to the new surface size.
|
||||
fn resize(&mut self, surface_size: LogicalSize<u32>) {
|
||||
self.size = surface_size;
|
||||
/// Resize the window to the new inner size.
|
||||
fn resize(&mut self, inner_size: LogicalSize<u32>) {
|
||||
self.size = inner_size;
|
||||
|
||||
// Update the stateless size.
|
||||
if Some(true) == self.last_configure.as_ref().map(Self::is_stateless) {
|
||||
self.stateless_size = surface_size;
|
||||
self.stateless_size = inner_size;
|
||||
}
|
||||
|
||||
// Update the inner frame.
|
||||
@@ -673,7 +673,7 @@ impl WindowState {
|
||||
|
||||
// Update the target viewport, this is used if and only if fractional scaling is in use.
|
||||
if let Some(viewport) = self.viewport.as_ref() {
|
||||
// Set surface size without the borders.
|
||||
// Set inner size without the borders.
|
||||
viewport.set_destination(self.size.width as _, self.size.height as _);
|
||||
}
|
||||
}
|
||||
@@ -753,7 +753,7 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Set maximum inner window size.
|
||||
pub fn set_min_surface_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
pub fn set_min_inner_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
// Ensure that the window has the right minimum size.
|
||||
let mut size = size.unwrap_or(MIN_WINDOW_SIZE);
|
||||
size.width = size.width.max(MIN_WINDOW_SIZE.width);
|
||||
@@ -766,12 +766,12 @@ impl WindowState {
|
||||
.map(|frame| frame.add_borders(size.width, size.height).into())
|
||||
.unwrap_or(size);
|
||||
|
||||
self.min_surface_size = size;
|
||||
self.min_inner_size = size;
|
||||
self.window.set_min_size(Some(size.into()));
|
||||
}
|
||||
|
||||
/// Set maximum inner window size.
|
||||
pub fn set_max_surface_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
pub fn set_max_inner_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
let size = size.map(|size| {
|
||||
self.frame
|
||||
.as_ref()
|
||||
@@ -779,7 +779,7 @@ impl WindowState {
|
||||
.unwrap_or(size)
|
||||
});
|
||||
|
||||
self.max_surface_size = size;
|
||||
self.max_inner_size = size;
|
||||
self.window.set_max_size(size.map(Into::into));
|
||||
}
|
||||
|
||||
@@ -799,7 +799,7 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Set the cursor grabbing state on the top-level.
|
||||
pub fn set_cursor_grab(&mut self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
pub fn set_cursor_grab(&mut self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
if self.cursor_grab_mode.user_grab_mode == mode {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -812,20 +812,16 @@ impl WindowState {
|
||||
|
||||
/// Reload the hints for minimum and maximum sizes.
|
||||
pub fn reload_min_max_hints(&mut self) {
|
||||
self.set_min_surface_size(Some(self.min_surface_size));
|
||||
self.set_max_surface_size(self.max_surface_size);
|
||||
self.set_min_inner_size(Some(self.min_inner_size));
|
||||
self.set_max_inner_size(self.max_inner_size);
|
||||
}
|
||||
|
||||
/// Set the grabbing state on the surface.
|
||||
fn set_cursor_grab_inner(&mut self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
fn set_cursor_grab_inner(&mut self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
let pointer_constraints = match self.pointer_constraints.as_ref() {
|
||||
Some(pointer_constraints) => pointer_constraints,
|
||||
None if mode == CursorGrabMode::None => return Ok(()),
|
||||
None => {
|
||||
return Err(
|
||||
NotSupportedError::new("zwp_pointer_constraints is not available").into()
|
||||
)
|
||||
},
|
||||
None => return Err(ExternalError::NotSupported(NotSupportedError::new())),
|
||||
};
|
||||
|
||||
// Replace the current mode.
|
||||
@@ -869,17 +865,16 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Set the position of the cursor.
|
||||
pub fn set_cursor_position(&self, position: LogicalPosition<f64>) -> Result<(), RequestError> {
|
||||
pub fn set_cursor_position(&self, position: LogicalPosition<f64>) -> Result<(), ExternalError> {
|
||||
if self.pointer_constraints.is_none() {
|
||||
return Err(NotSupportedError::new("zwp_pointer_constraints is not available").into());
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
||||
}
|
||||
|
||||
// Position can be set only for locked cursor.
|
||||
if self.cursor_grab_mode.current_grab_mode != CursorGrabMode::Locked {
|
||||
return Err(NotSupportedError::new(
|
||||
"cursor position could only be changed for locked pointer",
|
||||
)
|
||||
.into());
|
||||
return Err(ExternalError::Os(os_error!(crate::platform_impl::OsError::Misc(
|
||||
"cursor position can be set only for locked cursor."
|
||||
))));
|
||||
}
|
||||
|
||||
self.apply_on_pointer(|_, data| {
|
||||
|
||||
@@ -22,8 +22,8 @@ use xkbcommon_dl::xkb_mod_mask_t;
|
||||
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::event::{
|
||||
DeviceEvent, ElementState, Event, Ime, MouseButton, MouseScrollDelta, RawKeyEvent,
|
||||
SurfaceSizeWriter, Touch, TouchPhase, WindowEvent,
|
||||
DeviceEvent, ElementState, Event, Ime, InnerSizeWriter, MouseButton, MouseScrollDelta,
|
||||
RawKeyEvent, Touch, TouchPhase, WindowEvent,
|
||||
};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::platform_impl::common::xkb::{self, XkbState};
|
||||
@@ -598,19 +598,19 @@ impl EventProcessor {
|
||||
// `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root
|
||||
// `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent
|
||||
// https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5
|
||||
// We don't want to send `Moved` when this is false, since then every `SurfaceResized`
|
||||
// We don't want to send `Moved` when this is false, since then every `Resized`
|
||||
// (whether the window moved or not) is accompanied by an extraneous `Moved` event
|
||||
// that has a position relative to the parent window.
|
||||
let is_synthetic = xev.send_event == xlib::True;
|
||||
|
||||
// These are both in physical space.
|
||||
let new_surface_size = (xev.width as u32, xev.height as u32);
|
||||
let new_inner_size = (xev.width as u32, xev.height as u32);
|
||||
let new_inner_position = (xev.x, xev.y);
|
||||
|
||||
let (mut resized, moved) = {
|
||||
let mut shared_state_lock = window.shared_state_lock();
|
||||
|
||||
let resized = util::maybe_change(&mut shared_state_lock.size, new_surface_size);
|
||||
let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size);
|
||||
let moved = if is_synthetic {
|
||||
util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position)
|
||||
} else {
|
||||
@@ -671,7 +671,7 @@ impl EventProcessor {
|
||||
|
||||
let last_scale_factor = shared_state_lock.last_monitor.scale_factor;
|
||||
let new_scale_factor = {
|
||||
let window_rect = util::AaRect::new(new_outer_position, new_surface_size);
|
||||
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
|
||||
let monitor = self
|
||||
.target
|
||||
.xconn
|
||||
@@ -695,30 +695,27 @@ impl EventProcessor {
|
||||
&shared_state_lock,
|
||||
);
|
||||
|
||||
let old_surface_size = PhysicalSize::new(width, height);
|
||||
let new_surface_size = PhysicalSize::new(new_width, new_height);
|
||||
let old_inner_size = PhysicalSize::new(width, height);
|
||||
let new_inner_size = PhysicalSize::new(new_width, new_height);
|
||||
|
||||
// Unlock shared state to prevent deadlock in callback below
|
||||
drop(shared_state_lock);
|
||||
|
||||
let surface_size = Arc::new(Mutex::new(new_surface_size));
|
||||
let inner_size = Arc::new(Mutex::new(new_inner_size));
|
||||
callback(&self.target, Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor: new_scale_factor,
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&surface_size)),
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&inner_size)),
|
||||
},
|
||||
});
|
||||
|
||||
let new_surface_size = *surface_size.lock().unwrap();
|
||||
drop(surface_size);
|
||||
let new_inner_size = *inner_size.lock().unwrap();
|
||||
drop(inner_size);
|
||||
|
||||
if new_surface_size != old_surface_size {
|
||||
window.request_surface_size_physical(
|
||||
new_surface_size.width,
|
||||
new_surface_size.height,
|
||||
);
|
||||
window.shared_state_lock().dpi_adjusted = Some(new_surface_size.into());
|
||||
if new_inner_size != old_inner_size {
|
||||
window.request_inner_size_physical(new_inner_size.width, new_inner_size.height);
|
||||
window.shared_state_lock().dpi_adjusted = Some(new_inner_size.into());
|
||||
// if the DPI factor changed, force a resize event to ensure the logical
|
||||
// size is computed with the right DPI factor
|
||||
resized = true;
|
||||
@@ -739,13 +736,13 @@ impl EventProcessor {
|
||||
// XResizeWindow requests, making Xorg, the winit client, and the WM
|
||||
// consume 100% of CPU.
|
||||
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
|
||||
if new_surface_size == adjusted_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
|
||||
if new_inner_size == adjusted_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
|
||||
// When this finally happens, the event will not be synthetic.
|
||||
shared_state_lock.dpi_adjusted = None;
|
||||
} else {
|
||||
// Unlock shared state to prevent deadlock in callback below
|
||||
drop(shared_state_lock);
|
||||
window.request_surface_size_physical(adjusted_size.0, adjusted_size.1);
|
||||
window.request_inner_size_physical(adjusted_size.0, adjusted_size.1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,7 +757,7 @@ impl EventProcessor {
|
||||
if resized {
|
||||
callback(&self.target, Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::SurfaceResized(new_surface_size.into()),
|
||||
event: WindowEvent::Resized(new_inner_size.into()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub use x11_dl::error::OpenError;
|
||||
pub use x11_dl::xcursor::*;
|
||||
pub use x11_dl::xinput2::*;
|
||||
pub use x11_dl::xlib::*;
|
||||
pub use x11_dl::xlib_xcb::*;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::error::Error;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_short;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ptr};
|
||||
use std::{mem, ptr};
|
||||
|
||||
use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct};
|
||||
|
||||
@@ -20,19 +19,6 @@ pub enum ImeContextCreationError {
|
||||
Null,
|
||||
}
|
||||
|
||||
impl fmt::Display for ImeContextCreationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ImeContextCreationError::XError(err) => err.fmt(f),
|
||||
ImeContextCreationError::Null => {
|
||||
write!(f, "got null pointer from Xlib without exact reason")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ImeContextCreationError {}
|
||||
|
||||
/// The callback used by XIM preedit functions.
|
||||
type XIMProcNonnull = unsafe extern "C" fn(ffi::XIM, ffi::XPointer, ffi::XPointer);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::ffi::CStr;
|
||||
@@ -18,12 +19,13 @@ use tracing::warn;
|
||||
use x11rb::connection::RequestConnection;
|
||||
use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
|
||||
use x11rb::protocol::xinput::{self, ConnectionExt as _};
|
||||
use x11rb::protocol::{xkb, xproto};
|
||||
use x11rb::protocol::xkb;
|
||||
use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||
use x11rb::x11_utils::X11Error as LogicalError;
|
||||
use x11rb::xcb_ffi::ReplyOrIdError;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, RequestError};
|
||||
use crate::error::{EventLoopError, ExternalError, OsError as RootOsError};
|
||||
use crate::event::{Event, StartCause, WindowEvent};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
@@ -32,11 +34,9 @@ use crate::event_loop::{
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::common::xkb::Context;
|
||||
use crate::platform_impl::platform::{min_timeout, WindowId};
|
||||
use crate::platform_impl::x11::window::Window;
|
||||
use crate::platform_impl::{OwnedDisplayHandle, PlatformCustomCursor};
|
||||
use crate::platform_impl::{OsError, OwnedDisplayHandle, PlatformCustomCursor};
|
||||
use crate::window::{
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
|
||||
WindowAttributes,
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowAttributes,
|
||||
};
|
||||
|
||||
mod activation;
|
||||
@@ -47,7 +47,7 @@ pub mod ffi;
|
||||
mod ime;
|
||||
mod monitor;
|
||||
mod util;
|
||||
pub(crate) mod window;
|
||||
mod window;
|
||||
mod xdisplay;
|
||||
mod xsettings;
|
||||
|
||||
@@ -399,11 +399,9 @@ impl EventLoop {
|
||||
// `run_on_demand` calls but if they have only just dropped their
|
||||
// windows we need to make sure those last requests are sent to the
|
||||
// X Server.
|
||||
self.event_processor
|
||||
.target
|
||||
.x_connection()
|
||||
.sync_with_server()
|
||||
.map_err(|x_err| EventLoopError::Os(os_error!(X11Error::Xlib(x_err))))?;
|
||||
self.event_processor.target.x_connection().sync_with_server().map_err(|x_err| {
|
||||
EventLoopError::Os(os_error!(OsError::XError(Arc::new(X11Error::Xlib(x_err)))))
|
||||
})?;
|
||||
|
||||
exit
|
||||
}
|
||||
@@ -690,14 +688,16 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: WindowAttributes,
|
||||
) -> Result<Box<dyn CoreWindow>, RequestError> {
|
||||
Ok(Box::new(Window::new(self, window_attributes)?))
|
||||
) -> Result<crate::window::Window, RootOsError> {
|
||||
let window = crate::platform_impl::x11::Window::new(self, window_attributes)?;
|
||||
let window = crate::platform_impl::Window::X(window);
|
||||
Ok(crate::window::Window { window })
|
||||
}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
custom_cursor: CustomCursorSource,
|
||||
) -> Result<RootCustomCursor, RequestError> {
|
||||
) -> Result<RootCustomCursor, ExternalError> {
|
||||
Ok(RootCustomCursor {
|
||||
inner: PlatformCustomCursor::X(CustomCursor::new(self, custom_cursor.inner)?),
|
||||
})
|
||||
@@ -751,6 +751,10 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
RootOwnedDisplayHandle { platform: handle }
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
@@ -828,6 +832,39 @@ impl FingerId {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Window(Arc<UnownedWindow>);
|
||||
|
||||
impl Deref for Window {
|
||||
type Target = UnownedWindow;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &UnownedWindow {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new(
|
||||
event_loop: &ActiveEventLoop,
|
||||
attribs: WindowAttributes,
|
||||
) -> Result<Self, RootOsError> {
|
||||
let window = Arc::new(UnownedWindow::new(event_loop, attribs)?);
|
||||
event_loop.windows.borrow_mut().insert(window.id(), Arc::downgrade(&window));
|
||||
Ok(Window(window))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
let window = self.deref();
|
||||
let xconn = &window.xconn;
|
||||
|
||||
if let Ok(c) = xconn.xcb_connection().destroy_window(window.id().0 as xproto::Window) {
|
||||
c.ignore_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
ping: Ping,
|
||||
@@ -874,9 +911,6 @@ pub enum X11Error {
|
||||
|
||||
/// Failed to get property.
|
||||
GetProperty(util::GetPropertyError),
|
||||
|
||||
/// Could not find an ARGB32 pict format.
|
||||
NoArgb32Format,
|
||||
}
|
||||
|
||||
impl fmt::Display for X11Error {
|
||||
@@ -901,9 +935,6 @@ impl fmt::Display for X11Error {
|
||||
X11Error::XsettingsParse(err) => {
|
||||
write!(f, "Failed to parse xsettings: {:?}", err)
|
||||
},
|
||||
X11Error::NoArgb32Format => {
|
||||
f.write_str("winit only supports X11 displays with ARGB32 picture formats")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,161 +1,92 @@
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::ffi::CString;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
use std::{iter, slice};
|
||||
|
||||
use x11rb::connection::Connection;
|
||||
use x11rb::protocol::render::{self, ConnectionExt as _};
|
||||
use x11rb::protocol::xproto;
|
||||
|
||||
use super::super::ActiveEventLoop;
|
||||
use super::*;
|
||||
use crate::error::RequestError;
|
||||
use crate::platform_impl::PlatformCustomCursorSource;
|
||||
use crate::error::ExternalError;
|
||||
use crate::platform_impl::{OsError, PlatformCustomCursorSource};
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
impl XConnection {
|
||||
pub fn set_cursor_icon(
|
||||
&self,
|
||||
window: xproto::Window,
|
||||
cursor: Option<CursorIcon>,
|
||||
) -> Result<(), X11Error> {
|
||||
let cursor = {
|
||||
let mut cache = self.cursor_cache.lock().unwrap_or_else(|e| e.into_inner());
|
||||
pub fn set_cursor_icon(&self, window: xproto::Window, cursor: Option<CursorIcon>) {
|
||||
let cursor = *self
|
||||
.cursor_cache
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(cursor)
|
||||
.or_insert_with(|| self.get_cursor(cursor));
|
||||
|
||||
match cache.entry(cursor) {
|
||||
Entry::Occupied(o) => *o.get(),
|
||||
Entry::Vacant(v) => *v.insert(self.get_cursor(cursor)?),
|
||||
}
|
||||
self.update_cursor(window, cursor).expect("Failed to set cursor");
|
||||
}
|
||||
|
||||
pub(crate) fn set_custom_cursor(&self, window: xproto::Window, cursor: &CustomCursor) {
|
||||
self.update_cursor(window, cursor.inner.cursor).expect("Failed to set cursor");
|
||||
}
|
||||
|
||||
fn create_empty_cursor(&self) -> ffi::Cursor {
|
||||
let data = 0;
|
||||
let pixmap = unsafe {
|
||||
let screen = (self.xlib.XDefaultScreen)(self.display);
|
||||
let window = (self.xlib.XRootWindow)(self.display, screen);
|
||||
(self.xlib.XCreateBitmapFromData)(self.display, window, &data, 1, 1)
|
||||
};
|
||||
|
||||
self.update_cursor(window, cursor)
|
||||
}
|
||||
|
||||
pub(crate) fn set_custom_cursor(
|
||||
&self,
|
||||
window: xproto::Window,
|
||||
cursor: &CustomCursor,
|
||||
) -> Result<(), X11Error> {
|
||||
self.update_cursor(window, cursor.inner.cursor)
|
||||
}
|
||||
|
||||
/// Create a cursor from an image.
|
||||
fn create_cursor_from_image(
|
||||
&self,
|
||||
width: u16,
|
||||
height: u16,
|
||||
hotspot_x: u16,
|
||||
hotspot_y: u16,
|
||||
image: &[u8],
|
||||
) -> Result<xproto::Cursor, X11Error> {
|
||||
// Create a pixmap for the default root window.
|
||||
let root = self.default_root().root;
|
||||
let pixmap =
|
||||
xproto::PixmapWrapper::create_pixmap(self.xcb_connection(), 32, root, width, height)?;
|
||||
|
||||
// Create a GC to draw with.
|
||||
let gc = xproto::GcontextWrapper::create_gc(
|
||||
self.xcb_connection(),
|
||||
pixmap.pixmap(),
|
||||
&Default::default(),
|
||||
)?;
|
||||
|
||||
// Draw the data into it.
|
||||
self.xcb_connection()
|
||||
.put_image(
|
||||
xproto::ImageFormat::Z_PIXMAP,
|
||||
pixmap.pixmap(),
|
||||
gc.gcontext(),
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
32,
|
||||
image,
|
||||
)?
|
||||
.ignore_error();
|
||||
drop(gc);
|
||||
|
||||
// Create the XRender picture.
|
||||
let picture = render::PictureWrapper::create_picture(
|
||||
self.xcb_connection(),
|
||||
pixmap.pixmap(),
|
||||
self.find_argb32_format()?,
|
||||
&Default::default(),
|
||||
)?;
|
||||
drop(pixmap);
|
||||
|
||||
// Create the cursor.
|
||||
let cursor = self.xcb_connection().generate_id()?;
|
||||
self.xcb_connection()
|
||||
.render_create_cursor(cursor, picture.picture(), hotspot_x, hotspot_y)?
|
||||
.check()?;
|
||||
|
||||
Ok(cursor)
|
||||
}
|
||||
|
||||
/// Find the render format that corresponds to ARGB32.
|
||||
fn find_argb32_format(&self) -> Result<render::Pictformat, X11Error> {
|
||||
macro_rules! direct {
|
||||
($format:expr, $shift_name:ident, $mask_name:ident, $shift:expr) => {{
|
||||
($format).direct.$shift_name == $shift && ($format).direct.$mask_name == 0xff
|
||||
}};
|
||||
if pixmap == 0 {
|
||||
panic!("failed to allocate pixmap for cursor");
|
||||
}
|
||||
|
||||
self.render_formats()
|
||||
.formats
|
||||
.iter()
|
||||
.find(|format| {
|
||||
format.type_ == render::PictType::DIRECT
|
||||
&& format.depth == 32
|
||||
&& direct!(format, red_shift, red_mask, 16)
|
||||
&& direct!(format, green_shift, green_mask, 8)
|
||||
&& direct!(format, blue_shift, blue_mask, 0)
|
||||
&& direct!(format, alpha_shift, alpha_mask, 24)
|
||||
})
|
||||
.ok_or(X11Error::NoArgb32Format)
|
||||
.map(|format| format.id)
|
||||
unsafe {
|
||||
// We don't care about this color, since it only fills bytes
|
||||
// in the pixmap which are not 0 in the mask.
|
||||
let mut dummy_color = MaybeUninit::uninit();
|
||||
let cursor = (self.xlib.XCreatePixmapCursor)(
|
||||
self.display,
|
||||
pixmap,
|
||||
pixmap,
|
||||
dummy_color.as_mut_ptr(),
|
||||
dummy_color.as_mut_ptr(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
(self.xlib.XFreePixmap)(self.display, pixmap);
|
||||
|
||||
cursor
|
||||
}
|
||||
}
|
||||
|
||||
fn create_empty_cursor(&self) -> Result<xproto::Cursor, X11Error> {
|
||||
self.create_cursor_from_image(1, 1, 0, 0, &[0, 0, 0, 0])
|
||||
}
|
||||
|
||||
fn get_cursor(&self, cursor: Option<CursorIcon>) -> Result<xproto::Cursor, X11Error> {
|
||||
fn get_cursor(&self, cursor: Option<CursorIcon>) -> ffi::Cursor {
|
||||
let cursor = match cursor {
|
||||
Some(cursor) => cursor,
|
||||
None => return self.create_empty_cursor(),
|
||||
};
|
||||
|
||||
let database = self.database();
|
||||
let handle = x11rb::cursor::Handle::new(
|
||||
self.xcb_connection(),
|
||||
self.default_screen_index(),
|
||||
&database,
|
||||
)?
|
||||
.reply()?;
|
||||
|
||||
let mut last_error = None;
|
||||
let mut xcursor = 0;
|
||||
for &name in iter::once(&cursor.name()).chain(cursor.alt_names().iter()) {
|
||||
match handle.load_cursor(self.xcb_connection(), name) {
|
||||
Ok(cursor) => return Ok(cursor),
|
||||
Err(err) => last_error = Some(err.into()),
|
||||
let name = CString::new(name).unwrap();
|
||||
xcursor = unsafe {
|
||||
(self.xcursor.XcursorLibraryLoadCursor)(
|
||||
self.display,
|
||||
name.as_ptr() as *const c_char,
|
||||
)
|
||||
};
|
||||
|
||||
if xcursor != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Err(last_error.unwrap())
|
||||
xcursor
|
||||
}
|
||||
|
||||
fn update_cursor(
|
||||
&self,
|
||||
window: xproto::Window,
|
||||
cursor: xproto::Cursor,
|
||||
) -> Result<(), X11Error> {
|
||||
fn update_cursor(&self, window: xproto::Window, cursor: ffi::Cursor) -> Result<(), X11Error> {
|
||||
self.xcb_connection()
|
||||
.change_window_attributes(
|
||||
window,
|
||||
&xproto::ChangeWindowAttributesAux::new().cursor(cursor),
|
||||
&xproto::ChangeWindowAttributesAux::new().cursor(cursor as xproto::Cursor),
|
||||
)?
|
||||
.ignore_error();
|
||||
|
||||
@@ -192,44 +123,51 @@ impl Eq for CustomCursor {}
|
||||
impl CustomCursor {
|
||||
pub(crate) fn new(
|
||||
event_loop: &ActiveEventLoop,
|
||||
mut cursor: PlatformCustomCursorSource,
|
||||
) -> Result<CustomCursor, RequestError> {
|
||||
// Reverse RGBA order to BGRA.
|
||||
cursor.0.rgba.chunks_mut(4).for_each(|chunk| {
|
||||
let chunk: &mut [u8; 4] = chunk.try_into().unwrap();
|
||||
chunk[0..3].reverse();
|
||||
|
||||
// Byteswap if we need to.
|
||||
if event_loop.xconn.needs_endian_swap() {
|
||||
let value = u32::from_ne_bytes(*chunk).swap_bytes();
|
||||
*chunk = value.to_ne_bytes();
|
||||
cursor: PlatformCustomCursorSource,
|
||||
) -> Result<CustomCursor, ExternalError> {
|
||||
unsafe {
|
||||
let ximage = (event_loop.xconn.xcursor.XcursorImageCreate)(
|
||||
cursor.0.width as i32,
|
||||
cursor.0.height as i32,
|
||||
);
|
||||
if ximage.is_null() {
|
||||
return Err(ExternalError::Os(os_error!(OsError::Misc(
|
||||
"`XcursorImageCreate` failed"
|
||||
))));
|
||||
}
|
||||
});
|
||||
(*ximage).xhot = cursor.0.hotspot_x as u32;
|
||||
(*ximage).yhot = cursor.0.hotspot_y as u32;
|
||||
(*ximage).delay = 0;
|
||||
|
||||
let cursor = event_loop
|
||||
.xconn
|
||||
.create_cursor_from_image(
|
||||
cursor.0.width,
|
||||
cursor.0.height,
|
||||
cursor.0.hotspot_x,
|
||||
cursor.0.hotspot_y,
|
||||
&cursor.0.rgba,
|
||||
)
|
||||
.map_err(|err| os_error!(err))?;
|
||||
let dst = slice::from_raw_parts_mut((*ximage).pixels, cursor.0.rgba.len() / 4);
|
||||
for (dst, chunk) in dst.iter_mut().zip(cursor.0.rgba.chunks_exact(4)) {
|
||||
*dst = (chunk[0] as u32) << 16
|
||||
| (chunk[1] as u32) << 8
|
||||
| (chunk[2] as u32)
|
||||
| (chunk[3] as u32) << 24;
|
||||
}
|
||||
|
||||
Ok(Self { inner: Arc::new(CustomCursorInner { xconn: event_loop.xconn.clone(), cursor }) })
|
||||
let cursor =
|
||||
(event_loop.xconn.xcursor.XcursorImageLoadCursor)(event_loop.xconn.display, ximage);
|
||||
(event_loop.xconn.xcursor.XcursorImageDestroy)(ximage);
|
||||
Ok(Self {
|
||||
inner: Arc::new(CustomCursorInner { xconn: event_loop.xconn.clone(), cursor }),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CustomCursorInner {
|
||||
xconn: Arc<XConnection>,
|
||||
cursor: xproto::Cursor,
|
||||
cursor: ffi::Cursor,
|
||||
}
|
||||
|
||||
impl Drop for CustomCursorInner {
|
||||
fn drop(&mut self) {
|
||||
self.xconn.xcb_connection().free_cursor(self.cursor).map(|r| r.ignore_error()).ok();
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFreeCursor)(self.xconn.display, self.cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ impl FrameExtentsHeuristic {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surface_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
|
||||
pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
|
||||
(
|
||||
width.saturating_add(
|
||||
self.frame_extents.left.saturating_add(self.frame_extents.right) as _
|
||||
@@ -98,7 +98,7 @@ impl XConnection {
|
||||
self.xcb_connection().translate_coordinates(window, root, 0, 0)?.reply().map_err(Into::into)
|
||||
}
|
||||
|
||||
// This is adequate for surface_size
|
||||
// This is adequate for inner_size
|
||||
pub fn get_geometry(
|
||||
&self,
|
||||
window: xproto::Window,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::ffi::CString;
|
||||
use std::mem::replace;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::*;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
@@ -22,8 +21,8 @@ use super::{
|
||||
};
|
||||
use crate::cursor::{Cursor, CustomCursor as RootCustomCursor};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{Event, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event::{Event, InnerSizeWriter, WindowEvent};
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::platform::x11::WindowType;
|
||||
use crate::platform_impl::x11::atoms::*;
|
||||
@@ -31,312 +30,14 @@ use crate::platform_impl::x11::{
|
||||
xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender, X11Error,
|
||||
};
|
||||
use crate::platform_impl::{
|
||||
common, Fullscreen, MonitorHandle as PlatformMonitorHandle, PlatformCustomCursor, PlatformIcon,
|
||||
VideoModeHandle as PlatformVideoModeHandle,
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor,
|
||||
PlatformIcon, VideoModeHandle as PlatformVideoModeHandle,
|
||||
};
|
||||
use crate::window::{
|
||||
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
|
||||
WindowButtons, WindowLevel,
|
||||
};
|
||||
|
||||
pub(crate) struct Window(Arc<UnownedWindow>);
|
||||
|
||||
impl Deref for Window {
|
||||
type Target = UnownedWindow;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &UnownedWindow {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new(
|
||||
event_loop: &ActiveEventLoop,
|
||||
attribs: WindowAttributes,
|
||||
) -> Result<Self, RequestError> {
|
||||
let window = Arc::new(UnownedWindow::new(event_loop, attribs)?);
|
||||
event_loop.windows.borrow_mut().insert(window.id(), Arc::downgrade(&window));
|
||||
Ok(Window(window))
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> crate::window::WindowId {
|
||||
crate::window::WindowId(self.0.id())
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
self.0.scale_factor()
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
self.0.request_redraw()
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {
|
||||
self.0.pre_present_notify()
|
||||
}
|
||||
|
||||
fn reset_dead_keys(&self) {
|
||||
common::xkb::reset_dead_keys();
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
self.0.inner_position()
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
self.0.outer_position()
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
self.0.set_outer_position(position)
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
self.0.surface_size()
|
||||
}
|
||||
|
||||
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
self.0.request_surface_size(size)
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
self.0.outer_size()
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.0.set_min_surface_size(min_size)
|
||||
}
|
||||
|
||||
fn set_max_surface_size(&self, max_size: Option<Size>) {
|
||||
self.0.set_max_surface_size(max_size)
|
||||
}
|
||||
|
||||
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
self.0.surface_resize_increments()
|
||||
}
|
||||
|
||||
fn set_surface_resize_increments(&self, increments: Option<Size>) {
|
||||
self.0.set_surface_resize_increments(increments)
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
self.0.set_title(title);
|
||||
}
|
||||
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
self.0.set_transparent(transparent);
|
||||
}
|
||||
|
||||
fn set_blur(&self, blur: bool) {
|
||||
self.0.set_blur(blur);
|
||||
}
|
||||
|
||||
fn set_visible(&self, visible: bool) {
|
||||
self.0.set_visible(visible);
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
self.0.is_visible()
|
||||
}
|
||||
|
||||
fn set_resizable(&self, resizable: bool) {
|
||||
self.0.set_resizable(resizable);
|
||||
}
|
||||
|
||||
fn is_resizable(&self) -> bool {
|
||||
self.0.is_resizable()
|
||||
}
|
||||
|
||||
fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||
self.0.set_enabled_buttons(buttons)
|
||||
}
|
||||
|
||||
fn enabled_buttons(&self) -> WindowButtons {
|
||||
self.0.enabled_buttons()
|
||||
}
|
||||
|
||||
fn set_minimized(&self, minimized: bool) {
|
||||
self.0.set_minimized(minimized)
|
||||
}
|
||||
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
self.0.is_minimized()
|
||||
}
|
||||
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
self.0.set_maximized(maximized)
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.0.is_maximized()
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, fullscreen: Option<crate::window::Fullscreen>) {
|
||||
self.0.set_fullscreen(fullscreen.map(Into::into))
|
||||
}
|
||||
|
||||
fn fullscreen(&self) -> Option<crate::window::Fullscreen> {
|
||||
self.0.fullscreen().map(Into::into)
|
||||
}
|
||||
|
||||
fn set_decorations(&self, decorations: bool) {
|
||||
self.0.set_decorations(decorations);
|
||||
}
|
||||
|
||||
fn is_decorated(&self) -> bool {
|
||||
self.0.is_decorated()
|
||||
}
|
||||
|
||||
fn set_window_level(&self, level: WindowLevel) {
|
||||
self.0.set_window_level(level);
|
||||
}
|
||||
|
||||
fn set_window_icon(&self, window_icon: Option<crate::window::Icon>) {
|
||||
self.0.set_window_icon(window_icon.map(|inner| inner.inner))
|
||||
}
|
||||
|
||||
fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
self.0.set_ime_cursor_area(position, size);
|
||||
}
|
||||
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
self.0.set_ime_allowed(allowed);
|
||||
}
|
||||
|
||||
fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.0.set_ime_purpose(purpose);
|
||||
}
|
||||
|
||||
fn focus_window(&self) {
|
||||
self.0.focus_window();
|
||||
}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
self.0.has_focus()
|
||||
}
|
||||
|
||||
fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
self.0.request_user_attention(request_type);
|
||||
}
|
||||
|
||||
fn set_theme(&self, theme: Option<Theme>) {
|
||||
self.0.set_theme(theme);
|
||||
}
|
||||
|
||||
fn theme(&self) -> Option<Theme> {
|
||||
self.0.theme()
|
||||
}
|
||||
|
||||
fn set_content_protected(&self, protected: bool) {
|
||||
self.0.set_content_protected(protected);
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
self.0.title()
|
||||
}
|
||||
|
||||
fn set_cursor(&self, cursor: Cursor) {
|
||||
self.0.set_cursor(cursor);
|
||||
}
|
||||
|
||||
fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
|
||||
self.0.set_cursor_position(position)
|
||||
}
|
||||
|
||||
fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
self.0.set_cursor_grab(mode)
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
self.0.set_cursor_visible(visible);
|
||||
}
|
||||
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
self.0.drag_window()
|
||||
}
|
||||
|
||||
fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
|
||||
self.0.drag_resize_window(direction)
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, position: Position) {
|
||||
self.0.show_window_menu(position);
|
||||
}
|
||||
|
||||
fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
|
||||
self.0.set_cursor_hittest(hittest)
|
||||
}
|
||||
|
||||
fn current_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
|
||||
self.0
|
||||
.current_monitor()
|
||||
.map(crate::platform_impl::MonitorHandle::X)
|
||||
.map(|inner| crate::monitor::MonitorHandle { inner })
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
|
||||
Box::new(
|
||||
self.0
|
||||
.available_monitors()
|
||||
.into_iter()
|
||||
.map(crate::platform_impl::MonitorHandle::X)
|
||||
.map(|inner| crate::monitor::MonitorHandle { inner }),
|
||||
)
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
|
||||
self.0
|
||||
.primary_monitor()
|
||||
.map(crate::platform_impl::MonitorHandle::X)
|
||||
.map(|inner| crate::monitor::MonitorHandle { inner })
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.0.raw_display_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.0.raw_window_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
let window = &self.0;
|
||||
let xconn = &window.xconn;
|
||||
|
||||
// Restore the video mode on drop.
|
||||
if let Some(Fullscreen::Exclusive(_)) = window.fullscreen() {
|
||||
window.set_fullscreen(None);
|
||||
}
|
||||
|
||||
if let Ok(c) = xconn.xcb_connection().destroy_window(window.id().0 as xproto::Window) {
|
||||
c.ignore_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SharedState {
|
||||
pub cursor_pos: Option<(f64, f64)>,
|
||||
@@ -356,9 +57,9 @@ pub struct SharedState {
|
||||
// Used to restore video mode after exiting fullscreen
|
||||
pub desktop_video_mode: Option<(randr::Crtc, randr::Mode)>,
|
||||
pub frame_extents: Option<util::FrameExtentsHeuristic>,
|
||||
pub min_surface_size: Option<Size>,
|
||||
pub max_surface_size: Option<Size>,
|
||||
pub surface_resize_increments: Option<Size>,
|
||||
pub min_inner_size: Option<Size>,
|
||||
pub max_inner_size: Option<Size>,
|
||||
pub resize_increments: Option<Size>,
|
||||
pub base_size: Option<Size>,
|
||||
pub visibility: Visibility,
|
||||
pub has_focus: bool,
|
||||
@@ -396,9 +97,9 @@ impl SharedState {
|
||||
restore_position: None,
|
||||
desktop_video_mode: None,
|
||||
frame_extents: None,
|
||||
min_surface_size: None,
|
||||
max_surface_size: None,
|
||||
surface_resize_increments: None,
|
||||
min_inner_size: None,
|
||||
max_inner_size: None,
|
||||
resize_increments: None,
|
||||
base_size: None,
|
||||
has_focus: false,
|
||||
cursor_hittest: None,
|
||||
@@ -427,9 +128,13 @@ pub struct UnownedWindow {
|
||||
redraw_sender: WakeSender<WindowId>,
|
||||
activation_sender: WakeSender<super::ActivationToken>,
|
||||
}
|
||||
|
||||
macro_rules! leap {
|
||||
($e:expr) => {
|
||||
$e.map_err(|err| os_error!(err))?
|
||||
match $e {
|
||||
Ok(x) => x,
|
||||
Err(err) => return Err(os_error!(OsError::XError(X11Error::from(err).into()))),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -438,7 +143,7 @@ impl UnownedWindow {
|
||||
pub(crate) fn new(
|
||||
event_loop: &ActiveEventLoop,
|
||||
window_attrs: WindowAttributes,
|
||||
) -> Result<UnownedWindow, RequestError> {
|
||||
) -> Result<UnownedWindow, RootOsError> {
|
||||
let xconn = &event_loop.xconn;
|
||||
let atoms = xconn.atoms();
|
||||
#[cfg(feature = "rwh_06")]
|
||||
@@ -475,10 +180,10 @@ impl UnownedWindow {
|
||||
|
||||
info!("Guessed window scale factor: {}", scale_factor);
|
||||
|
||||
let max_surface_size: Option<(u32, u32)> =
|
||||
window_attrs.max_surface_size.map(|size| size.to_physical::<u32>(scale_factor).into());
|
||||
let min_surface_size: Option<(u32, u32)> =
|
||||
window_attrs.min_surface_size.map(|size| size.to_physical::<u32>(scale_factor).into());
|
||||
let max_inner_size: Option<(u32, u32)> =
|
||||
window_attrs.max_inner_size.map(|size| size.to_physical::<u32>(scale_factor).into());
|
||||
let min_inner_size: Option<(u32, u32)> =
|
||||
window_attrs.min_inner_size.map(|size| size.to_physical::<u32>(scale_factor).into());
|
||||
|
||||
let position =
|
||||
window_attrs.position.map(|position| position.to_physical::<i32>(scale_factor));
|
||||
@@ -487,16 +192,16 @@ impl UnownedWindow {
|
||||
// x11 only applies constraints when the window is actively resized
|
||||
// by the user, so we have to manually apply the initial constraints
|
||||
let mut dimensions: (u32, u32) = window_attrs
|
||||
.surface_size
|
||||
.inner_size
|
||||
.map(|size| size.to_physical::<u32>(scale_factor))
|
||||
.or_else(|| Some((800, 600).into()))
|
||||
.map(Into::into)
|
||||
.unwrap();
|
||||
if let Some(max) = max_surface_size {
|
||||
if let Some(max) = max_inner_size {
|
||||
dimensions.0 = cmp::min(dimensions.0, max.0);
|
||||
dimensions.1 = cmp::min(dimensions.1, max.1);
|
||||
}
|
||||
if let Some(min) = min_surface_size {
|
||||
if let Some(min) = min_inner_size {
|
||||
dimensions.0 = cmp::max(dimensions.0, min.0);
|
||||
dimensions.1 = cmp::max(dimensions.1, min.1);
|
||||
}
|
||||
@@ -523,9 +228,10 @@ impl UnownedWindow {
|
||||
match window_attrs.platform_specific.x11.visual_id {
|
||||
Some(vi) => {
|
||||
// Find this specific visual.
|
||||
let (visualtype, depth) = all_visuals
|
||||
.find(|(visual, _)| visual.visual_id == vi)
|
||||
.ok_or_else(|| os_error!(X11Error::NoSuchVisual(vi)))?;
|
||||
let (visualtype, depth) =
|
||||
all_visuals.find(|(visual, _)| visual.visual_id == vi).ok_or_else(
|
||||
|| os_error!(OsError::XError(X11Error::NoSuchVisual(vi).into())),
|
||||
)?;
|
||||
|
||||
(Some(visualtype), depth, true)
|
||||
},
|
||||
@@ -712,24 +418,24 @@ impl UnownedWindow {
|
||||
.ignore_error();
|
||||
|
||||
// Set size hints.
|
||||
let mut min_surface_size =
|
||||
window_attrs.min_surface_size.map(|size| size.to_physical::<u32>(scale_factor));
|
||||
let mut max_surface_size =
|
||||
window_attrs.max_surface_size.map(|size| size.to_physical::<u32>(scale_factor));
|
||||
let mut min_inner_size =
|
||||
window_attrs.min_inner_size.map(|size| size.to_physical::<u32>(scale_factor));
|
||||
let mut max_inner_size =
|
||||
window_attrs.max_inner_size.map(|size| size.to_physical::<u32>(scale_factor));
|
||||
|
||||
if !window_attrs.resizable {
|
||||
if util::wm_name_is_one_of(&["Xfwm4"]) {
|
||||
warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
|
||||
} else {
|
||||
max_surface_size = Some(dimensions.into());
|
||||
min_surface_size = Some(dimensions.into());
|
||||
max_inner_size = Some(dimensions.into());
|
||||
min_inner_size = Some(dimensions.into());
|
||||
}
|
||||
}
|
||||
|
||||
let shared_state = window.shared_state.get_mut().unwrap();
|
||||
shared_state.min_surface_size = min_surface_size.map(Into::into);
|
||||
shared_state.max_surface_size = max_surface_size.map(Into::into);
|
||||
shared_state.surface_resize_increments = window_attrs.surface_resize_increments;
|
||||
shared_state.min_inner_size = min_inner_size.map(Into::into);
|
||||
shared_state.max_inner_size = max_inner_size.map(Into::into);
|
||||
shared_state.resize_increments = window_attrs.resize_increments;
|
||||
shared_state.base_size = window_attrs.platform_specific.x11.base_size;
|
||||
|
||||
let normal_hints = WmSizeHints {
|
||||
@@ -741,10 +447,10 @@ impl UnownedWindow {
|
||||
cast_dimension_to_hint(dimensions.0),
|
||||
cast_dimension_to_hint(dimensions.1),
|
||||
)),
|
||||
max_size: max_surface_size.map(cast_physical_size_to_hint),
|
||||
min_size: min_surface_size.map(cast_physical_size_to_hint),
|
||||
max_size: max_inner_size.map(cast_physical_size_to_hint),
|
||||
min_size: min_inner_size.map(cast_physical_size_to_hint),
|
||||
size_increment: window_attrs
|
||||
.surface_resize_increments
|
||||
.resize_increments
|
||||
.map(|size| cast_size_to_hint(size, scale_factor)),
|
||||
base_size: window_attrs
|
||||
.platform_specific
|
||||
@@ -823,7 +529,7 @@ impl UnownedWindow {
|
||||
&mut supported_ptr,
|
||||
);
|
||||
if supported_ptr == ffi::False {
|
||||
return Err(os_error!("`XkbSetDetectableAutoRepeat` failed").into());
|
||||
return Err(os_error!(OsError::Misc("`XkbSetDetectableAutoRepeat` failed")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -843,16 +549,14 @@ impl UnownedWindow {
|
||||
|
||||
// Try to create input context for the window.
|
||||
if let Some(ime) = event_loop.ime.as_ref() {
|
||||
ime.borrow_mut()
|
||||
.create_context(window.xwindow as ffi::Window, false)
|
||||
.map_err(|err| os_error!(err))?;
|
||||
let result = ime.borrow_mut().create_context(window.xwindow as ffi::Window, false);
|
||||
leap!(result);
|
||||
}
|
||||
|
||||
// These properties must be set after mapping
|
||||
if window_attrs.maximized {
|
||||
leap!(window.set_maximized_inner(window_attrs.maximized)).ignore_error();
|
||||
}
|
||||
|
||||
if window_attrs.fullscreen.is_some() {
|
||||
if let Some(flusher) =
|
||||
leap!(window
|
||||
@@ -885,7 +589,7 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
/// Embed this window into a parent window.
|
||||
pub(super) fn embed_window(&self) -> Result<(), RequestError> {
|
||||
pub(super) fn embed_window(&self) -> Result<(), RootOsError> {
|
||||
let atoms = self.xconn.atoms();
|
||||
leap!(leap!(self.xconn.change_property(
|
||||
self.xwindow,
|
||||
@@ -1207,7 +911,7 @@ impl UnownedWindow {
|
||||
// Check if the self is on this monitor
|
||||
let monitor = self.shared_state_lock().last_monitor.clone();
|
||||
if monitor.name == new_monitor.name {
|
||||
let (width, height) = self.surface_size_physical();
|
||||
let (width, height) = self.inner_size_physical();
|
||||
let (new_width, new_height) = self.adjust_for_dpi(
|
||||
// If we couldn't determine the previous scale
|
||||
// factor (e.g., because all monitors were closed
|
||||
@@ -1221,22 +925,22 @@ impl UnownedWindow {
|
||||
);
|
||||
|
||||
let window_id = crate::window::WindowId(self.id());
|
||||
let old_surface_size = PhysicalSize::new(width, height);
|
||||
let surface_size = Arc::new(Mutex::new(PhysicalSize::new(new_width, new_height)));
|
||||
let old_inner_size = PhysicalSize::new(width, height);
|
||||
let inner_size = Arc::new(Mutex::new(PhysicalSize::new(new_width, new_height)));
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor: new_monitor.scale_factor,
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&surface_size)),
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&inner_size)),
|
||||
},
|
||||
});
|
||||
|
||||
let new_surface_size = *surface_size.lock().unwrap();
|
||||
drop(surface_size);
|
||||
let new_inner_size = *inner_size.lock().unwrap();
|
||||
drop(inner_size);
|
||||
|
||||
if new_surface_size != old_surface_size {
|
||||
let (new_width, new_height) = new_surface_size.into();
|
||||
self.request_surface_size_physical(new_width, new_height);
|
||||
if new_inner_size != old_inner_size {
|
||||
let (new_width, new_height) = new_inner_size.into();
|
||||
self.request_inner_size_physical(new_width, new_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1497,7 +1201,7 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
let extents = self.shared_state_lock().frame_extents.clone();
|
||||
if let Some(extents) = extents {
|
||||
let (x, y) = self.inner_position_physical();
|
||||
@@ -1518,7 +1222,7 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
Ok(self.inner_position_physical().into())
|
||||
}
|
||||
|
||||
@@ -1556,7 +1260,7 @@ impl UnownedWindow {
|
||||
self.set_position_physical(x, y);
|
||||
}
|
||||
|
||||
pub(crate) fn surface_size_physical(&self) -> (u32, u32) {
|
||||
pub(crate) fn inner_size_physical(&self) -> (u32, u32) {
|
||||
// This should be okay to unwrap since the only error XGetGeometry can return
|
||||
// is BadWindow, and if the window handle is bad we have bigger problems.
|
||||
self.xconn
|
||||
@@ -1566,23 +1270,23 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
self.surface_size_physical().into()
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
self.inner_size_physical().into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
let extents = self.shared_state_lock().frame_extents.clone();
|
||||
if let Some(extents) = extents {
|
||||
let (width, height) = self.surface_size_physical();
|
||||
extents.surface_size_to_outer(width, height).into()
|
||||
let (width, height) = self.inner_size_physical();
|
||||
extents.inner_size_to_outer(width, height).into()
|
||||
} else {
|
||||
self.update_cached_frame_extents();
|
||||
self.outer_size()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn request_surface_size_physical(&self, width: u32, height: u32) {
|
||||
pub(crate) fn request_inner_size_physical(&self, width: u32, height: u32) {
|
||||
self.xconn
|
||||
.xcb_connection()
|
||||
.configure_window(
|
||||
@@ -1598,7 +1302,7 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let size = size.to_physical::<u32>(scale_factor).into();
|
||||
if !self.shared_state_lock().is_resizable {
|
||||
@@ -1608,7 +1312,7 @@ impl UnownedWindow {
|
||||
})
|
||||
.expect("Failed to call `XSetWMNormalHints`");
|
||||
}
|
||||
self.request_surface_size_physical(size.0 as u32, size.1 as u32);
|
||||
self.request_inner_size_physical(size.0 as u32, size.1 as u32);
|
||||
|
||||
None
|
||||
}
|
||||
@@ -1635,7 +1339,7 @@ impl UnownedWindow {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn set_min_surface_size_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||
pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.update_normal_hints(|normal_hints| {
|
||||
normal_hints.min_size =
|
||||
dimensions.map(|(w, h)| (cast_dimension_to_hint(w), cast_dimension_to_hint(h)))
|
||||
@@ -1644,14 +1348,14 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_surface_size(&self, dimensions: Option<Size>) {
|
||||
self.shared_state_lock().min_surface_size = dimensions;
|
||||
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
||||
self.shared_state_lock().min_inner_size = dimensions;
|
||||
let physical_dimensions =
|
||||
dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
|
||||
self.set_min_surface_size_physical(physical_dimensions);
|
||||
self.set_min_inner_size_physical(physical_dimensions);
|
||||
}
|
||||
|
||||
pub(crate) fn set_max_surface_size_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||
pub(crate) fn set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.update_normal_hints(|normal_hints| {
|
||||
normal_hints.max_size =
|
||||
dimensions.map(|(w, h)| (cast_dimension_to_hint(w), cast_dimension_to_hint(h)))
|
||||
@@ -1660,15 +1364,15 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_surface_size(&self, dimensions: Option<Size>) {
|
||||
self.shared_state_lock().max_surface_size = dimensions;
|
||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||
self.shared_state_lock().max_inner_size = dimensions;
|
||||
let physical_dimensions =
|
||||
dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
|
||||
self.set_max_surface_size_physical(physical_dimensions);
|
||||
self.set_max_inner_size_physical(physical_dimensions);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
WmSizeHints::get(
|
||||
self.xconn.xcb_connection(),
|
||||
self.xwindow as xproto::Window,
|
||||
@@ -1682,8 +1386,8 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_surface_resize_increments(&self, increments: Option<Size>) {
|
||||
self.shared_state_lock().surface_resize_increments = increments;
|
||||
pub fn set_resize_increments(&self, increments: Option<Size>) {
|
||||
self.shared_state_lock().resize_increments = increments;
|
||||
let physical_increments =
|
||||
increments.map(|increments| cast_size_to_hint(increments, self.scale_factor()));
|
||||
self.update_normal_hints(|hints| hints.size_increment = physical_increments)
|
||||
@@ -1701,15 +1405,14 @@ impl UnownedWindow {
|
||||
let scale_factor = new_scale_factor / old_scale_factor;
|
||||
self.update_normal_hints(|normal_hints| {
|
||||
let dpi_adjuster = |size: Size| -> (i32, i32) { cast_size_to_hint(size, scale_factor) };
|
||||
let max_size = shared_state.max_surface_size.map(dpi_adjuster);
|
||||
let min_size = shared_state.min_surface_size.map(dpi_adjuster);
|
||||
let surface_resize_increments =
|
||||
shared_state.surface_resize_increments.map(dpi_adjuster);
|
||||
let max_size = shared_state.max_inner_size.map(dpi_adjuster);
|
||||
let min_size = shared_state.min_inner_size.map(dpi_adjuster);
|
||||
let resize_increments = shared_state.resize_increments.map(dpi_adjuster);
|
||||
let base_size = shared_state.base_size.map(dpi_adjuster);
|
||||
|
||||
normal_hints.max_size = max_size;
|
||||
normal_hints.min_size = min_size;
|
||||
normal_hints.size_increment = surface_resize_increments;
|
||||
normal_hints.size_increment = resize_increments;
|
||||
normal_hints.base_size = base_size;
|
||||
})
|
||||
.expect("Failed to update normal hints");
|
||||
@@ -1732,9 +1435,9 @@ impl UnownedWindow {
|
||||
|
||||
let (min_size, max_size) = if resizable {
|
||||
let shared_state_lock = self.shared_state_lock();
|
||||
(shared_state_lock.min_surface_size, shared_state_lock.max_surface_size)
|
||||
(shared_state_lock.min_inner_size, shared_state_lock.max_inner_size)
|
||||
} else {
|
||||
let window_size = Some(Size::from(self.surface_size()));
|
||||
let window_size = Some(Size::from(self.inner_size()));
|
||||
(window_size, window_size)
|
||||
};
|
||||
self.shared_state_lock().is_resizable = resizable;
|
||||
@@ -1743,11 +1446,11 @@ impl UnownedWindow {
|
||||
.expect_then_ignore_error("Failed to call `XSetWMNormalHints`");
|
||||
|
||||
let scale_factor = self.scale_factor();
|
||||
let min_surface_size = min_size.map(|size| cast_size_to_hint(size, scale_factor));
|
||||
let max_surface_size = max_size.map(|size| cast_size_to_hint(size, scale_factor));
|
||||
let min_inner_size = min_size.map(|size| cast_size_to_hint(size, scale_factor));
|
||||
let max_inner_size = max_size.map(|size| cast_size_to_hint(size, scale_factor));
|
||||
self.update_normal_hints(|normal_hints| {
|
||||
normal_hints.min_size = min_surface_size;
|
||||
normal_hints.max_size = max_surface_size;
|
||||
normal_hints.min_size = min_inner_size;
|
||||
normal_hints.max_size = max_inner_size;
|
||||
})
|
||||
.expect("Failed to call `XSetWMNormalHints`");
|
||||
}
|
||||
@@ -1789,17 +1492,13 @@ impl UnownedWindow {
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
if SelectedCursor::Named(icon) != old_cursor && *self.cursor_visible.lock().unwrap()
|
||||
{
|
||||
if let Err(err) = self.xconn.set_cursor_icon(self.xwindow, Some(icon)) {
|
||||
tracing::error!("failed to set cursor icon: {err}");
|
||||
}
|
||||
self.xconn.set_cursor_icon(self.xwindow, Some(icon));
|
||||
}
|
||||
},
|
||||
Cursor::Custom(RootCustomCursor { inner: PlatformCustomCursor::X(cursor) }) => {
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
if *self.cursor_visible.lock().unwrap() {
|
||||
if let Err(err) = self.xconn.set_custom_cursor(self.xwindow, &cursor) {
|
||||
tracing::error!("failed to set window icon: {err}");
|
||||
}
|
||||
self.xconn.set_custom_cursor(self.xwindow, &cursor);
|
||||
}
|
||||
|
||||
*self.selected_cursor.lock().unwrap() = SelectedCursor::Custom(cursor);
|
||||
@@ -1812,7 +1511,7 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
let mut grabbed_lock = self.cursor_grabbed_mode.lock().unwrap();
|
||||
if mode == *grabbed_lock {
|
||||
return Ok(());
|
||||
@@ -1826,10 +1525,9 @@ impl UnownedWindow {
|
||||
.expect_then_ignore_error("Failed to call `xcb_ungrab_pointer`");
|
||||
|
||||
let result = match mode {
|
||||
CursorGrabMode::None => self
|
||||
.xconn
|
||||
.flush_requests()
|
||||
.map_err(|err| RequestError::Os(os_error!(X11Error::Xlib(err)))),
|
||||
CursorGrabMode::None => self.xconn.flush_requests().map_err(|err| {
|
||||
ExternalError::Os(os_error!(OsError::XError(X11Error::Xlib(err).into())))
|
||||
}),
|
||||
CursorGrabMode::Confined => {
|
||||
let result = {
|
||||
self.xconn
|
||||
@@ -1876,12 +1574,10 @@ impl UnownedWindow {
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.map_err(|err| RequestError::Os(os_error!(err)))
|
||||
.map_err(|err| ExternalError::Os(os_error!(OsError::Misc(err))))
|
||||
},
|
||||
CursorGrabMode::Locked => {
|
||||
return Err(
|
||||
NotSupportedError::new("locked cursor is not implemented on X11").into()
|
||||
);
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1903,18 +1599,16 @@ impl UnownedWindow {
|
||||
if visible { Some((*self.selected_cursor.lock().unwrap()).clone()) } else { None };
|
||||
*visible_lock = visible;
|
||||
drop(visible_lock);
|
||||
let result = match cursor {
|
||||
match cursor {
|
||||
Some(SelectedCursor::Custom(cursor)) => {
|
||||
self.xconn.set_custom_cursor(self.xwindow, &cursor)
|
||||
self.xconn.set_custom_cursor(self.xwindow, &cursor);
|
||||
},
|
||||
Some(SelectedCursor::Named(cursor)) => {
|
||||
self.xconn.set_cursor_icon(self.xwindow, Some(cursor))
|
||||
self.xconn.set_cursor_icon(self.xwindow, Some(cursor));
|
||||
},
|
||||
None => {
|
||||
self.xconn.set_cursor_icon(self.xwindow, None);
|
||||
},
|
||||
None => self.xconn.set_cursor_icon(self.xwindow, None),
|
||||
};
|
||||
|
||||
if let Err(err) = result {
|
||||
tracing::error!("failed to set cursor icon: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1923,26 +1617,31 @@ impl UnownedWindow {
|
||||
self.shared_state_lock().last_monitor.scale_factor
|
||||
}
|
||||
|
||||
pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), RequestError> {
|
||||
self.xconn
|
||||
.xcb_connection()
|
||||
.warp_pointer(x11rb::NONE, self.xwindow, 0, 0, 0, 0, x as _, y as _)
|
||||
.map_err(|err| os_error!(X11Error::from(err)))?;
|
||||
self.xconn.flush_requests().map_err(|err| os_error!(X11Error::Xlib(err)))?;
|
||||
Ok(())
|
||||
pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> {
|
||||
{
|
||||
self.xconn
|
||||
.xcb_connection()
|
||||
.warp_pointer(x11rb::NONE, self.xwindow, 0, 0, 0, 0, x as _, y as _)
|
||||
.map_err(|e| {
|
||||
ExternalError::Os(os_error!(OsError::XError(X11Error::from(e).into())))
|
||||
})?;
|
||||
self.xconn.flush_requests().map_err(|e| {
|
||||
ExternalError::Os(os_error!(OsError::XError(X11Error::Xlib(e).into())))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
|
||||
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
|
||||
let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
|
||||
self.set_cursor_position_physical(x, y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
let mut rectangles: Vec<Rectangle> = Vec::new();
|
||||
if hittest {
|
||||
let size = self.surface_size();
|
||||
let size = self.inner_size();
|
||||
rectangles.push(Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
@@ -1951,17 +1650,17 @@ impl UnownedWindow {
|
||||
})
|
||||
}
|
||||
let region = RegionWrapper::create_region(self.xconn.xcb_connection(), &rectangles)
|
||||
.map_err(|_e| RequestError::Ignored)?;
|
||||
.map_err(|_e| ExternalError::Ignored)?;
|
||||
self.xconn
|
||||
.xcb_connection()
|
||||
.xfixes_set_window_shape_region(self.xwindow, SK::INPUT, 0, 0, region.region())
|
||||
.map_err(|_e| RequestError::Ignored)?;
|
||||
.map_err(|_e| ExternalError::Ignored)?;
|
||||
self.shared_state_lock().cursor_hittest = Some(hittest);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves the window while it is being dragged.
|
||||
pub fn drag_window(&self) -> Result<(), RequestError> {
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
self.drag_initiate(util::MOVERESIZE_MOVE)
|
||||
}
|
||||
|
||||
@@ -1969,7 +1668,7 @@ impl UnownedWindow {
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
/// Resizes the window while it is being dragged.
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
self.drag_initiate(match direction {
|
||||
ResizeDirection::East => util::MOVERESIZE_RIGHT,
|
||||
ResizeDirection::North => util::MOVERESIZE_TOP,
|
||||
@@ -1983,13 +1682,13 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
/// Initiates a drag operation while the left mouse button is pressed.
|
||||
fn drag_initiate(&self, action: isize) -> Result<(), RequestError> {
|
||||
fn drag_initiate(&self, action: isize) -> Result<(), ExternalError> {
|
||||
let pointer = self
|
||||
.xconn
|
||||
.query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER)
|
||||
.map_err(|err| os_error!(err))?;
|
||||
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err.into()))))?;
|
||||
|
||||
let window_position = self.inner_position()?;
|
||||
let window = self.inner_position().map_err(ExternalError::NotSupported)?;
|
||||
|
||||
let atoms = self.xconn.atoms();
|
||||
let message = atoms[_NET_WM_MOVERESIZE];
|
||||
@@ -2000,9 +1699,13 @@ impl UnownedWindow {
|
||||
self.xconn
|
||||
.xcb_connection()
|
||||
.ungrab_pointer(x11rb::CURRENT_TIME)
|
||||
.map_err(|err| os_error!(X11Error::from(err)))?
|
||||
.map_err(|err| {
|
||||
ExternalError::Os(os_error!(OsError::XError(X11Error::from(err).into())))
|
||||
})?
|
||||
.ignore_error();
|
||||
self.xconn.flush_requests().map_err(|err| os_error!(X11Error::Xlib(err)))?;
|
||||
self.xconn.flush_requests().map_err(|err| {
|
||||
ExternalError::Os(os_error!(OsError::XError(X11Error::Xlib(err).into())))
|
||||
})?;
|
||||
*grabbed_lock = CursorGrabMode::None;
|
||||
|
||||
// we keep the lock until we are done
|
||||
@@ -2016,18 +1719,18 @@ impl UnownedWindow {
|
||||
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
|
||||
),
|
||||
[
|
||||
(window_position.x + xinput_fp1616_to_float(pointer.win_x) as i32) as u32,
|
||||
(window_position.y + xinput_fp1616_to_float(pointer.win_y) as i32) as u32,
|
||||
(window.x + xinput_fp1616_to_float(pointer.win_x) as i32) as u32,
|
||||
(window.y + xinput_fp1616_to_float(pointer.win_y) as i32) as u32,
|
||||
action.try_into().unwrap(),
|
||||
1, // Button 1
|
||||
1,
|
||||
],
|
||||
)
|
||||
.map_err(|err| os_error!(err))?;
|
||||
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err.into()))))?;
|
||||
|
||||
self.xconn.flush_requests().map_err(|err| os_error!(X11Error::Xlib(err)))?;
|
||||
|
||||
Ok(())
|
||||
self.xconn.flush_requests().map_err(|err| {
|
||||
ExternalError::Os(os_error!(OsError::XError(X11Error::Xlib(err).into())))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -2126,7 +1829,7 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
|
||||
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||
let serial = AsyncRequestSerial::get();
|
||||
self.activation_sender.send((self.id(), serial));
|
||||
Ok(serial)
|
||||
|
||||
@@ -6,7 +6,6 @@ use std::{fmt, ptr};
|
||||
|
||||
use x11rb::connection::Connection;
|
||||
use x11rb::protocol::randr::ConnectionExt as _;
|
||||
use x11rb::protocol::render;
|
||||
use x11rb::protocol::xproto::{self, ConnectionExt};
|
||||
use x11rb::resource_manager;
|
||||
use x11rb::xcb_ffi::XCBConnection;
|
||||
@@ -19,6 +18,7 @@ use crate::window::CursorIcon;
|
||||
/// A connection to an X server.
|
||||
pub struct XConnection {
|
||||
pub xlib: ffi::Xlib,
|
||||
pub xcursor: ffi::Xcursor,
|
||||
|
||||
// TODO(notgull): I'd like to remove this, but apparently Xlib and Xinput2 are tied together
|
||||
// for some reason.
|
||||
@@ -55,11 +55,8 @@ pub struct XConnection {
|
||||
/// Atom for the XSettings screen.
|
||||
xsettings_screen: Option<xproto::Atom>,
|
||||
|
||||
/// XRender format information.
|
||||
render_formats: render::QueryPictFormatsReply,
|
||||
|
||||
pub latest_error: Mutex<Option<XError>>,
|
||||
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, xproto::Cursor>>,
|
||||
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for XConnection {}
|
||||
@@ -72,6 +69,7 @@ impl XConnection {
|
||||
pub fn new(error_handler: XErrorHandler) -> Result<XConnection, XNotSupported> {
|
||||
// opening the libraries
|
||||
let xlib = ffi::Xlib::open()?;
|
||||
let xcursor = ffi::Xcursor::open()?;
|
||||
let xlib_xcb = ffi::Xlib_xcb::open()?;
|
||||
let xinput2 = ffi::XInput2::open()?;
|
||||
|
||||
@@ -120,22 +118,15 @@ impl XConnection {
|
||||
tracing::warn!("error setting XSETTINGS; Xft options won't reload automatically")
|
||||
}
|
||||
|
||||
// Start getting the XRender formats.
|
||||
let formats_cookie = render::query_pict_formats(&xcb)
|
||||
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
||||
|
||||
// Fetch atoms.
|
||||
let atoms = Atoms::new(&xcb)
|
||||
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?
|
||||
.reply()
|
||||
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
||||
|
||||
// Finish getting everything else.
|
||||
let formats =
|
||||
formats_cookie.reply().map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
||||
|
||||
Ok(XConnection {
|
||||
xlib,
|
||||
xcursor,
|
||||
xinput2,
|
||||
display,
|
||||
xcb: Some(xcb),
|
||||
@@ -147,7 +138,6 @@ impl XConnection {
|
||||
database: RwLock::new(database),
|
||||
cursor_cache: Default::default(),
|
||||
randr_version: (randr_version.major_version, randr_version.minor_version),
|
||||
render_formats: formats,
|
||||
xsettings_screen,
|
||||
})
|
||||
}
|
||||
@@ -267,23 +257,6 @@ impl XConnection {
|
||||
pub fn xsettings_screen(&self) -> Option<xproto::Atom> {
|
||||
self.xsettings_screen
|
||||
}
|
||||
|
||||
/// Get the data containing our rendering formats.
|
||||
#[inline]
|
||||
pub fn render_formats(&self) -> &render::QueryPictFormatsReply {
|
||||
&self.render_formats
|
||||
}
|
||||
|
||||
/// Do we need to do an endian swap?
|
||||
#[inline]
|
||||
pub fn needs_endian_swap(&self) -> bool {
|
||||
#[cfg(target_endian = "big")]
|
||||
let endian = xproto::ImageOrder::MSB_FIRST;
|
||||
#[cfg(not(target_endian = "big"))]
|
||||
let endian = xproto::ImageOrder::LSB_FIRST;
|
||||
|
||||
self.xcb_connection().setup().image_byte_order != endian
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for XConnection {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::Any;
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
@@ -12,21 +13,19 @@ use orbclient::{
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use super::{
|
||||
DeviceId, KeyEventExtra, MonitorHandle, PlatformSpecificEventLoopAttributes, RedoxSocket,
|
||||
TimeSocket, WindowId, WindowProperties,
|
||||
DeviceId, KeyEventExtra, MonitorHandle, OsError, PlatformSpecificEventLoopAttributes,
|
||||
RedoxSocket, TimeSocket, WindowId, WindowProperties,
|
||||
};
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use crate::error::{EventLoopError, ExternalError, NotSupportedError};
|
||||
use crate::event::{self, Ime, Modifiers, StartCause};
|
||||
use crate::event_loop::{self, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
|
||||
use crate::keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
|
||||
PhysicalKey,
|
||||
};
|
||||
use crate::platform_impl::Window;
|
||||
use crate::window::{
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
|
||||
WindowId as RootWindowId,
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId as RootWindowId,
|
||||
};
|
||||
|
||||
fn convert_scancode(scancode: u8) -> (PhysicalKey, Option<NamedKey>) {
|
||||
@@ -284,11 +283,17 @@ impl EventLoop {
|
||||
// events.
|
||||
let (user_events_sender, user_events_receiver) = mpsc::sync_channel(1);
|
||||
|
||||
let event_socket =
|
||||
Arc::new(RedoxSocket::event().map_err(|error| os_error!(format!("{error}")))?);
|
||||
let event_socket = Arc::new(
|
||||
RedoxSocket::event()
|
||||
.map_err(OsError::new)
|
||||
.map_err(|error| EventLoopError::Os(os_error!(error)))?,
|
||||
);
|
||||
|
||||
let wake_socket =
|
||||
Arc::new(TimeSocket::open().map_err(|error| os_error!(format!("{error}")))?);
|
||||
let wake_socket = Arc::new(
|
||||
TimeSocket::open()
|
||||
.map_err(OsError::new)
|
||||
.map_err(|error| EventLoopError::Os(os_error!(error)))?,
|
||||
);
|
||||
|
||||
event_socket
|
||||
.write(&syscall::Event {
|
||||
@@ -296,7 +301,8 @@ impl EventLoop {
|
||||
flags: syscall::EventFlags::EVENT_READ,
|
||||
data: wake_socket.0.fd,
|
||||
})
|
||||
.map_err(|error| os_error!(format!("{error}")))?;
|
||||
.map_err(OsError::new)
|
||||
.map_err(|error| EventLoopError::Os(os_error!(error)))?;
|
||||
|
||||
Ok(Self {
|
||||
windows: Vec::new(),
|
||||
@@ -469,7 +475,7 @@ impl EventLoop {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::SurfaceResized((width, height).into()),
|
||||
event::WindowEvent::Resized((width, height).into()),
|
||||
);
|
||||
|
||||
// Acknowledge resize after event loop.
|
||||
@@ -516,7 +522,7 @@ impl EventLoop {
|
||||
let window_id = RootWindowId(window_id);
|
||||
|
||||
// Send resize event on create to indicate first size.
|
||||
let event = event::WindowEvent::SurfaceResized((properties.w, properties.h).into());
|
||||
let event = event::WindowEvent::Resized((properties.w, properties.h).into());
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
|
||||
// Send moved event on create to indicate first position.
|
||||
@@ -724,15 +730,16 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: crate::window::WindowAttributes,
|
||||
) -> Result<Box<dyn CoreWindow>, RequestError> {
|
||||
Ok(Box::new(Window::new(self, window_attributes)?))
|
||||
) -> Result<crate::window::Window, crate::error::OsError> {
|
||||
let window = crate::platform_impl::Window::new(self, window_attributes)?;
|
||||
Ok(crate::window::Window { window })
|
||||
}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
_: CustomCursorSource,
|
||||
) -> Result<RootCustomCursor, RequestError> {
|
||||
Err(NotSupportedError::new("create_custom_cursor is not supported").into())
|
||||
) -> Result<RootCustomCursor, ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
|
||||
@@ -771,6 +778,10 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
event_loop::OwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#![cfg(target_os = "redox")]
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::num::{NonZeroU16, NonZeroU32};
|
||||
use std::{fmt, str};
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
||||
@@ -13,11 +15,6 @@ mod event_loop;
|
||||
pub use self::window::Window;
|
||||
mod window;
|
||||
|
||||
pub(crate) use crate::cursor::{
|
||||
NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource,
|
||||
};
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
|
||||
struct RedoxSocket {
|
||||
fd: usize,
|
||||
}
|
||||
@@ -176,6 +173,26 @@ impl<'a> fmt::Display for WindowProperties<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OsError(Arc<syscall::Error>);
|
||||
|
||||
impl OsError {
|
||||
fn new(error: syscall::Error) -> Self {
|
||||
Self(Arc::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for OsError {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
self.0.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use crate::cursor::{
|
||||
NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource,
|
||||
};
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct MonitorHandle;
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, TimeSocket, WindowId, WindowProperties};
|
||||
use super::{
|
||||
ActiveEventLoop, MonitorHandle, OsError, RedoxSocket, TimeSocket, WindowId, WindowProperties,
|
||||
};
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
use crate::window::{self, Fullscreen, ImePurpose, Window as CoreWindow, WindowId as CoreWindowId};
|
||||
use crate::platform_impl::Fullscreen;
|
||||
use crate::window::ImePurpose;
|
||||
use crate::{error, window};
|
||||
|
||||
// These values match the values uses in the `window_new` function in orbital:
|
||||
// https://gitlab.redox-os.org/redox-os/orbital/-/blob/master/src/scheme.rs
|
||||
@@ -30,7 +32,7 @@ impl Window {
|
||||
pub(crate) fn new(
|
||||
el: &ActiveEventLoop,
|
||||
attrs: window::WindowAttributes,
|
||||
) -> Result<Self, RequestError> {
|
||||
) -> Result<Self, error::OsError> {
|
||||
let scale = MonitorHandle.scale_factor();
|
||||
|
||||
let (x, y) = if let Some(pos) = attrs.position {
|
||||
@@ -40,13 +42,13 @@ impl Window {
|
||||
(-1, -1)
|
||||
};
|
||||
|
||||
let (w, h): (u32, u32) = if let Some(size) = attrs.surface_size {
|
||||
let (w, h): (u32, u32) = if let Some(size) = attrs.inner_size {
|
||||
size.to_physical::<u32>(scale).into()
|
||||
} else {
|
||||
(1024, 768)
|
||||
};
|
||||
|
||||
// TODO: min/max surface_size
|
||||
// TODO: min/max inner_size
|
||||
|
||||
// Async by default.
|
||||
let mut flag_str = ORBITAL_FLAG_ASYNC.to_string();
|
||||
@@ -123,65 +125,61 @@ impl Window {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_flag(&self, flag: char) -> Result<bool, RequestError> {
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) {
|
||||
f(self)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Self) -> R + Send) -> R {
|
||||
f(self)
|
||||
}
|
||||
|
||||
fn get_flag(&self, flag: char) -> Result<bool, error::ExternalError> {
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
let path = self.window_socket.fpath(&mut buf).map_err(|err| os_error!(format!("{err}")))?;
|
||||
let path = self
|
||||
.window_socket
|
||||
.fpath(&mut buf)
|
||||
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
|
||||
let properties = WindowProperties::new(path);
|
||||
Ok(properties.flags.contains(flag))
|
||||
}
|
||||
|
||||
fn set_flag(&self, flag: char, value: bool) -> Result<(), RequestError> {
|
||||
fn set_flag(&self, flag: char, value: bool) -> Result<(), error::ExternalError> {
|
||||
self.window_socket
|
||||
.write(format!("F,{flag},{}", if value { 1 } else { 0 }).as_bytes())
|
||||
.map_err(|err| os_error!(format!("{err}")))?;
|
||||
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
let handle = rwh_06::OrbitalWindowHandle::new({
|
||||
let window = self.window_socket.fd as *mut _;
|
||||
std::ptr::NonNull::new(window).expect("orbital fd should never be null")
|
||||
});
|
||||
Ok(rwh_06::RawWindowHandle::Orbital(handle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> CoreWindowId {
|
||||
CoreWindowId(WindowId { fd: self.window_socket.fd as u64 })
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId { fd: self.window_socket.fd as u64 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
Some(CoreMonitorHandle { inner: MonitorHandle })
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(MonitorHandle)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
|
||||
Box::new(vec![CoreMonitorHandle { inner: MonitorHandle }].into_iter())
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
let mut v = VecDeque::with_capacity(1);
|
||||
v.push_back(MonitorHandle);
|
||||
v
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
Some(CoreMonitorHandle { inner: MonitorHandle })
|
||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(MonitorHandle)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn scale_factor(&self) -> f64 {
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
MonitorHandle.scale_factor()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn request_redraw(&self) {
|
||||
let window_id = self.id().0;
|
||||
pub fn request_redraw(&self) {
|
||||
let window_id = self.id();
|
||||
let mut redraws = self.redraws.lock().unwrap();
|
||||
if !redraws.contains(&window_id) {
|
||||
redraws.push_back(window_id);
|
||||
@@ -191,15 +189,15 @@ impl CoreWindow for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pre_present_notify(&self) {}
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
#[inline]
|
||||
fn reset_dead_keys(&self) {
|
||||
pub fn reset_dead_keys(&self) {
|
||||
// TODO?
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
let path = self.window_socket.fpath(&mut buf).expect("failed to read properties");
|
||||
let properties = WindowProperties::new(path);
|
||||
@@ -207,20 +205,20 @@ impl CoreWindow for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
|
||||
// TODO: adjust for window decorations
|
||||
self.inner_position()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
pub fn set_outer_position(&self, position: Position) {
|
||||
// TODO: adjust for window decorations
|
||||
let (x, y): (i32, i32) = position.to_physical::<i32>(self.scale_factor()).into();
|
||||
self.window_socket.write(format!("P,{x},{y}").as_bytes()).expect("failed to set position");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
let path = self.window_socket.fpath(&mut buf).expect("failed to read properties");
|
||||
let properties = WindowProperties::new(path);
|
||||
@@ -228,26 +226,26 @@ impl CoreWindow for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let (w, h): (u32, u32) = size.to_physical::<u32>(self.scale_factor()).into();
|
||||
self.window_socket.write(format!("S,{w},{h}").as_bytes()).expect("failed to set size");
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
// TODO: adjust for window decorations
|
||||
self.surface_size()
|
||||
self.inner_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_min_surface_size(&self, _: Option<Size>) {}
|
||||
pub fn set_min_inner_size(&self, _: Option<Size>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_max_surface_size(&self, _: Option<Size>) {}
|
||||
pub fn set_max_inner_size(&self, _: Option<Size>) {}
|
||||
|
||||
#[inline]
|
||||
fn title(&self) -> String {
|
||||
pub fn title(&self) -> String {
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
let path = self.window_socket.fpath(&mut buf).expect("failed to read properties");
|
||||
let properties = WindowProperties::new(path);
|
||||
@@ -255,82 +253,84 @@ impl CoreWindow for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_title(&self, title: &str) {
|
||||
pub fn set_title(&self, title: &str) {
|
||||
self.window_socket.write(format!("T,{title}").as_bytes()).expect("failed to set title");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
pub fn set_transparent(&self, transparent: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_TRANSPARENT, transparent);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_blur(&self, _blur: bool) {}
|
||||
pub fn set_blur(&self, _blur: bool) {}
|
||||
|
||||
#[inline]
|
||||
fn set_visible(&self, visible: bool) {
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_HIDDEN, !visible);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
pub fn is_visible(&self) -> Option<bool> {
|
||||
Some(!self.get_flag(ORBITAL_FLAG_HIDDEN).unwrap_or(false))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_surface_resize_increments(&self, _increments: Option<Size>) {}
|
||||
pub fn set_resize_increments(&self, _increments: Option<Size>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_resizable(&self, resizeable: bool) {
|
||||
pub fn set_resizable(&self, resizeable: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_RESIZABLE, resizeable);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_resizable(&self) -> bool {
|
||||
pub fn is_resizable(&self) -> bool {
|
||||
self.get_flag(ORBITAL_FLAG_RESIZABLE).unwrap_or(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_minimized(&self, _minimized: bool) {}
|
||||
pub fn set_minimized(&self, _minimized: bool) {}
|
||||
|
||||
#[inline]
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
pub fn is_minimized(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_MAXIMIZED, maximized);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_maximized(&self) -> bool {
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
self.get_flag(ORBITAL_FLAG_MAXIMIZED).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, _monitor: Option<Fullscreen>) {}
|
||||
#[inline]
|
||||
pub(crate) fn set_fullscreen(&self, _monitor: Option<Fullscreen>) {}
|
||||
|
||||
fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
#[inline]
|
||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_decorations(&self, decorations: bool) {
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_BORDERLESS, !decorations);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_decorated(&self) -> bool {
|
||||
pub fn is_decorated(&self) -> bool {
|
||||
!self.get_flag(ORBITAL_FLAG_BORDERLESS).unwrap_or(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_window_level(&self, level: window::WindowLevel) {
|
||||
pub fn set_window_level(&self, level: window::WindowLevel) {
|
||||
match level {
|
||||
window::WindowLevel::AlwaysOnBottom => {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_BACK, true);
|
||||
@@ -346,33 +346,36 @@ impl CoreWindow for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
|
||||
pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
|
||||
pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_allowed(&self, _allowed: bool) {}
|
||||
pub fn set_ime_allowed(&self, _allowed: bool) {}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_purpose(&self, _purpose: ImePurpose) {}
|
||||
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}
|
||||
|
||||
#[inline]
|
||||
fn focus_window(&self) {}
|
||||
pub fn focus_window(&self) {}
|
||||
|
||||
#[inline]
|
||||
fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
|
||||
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor(&self, _: Cursor) {}
|
||||
pub fn set_cursor(&self, _: Cursor) {}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor_position(&self, _: Position) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("set_cursor_position is not supported").into())
|
||||
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(error::NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor_grab(&self, mode: window::CursorGrabMode) -> Result<(), RequestError> {
|
||||
pub fn set_cursor_grab(
|
||||
&self,
|
||||
mode: window::CursorGrabMode,
|
||||
) -> Result<(), error::ExternalError> {
|
||||
let (grab, relative) = match mode {
|
||||
window::CursorGrabMode::None => (false, false),
|
||||
window::CursorGrabMode::Confined => (true, false),
|
||||
@@ -380,26 +383,31 @@ impl CoreWindow for Window {
|
||||
};
|
||||
self.window_socket
|
||||
.write(format!("M,G,{}", if grab { 1 } else { 0 }).as_bytes())
|
||||
.map_err(|err| os_error!(format!("{err}")))?;
|
||||
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
|
||||
self.window_socket
|
||||
.write(format!("M,R,{}", if relative { 1 } else { 0 }).as_bytes())
|
||||
.map_err(|err| os_error!(format!("{err}")))?;
|
||||
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
let _ = self.window_socket.write(format!("M,C,{}", if visible { 1 } else { 0 }).as_bytes());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
self.window_socket.write(b"D").map_err(|err| os_error!(format!("{err}")))?;
|
||||
pub fn drag_window(&self) -> Result<(), error::ExternalError> {
|
||||
self.window_socket
|
||||
.write(b"D")
|
||||
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn drag_resize_window(&self, direction: window::ResizeDirection) -> Result<(), RequestError> {
|
||||
pub fn drag_resize_window(
|
||||
&self,
|
||||
direction: window::ResizeDirection,
|
||||
) -> Result<(), error::ExternalError> {
|
||||
let arg = match direction {
|
||||
window::ResizeDirection::East => "R",
|
||||
window::ResizeDirection::North => "T",
|
||||
@@ -412,73 +420,65 @@ impl CoreWindow for Window {
|
||||
};
|
||||
self.window_socket
|
||||
.write(format!("D,{}", arg).as_bytes())
|
||||
.map_err(|err| os_error!(format!("{err}")))?;
|
||||
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn show_window_menu(&self, _position: Position) {}
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("set_cursor_hittest is not supported").into())
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(error::NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
let handle = rwh_06::OrbitalWindowHandle::new({
|
||||
let window = self.window_socket.fd as *mut _;
|
||||
std::ptr::NonNull::new(window).expect("orbital fd should never be null")
|
||||
});
|
||||
Ok(rwh_06::RawWindowHandle::Orbital(handle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_enabled_buttons(&self, _buttons: window::WindowButtons) {}
|
||||
pub fn set_enabled_buttons(&self, _buttons: window::WindowButtons) {}
|
||||
|
||||
#[inline]
|
||||
fn enabled_buttons(&self) -> window::WindowButtons {
|
||||
pub fn enabled_buttons(&self) -> window::WindowButtons {
|
||||
window::WindowButtons::all()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn theme(&self) -> Option<window::Theme> {
|
||||
pub fn theme(&self) -> Option<window::Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_focus(&self) -> bool {
|
||||
pub fn has_focus(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_theme(&self, _theme: Option<window::Theme>) {}
|
||||
pub fn set_theme(&self, _theme: Option<window::Theme>) {}
|
||||
|
||||
fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
pub fn set_content_protected(&self, _protected: bool) {}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
{
|
||||
let mut destroys = self.destroys.lock().unwrap();
|
||||
destroys.push_back(self.id().0);
|
||||
destroys.push_back(self.id());
|
||||
}
|
||||
|
||||
self.wake_socket.wake().unwrap();
|
||||
|
||||
@@ -542,8 +542,8 @@ fn from_rgba(
|
||||
//
|
||||
// We call `createImageBitmap()` before spawning the future,
|
||||
// to not have to clone the image buffer.
|
||||
let options = ImageBitmapOptions::new();
|
||||
options.set_premultiply_alpha(PremultiplyAlpha::None);
|
||||
let mut options = ImageBitmapOptions::new();
|
||||
options.premultiply_alpha(PremultiplyAlpha::None);
|
||||
let bitmap = JsFuture::from(
|
||||
window
|
||||
.create_image_bitmap_with_image_data_and_image_bitmap_options(&image_data, &options)
|
||||
|
||||
@@ -4,7 +4,8 @@ use std::iter;
|
||||
use std::ops::Deref;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
use js_sys::Function;
|
||||
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Document, KeyboardEvent, Navigator, PageTransitionEvent, PointerEvent, WheelEvent};
|
||||
use web_time::{Duration, Instant};
|
||||
@@ -494,8 +495,14 @@ impl Shared {
|
||||
if let Ok(RunnerEnum::Running(_)) =
|
||||
self.0.runner.try_borrow().as_ref().map(Deref::deref)
|
||||
{
|
||||
self.window().queue_microtask(
|
||||
&Closure::once_into_js({
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_name = queueMicrotask)]
|
||||
fn queue_microtask(task: Function);
|
||||
}
|
||||
|
||||
queue_microtask(
|
||||
Closure::once_into_js({
|
||||
let this = Rc::downgrade(&self.0);
|
||||
move || {
|
||||
if let Some(shared) = this.upgrade() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::Any;
|
||||
use std::cell::Cell;
|
||||
use std::clone::Clone;
|
||||
use std::iter;
|
||||
@@ -11,7 +12,7 @@ use super::event::DeviceId;
|
||||
use super::runner::{EventWrapper, WeakShared};
|
||||
use super::window::WindowId;
|
||||
use super::{backend, runner, EventLoopProxy};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::error::{ExternalError, NotSupportedError};
|
||||
use crate::event::{
|
||||
DeviceId as RootDeviceId, ElementState, Event, FingerId as RootFingerId, KeyEvent, Touch,
|
||||
TouchPhase, WindowEvent,
|
||||
@@ -27,7 +28,8 @@ use crate::platform_impl::platform::cursor::CustomCursor;
|
||||
use crate::platform_impl::platform::r#async::Waker;
|
||||
use crate::platform_impl::Window;
|
||||
use crate::window::{
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId as RootWindowId,
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as RootWindow,
|
||||
WindowId as RootWindowId,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -556,7 +558,7 @@ impl ActiveEventLoop {
|
||||
canvas.set_old_size(new_size);
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::SurfaceResized(new_size),
|
||||
event: WindowEvent::Resized(new_size),
|
||||
});
|
||||
canvas.request_animation_frame();
|
||||
}
|
||||
@@ -606,10 +608,7 @@ impl ActiveEventLoop {
|
||||
}
|
||||
|
||||
pub(crate) fn has_multiple_screens(&self) -> Result<bool, NotSupportedError> {
|
||||
self.runner
|
||||
.monitor()
|
||||
.is_extended()
|
||||
.ok_or(NotSupportedError::new("has_multiple_screens is not supported"))
|
||||
self.runner.monitor().is_extended().ok_or(NotSupportedError::new())
|
||||
}
|
||||
|
||||
pub(crate) fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
|
||||
@@ -634,15 +633,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: crate::window::WindowAttributes,
|
||||
) -> Result<Box<dyn crate::window::Window>, RequestError> {
|
||||
) -> Result<crate::window::Window, crate::error::OsError> {
|
||||
let window = Window::new(self, window_attributes)?;
|
||||
Ok(Box::new(window))
|
||||
Ok(RootWindow { window })
|
||||
}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
source: CustomCursorSource,
|
||||
) -> Result<RootCustomCursor, RequestError> {
|
||||
) -> Result<RootCustomCursor, ExternalError> {
|
||||
Ok(RootCustomCursor { inner: CustomCursor::new(self, source.inner) })
|
||||
}
|
||||
|
||||
@@ -694,6 +693,10 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
|
||||
@@ -37,6 +37,7 @@ pub(crate) use cursor::{
|
||||
CustomCursorSource as PlatformCustomCursorSource,
|
||||
};
|
||||
|
||||
pub use self::error::OsError;
|
||||
pub use self::event::{DeviceId, FingerId};
|
||||
pub(crate) use self::event_loop::{
|
||||
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
|
||||
@@ -50,3 +51,4 @@ pub(crate) use self::monitor::{
|
||||
use self::web_sys as backend;
|
||||
pub use self::window::{PlatformSpecificWindowAttributes, Window, WindowId};
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
|
||||
@@ -29,7 +29,6 @@ use super::main_thread::MainThreadMarker;
|
||||
use super::r#async::{Dispatcher, Notified, Notifier};
|
||||
use super::web_sys::{Engine, EventListenerHandle};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform::web::{
|
||||
MonitorPermissionError, Orientation, OrientationData, OrientationLock, OrientationLockError,
|
||||
};
|
||||
@@ -189,12 +188,6 @@ impl PartialOrd for MonitorHandle {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MonitorHandle> for RootMonitorHandle {
|
||||
fn from(inner: MonitorHandle) -> Self {
|
||||
RootMonitorHandle { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OrientationLockFuture {
|
||||
Future(Notified<Result<(), OrientationLockError>>),
|
||||
@@ -329,22 +322,22 @@ impl Inner {
|
||||
OrientationType::LandscapePrimary => OrientationData {
|
||||
orientation: Orientation::Landscape,
|
||||
flipped: false,
|
||||
natural: if angle == 0 { Orientation::Landscape } else { Orientation::Portrait },
|
||||
natural: angle == 0,
|
||||
},
|
||||
OrientationType::LandscapeSecondary => OrientationData {
|
||||
orientation: Orientation::Landscape,
|
||||
flipped: true,
|
||||
natural: if angle == 180 { Orientation::Landscape } else { Orientation::Portrait },
|
||||
natural: angle == 180,
|
||||
},
|
||||
OrientationType::PortraitPrimary => OrientationData {
|
||||
orientation: Orientation::Portrait,
|
||||
flipped: false,
|
||||
natural: if angle == 0 { Orientation::Portrait } else { Orientation::Landscape },
|
||||
natural: angle == 0,
|
||||
},
|
||||
OrientationType::PortraitSecondary => OrientationData {
|
||||
orientation: Orientation::Portrait,
|
||||
flipped: true,
|
||||
natural: if angle == 180 { Orientation::Portrait } else { Orientation::Landscape },
|
||||
natural: angle == 180,
|
||||
},
|
||||
_ => {
|
||||
unreachable!("found unrecognized orientation: {}", orientation.type_string())
|
||||
|
||||
@@ -22,10 +22,10 @@ use super::media_query_handle::MediaQueryListHandle;
|
||||
use super::pointer::PointerHandler;
|
||||
use super::{event, fullscreen, ButtonsState, ResizeScaleHandle};
|
||||
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
|
||||
use crate::error::RequestError;
|
||||
use crate::event::{Force, MouseButton, MouseScrollDelta, SurfaceSizeWriter};
|
||||
use crate::error::OsError as RootOE;
|
||||
use crate::event::{Force, InnerSizeWriter, MouseButton, MouseScrollDelta};
|
||||
use crate::keyboard::{Key, KeyLocation, ModifiersState, PhysicalKey};
|
||||
use crate::platform_impl::Fullscreen;
|
||||
use crate::platform_impl::{Fullscreen, OsError};
|
||||
use crate::window::{WindowAttributes, WindowId as RootWindowId};
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -83,13 +83,13 @@ impl Canvas {
|
||||
navigator: Navigator,
|
||||
document: Document,
|
||||
attr: WindowAttributes,
|
||||
) -> Result<Self, RequestError> {
|
||||
) -> Result<Self, RootOE> {
|
||||
let canvas = match attr.platform_specific.canvas.map(Arc::try_unwrap) {
|
||||
Some(Ok(canvas)) => canvas.into_inner(main_thread),
|
||||
Some(Err(canvas)) => canvas.get(main_thread).clone(),
|
||||
None => document
|
||||
.create_element("canvas")
|
||||
.map_err(|_| os_error!("Failed to create canvas element"))?
|
||||
.map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))?
|
||||
.unchecked_into(),
|
||||
};
|
||||
|
||||
@@ -109,7 +109,7 @@ impl Canvas {
|
||||
if attr.platform_specific.focusable {
|
||||
canvas
|
||||
.set_attribute("tabindex", "0")
|
||||
.map_err(|_| os_error!("Failed to set a tabindex"))?;
|
||||
.map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?;
|
||||
}
|
||||
|
||||
let style = Style::new(&window, &canvas);
|
||||
@@ -126,17 +126,17 @@ impl Canvas {
|
||||
current_size: Rc::default(),
|
||||
};
|
||||
|
||||
if let Some(size) = attr.surface_size {
|
||||
if let Some(size) = attr.inner_size {
|
||||
let size = size.to_logical(super::scale_factor(&common.window));
|
||||
super::set_canvas_size(&common.document, &common.raw, &common.style, size);
|
||||
}
|
||||
|
||||
if let Some(size) = attr.min_surface_size {
|
||||
if let Some(size) = attr.min_inner_size {
|
||||
let size = size.to_logical(super::scale_factor(&common.window));
|
||||
super::set_canvas_min_size(&common.document, &common.raw, &common.style, Some(size));
|
||||
}
|
||||
|
||||
if let Some(size) = attr.max_surface_size {
|
||||
if let Some(size) = attr.max_inner_size {
|
||||
let size = size.to_logical(super::scale_factor(&common.window));
|
||||
super::set_canvas_max_size(&common.document, &common.raw, &common.style, Some(size));
|
||||
}
|
||||
@@ -213,7 +213,7 @@ impl Canvas {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
self.common.current_size.get()
|
||||
}
|
||||
|
||||
@@ -504,7 +504,7 @@ impl Canvas {
|
||||
window_id: RootWindowId(self.id),
|
||||
event: crate::event::WindowEvent::ScaleFactorChanged {
|
||||
scale_factor: scale,
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_size)),
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_size)),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -513,8 +513,8 @@ impl Canvas {
|
||||
};
|
||||
|
||||
if current_size != new_size {
|
||||
// Then we resize the canvas to the new size, a new `SurfaceResized` event will be sent
|
||||
// by the `ResizeObserver`:
|
||||
// Then we resize the canvas to the new size, a new
|
||||
// `Resized` event will be sent by the `ResizeObserver`:
|
||||
let new_size = new_size.to_logical(scale);
|
||||
super::set_canvas_size(self.document(), self.raw(), self.style(), new_size);
|
||||
|
||||
@@ -530,7 +530,7 @@ impl Canvas {
|
||||
self.set_old_size(new_size);
|
||||
runner.send_event(crate::event::Event::WindowEvent {
|
||||
window_id: RootWindowId(self.id),
|
||||
event: crate::event::WindowEvent::SurfaceResized(new_size),
|
||||
event: crate::event::WindowEvent::Resized(new_size),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,9 +138,10 @@ impl ResizeScaleInternal {
|
||||
|
||||
// Safari doesn't support `devicePixelContentBoxSize`
|
||||
if has_device_pixel_support() {
|
||||
let options = ResizeObserverOptions::new();
|
||||
options.set_box(ResizeObserverBoxOptions::DevicePixelContentBox);
|
||||
observer.observe_with_options(canvas, &options);
|
||||
observer.observe_with_options(
|
||||
canvas,
|
||||
ResizeObserverOptions::new().box_(ResizeObserverBoxOptions::DevicePixelContentBox),
|
||||
);
|
||||
} else {
|
||||
observer.observe(canvas);
|
||||
}
|
||||
|
||||
@@ -275,8 +275,8 @@ struct ScriptUrl(String);
|
||||
impl ScriptUrl {
|
||||
fn new(script: &str) -> Self {
|
||||
let sequence = Array::of1(&script.into());
|
||||
let property = BlobPropertyBag::new();
|
||||
property.set_type("text/javascript");
|
||||
let mut property = BlobPropertyBag::new();
|
||||
property.type_("text/javascript");
|
||||
let blob = Blob::new_with_str_sequence_and_options(&sequence, &property)
|
||||
.expect("`new Blob()` should never throw");
|
||||
|
||||
|
||||
@@ -5,17 +5,15 @@ use std::sync::Arc;
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
use super::main_thread::{MainThreadMarker, MainThreadSafe};
|
||||
use super::monitor::MonitorHandler;
|
||||
use super::monitor::{MonitorHandle, MonitorHandler};
|
||||
use super::r#async::Dispatcher;
|
||||
use super::{backend, lock, ActiveEventLoop};
|
||||
use super::{backend, lock, ActiveEventLoop, Fullscreen};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOE};
|
||||
use crate::icon::Icon;
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::window::{
|
||||
Cursor, CursorGrabMode, Fullscreen as RootFullscreen, ImePurpose, ResizeDirection, Theme,
|
||||
UserAttentionType, Window as RootWindow, WindowAttributes, WindowButtons, WindowId as RootWI,
|
||||
WindowLevel,
|
||||
Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons, WindowId as RootWI, WindowLevel,
|
||||
};
|
||||
|
||||
pub struct Window {
|
||||
@@ -31,10 +29,7 @@ pub struct Inner {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new(
|
||||
target: &ActiveEventLoop,
|
||||
attr: WindowAttributes,
|
||||
) -> Result<Self, RequestError> {
|
||||
pub(crate) fn new(target: &ActiveEventLoop, attr: WindowAttributes) -> Result<Self, RootOE> {
|
||||
let id = target.generate_id();
|
||||
|
||||
let window = target.runner.window();
|
||||
@@ -70,6 +65,14 @@ impl Window {
|
||||
Ok(Window { inner: dispatcher })
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Inner) + Send + 'static) {
|
||||
self.inner.dispatch(f)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Inner) -> R + Send) -> R {
|
||||
self.inner.queue(f)
|
||||
}
|
||||
|
||||
pub fn canvas(&self) -> Option<Ref<'_, HtmlCanvasElement>> {
|
||||
MainThreadMarker::new()
|
||||
.map(|main_thread| Ref::map(self.inner.value(main_thread), |inner| inner.canvas.raw()))
|
||||
@@ -88,307 +91,10 @@ impl Window {
|
||||
lock::is_cursor_lock_raw(inner.canvas.navigator(), inner.canvas.document())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RootWindow for Window {
|
||||
fn id(&self) -> RootWI {
|
||||
RootWI(self.inner.queue(|inner| inner.id))
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
self.inner.queue(Inner::scale_factor)
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
self.inner.dispatch(|inner| inner.canvas.request_animation_frame())
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {}
|
||||
|
||||
fn reset_dead_keys(&self) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
// Note: the canvas element has no window decorations, so this is equal to `outer_position`.
|
||||
self.outer_position()
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.inner.queue(|inner| inner.canvas.position().to_physical(inner.scale_factor())))
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
let position = position.to_logical::<f64>(inner.scale_factor());
|
||||
backend::set_canvas_position(
|
||||
inner.canvas.document(),
|
||||
inner.canvas.raw(),
|
||||
inner.canvas.style(),
|
||||
position,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
self.inner.queue(|inner| inner.canvas.surface_size())
|
||||
}
|
||||
|
||||
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
self.inner.queue(|inner| {
|
||||
let size = size.to_logical(self.scale_factor());
|
||||
backend::set_canvas_size(
|
||||
inner.canvas.document(),
|
||||
inner.canvas.raw(),
|
||||
inner.canvas.style(),
|
||||
size,
|
||||
);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
// Note: the canvas element has no window decorations, so this is equal to `surface_size`.
|
||||
self.surface_size()
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
let dimensions = min_size.map(|min_size| min_size.to_logical(inner.scale_factor()));
|
||||
backend::set_canvas_min_size(
|
||||
inner.canvas.document(),
|
||||
inner.canvas.raw(),
|
||||
inner.canvas.style(),
|
||||
dimensions,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn set_max_surface_size(&self, max_size: Option<Size>) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
let dimensions = max_size.map(|dimensions| dimensions.to_logical(inner.scale_factor()));
|
||||
backend::set_canvas_max_size(
|
||||
inner.canvas.document(),
|
||||
inner.canvas.raw(),
|
||||
inner.canvas.style(),
|
||||
dimensions,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_surface_resize_increments(&self, _: Option<Size>) {
|
||||
// Intentionally a no-op: users can't resize canvas elements
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
self.inner.queue(|inner| inner.canvas.set_attribute("alt", title))
|
||||
}
|
||||
|
||||
fn set_transparent(&self, _: bool) {}
|
||||
|
||||
fn set_blur(&self, _: bool) {}
|
||||
|
||||
fn set_visible(&self, _: bool) {
|
||||
// Intentionally a no-op
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_resizable(&self, _: bool) {
|
||||
// Intentionally a no-op: users can't resize canvas elements
|
||||
}
|
||||
|
||||
fn is_resizable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_enabled_buttons(&self, _: WindowButtons) {}
|
||||
|
||||
fn enabled_buttons(&self) -> WindowButtons {
|
||||
WindowButtons::all()
|
||||
}
|
||||
|
||||
fn set_minimized(&self, _: bool) {
|
||||
// Intentionally a no-op, as canvases cannot be 'minimized'
|
||||
}
|
||||
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
// Canvas cannot be 'minimized'
|
||||
Some(false)
|
||||
}
|
||||
|
||||
fn set_maximized(&self, _: bool) {
|
||||
// Intentionally a no-op, as canvases cannot be 'maximized'
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
// Canvas cannot be 'maximized'
|
||||
false
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, fullscreen: Option<RootFullscreen>) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
if let Some(fullscreen) = fullscreen {
|
||||
inner.canvas.request_fullscreen(fullscreen.into());
|
||||
} else {
|
||||
inner.canvas.exit_fullscreen()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn fullscreen(&self) -> Option<RootFullscreen> {
|
||||
self.inner.queue(|inner| {
|
||||
if inner.canvas.is_fullscreen() {
|
||||
Some(RootFullscreen::Borderless(None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set_decorations(&self, _: bool) {
|
||||
// Intentionally a no-op, no canvas decorations
|
||||
}
|
||||
|
||||
fn is_decorated(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_window_level(&self, _: WindowLevel) {
|
||||
// Intentionally a no-op, no window ordering
|
||||
}
|
||||
|
||||
fn set_window_icon(&self, _: Option<Icon>) {
|
||||
// Currently an intentional no-op
|
||||
}
|
||||
|
||||
fn set_ime_cursor_area(&self, _: Position, _: Size) {
|
||||
// Currently not implemented
|
||||
}
|
||||
|
||||
fn set_ime_allowed(&self, _: bool) {
|
||||
// Currently not implemented
|
||||
}
|
||||
|
||||
fn set_ime_purpose(&self, _: ImePurpose) {
|
||||
// Currently not implemented
|
||||
}
|
||||
|
||||
fn focus_window(&self) {
|
||||
self.inner.dispatch(|inner| {
|
||||
let _ = inner.canvas.raw().focus();
|
||||
})
|
||||
}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
self.inner.queue(|inner| inner.canvas.has_focus.get())
|
||||
}
|
||||
|
||||
fn request_user_attention(&self, _: Option<UserAttentionType>) {
|
||||
// Currently an intentional no-op
|
||||
}
|
||||
|
||||
fn set_theme(&self, _: Option<Theme>) {}
|
||||
|
||||
fn theme(&self) -> Option<Theme> {
|
||||
self.inner.queue(|inner| {
|
||||
backend::is_dark_mode(&inner.window).map(|is_dark_mode| {
|
||||
if is_dark_mode {
|
||||
Theme::Dark
|
||||
} else {
|
||||
Theme::Light
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn set_content_protected(&self, _: bool) {}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
|
||||
fn set_cursor(&self, cursor: Cursor) {
|
||||
self.inner.dispatch(move |inner| inner.canvas.cursor.set_cursor(cursor))
|
||||
}
|
||||
|
||||
fn set_cursor_position(&self, _: Position) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("set_cursor_position is not supported").into())
|
||||
}
|
||||
|
||||
fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
Ok(self.inner.queue(|inner| {
|
||||
match mode {
|
||||
CursorGrabMode::None => inner.canvas.document().exit_pointer_lock(),
|
||||
CursorGrabMode::Locked => lock::request_pointer_lock(
|
||||
inner.canvas.navigator(),
|
||||
inner.canvas.document(),
|
||||
inner.canvas.raw(),
|
||||
),
|
||||
CursorGrabMode::Confined => {
|
||||
return Err(NotSupportedError::new("confined cursor mode is not supported"))
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?)
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
self.inner.dispatch(move |inner| inner.canvas.cursor.set_cursor_visible(visible))
|
||||
}
|
||||
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("drag_window is not supported").into())
|
||||
}
|
||||
|
||||
fn drag_resize_window(&self, _: ResizeDirection) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("drag_resize_window is not supported").into())
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, _: Position) {}
|
||||
|
||||
fn set_cursor_hittest(&self, _: bool) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("set_cursor_hittest is not supported").into())
|
||||
}
|
||||
|
||||
fn current_monitor(&self) -> Option<RootMonitorHandle> {
|
||||
Some(self.inner.queue(|inner| inner.monitor.current_monitor()).into())
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
|
||||
Box::new(
|
||||
self.inner
|
||||
.queue(|inner| inner.monitor.available_monitors())
|
||||
.into_iter()
|
||||
.map(RootMonitorHandle::from),
|
||||
)
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<RootMonitorHandle> {
|
||||
self.inner.queue(|inner| inner.monitor.primary_monitor()).map(RootMonitorHandle::from)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
MainThreadMarker::new()
|
||||
.map(|main_thread| {
|
||||
let inner = self.inner.value(main_thread);
|
||||
@@ -396,30 +102,323 @@ impl rwh_06::HasWindowHandle for Window {
|
||||
let canvas: &wasm_bindgen::JsValue = inner.canvas.raw();
|
||||
let window_handle =
|
||||
rwh_06::WebCanvasWindowHandle::new(std::ptr::NonNull::from(canvas).cast());
|
||||
// SAFETY: The pointer won't be invalidated as long as `Window` lives, which the
|
||||
// lifetime is bound to.
|
||||
unsafe {
|
||||
rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::WebCanvas(
|
||||
window_handle,
|
||||
))
|
||||
}
|
||||
rwh_06::RawWindowHandle::WebCanvas(window_handle)
|
||||
})
|
||||
.ok_or(rwh_06::HandleError::Unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
Ok(rwh_06::DisplayHandle::web())
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub(crate) fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::Web(rwh_06::WebDisplayHandle::new()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
pub fn set_title(&self, title: &str) {
|
||||
self.canvas.set_attribute("alt", title)
|
||||
}
|
||||
|
||||
pub fn set_transparent(&self, _transparent: bool) {}
|
||||
|
||||
pub fn set_blur(&self, _blur: bool) {}
|
||||
|
||||
pub fn set_visible(&self, _visible: bool) {
|
||||
// Intentionally a no-op
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_visible(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
self.canvas.request_animation_frame();
|
||||
}
|
||||
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
Ok(self.canvas.position().to_physical(self.scale_factor()))
|
||||
}
|
||||
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
// Note: the canvas element has no window decorations, so this is equal to `outer_position`.
|
||||
self.outer_position()
|
||||
}
|
||||
|
||||
pub fn set_outer_position(&self, position: Position) {
|
||||
let position = position.to_logical::<f64>(self.scale_factor());
|
||||
|
||||
backend::set_canvas_position(
|
||||
self.canvas.document(),
|
||||
self.canvas.raw(),
|
||||
self.canvas.style(),
|
||||
position,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
self.canvas.inner_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
// Note: the canvas element has no window decorations, so this is equal to `inner_size`.
|
||||
self.inner_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let size = size.to_logical(self.scale_factor());
|
||||
backend::set_canvas_size(
|
||||
self.canvas.document(),
|
||||
self.canvas.raw(),
|
||||
self.canvas.style(),
|
||||
size,
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
||||
let dimensions = dimensions.map(|dimensions| dimensions.to_logical(self.scale_factor()));
|
||||
backend::set_canvas_min_size(
|
||||
self.canvas.document(),
|
||||
self.canvas.raw(),
|
||||
self.canvas.style(),
|
||||
dimensions,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||
let dimensions = dimensions.map(|dimensions| dimensions.to_logical(self.scale_factor()));
|
||||
backend::set_canvas_max_size(
|
||||
self.canvas.document(),
|
||||
self.canvas.raw(),
|
||||
self.canvas.style(),
|
||||
dimensions,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resize_increments(&self, _increments: Option<Size>) {
|
||||
// Intentionally a no-op: users can't resize canvas elements
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// Intentionally a no-op: users can't resize canvas elements
|
||||
}
|
||||
|
||||
pub fn is_resizable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
|
||||
|
||||
#[inline]
|
||||
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||
WindowButtons::all()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
super::backend::scale_factor(&self.window)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: Cursor) {
|
||||
self.canvas.cursor.set_cursor(cursor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
match mode {
|
||||
CursorGrabMode::None => self.canvas.document().exit_pointer_lock(),
|
||||
CursorGrabMode::Locked => lock::request_pointer_lock(
|
||||
self.canvas.navigator(),
|
||||
self.canvas.document(),
|
||||
self.canvas.raw(),
|
||||
),
|
||||
CursorGrabMode::Confined => {
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
self.canvas.cursor.set_cursor_visible(visible)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, _minimized: bool) {
|
||||
// Intentionally a no-op, as canvases cannot be 'minimized'
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_minimized(&self) -> Option<bool> {
|
||||
// Canvas cannot be 'minimized'
|
||||
Some(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
// Intentionally a no-op, as canvases cannot be 'maximized'
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
// Canvas cannot be 'maximized'
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
if self.canvas.is_fullscreen() {
|
||||
Some(Fullscreen::Borderless(None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
if let Some(fullscreen) = fullscreen {
|
||||
self.canvas.request_fullscreen(fullscreen);
|
||||
} else {
|
||||
self.canvas.exit_fullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, _decorations: bool) {
|
||||
// Intentionally a no-op, no canvas decorations
|
||||
}
|
||||
|
||||
pub fn is_decorated(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_level(&self, _level: WindowLevel) {
|
||||
// Intentionally a no-op, no window ordering
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _window_icon: Option<Icon>) {
|
||||
// Currently an intentional no-op
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {
|
||||
// Currently not implemented
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_allowed(&self, _allowed: bool) {
|
||||
// Currently not implemented
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {
|
||||
// Currently not implemented
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
let _ = self.canvas.raw().focus();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
|
||||
// Currently an intentional no-op
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(self.monitor.current_monitor())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
|
||||
self.monitor.available_monitors()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
self.monitor.primary_monitor()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
self.id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_theme(&self, _theme: Option<Theme>) {}
|
||||
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
backend::is_dark_mode(&self.window).map(|is_dark_mode| {
|
||||
if is_dark_mode {
|
||||
Theme::Dark
|
||||
} else {
|
||||
Theme::Light
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
self.canvas.has_focus.get()
|
||||
}
|
||||
|
||||
pub fn title(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
|
||||
pub fn reset_dead_keys(&self) {
|
||||
// Not supported
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Inner {
|
||||
|
||||
@@ -56,9 +56,9 @@ use super::window::set_skip_taskbar;
|
||||
use super::SelectedCursor;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::error::{EventLoopError, RequestError};
|
||||
use crate::error::{EventLoopError, ExternalError, OsError};
|
||||
use crate::event::{
|
||||
Event, FingerId as RootFingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, Touch, TouchPhase,
|
||||
Event, FingerId as RootFingerId, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase,
|
||||
WindowEvent,
|
||||
};
|
||||
use crate::event_loop::{
|
||||
@@ -86,8 +86,8 @@ use crate::platform_impl::platform::{
|
||||
use crate::platform_impl::Window;
|
||||
use crate::utils::Lazy;
|
||||
use crate::window::{
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
|
||||
WindowAttributes, WindowId as CoreWindowId,
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as RootWindow,
|
||||
WindowAttributes, WindowId as RootWindowId,
|
||||
};
|
||||
|
||||
pub(crate) struct WindowData {
|
||||
@@ -518,17 +518,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
RootEventLoopProxy { event_loop_proxy }
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: WindowAttributes,
|
||||
) -> Result<Box<dyn CoreWindow>, RequestError> {
|
||||
Ok(Box::new(Window::new(self, window_attributes)?))
|
||||
fn create_window(&self, window_attributes: WindowAttributes) -> Result<RootWindow, OsError> {
|
||||
let window = Window::new(self, window_attributes)?;
|
||||
Ok(RootWindow { window })
|
||||
}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
source: CustomCursorSource,
|
||||
) -> Result<RootCustomCursor, RequestError> {
|
||||
) -> Result<RootCustomCursor, ExternalError> {
|
||||
Ok(RootCustomCursor { inner: WinCursor::new(&source.inner.0)? })
|
||||
}
|
||||
|
||||
@@ -572,6 +570,10 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
@@ -920,7 +922,7 @@ fn update_modifiers(window: HWND, userdata: &WindowData) {
|
||||
drop(window_state);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: ModifiersChanged(modifiers.into()),
|
||||
});
|
||||
}
|
||||
@@ -932,7 +934,7 @@ unsafe fn gain_active_focus(window: HWND, userdata: &WindowData) {
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: Focused(true),
|
||||
});
|
||||
}
|
||||
@@ -942,12 +944,12 @@ unsafe fn lose_active_focus(window: HWND, userdata: &WindowData) {
|
||||
|
||||
userdata.window_state_lock().modifiers_state = ModifiersState::empty();
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: ModifiersChanged(ModifiersState::empty().into()),
|
||||
});
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: Focused(false),
|
||||
});
|
||||
}
|
||||
@@ -1045,7 +1047,7 @@ unsafe fn public_window_callback_inner(
|
||||
userdata.key_event_builder.process_message(window, msg, wparam, lparam, &mut result);
|
||||
for event in events {
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event: event.event,
|
||||
@@ -1130,7 +1132,7 @@ unsafe fn public_window_callback_inner(
|
||||
WM_CLOSE => {
|
||||
use crate::event::WindowEvent::CloseRequested;
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: CloseRequested,
|
||||
});
|
||||
result = ProcResult::Value(0);
|
||||
@@ -1140,7 +1142,7 @@ unsafe fn public_window_callback_inner(
|
||||
use crate::event::WindowEvent::Destroyed;
|
||||
unsafe { RevokeDragDrop(window) };
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: Destroyed,
|
||||
});
|
||||
result = ProcResult::Value(0);
|
||||
@@ -1161,7 +1163,7 @@ unsafe fn public_window_callback_inner(
|
||||
// and request a normal redraw with `RedrawWindow`.
|
||||
if !userdata.event_loop_runner.should_buffer() {
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
}
|
||||
@@ -1264,7 +1266,7 @@ unsafe fn public_window_callback_inner(
|
||||
let physical_position =
|
||||
unsafe { PhysicalPosition::new((*windowpos).x, (*windowpos).y) };
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: Moved(physical_position),
|
||||
});
|
||||
}
|
||||
@@ -1274,14 +1276,14 @@ unsafe fn public_window_callback_inner(
|
||||
},
|
||||
|
||||
WM_SIZE => {
|
||||
use crate::event::WindowEvent::SurfaceResized;
|
||||
use crate::event::WindowEvent::Resized;
|
||||
let w = super::loword(lparam as u32) as u32;
|
||||
let h = super::hiword(lparam as u32) as u32;
|
||||
|
||||
let physical_size = PhysicalSize::new(w, h);
|
||||
let event = Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
event: SurfaceResized(physical_size),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: Resized(physical_size),
|
||||
};
|
||||
|
||||
{
|
||||
@@ -1308,7 +1310,7 @@ unsafe fn public_window_callback_inner(
|
||||
let scale_factor = userdata.window_state_lock().scale_factor;
|
||||
let Some(inc) = userdata
|
||||
.window_state_lock()
|
||||
.surface_resize_increments
|
||||
.resize_increments
|
||||
.map(|inc| inc.to_physical(scale_factor))
|
||||
.filter(|inc| inc.width > 0 && inc.height > 0)
|
||||
else {
|
||||
@@ -1395,7 +1397,7 @@ unsafe fn public_window_callback_inner(
|
||||
userdata.window_state_lock().ime_state = ImeState::Enabled;
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Ime(Ime::Enabled),
|
||||
});
|
||||
}
|
||||
@@ -1415,7 +1417,7 @@ unsafe fn public_window_callback_inner(
|
||||
|
||||
if lparam == 0 {
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
});
|
||||
}
|
||||
@@ -1427,11 +1429,11 @@ unsafe fn public_window_callback_inner(
|
||||
userdata.window_state_lock().ime_state = ImeState::Enabled;
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
});
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Ime(Ime::Commit(text)),
|
||||
});
|
||||
}
|
||||
@@ -1446,7 +1448,7 @@ unsafe fn public_window_callback_inner(
|
||||
let cursor_range = first.map(|f| (f, last.unwrap_or(f)));
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Ime(Ime::Preedit(text, cursor_range)),
|
||||
});
|
||||
}
|
||||
@@ -1469,11 +1471,11 @@ unsafe fn public_window_callback_inner(
|
||||
let ime_context = unsafe { ImeContext::current(window) };
|
||||
if let Some(text) = unsafe { ime_context.get_composed_text() } {
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
});
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Ime(Ime::Commit(text)),
|
||||
});
|
||||
}
|
||||
@@ -1482,7 +1484,7 @@ unsafe fn public_window_callback_inner(
|
||||
userdata.window_state_lock().ime_state = ImeState::Disabled;
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Ime(Ime::Disabled),
|
||||
});
|
||||
}
|
||||
@@ -1540,7 +1542,7 @@ unsafe fn public_window_callback_inner(
|
||||
|
||||
drop(w);
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: CursorEntered { device_id: DEVICE_ID },
|
||||
});
|
||||
|
||||
@@ -1561,7 +1563,7 @@ unsafe fn public_window_callback_inner(
|
||||
|
||||
drop(w);
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: CursorLeft { device_id: DEVICE_ID },
|
||||
});
|
||||
},
|
||||
@@ -1580,7 +1582,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: CursorMoved { device_id: DEVICE_ID, position },
|
||||
});
|
||||
}
|
||||
@@ -1596,7 +1598,7 @@ unsafe fn public_window_callback_inner(
|
||||
}
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: CursorLeft { device_id: DEVICE_ID },
|
||||
});
|
||||
|
||||
@@ -1612,7 +1614,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::MouseWheel {
|
||||
device_id: DEVICE_ID,
|
||||
delta: LineDelta(0.0, value),
|
||||
@@ -1632,7 +1634,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::MouseWheel {
|
||||
device_id: DEVICE_ID,
|
||||
delta: LineDelta(value, 0.0),
|
||||
@@ -1667,7 +1669,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left },
|
||||
});
|
||||
result = ProcResult::Value(0);
|
||||
@@ -1683,7 +1685,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left },
|
||||
});
|
||||
result = ProcResult::Value(0);
|
||||
@@ -1699,7 +1701,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right },
|
||||
});
|
||||
result = ProcResult::Value(0);
|
||||
@@ -1715,7 +1717,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right },
|
||||
});
|
||||
result = ProcResult::Value(0);
|
||||
@@ -1731,7 +1733,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle },
|
||||
});
|
||||
result = ProcResult::Value(0);
|
||||
@@ -1747,7 +1749,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle },
|
||||
});
|
||||
result = ProcResult::Value(0);
|
||||
@@ -1764,7 +1766,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
device_id: DEVICE_ID,
|
||||
state: Pressed,
|
||||
@@ -1789,7 +1791,7 @@ unsafe fn public_window_callback_inner(
|
||||
update_modifiers(window, userdata);
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
device_id: DEVICE_ID,
|
||||
state: Released,
|
||||
@@ -1838,7 +1840,7 @@ unsafe fn public_window_callback_inner(
|
||||
let y = location.y as f64 + (input.y % 100) as f64 / 100f64;
|
||||
let location = PhysicalPosition::new(x, y);
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
phase: if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) {
|
||||
TouchPhase::Started
|
||||
@@ -1986,7 +1988,7 @@ unsafe fn public_window_callback_inner(
|
||||
let y = location.y as f64 + y.fract();
|
||||
let location = PhysicalPosition::new(x, y);
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
phase: if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) {
|
||||
TouchPhase::Started
|
||||
@@ -2131,7 +2133,7 @@ unsafe fn public_window_callback_inner(
|
||||
// New size as suggested by Windows.
|
||||
let suggested_rect = unsafe { *(lparam as *const RECT) };
|
||||
|
||||
// The window rect provided is the window's outer size, not it's surface size. However,
|
||||
// The window rect provided is the window's outer size, not it's inner size. However,
|
||||
// win32 doesn't provide an `UnadjustWindowRectEx` function to get the client rect from
|
||||
// the outer rect, so we instead adjust the window rect to get the decoration margins
|
||||
// and remove them from the outer size.
|
||||
@@ -2151,33 +2153,33 @@ unsafe fn public_window_callback_inner(
|
||||
let old_physical_inner_rect = util::WindowArea::Inner
|
||||
.get_rect(window)
|
||||
.expect("failed to query (old) inner window area");
|
||||
let old_physical_surface_size = PhysicalSize::new(
|
||||
let old_physical_inner_size = PhysicalSize::new(
|
||||
(old_physical_inner_rect.right - old_physical_inner_rect.left) as u32,
|
||||
(old_physical_inner_rect.bottom - old_physical_inner_rect.top) as u32,
|
||||
);
|
||||
|
||||
// `allow_resize` prevents us from re-applying DPI adjustment to the restored size after
|
||||
// exiting fullscreen (the restored size is already DPI adjusted).
|
||||
let new_physical_surface_size = match allow_resize {
|
||||
let new_physical_inner_size = match allow_resize {
|
||||
// We calculate our own size because the default suggested rect doesn't do a great
|
||||
// job of preserving the window's logical size.
|
||||
true => old_physical_surface_size
|
||||
true => old_physical_inner_size
|
||||
.to_logical::<f64>(old_scale_factor)
|
||||
.to_physical::<u32>(new_scale_factor),
|
||||
false => old_physical_surface_size,
|
||||
false => old_physical_inner_size,
|
||||
};
|
||||
|
||||
let new_surface_size = Arc::new(Mutex::new(new_physical_surface_size));
|
||||
let new_inner_size = Arc::new(Mutex::new(new_physical_inner_size));
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: ScaleFactorChanged {
|
||||
scale_factor: new_scale_factor,
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
},
|
||||
});
|
||||
|
||||
let new_physical_surface_size = *new_surface_size.lock().unwrap();
|
||||
drop(new_surface_size);
|
||||
let new_physical_inner_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
|
||||
let dragging_window: bool;
|
||||
|
||||
@@ -2186,7 +2188,7 @@ unsafe fn public_window_callback_inner(
|
||||
dragging_window =
|
||||
window_state.window_flags().contains(WindowFlags::MARKER_IN_SIZE_MOVE);
|
||||
// Unset maximized if we're changing the window's size.
|
||||
if new_physical_surface_size != old_physical_surface_size {
|
||||
if new_physical_inner_size != old_physical_inner_size {
|
||||
WindowState::set_window_flags(window_state, window, |f| {
|
||||
f.set(WindowFlags::MAXIMIZED, false)
|
||||
});
|
||||
@@ -2201,8 +2203,8 @@ unsafe fn public_window_callback_inner(
|
||||
let mut conservative_rect = RECT {
|
||||
left: suggested_ul.0,
|
||||
top: suggested_ul.1,
|
||||
right: suggested_ul.0 + new_physical_surface_size.width as i32,
|
||||
bottom: suggested_ul.1 + new_physical_surface_size.height as i32,
|
||||
right: suggested_ul.0 + new_physical_inner_size.width as i32,
|
||||
bottom: suggested_ul.1 + new_physical_inner_size.height as i32,
|
||||
};
|
||||
|
||||
conservative_rect = window_flags
|
||||
@@ -2321,7 +2323,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_state.current_theme = new_theme;
|
||||
drop(window_state);
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: CoreWindowId(WindowId(window)),
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: ThemeChanged(new_theme),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use windows_sys::Win32::Foundation::HWND;
|
||||
|
||||
use super::ControlFlow;
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||
use crate::platform_impl::platform::event_loop::{WindowData, GWL_USERDATA};
|
||||
use crate::platform_impl::platform::get_window_long;
|
||||
use crate::window::WindowId;
|
||||
@@ -357,12 +357,12 @@ impl BufferedEvent {
|
||||
pub fn from_event(event: Event) -> BufferedEvent {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::ScaleFactorChanged { scale_factor, surface_size_writer },
|
||||
event: WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer },
|
||||
window_id,
|
||||
} => BufferedEvent::ScaleFactorChanged(
|
||||
window_id,
|
||||
scale_factor,
|
||||
*surface_size_writer.new_surface_size.upgrade().unwrap().lock().unwrap(),
|
||||
*inner_size_writer.new_inner_size.upgrade().unwrap().lock().unwrap(),
|
||||
),
|
||||
event => BufferedEvent::Event(event),
|
||||
}
|
||||
@@ -371,29 +371,29 @@ impl BufferedEvent {
|
||||
pub fn dispatch_event(self, dispatch: impl FnOnce(Event)) {
|
||||
match self {
|
||||
Self::Event(event) => dispatch(event),
|
||||
Self::ScaleFactorChanged(window_id, scale_factor, new_surface_size) => {
|
||||
let user_new_surface_size = Arc::new(Mutex::new(new_surface_size));
|
||||
Self::ScaleFactorChanged(window_id, scale_factor, new_inner_size) => {
|
||||
let user_new_innner_size = Arc::new(Mutex::new(new_inner_size));
|
||||
dispatch(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(
|
||||
&user_new_surface_size,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&user_new_innner_size,
|
||||
)),
|
||||
},
|
||||
});
|
||||
let surface_size = *user_new_surface_size.lock().unwrap();
|
||||
let inner_size = *user_new_innner_size.lock().unwrap();
|
||||
|
||||
drop(user_new_surface_size);
|
||||
drop(user_new_innner_size);
|
||||
|
||||
if surface_size != new_surface_size {
|
||||
if inner_size != new_inner_size {
|
||||
let window_flags = unsafe {
|
||||
let userdata =
|
||||
get_window_long(window_id.0.into(), GWL_USERDATA) as *mut WindowData;
|
||||
(*userdata).window_state_lock().window_flags
|
||||
};
|
||||
|
||||
window_flags.set_size((window_id.0).0, surface_size);
|
||||
window_flags.set_size((window_id.0).0, inner_size);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
use super::util;
|
||||
use crate::cursor::CursorImage;
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::error::RequestError;
|
||||
use crate::error::ExternalError;
|
||||
use crate::icon::*;
|
||||
|
||||
impl Pixel {
|
||||
@@ -179,7 +179,7 @@ impl Default for SelectedCursor {
|
||||
pub struct WinCursor(pub(super) Arc<RaiiCursor>);
|
||||
|
||||
impl WinCursor {
|
||||
pub(crate) fn new(image: &CursorImage) -> Result<Self, RequestError> {
|
||||
pub(crate) fn new(image: &CursorImage) -> Result<Self, ExternalError> {
|
||||
let mut bgra = image.rgba.clone();
|
||||
bgra.chunks_exact_mut(4).for_each(|chunk| chunk.swap(0, 2));
|
||||
|
||||
@@ -189,16 +189,16 @@ impl WinCursor {
|
||||
unsafe {
|
||||
let hdc_screen = GetDC(0);
|
||||
if hdc_screen == 0 {
|
||||
return Err(os_error!(io::Error::last_os_error()).into());
|
||||
return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
|
||||
}
|
||||
let hbm_color = CreateCompatibleBitmap(hdc_screen, w, h);
|
||||
ReleaseDC(0, hdc_screen);
|
||||
if hbm_color == 0 {
|
||||
return Err(os_error!(io::Error::last_os_error()).into());
|
||||
return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
|
||||
}
|
||||
if SetBitmapBits(hbm_color, bgra.len() as u32, bgra.as_ptr() as *const c_void) == 0 {
|
||||
DeleteObject(hbm_color);
|
||||
return Err(os_error!(io::Error::last_os_error()).into());
|
||||
return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
|
||||
};
|
||||
|
||||
// Mask created according to https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbitmap#parameters
|
||||
@@ -206,7 +206,7 @@ impl WinCursor {
|
||||
let hbm_mask = CreateBitmap(w, h, 1, 1, mask_bits.as_ptr() as *const _);
|
||||
if hbm_mask == 0 {
|
||||
DeleteObject(hbm_color);
|
||||
return Err(os_error!(io::Error::last_os_error()).into());
|
||||
return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
|
||||
}
|
||||
|
||||
let icon_info = ICONINFO {
|
||||
@@ -221,7 +221,7 @@ impl WinCursor {
|
||||
DeleteObject(hbm_color);
|
||||
DeleteObject(hbm_mask);
|
||||
if handle == 0 {
|
||||
return Err(os_error!(io::Error::last_os_error()).into());
|
||||
return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
|
||||
}
|
||||
|
||||
Ok(Self(Arc::new(RaiiCursor { handle })))
|
||||
|
||||
@@ -103,6 +103,8 @@ fn wrap_device_id(id: u32) -> RootDeviceId {
|
||||
RootDeviceId(DeviceId(id))
|
||||
}
|
||||
|
||||
pub type OsError = std::io::Error;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
pub text_with_all_modifiers: Option<SmolStr>,
|
||||
|
||||
@@ -16,6 +16,7 @@ use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::monitor::VideoModeHandle as RootVideoModeHandle;
|
||||
use crate::platform_impl::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
|
||||
use crate::platform_impl::platform::util::has_flag;
|
||||
use crate::platform_impl::platform::window::Window;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VideoModeHandle {
|
||||
@@ -135,6 +136,17 @@ pub fn current_monitor(hwnd: HWND) -> MonitorHandle {
|
||||
MonitorHandle::new(hmonitor)
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
available_monitors()
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
let monitor = primary_monitor();
|
||||
Some(monitor)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFOEXW, io::Error> {
|
||||
let mut monitor_info: MONITORINFOEXW = unsafe { mem::zeroed() };
|
||||
monitor_info.monitorInfo.cbSize = mem::size_of::<MONITORINFOEXW>() as u32;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ pub(crate) struct WindowState {
|
||||
pub min_size: Option<Size>,
|
||||
pub max_size: Option<Size>,
|
||||
|
||||
pub surface_resize_increments: Option<Size>,
|
||||
pub resize_increments: Option<Size>,
|
||||
|
||||
pub window_icon: Option<Icon>,
|
||||
pub taskbar_icon: Option<Icon>,
|
||||
@@ -151,10 +151,10 @@ impl WindowState {
|
||||
last_position: None,
|
||||
},
|
||||
|
||||
min_size: attributes.min_surface_size,
|
||||
max_size: attributes.max_surface_size,
|
||||
min_size: attributes.min_inner_size,
|
||||
max_size: attributes.max_inner_size,
|
||||
|
||||
surface_resize_increments: attributes.surface_resize_increments,
|
||||
resize_increments: attributes.resize_increments,
|
||||
|
||||
window_icon: attributes.window_icon.clone(),
|
||||
taskbar_icon: None,
|
||||
|
||||
18
src/utils.rs
18
src/utils.rs
@@ -4,7 +4,6 @@
|
||||
// This isn't used on every platform, which can come up as dead code warnings.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::any::Any;
|
||||
use std::ops::Deref;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
@@ -27,20 +26,3 @@ impl<T> Deref for Lazy<T> {
|
||||
self.cell.get_or_init(self.init)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsAny {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
|
||||
impl<T: Any> AsAny for T {
|
||||
#[inline(always)]
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
777
src/window.rs
777
src/window.rs
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
#[allow(dead_code)]
|
||||
fn needs_send<T: Send + ?Sized>() {}
|
||||
fn needs_send<T: Send>() {}
|
||||
|
||||
#[test]
|
||||
fn event_loop_proxy_send() {
|
||||
@@ -8,7 +8,7 @@ fn event_loop_proxy_send() {
|
||||
|
||||
#[test]
|
||||
fn window_send() {
|
||||
needs_send::<dyn winit::window::Window>();
|
||||
needs_send::<winit::window::Window>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#[allow(dead_code)]
|
||||
fn needs_sync<T: Sync + ?Sized>() {}
|
||||
fn needs_sync<T: Sync>() {}
|
||||
|
||||
#[test]
|
||||
fn event_loop_proxy_send() {
|
||||
@@ -8,7 +8,7 @@ fn event_loop_proxy_send() {
|
||||
|
||||
#[test]
|
||||
fn window_sync() {
|
||||
needs_sync::<dyn winit::window::Window>();
|
||||
needs_sync::<winit::window::Window>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user