Compare commits

..

1 Commits

Author SHA1 Message Date
Kirill Chibisov
9ea7a54130 api: show example for platform specific application trait 2024-08-08 22:54:24 +03:00
103 changed files with 5832 additions and 6472 deletions

View File

@@ -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

View File

@@ -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
@@ -91,8 +90,8 @@ image = { version = "0.25.0", default-features = false, features = ["png"] }
tracing = { version = "0.1.40", default-features = false, features = ["log"] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
[target.'cfg(not(target_os = "android"))'.dev-dependencies]
softbuffer = { version = "0.4.6", default-features = false, features = [
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
softbuffer = { version = "0.4.0", default-features = false, features = [
"x11",
"x11-dlopen",
"wayland",
@@ -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",
@@ -270,16 +263,16 @@ xkbcommon-dl = "0.4.2"
# Orbital
[target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.47", default-features = false }
redox_syscall = "0.5.7"
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",

View File

@@ -8,7 +8,7 @@
```toml
[dependencies]
winit = "0.30.5"
winit = "0.30.4"
```
## [Documentation](https://docs.rs/winit)

View File

@@ -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"

View File

@@ -11,8 +11,6 @@ Unreleased` header.
## Unreleased
- Add `Rect`, `PhysicalRect` and `LogicalRect`.
## 0.1.1
- Derive `Debug`, `Copy`, `Clone`, `PartialEq`, `Serialize`, `Deserialize` traits for `PixelUnit`.

View File

@@ -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

View File

@@ -759,120 +759,6 @@ impl<P: Pixel> From<LogicalPosition<P>> for Position {
}
}
/// A rectangle represented in logical pixels.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalRect<P> {
pub origin: LogicalPosition<P>,
pub size: LogicalSize<P>,
}
impl<P> LogicalRect<P> {
#[inline]
pub const fn new(origin: LogicalPosition<P>, size: LogicalSize<P>) -> Self {
Self { origin, size }
}
}
impl<P: Pixel> LogicalRect<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalRect<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalRect<X> {
let origin = self.origin.to_physical(scale_factor);
let size = self.size.to_physical(scale_factor);
PhysicalRect::new(origin, size)
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalRect<X> {
LogicalRect { origin: self.origin.cast(), size: self.size.cast() }
}
}
/// A rectangle represented in physical pixels.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalRect<P> {
pub origin: PhysicalPosition<P>,
pub size: PhysicalSize<P>,
}
impl<P> PhysicalRect<P> {
#[inline]
pub const fn new(origin: PhysicalPosition<P>, size: PhysicalSize<P>) -> Self {
Self { origin, size }
}
}
impl<P: Pixel> PhysicalRect<P> {
#[inline]
pub fn from_logical<T: Into<LogicalRect<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalRect<X> {
assert!(validate_scale_factor(scale_factor));
let origin = self.origin.to_logical(scale_factor);
let size = self.size.to_logical(scale_factor);
LogicalRect::new(origin, size)
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalRect<X> {
PhysicalRect { origin: self.origin.cast(), size: self.size.cast() }
}
}
/// A rectangle that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Rect {
Physical(PhysicalRect<i32>),
Logical(LogicalRect<f64>),
}
impl Rect {
pub fn new<R: Into<Self>>(rect: R) -> Self {
rect.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalRect<P> {
match *self {
Self::Physical(rect) => rect.to_logical(scale_factor),
Self::Logical(rect) => rect.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalRect<P> {
match *self {
Self::Physical(rect) => rect.cast(),
Self::Logical(rect) => rect.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalRect<P>> for Rect {
#[inline]
fn from(rect: PhysicalRect<P>) -> Self {
Self::Physical(rect.cast())
}
}
impl<P: Pixel> From<LogicalRect<P>> for Rect {
#[inline]
fn from(rect: LogicalRect<P>) -> Self {
Self::Logical(rect.cast())
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;

View File

@@ -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());
@@ -44,7 +44,7 @@ fn main() -> Result<(), impl std::error::Error> {
self.windows.clear();
event_loop.exit();
},
WindowEvent::PointerEntered { device_id: _, .. } => {
WindowEvent::CursorEntered { device_id: _ } => {
// On x11, println when the cursor entered in a window even if the child window
// is created by some key inputs.
// the child windows are always placed at (0, 0) with size (200, 200) in the
@@ -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.

View File

@@ -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);
},
_ => (),
}

View File

@@ -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();
},
_ => (),

View File

@@ -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);
},
_ => (),
}

View File

@@ -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.
}
}

View File

@@ -3,7 +3,7 @@
use std::collections::HashMap;
use std::error::Error;
use std::fmt::Debug;
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
use std::num::NonZeroU32;
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::Arc;
@@ -11,29 +11,28 @@ use std::{fmt, mem};
use ::tracing::{error, info};
use cursor_icon::CursorIcon;
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
use rwh_06::{DisplayHandle, HasDisplayHandle};
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
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};
#[cfg(macos_platform)]
use winit::platform::macos::{
ApplicationHandlerExtMacOS, OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS,
};
use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS};
#[cfg(any(x11_platform, wayland_platform))]
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"]
@@ -48,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.
@@ -69,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)?)
}
@@ -78,21 +78,21 @@ 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>,
/// Drawing context.
///
/// With OpenGL it could be EGLDisplay.
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
context: Option<Context<DisplayHandle<'static>>>,
}
impl Application {
fn new(event_loop: &EventLoop, receiver: Receiver<Action>, sender: Sender<Action>) -> Self {
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
let context = Some(
Context::new(unsafe {
std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(
@@ -121,7 +121,7 @@ impl Application {
Self {
receiver,
sender,
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
context,
custom_cursors,
icon,
@@ -137,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()));
@@ -373,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)
@@ -391,7 +396,7 @@ impl ApplicationHandler for Application {
};
match event {
WindowEvent::SurfaceResized(size) => {
WindowEvent::Resized(size) => {
window.resize(size);
},
WindowEvent::Focused(focused) => {
@@ -448,23 +453,20 @@ impl ApplicationHandler for Application {
}
}
},
WindowEvent::PointerButton { button, state, .. } => {
info!("Pointer button {button:?} {state:?}");
WindowEvent::MouseInput { button, state, .. } => {
let mods = window.modifiers;
if let Some(action) = state
.is_pressed()
.then(|| Self::process_mouse_binding(button.mouse_button(), &mods))
.flatten()
if let Some(action) =
state.is_pressed().then(|| Self::process_mouse_binding(button, &mods)).flatten()
{
self.handle_action_with_window(event_loop, window_id, action);
}
},
WindowEvent::PointerLeft { .. } => {
info!("Pointer left Window={window_id:?}");
WindowEvent::CursorLeft { .. } => {
info!("Cursor left Window={window_id:?}");
window.cursor_left();
},
WindowEvent::PointerMoved { position, .. } => {
info!("Moved pointer to {position:?}");
WindowEvent::CursorMoved { position, .. } => {
info!("Moved cursor to {position:?}");
window.cursor_moved(position);
},
WindowEvent::ActivationTokenDone { token: _token, .. } => {
@@ -515,10 +517,11 @@ impl ApplicationHandler for Application {
WindowEvent::TouchpadPressure { .. }
| WindowEvent::HoveredFileCancelled
| WindowEvent::KeyboardInput { .. }
| WindowEvent::PointerEntered { .. }
| WindowEvent::CursorEntered { .. }
| WindowEvent::DroppedFile(_)
| WindowEvent::HoveredFile(_)
| WindowEvent::Destroyed
| WindowEvent::Touch(_)
| WindowEvent::Moved(_) => (),
}
}
@@ -526,7 +529,7 @@ impl ApplicationHandler for Application {
fn device_event(
&mut self,
_event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
device_id: DeviceId,
event: DeviceEvent,
) {
info!("Device {device_id:?} event: {event:?}");
@@ -549,27 +552,16 @@ impl ApplicationHandler for Application {
}
}
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
fn exiting(&mut self, _event_loop: &dyn ActiveEventLoop) {
// We must drop the context here.
self.context = None;
}
#[cfg(target_os = "macos")]
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
Some(self)
}
}
#[cfg(target_os = "macos")]
impl ApplicationHandlerExtMacOS for Application {
fn standard_key_binding(
&mut self,
_event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
action: &str,
) {
info!(?window_id, ?action, "macOS standard key binding");
impl WaylandApplicationHandler for Application {
fn wayland_callback(&mut self) {
println!("Wayland callback!");
}
}
@@ -580,10 +572,10 @@ struct WindowState {
/// Render surface.
///
/// NOTE: This surface must be dropped before the `Window`.
#[cfg(not(android_platform))]
surface: Surface<DisplayHandle<'static>, Arc<dyn Window>>,
#[cfg(not(any(android_platform, ios_platform)))]
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.
@@ -611,31 +603,31 @@ 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.
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
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(),
custom_idx: app.custom_cursors.as_ref().map(Vec::len).unwrap_or(1) - 1,
cursor_grab: CursorGrabMode::None,
named_idx,
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
surface,
window,
theme,
@@ -657,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));
}
}
@@ -668,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));
}
}
@@ -702,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.
@@ -746,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");
}
}
@@ -787,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(())
}
@@ -809,15 +801,15 @@ 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:?}");
#[cfg(not(android_platform))]
info!("Resized to {size:?}");
#[cfg(not(any(android_platform, ios_platform)))]
{
let (width, height) = match (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
@@ -838,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);
}
}
@@ -861,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 {
@@ -910,7 +902,7 @@ impl WindowState {
}
/// Draw the window contents.
#[cfg(not(android_platform))]
#[cfg(not(any(android_platform, ios_platform)))]
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
if self.occluded {
info!("Skipping drawing occluded window={:?}", self.window.id());
@@ -932,7 +924,7 @@ impl WindowState {
Ok(())
}
#[cfg(android_platform)]
#[cfg(any(android_platform, ios_platform))]
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
info!("Drawing but without rendering...");
Ok(())

View File

@@ -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);
},
_ => (),
}

View File

@@ -1,9 +1,9 @@
//! End user application handling.
use std::any::Any;
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
use crate::event_loop::ActiveEventLoop;
#[cfg(any(docsrs, macos_platform))]
use crate::platform::macos::ApplicationHandlerExtMacOS;
use crate::window::WindowId;
/// The handler of the application events.
@@ -43,21 +43,11 @@ pub trait ApplicationHandler {
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`bfcache`]: https://web.dev/bfcache/
///
/// ### Android
///
/// On Android, the [`resumed()`] method is called when the `Activity` is (again, if after a
/// prior [`suspended()`]) being displayed to the user. This is a good place to begin drawing
/// visual elements, running animations, etc. It is driven by Android's [`onStart()`] method.
///
/// [`onStart()`]: https://developer.android.com/reference/android/app/Activity#onStart()
///
/// ### Others
///
/// **macOS / Orbital / Wayland / Windows / X11:** Unsupported.
/// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported.
///
/// [`resumed()`]: Self::resumed()
/// [`suspended()`]: Self::suspended()
/// [`exiting()`]: Self::exiting()
/// [`resumed()`]: Self::resumed
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
@@ -83,24 +73,26 @@ pub trait ApplicationHandler {
///
/// ### Android
///
/// On Android, the [`can_create_surfaces()`] method is called when a new [`NativeWindow`]
/// (native [`Surface`]) is created which backs the application window. This is expected to
/// closely correlate with the [`onStart`] lifecycle event which typically results in a surface
/// to be created after the app becomes visible.
/// On Android, the [`can_create_surfaces()`] method is called when a new [`SurfaceView`] has
/// been created. This is expected to closely correlate with the [`onResume`] lifecycle
/// event but there may technically be a discrepancy.
///
/// Applications that need to run on Android must wait until they have received a surface before
/// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
///
/// Applications that need to run on Android must wait until they have been "resumed" before
/// they will be able to create a render surface (such as an `EGLSurface`, [`VkSurfaceKHR`]
/// or [`wgpu::Surface`]) which depend on having a [`NativeWindow`]. Applications must handle
/// [`destroy_surfaces()`], where their render surfaces are invalid and should be dropped.
/// or [`wgpu::Surface`]) which depend on having a [`SurfaceView`]. Applications must also
/// assume that if they are [suspended], then their render surfaces are invalid and should
/// be dropped.
///
/// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window
/// [`Surface`]: https://developer.android.com/reference/android/view/Surface
/// [`onStart`]: https://developer.android.com/reference/android/app/Activity#onStart()
/// [suspended]: Self::destroy_surfaces
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// [`can_create_surfaces()`]: Self::can_create_surfaces()
/// [`destroy_surfaces()`]: Self::destroy_surfaces()
/// [`can_create_surfaces()`]: Self::can_create_surfaces
/// [`destroy_surfaces()`]: Self::destroy_surfaces
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop);
/// Called after a wake up is requested using [`EventLoopProxy::wake_up()`].
@@ -206,7 +198,7 @@ pub trait ApplicationHandler {
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
device_id: DeviceId,
event: DeviceEvent,
) {
let _ = (event_loop, device_id, event);
@@ -245,30 +237,18 @@ pub trait ApplicationHandler {
/// ### Web
///
/// On Web, the [`suspended()`] method is called in response to a [`pagehide`] event if the
/// page is being stored in the [`bfcache`] (back/forward cache) - an in-memory cache that
/// page is being restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
/// stores a complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
/// [`bfcache`]: https://web.dev/bfcache/
///
/// ### Android
///
/// On Android, the [`suspended()`] method is called when the `Activity` is no longer visible
/// to the user. This is a good place to stop refreshing UI, running animations and other visual
/// things. It is driven by Android's [`onStop()`] method.
///
/// After this event the application either receives [`resumed()`] again, or [`exiting()`].
///
/// [`onStop()`]: https://developer.android.com/reference/android/app/Activity#onStop()
///
/// ### Others
///
/// **macOS / Orbital / Wayland / Windows / X11:** Unsupported.
/// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported.
///
/// [`resumed()`]: Self::resumed()
/// [`suspended()`]: Self::suspended()
/// [`exiting()`]: Self::exiting()
/// [`suspended()`]: Self::suspended
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
@@ -281,22 +261,24 @@ pub trait ApplicationHandler {
///
/// ### Android
///
/// On Android, the [`destroy_surfaces()`] method is called when the application's
/// [`NativeWindow`] (native [`Surface`]) is destroyed. This is expected to closely correlate
/// with the [`onStop`] lifecycle event which typically results in the surface to be destroyed
/// after the app becomes invisible.
/// On Android, the [`destroy_surfaces()`] method is called when the application's associated
/// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`]
/// lifecycle event but there may technically be a discrepancy.
///
/// Applications that need to run on Android should assume their [`NativeWindow`] has been
/// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause()
///
/// Applications that need to run on Android should assume their [`SurfaceView`] has been
/// destroyed, which indirectly invalidates any existing render surfaces that may have been
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
///
/// When receiving [`destroy_surfaces()`] Android applications should drop all render surfaces
/// before the event callback completes, which may be re-created when the application next
/// receives [`can_create_surfaces()`].
/// After being [suspended] on Android applications must drop all render surfaces before
/// the event callback completes, which may be re-created when the application is next
/// [resumed].
///
/// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window
/// [`Surface`]: https://developer.android.com/reference/android/view/Surface
/// [`onStop`]: https://developer.android.com/reference/android/app/Activity#onStop()
/// [suspended]: Self::destroy_surfaces
/// [resumed]: Self::can_create_surfaces
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
@@ -304,8 +286,8 @@ pub trait ApplicationHandler {
///
/// - **iOS / macOS / Orbital / Wayland / Web / Windows / X11:** Unsupported.
///
/// [`can_create_surfaces()`]: Self::can_create_surfaces()
/// [`destroy_surfaces()`]: Self::destroy_surfaces()
/// [`can_create_surfaces()`]: Self::can_create_surfaces
/// [`destroy_surfaces()`]: Self::destroy_surfaces
fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
@@ -346,18 +328,22 @@ pub trait ApplicationHandler {
let _ = event_loop;
}
/// The macOS-specific handler.
/// Get the [`ApplicationHandler`] as [`Any`].
///
/// The return value from this should not change at runtime.
#[cfg(any(docsrs, macos_platform))]
/// This is useful for downcasting to a concrete application type.
#[inline(always)]
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
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);
@@ -392,7 +378,7 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
device_id: DeviceId,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
@@ -422,16 +408,15 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
#[cfg(any(docsrs, macos_platform))]
#[inline]
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
(**self).macos_handler()
}
}
#[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);
@@ -466,7 +451,7 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
device_id: DeviceId,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
@@ -496,10 +481,4 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
#[cfg(any(docsrs, macos_platform))]
#[inline]
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
(**self).macos_handler()
}
}

View File

@@ -43,37 +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`.
- Add `ApplicationHandlerExtMacOS` trait, and a `macos_handler` method to `ApplicationHandler` which returns a `dyn ApplicationHandlerExtMacOS` which allows for macOS specific extensions to winit.
- Add a `standard_key_binding` method to the `ApplicationHandlerExtMacOS` trait. This allows handling of standard keybindings such as "go to end of line" on macOS.
- 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.
- Add `WindowId::into_raw()` and `from_raw()`.
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId` and `position` to all pointer
events as part of the pointer event overhaul.
- Add `DeviceId::into_raw()` and `from_raw()`.
### 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`.
@@ -86,14 +78,14 @@ changelog entry.
- Changed `EventLoopProxy::send_event` to `EventLoopProxy::wake_up`, it now
only wakes up the loop.
- On X11, implement smooth resizing through the sync extension API.
- `ApplicationHandler::can_create|destroy_surfaces()` was split off from
- `ApplicationHandler::create|destroy_surfaces()` was split off from
`ApplicationHandler::resumed/suspended()`.
`ApplicationHandler::can_create_surfaces()` should, for portability reasons
to Android, be the only place to create render surfaces.
`ApplicationHandler::resumed/suspended()` are now only emitted by iOS, Web
and Android, and now signify actually resuming/suspending the application.
`ApplicationHandler::resumed/suspended()` are now only emitted by iOS and Web
and now signify actually resuming/suspending the application.
- Rename `platform::web::*ExtWebSys` to `*ExtWeb`.
- Change signature of `EventLoop::run_app`, `EventLoopExtPumpEvents::pump_app_events` and
`EventLoopExtRunOnDemand::run_app_on_demand` to accept a `impl ApplicationHandler` directly,
@@ -105,53 +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.
- Every event carrying a `DeviceId` now uses `Option<DeviceId>` instead. A `None` value signifies that the
device can't be uniquely identified.
- Pointer `WindowEvent`s were overhauled. The new events can handle any type of pointer, serving as
a single pointer input source. Now your application can handle any pointer type without having to
explicitly handle e.g. `Touch`:
- Rename `CursorMoved` to `PointerMoved`.
- Rename `CursorEntered` to `PointerEntered`.
- Rename `CursorLeft` to `PointerLeft`.
- Rename `MouseInput` to `PointerButton`.
- Add `position` to every `PointerEvent`.
- `PointerMoved` is **not sent** after `PointerEntered` anymore.
- Remove `Touch`, which is folded into the `Pointer*` events.
- New `PointerKind` added to `PointerEntered` and `PointerLeft`, signifying which pointer type is
the source of this event.
- New `PointerSource` added to `PointerMoved`, similar to `PointerKind` but holding additional
data.
- New `ButtonSource` added to `PointerButton`, similar to `PointerKind` but holding pointer type
specific buttons. Use `ButtonSource::mouse_button()` to easily normalize any pointer button
type to a generic mouse button.
- New `FingerId` added to `PointerKind::Touch` and `PointerSource::Touch` able to uniquely
identify a finger in a multi-touch interaction. Replaces the old `Touch::id`.
- On Web and Windows, add `FingerIdExt*::is_primary()`, exposing a way to determine
the primary finger in a multi-touch interaction.
- In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`.
- Remove `Force::Calibrated::altitude_angle`.
### Removed
@@ -174,21 +119,14 @@ changelog entry.
v0.5 support. v0.6 remains in place and is enabled by default.
- Remove `DeviceEvent::Added` and `DeviceEvent::Removed`.
- Remove `DeviceEvent::Motion` and `WindowEvent::AxisMotion`.
- Remove `Touch::id` in favor of `Touch::finger_id`.
- Remove `MonitorHandle::size()` and `refresh_rate_millihertz()` in favor of
`MonitorHandle::current_video_mode()`.
- On Android, remove all `MonitorHandle` support instead of emitting false data.
- Remove `impl From<u64> for WindowId` and `impl From<WindowId> for u64`. Replaced with
`WindowId::into_raw()` and `from_raw()`.
- Remove `dummy()` from `WindowId` and `DeviceId`.
- Remove `WindowEvent::Touch` and `Touch` in favor of the new `PointerKind`, `PointerSource` and
`ButtonSource` as part of the new pointer event overhaul.
- Remove `Force::altitude_angle`.
### 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.
- On macOS, fix `WindowEvent::Moved` sometimes being triggered unnecessarily on resize.
- On macOS, package manifest definitions of `LSUIElement` will no longer be overridden with the
default activation policy, unless explicitly provided during initialization.
- On macOS, fix crash when calling `drag_window()` without a left click present.
- On X11, key events forward to IME anyway, even when it's disabled.

View File

@@ -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

View File

@@ -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());
/// }
/// # }
/// ```

View File

@@ -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())
);
}
}

View File

@@ -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;
@@ -63,51 +63,51 @@ use crate::window::{ActivationToken, Theme, WindowId};
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum Event {
/// See [`ApplicationHandler::new_events()`] for details.
/// See [`ApplicationHandler::new_events`] for details.
///
/// [`ApplicationHandler::new_events()`]: crate::application::ApplicationHandler::new_events()
/// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events
NewEvents(StartCause),
/// See [`ApplicationHandler::window_event()`] for details.
/// See [`ApplicationHandler::window_event`] for details.
///
/// [`ApplicationHandler::window_event()`]: crate::application::ApplicationHandler::window_event()
/// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event
#[allow(clippy::enum_variant_names)]
WindowEvent { window_id: WindowId, event: WindowEvent },
/// See [`ApplicationHandler::device_event()`] for details.
/// See [`ApplicationHandler::device_event`] for details.
///
/// [`ApplicationHandler::device_event()`]: crate::application::ApplicationHandler::device_event()
/// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event
#[allow(clippy::enum_variant_names)]
DeviceEvent { device_id: Option<DeviceId>, event: DeviceEvent },
DeviceEvent { device_id: DeviceId, event: DeviceEvent },
/// See [`ApplicationHandler::suspended()`] for details.
/// See [`ApplicationHandler::suspended`] for details.
///
/// [`ApplicationHandler::suspended()`]: crate::application::ApplicationHandler::suspended()
/// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended
Suspended,
/// See [`ApplicationHandler::can_create_surfaces()`] for details.
/// See [`ApplicationHandler::can_create_surfaces`] for details.
///
/// [`ApplicationHandler::can_create_surfaces()`]: crate::application::ApplicationHandler::can_create_surfaces()
/// [`ApplicationHandler::can_create_surfaces`]: crate::application::ApplicationHandler::can_create_surfaces
CreateSurfaces,
/// See [`ApplicationHandler::resumed()`] for details.
/// See [`ApplicationHandler::resumed`] for details.
///
/// [`ApplicationHandler::resumed()`]: crate::application::ApplicationHandler::resumed()
/// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
Resumed,
/// See [`ApplicationHandler::about_to_wait()`] for details.
/// See [`ApplicationHandler::about_to_wait`] for details.
///
/// [`ApplicationHandler::about_to_wait()`]: crate::application::ApplicationHandler::about_to_wait()
/// [`ApplicationHandler::about_to_wait`]: crate::application::ApplicationHandler::about_to_wait
AboutToWait,
/// See [`ApplicationHandler::exiting()`] for details.
/// See [`ApplicationHandler::exiting`] for details.
///
/// [`ApplicationHandler::exiting()`]: crate::application::ApplicationHandler::exiting()
/// [`ApplicationHandler::exiting`]: crate::application::ApplicationHandler::exiting
LoopExiting,
/// See [`ApplicationHandler::memory_warning()`] for details.
/// See [`ApplicationHandler::memory_warning`] for details.
///
/// [`ApplicationHandler::memory_warning()`]: crate::application::ApplicationHandler::memory_warning()
/// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning
MemoryWarning,
/// User requested a wake up.
@@ -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.
///
@@ -199,7 +194,7 @@ pub enum WindowEvent {
/// numpad keys act as if NumLock wasn't active. When this is used, the OS sends fake key
/// events which are not marked as `is_synthetic`.
KeyboardInput {
device_id: Option<DeviceId>,
device_id: DeviceId,
event: KeyEvent,
/// If `true`, the event was generated synthetically by winit
@@ -226,89 +221,52 @@ pub enum WindowEvent {
/// - **iOS / Android / Web / Orbital:** Unsupported.
Ime(Ime),
/// The pointer has moved on the window.
PointerMoved {
device_id: Option<DeviceId>,
/// The cursor has moved on the window.
///
/// ## Platform-specific
///
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
CursorMoved {
device_id: DeviceId,
/// (x,y) coordinates in pixels relative to the top-left corner of the window. Because the
/// range of this data is limited by the display area and it may have been
/// transformed by the OS to implement effects such as pointer acceleration, it
/// should not be used to implement non-pointer-like interactions such as 3D camera
/// control. For that, consider [`DeviceEvent::PointerMotion`].
///
/// ## Platform-specific
///
/// **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range
/// of this data is limited by the display area and it may have been transformed by
/// the OS to implement effects such as cursor acceleration, it should not be used
/// to implement non-cursor-like interactions such as 3D camera control.
position: PhysicalPosition<f64>,
source: PointerSource,
},
/// The pointer has entered the window.
PointerEntered {
device_id: Option<DeviceId>,
/// The cursor has entered the window.
///
/// ## Platform-specific
///
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
CursorEntered { device_id: DeviceId },
/// The position of the pointer when it entered the window.
///
/// ## Platform-specific
///
/// - **Orbital: Always emits `(0., 0.)`.
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: PhysicalPosition<f64>,
kind: PointerKind,
},
/// The pointer has left the window.
PointerLeft {
device_id: Option<DeviceId>,
/// The position of the pointer when it left the window. The position reported can be
/// outside the bounds of the window.
///
/// ## Platform-specific
///
/// - **Orbital/Windows:** Always emits [`None`].
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: Option<PhysicalPosition<f64>>,
kind: PointerKind,
},
/// The cursor has left the window.
///
/// ## Platform-specific
///
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
CursorLeft { device_id: DeviceId },
/// A mouse wheel movement or touchpad scroll occurred.
MouseWheel { device_id: Option<DeviceId>, delta: MouseScrollDelta, phase: TouchPhase },
MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase },
/// An mouse button press has been received.
PointerButton {
device_id: Option<DeviceId>,
state: ElementState,
/// The position of the pointer when the button was pressed.
///
/// ## Platform-specific
///
/// - **Orbital: Always emits `(0., 0.)`.
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: PhysicalPosition<f64>,
button: ButtonSource,
},
MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton },
/// Two-finger pinch gesture, often used for magnification.
///
@@ -317,7 +275,7 @@ pub enum WindowEvent {
/// - Only available on **macOS** and **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
PinchGesture {
device_id: Option<DeviceId>,
device_id: DeviceId,
/// Positive values indicate magnification (zooming in) and negative
/// values indicate shrinking (zooming out).
///
@@ -333,7 +291,7 @@ pub enum WindowEvent {
/// - Only available on **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
PanGesture {
device_id: Option<DeviceId>,
device_id: DeviceId,
/// Change in pixels of pan gesture from last update.
delta: PhysicalPosition<f32>,
phase: TouchPhase,
@@ -357,7 +315,7 @@ pub enum WindowEvent {
///
/// - Only available on **macOS 10.8** and later, and **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
DoubleTapGesture { device_id: Option<DeviceId> },
DoubleTapGesture { device_id: DeviceId },
/// Two-finger rotation gesture.
///
@@ -369,7 +327,7 @@ pub enum WindowEvent {
/// - Only available on **macOS** and **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
RotationGesture {
device_id: Option<DeviceId>,
device_id: DeviceId,
/// change in rotation in degrees
delta: f32,
phase: TouchPhase,
@@ -380,7 +338,19 @@ pub enum WindowEvent {
/// At the moment, only supported on Apple forcetouch-capable macbooks.
/// The parameters are: pressure level (value between 0 and 1 representing how hard the
/// touchpad is being pressed) and stage (integer representing the click level).
TouchpadPressure { device_id: Option<DeviceId>, pressure: f32, stage: i64 },
TouchpadPressure { device_id: DeviceId, pressure: f32, stage: i64 },
/// Touch event has been received
///
/// ## Platform-specific
///
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
/// - **macOS:** Unsupported.
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
Touch(Touch),
/// The window's scale factor has changed.
///
@@ -390,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.
@@ -455,129 +425,6 @@ pub enum WindowEvent {
RedrawRequested,
}
/// Represents the kind type of a pointer event.
///
/// ## Platform-specific
///
/// **Wayland/X11:** [`Unknown`](Self::Unknown) device types are converted to known variants by the
/// system.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerKind {
Mouse,
/// See [`PointerSource::Touch`] for more details.
///
/// ## Platform-specific
///
/// **macOS:** Unsupported.
Touch(FingerId),
Unknown,
}
/// Represents the pointer type and its data for a pointer event.
///
/// **Wayland/X11:** [`Unknown`](Self::Unknown) device types are converted to known variants by the
/// system.
#[derive(Clone, Debug, PartialEq)]
pub enum PointerSource {
Mouse,
/// Represents a touch event.
///
/// Every time the user touches the screen, a [`WindowEvent::PointerEntered`] and a
/// [`WindowEvent::PointerButton`] with [`ElementState::Pressed`] event with an unique
/// identifier for the finger is emitted. When a finger is lifted, a
/// [`WindowEvent::PointerButton`] with [`ElementState::Released`] and a
/// [`WindowEvent::PointerLeft`] event is generated with the same [`FingerId`].
///
/// After a [`WindowEvent::PointerEntered`] event has been emitted, there may be zero or more
/// [`WindowEvent::PointerMoved`] events when the finger is moved or the touch pressure
/// changes.
///
/// A [`WindowEvent::PointerLeft`] without a [`WindowEvent::PointerButton`] with
/// [`ElementState::Released`] event is emitted when the system has canceled tracking this
/// touch, such as when the window loses focus, or on mobile devices if the user moves the
/// device against their face.
///
/// The [`FingerId`] may be reused by the system after a [`WindowEvent::PointerLeft`] event.
/// The user should assume that a new [`WindowEvent::PointerEntered`] event received with the
/// same ID has nothing to do with the old finger and is a new finger.
///
/// ## Platform-specific
///
/// **macOS:** Unsupported.
Touch {
finger_id: FingerId,
/// Describes how hard the screen was pressed. May be [`None`] if the hardware does not
/// support pressure sensitivity.
///
/// ## Platform-specific
///
/// - **MacOS / Orbital / Wayland / X11:** Always emits [`None`].
/// - **Android:** Will never be [`None`]. If the device doesn't support pressure
/// sensitivity, force will either be 0.0 or 1.0. Also see the
/// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE).#[derive(Debug, Clone, Copy, PartialEq)]
/// - **Web:** Will never be [`None`]. If the device doesn't support pressure sensitivity,
/// force will be 0.5 when a button is pressed or 0.0 otherwise.
force: Option<Force>,
},
Unknown,
}
impl From<PointerSource> for PointerKind {
fn from(source: PointerSource) -> Self {
match source {
PointerSource::Mouse => Self::Mouse,
PointerSource::Touch { finger_id, .. } => Self::Touch(finger_id),
PointerSource::Unknown => Self::Unknown,
}
}
}
/// Represents the pointer type of a [`WindowEvent::PointerButton`].
///
/// **Wayland/X11:** [`Unknown`](Self::Unknown) device types are converted to known variants by the
/// system.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ButtonSource {
Mouse(MouseButton),
/// See [`PointerSource::Touch`] for more details.
///
/// ## Platform-specific
///
/// **macOS:** Unsupported.
Touch {
finger_id: FingerId,
force: Option<Force>,
},
Unknown(u16),
}
impl ButtonSource {
/// Convert any [`ButtonSource`] to an equivalent [`MouseButton`]. If a pointer type has no
/// special handling in an application, this method can be used to handle it like any generic
/// mouse input.
pub fn mouse_button(self) -> MouseButton {
match self {
ButtonSource::Mouse(mouse) => mouse,
ButtonSource::Touch { .. } => MouseButton::Left,
ButtonSource::Unknown(button) => match button {
0 => MouseButton::Left,
1 => MouseButton::Middle,
2 => MouseButton::Right,
3 => MouseButton::Back,
4 => MouseButton::Forward,
_ => MouseButton::Other(button),
},
}
}
}
impl From<MouseButton> for ButtonSource {
fn from(mouse: MouseButton) -> Self {
Self::Mouse(mouse)
}
}
/// Identifier of an input device.
///
/// Whenever you receive an event arising from a particular input device, this event contains a
@@ -585,23 +432,24 @@ impl From<MouseButton> for ButtonSource {
/// on-screen cursor and keyboard focus) or physical. Virtual devices typically aggregate inputs
/// from multiple physical devices.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(i64);
pub struct DeviceId(pub(crate) platform_impl::DeviceId);
impl Default for DeviceId {
fn default() -> Self {
Self::dummy()
}
}
impl DeviceId {
/// Convert the [`DeviceId`] into the underlying integer.
/// Returns a dummy id, useful for unit testing.
///
/// This is useful if you need to pass the ID across an FFI boundary, or store it in an atomic.
#[allow(dead_code)]
pub(crate) const fn into_raw(self) -> i64 {
self.0
}
/// Construct a [`DeviceId`] from the underlying integer.
/// # Notes
///
/// This should only be called with integers returned from [`DeviceId::into_raw`].
#[allow(dead_code)]
pub(crate) const fn from_raw(id: i64) -> Self {
Self(id)
/// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `DeviceId`.
pub const fn dummy() -> Self {
DeviceId(platform_impl::DeviceId::dummy())
}
}
@@ -613,8 +461,14 @@ impl DeviceId {
pub struct FingerId(pub(crate) platform_impl::FingerId);
impl FingerId {
#[cfg(test)]
pub(crate) const fn dummy() -> Self {
/// Returns a dummy id, useful for unit testing.
///
/// # Notes
///
/// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `FingerId`.
pub const fn dummy() -> Self {
FingerId(platform_impl::FingerId::dummy())
}
}
@@ -624,7 +478,7 @@ impl FingerId {
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera
/// or first-person game controls. Many physical actions, such as mouse movement, can produce both
/// device and window events. Because window events typically arise from virtual devices
/// (corresponding to GUI pointers and keyboard focus) the device IDs may not match.
/// (corresponding to GUI cursors and keyboard focus) the device IDs may not match.
///
/// Note that these events are delivered regardless of input focus.
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -632,7 +486,7 @@ pub enum DeviceEvent {
/// Change in physical position of a pointing device.
///
/// This represents raw, unfiltered physical motion. Not to be confused with
/// [`WindowEvent::PointerMoved`].
/// [`WindowEvent::CursorMoved`].
///
/// ## Platform-specific
///
@@ -649,7 +503,7 @@ pub enum DeviceEvent {
///
#[rustfmt::skip]
/// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked
PointerMotion {
MouseMotion {
/// (x, y) change in position in unspecified units.
///
/// Different devices may use different units.
@@ -978,6 +832,50 @@ pub enum TouchPhase {
Cancelled,
}
/// Represents a touch event
///
/// Every time the user touches the screen, a new [`TouchPhase::Started`] event with an unique
/// identifier for the finger is generated. When the finger is lifted, an [`TouchPhase::Ended`]
/// event is generated with the same finger id.
///
/// After a `Started` event has been emitted, there may be zero or more `Move`
/// events when the finger is moved or the touch pressure changes.
///
/// The finger id may be reused by the system after an `Ended` event. The user
/// should assume that a new `Started` event received with the same id has nothing
/// to do with the old finger and is a new finger.
///
/// A [`TouchPhase::Cancelled`] event is emitted when the system has canceled tracking this
/// touch, such as when the window loses focus, or on iOS if the user moves the
/// device against their face.
///
/// ## Platform-specific
///
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
/// - **macOS:** Unsupported.
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Touch {
pub device_id: DeviceId,
pub phase: TouchPhase,
pub location: PhysicalPosition<f64>,
/// Describes how hard the screen was pressed. May be `None` if the platform
/// does not support pressure sensitivity.
///
/// ## Platform-specific
///
/// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**.
/// - **Android**: This will never be [None]. If the device doesn't support pressure
/// sensitivity, force will either be 0.0 or 1.0. Also see the
/// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE).
pub force: Option<Force>,
/// Unique identifier of a finger.
pub finger_id: FingerId,
}
/// Describes the force of a touch event
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -998,6 +896,12 @@ pub enum Force {
/// The value of this field is sufficiently high to provide a wide
/// dynamic range for values of the `force` field.
max_possible_force: f64,
/// The altitude (in radians) of the stylus.
///
/// A value of 0 radians indicates that the stylus is parallel to the
/// surface. The value of this property is Pi/2 when the stylus is
/// perpendicular to the surface.
altitude_angle: Option<f64>,
},
/// If the platform reports the force as normalized, we have no way of
/// knowing how much pressure 1.0 corresponds to we know it's the maximum
@@ -1014,7 +918,13 @@ impl Force {
/// consistent across devices.
pub fn normalized(&self) -> f64 {
match self {
Force::Calibrated { force, max_possible_force } => force / max_possible_force,
Force::Calibrated { force, max_possible_force, altitude_angle } => {
let force = match altitude_angle {
Some(altitude_angle) => force / altitude_angle.sin(),
None => *force,
};
force / max_possible_force
},
Force::Normalized(force) => *force,
}
}
@@ -1085,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 {
@@ -1130,6 +1041,7 @@ mod tests {
($closure:expr) => {{
#[allow(unused_mut)]
let mut x = $closure;
let did = event::DeviceId::dummy();
let fid = event::FingerId::dummy();
#[allow(deprecated)]
@@ -1137,11 +1049,10 @@ mod tests {
use crate::event::Event::*;
use crate::event::Ime::Enabled;
use crate::event::WindowEvent::*;
use crate::event::{PointerKind, PointerSource};
use crate::window::WindowId;
// Mainline events.
let wid = WindowId::from_raw(0);
let wid = WindowId::dummy();
x(NewEvents(event::StartCause::Init));
x(AboutToWait);
x(LoopExiting);
@@ -1155,64 +1066,49 @@ 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);
with_window_event(Ime(Enabled));
with_window_event(PointerMoved {
device_id: None,
position: (0, 0).into(),
source: PointerSource::Mouse,
});
with_window_event(CursorMoved { device_id: did, position: (0, 0).into() });
with_window_event(ModifiersChanged(event::Modifiers::default()));
with_window_event(PointerEntered {
device_id: None,
position: (0, 0).into(),
kind: PointerKind::Mouse,
});
with_window_event(PointerLeft {
device_id: None,
position: Some((0, 0).into()),
kind: PointerKind::Mouse,
});
with_window_event(CursorEntered { device_id: did });
with_window_event(CursorLeft { device_id: did });
with_window_event(MouseWheel {
device_id: None,
device_id: did,
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
phase: event::TouchPhase::Started,
});
with_window_event(PointerButton {
device_id: None,
with_window_event(MouseInput {
device_id: did,
state: event::ElementState::Pressed,
position: (0, 0).into(),
button: event::MouseButton::Other(0).into(),
});
with_window_event(PointerButton {
device_id: None,
state: event::ElementState::Released,
position: (0, 0).into(),
button: event::ButtonSource::Touch {
finger_id: fid,
force: Some(event::Force::Normalized(0.0)),
},
button: event::MouseButton::Other(0),
});
with_window_event(PinchGesture {
device_id: None,
device_id: did,
delta: 0.0,
phase: event::TouchPhase::Started,
});
with_window_event(DoubleTapGesture { device_id: None });
with_window_event(DoubleTapGesture { device_id: did });
with_window_event(RotationGesture {
device_id: None,
device_id: did,
delta: 0.0,
phase: event::TouchPhase::Started,
});
with_window_event(PanGesture {
device_id: None,
device_id: did,
delta: PhysicalPosition::<f32>::new(0.0, 0.0),
phase: event::TouchPhase::Started,
});
with_window_event(TouchpadPressure { device_id: None, pressure: 0.0, stage: 0 });
with_window_event(TouchpadPressure { device_id: did, pressure: 0.0, stage: 0 });
with_window_event(Touch(event::Touch {
device_id: did,
phase: event::TouchPhase::Started,
location: (0.0, 0.0).into(),
finger_id: fid,
force: Some(event::Force::Normalized(0.0)),
}));
with_window_event(ThemeChanged(crate::window::Theme::Light));
with_window_event(Occluded(true));
}
@@ -1222,9 +1118,9 @@ mod tests {
use event::DeviceEvent::*;
let with_device_event =
|dev_ev| x(event::Event::DeviceEvent { device_id: None, event: dev_ev });
|dev_ev| x(event::Event::DeviceEvent { device_id: did, event: dev_ev });
with_device_event(PointerMotion { delta: (0.0, 0.0).into() });
with_device_event(MouseMotion { delta: (0.0, 0.0).into() });
with_device_event(MouseWheel {
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
});
@@ -1247,10 +1143,15 @@ mod tests {
let force = event::Force::Normalized(0.0);
assert_eq!(force.normalized(), 0.0);
let force2 = event::Force::Calibrated { force: 5.0, max_possible_force: 2.5 };
let force2 =
event::Force::Calibrated { force: 5.0, max_possible_force: 2.5, altitude_angle: None };
assert_eq!(force2.normalized(), 2.0);
let force3 = event::Force::Calibrated { force: 5.0, max_possible_force: 2.5 };
let force3 = event::Force::Calibrated {
force: 5.0,
max_possible_force: 2.5,
altitude_angle: Some(std::f64::consts::PI / 2.0),
};
assert_eq!(force3.normalized(), 2.0);
}
@@ -1262,18 +1163,29 @@ mod tests {
});
let _ = event::StartCause::Init.clone();
let did = crate::event::DeviceId::dummy().clone();
let fid = crate::event::FingerId::dummy().clone();
HashSet::new().insert(fid);
let mut set = [fid, fid, fid];
HashSet::new().insert(did);
let mut set = [did, did, did];
set.sort_unstable();
let mut set2 = BTreeSet::new();
set2.insert(fid);
set2.insert(fid);
set2.insert(did);
set2.insert(did);
HashSet::new().insert(event::TouchPhase::Started.clone());
HashSet::new().insert(event::MouseButton::Left.clone());
HashSet::new().insert(event::Ime::Enabled);
let _ = event::Force::Calibrated { force: 0.0, max_possible_force: 0.0 }.clone();
let _ = event::Touch {
device_id: did,
phase: event::TouchPhase::Started,
location: (0.0, 0.0).into(),
finger_id: fid,
force: Some(event::Force::Normalized(0.0)),
}
.clone();
let _ =
event::Force::Calibrated { force: 0.0, max_possible_force: 0.0, altitude_angle: None }
.clone();
}
}

View File

@@ -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
@@ -72,7 +72,7 @@ impl EventLoopBuilder {
/// Attempting to create the event loop off the main thread will panic. This
/// restriction isn't strictly necessary on all platforms, but is imposed to
/// eliminate any nasty surprises when porting to platforms that require it.
/// `EventLoopBuilderExt::with_any_thread` functions are exposed in the relevant
/// `EventLoopBuilderExt::any_thread` functions are exposed in the relevant
/// [`platform`] module if the target platform supports creating an event
/// loop on any thread.
///
@@ -268,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)
}
}
@@ -308,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;
@@ -321,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.
///
@@ -334,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.
///
@@ -406,21 +403,19 @@ 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 cloneable handle to the underlying
/// The purpose of this type is to provide a cheaply clonable handle to the underlying
/// display handle. This is often used by graphics APIs to connect to the underlying APIs.
/// It is difficult to keep a handle to the [`EventLoop`] type or the [`ActiveEventLoop`]
/// type. In contrast, this type involves no lifetimes and can be persisted for as long as

View File

@@ -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) {
@@ -147,62 +147,6 @@
//! See the [`platform`] module for documentation on platform-specific cargo
//! features.
//!
//! # Platform/Architecture Support
//!
//! Platform support on `winit` has two tiers: Tier 1 and Tier 2.
//!
//! - Tier 1 is **guaranteed to work**. Targets in this tier are actively tested both in CI and by
//! maintainers.
//! - Tier 2 is **guaranteed to build**. Code compilation is tested in CI, but deeper testing is not
//! done.
//!
//! Please open an issue if you would like to add a Tier 2 target, or if you would
//! like a Tier 2 target moved to Tier 1.
//!
//! ## Tier 1 Targets
//!
//! |Target Name |Target Triple |APIs |
//! |-------------------------------|------------------------------------|---------------|
//! |32-Bit x86 Windows with MSVC |`i686-pc-windows-msvc` |Win32 |
//! |64-Bit x86 Windows with MSVC |`x86_64-pc-windows-msvc` |Win32 |
//! |32-Bit x86 Windows with glibc |`i686-pc-windows-gnu` |Win32 |
//! |64-Bit x86 Windows with glibc |`x86_64-pc-windows-gnu` |Win32 |
//! |32-Bit x86 Linux with glibc |`i686-unknown-linux-gnu` |X11, Wayland |
//! |64-Bit x86 Linux with glibc |`x86_64-unknown-linux-gnu` |X11, Wayland |
//! |64-Bit ARM Android |`aarch64-linux-android` |Android |
//! |64-Bit x86 Redox OS |`x86_64-unknown-redox` |Orbital |
//! |32-Bit x86 Redox OS |`i686-unknown-redox` |Orbital |
//! |64-Bit ARM Redox OS |`aarch64-unknown-redox` |Orbital |
//! |64-bit x64 macOS |`x86_64-apple-darwin` |AppKit |
//! |64-bit ARM macOS |`aarch64-apple-darwin` |AppKit |
//! |32-bit Wasm Web browser |`wasm32-unknown-unknown` |`wasm-bindgen` |
//!
//! ## Tier 2 Targets
//!
//! |Target Name |Target Triple |APIs |
//! |------------------------------------|------------------------------------|---------------|
//! |64-Bit ARM Windows with MSVC |`aarch64-pc-windows-msvc` |Win32 |
//! |32-Bit x86 Windows 7 with MSVC |`i686-win7-windows-msvc` |Win32 |
//! |64-Bit x86 Windows 7 with MSVC |`x86_64-win7-windows-msvc` |Win32 |
//! |64-bit x86 Linux with Musl |`x86_64-unknown-linux-musl` |X11, Wayland |
//! |64-bit x86 Linux with 32-bit glibc |`x86_64-unknown-linux-gnux32` |X11, Wayland |
//! |64-bit x86 Android |`x86_64-linux-android` |Android |
//! |64-bit x64 iOS |`x86_64-apple-ios` |UIKit |
//! |64-bit ARM iOS |`aarch64-apple-ios` |UIKit |
//! |64-bit ARM Mac Catalyst |`aarch64-apple-ios-macabi` |UIKit |
//! |32-bit x86 Android |`i686-linux-android` |Android |
//! |64-bit x86 FreeBSD |`x86_64-unknown-freebsd` |X11, Wayland |
//! |64-bit x86 NetBSD |`x86_64-unknown-netbsd` |X11 |
//! |32-bit x86 Linux with Musl |`i686-unknown-linux-musl` |X11, Wayland |
//! |64-bit RISC-V Linux with glibc |`riscv64gc-unknown-linux-gnu` |X11, Wayland |
//! |64-bit ARM Linux with glibc |`aarch64-unknown-linux-gnu` |X11, Wayland |
//! |64-bit ARM Linux with Musl |`aarch64-unknown-linux-musl` |X11, Wayland |
//! |64-bit PowerPC Linux with glibc |`powerpc64le-unknown-linux-gnu` |X11, Wayland |
//! |32-Bit ARM Linux with glibc |`armv5te-unknown-linux-gnueabi` |X11, Wayland |
//! |64-Bit Linux on IBM Supercomputers |`s390x-unknown-linux-gnu` |X11, Wayland |
//! |32-bit ARM Android |`arm-linux-androideabi` |Android |
//! |64-bit SPARC Linux with glibc |`sparc64-unknown-linux-gnu` |X11, Wayland |
//!
//! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app

View File

@@ -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()

View File

@@ -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();

View File

@@ -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));
}
}

View File

@@ -1,84 +1,27 @@
//! # 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;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::application::ApplicationHandler;
use crate::event_loop::{ActiveEventLoop, EventLoopBuilder};
use crate::monitor::MonitorHandle;
use crate::window::{Window, WindowAttributes, WindowId};
use crate::window::{Window, WindowAttributes};
/// Additional methods on [`Window`] that are specific to MacOS.
pub trait WindowExtMacOS {
@@ -151,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())
}
}
@@ -306,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 {
@@ -376,32 +285,23 @@ 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 {
/// Sets the activation policy for the application. If used, this will override
/// any relevant settings provided in the package manifest.
/// For instance, `with_activation_policy(ActivationPolicy::Regular)` will prevent
/// the application from running as an "agent", even if LSUIElement is set to true.
/// Sets the activation policy for the application.
///
/// If unused, the Winit will honor the package manifest.
/// It is set to [`ActivationPolicy::Regular`] by default.
///
/// # Example
///
/// 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
@@ -419,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
@@ -442,7 +342,7 @@ pub trait EventLoopBuilderExtMacOS {
impl EventLoopBuilderExtMacOS for EventLoopBuilder {
#[inline]
fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self {
self.platform_specific.activation_policy = Some(activation_policy);
self.platform_specific.activation_policy = activation_policy;
self
}
@@ -496,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()
@@ -549,52 +449,3 @@ pub enum OptionAsAlt {
#[default]
None,
}
/// Additional events on [`ApplicationHandler`] that are specific to macOS.
///
/// This can be registered with [`ApplicationHandler::macos_handler`].
pub trait ApplicationHandlerExtMacOS: ApplicationHandler {
/// The system interpreted a keypress as a standard key binding command.
///
/// Examples include inserting tabs and newlines, or moving the insertion point, see
/// [`NSStandardKeyBindingResponding`] for the full list of key bindings. They are often text
/// editing related.
///
/// This corresponds to the [`doCommandBySelector:`] method on `NSTextInputClient`.
///
/// The `action` parameter contains the string representation of the selector. Examples include
/// `"insertBacktab:"`, `"indent:"` and `"noop:"`.
///
/// # Example
///
/// ```ignore
/// impl ApplicationHandlerExtMacOS for App {
/// fn standard_key_binding(
/// &mut self,
/// event_loop: &dyn ActiveEventLoop,
/// window_id: WindowId,
/// action: &str,
/// ) {
/// match action {
/// "moveBackward:" => self.cursor.position -= 1,
/// "moveForward:" => self.cursor.position += 1,
/// _ => {} // Ignore other actions
/// }
/// }
/// }
/// ```
///
/// [`NSStandardKeyBindingResponding`]: https://developer.apple.com/documentation/appkit/nsstandardkeybindingresponding?language=objc
/// [`doCommandBySelector:`]: https://developer.apple.com/documentation/appkit/nstextinputclient/1438256-docommandbyselector?language=objc
#[doc(alias = "doCommandBySelector:")]
fn standard_key_binding(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
action: &str,
) {
let _ = event_loop;
let _ = window_id;
let _ = action;
}
}

View File

@@ -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()
}
}

View File

@@ -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 {

View File

@@ -27,18 +27,19 @@
//! - [`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::PointerMoved`], [`WindowEvent::PointerEntered`] and
//! [`WindowEvent::PointerLeft`].
//! - [`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::PointerMoved`]: crate::event::WindowEvent::PointerMoved
//! [`WindowEvent::PointerEntered`]: crate::event::WindowEvent::PointerEntered
//! [`WindowEvent::PointerLeft`]: crate::event::WindowEvent::PointerLeft
//! [`WindowEvent::CursorMoved`]: crate::event::WindowEvent::CursorMoved
//! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered
//! [`WindowEvent::CursorLeft`]: crate::event::WindowEvent::CursorLeft
//! [`WindowEvent::Touch`]: crate::event::WindowEvent::Touch
//! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position
use std::cell::Ref;
@@ -81,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
@@ -103,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()
}
}
@@ -147,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;
@@ -358,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
@@ -710,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.
@@ -736,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>,
},

View File

@@ -4,13 +4,10 @@
//! tested regularly.
use std::borrow::Borrow;
use std::ffi::c_void;
use std::ops::Deref;
use std::path::Path;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(windows_platform)]
use windows_sys::Win32::Foundation::HANDLE;
use crate::dpi::PhysicalSize;
use crate::event::{DeviceId, FingerId};
@@ -118,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() }
@@ -319,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;
@@ -343,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]
@@ -385,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))
@@ -418,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
@@ -445,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)]
@@ -658,15 +667,10 @@ pub trait DeviceIdExtWindows {
fn persistent_identifier(&self) -> Option<String>;
}
#[cfg(windows_platform)]
impl DeviceIdExtWindows for DeviceId {
#[inline]
fn persistent_identifier(&self) -> Option<String> {
let raw_id = self.into_raw();
if raw_id != 0 {
crate::platform_impl::raw_input::get_raw_input_device_name(raw_id as HANDLE)
} else {
None
}
self.0.persistent_identifier()
}
}

View File

@@ -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).
@@ -80,7 +80,9 @@ pub type XWindow = u32;
#[inline]
pub fn register_xlib_error_hook(hook: XlibErrorHook) {
// Append new hook.
crate::platform_impl::XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
unsafe {
crate::platform_impl::XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
}
}
/// Additional methods on [`ActiveEventLoop`] that are specific to X11.
@@ -89,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()
@@ -136,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 {
@@ -169,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;
@@ -184,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(()) }
/// ```

View File

@@ -1,3 +1,4 @@
use std::any::Any;
use std::cell::Cell;
use std::hash::Hash;
use std::num::{NonZeroU16, NonZeroU32};
@@ -14,18 +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, DeviceId, 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, WindowId,
WindowLevel,
self, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose, ResizeDirection, Theme,
Window as RootWindow, WindowAttributes, WindowButtons, WindowLevel,
};
mod keycodes;
@@ -123,9 +124,6 @@ impl Default for PlatformSpecificEventLoopAttributes {
}
}
// Android currently only supports one window
const GLOBAL_WINDOW: WindowId = WindowId::from_raw(0);
impl EventLoop {
pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes,
@@ -191,38 +189,41 @@ impl EventLoop {
},
MainEvent::GainedFocus => {
HAS_FOCUS.store(true, Ordering::Relaxed);
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::Focused(true);
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
app.window_event(&self.window_target, window_id, event);
},
MainEvent::LostFocus => {
HAS_FOCUS.store(false, Ordering::Relaxed);
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::Focused(false);
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
app.window_event(&self.window_target, window_id, event);
},
MainEvent::ConfigChanged { .. } => {
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,
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
app.window_event(&self.window_target, window_id, event);
}
},
MainEvent::LowMemory => {
app.memory_warning(&self.window_target);
},
MainEvent::Start => {
app.resumed(self.window_target());
// XXX: how to forward this state to applications?
warn!("TODO: forward onStart notification to application");
},
MainEvent::Resume { .. } => {
debug!("App Resumed - is running");
// TODO: This is incorrect - will be solved in https://github.com/rust-windowing/winit/pull/3897
self.running = true;
},
MainEvent::SaveState { .. } => {
@@ -232,11 +233,11 @@ impl EventLoop {
},
MainEvent::Pause => {
debug!("App Paused - stopped running");
// TODO: This is incorrect - will be solved in https://github.com/rust-windowing/winit/pull/3897
self.running = false;
},
MainEvent::Stop => {
app.suspended(self.window_target());
// XXX: how to forward this state to applications?
warn!("TODO: forward onStop notification to application");
},
MainEvent::Destroy => {
// XXX: maybe exit mainloop to drop things before being
@@ -287,15 +288,17 @@ impl EventLoop {
} else {
PhysicalSize::new(0, 0)
};
let event = event::WindowEvent::SurfaceResized(size);
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::Resized(size);
app.window_event(&self.window_target, window_id, event);
}
pending_redraw |= self.redraw_flag.get_and_reset();
if pending_redraw {
pending_redraw = false;
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::RedrawRequested;
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
app.window_event(&self.window_target, window_id, event);
}
}
@@ -314,116 +317,50 @@ impl EventLoop {
let mut input_status = InputStatus::Handled;
match event {
InputEvent::MotionEvent(motion_event) => {
let device_id = Some(DeviceId::from_raw(motion_event.device_id() as i64));
let action = motion_event.action();
let window_id = window::WindowId(WindowId);
let device_id = event::DeviceId(DeviceId(motion_event.device_id()));
let pointers: Option<
Box<dyn Iterator<Item = android_activity::input::Pointer<'_>>>,
> = match action {
MotionAction::Down
| MotionAction::PointerDown
| MotionAction::Up
| MotionAction::PointerUp => Some(Box::new(std::iter::once(
motion_event.pointer_at_index(motion_event.pointer_index()),
))),
MotionAction::Move | MotionAction::Cancel => {
Some(Box::new(motion_event.pointers()))
let phase = match motion_event.action() {
MotionAction::Down | MotionAction::PointerDown => {
Some(event::TouchPhase::Started)
},
MotionAction::Up | MotionAction::PointerUp => Some(event::TouchPhase::Ended),
MotionAction::Move => Some(event::TouchPhase::Moved),
MotionAction::Cancel => Some(event::TouchPhase::Cancelled),
_ => {
None // TODO mouse events
},
// TODO mouse events
_ => None,
};
if let Some(phase) = phase {
let pointers: Box<dyn Iterator<Item = android_activity::input::Pointer<'_>>> =
match phase {
event::TouchPhase::Started | event::TouchPhase::Ended => {
Box::new(std::iter::once(
motion_event.pointer_at_index(motion_event.pointer_index()),
))
},
event::TouchPhase::Moved | event::TouchPhase::Cancelled => {
Box::new(motion_event.pointers())
},
};
if let Some(pointers) = pointers {
for pointer in pointers {
let tool_type = pointer.tool_type();
let position =
let location =
PhysicalPosition { x: pointer.x() as _, y: pointer.y() as _ };
trace!(
"Input event {device_id:?}, {action:?}, loc={position:?}, \
pointer={pointer:?}, tool_type={tool_type:?}"
"Input event {device_id:?}, {phase:?}, loc={location:?}, \
pointer={pointer:?}"
);
let finger_id = event::FingerId(FingerId(pointer.pointer_id()));
let force = Some(Force::Normalized(pointer.pressure() as f64));
match action {
MotionAction::Down | MotionAction::PointerDown => {
let event = event::WindowEvent::PointerEntered {
device_id,
position,
kind: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerKind::Touch(finger_id)
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerKind::Unknown,
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
let event = event::WindowEvent::PointerButton {
device_id,
state: event::ElementState::Pressed,
position,
button: match tool_type {
android_activity::input::ToolType::Finger => {
event::ButtonSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::ButtonSource::Unknown(0),
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
MotionAction::Move => {
let event = event::WindowEvent::PointerMoved {
device_id,
position,
source: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerSource::Unknown,
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
MotionAction::Up | MotionAction::PointerUp | MotionAction::Cancel => {
if let MotionAction::Up | MotionAction::PointerUp = action {
let event = event::WindowEvent::PointerButton {
device_id,
state: event::ElementState::Released,
position,
button: match tool_type {
android_activity::input::ToolType::Finger => {
event::ButtonSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::ButtonSource::Unknown(0),
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
}
let event = event::WindowEvent::Touch(event::Touch {
device_id,
phase,
location,
finger_id: event::FingerId(FingerId(pointer.pointer_id())),
force: Some(Force::Normalized(pointer.pressure() as f64)),
});
let event = event::WindowEvent::PointerLeft {
device_id,
position: Some(position),
kind: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerKind::Touch(finger_id)
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerKind::Unknown,
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
_ => unreachable!(),
}
app.window_event(&self.window_target, window_id, event);
}
}
},
@@ -451,8 +388,9 @@ impl EventLoop {
&mut self.combining_accent,
);
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::KeyboardInput {
device_id: Some(DeviceId::from_raw(key.device_id() as i64)),
device_id: event::DeviceId(DeviceId(key.device_id())),
event: event::KeyEvent {
state,
physical_key: keycodes::to_physical_key(keycode),
@@ -465,7 +403,7 @@ impl EventLoop {
is_synthetic: false,
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
app.window_event(&self.window_target, window_id, event);
},
}
},
@@ -656,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>> {
@@ -701,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
@@ -728,11 +671,40 @@ impl OwnedDisplayHandle {
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) struct WindowId;
impl WindowId {
pub const fn dummy() -> Self {
WindowId
}
}
impl From<WindowId> for u64 {
fn from(_: WindowId) -> Self {
0
}
}
impl From<u64> for WindowId {
fn from(_: u64) -> Self {
Self
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DeviceId(i32);
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId(0)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FingerId(i32);
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId(0)
}
@@ -750,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() {
@@ -783,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) -> WindowId {
GLOBAL_WINDOW
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)]

View File

@@ -1,12 +1,11 @@
#![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};
declare_class!(
@@ -39,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 {
@@ -59,8 +58,8 @@ 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| {
app.device_event(event_loop, None, DeviceEvent::PointerMotion {
delegate.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseMotion {
delta: (delta_x, delta_y),
});
});
@@ -68,8 +67,8 @@ 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| {
app.device_event(event_loop, None, DeviceEvent::Button {
delegate.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
button,
state: ElementState::Pressed,
});
@@ -77,8 +76,8 @@ 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| {
app.device_event(event_loop, None, DeviceEvent::Button {
delegate.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
button,
state: ElementState::Released,
});

View File

@@ -1,26 +1,27 @@
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::menu;
use super::observer::{EventLoopWaker, RunLoop};
use super::{menu, WindowId};
use crate::application::ApplicationHandler;
use crate::event::{StartCause, WindowEvent};
use crate::event_loop::ControlFlow;
use crate::window::WindowId;
use crate::window::WindowId as RootWindowId;
#[derive(Debug)]
pub(super) struct AppState {
mtm: MainThreadMarker,
activation_policy: Option<NSApplicationActivationPolicy>,
activation_policy: NSApplicationActivationPolicy,
default_menu: bool,
activate_ignoring_other_apps: bool,
run_loop: RunLoop,
@@ -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: Option<NSApplicationActivationPolicy>,
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,45 +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.
// If no activation policy is explicitly provided, do not set it at all
// to allow the package manifest to define behavior via LSUIElement.
if self.activation_policy.is_some() {
app.setActivationPolicy(self.activation_policy.unwrap());
}
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();
@@ -138,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);
});
@@ -209,66 +221,67 @@ 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, window_id, WindowEvent::RedrawRequested);
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
});
// `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.)
@@ -277,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.
@@ -306,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 },
@@ -338,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.");
@@ -346,18 +359,18 @@ 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, window_id, WindowEvent::RedrawRequested);
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
});
}
self.with_handler(|app, event_loop| {
@@ -365,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));
}
}
@@ -390,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");
}
})
}

View File

@@ -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);

View File

@@ -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");

View File

@@ -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,29 +183,30 @@ 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)]
pub(crate) struct PlatformSpecificEventLoopAttributes {
pub(crate) activation_policy: Option<ActivationPolicy>,
pub(crate) activation_policy: ActivationPolicy,
pub(crate) default_menu: bool,
pub(crate) activate_ignoring_other_apps: bool,
}
impl Default for PlatformSpecificEventLoopAttributes {
fn default() -> Self {
Self { activation_policy: None, default_menu: true, activate_ignoring_other_apps: true }
Self {
activation_policy: Default::default(), // Regular
default_menu: true,
activate_ignoring_other_apps: true,
}
}
}
@@ -219,55 +228,30 @@ impl EventLoop {
}
let activation_policy = match attributes.activation_policy {
None => None,
Some(ActivationPolicy::Regular) => Some(NSApplicationActivationPolicy::Regular),
Some(ActivationPolicy::Accessory) => Some(NSApplicationActivationPolicy::Accessory),
Some(ActivationPolicy::Prohibited) => Some(NSApplicationActivationPolicy::Prohibited),
ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular,
ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory,
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(
&center,
// `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(
&center,
// `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,
})
}
@@ -287,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
@@ -314,7 +298,7 @@ impl EventLoop {
resume_unwind(panic);
}
self.app_state.internal_exit()
self.delegate.internal_exit()
})
});
@@ -326,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() };
}
@@ -380,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

View File

@@ -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::{
@@ -21,18 +24,45 @@ pub(crate) use self::event_loop::{
PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::Window;
pub(crate) use self::window::{Window, WindowId};
pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
use crate::event::DeviceId as RootDeviceId;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId;
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId
}
}
// Constant device ID; to be removed when if backend is updated to report real device IDs.
pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId;
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
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),
}
}
}

View File

@@ -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

View File

@@ -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,20 +16,22 @@ 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,
scancode_to_physicalkey,
};
use super::window::WinitWindow;
use super::DEVICE_ID;
use crate::dpi::{LogicalPosition, LogicalSize};
use crate::event::{
DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, PointerKind,
PointerSource, TouchPhase, WindowEvent,
DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, TouchPhase,
WindowEvent,
};
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey};
use crate::platform::macos::OptionAsAlt;
use crate::window::WindowId as RootWindowId;
#[derive(Debug)]
struct CursorState {
@@ -111,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>,
@@ -196,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:)]
@@ -205,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:
@@ -412,9 +413,8 @@ declare_class!(
// Basically, we're sent this message whenever a keyboard event that doesn't generate a "human
// readable" character happens, i.e. newlines, tabs, and Ctrl+C.
#[method(doCommandBySelector:)]
fn do_command_by_selector(&self, command: Sel) {
fn do_command_by_selector(&self, _command: Sel) {
trace_scope!("doCommandBySelector:");
// We shouldn't forward any character from just committed text, since we'll end up sending
// it twice with some IMEs like Korean one. We'll also always send `Enter` in that case,
// which is not desired given it was used to confirm IME input.
@@ -429,18 +429,6 @@ declare_class!(
// Leave preedit so that we also report the key-up for this key.
self.ivars().ime_state.set(ImeState::Ground);
}
// Send command action to user if they requested it.
let window_id = self.window().id();
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
if let Some(handler) = app.macos_handler() {
handler.standard_key_binding(event_loop, window_id, command.name());
}
});
// The documentation for `-[NSTextInputClient doCommandBySelector:]` clearly states that
// we should not be forwarding this event up the responder chain, so no calling `super`
// here either.
}
}
@@ -497,7 +485,7 @@ declare_class!(
if !had_ime_input || self.ivars().forward_key_to_app.get() {
let key_event = create_key_event(&event, true, unsafe { event.isARepeat() }, None);
self.queue_event(WindowEvent::KeyboardInput {
device_id: None,
device_id: DEVICE_ID,
event: key_event,
is_synthetic: false,
});
@@ -517,7 +505,7 @@ declare_class!(
ImeState::Ground | ImeState::Disabled
) {
self.queue_event(WindowEvent::KeyboardInput {
device_id: None,
device_id: DEVICE_ID,
event: create_key_event(&event, false, false, None),
is_synthetic: false,
});
@@ -568,7 +556,7 @@ declare_class!(
let event = create_key_event(&event, true, unsafe { event.isARepeat() }, None);
self.queue_event(WindowEvent::KeyboardInput {
device_id: None,
device_id: DEVICE_ID,
event,
is_synthetic: false,
});
@@ -650,28 +638,19 @@ declare_class!(
}
#[method(mouseEntered:)]
fn mouse_entered(&self, event: &NSEvent) {
fn mouse_entered(&self, _event: &NSEvent) {
trace_scope!("mouseEntered:");
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
self.queue_event(WindowEvent::PointerEntered {
device_id: None,
position,
kind: PointerKind::Mouse,
self.queue_event(WindowEvent::CursorEntered {
device_id: DEVICE_ID,
});
}
#[method(mouseExited:)]
fn mouse_exited(&self, event: &NSEvent) {
fn mouse_exited(&self, _event: &NSEvent) {
trace_scope!("mouseExited:");
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
self.queue_event(WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Mouse,
self.queue_event(WindowEvent::CursorLeft {
device_id: DEVICE_ID,
});
}
@@ -708,11 +687,11 @@ declare_class!(
self.update_modifiers(event, false);
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop|
app.device_event(event_loop, None, DeviceEvent::MouseWheel { delta })
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 {
device_id: None,
device_id: DEVICE_ID,
delta,
phase,
});
@@ -734,7 +713,7 @@ declare_class!(
};
self.queue_event(WindowEvent::PinchGesture {
device_id: None,
device_id: DEVICE_ID,
delta: unsafe { event.magnification() },
phase,
});
@@ -747,7 +726,7 @@ declare_class!(
self.mouse_motion(event);
self.queue_event(WindowEvent::DoubleTapGesture {
device_id: None,
device_id: DEVICE_ID,
});
}
@@ -767,7 +746,7 @@ declare_class!(
};
self.queue_event(WindowEvent::RotationGesture {
device_id: None,
device_id: DEVICE_ID,
delta: unsafe { event.rotation() },
phase,
});
@@ -778,7 +757,7 @@ declare_class!(
trace_scope!("pressureChangeWithEvent:");
self.queue_event(WindowEvent::TouchpadPressure {
device_id: None,
device_id: DEVICE_ID,
pressure: unsafe { event.pressure() },
stage: unsafe { event.stage() } as i64,
});
@@ -803,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(),
@@ -854,8 +833,8 @@ impl WinitView {
}
fn queue_event(&self, event: WindowEvent) {
let window_id = self.window().id();
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
let window_id = RootWindowId(self.window().id());
self.ivars().app_delegate.maybe_queue_with_handler(move |app, event_loop| {
app.window_event(event_loop, window_id, event);
});
}
@@ -992,7 +971,7 @@ impl WinitView {
event.location = KeyLocation::Left;
event.physical_key = get_left_modifier_code(&event.logical_key).into();
events.push_back(WindowEvent::KeyboardInput {
device_id: None,
device_id: DEVICE_ID,
event,
is_synthetic: false,
});
@@ -1001,7 +980,7 @@ impl WinitView {
event.location = KeyLocation::Right;
event.physical_key = get_right_modifier_code(&event.logical_key).into();
events.push_back(WindowEvent::KeyboardInput {
device_id: None,
device_id: DEVICE_ID,
event,
is_synthetic: false,
});
@@ -1032,7 +1011,7 @@ impl WinitView {
}
events.push_back(WindowEvent::KeyboardInput {
device_id: None,
device_id: DEVICE_ID,
event,
is_synthetic: false,
});
@@ -1054,21 +1033,20 @@ impl WinitView {
}
fn mouse_click(&self, event: &NSEvent, button_state: ElementState) {
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
let button = mouse_button(event);
self.update_modifiers(event, false);
self.queue_event(WindowEvent::PointerButton {
device_id: None,
self.queue_event(WindowEvent::MouseInput {
device_id: DEVICE_ID,
state: button_state,
position,
button: button.into(),
button,
});
}
fn mouse_motion(&self, event: &NSEvent) {
let view_point = self.mouse_view_point(event);
let window_point = unsafe { event.locationInWindow() };
let view_point = self.convertPoint_fromView(window_point, None);
let frame = self.frame();
if view_point.x.is_sign_negative()
@@ -1083,21 +1061,15 @@ impl WinitView {
}
}
let view_point = LogicalPosition::new(view_point.x, view_point.y);
self.update_modifiers(event, false);
self.queue_event(WindowEvent::PointerMoved {
device_id: None,
self.queue_event(WindowEvent::CursorMoved {
device_id: DEVICE_ID,
position: view_point.to_physical(self.scale_factor()),
source: PointerSource::Mouse,
});
}
fn mouse_view_point(&self, event: &NSEvent) -> LogicalPosition<f64> {
let window_point = unsafe { event.locationInWindow() };
let view_point = self.convertPoint_fromView(window_point, None);
LogicalPosition::new(view_point.x, view_point.y)
}
}
/// Get the mouse button from the NSEvent.

View File

@@ -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, WindowId, 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,274 +70,24 @@ 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);
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(pub usize);
self.window.get_on_main(|window| autoreleasepool(|_| window.close()))
impl WindowId {
pub const fn dummy() -> Self {
Self(0)
}
}
#[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)) }
impl From<WindowId> for u64 {
fn from(window_id: WindowId) -> Self {
window_id.0 as u64
}
}
#[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| 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())
}
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
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self(raw_id as usize)
}
}
@@ -364,6 +121,6 @@ declare_class!(
impl WinitWindow {
pub(super) fn id(&self) -> WindowId {
WindowId::from_raw(self as *const Self as usize)
WindowId(self as *const Self as usize)
}
}

View File

@@ -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,20 +26,20 @@ 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};
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,
WindowAttributes, WindowButtons, WindowId, WindowLevel,
WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel,
};
#[derive(Clone, Debug, PartialEq)]
@@ -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,20 +79,20 @@ 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>,
// During `windowDidResize`, we use this to only send Moved if the position changed.
//
// This is expressed in desktop coordinates, and flipped to match Winit's coordinate system.
previous_position: Cell<NSPoint>,
// This is expressed in native screen coordinates.
previous_position: Cell<Option<NSPoint>>,
// Used to prevent redundant events.
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);
}
@@ -218,10 +214,10 @@ declare_class!(
trace_scope!("windowDidResignKey:");
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
// NSWindowDelegate will receive a didResignKey event despite no event
// being received when the modifiers are released. This is because
// being received when the modifiers are released. This is because
// flagsChanged events are received by the NSView instead of the
// NSWindowDelegate, and as a result a tracked modifiers state can quite
// easily fall out of synchrony with reality. This requires us to emit
// easily fall out of synchrony with reality. This requires us to emit
// a synthetic ModifiersChanged event when we lose focus.
self.view().reset_modifiers();
@@ -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(flip_window_screen_coordinates(window.frame())),
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);
@@ -819,8 +807,8 @@ impl WindowDelegate {
}
pub(crate) fn queue_event(&self, event: WindowEvent) {
let window_id = self.window().id();
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
let window_id = RootWindowId(self.window().id());
self.ivars().app_delegate.maybe_queue_with_handler(move |app, event_loop| {
app.window_event(event_loop, window_id, event);
});
}
@@ -832,29 +820,30 @@ 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) {
let position = flip_window_screen_coordinates(self.window().frame());
if self.ivars().previous_position.get() == position {
let frame = self.window().frame();
if self.ivars().previous_position.get() == Some(frame.origin) {
return;
}
self.ivars().previous_position.set(position);
self.ivars().previous_position.set(Some(frame.origin));
let position = flip_window_screen_coordinates(frame);
let position =
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor());
self.queue_event(WindowEvent::Moved(position));
@@ -918,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) {
@@ -945,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())
@@ -959,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());
@@ -985,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,
@@ -1007,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()))
@@ -1017,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());
@@ -1124,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]
@@ -1153,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);
@@ -1163,33 +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) -> Result<(), RequestError> {
pub fn drag_window(&self) -> Result<(), ExternalError> {
let mtm = MainThreadMarker::from(self);
let event =
NSApplication::sharedApplication(mtm).currentEvent().ok_or(RequestError::Ignored)?;
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 {
@@ -1422,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`.
@@ -1432,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) => {
@@ -1829,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 =

View File

@@ -2,8 +2,6 @@
#[cfg(target_os = "macos")]
mod appkit;
mod event_handler;
mod notification_center;
#[cfg(not(target_os = "macos"))]
mod uikit;

View File

@@ -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,
)
}
}

View 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))
}
}
);

View File

@@ -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,20 @@ 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 {
($($msg:tt)*) => {
@@ -42,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)]
@@ -97,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 {
@@ -111,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,
},
@@ -125,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,
}
@@ -149,8 +147,6 @@ impl AppState {
// must be mut because plain `static` requires `Sync`
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
#[allow(unknown_lints)] // New lint below
#[allow(static_mut_refs)] // TODO: Use `MainThreadBound` instead.
let mut guard = unsafe { APP_STATE.borrow_mut() };
if guard.is_none() {
#[inline(never)]
@@ -158,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(),
}),
@@ -210,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> {
@@ -238,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,
@@ -259,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,
});
@@ -275,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.
@@ -299,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 { .. }
@@ -316,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
}
@@ -334,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),
};
@@ -343,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),
}
}
@@ -389,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);
@@ -406,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)),
@@ -427,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`
@@ -454,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 {
@@ -476,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),
}
}
@@ -502,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;
}
@@ -522,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),
}
}
}
@@ -532,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);
@@ -565,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);
}
}
@@ -598,7 +685,7 @@ pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, o
&*ptr
};
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::Occluded(occluded),
}));
}
@@ -625,7 +712,7 @@ pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
.into_iter()
.map(|window| {
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::RedrawRequested,
})
})
@@ -654,7 +741,7 @@ pub(crate) fn terminated(application: &UIApplication) {
&*ptr
};
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::Destroyed,
}));
}
@@ -662,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: window.id(),
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);

View File

@@ -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(
&center,
// `application:didFinishLaunchingWithOptions:`
unsafe { UIApplicationDidFinishLaunchingNotification },
move |_| {
app_state::did_finish_launching(mtm);
},
);
let _did_become_active_observer = create_observer(
&center,
// `applicationDidBecomeActive:`
unsafe { UIApplicationDidBecomeActiveNotification },
move |_| {
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed));
},
);
let _will_resign_active_observer = create_observer(
&center,
// `applicationWillResignActive:`
unsafe { UIApplicationWillResignActiveNotification },
move |_| {
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended));
},
);
let _will_enter_foreground_observer = create_observer(
&center,
// `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(
&center,
// `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(
&center,
// `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(
&center,
// `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!()
}

View File

@@ -1,5 +1,6 @@
#![allow(clippy::let_unit_value)]
mod app_delegate;
mod app_state;
mod event_loop;
mod monitor;
@@ -14,18 +15,33 @@ pub(crate) use self::event_loop::{
PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window, WindowId};
pub(crate) use crate::cursor::{
NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource,
};
use crate::event::DeviceId as RootDeviceId;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
/// There is no way to detect which device that performed a certain event in
/// UIKit (i.e. you can't differentiate between different external keyboards,
/// or whether it was the main touchscreen, assistive technologies, or some
/// other pointer device that caused a touch event).
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId;
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId
}
}
pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId(usize);
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId(0)
}

View File

@@ -4,25 +4,20 @@ 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;
use super::{FingerId, DEVICE_ID};
use crate::dpi::PhysicalPosition;
use crate::event::{
ButtonSource, ElementState, Event, FingerId as RootFingerId, Force, KeyEvent, PointerKind,
PointerSource, TouchPhase, WindowEvent,
};
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
use crate::platform_impl::KeyEventExtra;
use crate::window::WindowAttributes;
use crate::event::{Event, FingerId as RootFingerId, Force, Touch, TouchPhase, WindowEvent};
use crate::window::{WindowAttributes, WindowId as RootWindowId};
pub struct WinitViewState {
pinch_gesture_recognizer: RefCell<Option<Retained<UIPinchGestureRecognizer>>>,
@@ -58,7 +53,7 @@ declare_class!(
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::RedrawRequested,
}),
);
@@ -93,8 +88,8 @@ declare_class!(
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
event: WindowEvent::SurfaceResized(size),
window_id: RootWindowId(window.id()),
event: WindowEvent::Resized(size),
}),
);
}
@@ -132,7 +127,7 @@ declare_class!(
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
};
let window_id = window.id();
let window_id = RootWindowId(window.id());
app_state::handle_nonuser_events(
mtm,
std::iter::once(EventWrapper::ScaleFactorChanged(
@@ -145,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)),
},
))),
);
@@ -197,9 +192,9 @@ declare_class!(
};
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::PinchGesture {
device_id: None,
device_id: DEVICE_ID,
delta: delta as f64,
phase,
},
@@ -215,9 +210,9 @@ declare_class!(
if recognizer.state() == UIGestureRecognizerState::Ended {
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::DoubleTapGesture {
device_id: None,
device_id: DEVICE_ID,
},
});
@@ -257,9 +252,9 @@ declare_class!(
// Make delta negative to match macos, convert to degrees
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::RotationGesture {
device_id: None,
device_id: DEVICE_ID,
delta: -delta.to_degrees() as _,
phase,
},
@@ -308,9 +303,9 @@ declare_class!(
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::PanGesture {
device_id: None,
device_id: DEVICE_ID,
delta: PhysicalPosition::new(dx as _, dy as _),
phase,
},
@@ -319,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 {}
@@ -334,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 {
@@ -484,18 +454,25 @@ impl WinitView {
for touch in touches {
let logical_location = touch.locationInView(None);
let touch_type = touch.r#type();
let force = if let UITouchType::Pencil = touch_type {
None
} else if os_supports_force {
let force = if os_supports_force {
let trait_collection = self.traitCollection();
let touch_capability = trait_collection.forceTouchCapability();
// Both the OS _and_ the device need to be checked for force touch support.
if touch_capability == UIForceTouchCapability::Available {
if touch_capability == UIForceTouchCapability::Available
|| touch_type == UITouchType::Pencil
{
let force = touch.force();
let max_possible_force = touch.maximumPossibleForce();
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
let angle = touch.altitudeAngle();
Some(angle as _)
} else {
None
};
Some(Force::Calibrated {
force: force as _,
max_possible_force: max_possible_force as _,
altitude_angle,
})
} else {
None
@@ -505,158 +482,34 @@ impl WinitView {
};
let touch_id = touch as *const UITouch as usize;
let phase = touch.phase();
let position = {
let phase = match phase {
UITouchPhase::Began => TouchPhase::Started,
UITouchPhase::Moved => TouchPhase::Moved,
// 2 is UITouchPhase::Stationary and is not expected here
UITouchPhase::Ended => TouchPhase::Ended,
UITouchPhase::Cancelled => TouchPhase::Cancelled,
_ => panic!("unexpected touch phase: {phase:?}"),
};
let physical_location = {
let scale_factor = self.contentScaleFactor();
PhysicalPosition::from_logical::<(f64, f64), f64>(
(logical_location.x as _, logical_location.y as _),
scale_factor as f64,
)
};
let window_id = window.id();
let finger_id = RootFingerId(FingerId(touch_id));
match phase {
UITouchPhase::Began => {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id: None,
position,
kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown
} else {
PointerKind::Touch(finger_id)
},
},
}));
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: ElementState::Pressed,
position,
button: if let UITouchType::Pencil = touch_type {
ButtonSource::Unknown(0)
} else {
ButtonSource::Touch { finger_id, force }
},
},
}));
},
UITouchPhase::Moved => {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
position,
source: if let UITouchType::Pencil = touch_type {
PointerSource::Unknown
} else {
PointerSource::Touch { finger_id, force }
},
},
}));
},
// 2 is UITouchPhase::Stationary and is not expected here
UITouchPhase::Ended | UITouchPhase::Cancelled => {
if let UITouchPhase::Ended = phase {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: ElementState::Released,
position,
button: if let UITouchType::Pencil = touch_type {
ButtonSource::Unknown(0)
} else {
ButtonSource::Touch { finger_id, force }
},
},
}));
}
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown
} else {
PointerKind::Touch(finger_id)
},
},
}));
},
_ => panic!("unexpected touch phase: {phase:?}"),
}
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::Touch(Touch {
device_id: DEVICE_ID,
finger_id: RootFingerId(FingerId(touch_id)),
location: physical_location,
force,
phase,
}),
}));
}
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 = 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: None,
},
})
})
}),
);
}
fn handle_delete_backward(&self) {
let window = self.window().unwrap();
let window_id = 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: None,
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,
},
})
}),
);
}
}

View File

@@ -3,9 +3,10 @@
use std::collections::VecDeque;
use objc2::rc::Retained;
use objc2::runtime::{AnyObject, NSObject};
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass};
use objc2_foundation::{
CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker, NSObject, NSObjectProtocol,
CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker, NSObjectProtocol,
};
use objc2_ui_kit::{
UIApplication, UICoordinateSpace, UIResponder, UIScreen, UIScreenOverscanCompensation,
@@ -19,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, WindowLevel,
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowId as RootWindowId, WindowLevel,
};
declare_class!(
@@ -49,7 +49,7 @@ declare_class!(
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: self.id(),
window_id: RootWindowId(self.id()),
event: WindowEvent::Focused(true),
}),
);
@@ -62,7 +62,7 @@ declare_class!(
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: self.id(),
window_id: RootWindowId(self.id()),
event: WindowEvent::Focused(false),
}),
);
@@ -78,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));
@@ -105,7 +100,7 @@ impl WinitUIWindow {
}
pub(crate) fn id(&self) -> WindowId {
WindowId::from_raw(self as *const Self as usize)
(self as *const Self as usize as u64).into()
}
}
@@ -158,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) {
@@ -186,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 {
@@ -206,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) {
@@ -255,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) {
@@ -370,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) {
@@ -465,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
@@ -488,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);
@@ -507,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;
@@ -522,6 +506,7 @@ impl Window {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
};
let window_id = RootWindowId(window.id());
app_state::handle_nonuser_events(
mtm,
std::iter::once(EventWrapper::ScaleFactorChanged(app_state::ScaleFactorChanged {
@@ -531,8 +516,8 @@ impl Window {
}))
.chain(std::iter::once(EventWrapper::StaticEvent(
Event::WindowEvent {
window_id: window.id(),
event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)),
window_id,
event: WindowEvent::Resized(size.to_physical(scale_factor)),
},
))),
);
@@ -542,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))
}
@@ -567,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| 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) {
@@ -939,6 +670,38 @@ impl Inner {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId {
window: *mut WinitUIWindow,
}
impl WindowId {
pub const fn dummy() -> Self {
WindowId { window: std::ptr::null_mut() }
}
}
impl From<WindowId> for u64 {
fn from(window_id: WindowId) -> Self {
window_id.window as u64
}
}
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self { window: raw_id as _ }
}
}
unsafe impl Send for WindowId {}
unsafe impl Sync for WindowId {}
impl From<&AnyObject> for WindowId {
fn from(window: &AnyObject) -> WindowId {
WindowId { window: window as *const _ as _ }
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct PlatformSpecificWindowAttributes {
pub scale_factor: Option<f64>,

View File

@@ -638,7 +638,7 @@ pub fn keysym_to_key(keysym: u32) -> Key {
// keysyms::ISO_Release_Margin_Left => NamedKey::IsoReleaseMarginLeft,
// keysyms::ISO_Release_Margin_Right => NamedKey::IsoReleaseMarginRight,
// keysyms::ISO_Release_Both_Margins => NamedKey::IsoReleaseBothMargins,
// keysyms::ISO_Fast_Cursor_Left => NamedKey::IsoFastPointerLeft,
// keysyms::ISO_Fast_Cursor_Left => NamedKey::IsoFastCursorLeft,
// keysyms::ISO_Fast_Cursor_Right => NamedKey::IsoFastCursorRight,
// keysyms::ISO_Fast_Cursor_Up => NamedKey::IsoFastCursorUp,
// keysyms::ISO_Fast_Cursor_Down => NamedKey::IsoFastCursorDown,

View File

@@ -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,72 @@ 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);
impl From<WindowId> for u64 {
fn from(window_id: WindowId) -> Self {
window_id.0
}
}
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self(raw_id)
}
}
impl WindowId {
pub const fn dummy() -> Self {
Self(0)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum DeviceId {
#[cfg(x11_platform)]
X(x11::DeviceId),
#[cfg(wayland_platform)]
Wayland(wayland::DeviceId),
}
impl DeviceId {
pub const fn dummy() -> Self {
#[cfg(wayland_platform)]
return DeviceId::Wayland(wayland::DeviceId::dummy());
#[cfg(all(not(wayland_platform), x11_platform))]
return DeviceId::X(x11::DeviceId::dummy());
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FingerId {
#[cfg(x11_platform)]
@@ -116,7 +187,6 @@ pub enum FingerId {
}
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
#[cfg(wayland_platform)]
return FingerId::Wayland(wayland::FingerId::dummy());
@@ -223,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>,
@@ -239,18 +619,18 @@ pub(crate) enum PlatformCustomCursor {
/// Hooks for X11 errors.
#[cfg(x11_platform)]
pub(crate) static XLIB_ERROR_HOOKS: Mutex<Vec<XlibErrorHook>> = Mutex::new(Vec::new());
pub(crate) static mut XLIB_ERROR_HOOKS: Mutex<Vec<XlibErrorHook>> = Mutex::new(Vec::new());
#[cfg(x11_platform)]
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;
for hook in XLIB_ERROR_HOOKS.lock().unwrap().iter() {
for hook in unsafe { XLIB_ERROR_HOOKS.lock() }.unwrap().iter() {
error_handled |= hook(display as *mut _, event as *mut _);
}
@@ -312,8 +692,8 @@ impl EventLoop {
"Initializing the event loop outside of the main thread is a significant \
cross-platform compatibility hazard. If you absolutely need to create an \
EventLoop on a different thread, you can use the \
`EventLoopBuilderExtX11::with_any_thread` or \
`EventLoopBuilderExtWayland::with_any_thread` functions."
`EventLoopBuilderExtX11::any_thread` or `EventLoopBuilderExtWayland::any_thread` \
functions."
);
}
@@ -348,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))));
},
};
@@ -368,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)))

View File

@@ -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, 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,23 +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, window_id, event);
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 {
@@ -330,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.
@@ -346,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
@@ -360,11 +380,13 @@ impl EventLoop {
size
});
let event = WindowEvent::SurfaceResized(physical_size);
let window_id = crate::window::WindowId(window_id);
let event = WindowEvent::Resized(physical_size);
app.window_event(&self.active_event_loop, window_id, event);
}
if compositor_update.close_window {
let window_id = crate::window::WindowId(window_id);
app.window_event(&self.active_event_loop, window_id, WindowEvent::CloseRequested);
}
}
@@ -434,7 +456,8 @@ impl EventLoop {
});
if let Some(event) = event {
app.window_event(&self.active_event_loop, *window_id, event);
let window_id = crate::window::WindowId(*window_id);
app.window_event(&self.active_event_loop, window_id, event);
}
}
@@ -500,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 {
@@ -562,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 {
@@ -595,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))),
})
@@ -609,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>> {
@@ -637,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

View File

@@ -2,8 +2,10 @@
use std::vec::Drain;
use crate::event::{DeviceEvent, Event, WindowEvent};
use crate::window::WindowId;
use super::{DeviceId, WindowId};
use crate::event::{DeviceEvent, DeviceId as RootDeviceId, Event, WindowEvent};
use crate::platform_impl::platform::DeviceId as PlatformDeviceId;
use crate::window::WindowId as RootWindowId;
/// An event loop's sink to deliver events from the Wayland event callbacks
/// to the winit's user.
@@ -25,14 +27,17 @@ impl EventSink {
/// Add new device event to a queue.
#[inline]
pub fn push_device_event(&mut self, event: DeviceEvent) {
self.window_events.push(Event::DeviceEvent { event, device_id: None });
pub fn push_device_event(&mut self, event: DeviceEvent, device_id: DeviceId) {
self.window_events.push(Event::DeviceEvent {
event,
device_id: RootDeviceId(PlatformDeviceId::Wayland(device_id)),
});
}
/// Add new window event to a queue.
#[inline]
pub fn push_window_event(&mut self, event: WindowEvent, window_id: WindowId) {
self.window_events.push(Event::WindowEvent { event, window_id });
self.window_events.push(Event::WindowEvent { event, window_id: RootWindowId(window_id) });
}
#[inline]

View File

@@ -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};
use crate::window::WindowId;
pub use crate::platform_impl::platform::{OsError, WindowId};
mod event_loop;
mod output;
@@ -17,11 +21,60 @@ 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;
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId(i32);
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId(0)
}
@@ -30,7 +83,7 @@ impl FingerId {
/// Get the WindowId out of the surface.
#[inline]
fn make_wid(surface: &WlSurface) -> WindowId {
WindowId::from_raw(surface.id().as_ptr() as usize)
WindowId(surface.id().as_ptr() as u64)
}
/// The default routine does floor, but we need round on Wayland.

View File

@@ -17,7 +17,7 @@ use crate::keyboard::ModifiersState;
use crate::platform_impl::common::xkb::Context;
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, WindowId};
use crate::platform_impl::wayland::{self, DeviceId, WindowId};
impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
fn event(
@@ -369,9 +369,10 @@ fn key_input(
None => return,
};
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
if let Some(mut key_context) = keyboard_state.xkb_context.key_context() {
let event = key_context.process_key_event(keycode, state, repeat);
let event = WindowEvent::KeyboardInput { device_id: None, event, is_synthetic: false };
let event = WindowEvent::KeyboardInput { device_id, event, is_synthetic: false };
event_sink.push_window_event(event, window_id);
}
}

View File

@@ -27,10 +27,10 @@ use sctk::seat::pointer::{
use sctk::seat::SeatState;
use crate::dpi::{LogicalPosition, PhysicalPosition};
use crate::event::{ElementState, MouseButton, MouseScrollDelta, PointerSource, PointerKind, TouchPhase, WindowEvent};
use crate::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent};
use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, WindowId};
use crate::platform_impl::wayland::{self, DeviceId, WindowId};
pub mod relative_pointer;
@@ -59,6 +59,8 @@ impl PointerHandler for WinitState {
},
};
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
for event in events {
let surface = &event.surface;
@@ -122,19 +124,18 @@ impl PointerHandler for WinitState {
},
// Regular events on the main surface.
PointerEventKind::Enter { .. } => {
self.events_sink.push_window_event(
WindowEvent::PointerEntered {
device_id: None,
position,
kind: PointerKind::Mouse,
},
window_id,
);
self.events_sink
.push_window_event(WindowEvent::CursorEntered { device_id }, window_id);
window.pointer_entered(Arc::downgrade(themed_pointer));
// Set the currently focused surface.
pointer.winit_data().inner.lock().unwrap().surface = Some(window_id);
self.events_sink.push_window_event(
WindowEvent::CursorMoved { device_id, position },
window_id,
);
},
PointerEventKind::Leave { .. } => {
window.pointer_left(Arc::downgrade(themed_pointer));
@@ -142,22 +143,12 @@ impl PointerHandler for WinitState {
// Remove the active surface.
pointer.winit_data().inner.lock().unwrap().surface = None;
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Mouse,
},
window_id,
);
self.events_sink
.push_window_event(WindowEvent::CursorLeft { device_id }, window_id);
},
PointerEventKind::Motion { .. } => {
self.events_sink.push_window_event(
WindowEvent::PointerMoved {
device_id: None,
position,
source: PointerSource::Mouse,
},
WindowEvent::CursorMoved { device_id, position },
window_id,
);
},
@@ -173,12 +164,7 @@ impl PointerHandler for WinitState {
ElementState::Released
};
self.events_sink.push_window_event(
WindowEvent::PointerButton {
device_id: None,
state,
position,
button: button.into(),
},
WindowEvent::MouseInput { device_id, state, button },
window_id,
);
},
@@ -223,7 +209,7 @@ impl PointerHandler for WinitState {
};
self.events_sink.push_window_event(
WindowEvent::MouseWheel { device_id: None, delta, phase },
WindowEvent::MouseWheel { device_id, delta, phase },
window_id,
)
},

View File

@@ -66,9 +66,10 @@ impl Dispatch<ZwpRelativePointerV1, GlobalData, WinitState> for RelativePointerS
},
_ => return,
};
state
.events_sink
.push_device_event(DeviceEvent::PointerMotion { delta: (dx_unaccel, dy_unaccel) });
state.events_sink.push_device_event(
DeviceEvent::MouseMotion { delta: (dx_unaccel, dy_unaccel) },
super::DeviceId,
);
}
}

View File

@@ -8,9 +8,9 @@ use sctk::seat::touch::{TouchData, TouchHandler};
use tracing::warn;
use crate::dpi::LogicalPosition;
use crate::event::{ButtonSource, ElementState, PointerKind, PointerSource, WindowEvent};
use crate::event::{Touch, TouchPhase, WindowEvent};
use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, FingerId};
use crate::platform_impl::wayland::{self, DeviceId, FingerId};
impl TouchHandler for WinitState {
fn down(
@@ -42,25 +42,18 @@ impl TouchHandler for WinitState {
let location = LogicalPosition::<f64>::from(position);
seat_state.touch_map.insert(id, TouchPoint { surface, location });
let position = location.to_physical(scale_factor);
let finger_id =
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
self.events_sink.push_window_event(
WindowEvent::PointerEntered {
device_id: None,
position,
kind: PointerKind::Touch(finger_id),
},
window_id,
);
self.events_sink.push_window_event(
WindowEvent::PointerButton {
device_id: None,
state: ElementState::Pressed,
position,
button: ButtonSource::Touch { finger_id, force: None },
},
WindowEvent::Touch(Touch {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
phase: TouchPhase::Started,
location: location.to_physical(scale_factor),
force: None,
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
FingerId(id),
)),
}),
window_id,
);
}
@@ -94,25 +87,18 @@ impl TouchHandler for WinitState {
None => return,
};
let position = touch_point.location.to_physical(scale_factor);
let finger_id =
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
self.events_sink.push_window_event(
WindowEvent::PointerButton {
device_id: None,
state: ElementState::Released,
position,
button: ButtonSource::Touch { finger_id, force: None },
},
window_id,
);
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
WindowEvent::Touch(Touch {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
phase: TouchPhase::Ended,
location: touch_point.location.to_physical(scale_factor),
force: None,
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
FingerId(id),
)),
}),
window_id,
);
}
@@ -149,16 +135,17 @@ impl TouchHandler for WinitState {
touch_point.location = LogicalPosition::<f64>::from(position);
self.events_sink.push_window_event(
WindowEvent::PointerMoved {
device_id: None,
position: touch_point.location.to_physical(scale_factor),
source: PointerSource::Touch {
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
FingerId(id),
)),
force: None,
},
},
WindowEvent::Touch(Touch {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
phase: TouchPhase::Moved,
location: touch_point.location.to_physical(scale_factor),
force: None,
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
FingerId(id),
)),
}),
window_id,
);
}
@@ -179,16 +166,20 @@ impl TouchHandler for WinitState {
None => return,
};
let position = touch_point.location.to_physical(scale_factor);
let location = touch_point.location.to_physical(scale_factor);
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Touch(crate::event::FingerId(
crate::platform_impl::FingerId::Wayland(FingerId(id)),
WindowEvent::Touch(Touch {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
},
phase: TouchPhase::Cancelled,
location,
force: None,
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
FingerId(id),
)),
}),
window_id,
);
}

View File

@@ -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(),

View File

@@ -14,7 +14,8 @@ use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::
use crate::event_loop::AsyncRequestSerial;
use crate::platform_impl::wayland::state::WinitState;
use crate::window::{ActivationToken, WindowId};
use crate::platform_impl::WindowId;
use crate::window::ActivationToken;
pub struct XdgActivationState {
xdg_activation: XdgActivationV1,

View File

@@ -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;
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,
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 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) -> WindowId {
pub fn id(&self) -> WindowId {
self.window_id
}
fn request_redraw(&self) {
#[inline]
pub fn set_title(&self, title: impl ToString) {
let new_title = title.to_string();
self.window_state.lock().unwrap().set_title(new_title);
}
#[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();
}
}

View File

@@ -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,
@@ -38,15 +38,15 @@ use crate::platform_impl::wayland::seat::{
use crate::platform_impl::wayland::state::{WindowCompositorUpdate, WinitState};
use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor};
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::PlatformCustomCursor;
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, WindowId};
use crate::platform_impl::{PlatformCustomCursor, WindowId};
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};
#[cfg(feature = "sctk-adwaita")]
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| {

View File

@@ -22,8 +22,8 @@ use xkbcommon_dl::xkb_mod_mask_t;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::event::{
ButtonSource, DeviceEvent, DeviceId, ElementState, Event, Ime, MouseButton, MouseScrollDelta,
PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, 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};
@@ -33,8 +33,8 @@ use crate::platform_impl::platform::x11::ActiveEventLoop;
use crate::platform_impl::x11::atoms::*;
use crate::platform_impl::x11::util::cookie::GenericEventCookie;
use crate::platform_impl::x11::{
mkdid, mkfid, mkwid, util, CookieResultExt, Device, DeviceInfo, Dnd, DndState, ImeReceiver,
ScrollOrientation, UnownedWindow, WindowId,
mkdid, mkfid, mkwid, util, CookieResultExt, Device, DeviceId, DeviceInfo, Dnd, DndState,
ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
};
/// The maximum amount of X modifiers to replay.
@@ -142,18 +142,8 @@ impl EventProcessor {
{
let event_type = xev.get_type();
// If we have IME disabled, don't try to `filter_event`, since only IME can consume them
// and forward back. This is not desired for e.g. games since some IMEs may delay the input
// and game can toggle IME back when e.g. typing into some field where latency won't really
// matter.
if event_type == xlib::KeyPress || event_type == xlib::KeyRelease {
let ime = self.target.ime.as_ref();
let window = self.active_window.map(|window| window as XWindow);
let forward_to_ime = ime
.and_then(|ime| window.map(|window| ime.borrow().is_ime_allowed(window)))
.unwrap_or(false);
if forward_to_ime && self.filter_event(xev) {
if self.filter_event(xev) {
if event_type == xlib::KeyPress || event_type == xlib::KeyRelease {
let xev: &XKeyEvent = xev.as_ref();
if self.xmodmap.is_modifier(xev.keycode as u8) {
// Don't grow the buffer past the `MAX_MOD_REPLAY_LEN`. This could happen
@@ -166,8 +156,7 @@ impl EventProcessor {
self.xfiltered_modifiers.push_front(xev.serial);
}
}
} else {
self.filter_event(xev);
return;
}
match event_type {
@@ -249,8 +238,15 @@ impl EventProcessor {
self.xinput2_unfocused(xev, &mut callback);
},
xinput2::XI_TouchBegin | xinput2::XI_TouchUpdate | xinput2::XI_TouchEnd => {
let phase = match evtype {
xinput2::XI_TouchBegin => TouchPhase::Started,
xinput2::XI_TouchUpdate => TouchPhase::Moved,
xinput2::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!(),
};
let xev: &XIDeviceEvent = unsafe { xev.as_event() };
self.xinput2_touch(xev, evtype, &mut callback);
self.xinput2_touch(xev, phase, &mut callback);
},
xinput2::XI_RawButtonPress | xinput2::XI_RawButtonRelease => {
let state = match evtype {
@@ -330,7 +326,7 @@ impl EventProcessor {
let mut devices = self.devices.borrow_mut();
if let Some(info) = DeviceInfo::get(&self.target.xconn, device as _) {
for info in info.iter() {
devices.insert(mkdid(info.deviceid as xinput::DeviceId), Device::new(info));
devices.insert(DeviceId(info.deviceid as _), Device::new(info));
}
}
}
@@ -340,7 +336,7 @@ impl EventProcessor {
F: Fn(&Arc<UnownedWindow>) -> Ret,
{
let mut deleted = false;
let window_id = WindowId::from_raw(window_id as _);
let window_id = WindowId(window_id as _);
let result = self
.target
.windows
@@ -602,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 {
@@ -675,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
@@ -699,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;
@@ -743,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);
}
}
@@ -764,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()),
});
}
}
@@ -809,7 +802,7 @@ impl EventProcessor {
// In the event that the window's been destroyed without being dropped first, we
// cleanup again here.
self.target.windows.borrow_mut().remove(&WindowId::from_raw(window as _));
self.target.windows.borrow_mut().remove(&WindowId(window as _));
// Since all XIM stuff needs to happen from the same thread, we destroy the input
// context here instead of when dropping the window.
@@ -882,6 +875,7 @@ impl EventProcessor {
};
let window_id = mkwid(window);
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
let keycode = xev.keycode as _;
@@ -945,11 +939,7 @@ impl EventProcessor {
let event = key_processor.process_key_event(keycode, state, repeat);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput {
device_id: None,
event,
is_synthetic: false,
},
event: WindowEvent::KeyboardInput { device_id, event, is_synthetic: false },
};
callback(&self.target, event);
}
@@ -1019,7 +1009,7 @@ impl EventProcessor {
F: FnMut(&ActiveEventLoop, Event),
{
let window_id = mkwid(event.event as xproto::Window);
let device_id = Some(mkdid(event.deviceid as xinput::DeviceId));
let device_id = mkdid(event.deviceid as xinput::DeviceId);
// Set the timestamp.
self.target.xconn.set_timestamp(event.time as xproto::Timestamp);
@@ -1029,27 +1019,16 @@ impl EventProcessor {
return;
}
let position = PhysicalPosition::new(event.event_x, event.event_y);
let event = match event.detail as u32 {
xlib::Button1 => WindowEvent::PointerButton {
device_id,
state,
position,
button: MouseButton::Left.into(),
xlib::Button1 => {
WindowEvent::MouseInput { device_id, state, button: MouseButton::Left }
},
xlib::Button2 => WindowEvent::PointerButton {
device_id,
state,
position,
button: MouseButton::Middle.into(),
xlib::Button2 => {
WindowEvent::MouseInput { device_id, state, button: MouseButton::Middle }
},
xlib::Button3 => WindowEvent::PointerButton {
device_id,
state,
position,
button: MouseButton::Right.into(),
xlib::Button3 => {
WindowEvent::MouseInput { device_id, state, button: MouseButton::Right }
},
// Suppress emulated scroll wheel clicks, since we handle the real motion events for
@@ -1067,25 +1046,10 @@ impl EventProcessor {
},
phase: TouchPhase::Moved,
},
8 => WindowEvent::PointerButton {
device_id,
state,
position,
button: MouseButton::Back.into(),
},
8 => WindowEvent::MouseInput { device_id, state, button: MouseButton::Back },
9 => WindowEvent::PointerButton {
device_id,
state,
position,
button: MouseButton::Forward.into(),
},
x => WindowEvent::PointerButton {
device_id,
state,
position,
button: MouseButton::Other(x as u16).into(),
},
9 => WindowEvent::MouseInput { device_id, state, button: MouseButton::Forward },
x => WindowEvent::MouseInput { device_id, state, button: MouseButton::Other(x as u16) },
};
let event = Event::WindowEvent { window_id, event };
@@ -1099,7 +1063,7 @@ impl EventProcessor {
// Set the timestamp.
self.target.xconn.set_timestamp(event.time as xproto::Timestamp);
let device_id = Some(mkdid(event.deviceid as xinput::DeviceId));
let device_id = mkdid(event.deviceid as xinput::DeviceId);
let window = event.event as xproto::Window;
let window_id = mkwid(window);
let new_cursor_pos = (event.event_x, event.event_y);
@@ -1114,11 +1078,7 @@ impl EventProcessor {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id,
position,
source: PointerSource::Mouse,
},
event: WindowEvent::CursorMoved { device_id, position },
};
callback(&self.target, event);
} else if cursor_moved.is_none() {
@@ -1130,7 +1090,7 @@ impl EventProcessor {
slice::from_raw_parts(event.valuators.mask, event.valuators.mask_len as usize)
};
let mut devices = self.devices.borrow_mut();
let physical_device = match devices.get_mut(&mkdid(event.sourceid as xinput::DeviceId)) {
let physical_device = match devices.get_mut(&DeviceId(event.sourceid as xinput::DeviceId)) {
Some(device) => device,
None => return,
};
@@ -1189,7 +1149,7 @@ impl EventProcessor {
if device_info.deviceid == event.sourceid
|| device_info.attachment == event.sourceid
{
let device_id = mkdid(device_info.deviceid as xinput::DeviceId);
let device_id = DeviceId(device_info.deviceid as _);
if let Some(device) = devices.get_mut(&device_id) {
device.reset_scroll_position(device_info);
}
@@ -1198,16 +1158,15 @@ impl EventProcessor {
}
if self.window_exists(window) {
let device_id = Some(device_id);
let position = PhysicalPosition::new(event.event_x, event.event_y);
let event =
Event::WindowEvent { window_id, event: WindowEvent::CursorEntered { device_id } };
callback(&self.target, event);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id,
position,
kind: PointerKind::Mouse,
},
event: WindowEvent::CursorMoved { device_id, position },
};
callback(&self.target, event);
}
@@ -1227,10 +1186,8 @@ impl EventProcessor {
if self.window_exists(window) {
let event = Event::WindowEvent {
window_id: mkwid(window),
event: WindowEvent::PointerLeft {
device_id: Some(mkdid(event.deviceid as xinput::DeviceId)),
position: Some(PhysicalPosition::new(event.event_x, event.event_y)),
kind: PointerKind::Mouse,
event: WindowEvent::CursorLeft {
device_id: mkdid(event.deviceid as xinput::DeviceId),
},
};
callback(&self.target, event);
@@ -1281,15 +1238,16 @@ impl EventProcessor {
// The deviceid for this event is for a keyboard instead of a pointer,
// so we have to do a little extra work.
let device_id = self
let pointer_id = self
.devices
.borrow()
.get(&mkdid(xev.deviceid as xinput::DeviceId))
.map(|device| mkdid(device.attachment as xinput::DeviceId));
.get(&DeviceId(xev.deviceid as xinput::DeviceId))
.map(|device| device.attachment)
.unwrap_or(2);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved { device_id, position, source: PointerSource::Mouse },
event: WindowEvent::CursorMoved { device_id: mkdid(pointer_id as _), position },
};
callback(&self.target, event);
}
@@ -1345,7 +1303,7 @@ impl EventProcessor {
}
}
fn xinput2_touch<F>(&mut self, xev: &XIDeviceEvent, phase: i32, mut callback: F)
fn xinput2_touch<F>(&mut self, xev: &XIDeviceEvent, phase: TouchPhase, mut callback: F)
where
F: FnMut(&ActiveEventLoop, Event),
{
@@ -1356,81 +1314,32 @@ impl EventProcessor {
if self.window_exists(window) {
let window_id = mkwid(window);
let id = xev.detail as u32;
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
let location = PhysicalPosition::new(xev.event_x, xev.event_y);
// Mouse cursor position changes when touch events are received.
// Only the first concurrently active touch ID moves the mouse cursor.
if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase) {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
position: position.cast(),
source: PointerSource::Mouse,
event: WindowEvent::CursorMoved {
device_id: mkdid(util::VIRTUAL_CORE_POINTER),
position: location.cast(),
},
};
callback(&self.target, event);
}
let device_id = Some(mkdid(xev.deviceid as xinput::DeviceId));
let finger_id = mkfid(id);
match phase {
xinput2::XI_TouchBegin => {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id,
position,
kind: PointerKind::Touch(finger_id),
},
};
callback(&self.target, event);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id,
state: ElementState::Pressed,
position,
button: ButtonSource::Touch { finger_id, force: None },
},
};
callback(&self.target, event);
},
xinput2::XI_TouchUpdate => {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id,
position,
source: PointerSource::Touch { finger_id, force: None },
},
};
callback(&self.target, event);
},
xinput2::XI_TouchEnd => {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id,
state: ElementState::Released,
position,
button: ButtonSource::Touch { finger_id, force: None },
},
};
callback(&self.target, event);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft {
device_id,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
};
callback(&self.target, event);
},
_ => unreachable!(),
}
let event = Event::WindowEvent {
window_id,
event: WindowEvent::Touch(Touch {
device_id: mkdid(xev.deviceid as xinput::DeviceId),
phase,
location,
force: None, // TODO
finger_id: mkfid(id),
}),
};
callback(&self.target, event)
}
}
@@ -1443,7 +1352,7 @@ impl EventProcessor {
if xev.flags & xinput2::XIPointerEmulated == 0 {
let event = Event::DeviceEvent {
device_id: Some(mkdid(xev.deviceid as xinput::DeviceId)),
device_id: mkdid(xev.deviceid as xinput::DeviceId),
event: DeviceEvent::Button { state, button: xev.detail as u32 },
};
callback(&self.target, event);
@@ -1457,7 +1366,7 @@ impl EventProcessor {
// Set the timestamp.
self.target.xconn.set_timestamp(xev.time as xproto::Timestamp);
let did = Some(mkdid(xev.deviceid as xinput::DeviceId));
let did = mkdid(xev.deviceid as xinput::DeviceId);
let mask =
unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
@@ -1486,7 +1395,7 @@ impl EventProcessor {
if let Some(mouse_delta) = mouse_delta.consume() {
let event = Event::DeviceEvent {
device_id: did,
event: DeviceEvent::PointerMotion { delta: mouse_delta },
event: DeviceEvent::MouseMotion { delta: mouse_delta },
};
callback(&self.target, event);
}
@@ -1509,7 +1418,7 @@ impl EventProcessor {
// Set the timestamp.
self.target.xconn.set_timestamp(xev.time as xproto::Timestamp);
let device_id = Some(mkdid(xev.sourceid as xinput::DeviceId));
let device_id = mkdid(xev.sourceid as xinput::DeviceId);
let keycode = xev.detail as u32;
if keycode < KEYCODE_OFFSET as u32 {
return;
@@ -1531,7 +1440,7 @@ impl EventProcessor {
self.init_device(info.deviceid as xinput::DeviceId);
} else if 0 != info.flags & (xinput2::XISlaveRemoved | xinput2::XIMasterRemoved) {
let mut devices = self.devices.borrow_mut();
devices.remove(&mkdid(info.deviceid as xinput::DeviceId));
devices.remove(&DeviceId(info.deviceid as xinput::DeviceId));
}
}
}
@@ -1783,6 +1692,8 @@ impl EventProcessor {
) where
F: FnMut(&ActiveEventLoop, Event),
{
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
// Update modifiers state and emit key events based on which keys are currently pressed.
let xcb = target.xconn.xcb_connection().get_raw_xcb_connection();
@@ -1805,7 +1716,7 @@ impl EventProcessor {
let event = key_processor.process_key_event(keycode as u32, state, false);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput { device_id: None, event, is_synthetic: true },
event: WindowEvent::KeyboardInput { device_id, event, is_synthetic: true },
};
callback(target, event);
}
@@ -1850,15 +1761,15 @@ impl EventProcessor {
}
}
fn is_first_touch(first: &mut Option<u32>, num: &mut u32, id: u32, phase: i32) -> bool {
fn is_first_touch(first: &mut Option<u32>, num: &mut u32, id: u32, phase: TouchPhase) -> bool {
match phase {
xinput2::XI_TouchBegin => {
TouchPhase::Started => {
if *num == 0 {
*first = Some(id);
}
*num += 1;
},
xinput2::XI_TouchEnd => {
TouchPhase::Cancelled | TouchPhase::Ended => {
if *first == Some(id) {
*first = None;
}

View File

@@ -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::*;

View File

@@ -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);

View File

@@ -225,16 +225,6 @@ impl Ime {
// Create new context supporting IME input.
let _ = self.create_context(window, allowed);
}
pub fn is_ime_allowed(&self, window: ffi::Window) -> bool {
if self.is_destroyed() {
false
} else if let Some(Some(context)) = self.inner.contexts.get(&window) {
context.is_allowed()
} else {
false
}
}
}
impl Drop for Ime {

View File

@@ -1,3 +1,4 @@
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, HashSet, VecDeque};
use std::ffi::CStr;
@@ -18,25 +19,24 @@ 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::event::{DeviceId, Event, StartCause, WindowEvent};
use crate::error::{EventLoopError, ExternalError, OsError as RootOsError};
use crate::event::{Event, StartCause, WindowEvent};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
OwnedDisplayHandle as RootOwnedDisplayHandle,
};
use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::common::xkb::Context;
use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::x11::window::Window;
use crate::platform_impl::{OwnedDisplayHandle, PlatformCustomCursor};
use crate::platform_impl::platform::{min_timeout, WindowId};
use crate::platform_impl::{OsError, OwnedDisplayHandle, PlatformCustomCursor};
use crate::window::{
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
WindowAttributes, WindowId,
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
}
@@ -521,14 +519,13 @@ impl EventLoop {
// Empty activation tokens.
while let Ok((window_id, serial)) = self.activation_receiver.try_recv() {
let token = self
.event_processor
.with_window(window_id.into_raw() as xproto::Window, |window| {
window.generate_activation_token()
});
let token = self.event_processor.with_window(window_id.0 as xproto::Window, |window| {
window.generate_activation_token()
});
match token {
Some(Ok(token)) => {
let window_id = crate::window::WindowId(window_id);
let event = WindowEvent::ActivationTokenDone {
serial,
token: crate::window::ActivationToken::_new(token),
@@ -556,6 +553,7 @@ impl EventLoop {
}
for window_id in windows {
let window_id = crate::window::WindowId(window_id);
app.window_event(
&self.event_processor.target,
window_id,
@@ -574,9 +572,12 @@ impl EventLoop {
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
let mut xev = unsafe { xev.assume_init() };
self.event_processor.process_event(&mut xev, |window_target, event: Event| {
if let Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested } = event
if let Event::WindowEvent {
window_id: crate::window::WindowId(wid),
event: WindowEvent::RedrawRequested,
} = event
{
window_target.redraw_sender.send(window_id);
window_target.redraw_sender.send(wid);
} else {
match event {
Event::WindowEvent { window_id, event } => {
@@ -687,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)?),
})
@@ -748,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
@@ -805,17 +812,59 @@ impl<'a> Deref for DeviceInfo<'a> {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(xinput::DeviceId);
impl DeviceId {
#[allow(unused)]
pub const fn dummy() -> Self {
DeviceId(0)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId(u32);
impl FingerId {
#[cfg(test)]
#[allow(unused)]
pub const fn dummy() -> Self {
FingerId(0)
}
}
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,
@@ -862,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 {
@@ -889,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")
},
}
}
}
@@ -988,10 +1031,10 @@ impl<'a, E: fmt::Debug> CookieResultExt for Result<VoidCookie<'a>, E> {
}
fn mkwid(w: xproto::Window) -> crate::window::WindowId {
crate::window::WindowId::from_raw(w as _)
crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _))
}
fn mkdid(w: xinput::DeviceId) -> DeviceId {
DeviceId::from_raw(w as i64)
fn mkdid(w: xinput::DeviceId) -> crate::event::DeviceId {
crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w)))
}
fn mkfid(w: u32) -> crate::event::FingerId {

View File

@@ -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);
}
}
}

View File

@@ -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,

View File

@@ -6,6 +6,7 @@ use x11rb::protocol::xkb;
use super::*;
pub const VIRTUAL_CORE_POINTER: u16 = 2;
pub const VIRTUAL_CORE_KEYBOARD: u16 = 3;
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
// re-allocate (and make another round-trip) in the *vast* majority of cases.

View File

@@ -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};
@@ -18,12 +17,12 @@ use x11rb::protocol::{randr, xinput};
use super::util::{self, SelectedCursor};
use super::{
ffi, ActiveEventLoop, CookieResultExt, ImeRequest, ImeSender, VoidCookie, XConnection,
ffi, ActiveEventLoop, CookieResultExt, ImeRequest, ImeSender, VoidCookie, WindowId, XConnection,
};
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,314 +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, WindowId, 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) -> 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().into_raw() as xproto::Window)
{
c.ignore_error();
}
}
}
#[derive(Debug)]
pub struct SharedState {
pub cursor_pos: Option<(f64, f64)>,
@@ -358,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,
@@ -398,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,
@@ -429,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()))),
}
};
}
@@ -440,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")]
@@ -477,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));
@@ -489,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);
}
@@ -525,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)
},
@@ -714,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 {
@@ -743,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
@@ -825,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")));
}
}
@@ -845,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
@@ -887,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,
@@ -1209,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
@@ -1222,22 +924,23 @@ impl UnownedWindow {
&self.shared_state_lock(),
);
let old_surface_size = PhysicalSize::new(width, height);
let surface_size = Arc::new(Mutex::new(PhysicalSize::new(new_width, new_height)));
let window_id = crate::window::WindowId(self.id());
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: self.id(),
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);
}
}
}
@@ -1498,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();
@@ -1519,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())
}
@@ -1557,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
@@ -1567,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(
@@ -1599,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 {
@@ -1609,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
}
@@ -1636,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)))
@@ -1645,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)))
@@ -1661,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,
@@ -1683,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)
@@ -1702,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");
@@ -1733,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;
@@ -1744,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`");
}
@@ -1790,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);
@@ -1813,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(());
@@ -1827,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
@@ -1877,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()));
},
};
@@ -1904,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}");
}
}
@@ -1924,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,
@@ -1952,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)
}
@@ -1970,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,
@@ -1984,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];
@@ -2001,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
@@ -2017,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]
@@ -2127,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)
@@ -2135,7 +1837,7 @@ impl UnownedWindow {
#[inline]
pub fn id(&self) -> WindowId {
WindowId::from_raw(self.xwindow as _)
WindowId(self.xwindow as _)
}
pub(super) fn sync_counter_id(&self) -> Option<NonZeroU32> {
@@ -2144,7 +1846,7 @@ impl UnownedWindow {
#[inline]
pub fn request_redraw(&self) {
self.redraw_sender.send(WindowId::from_raw(self.xwindow as _));
self.redraw_sender.send(WindowId(self.xwindow as _));
}
#[inline]

View File

@@ -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 {

View File

@@ -1,3 +1,4 @@
use std::any::Any;
use std::cell::Cell;
use std::collections::VecDeque;
use std::sync::{mpsc, Arc, Mutex};
@@ -12,20 +13,19 @@ use orbclient::{
use smol_str::SmolStr;
use super::{
KeyEventExtra, MonitorHandle, PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket,
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,
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId as RootWindowId,
};
fn convert_scancode(scancode: u8) -> (PhysicalKey, Option<NamedKey>) {
@@ -283,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 {
@@ -295,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(),
@@ -361,8 +368,9 @@ impl EventLoop {
key_without_modifiers = logical_key.clone();
}
let window_id = RootWindowId(window_id);
let event = event::WindowEvent::KeyboardInput {
device_id: None,
device_id: event::DeviceId(DeviceId),
event: event::KeyEvent {
logical_key,
physical_key,
@@ -392,62 +400,82 @@ impl EventLoop {
EventOption::TextInput(TextInputEvent { character }) => {
app.window_event(
window_target,
window_id,
RootWindowId(window_id),
event::WindowEvent::Ime(Ime::Preedit("".into(), None)),
);
app.window_event(
window_target,
window_id,
RootWindowId(window_id),
event::WindowEvent::Ime(Ime::Commit(character.into())),
);
},
EventOption::Mouse(MouseEvent { x, y }) => {
app.window_event(window_target, window_id, event::WindowEvent::PointerMoved {
device_id: None,
position: (x, y).into(),
source: event::PointerSource::Mouse,
});
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::CursorMoved {
device_id: event::DeviceId(DeviceId),
position: (x, y).into(),
},
);
},
EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => {
app.device_event(window_target, None, event::DeviceEvent::PointerMotion {
delta: (dx as f64, dy as f64),
});
app.device_event(
window_target,
event::DeviceId(DeviceId),
event::DeviceEvent::MouseMotion { delta: (dx as f64, dy as f64) },
);
},
EventOption::Button(ButtonEvent { left, middle, right }) => {
while let Some((button, state)) = event_state.mouse(left, middle, right) {
app.window_event(window_target, window_id, event::WindowEvent::PointerButton {
device_id: None,
state,
position: dpi::PhysicalPosition::default(),
button: button.into(),
});
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::MouseInput {
device_id: event::DeviceId(DeviceId),
state,
button,
},
);
}
},
EventOption::Scroll(ScrollEvent { x, y }) => {
app.window_event(window_target, window_id, event::WindowEvent::MouseWheel {
device_id: None,
delta: event::MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: event::TouchPhase::Moved,
});
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::MouseWheel {
device_id: event::DeviceId(DeviceId),
delta: event::MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: event::TouchPhase::Moved,
},
);
},
EventOption::Quit(QuitEvent {}) => {
app.window_event(window_target, window_id, event::WindowEvent::CloseRequested);
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::CloseRequested,
);
},
EventOption::Focus(FocusEvent { focused }) => {
app.window_event(window_target, window_id, event::WindowEvent::Focused(focused));
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::Focused(focused),
);
},
EventOption::Move(MoveEvent { x, y }) => {
app.window_event(
window_target,
window_id,
RootWindowId(window_id),
event::WindowEvent::Moved((x, y).into()),
);
},
EventOption::Resize(ResizeEvent { width, height }) => {
app.window_event(
window_target,
window_id,
event::WindowEvent::SurfaceResized((width, height).into()),
RootWindowId(window_id),
event::WindowEvent::Resized((width, height).into()),
);
// Acknowledge resize after event loop.
@@ -456,20 +484,12 @@ impl EventLoop {
// TODO: Screen, Clipboard, Drop
EventOption::Hover(HoverEvent { entered }) => {
let event = if entered {
event::WindowEvent::PointerEntered {
device_id: None,
position: dpi::PhysicalPosition::default(),
kind: event::PointerKind::Mouse,
}
event::WindowEvent::CursorEntered { device_id: event::DeviceId(DeviceId) }
} else {
event::WindowEvent::PointerLeft {
device_id: None,
position: None,
kind: event::PointerKind::Mouse,
}
event::WindowEvent::CursorLeft { device_id: event::DeviceId(DeviceId) }
};
app.window_event(window_target, window_id, event);
app.window_event(window_target, RootWindowId(window_id), event);
},
other => {
tracing::warn!("unhandled event: {:?}", other);
@@ -491,7 +511,7 @@ impl EventLoop {
let mut creates = self.window_target.creates.lock().unwrap();
creates.pop_front()
} {
let window_id = WindowId::from_raw(window.fd);
let window_id = WindowId { fd: window.fd as u64 };
let mut buf: [u8; 4096] = [0; 4096];
let path = window.fpath(&mut buf).expect("failed to read properties");
@@ -499,8 +519,10 @@ impl EventLoop {
self.windows.push((window, EventState::default()));
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.
@@ -513,16 +535,16 @@ impl EventLoop {
let mut destroys = self.window_target.destroys.lock().unwrap();
destroys.pop_front()
} {
app.window_event(&self.window_target, destroy_id, event::WindowEvent::Destroyed);
self.windows
.retain(|(window, _event_state)| WindowId::from_raw(window.fd) != destroy_id);
let window_id = RootWindowId(destroy_id);
app.window_event(&self.window_target, window_id, event::WindowEvent::Destroyed);
self.windows.retain(|(window, _event_state)| window.fd as u64 != destroy_id.fd);
}
// Handle window events.
let mut i = 0;
// While loop is used here because the same window may be processed more than once.
while let Some((window, event_state)) = self.windows.get_mut(i) {
let window_id = WindowId::from_raw(window.fd);
let window_id = WindowId { fd: window.fd as u64 };
let mut event_buf = [0u8; 16 * mem::size_of::<orbclient::Event>()];
let count =
@@ -580,7 +602,7 @@ impl EventLoop {
} {
app.window_event(
&self.window_target,
window_id,
RootWindowId(window_id),
event::WindowEvent::RedrawRequested,
);
}
@@ -708,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>> {
@@ -755,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

View File

@@ -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,
}
@@ -99,11 +96,42 @@ impl TimeSocket {
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct PlatformSpecificEventLoopAttributes {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct WindowId {
fd: u64,
}
impl WindowId {
pub const fn dummy() -> Self {
WindowId { fd: u64::MAX }
}
}
impl From<WindowId> for u64 {
fn from(id: WindowId) -> Self {
id.fd
}
}
impl From<u64> for WindowId {
fn from(fd: u64) -> Self {
Self { fd }
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DeviceId;
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FingerId;
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId
}
@@ -145,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;

View File

@@ -1,12 +1,14 @@
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, TimeSocket, 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};
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,64 +125,60 @@ 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) -> WindowId {
WindowId::from_raw(self.window_socket.fd)
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) {
pub fn request_redraw(&self) {
let window_id = self.id();
let mut redraws = self.redraws.lock().unwrap();
if !redraws.contains(&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,66 +420,58 @@ 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 {

View File

@@ -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)

View File

@@ -1,13 +1,13 @@
use crate::event::{DeviceId, FingerId as RootFingerId};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(i32);
pub(crate) fn mkdid(pointer_id: i32) -> Option<DeviceId> {
if let Ok(pointer_id) = u32::try_from(pointer_id) {
Some(DeviceId::from_raw(pointer_id as i64))
} else if pointer_id == -1 {
None
} else {
tracing::error!("found unexpected negative `PointerEvent.pointerId`: {pointer_id}");
None
impl DeviceId {
pub fn new(pointer_id: i32) -> Self {
Self(pointer_id)
}
pub const fn dummy() -> Self {
Self(-1)
}
}
@@ -22,7 +22,6 @@ impl FingerId {
Self { pointer_id, primary }
}
#[cfg(test)]
pub const fn dummy() -> Self {
Self { pointer_id: -1, primary: false }
}
@@ -31,9 +30,3 @@ impl FingerId {
self.primary
}
}
impl From<FingerId> for RootFingerId {
fn from(id: FingerId) -> Self {
Self(id)
}
}

View File

@@ -1,4 +1,4 @@
use super::{backend, HasMonitorPermissionFuture, MonitorPermissionFuture};
use super::{backend, event, window, HasMonitorPermissionFuture, MonitorPermissionFuture};
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, NotSupportedError};
use crate::event::Event;

View File

@@ -4,18 +4,22 @@ 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};
use super::super::event;
use super::super::main_thread::MainThreadMarker;
use super::super::monitor::MonitorHandler;
use super::super::DeviceId;
use super::backend;
use super::state::State;
use crate::dpi::PhysicalSize;
use crate::event::{DeviceEvent, ElementState, Event, RawKeyEvent, StartCause, WindowEvent};
use crate::event::{
DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, RawKeyEvent, StartCause,
WindowEvent,
};
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::platform::web::{PollStrategy, WaitUntilStrategy};
use crate::platform_impl::platform::backend::EventListenerHandle;
@@ -46,7 +50,7 @@ struct Execution {
suspended: Cell<bool>,
event_loop_recreation: Cell<bool>,
events: RefCell<VecDeque<EventWrapper>>,
id: Cell<usize>,
id: RefCell<u32>,
window: web_sys::Window,
navigator: Navigator,
document: Document,
@@ -168,7 +172,7 @@ impl Shared {
window,
navigator,
document,
id: Cell::new(0),
id: RefCell::new(0),
all_canvases: RefCell::new(Vec::new()),
redraw_pending: RefCell::new(HashSet::new()),
destroy_pending: RefCell::new(VecDeque::new()),
@@ -283,7 +287,7 @@ impl Shared {
}
// chorded button event
let device_id = event::mkdid(event.pointer_id());
let device_id = RootDeviceId(DeviceId::new(event.pointer_id()));
if let Some(button) = backend::event::mouse_button(&event) {
let state = if backend::event::mouse_buttons(&event).contains(button.into()) {
@@ -294,7 +298,7 @@ impl Shared {
runner.send_event(Event::DeviceEvent {
device_id,
event: DeviceEvent::Button { button: button.to_id().into(), state },
event: DeviceEvent::Button { button: button.to_id(), state },
});
return;
@@ -307,7 +311,7 @@ impl Shared {
Event::DeviceEvent {
device_id,
event: DeviceEvent::PointerMotion { delta: (delta.x, delta.y) },
event: DeviceEvent::MouseMotion { delta: (delta.x, delta.y) },
}
}));
}),
@@ -324,7 +328,7 @@ impl Shared {
if let Some(delta) = backend::event::mouse_scroll_delta(&window, &event) {
runner.send_event(Event::DeviceEvent {
device_id: None,
device_id: RootDeviceId(DeviceId::dummy()),
event: DeviceEvent::MouseWheel { delta },
});
}
@@ -341,9 +345,9 @@ impl Shared {
let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
runner.send_event(Event::DeviceEvent {
device_id: event::mkdid(event.pointer_id()),
device_id: RootDeviceId(DeviceId::new(event.pointer_id())),
event: DeviceEvent::Button {
button: button.to_id().into(),
button: button.to_id(),
state: ElementState::Pressed,
},
});
@@ -360,9 +364,9 @@ impl Shared {
let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
runner.send_event(Event::DeviceEvent {
device_id: event::mkdid(event.pointer_id()),
device_id: RootDeviceId(DeviceId::new(event.pointer_id())),
event: DeviceEvent::Button {
button: button.to_id().into(),
button: button.to_id(),
state: ElementState::Released,
},
});
@@ -378,7 +382,7 @@ impl Shared {
}
runner.send_event(Event::DeviceEvent {
device_id: None,
device_id: RootDeviceId(DeviceId::dummy()),
event: DeviceEvent::Key(RawKeyEvent {
physical_key: backend::event::key_code(&event),
state: ElementState::Pressed,
@@ -396,7 +400,7 @@ impl Shared {
}
runner.send_event(Event::DeviceEvent {
device_id: None,
device_id: RootDeviceId(DeviceId::dummy()),
event: DeviceEvent::Key(RawKeyEvent {
physical_key: backend::event::key_code(&event),
state: ElementState::Released,
@@ -435,11 +439,11 @@ impl Shared {
// Generate a strictly increasing ID
// This is used to differentiate windows when handling events
pub fn generate_id(&self) -> usize {
let id = self.0.id.get();
self.0.id.set(id.checked_add(1).expect("exhausted `WindowId`"));
pub fn generate_id(&self) -> u32 {
let mut id = self.0.id.borrow_mut();
*id += 1;
id
*id
}
pub fn request_redraw(&self, id: WindowId) {
@@ -491,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() {

View File

@@ -1,3 +1,4 @@
use std::any::Any;
use std::cell::Cell;
use std::clone::Clone;
use std::iter;
@@ -7,10 +8,15 @@ use web_sys::Element;
use super::super::monitor::MonitorPermissionFuture;
use super::super::{lock, KeyEventExtra};
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::event::{ElementState, Event, KeyEvent, TouchPhase, WindowEvent};
use crate::error::{ExternalError, NotSupportedError};
use crate::event::{
DeviceId as RootDeviceId, ElementState, Event, FingerId as RootFingerId, KeyEvent, Touch,
TouchPhase, WindowEvent,
};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
@@ -21,7 +27,10 @@ use crate::platform::web::{CustomCursorFuture, PollStrategy, WaitUntilStrategy};
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};
use crate::window::{
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as RootWindow,
WindowId as RootWindowId,
};
#[derive(Default)]
struct ModifiersShared(Rc<Cell<ModifiersState>>);
@@ -63,14 +72,14 @@ impl ActiveEventLoop {
}
pub fn generate_id(&self) -> WindowId {
WindowId::from_raw(self.runner.generate_id())
WindowId(self.runner.generate_id())
}
pub fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture {
CustomCursorFuture(CustomCursor::new_async(self, source.inner))
}
pub fn register(&self, canvas: &Rc<backend::Canvas>, window_id: WindowId) {
pub fn register(&self, canvas: &Rc<backend::Canvas>, id: WindowId) {
let canvas_clone = canvas.clone();
canvas.on_touch_start();
@@ -84,13 +93,13 @@ impl ActiveEventLoop {
let clear_modifiers = (!modifiers.get().is_empty()).then(|| {
modifiers.set(ModifiersState::empty());
Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
}
});
runner.send_events(clear_modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::Focused(false),
})));
});
@@ -100,7 +109,7 @@ impl ActiveEventLoop {
canvas.on_focus(move || {
if !has_focus.replace(true) {
runner.send_event(Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::Focused(true),
});
}
@@ -119,8 +128,10 @@ impl ActiveEventLoop {
if focused {
canvas.has_focus.set(true);
self.runner
.send_event(Event::WindowEvent { window_id, event: WindowEvent::Focused(true) })
self.runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Focused(true),
})
}
let runner = self.runner.clone();
@@ -130,16 +141,18 @@ impl ActiveEventLoop {
let modifiers_changed = (modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let device_id = RootDeviceId(DeviceId::dummy());
runner.send_events(
iter::once(Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::KeyboardInput {
device_id: None,
device_id,
event: KeyEvent {
physical_key,
logical_key,
@@ -164,16 +177,18 @@ impl ActiveEventLoop {
let modifiers_changed = (modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let device_id = RootDeviceId(DeviceId::dummy());
runner.send_events(
iter::once(Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::KeyboardInput {
device_id: None,
device_id,
event: KeyEvent {
physical_key,
logical_key,
@@ -192,146 +207,286 @@ impl ActiveEventLoop {
);
let has_focus = canvas.has_focus.clone();
canvas.on_pointer_leave({
canvas.on_cursor_leave({
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, kind| {
move |active_modifiers, pointer_id| {
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft { device_id, position: Some(position), kind },
})))
}
});
canvas.on_pointer_enter({
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, kind| {
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
let pointer = pointer_id.map(|device_id| Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorLeft { device_id: RootDeviceId(device_id) },
});
runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered { device_id, position, kind },
})))
}
});
canvas.on_pointer_move(
{
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |device_id, events| {
runner.send_events(events.flat_map(|(active_modifiers, position, source)| {
let modifiers = (has_focus.get() && modifiers.get() != active_modifiers)
.then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved { device_id, position, source },
}))
}));
if focus.is_some() || pointer.is_some() {
runner.send_events(focus.into_iter().chain(pointer))
}
},
}
});
canvas.on_cursor_enter({
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id| {
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let pointer = pointer_id.map(|device_id| Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorEntered { device_id: RootDeviceId(device_id) },
});
if focus.is_some() || pointer.is_some() {
runner.send_events(focus.into_iter().chain(pointer))
}
}
});
canvas.on_cursor_move(
{
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, state, button| {
move |active_modifiers, pointer_id, events| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers.into_iter().chain([Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton { device_id, state, position, button },
}]));
runner.send_events(modifiers.into_iter().chain(events.flat_map(|position| {
let device_id = RootDeviceId(pointer_id);
iter::once(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { device_id, position },
})
})));
}
},
{
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, finger_id, events| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers.into_iter().chain(events.map(
|(location, force)| Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Touch(Touch {
finger_id: RootFingerId(finger_id),
device_id: RootDeviceId(device_id),
phase: TouchPhase::Moved,
force: Some(force),
location,
}),
},
)));
}
},
{
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers,
device_id,
position: crate::dpi::PhysicalPosition<f64>,
buttons,
button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let device_id = RootDeviceId(device_id);
let state = if buttons.contains(button.into()) {
ElementState::Pressed
} else {
ElementState::Released
};
// A chorded button event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position.
runner.send_events(modifiers.into_iter().chain([
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { device_id, position },
},
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput { device_id, state, button },
},
]));
}
},
);
canvas.on_pointer_press({
let runner = self.runner.clone();
let modifiers = self.modifiers.clone();
canvas.on_mouse_press(
{
let runner = self.runner.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, button| {
let modifiers = (modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id,
state: ElementState::Pressed,
position,
button,
},
})));
}
});
canvas.on_pointer_release({
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
move |active_modifiers, pointer_id, position, button| {
let modifiers = (modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id,
state: ElementState::Released,
position,
button,
},
})));
}
});
let device_id: RootDeviceId = RootDeviceId(pointer_id);
// A mouse down event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position.
runner.send_events(modifiers.into_iter().chain([
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { device_id, position },
},
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id,
state: ElementState::Pressed,
button,
},
},
]));
}
},
{
let runner = self.runner.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, finger_id, location, force| {
let modifiers = (modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers.into_iter().chain(iter::once(
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Touch(Touch {
finger_id: RootFingerId(finger_id),
device_id: RootDeviceId(device_id),
phase: TouchPhase::Started,
force: Some(force),
location,
}),
},
)))
}
},
);
canvas.on_mouse_release(
{
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id, position, button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let device_id: RootDeviceId = RootDeviceId(pointer_id);
// A mouse up event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position.
runner.send_events(modifiers.into_iter().chain([
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { device_id, position },
},
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id,
state: ElementState::Released,
button,
},
},
]));
}
},
{
let runner_touch = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, finger_id, location, force| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner_touch.send_events(modifiers.into_iter().chain(iter::once(
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Touch(Touch {
finger_id: RootFingerId(finger_id),
device_id: RootDeviceId(device_id),
phase: TouchPhase::Ended,
force: Some(force),
location,
}),
},
)));
}
},
);
let runner = self.runner.clone();
let modifiers = self.modifiers.clone();
@@ -340,16 +495,16 @@ impl ActiveEventLoop {
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers_changed.into_iter().chain(iter::once(
Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::MouseWheel {
device_id: None,
device_id: RootDeviceId(DeviceId::dummy()),
delta,
phase: TouchPhase::Moved,
},
@@ -357,11 +512,25 @@ impl ActiveEventLoop {
)));
});
let runner = self.runner.clone();
canvas.on_touch_cancel(move |device_id, finger_id, location, force| {
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Touch(Touch {
finger_id: RootFingerId(finger_id),
device_id: RootDeviceId(device_id),
phase: TouchPhase::Cancelled,
force: Some(force),
location,
}),
});
});
let runner = self.runner.clone();
canvas.on_dark_mode(move |is_dark_mode| {
let theme = if is_dark_mode { Theme::Dark } else { Theme::Light };
runner.send_event(Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::ThemeChanged(theme),
});
});
@@ -388,8 +557,8 @@ impl ActiveEventLoop {
if canvas.old_size() != new_size {
canvas.set_old_size(new_size);
runner.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::SurfaceResized(new_size),
window_id: RootWindowId(id),
event: WindowEvent::Resized(new_size),
});
canvas.request_animation_frame();
}
@@ -404,7 +573,7 @@ impl ActiveEventLoop {
&& !(is_intersecting && canvas_clone.is_intersecting.get().is_none())
{
runner.send_event(Event::WindowEvent {
window_id,
window_id: RootWindowId(id),
event: WindowEvent::Occluded(!is_intersecting),
});
}
@@ -413,7 +582,7 @@ impl ActiveEventLoop {
});
let runner = self.runner.clone();
canvas.on_animation_frame(move || runner.request_redraw(window_id));
canvas.on_animation_frame(move || runner.request_redraw(RootWindowId(id)));
canvas.on_context_menu();
}
@@ -439,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 {
@@ -467,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) })
}
@@ -527,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

View File

@@ -37,7 +37,8 @@ pub(crate) use cursor::{
CustomCursorSource as PlatformCustomCursorSource,
};
pub use self::event::FingerId;
pub use self::error::OsError;
pub use self::event::{DeviceId, FingerId};
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
@@ -48,5 +49,6 @@ pub(crate) use self::monitor::{
VideoModeHandle,
};
use self::web_sys as backend;
pub use self::window::{PlatformSpecificWindowAttributes, Window};
pub use self::window::{PlatformSpecificWindowAttributes, Window, WindowId};
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;

View File

@@ -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())

View File

@@ -12,22 +12,21 @@ use web_sys::{
};
use super::super::cursor::CursorHandler;
use super::super::event::{DeviceId, FingerId};
use super::super::main_thread::MainThreadMarker;
use super::super::WindowId;
use super::animation_frame::AnimationFrameHandler;
use super::event_handle::EventListenerHandle;
use super::intersection_handle::IntersectionObserverHandle;
use super::media_query_handle::MediaQueryListHandle;
use super::pointer::PointerHandler;
use super::{event, fullscreen, ResizeScaleHandle};
use super::{event, fullscreen, ButtonsState, ResizeScaleHandle};
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::error::RequestError;
use crate::event::{
ButtonSource, DeviceId, ElementState, MouseScrollDelta, PointerKind, PointerSource,
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::window::{WindowAttributes, WindowId};
use crate::platform_impl::{Fullscreen, OsError};
use crate::window::{WindowAttributes, WindowId as RootWindowId};
#[allow(dead_code)]
pub struct Canvas {
@@ -84,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(),
};
@@ -110,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);
@@ -127,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));
}
@@ -214,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()
}
@@ -329,62 +328,75 @@ impl Canvas {
}));
}
pub fn on_pointer_leave<F>(&self, handler: F)
pub fn on_cursor_leave<F>(&self, handler: F)
where
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
F: 'static + FnMut(ModifiersState, Option<DeviceId>),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_leave(&self.common, handler)
self.handlers.borrow_mut().pointer_handler.on_cursor_leave(&self.common, handler)
}
pub fn on_pointer_enter<F>(&self, handler: F)
pub fn on_cursor_enter<F>(&self, handler: F)
where
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
F: 'static + FnMut(ModifiersState, Option<DeviceId>),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_enter(&self.common, handler)
self.handlers.borrow_mut().pointer_handler.on_cursor_enter(&self.common, handler)
}
pub fn on_pointer_release<C>(&self, handler: C)
pub fn on_mouse_release<M, T>(&self, mouse_handler: M, touch_handler: T)
where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
M: 'static + FnMut(ModifiersState, DeviceId, PhysicalPosition<f64>, MouseButton),
T: 'static + FnMut(ModifiersState, DeviceId, FingerId, PhysicalPosition<f64>, Force),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_release(&self.common, handler)
}
pub fn on_pointer_press<C>(&self, handler: C)
where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_press(
self.handlers.borrow_mut().pointer_handler.on_mouse_release(
&self.common,
handler,
mouse_handler,
touch_handler,
)
}
pub fn on_mouse_press<M, T>(&self, mouse_handler: M, touch_handler: T)
where
M: 'static + FnMut(ModifiersState, DeviceId, PhysicalPosition<f64>, MouseButton),
T: 'static + FnMut(ModifiersState, DeviceId, FingerId, PhysicalPosition<f64>, Force),
{
self.handlers.borrow_mut().pointer_handler.on_mouse_press(
&self.common,
mouse_handler,
touch_handler,
Rc::clone(&self.prevent_default),
)
}
pub fn on_pointer_move<C, B>(&self, cursor_handler: C, button_handler: B)
pub fn on_cursor_move<M, T, B>(&self, mouse_handler: M, touch_handler: T, button_handler: B)
where
C: 'static
+ FnMut(
Option<DeviceId>,
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
),
B: 'static
M: 'static
+ FnMut(ModifiersState, DeviceId, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
T: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
PhysicalPosition<f64>,
ElementState,
ButtonSource,
DeviceId,
FingerId,
&mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>,
),
B: 'static
+ FnMut(ModifiersState, DeviceId, PhysicalPosition<f64>, ButtonsState, MouseButton),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_move(
self.handlers.borrow_mut().pointer_handler.on_cursor_move(
&self.common,
cursor_handler,
mouse_handler,
touch_handler,
button_handler,
Rc::clone(&self.prevent_default),
)
}
pub fn on_touch_cancel<F>(&self, handler: F)
where
F: 'static + FnMut(DeviceId, FingerId, PhysicalPosition<f64>, Force),
{
self.handlers.borrow_mut().pointer_handler.on_touch_cancel(&self.common, handler)
}
pub fn on_mouse_wheel<F>(&self, mut handler: F)
where
F: 'static + FnMut(MouseScrollDelta, ModifiersState),
@@ -489,10 +501,10 @@ impl Canvas {
let new_size = {
let new_size = Arc::new(Mutex::new(current_size));
event_handler(crate::event::Event::WindowEvent {
window_id: self.id,
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)),
},
});
@@ -501,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);
@@ -517,8 +529,8 @@ impl Canvas {
// Then we at least send a resized event.
self.set_old_size(new_size);
runner.send_event(crate::event::Event::WindowEvent {
window_id: self.id,
event: crate::event::WindowEvent::SurfaceResized(new_size),
window_id: RootWindowId(self.id),
event: crate::event::WindowEvent::Resized(new_size),
})
}
}

View File

@@ -6,9 +6,8 @@ use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{KeyboardEvent, MouseEvent, Navigator, PointerEvent, WheelEvent};
use super::super::FingerId;
use super::Engine;
use crate::event::{MouseButton, MouseScrollDelta, PointerKind};
use crate::event::{MouseButton, MouseScrollDelta};
use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey};
bitflags::bitflags! {
@@ -69,14 +68,14 @@ pub fn mouse_button(event: &MouseEvent) -> Option<MouseButton> {
}
impl MouseButton {
pub fn to_id(self) -> u16 {
pub fn to_id(self) -> u32 {
match self {
MouseButton::Left => 0,
MouseButton::Right => 1,
MouseButton::Middle => 2,
MouseButton::Back => 3,
MouseButton::Forward => 4,
MouseButton::Other(value) => value,
MouseButton::Other(value) => value.into(),
}
}
}
@@ -161,14 +160,6 @@ pub fn mouse_scroll_delta(
}
}
pub fn pointer_type(event: &PointerEvent, pointer_id: i32) -> PointerKind {
match event.pointer_type().as_str() {
"mouse" => PointerKind::Mouse,
"touch" => PointerKind::Touch(FingerId::new(pointer_id, event.is_primary()).into()),
_ => PointerKind::Unknown,
}
}
pub fn key_code(event: &KeyboardEvent) -> PhysicalKey {
let code = event.code();
PhysicalKey::from_key_code_attribute_value(&code)

View File

@@ -18,6 +18,7 @@ use wasm_bindgen::JsCast;
use web_sys::{Document, HtmlCanvasElement, Navigator, PageTransitionEvent, VisibilityState};
pub use self::canvas::{Canvas, Style};
pub use self::event::ButtonsState;
pub use self::event_handle::EventListenerHandle;
pub use self::resize_scaling::ResizeScaleHandle;
pub use self::schedule::Schedule;

View File

@@ -1,15 +1,16 @@
use std::cell::Cell;
use std::rc::Rc;
use event::ButtonsState;
use web_sys::PointerEvent;
use super::super::event::{DeviceId, FingerId};
use super::canvas::Common;
use super::event;
use super::event_handle::EventListenerHandle;
use crate::dpi::PhysicalPosition;
use crate::event::{ButtonSource, DeviceId, ElementState, Force, PointerKind, PointerSource};
use crate::event::{Force, MouseButton};
use crate::keyboard::ModifiersState;
use crate::platform_impl::web::event::mkdid;
#[allow(dead_code)]
pub(super) struct PointerHandler {
@@ -33,78 +34,86 @@ impl PointerHandler {
}
}
pub fn on_pointer_leave<F>(&mut self, canvas_common: &Common, mut handler: F)
pub fn on_cursor_leave<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
F: 'static + FnMut(ModifiersState, Option<DeviceId>),
{
let window = canvas_common.window.clone();
self.on_cursor_leave =
Some(canvas_common.add_event("pointerout", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
let device_id = mkdid(pointer_id);
let position =
event::mouse_position(&event).to_physical(super::scale_factor(&window));
let kind = event::pointer_type(&event, pointer_id);
handler(modifiers, device_id, position, kind);
// touch events are handled separately
// handling them here would produce duplicate mouse events, inconsistent with
// other platforms.
let device_id =
(event.pointer_type() != "touch").then(|| DeviceId::new(event.pointer_id()));
handler(modifiers, device_id);
}));
}
pub fn on_pointer_enter<F>(&mut self, canvas_common: &Common, mut handler: F)
pub fn on_cursor_enter<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
F: 'static + FnMut(ModifiersState, Option<DeviceId>),
{
let window = canvas_common.window.clone();
self.on_cursor_enter =
Some(canvas_common.add_event("pointerover", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
let device_id = mkdid(pointer_id);
let position =
event::mouse_position(&event).to_physical(super::scale_factor(&window));
let kind = event::pointer_type(&event, pointer_id);
handler(modifiers, device_id, position, kind);
// touch events are handled separately
// handling them here would produce duplicate mouse events, inconsistent with
// other platforms.
let device_id =
(event.pointer_type() != "touch").then(|| DeviceId::new(event.pointer_id()));
handler(modifiers, device_id);
}));
}
pub fn on_pointer_release<C>(&mut self, canvas_common: &Common, mut handler: C)
where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
pub fn on_mouse_release<M, T>(
&mut self,
canvas_common: &Common,
mut mouse_handler: M,
mut touch_handler: T,
) where
M: 'static + FnMut(ModifiersState, DeviceId, PhysicalPosition<f64>, MouseButton),
T: 'static + FnMut(ModifiersState, DeviceId, FingerId, PhysicalPosition<f64>, Force),
{
let window = canvas_common.window.clone();
self.on_pointer_release =
Some(canvas_common.add_event("pointerup", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
let kind = event::pointer_type(&event, pointer_id);
let button = event::mouse_button(&event).expect("no mouse button pressed");
let source = match kind {
PointerKind::Mouse => ButtonSource::Mouse(button),
PointerKind::Touch(finger_id) => ButtonSource::Touch {
finger_id,
force: Some(Force::Normalized(event.pressure().into())),
match event.pointer_type().as_str() {
"touch" => {
let pointer_id = event.pointer_id();
touch_handler(
modifiers,
DeviceId::new(pointer_id),
FingerId::new(pointer_id, event.is_primary()),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
Force::Normalized(event.pressure() as f64),
)
},
PointerKind::Unknown => ButtonSource::Unknown(button.to_id()),
};
handler(
modifiers,
mkdid(pointer_id),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
_ => mouse_handler(
modifiers,
DeviceId::new(event.pointer_id()),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
event::mouse_button(&event).expect("no mouse button released"),
),
}
}));
}
pub fn on_pointer_press<C>(
pub fn on_mouse_press<M, T>(
&mut self,
canvas_common: &Common,
mut handler: C,
mut mouse_handler: M,
mut touch_handler: T,
prevent_default: Rc<Cell<bool>>,
) where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
M: 'static + FnMut(ModifiersState, DeviceId, PhysicalPosition<f64>, MouseButton),
T: 'static + FnMut(ModifiersState, DeviceId, FingerId, PhysicalPosition<f64>, Force),
{
let window = canvas_common.window.clone();
let canvas = canvas_common.raw().clone();
@@ -118,65 +127,69 @@ impl PointerHandler {
}
let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
let kind = event::pointer_type(&event, pointer_id);
let button = event::mouse_button(&event).expect("no mouse button pressed");
let pointer_type = &event.pointer_type();
let source = match kind {
PointerKind::Mouse => {
// Error is swallowed here since the error would occur every time the
// mouse is clicked when the cursor is
// grabbed, and there is probably not a
// situation where this could fail, that we
// care if it fails.
let _e = canvas.set_pointer_capture(pointer_id);
ButtonSource::Mouse(button)
match pointer_type.as_str() {
"touch" => {
let pointer_id = event.pointer_id();
touch_handler(
modifiers,
DeviceId::new(pointer_id),
FingerId::new(pointer_id, event.is_primary()),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
Force::Normalized(event.pressure() as f64),
);
},
PointerKind::Touch(finger_id) => ButtonSource::Touch {
finger_id,
force: Some(Force::Normalized(event.pressure().into())),
},
PointerKind::Unknown => ButtonSource::Unknown(button.to_id()),
};
_ => {
let pointer_id = event.pointer_id();
handler(
modifiers,
mkdid(pointer_id),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
mouse_handler(
modifiers,
DeviceId::new(pointer_id),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
event::mouse_button(&event).expect("no mouse button pressed"),
);
if pointer_type == "mouse" {
// Error is swallowed here since the error would occur every time the
// mouse is clicked when the cursor is
// grabbed, and there is probably not a
// situation where this could fail, that we
// care if it fails.
let _e = canvas.set_pointer_capture(pointer_id);
}
},
}
}));
}
pub fn on_pointer_move<C, B>(
pub fn on_cursor_move<M, T, B>(
&mut self,
canvas_common: &Common,
mut cursor_handler: C,
mut mouse_handler: M,
mut touch_handler: T,
mut button_handler: B,
prevent_default: Rc<Cell<bool>>,
) where
C: 'static
+ FnMut(
Option<DeviceId>,
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
),
B: 'static
M: 'static
+ FnMut(ModifiersState, DeviceId, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
T: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
PhysicalPosition<f64>,
ElementState,
ButtonSource,
DeviceId,
FingerId,
&mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>,
),
B: 'static
+ FnMut(ModifiersState, DeviceId, PhysicalPosition<f64>, ButtonsState, MouseButton),
{
let window = canvas_common.window.clone();
let canvas = canvas_common.raw().clone();
self.on_cursor_move =
Some(canvas_common.add_event("pointermove", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
let device_id = mkdid(pointer_id);
let kind = event::pointer_type(&event, pointer_id);
let device_id = DeviceId::new(pointer_id);
// chorded button event
if let Some(button) = event::mouse_button(&event) {
@@ -187,34 +200,11 @@ impl PointerHandler {
let _ = canvas.focus();
}
let state = if event::mouse_buttons(&event).contains(button.into()) {
ElementState::Pressed
} else {
ElementState::Released
};
let button = match kind {
PointerKind::Mouse => ButtonSource::Mouse(button),
PointerKind::Touch(finger_id) => {
let button_id = button.to_id();
if button_id != 1 {
tracing::error!("unexpected touch button id: {button_id}");
}
ButtonSource::Touch {
finger_id,
force: Some(Force::Normalized(event.pressure().into())),
}
},
PointerKind::Unknown => todo!(),
};
button_handler(
event::mouse_modifiers(&event),
modifiers,
device_id,
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
state,
event::mouse_buttons(&event),
button,
);
@@ -223,24 +213,44 @@ impl PointerHandler {
// pointer move event
let scale = super::scale_factor(&window);
match event.pointer_type().as_str() {
"touch" => touch_handler(
modifiers,
device_id,
FingerId::new(pointer_id, event.is_primary()),
&mut event::pointer_move_event(event).map(|event| {
(
event::mouse_position(&event).to_physical(scale),
Force::Normalized(event.pressure() as f64),
)
}),
),
_ => mouse_handler(
modifiers,
device_id,
&mut event::pointer_move_event(event)
.map(|event| event::mouse_position(&event).to_physical(scale)),
),
};
}));
}
cursor_handler(
device_id,
&mut event::pointer_move_event(event).map(|event| {
(
event::mouse_modifiers(&event),
event::mouse_position(&event).to_physical(scale),
match kind {
PointerKind::Mouse => PointerSource::Mouse,
PointerKind::Touch(finger_id) => PointerSource::Touch {
finger_id,
force: Some(Force::Normalized(event.pressure().into())),
},
PointerKind::Unknown => PointerSource::Unknown,
},
)
}),
);
pub fn on_touch_cancel<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static + FnMut(DeviceId, FingerId, PhysicalPosition<f64>, Force),
{
let window = canvas_common.window.clone();
self.on_touch_cancel =
Some(canvas_common.add_event("pointercancel", move |event: PointerEvent| {
if event.pointer_type() == "touch" {
let pointer_id = event.pointer_id();
handler(
DeviceId::new(pointer_id),
FingerId::new(pointer_id, event.is_primary()),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
Force::Normalized(event.pressure() as f64),
);
}
}));
}

View File

@@ -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);
}

View File

@@ -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");

View File

@@ -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,
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();
@@ -53,7 +48,7 @@ impl Window {
target.register(&canvas, id);
let runner = target.runner.clone();
let destroy_fn = Box::new(move || runner.notify_destroy_window(id));
let destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id)));
let inner = Inner {
id,
@@ -65,11 +60,19 @@ impl Window {
let canvas = Rc::downgrade(&inner.canvas);
let (dispatcher, runner) = Dispatcher::new(target.runner.main_thread(), inner);
target.runner.add_canvas(id, canvas, runner);
target.runner.add_canvas(RootWI(id), canvas, runner);
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) -> WindowId {
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 {
@@ -429,6 +428,27 @@ impl Drop for Inner {
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(pub(crate) u32);
impl WindowId {
pub const fn dummy() -> Self {
Self(0)
}
}
impl From<WindowId> for u64 {
fn from(window_id: WindowId) -> Self {
window_id.0 as u64
}
}
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self(raw_id as u32)
}
}
#[derive(Clone, Debug)]
pub struct PlatformSpecificWindowAttributes {
pub(crate) canvas: Option<Arc<MainThreadSafe<backend::RawCanvasType>>>,

View File

@@ -15,7 +15,8 @@ use crate::event::Event;
use crate::platform_impl::platform::definitions::{
IDataObjectVtbl, IDropTarget, IDropTargetVtbl, IUnknownVtbl,
};
use crate::window::WindowId;
use crate::platform_impl::platform::WindowId;
use crate::window::WindowId as RootWindowId;
#[repr(C)]
pub struct FileDropHandlerData {
@@ -85,7 +86,7 @@ impl FileDropHandler {
let hdrop = unsafe {
Self::iterate_filenames(pDataObj, |filename| {
drop_handler.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(drop_handler.window as usize),
window_id: RootWindowId(WindowId(drop_handler.window)),
event: HoveredFile(filename),
});
})
@@ -119,7 +120,7 @@ impl FileDropHandler {
let drop_handler = unsafe { Self::from_interface(this) };
if drop_handler.hovered_is_valid {
drop_handler.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(drop_handler.window as usize),
window_id: RootWindowId(WindowId(drop_handler.window)),
event: HoveredFileCancelled,
});
}
@@ -139,7 +140,7 @@ impl FileDropHandler {
let hdrop = unsafe {
Self::iterate_filenames(pDataObj, |filename| {
drop_handler.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(drop_handler.window as usize),
window_id: RootWindowId(WindowId(drop_handler.window)),
event: DroppedFile(filename),
});
})

View File

@@ -37,9 +37,9 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW,
RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos, TranslateMessage, CREATESTRUCTW,
GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS,
PM_REMOVE, PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM,
WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
PM_REMOVE, PT_PEN, PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE,
SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS,
@@ -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, TouchPhase,
Event, FingerId as RootFingerId, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase,
WindowEvent,
};
use crate::event_loop::{
@@ -80,12 +80,14 @@ use crate::platform_impl::platform::window::InitData;
use crate::platform_impl::platform::window_state::{
CursorFlags, ImeState, WindowFlags, WindowState,
};
use crate::platform_impl::platform::{raw_input, util, wrap_device_id, FingerId, Fullscreen};
use crate::platform_impl::platform::{
raw_input, util, wrap_device_id, FingerId, Fullscreen, WindowId, DEVICE_ID,
};
use crate::platform_impl::Window;
use crate::utils::Lazy;
use crate::window::{
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
WindowAttributes, WindowId,
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as RootWindow,
WindowAttributes, WindowId as RootWindowId,
};
pub(crate) struct WindowData {
@@ -180,7 +182,7 @@ impl EventLoop {
"Initializing the event loop outside of the main thread is a significant \
cross-platform compatibility hazard. If you absolutely need to create an \
EventLoop on a different thread, you can use the \
`EventLoopBuilderExtWindows::with_any_thread` function."
`EventLoopBuilderExtWindows::any_thread` function."
);
}
@@ -516,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)? })
}
@@ -570,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
}
@@ -918,7 +922,7 @@ fn update_modifiers(window: HWND, userdata: &WindowData) {
drop(window_state);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: ModifiersChanged(modifiers.into()),
});
}
@@ -930,7 +934,7 @@ unsafe fn gain_active_focus(window: HWND, userdata: &WindowData) {
update_modifiers(window, userdata);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: Focused(true),
});
}
@@ -940,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: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: ModifiersChanged(ModifiersState::empty().into()),
});
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: Focused(false),
});
}
@@ -1043,9 +1047,9 @@ 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: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: KeyboardInput {
device_id: None,
device_id: DEVICE_ID,
event: event.event,
is_synthetic: event.is_synthetic,
},
@@ -1128,7 +1132,7 @@ unsafe fn public_window_callback_inner(
WM_CLOSE => {
use crate::event::WindowEvent::CloseRequested;
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: CloseRequested,
});
result = ProcResult::Value(0);
@@ -1138,7 +1142,7 @@ unsafe fn public_window_callback_inner(
use crate::event::WindowEvent::Destroyed;
unsafe { RevokeDragDrop(window) };
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: Destroyed,
});
result = ProcResult::Value(0);
@@ -1159,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: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::RedrawRequested,
});
}
@@ -1262,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: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: Moved(physical_position),
});
}
@@ -1272,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: WindowId::from_raw(window as usize),
event: SurfaceResized(physical_size),
window_id: RootWindowId(WindowId(window)),
event: Resized(physical_size),
};
{
@@ -1306,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 {
@@ -1393,7 +1397,7 @@ unsafe fn public_window_callback_inner(
userdata.window_state_lock().ime_state = ImeState::Enabled;
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Enabled),
});
}
@@ -1413,7 +1417,7 @@ unsafe fn public_window_callback_inner(
if lparam == 0 {
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
});
}
@@ -1425,11 +1429,11 @@ unsafe fn public_window_callback_inner(
userdata.window_state_lock().ime_state = ImeState::Enabled;
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
});
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Commit(text)),
});
}
@@ -1444,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: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Preedit(text, cursor_range)),
});
}
@@ -1467,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: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
});
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Commit(text)),
});
}
@@ -1480,7 +1484,7 @@ unsafe fn public_window_callback_inner(
userdata.window_state_lock().ime_state = ImeState::Disabled;
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Disabled),
});
}
@@ -1518,8 +1522,7 @@ unsafe fn public_window_callback_inner(
},
WM_MOUSEMOVE => {
use crate::event::WindowEvent::{PointerEntered, PointerLeft, PointerMoved};
use crate::event::{PointerKind, PointerSource};
use crate::event::WindowEvent::{CursorEntered, CursorLeft, CursorMoved};
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
@@ -1539,12 +1542,8 @@ unsafe fn public_window_callback_inner(
drop(w);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerEntered {
device_id: None,
position,
kind: PointerKind::Mouse,
},
window_id: RootWindowId(WindowId(window)),
event: CursorEntered { device_id: DEVICE_ID },
});
// Calling TrackMouseEvent in order to receive mouse leave events.
@@ -1564,12 +1563,8 @@ unsafe fn public_window_callback_inner(
drop(w);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Mouse,
},
window_id: RootWindowId(WindowId(window)),
event: CursorLeft { device_id: DEVICE_ID },
});
},
PointerMoveKind::None => drop(w),
@@ -1587,8 +1582,8 @@ unsafe fn public_window_callback_inner(
update_modifiers(window, userdata);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerMoved { device_id: None, position, source: PointerSource::Mouse },
window_id: RootWindowId(WindowId(window)),
event: CursorMoved { device_id: DEVICE_ID, position },
});
}
@@ -1596,17 +1591,15 @@ unsafe fn public_window_callback_inner(
},
WM_MOUSELEAVE => {
use crate::event::PointerKind::Mouse;
use crate::event::WindowEvent::PointerLeft;
use crate::event::WindowEvent::CursorLeft;
{
let mut w = userdata.window_state_lock();
w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)).ok();
}
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerLeft { device_id: None, position: None, kind: Mouse },
window_id: RootWindowId(WindowId(window)),
event: CursorLeft { device_id: DEVICE_ID },
});
result = ProcResult::Value(0);
@@ -1621,9 +1614,9 @@ unsafe fn public_window_callback_inner(
update_modifiers(window, userdata);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::MouseWheel {
device_id: None,
device_id: DEVICE_ID,
delta: LineDelta(0.0, value),
phase: TouchPhase::Moved,
},
@@ -1641,9 +1634,9 @@ unsafe fn public_window_callback_inner(
update_modifiers(window, userdata);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::MouseWheel {
device_id: None,
device_id: DEVICE_ID,
delta: LineDelta(value, 0.0),
phase: TouchPhase::Moved,
},
@@ -1669,24 +1662,15 @@ unsafe fn public_window_callback_inner(
WM_LBUTTONDOWN => {
use crate::event::ElementState::Pressed;
use crate::event::MouseButton::Left;
use crate::event::WindowEvent::PointerButton;
use crate::event::WindowEvent::MouseInput;
unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
state: Pressed,
position,
button: Left.into(),
},
window_id: RootWindowId(WindowId(window)),
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left },
});
result = ProcResult::Value(0);
},
@@ -1694,24 +1678,15 @@ unsafe fn public_window_callback_inner(
WM_LBUTTONUP => {
use crate::event::ElementState::Released;
use crate::event::MouseButton::Left;
use crate::event::WindowEvent::PointerButton;
use crate::event::WindowEvent::MouseInput;
unsafe { release_mouse(userdata.window_state_lock()) };
update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
state: Released,
position,
button: Left.into(),
},
window_id: RootWindowId(WindowId(window)),
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left },
});
result = ProcResult::Value(0);
},
@@ -1719,24 +1694,15 @@ unsafe fn public_window_callback_inner(
WM_RBUTTONDOWN => {
use crate::event::ElementState::Pressed;
use crate::event::MouseButton::Right;
use crate::event::WindowEvent::PointerButton;
use crate::event::WindowEvent::MouseInput;
unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
state: Pressed,
position,
button: Right.into(),
},
window_id: RootWindowId(WindowId(window)),
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right },
});
result = ProcResult::Value(0);
},
@@ -1744,24 +1710,15 @@ unsafe fn public_window_callback_inner(
WM_RBUTTONUP => {
use crate::event::ElementState::Released;
use crate::event::MouseButton::Right;
use crate::event::WindowEvent::PointerButton;
use crate::event::WindowEvent::MouseInput;
unsafe { release_mouse(userdata.window_state_lock()) };
update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
state: Released,
position,
button: Right.into(),
},
window_id: RootWindowId(WindowId(window)),
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right },
});
result = ProcResult::Value(0);
},
@@ -1769,24 +1726,15 @@ unsafe fn public_window_callback_inner(
WM_MBUTTONDOWN => {
use crate::event::ElementState::Pressed;
use crate::event::MouseButton::Middle;
use crate::event::WindowEvent::PointerButton;
use crate::event::WindowEvent::MouseInput;
unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
state: Pressed,
position,
button: Middle.into(),
},
window_id: RootWindowId(WindowId(window)),
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle },
});
result = ProcResult::Value(0);
},
@@ -1794,24 +1742,15 @@ unsafe fn public_window_callback_inner(
WM_MBUTTONUP => {
use crate::event::ElementState::Released;
use crate::event::MouseButton::Middle;
use crate::event::WindowEvent::PointerButton;
use crate::event::WindowEvent::MouseInput;
unsafe { release_mouse(userdata.window_state_lock()) };
update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
state: Released,
position,
button: Middle.into(),
},
window_id: RootWindowId(WindowId(window)),
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle },
});
result = ProcResult::Value(0);
},
@@ -1819,29 +1758,23 @@ unsafe fn public_window_callback_inner(
WM_XBUTTONDOWN => {
use crate::event::ElementState::Pressed;
use crate::event::MouseButton::{Back, Forward, Other};
use crate::event::WindowEvent::PointerButton;
use crate::event::WindowEvent::MouseInput;
let xbutton = super::get_xbutton_wparam(wparam as u32);
unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
window_id: RootWindowId(WindowId(window)),
event: MouseInput {
device_id: DEVICE_ID,
state: Pressed,
position,
button: match xbutton {
1 => Back,
2 => Forward,
_ => Other(xbutton),
}
.into(),
},
},
});
result = ProcResult::Value(0);
@@ -1850,29 +1783,23 @@ unsafe fn public_window_callback_inner(
WM_XBUTTONUP => {
use crate::event::ElementState::Released;
use crate::event::MouseButton::{Back, Forward, Other};
use crate::event::WindowEvent::PointerButton;
use crate::event::WindowEvent::MouseInput;
let xbutton = super::get_xbutton_wparam(wparam as u32);
unsafe { release_mouse(userdata.window_state_lock()) };
update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
window_id: RootWindowId(WindowId(window)),
event: MouseInput {
device_id: DEVICE_ID,
state: Released,
position,
button: match xbutton {
1 => Back,
2 => Forward,
_ => Other(xbutton),
}
.into(),
},
},
});
result = ProcResult::Value(0);
@@ -1890,10 +1817,6 @@ unsafe fn public_window_callback_inner(
},
WM_TOUCH => {
use crate::event::ButtonSource::Touch;
use crate::event::ElementState::{Pressed, Released};
use crate::event::{PointerKind, PointerSource};
let pcount = super::loword(wparam as u32) as usize;
let mut inputs = Vec::with_capacity(pcount);
let htouch = lparam;
@@ -1907,70 +1830,36 @@ unsafe fn public_window_callback_inner(
} {
unsafe { inputs.set_len(pcount) };
for input in &inputs {
let mut position = POINT { x: input.x / 100, y: input.y / 100 };
let mut location = POINT { x: input.x / 100, y: input.y / 100 };
if unsafe { ScreenToClient(window, &mut position) } == false.into() {
if unsafe { ScreenToClient(window, &mut location) } == false.into() {
continue;
}
let x = position.x as f64 + (input.x % 100) as f64 / 100f64;
let y = position.y as f64 + (input.y % 100) as f64 / 100f64;
let position = PhysicalPosition::new(x, y);
let window_id = WindowId::from_raw(window as usize);
let finger_id = RootFingerId(FingerId {
id: input.dwID,
primary: util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY),
let x = location.x as f64 + (input.x % 100) as f64 / 100f64;
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: RootWindowId(WindowId(window)),
event: WindowEvent::Touch(Touch {
phase: if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) {
TouchPhase::Started
} else if util::has_flag(input.dwFlags, TOUCHEVENTF_UP) {
TouchPhase::Ended
} else if util::has_flag(input.dwFlags, TOUCHEVENTF_MOVE) {
TouchPhase::Moved
} else {
continue;
},
location,
force: None, // WM_TOUCH doesn't support pressure information
finger_id: RootFingerId(FingerId {
id: input.dwID,
primary: util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY),
}),
device_id: DEVICE_ID,
}),
});
if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id: None,
position,
kind: PointerKind::Touch(finger_id),
},
});
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: Pressed,
position,
button: Touch { finger_id, force: None },
},
});
} else if util::has_flag(input.dwFlags, TOUCHEVENTF_UP) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: Released,
position,
button: Touch { finger_id, force: None },
},
});
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
});
} else if util::has_flag(input.dwFlags, TOUCHEVENTF_MOVE) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
position,
source: PointerSource::Touch { finger_id, force: None },
},
});
} else {
continue;
}
}
}
unsafe { CloseTouchInputHandle(htouch) };
@@ -1978,9 +1867,6 @@ unsafe fn public_window_callback_inner(
},
WM_POINTERDOWN | WM_POINTERUPDATE | WM_POINTERUP => {
use crate::event::ElementState::{Pressed, Released};
use crate::event::{ButtonSource, PointerKind, PointerSource};
if let (
Some(GetPointerFrameInfoHistory),
Some(SkipPointerFrameMessages),
@@ -2065,100 +1951,67 @@ unsafe fn public_window_callback_inner(
continue;
}
let force = if let PT_TOUCH = pointer_info.pointerType {
let mut touch_info = mem::MaybeUninit::uninit();
util::GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
match unsafe {
GetPointerTouchInfo(pointer_info.pointerId, touch_info.as_mut_ptr())
} {
0 => None,
_ => normalize_pointer_pressure(unsafe {
touch_info.assume_init().pressure
}),
}
})
} else {
None
let force = match pointer_info.pointerType {
PT_TOUCH => {
let mut touch_info = mem::MaybeUninit::uninit();
util::GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
match unsafe {
GetPointerTouchInfo(
pointer_info.pointerId,
touch_info.as_mut_ptr(),
)
} {
0 => None,
_ => normalize_pointer_pressure(unsafe {
touch_info.assume_init().pressure
}),
}
})
},
PT_PEN => {
let mut pen_info = mem::MaybeUninit::uninit();
util::GET_POINTER_PEN_INFO.and_then(|GetPointerPenInfo| {
match unsafe {
GetPointerPenInfo(pointer_info.pointerId, pen_info.as_mut_ptr())
} {
0 => None,
_ => normalize_pointer_pressure(unsafe {
pen_info.assume_init().pressure
}),
}
})
},
_ => None,
};
let x = location.x as f64 + x.fract();
let y = location.y as f64 + y.fract();
let position = PhysicalPosition::new(x, y);
let window_id = WindowId::from_raw(window as usize);
let finger_id = RootFingerId(FingerId {
id: pointer_info.pointerId,
primary: util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY),
let location = PhysicalPosition::new(x, y);
userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Touch(Touch {
phase: if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) {
TouchPhase::Started
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UP) {
TouchPhase::Ended
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UPDATE)
{
TouchPhase::Moved
} else {
continue;
},
location,
force,
finger_id: RootFingerId(FingerId {
id: pointer_info.pointerId,
primary: util::has_flag(
pointer_info.pointerFlags,
POINTER_FLAG_PRIMARY,
),
}),
device_id: DEVICE_ID,
}),
});
if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id: None,
position,
kind: if let PT_TOUCH = pointer_info.pointerType {
PointerKind::Touch(finger_id)
} else {
PointerKind::Unknown
},
},
});
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: Pressed,
position,
button: if let PT_TOUCH = pointer_info.pointerType {
ButtonSource::Touch { finger_id, force }
} else {
ButtonSource::Unknown(0)
},
},
});
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UP) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: Released,
position,
button: if let PT_TOUCH = pointer_info.pointerType {
ButtonSource::Touch { finger_id, force }
} else {
ButtonSource::Unknown(0)
},
},
});
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: if let PT_TOUCH = pointer_info.pointerType {
PointerKind::Touch(finger_id)
} else {
PointerKind::Unknown
},
},
});
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UPDATE) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
position,
source: if let PT_TOUCH = pointer_info.pointerType {
PointerSource::Touch { finger_id, force }
} else {
PointerSource::Unknown
},
},
});
} else {
continue;
}
}
unsafe { SkipPointerFrameMessages(pointer_id) };
@@ -2280,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.
@@ -2300,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: WindowId::from_raw(window as usize),
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;
@@ -2335,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)
});
@@ -2350,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
@@ -2470,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: WindowId::from_raw(window as usize),
window_id: RootWindowId(WindowId(window)),
event: ThemeChanged(new_theme),
});
}
@@ -2580,11 +2433,11 @@ unsafe extern "system" fn thread_event_target_callback(
}
unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) {
use crate::event::DeviceEvent::{Button, Key, MouseWheel, PointerMotion};
use crate::event::DeviceEvent::{Button, Key, MouseMotion, MouseWheel};
use crate::event::ElementState::{Pressed, Released};
use crate::event::MouseScrollDelta::LineDelta;
let device_id = Some(wrap_device_id(data.header.hDevice as _));
let device_id = wrap_device_id(data.header.hDevice as _);
if data.header.dwType == RIM_TYPEMOUSE {
let mouse = unsafe { data.data.mouse };
@@ -2596,7 +2449,7 @@ unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) {
if x != 0.0 || y != 0.0 {
userdata.send_event(Event::DeviceEvent {
device_id,
event: PointerMotion { delta: (x, y) },
event: MouseMotion { delta: (x, y) },
});
}
}

View File

@@ -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;
@@ -53,7 +53,7 @@ pub(crate) enum RunnerState {
enum BufferedEvent {
Event(Event),
ScaleFactorChanged(HWND, f64, PhysicalSize<u32>),
ScaleFactorChanged(WindowId, f64, PhysicalSize<u32>),
}
impl EventLoopRunner {
@@ -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.into_raw() as HWND,
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,28 +371,29 @@ impl BufferedEvent {
pub fn dispatch_event(self, dispatch: impl FnOnce(Event)) {
match self {
Self::Event(event) => dispatch(event),
Self::ScaleFactorChanged(window, 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: WindowId::from_raw(window as usize),
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, GWL_USERDATA) as *mut WindowData;
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, surface_size);
window_flags.set_size((window_id.0).0, inner_size);
}
},
}

View File

@@ -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 })))

View File

@@ -1,5 +1,5 @@
use smol_str::SmolStr;
use windows_sys::Win32::Foundation::HWND;
use windows_sys::Win32::Foundation::{HANDLE, HWND};
use windows_sys::Win32::UI::WindowsAndMessaging::{HMENU, WINDOW_LONG_PTR_INDEX};
pub(crate) use self::event_loop::{
@@ -11,7 +11,7 @@ pub(crate) use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::Window;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
use crate::event::DeviceId;
use crate::event::DeviceId as RootDeviceId;
use crate::icon::Icon;
use crate::keyboard::Key;
use crate::platform::windows::{BackdropType, Color, CornerPreference};
@@ -59,6 +59,25 @@ impl Default for PlatformSpecificWindowAttributes {
unsafe impl Send for PlatformSpecificWindowAttributes {}
unsafe impl Sync for PlatformSpecificWindowAttributes {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(u32);
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId(0)
}
}
impl DeviceId {
pub fn persistent_identifier(&self) -> Option<String> {
if self.0 != 0 {
raw_input::get_raw_input_device_name(self.0 as HANDLE)
} else {
None
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId {
id: u32,
@@ -66,7 +85,6 @@ pub struct FingerId {
}
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId { id: 0, primary: false }
}
@@ -78,16 +96,50 @@ impl FingerId {
}
}
fn wrap_device_id(id: u32) -> DeviceId {
DeviceId::from_raw(id as i64)
// Constant device ID, to be removed when this backend is updated to report real device IDs.
const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId(0));
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>,
pub key_without_modifiers: Key,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(HWND);
unsafe impl Send for WindowId {}
unsafe impl Sync for WindowId {}
impl WindowId {
pub const fn dummy() -> Self {
WindowId(0)
}
}
impl From<WindowId> for u64 {
fn from(window_id: WindowId) -> Self {
window_id.0 as u64
}
}
impl From<WindowId> for HWND {
fn from(window_id: WindowId) -> Self {
window_id.0
}
}
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self(raw_id as HWND)
}
}
#[inline(always)]
const fn get_xbutton_wparam(x: u32) -> u16 {
hiword(x)
@@ -153,6 +205,6 @@ mod ime;
mod keyboard;
mod keyboard_layout;
mod monitor;
pub(crate) mod raw_input;
mod raw_input;
mod window;
mod window_state;

View File

@@ -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;

View File

@@ -14,7 +14,7 @@ use windows_sys::Win32::UI::HiDpi::{
DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS,
};
use windows_sys::Win32::UI::Input::KeyboardAndMouse::GetActiveWindow;
use windows_sys::Win32::UI::Input::Pointer::{POINTER_INFO, POINTER_TOUCH_INFO};
use windows_sys::Win32::UI::Input::Pointer::{POINTER_INFO, POINTER_PEN_INFO, POINTER_TOUCH_INFO};
use windows_sys::Win32::UI::WindowsAndMessaging::{
ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowPlacement, GetWindowRect,
IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM,
@@ -244,6 +244,9 @@ pub type GetPointerDeviceRects = unsafe extern "system" fn(
pub type GetPointerTouchInfo =
unsafe extern "system" fn(pointerId: u32, touchInfo: *mut POINTER_TOUCH_INFO) -> BOOL;
pub type GetPointerPenInfo =
unsafe extern "system" fn(pointId: u32, penInfo: *mut POINTER_PEN_INFO) -> BOOL;
pub(crate) static GET_DPI_FOR_WINDOW: Lazy<Option<GetDpiForWindow>> =
Lazy::new(|| get_function!("user32.dll", GetDpiForWindow));
pub(crate) static ADJUST_WINDOW_RECT_EX_FOR_DPI: Lazy<Option<AdjustWindowRectExForDpi>> =
@@ -266,3 +269,5 @@ pub(crate) static GET_POINTER_DEVICE_RECTS: Lazy<Option<GetPointerDeviceRects>>
Lazy::new(|| get_function!("user32.dll", GetPointerDeviceRects));
pub(crate) static GET_POINTER_TOUCH_INFO: Lazy<Option<GetPointerTouchInfo>> =
Lazy::new(|| get_function!("user32.dll", GetPointerTouchInfo));
pub(crate) static GET_POINTER_PEN_INFO: Lazy<Option<GetPointerPenInfo>> =
Lazy::new(|| get_function!("user32.dll", GetPointerPenInfo));

File diff suppressed because it is too large Load Diff

View File

@@ -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,

View File

@@ -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
}
}

Some files were not shown because too many files have changed in this diff Show More