mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
85 Commits
madsmtm/ob
...
v0.30.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cbce055d3 | ||
|
|
727583ffbf | ||
|
|
82f0949aea | ||
|
|
5d2151e893 | ||
|
|
6f22822025 | ||
|
|
cf829a44e7 | ||
|
|
f2fb7dee3d | ||
|
|
836e717021 | ||
|
|
1e44c354fe | ||
|
|
1fb118031c | ||
|
|
d6ab3dcb86 | ||
|
|
d828523b39 | ||
|
|
fccfd359db | ||
|
|
5c1cc652c4 | ||
|
|
1d18a6ca57 | ||
|
|
bab2d82fd3 | ||
|
|
bbab3d8e35 | ||
|
|
b02a70e6bb | ||
|
|
9ceb8d4144 | ||
|
|
485ae90aae | ||
|
|
107bf91a2d | ||
|
|
1bc405e526 | ||
|
|
685090021b | ||
|
|
cfe7eb8954 | ||
|
|
838b2459a7 | ||
|
|
f4a5ad9c22 | ||
|
|
00ecd1533e | ||
|
|
114512bb32 | ||
|
|
8c07319ced | ||
|
|
fa3ec006e2 | ||
|
|
f3d1f922f9 | ||
|
|
77e8fe6094 | ||
|
|
5fce4a09de | ||
|
|
5735786f42 | ||
|
|
9ca8c09208 | ||
|
|
4b1aa51094 | ||
|
|
1dec9b4b33 | ||
|
|
04d8a284a0 | ||
|
|
949cb0f203 | ||
|
|
bf68ac0b14 | ||
|
|
ba188376d1 | ||
|
|
6509f8a18b | ||
|
|
71dea4637d | ||
|
|
ec24c3efd3 | ||
|
|
feca480b4c | ||
|
|
655fdc896f | ||
|
|
faa641e57f | ||
|
|
7c81364e1c | ||
|
|
6abfef1220 | ||
|
|
d2d4d20108 | ||
|
|
d8f4d8f1b7 | ||
|
|
a974640a66 | ||
|
|
3d7d766182 | ||
|
|
c73d8cff20 | ||
|
|
79aa95b212 | ||
|
|
ecd14688dc | ||
|
|
b512ed1e63 | ||
|
|
96388f4f6b | ||
|
|
1745b01502 | ||
|
|
54e974c090 | ||
|
|
b14d5c0c99 | ||
|
|
437747b966 | ||
|
|
21e266f3b7 | ||
|
|
3a0928af45 | ||
|
|
ad92b4f89d | ||
|
|
bf4445bb62 | ||
|
|
391a22217d | ||
|
|
fb4a674ee5 | ||
|
|
43f296b2b3 | ||
|
|
042667c5eb | ||
|
|
3206d105fe | ||
|
|
1afec3ca0d | ||
|
|
c4a8e9321d | ||
|
|
dee7a405fc | ||
|
|
a298b4d00e | ||
|
|
aebd5edc9e | ||
|
|
c801b69d3e | ||
|
|
ebd6454f8f | ||
|
|
4f2f0bc08f | ||
|
|
4b3c0655bf | ||
|
|
0812adc983 | ||
|
|
cd6ec19300 | ||
|
|
61bd8172bd | ||
|
|
c04c113e7e | ||
|
|
ce32a3024e |
9
.github/CODEOWNERS
vendored
9
.github/CODEOWNERS
vendored
@@ -2,10 +2,9 @@
|
||||
/src/platform/android.rs @MarijnS95
|
||||
/src/platform_impl/android @MarijnS95
|
||||
|
||||
# Apple (AppKit + UIKit)
|
||||
# iOS
|
||||
/src/platform/ios.rs @madsmtm
|
||||
/src/platform/macos.rs @madsmtm
|
||||
/src/platform_impl/apple @madsmtm
|
||||
/src/platform_impl/ios @madsmtm
|
||||
|
||||
# Unix
|
||||
/src/platform_impl/linux/mod.rs @kchibisov
|
||||
@@ -18,6 +17,10 @@
|
||||
/src/platform/x11.rs @kchibisov @notgull
|
||||
/src/platform_impl/linux/x11 @kchibisov @notgull
|
||||
|
||||
# macOS
|
||||
/src/platform/macos.rs @madsmtm
|
||||
/src/platform_impl/macos @madsmtm
|
||||
|
||||
# Web
|
||||
/src/platform/web.rs @daxpedda
|
||||
/src/platform_impl/web @daxpedda
|
||||
|
||||
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain: [stable, nightly, '1.73']
|
||||
toolchain: [stable, nightly, '1.70.0']
|
||||
platform:
|
||||
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
|
||||
- { name: 'Windows 64bit MSVC', target: x86_64-pc-windows-msvc, os: windows-latest, }
|
||||
@@ -61,10 +61,10 @@ jobs:
|
||||
- { name: 'web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
|
||||
exclude:
|
||||
# Android is tested on stable-3
|
||||
- toolchain: '1.73'
|
||||
- toolchain: '1.70.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
include:
|
||||
- toolchain: '1.73'
|
||||
- toolchain: '1.70.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
- toolchain: 'nightly'
|
||||
platform: {
|
||||
@@ -149,13 +149,13 @@ jobs:
|
||||
- name: Test dpi crate
|
||||
if: >
|
||||
contains(matrix.platform.name, 'Linux 64bit') &&
|
||||
matrix.toolchain != '1.73'
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo test -p dpi
|
||||
|
||||
- name: Build tests
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.73'
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS
|
||||
|
||||
- name: Run tests
|
||||
@@ -164,7 +164,7 @@ jobs:
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.73'
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test $OPTIONS
|
||||
|
||||
- name: Lint with clippy
|
||||
@@ -174,7 +174,7 @@ jobs:
|
||||
- name: Build tests with serde enabled
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.73'
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS --features serde
|
||||
|
||||
- name: Run tests with serde enabled
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.73'
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test $OPTIONS --features serde
|
||||
|
||||
- name: Check docs.rs documentation
|
||||
@@ -221,7 +221,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: taiki-e/checkout-action@v1
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
with:
|
||||
command: check
|
||||
log-level: error
|
||||
|
||||
180
Cargo.toml
180
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.30.3"
|
||||
version = "0.30.7"
|
||||
authors = [
|
||||
"The winit contributors",
|
||||
"Pierre Krieger <pierre.krieger1708@gmail.com>",
|
||||
@@ -14,7 +14,17 @@ rust-version.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
exclude = ["/.cargo"]
|
||||
include = [
|
||||
"/build.rs",
|
||||
"/docs",
|
||||
"/examples",
|
||||
"/FEATURES.md",
|
||||
"/LICENSE",
|
||||
"/src",
|
||||
"!/src/platform_impl/web/script",
|
||||
"/src/platform_impl/web/script/**/*.min.js",
|
||||
"/tests",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [
|
||||
@@ -102,21 +112,21 @@ softbuffer = { version = "0.4.0", default-features = false, features = [
|
||||
"wayland-dlopen",
|
||||
] }
|
||||
|
||||
# Android
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android-activity = "0.6.0"
|
||||
ndk = { version = "0.9.0", default-features = false }
|
||||
|
||||
# AppKit or UIKit
|
||||
[target.'cfg(target_vendor = "apple")'.dependencies]
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = "0.5.2"
|
||||
|
||||
# AppKit
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-graphics = "0.23.1"
|
||||
block2 = "0.5.1"
|
||||
objc2-foundation = { version = "0.2.2", features = [
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
|
||||
version = "0.2.2"
|
||||
features = [
|
||||
"block2",
|
||||
"dispatch",
|
||||
"NSArray",
|
||||
@@ -134,8 +144,11 @@ objc2-foundation = { version = "0.2.2", features = [
|
||||
"NSString",
|
||||
"NSThread",
|
||||
"NSValue",
|
||||
] }
|
||||
objc2-app-kit = { version = "0.2.2", features = [
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.objc2-app-kit]
|
||||
version = "0.2.2"
|
||||
features = [
|
||||
"NSAppearance",
|
||||
"NSApplication",
|
||||
"NSBitmapImageRep",
|
||||
@@ -162,11 +175,11 @@ objc2-app-kit = { version = "0.2.2", features = [
|
||||
"NSWindow",
|
||||
"NSWindowScripting",
|
||||
"NSWindowTabGroup",
|
||||
] }
|
||||
]
|
||||
|
||||
# UIKit
|
||||
[target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dependencies]
|
||||
objc2-foundation = { version = "0.2.2", features = [
|
||||
[target.'cfg(target_os = "ios")'.dependencies.objc2-foundation]
|
||||
version = "0.2.2"
|
||||
features = [
|
||||
"dispatch",
|
||||
"NSArray",
|
||||
"NSEnumerator",
|
||||
@@ -176,13 +189,18 @@ objc2-foundation = { version = "0.2.2", features = [
|
||||
"NSProcessInfo",
|
||||
"NSThread",
|
||||
"NSSet",
|
||||
] }
|
||||
objc2-ui-kit = { version = "0.2.2", features = [
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies.objc2-ui-kit]
|
||||
version = "0.2.2"
|
||||
features = [
|
||||
"UIApplication",
|
||||
"UIDevice",
|
||||
"UIEvent",
|
||||
"UIGeometry",
|
||||
"UIGestureRecognizer",
|
||||
"UITextInput",
|
||||
"UITextInputTraits",
|
||||
"UIOrientation",
|
||||
"UIPanGestureRecognizer",
|
||||
"UIPinchGestureRecognizer",
|
||||
@@ -196,12 +214,14 @@ objc2-ui-kit = { version = "0.2.2", features = [
|
||||
"UIView",
|
||||
"UIViewController",
|
||||
"UIWindow",
|
||||
] }
|
||||
]
|
||||
|
||||
# Windows
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
unicode-segmentation = "1.7.1"
|
||||
windows-sys = { version = "0.52.0", features = [
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
|
||||
version = "0.52.0"
|
||||
features = [
|
||||
"Win32_Devices_HumanInterfaceDevice",
|
||||
"Win32_Foundation",
|
||||
"Win32_Globalization",
|
||||
@@ -212,6 +232,7 @@ windows-sys = { version = "0.52.0", features = [
|
||||
"Win32_System_Com",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Ole",
|
||||
"Win32_Security",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_System_Threading",
|
||||
@@ -226,13 +247,12 @@ windows-sys = { version = "0.52.0", features = [
|
||||
"Win32_UI_Shell",
|
||||
"Win32_UI_TextServices",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
] }
|
||||
]
|
||||
|
||||
# Linux
|
||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
|
||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
|
||||
ahash = { version = "0.8.7", features = ["no-rng"], optional = true }
|
||||
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
||||
calloop = "0.12.3"
|
||||
calloop = "0.13.0"
|
||||
libc = "0.2.64"
|
||||
memmap2 = { version = "0.9.0", optional = true }
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
@@ -242,18 +262,18 @@ rustix = { version = "0.38.4", default-features = false, features = [
|
||||
"thread",
|
||||
"process",
|
||||
] }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = [
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.19.2", default-features = false, features = [
|
||||
"calloop",
|
||||
], optional = true }
|
||||
sctk-adwaita = { version = "0.9.0", default-features = false, optional = true }
|
||||
wayland-backend = { version = "0.3.0", default-features = false, features = [
|
||||
sctk-adwaita = { version = "0.10.1", default-features = false, optional = true }
|
||||
wayland-backend = { version = "0.3.5", default-features = false, features = [
|
||||
"client_system",
|
||||
], optional = true }
|
||||
wayland-client = { version = "0.31.1", optional = true }
|
||||
wayland-protocols = { version = "0.31.0", features = [
|
||||
wayland-client = { version = "0.31.4", optional = true }
|
||||
wayland-protocols = { version = "0.32.2", features = [
|
||||
"staging",
|
||||
], optional = true }
|
||||
wayland-protocols-plasma = { version = "0.2.0", features = [
|
||||
wayland-protocols-plasma = { version = "0.3.2", features = [
|
||||
"client",
|
||||
], optional = true }
|
||||
x11-dl = { version = "2.19.1", optional = true }
|
||||
@@ -267,61 +287,67 @@ x11rb = { version = "0.13.0", default-features = false, features = [
|
||||
], optional = true }
|
||||
xkbcommon-dl = "0.4.2"
|
||||
|
||||
# Orbital
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
orbclient = { version = "0.3.47", default-features = false }
|
||||
redox_syscall = "0.4.1"
|
||||
|
||||
# Web
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
web_sys = { package = "web-sys", version = "0.3.64", features = [
|
||||
'AbortController',
|
||||
'AbortSignal',
|
||||
'Blob',
|
||||
'BlobPropertyBag',
|
||||
'console',
|
||||
'CssStyleDeclaration',
|
||||
'Document',
|
||||
'DomException',
|
||||
'DomRect',
|
||||
'DomRectReadOnly',
|
||||
'Element',
|
||||
'Event',
|
||||
'EventTarget',
|
||||
'FocusEvent',
|
||||
'HtmlCanvasElement',
|
||||
'HtmlElement',
|
||||
'HtmlImageElement',
|
||||
'ImageBitmap',
|
||||
'ImageBitmapOptions',
|
||||
'ImageBitmapRenderingContext',
|
||||
'ImageData',
|
||||
'IntersectionObserver',
|
||||
'IntersectionObserverEntry',
|
||||
'KeyboardEvent',
|
||||
'MediaQueryList',
|
||||
'MessageChannel',
|
||||
'MessagePort',
|
||||
'Node',
|
||||
'PageTransitionEvent',
|
||||
'PointerEvent',
|
||||
'PremultiplyAlpha',
|
||||
'ResizeObserver',
|
||||
'ResizeObserverBoxOptions',
|
||||
'ResizeObserverEntry',
|
||||
'ResizeObserverOptions',
|
||||
'ResizeObserverSize',
|
||||
'VisibilityState',
|
||||
'Window',
|
||||
'WheelEvent',
|
||||
'Worker',
|
||||
'Url',
|
||||
] }
|
||||
js-sys = "0.3.64"
|
||||
js-sys = "0.3.70"
|
||||
pin-project = "1"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-futures = "0.4.43"
|
||||
web-time = "1"
|
||||
web_sys = { package = "web-sys", version = "0.3.70", features = [
|
||||
"AbortController",
|
||||
"AbortSignal",
|
||||
"Blob",
|
||||
"BlobPropertyBag",
|
||||
"console",
|
||||
"CssStyleDeclaration",
|
||||
"Document",
|
||||
"DomException",
|
||||
"DomRect",
|
||||
"DomRectReadOnly",
|
||||
"Element",
|
||||
"Event",
|
||||
"EventTarget",
|
||||
"FocusEvent",
|
||||
"HtmlCanvasElement",
|
||||
"HtmlElement",
|
||||
"HtmlImageElement",
|
||||
"ImageBitmap",
|
||||
"ImageBitmapOptions",
|
||||
"ImageBitmapRenderingContext",
|
||||
"ImageData",
|
||||
"IntersectionObserver",
|
||||
"IntersectionObserverEntry",
|
||||
"KeyboardEvent",
|
||||
"MediaQueryList",
|
||||
"MessageChannel",
|
||||
"MessagePort",
|
||||
"Navigator",
|
||||
"Node",
|
||||
"OrientationLockType",
|
||||
"OrientationType",
|
||||
"PageTransitionEvent",
|
||||
"Permissions",
|
||||
"PermissionState",
|
||||
"PermissionStatus",
|
||||
"PointerEvent",
|
||||
"PremultiplyAlpha",
|
||||
"ResizeObserver",
|
||||
"ResizeObserverBoxOptions",
|
||||
"ResizeObserverEntry",
|
||||
"ResizeObserverOptions",
|
||||
"ResizeObserverSize",
|
||||
"Screen",
|
||||
"ScreenOrientation",
|
||||
"Url",
|
||||
"VisibilityState",
|
||||
"WheelEvent",
|
||||
"Window",
|
||||
"Worker",
|
||||
] }
|
||||
|
||||
[target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies]
|
||||
atomic-waker = "1"
|
||||
@@ -340,7 +366,7 @@ resolver = "2"
|
||||
members = ["dpi"]
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.73"
|
||||
rust-version = "1.70.0"
|
||||
repository = "https://github.com/rust-windowing/winit"
|
||||
license = "Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
@@ -154,6 +154,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* Home indicator visibility
|
||||
* Status bar visibility and style
|
||||
* Deferring system gestures
|
||||
* Getting the device idiom
|
||||
* Getting the preferred video mode
|
||||
|
||||
### Web
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.30.3"
|
||||
winit = "0.30.7"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -19,7 +19,7 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
|
||||
|
||||
## Contact Us
|
||||
|
||||
Join us in our [](https://matrix.to/#/#rust-windowing:matrix.org) room. If you don't get an answer there, try [](https://web.libera.chat/#winit).
|
||||
Join us in our [](https://matrix.to/#/#rust-windowing:matrix.org) room.
|
||||
|
||||
The maintainers have a meeting every friday at UTC 15. The meeting notes can be found [here](https://hackmd.io/@winit-meetings).
|
||||
|
||||
@@ -35,7 +35,7 @@ another library.
|
||||
|
||||
## MSRV Policy
|
||||
|
||||
This crate's Minimum Supported Rust Version (MSRV) is **1.73**. Changes to
|
||||
This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to
|
||||
the MSRV will be accompanied by a minor version bump.
|
||||
|
||||
As a **tentative** policy, the upper bound of the MSRV is given by the following
|
||||
|
||||
5
build.rs
5
build.rs
@@ -10,9 +10,10 @@ fn main() {
|
||||
android_platform: { target_os = "android" },
|
||||
web_platform: { all(target_family = "wasm", target_os = "unknown") },
|
||||
macos_platform: { target_os = "macos" },
|
||||
ios_platform: { all(target_vendor = "apple", not(target_os = "macos")) },
|
||||
ios_platform: { target_os = "ios" },
|
||||
windows_platform: { target_os = "windows" },
|
||||
free_unix: { all(unix, not(target_vendor = "apple"), not(android_platform), not(target_os = "emscripten")) },
|
||||
apple: { any(target_os = "ios", target_os = "macos") },
|
||||
free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) },
|
||||
redox: { target_os = "redox" },
|
||||
|
||||
// Native displays.
|
||||
|
||||
@@ -12,6 +12,4 @@ disallowed-methods = [
|
||||
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "objc2_app_kit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
|
||||
{ path = "objc2_app_kit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
|
||||
{ path = "core_foundation::runloop::kCFRunLoopCommonModes", reason = "Using the common modes includes the modal panel mode, which we usually want to avoid" },
|
||||
{ path = "objc2_foundation::NSRunLoopCommonModes", reason = "Using the common modes includes the modal panel mode, which we usually want to avoid" }
|
||||
]
|
||||
|
||||
85
deny.toml
85
deny.toml
@@ -1,15 +1,20 @@
|
||||
# https://embarkstudios.github.io/cargo-deny/
|
||||
# https://embarkstudios.github.io/cargo-deny
|
||||
# cargo install cargo-deny
|
||||
# cargo update && cargo deny --all-features --log-level error --target aarch64-apple-ios check
|
||||
# cargo update && cargo deny --target aarch64-apple-ios check
|
||||
# Note: running just `cargo deny check` without a `--target` will result in
|
||||
# false positives due to https://github.com/EmbarkStudios/cargo-deny/issues/324
|
||||
[graph]
|
||||
all-features = true
|
||||
exclude-dev = true
|
||||
targets = [
|
||||
{ triple = "aarch64-apple-ios" },
|
||||
{ triple = "aarch64-linux-android" },
|
||||
{ triple = "i686-pc-windows-gnu" },
|
||||
{ triple = "i686-pc-windows-msvc" },
|
||||
{ triple = "i686-unknown-linux-gnu" },
|
||||
{ triple = "wasm32-unknown-unknown" },
|
||||
{ triple = "wasm32-unknown-unknown", features = [
|
||||
"atomics",
|
||||
] },
|
||||
{ triple = "x86_64-apple-darwin" },
|
||||
{ triple = "x86_64-apple-ios" },
|
||||
{ triple = "x86_64-pc-windows-gnu" },
|
||||
@@ -18,46 +23,52 @@ targets = [
|
||||
{ triple = "x86_64-unknown-redox" },
|
||||
]
|
||||
|
||||
|
||||
[advisories]
|
||||
vulnerability = "deny"
|
||||
unmaintained = "warn"
|
||||
yanked = "deny"
|
||||
ignore = []
|
||||
|
||||
[licenses]
|
||||
allow = [
|
||||
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
|
||||
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
|
||||
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
|
||||
"ISC", # https://tldrlegal.com/license/isc-license
|
||||
"MIT", # https://tldrlegal.com/license/mit-license
|
||||
"Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html
|
||||
]
|
||||
confidence-threshold = 1.0
|
||||
private = { ignore = true }
|
||||
|
||||
[bans]
|
||||
multiple-versions = "deny"
|
||||
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
|
||||
deny = []
|
||||
skip = [
|
||||
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this
|
||||
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
|
||||
{ name = "libloading" }, # x11rb uses a different version until the next update
|
||||
{ crate = "raw-window-handle", reason = "we depend on multiple behind features" },
|
||||
{ crate = "bitflags@1", reason = "the ecosystem is in the process of migrating" },
|
||||
]
|
||||
skip-tree = []
|
||||
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
|
||||
|
||||
[bans.build]
|
||||
include-archives = true
|
||||
interpreted = "deny"
|
||||
|
||||
[licenses]
|
||||
private = { ignore = true }
|
||||
unlicensed = "deny"
|
||||
allow-osi-fsf-free = "neither"
|
||||
confidence-threshold = 0.92 # We want really high confidence when inferring licenses from text
|
||||
copyleft = "deny"
|
||||
[[bans.build.bypass]]
|
||||
allow = [
|
||||
"Apache-2.0 WITH LLVM-exception", # https://spdx.org/licenses/LLVM-exception.html
|
||||
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
|
||||
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
|
||||
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
|
||||
"BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained
|
||||
"CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/
|
||||
"ISC", # https://tldrlegal.com/license/-isc-license
|
||||
"LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 - no official SPDX, see https://github.com/emilk/egui/issues/2321
|
||||
"MIT-0", # https://choosealicense.com/licenses/mit-0/
|
||||
"MIT", # https://tldrlegal.com/license/mit-license
|
||||
"MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11. Used by webpki-roots on Linux.
|
||||
"OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html
|
||||
"OpenSSL", # https://www.openssl.org/source/license.html - used on Linux
|
||||
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
|
||||
"Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib)
|
||||
{ path = "generate-bindings.sh", checksum = "268ec23248218d779e33853cdc60e2985e70214ff004716cd734270de1f6b561" },
|
||||
]
|
||||
crate = "android-activity"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["freetype2/*"]
|
||||
crate = "freetype-sys"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["lib/*.a"]
|
||||
crate = "windows_i686_gnu"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["lib/*.lib"]
|
||||
crate = "windows_i686_msvc"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["lib/*.a"]
|
||||
crate = "windows_x86_64_gnu"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["lib/*.lib"]
|
||||
crate = "windows_x86_64_msvc"
|
||||
|
||||
@@ -3,75 +3,15 @@
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize, Position};
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::raw_window_handle::HasRawWindowHandle;
|
||||
use winit::window::{Window, WindowId};
|
||||
use winit::window::Window;
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Application {
|
||||
parent_window_id: Option<WindowId>,
|
||||
windows: HashMap<WindowId, Window>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for Application {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let attributes = Window::default_attributes()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||
let window = event_loop.create_window(attributes).unwrap();
|
||||
|
||||
println!("Parent window id: {:?})", window.id());
|
||||
self.parent_window_id = Some(window.id());
|
||||
|
||||
self.windows.insert(window.id(), window);
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
self.windows.clear();
|
||||
event_loop.exit();
|
||||
},
|
||||
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
|
||||
// parent window, so we also can see this log when we move
|
||||
// the cursor around (200, 200) in parent window.
|
||||
println!("cursor entered in the window {window_id:?}");
|
||||
},
|
||||
WindowEvent::KeyboardInput {
|
||||
event: KeyEvent { state: ElementState::Pressed, .. },
|
||||
..
|
||||
} => {
|
||||
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap();
|
||||
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);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let mut window_attributes = Window::default_attributes()
|
||||
@@ -85,9 +25,58 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event_loop.create_window(window_attributes).unwrap()
|
||||
}
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let mut app = Application::default();
|
||||
event_loop.run_app(&mut app)
|
||||
let mut windows = HashMap::new();
|
||||
|
||||
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
|
||||
let mut parent_window_id = None;
|
||||
|
||||
event_loop.run(move |event: Event<()>, event_loop| {
|
||||
match event {
|
||||
Event::Resumed => {
|
||||
let attributes = Window::default_attributes()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||
let window = event_loop.create_window(attributes).unwrap();
|
||||
|
||||
parent_window_id = Some(window.id());
|
||||
|
||||
println!("Parent window id: {parent_window_id:?})");
|
||||
windows.insert(window.id(), window);
|
||||
},
|
||||
Event::WindowEvent { window_id, event } => match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
windows.clear();
|
||||
event_loop.exit();
|
||||
},
|
||||
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
|
||||
// parent window, so we also can see this log when we move
|
||||
// the cursor around (200, 200) in parent window.
|
||||
println!("cursor entered in the window {window_id:?}");
|
||||
},
|
||||
WindowEvent::KeyboardInput {
|
||||
event: KeyEvent { state: ElementState::Pressed, .. },
|
||||
..
|
||||
} => {
|
||||
let parent_window = windows.get(&parent_window_id.unwrap()).unwrap();
|
||||
let child_window = spawn_child_window(parent_window, event_loop);
|
||||
let child_id = child_window.id();
|
||||
println!("Child window created with id: {child_id:?}");
|
||||
windows.insert(child_id, child_window);
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rwh_06", not(any(x11_platform, macos_platform, windows_platform))))]
|
||||
|
||||
@@ -31,6 +31,8 @@ use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMac
|
||||
use winit::platform::startup_notify::{
|
||||
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
|
||||
};
|
||||
#[cfg(x11_platform)]
|
||||
use winit::platform::x11::WindowAttributesExtX11;
|
||||
|
||||
#[path = "util/tracing.rs"]
|
||||
mod tracing;
|
||||
@@ -44,7 +46,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
tracing::init();
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
|
||||
let _event_loop_proxy = event_loop.create_proxy();
|
||||
|
||||
// Wire the user event from another thread.
|
||||
@@ -54,7 +56,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
// from a different thread.
|
||||
info!("Starting to send user event every second");
|
||||
loop {
|
||||
_event_loop_proxy.wake_up();
|
||||
let _ = _event_loop_proxy.send_event(UserEvent::WakeUp);
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
}
|
||||
});
|
||||
@@ -64,6 +66,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
event_loop.run_app(&mut state).map_err(Into::into)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum UserEvent {
|
||||
WakeUp,
|
||||
}
|
||||
|
||||
/// Application state and event handling.
|
||||
struct Application {
|
||||
/// Custom cursors assets.
|
||||
@@ -79,7 +87,7 @@ struct Application {
|
||||
}
|
||||
|
||||
impl Application {
|
||||
fn new(event_loop: &EventLoop) -> Self {
|
||||
fn new<T>(event_loop: &EventLoop<T>) -> Self {
|
||||
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
let context = Some(
|
||||
@@ -134,6 +142,28 @@ impl Application {
|
||||
window_attributes = window_attributes.with_activation_token(token);
|
||||
}
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
match std::env::var("X11_VISUAL_ID") {
|
||||
Ok(visual_id_str) => {
|
||||
info!("Using X11 visual id {visual_id_str}");
|
||||
let visual_id = visual_id_str.parse()?;
|
||||
window_attributes = window_attributes.with_x11_visual(visual_id);
|
||||
},
|
||||
Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"),
|
||||
}
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
match std::env::var("X11_SCREEN_ID") {
|
||||
Ok(screen_id_str) => {
|
||||
info!("Placing the window on X11 screen {screen_id_str}");
|
||||
let screen_id = screen_id_str.parse()?;
|
||||
window_attributes = window_attributes.with_x11_screen(screen_id);
|
||||
},
|
||||
Err(_) => info!(
|
||||
"Set the X11_SCREEN_ID env variable to place the window on non-default screen"
|
||||
),
|
||||
}
|
||||
|
||||
#[cfg(macos_platform)]
|
||||
if let Some(tab_id) = _tab_id {
|
||||
window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
|
||||
@@ -302,9 +332,9 @@ impl Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for Application {
|
||||
fn proxy_wake_up(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
info!("User wake up");
|
||||
impl ApplicationHandler<UserEvent> for Application {
|
||||
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
|
||||
info!("User event: {event:?}");
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::event_loop::ActiveEventLoop;
|
||||
use crate::window::WindowId;
|
||||
|
||||
/// The handler of the application events.
|
||||
pub trait ApplicationHandler {
|
||||
pub trait ApplicationHandler<T: 'static = ()> {
|
||||
/// Emitted when new events arrive from the OS to be processed.
|
||||
///
|
||||
/// This is a useful place to put code that should be done before you start processing
|
||||
@@ -82,95 +82,11 @@ pub trait ApplicationHandler {
|
||||
/// [`Suspended`]: Self::suspended
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop);
|
||||
|
||||
/// Called after a wake up is requested using [`EventLoopProxy::wake_up()`].
|
||||
/// Emitted when an event is sent from [`EventLoopProxy::send_event`].
|
||||
///
|
||||
/// Multiple calls to the aforementioned method will be merged, and will only wake the event
|
||||
/// loop once; however, due to the nature of multi-threading some wake ups may appear
|
||||
/// spuriously. For these reasons, you should not rely on the number of times that this was
|
||||
/// called.
|
||||
///
|
||||
/// The order in which this is emitted in relation to other events is not guaranteed. The time
|
||||
/// at which this will be emitted is not guaranteed, only that it will happen "soon". That is,
|
||||
/// there may be several executions of the event loop, including multiple redraws to windows,
|
||||
/// between [`EventLoopProxy::wake_up()`] being called and the event being delivered.
|
||||
///
|
||||
/// [`EventLoopProxy::wake_up()`]: crate::event_loop::EventLoopProxy::wake_up
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Use a [`std::sync::mpsc`] channel to handle events from a different thread.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::sync::mpsc;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// use winit::application::ApplicationHandler;
|
||||
/// use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
///
|
||||
/// struct MyApp {
|
||||
/// receiver: mpsc::Receiver<u64>,
|
||||
/// }
|
||||
///
|
||||
/// impl ApplicationHandler for MyApp {
|
||||
/// # fn window_event(
|
||||
/// # &mut self,
|
||||
/// # _event_loop: &ActiveEventLoop,
|
||||
/// # _window_id: winit::window::WindowId,
|
||||
/// # _event: winit::event::WindowEvent,
|
||||
/// # ) {
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn resumed(&mut self, _event_loop: &ActiveEventLoop) {}
|
||||
/// #
|
||||
/// fn proxy_wake_up(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
/// // Iterate current events, since wake-ups may have been merged.
|
||||
/// //
|
||||
/// // Note: We take care not to use `recv` or `iter` here, as those are blocking,
|
||||
/// // and that would be bad for performance and might lead to a deadlock.
|
||||
/// for i in self.receiver.try_iter() {
|
||||
/// println!("received: {i}");
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Rest of `ApplicationHandler`
|
||||
/// }
|
||||
///
|
||||
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let event_loop = EventLoop::new()?;
|
||||
///
|
||||
/// let (sender, receiver) = mpsc::channel();
|
||||
///
|
||||
/// let mut app = MyApp { receiver };
|
||||
///
|
||||
/// // Send an event in a loop
|
||||
/// let proxy = event_loop.create_proxy();
|
||||
/// let background_thread = thread::spawn(move || {
|
||||
/// let mut i = 0;
|
||||
/// loop {
|
||||
/// println!("sending: {i}");
|
||||
/// if sender.send(i).is_err() {
|
||||
/// // Stop sending once `MyApp` is dropped
|
||||
/// break;
|
||||
/// }
|
||||
/// // Trigger the wake-up _after_ we placed the event in the channel.
|
||||
/// // Otherwise, `proxy_wake_up` might be triggered prematurely.
|
||||
/// proxy.wake_up();
|
||||
/// i += 1;
|
||||
/// thread::sleep(Duration::from_secs(1));
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// event_loop.run_app(&mut app)?;
|
||||
///
|
||||
/// drop(app);
|
||||
/// background_thread.join().unwrap();
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn proxy_wake_up(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
/// [`EventLoopProxy::send_event`]: crate::event_loop::EventLoopProxy::send_event
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
|
||||
let _ = (event_loop, event);
|
||||
}
|
||||
|
||||
/// Emitted when the OS sends an event to a winit window.
|
||||
@@ -308,7 +224,7 @@ pub trait ApplicationHandler {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
||||
impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for &mut A {
|
||||
#[inline]
|
||||
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
|
||||
(**self).new_events(event_loop, cause);
|
||||
@@ -320,8 +236,8 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn proxy_wake_up(&mut self, event_loop: &ActiveEventLoop) {
|
||||
(**self).proxy_wake_up(event_loop);
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
|
||||
(**self).user_event(event_loop, event);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -365,7 +281,7 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
|
||||
impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for Box<A> {
|
||||
#[inline]
|
||||
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
|
||||
(**self).new_events(event_loop, cause);
|
||||
@@ -377,8 +293,8 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn proxy_wake_up(&mut self, event_loop: &ActiveEventLoop) {
|
||||
(**self).proxy_wake_up(event_loop);
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
|
||||
(**self).user_event(event_loop, event);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -39,27 +39,3 @@ The migration guide could reference other migration examples in the current
|
||||
changelog entry.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Changed
|
||||
|
||||
- On Web, let events wake up event loop immediately when using `ControlFlow::Poll`.
|
||||
- Bump MSRV from `1.70` to `1.73`.
|
||||
- Changed `ApplicationHandler::user_event` to `user_wake_up`, removing the
|
||||
generic user event.
|
||||
|
||||
Winit will now only indicate that wake up happened, you will have to pair
|
||||
this with an external mechanism like `std::sync::mpsc::channel` if you want
|
||||
to send specific data to be processed on the main thread.
|
||||
- Changed `EventLoopProxy::send_event` to `EventLoopProxy::wake_up`, it now
|
||||
only wakes up the loop.
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove `EventLoop::run`.
|
||||
- Remove `EventLoopExtRunOnDemand::run_on_demand`.
|
||||
- Remove `EventLoopExtPumpEvents::pump_events`.
|
||||
- Remove `Event`.
|
||||
- On iOS, remove `platform::ios::EventLoopExtIOS` and related `platform::ios::Idiom` type.
|
||||
|
||||
This feature was incomplete, and the equivalent functionality can be trivially achieved outside
|
||||
of `winit` using `objc2-ui-kit` and calling `UIDevice::currentDevice().userInterfaceIdiom()`.
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
|
||||
- On X11, non-resizable windows now have maximize explicitly disabled.
|
||||
- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile`
|
||||
and `WindowEvent::HoveredFile`.
|
||||
and `WindowEvent::HoveredFile`.
|
||||
- On Mac, implement `DeviceEvent::Button`.
|
||||
- Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`.
|
||||
- On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor.
|
||||
|
||||
@@ -1,3 +1,64 @@
|
||||
## 0.30.7
|
||||
|
||||
### Fixed
|
||||
|
||||
- On X11, fixed KeyboardInput delivered twice when IME enabled.
|
||||
|
||||
## 0.30.6
|
||||
|
||||
### Added
|
||||
|
||||
- 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.
|
||||
- On X11, the `window` example now understands the `X11_VISUAL_ID` and `X11_SCREEN_ID` env
|
||||
variables to test the respective modifiers of window creation.
|
||||
- On Android, the soft keyboard can now be shown using `Window::set_ime_allowed`.
|
||||
- Add basic iOS IME support. The soft keyboard can now be shown using `Window::set_ime_allowed`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- 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.
|
||||
- On Windows, make `ControlFlow::WaitUntil` work more precisely using `CREATE_WAITABLE_TIMER_HIGH_RESOLUTION`.
|
||||
- On X11, creating windows on screen that is not the first one (e.g. `DISPLAY=:0.1`) works again.
|
||||
- On X11, creating windows while passing `with_x11_screen(non_default_screen)` works again.
|
||||
- On X11, fix XInput handling that prevented a new window from getting the focus in some cases.
|
||||
- On macOS, fix crash when pressing Caps Lock in certain configurations.
|
||||
- On iOS, fixed `MonitorHandle`'s `PartialEq` and `Hash` implementations.
|
||||
- On macOS, fixed undocumented cursors (e.g. zoom, resize, help) always appearing to be invalid and falling back to the default cursor.
|
||||
|
||||
## 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
|
||||
|
||||
- `DeviceId::dummy()` and `WindowId::dummy()` are no longer marked `unsafe`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On Wayland, avoid crashing when compositor is misbehaving.
|
||||
- On Web, fix `WindowEvent::Resized` not using `requestAnimationFrame` when sending
|
||||
`WindowEvent::RedrawRequested` and also potentially causing `WindowEvent::RedrawRequested`
|
||||
to not be de-duplicated.
|
||||
- Account for different browser engine implementations of pointer movement coordinate space.
|
||||
|
||||
## 0.30.3
|
||||
|
||||
### Added
|
||||
@@ -35,7 +96,6 @@
|
||||
|
||||
- Reexport `raw-window-handle` versions 0.4 and 0.5 as `raw_window_handle_04` and `raw_window_handle_05`.
|
||||
- Implement `ApplicationHandler` for `&mut` references and heap allocations to something that implements `ApplicationHandler`.
|
||||
- Add traits `EventLoopExtWayland` and `EventLoopExtX11`, providing methods `is_wayland` and `is_x11` on `EventLoop`.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -112,7 +172,7 @@
|
||||
`Fn`. The semantics are mostly the same, given that the capture list of the
|
||||
closure is your new `State`. Consider the following code:
|
||||
|
||||
```rust,no_run,ignore
|
||||
```rust,no_run
|
||||
use winit::event::Event;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::Window;
|
||||
@@ -148,7 +208,7 @@
|
||||
we move particular `match event` arms into methods on `ApplicationHandler`,
|
||||
for example:
|
||||
|
||||
```rust,no_run,ignore
|
||||
```rust,no_run
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
|
||||
use winit::event_loop::{EventLoop, ActiveEventLoop};
|
||||
@@ -211,7 +271,7 @@
|
||||
|
||||
Using the migration example from above, you can change your code as follows:
|
||||
|
||||
```rust,no_run,ignore
|
||||
```rust,no_run
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
|
||||
use winit::event_loop::{EventLoop, ActiveEventLoop};
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
- Added event `WindowEvent::HiDPIFactorChanged`.
|
||||
- Added method `MonitorId::get_hidpi_factor`.
|
||||
- Deprecated `get_inner_size_pixels` and `get_inner_size_points` methods of `Window` in favor of
|
||||
`get_inner_size`.
|
||||
`get_inner_size`.
|
||||
- **Breaking:** `EventsLoop` is `!Send` and `!Sync` because of platform-dependant constraints,
|
||||
but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`.
|
||||
- `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`.
|
||||
- Rewrite of the wayland backend to use wayland-client-0.11
|
||||
- Support for dead keys on wayland for keyboard utf8 input
|
||||
- Monitor enumeration on Windows is now implemented using `EnumDisplayMonitors` instead of
|
||||
`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`.
|
||||
`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`.
|
||||
- On Windows added `MonitorIdExt::hmonitor` method
|
||||
- Impl `Clone` for `EventsLoopProxy`
|
||||
- `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found
|
||||
- Support for touch event on wayland
|
||||
- `WindowEvent`s `MouseMoved`, `MouseEntered`, and `MouseLeft` have been renamed to
|
||||
`CursorMoved`, `CursorEntered`, and `CursorLeft`.
|
||||
`CursorMoved`, `CursorEntered`, and `CursorLeft`.
|
||||
- New `DeviceEvent`s added, `MouseMotion` and `MouseWheel`.
|
||||
- Send `CursorMoved` event after `CursorEntered` and `Focused` events.
|
||||
- Add support for `ModifiersState`, `MouseMove`, `MouseInput`, `MouseMotion` for emscripten backend.
|
||||
|
||||
84
src/event.rs
84
src/event.rs
@@ -1,4 +1,4 @@
|
||||
//! The event enums and assorted supporting types.
|
||||
//! The [`Event`] enum and assorted supporting types.
|
||||
//!
|
||||
//! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get
|
||||
//! processed and used to modify the program state. For more details, see the root-level
|
||||
@@ -54,15 +54,11 @@ use crate::platform_impl;
|
||||
use crate::window::Window;
|
||||
use crate::window::{ActivationToken, Theme, WindowId};
|
||||
|
||||
// TODO: Remove once the backends can call `ApplicationHandler` methods directly. For now backends
|
||||
// like Windows and Web require `Event` to wire user events, otherwise each backend will have to
|
||||
// wrap `Event` in some other structure.
|
||||
/// Describes a generic event.
|
||||
///
|
||||
/// See the module-level docs for more information on the event loop manages each event.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) enum Event {
|
||||
pub enum Event<T: 'static> {
|
||||
/// See [`ApplicationHandler::new_events`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events
|
||||
@@ -71,15 +67,18 @@ pub(crate) enum Event {
|
||||
/// See [`ApplicationHandler::window_event`] for details.
|
||||
///
|
||||
/// [`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.
|
||||
///
|
||||
/// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
DeviceEvent { device_id: DeviceId, event: DeviceEvent },
|
||||
|
||||
/// See [`ApplicationHandler::user_event`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::user_event`]: crate::application::ApplicationHandler::user_event
|
||||
UserEvent(T),
|
||||
|
||||
/// See [`ApplicationHandler::suspended`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended
|
||||
@@ -104,9 +103,24 @@ pub(crate) enum Event {
|
||||
///
|
||||
/// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning
|
||||
MemoryWarning,
|
||||
}
|
||||
|
||||
/// User requested a wake up.
|
||||
UserWakeUp,
|
||||
impl<T> Event<T> {
|
||||
#[allow(clippy::result_large_err)]
|
||||
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
|
||||
use self::Event::*;
|
||||
match self {
|
||||
UserEvent(_) => Err(self),
|
||||
WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }),
|
||||
DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }),
|
||||
NewEvents(cause) => Ok(NewEvents(cause)),
|
||||
AboutToWait => Ok(AboutToWait),
|
||||
LoopExiting => Ok(LoopExiting),
|
||||
Suspended => Ok(Suspended),
|
||||
Resumed => Ok(Resumed),
|
||||
MemoryWarning => Ok(MemoryWarning),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the reason the event loop is resuming.
|
||||
@@ -435,16 +449,13 @@ pub struct DeviceId(pub(crate) platform_impl::DeviceId);
|
||||
impl DeviceId {
|
||||
/// Returns a dummy id, useful for unit testing.
|
||||
///
|
||||
/// # Safety
|
||||
/// # 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 `DeviceId`.
|
||||
///
|
||||
/// **Passing this into a winit function will result in undefined behavior.**
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
#[allow(unused_unsafe)]
|
||||
DeviceId(unsafe { platform_impl::DeviceId::dummy() })
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId(platform_impl::DeviceId::dummy())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,11 +532,11 @@ pub struct KeyEvent {
|
||||
/// ## Caveats
|
||||
///
|
||||
/// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that
|
||||
/// implements DVORAK in hardware (or firmware)
|
||||
/// implements DVORAK in hardware (or firmware)
|
||||
/// - Your application will likely have to handle keyboards which are missing keys that your
|
||||
/// own keyboard has.
|
||||
/// own keyboard has.
|
||||
/// - Certain `KeyCode`s will move between a couple of different positions depending on what
|
||||
/// layout the keyboard was manufactured to support.
|
||||
/// layout the keyboard was manufactured to support.
|
||||
///
|
||||
/// **Because of these caveats, it is important that you provide users with a way to configure
|
||||
/// most (if not all) keybinds in your application.**
|
||||
@@ -547,8 +558,7 @@ pub struct KeyEvent {
|
||||
///
|
||||
/// This has two use cases:
|
||||
/// - Allows querying whether the current input is a Dead key.
|
||||
/// - Allows handling key-bindings on platforms which don't
|
||||
/// support [`key_without_modifiers`].
|
||||
/// - Allows handling key-bindings on platforms which don't support [`key_without_modifiers`].
|
||||
///
|
||||
/// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard
|
||||
/// shortcuts, **it is important that you provide users with a way to configure your
|
||||
@@ -556,8 +566,8 @@ pub struct KeyEvent {
|
||||
/// incompatible keyboard layout.**
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Web:** Dead keys might be reported as the real key instead
|
||||
/// of `Dead` depending on the browser/OS.
|
||||
/// - **Web:** Dead keys might be reported as the real key instead of `Dead` depending on the
|
||||
/// browser/OS.
|
||||
///
|
||||
/// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
|
||||
pub logical_key: keyboard::Key,
|
||||
@@ -839,8 +849,8 @@ pub struct Touch {
|
||||
///
|
||||
/// - 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).
|
||||
/// 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 id: u64,
|
||||
@@ -1007,7 +1017,7 @@ mod tests {
|
||||
($closure:expr) => {{
|
||||
#[allow(unused_mut)]
|
||||
let mut x = $closure;
|
||||
let did = unsafe { event::DeviceId::dummy() };
|
||||
let did = event::DeviceId::dummy();
|
||||
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
@@ -1017,7 +1027,8 @@ mod tests {
|
||||
use crate::window::WindowId;
|
||||
|
||||
// Mainline events.
|
||||
let wid = unsafe { WindowId::dummy() };
|
||||
let wid = WindowId::dummy();
|
||||
x(UserEvent(()));
|
||||
x(NewEvents(event::StartCause::Init));
|
||||
x(AboutToWait);
|
||||
x(LoopExiting);
|
||||
@@ -1101,12 +1112,25 @@ mod tests {
|
||||
#[allow(clippy::redundant_clone)]
|
||||
#[test]
|
||||
fn test_event_clone() {
|
||||
foreach_event!(|event: event::Event| {
|
||||
foreach_event!(|event: event::Event<()>| {
|
||||
let event2 = event.clone();
|
||||
assert_eq!(event, event2);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_nonuser_event() {
|
||||
foreach_event!(|event: event::Event<()>| {
|
||||
let is_user = matches!(event, event::Event::UserEvent(()));
|
||||
let event2 = event.map_nonuser_event::<()>();
|
||||
if is_user {
|
||||
assert_eq!(event2, Err(event::Event::UserEvent(())));
|
||||
} else {
|
||||
assert!(event2.is_ok());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_force_normalize() {
|
||||
let force = event::Force::Normalized(0.0);
|
||||
@@ -1127,12 +1151,12 @@ mod tests {
|
||||
#[allow(clippy::clone_on_copy)]
|
||||
#[test]
|
||||
fn ensure_attrs_do_not_panic() {
|
||||
foreach_event!(|event: event::Event| {
|
||||
foreach_event!(|event: event::Event<()>| {
|
||||
let _ = format!("{:?}", event);
|
||||
});
|
||||
let _ = event::StartCause::Init.clone();
|
||||
|
||||
let did = unsafe { crate::event::DeviceId::dummy() }.clone();
|
||||
let did = crate::event::DeviceId::dummy().clone();
|
||||
HashSet::new().insert(did);
|
||||
let mut set = [did, did, did];
|
||||
set.sort_unstable();
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
//!
|
||||
//! If you want to send custom events to the event loop, use
|
||||
//! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its
|
||||
//! [`wake_up`][EventLoopProxy::wake_up] method. Then during handling the wake up
|
||||
//! you can poll your event sources.
|
||||
//! [`send_event`][EventLoopProxy::send_event] method.
|
||||
//!
|
||||
//! See the root-level documentation for information on how to create and use an event loop to
|
||||
//! handle events.
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::{error, fmt};
|
||||
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -21,9 +20,10 @@ use web_time::{Duration, Instant};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, OsError};
|
||||
use crate::event::Event;
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::platform_impl;
|
||||
use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
|
||||
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
|
||||
/// the events loop.
|
||||
@@ -40,8 +40,8 @@ use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
|
||||
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub struct EventLoop {
|
||||
pub(crate) event_loop: platform_impl::EventLoop,
|
||||
pub struct EventLoop<T: 'static> {
|
||||
pub(crate) event_loop: platform_impl::EventLoop<T>,
|
||||
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
|
||||
}
|
||||
|
||||
@@ -59,15 +59,16 @@ pub struct ActiveEventLoop {
|
||||
/// This is used to make specifying options that affect the whole application
|
||||
/// easier. But note that constructing multiple event loops is not supported.
|
||||
///
|
||||
/// This can be created using [`EventLoop::builder`].
|
||||
/// This can be created using [`EventLoop::new`] or [`EventLoop::with_user_event`].
|
||||
#[derive(Default)]
|
||||
pub struct EventLoopBuilder {
|
||||
pub struct EventLoopBuilder<T: 'static> {
|
||||
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
|
||||
_p: PhantomData<T>,
|
||||
}
|
||||
|
||||
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
impl EventLoopBuilder {
|
||||
impl EventLoopBuilder<()> {
|
||||
/// Start building a new event loop.
|
||||
#[inline]
|
||||
#[deprecated = "use `EventLoop::builder` instead"]
|
||||
@@ -76,7 +77,7 @@ impl EventLoopBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopBuilder {
|
||||
impl<T> EventLoopBuilder<T> {
|
||||
/// Builds a new event loop.
|
||||
///
|
||||
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
|
||||
@@ -111,7 +112,7 @@ impl EventLoopBuilder {
|
||||
doc = "[`.with_android_app(app)`]: #only-available-on-android"
|
||||
)]
|
||||
#[inline]
|
||||
pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
|
||||
pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> {
|
||||
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
|
||||
|
||||
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
|
||||
@@ -132,7 +133,7 @@ impl EventLoopBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventLoop {
|
||||
impl<T> fmt::Debug for EventLoop<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad("EventLoop { .. }")
|
||||
}
|
||||
@@ -146,12 +147,11 @@ impl fmt::Debug for ActiveEventLoop {
|
||||
|
||||
/// Set through [`ActiveEventLoop::set_control_flow()`].
|
||||
///
|
||||
/// Indicates the desired behavior of the event loop after [`about_to_wait`] is called.
|
||||
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
|
||||
///
|
||||
/// Defaults to [`Wait`].
|
||||
///
|
||||
/// [`Wait`]: Self::Wait
|
||||
/// [`about_to_wait`]: crate::application::ApplicationHandler::about_to_wait
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
@@ -189,12 +189,12 @@ impl ControlFlow {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
impl EventLoop<()> {
|
||||
/// Create the event loop.
|
||||
///
|
||||
/// This is an alias of `EventLoop::builder().build()`.
|
||||
#[inline]
|
||||
pub fn new() -> Result<EventLoop, EventLoopError> {
|
||||
pub fn new() -> Result<EventLoop<()>, EventLoopError> {
|
||||
Self::builder().build()
|
||||
}
|
||||
|
||||
@@ -204,12 +204,33 @@ impl EventLoop {
|
||||
///
|
||||
/// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that.
|
||||
#[inline]
|
||||
pub fn builder() -> EventLoopBuilder {
|
||||
EventLoopBuilder { platform_specific: Default::default() }
|
||||
pub fn builder() -> EventLoopBuilder<()> {
|
||||
Self::with_user_event()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
impl<T> EventLoop<T> {
|
||||
/// Start building a new event loop, with the given type as the user event
|
||||
/// type.
|
||||
pub fn with_user_event() -> EventLoopBuilder<T> {
|
||||
EventLoopBuilder { platform_specific: Default::default(), _p: PhantomData }
|
||||
}
|
||||
|
||||
/// See [`run_app`].
|
||||
///
|
||||
/// [`run_app`]: Self::run_app
|
||||
#[inline]
|
||||
#[deprecated = "use `EventLoop::run_app` instead"]
|
||||
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
|
||||
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &ActiveEventLoop),
|
||||
{
|
||||
let _span = tracing::debug_span!("winit::EventLoop::run").entered();
|
||||
|
||||
self.event_loop.run(event_handler)
|
||||
}
|
||||
|
||||
/// Run the application with the event loop on the calling thread.
|
||||
///
|
||||
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
|
||||
@@ -240,13 +261,13 @@ impl EventLoop {
|
||||
/// [^1]: `EventLoopExtWebSys::spawn_app()` is only available on Web.
|
||||
#[inline]
|
||||
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
|
||||
pub fn run_app<A: ApplicationHandler>(self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.event_loop.run_app(app)
|
||||
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.event_loop.run(|event, event_loop| dispatch_event_for_app(app, event_loop, event))
|
||||
}
|
||||
|
||||
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
|
||||
/// to the main event loop, possibly from another thread.
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy { event_loop_proxy: self.event_loop.create_proxy() }
|
||||
}
|
||||
|
||||
@@ -302,14 +323,14 @@ impl EventLoop {
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for EventLoop {
|
||||
impl<T> rwh_06::HasDisplayHandle for EventLoop<T> {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
unsafe impl rwh_05::HasRawDisplayHandle for EventLoop {
|
||||
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
|
||||
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::HasRawDisplayHandle::raw_display_handle(self.event_loop.window_target())
|
||||
@@ -317,7 +338,7 @@ unsafe impl rwh_05::HasRawDisplayHandle for EventLoop {
|
||||
}
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
impl AsFd for EventLoop {
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
/// Get the underlying [EventLoop]'s `fd` which you can register
|
||||
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
|
||||
/// loop must be polled with the [`pump_app_events`] API.
|
||||
@@ -331,7 +352,7 @@ impl AsFd for EventLoop {
|
||||
}
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
impl AsRawFd for EventLoop {
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
/// Get the underlying [EventLoop]'s raw `fd` which you can register
|
||||
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
|
||||
/// loop must be polled with the [`pump_app_events`] API.
|
||||
@@ -416,6 +437,17 @@ impl ActiveEventLoop {
|
||||
self.p.listen_device_events(allowed);
|
||||
}
|
||||
|
||||
/// Returns the current system theme.
|
||||
///
|
||||
/// Returns `None` if it cannot be determined on the current platform.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
|
||||
pub fn system_theme(&self) -> Option<Theme> {
|
||||
self.p.system_theme()
|
||||
}
|
||||
|
||||
/// Sets the [`ControlFlow`].
|
||||
pub fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.p.set_control_flow(control_flow)
|
||||
@@ -428,7 +460,7 @@ impl ActiveEventLoop {
|
||||
|
||||
/// This exits the event loop.
|
||||
///
|
||||
/// See [`exiting`][crate::application::ApplicationHandler::exiting].
|
||||
/// See [`LoopExiting`][Event::LoopExiting].
|
||||
pub fn exit(&self) {
|
||||
let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered();
|
||||
|
||||
@@ -469,7 +501,7 @@ unsafe impl rwh_05::HasRawDisplayHandle for ActiveEventLoop {
|
||||
|
||||
/// A proxy for the underlying display handle.
|
||||
///
|
||||
/// The purpose of this type is to provide a cheaply clonable handle to the underlying
|
||||
/// The purpose of this type is to provide a cheaply cloneable 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
|
||||
@@ -513,39 +545,53 @@ unsafe impl rwh_05::HasRawDisplayHandle for OwnedDisplayHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Control the [`EventLoop`], possibly from a different thread, without referencing it directly.
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
event_loop_proxy: platform_impl::EventLoopProxy,
|
||||
/// Used to send custom events to [`EventLoop`].
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
event_loop_proxy: platform_impl::EventLoopProxy<T>,
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
/// Wake up the [`EventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being
|
||||
/// called.
|
||||
///
|
||||
/// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the
|
||||
/// documentation on that for details.
|
||||
///
|
||||
/// If the event loop is no longer running, this is a no-op.
|
||||
///
|
||||
/// [`proxy_wake_up`]: ApplicationHandler::proxy_wake_up
|
||||
///
|
||||
/// # Platform-specific
|
||||
///
|
||||
/// - **Windows**: The wake-up may be ignored under high contention, see [#3687].
|
||||
///
|
||||
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
|
||||
pub fn wake_up(&self) {
|
||||
self.event_loop_proxy.wake_up();
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { event_loop_proxy: self.event_loop_proxy.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventLoopProxy {
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
/// Send an event to the [`EventLoop`] from which this proxy was created. This emits a
|
||||
/// `UserEvent(event)` event in the event loop, where `event` is the value passed to this
|
||||
/// function.
|
||||
///
|
||||
/// Returns an `Err` if the associated [`EventLoop`] no longer exists.
|
||||
///
|
||||
/// [`UserEvent(event)`]: Event::UserEvent
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
let _span = tracing::debug_span!("winit::EventLoopProxy::send_event",).entered();
|
||||
|
||||
self.event_loop_proxy.send_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad("EventLoopProxy { .. }")
|
||||
}
|
||||
}
|
||||
|
||||
/// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] that
|
||||
/// no longer exists.
|
||||
///
|
||||
/// Contains the original event given to [`EventLoopProxy::send_event`].
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventLoopClosed<T>(pub T);
|
||||
|
||||
impl<T> fmt::Display for EventLoopClosed<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("Tried to wake up a closed `EventLoop`")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {}
|
||||
|
||||
/// Control when device events are captured.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub enum DeviceEvents {
|
||||
@@ -583,3 +629,23 @@ impl AsyncRequestSerial {
|
||||
Self { serial }
|
||||
}
|
||||
}
|
||||
|
||||
/// Shim for various run APIs.
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_event_for_app<T: 'static, A: ApplicationHandler<T>>(
|
||||
app: &mut A,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event: Event<T>,
|
||||
) {
|
||||
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::UserEvent(event) => app.user_event(event_loop, event),
|
||||
Event::Suspended => app.suspended(event_loop),
|
||||
Event::Resumed => app.resumed(event_loop),
|
||||
Event::AboutToWait => app.about_to_wait(event_loop),
|
||||
Event::LoopExiting => app.exiting(event_loop),
|
||||
Event::MemoryWarning => app.memory_warning(event_loop),
|
||||
}
|
||||
}
|
||||
|
||||
37
src/lib.rs
37
src/lib.rs
@@ -7,7 +7,12 @@
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use winit::event_loop::EventLoop;
|
||||
//! let event_loop = EventLoop::new().unwrap();
|
||||
//!
|
||||
//! # // Intentionally use `fn main` for clarity
|
||||
//! fn main() {
|
||||
//! let event_loop = EventLoop::new().unwrap();
|
||||
//! // ...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Then you create a [`Window`] with [`create_window`].
|
||||
@@ -19,11 +24,12 @@
|
||||
//! window or a key getting pressed while the window is focused. Devices can generate
|
||||
//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window.
|
||||
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
|
||||
//! [`DeviceEvent`].
|
||||
//! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if
|
||||
//! desired.
|
||||
//!
|
||||
//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will
|
||||
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
|
||||
//! will run until [`exit()`] is used, at which point [`exiting()`] is called.
|
||||
//! will run until [`exit()`] is used, at which point [`Event::LoopExiting`].
|
||||
//!
|
||||
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
|
||||
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works
|
||||
@@ -83,19 +89,22 @@
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! let event_loop = EventLoop::new().unwrap();
|
||||
//! # // Intentionally use `fn main` for clarity
|
||||
//! fn main() {
|
||||
//! let event_loop = EventLoop::new().unwrap();
|
||||
//!
|
||||
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
|
||||
//! // dispatched any events. This is ideal for games and similar applications.
|
||||
//! event_loop.set_control_flow(ControlFlow::Poll);
|
||||
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
|
||||
//! // dispatched any events. This is ideal for games and similar applications.
|
||||
//! event_loop.set_control_flow(ControlFlow::Poll);
|
||||
//!
|
||||
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
|
||||
//! // This is ideal for non-game applications that only update in response to user
|
||||
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
|
||||
//! event_loop.set_control_flow(ControlFlow::Wait);
|
||||
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
|
||||
//! // This is ideal for non-game applications that only update in response to user
|
||||
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
|
||||
//! event_loop.set_control_flow(ControlFlow::Wait);
|
||||
//!
|
||||
//! let mut app = App::default();
|
||||
//! event_loop.run_app(&mut app);
|
||||
//! let mut app = App::default();
|
||||
//! event_loop.run_app(&mut app);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
|
||||
@@ -162,7 +171,7 @@
|
||||
//! [`WindowEvent`]: event::WindowEvent
|
||||
//! [`DeviceEvent`]: event::DeviceEvent
|
||||
//! [`Event::UserEvent`]: event::Event::UserEvent
|
||||
//! [`exiting()`]: crate::application::ApplicationHandler::exiting
|
||||
//! [`Event::LoopExiting`]: event::Event::LoopExiting
|
||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
|
||||
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland.
|
||||
|
||||
@@ -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.3",
|
||||
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.7",
|
||||
//! 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
|
||||
@@ -76,12 +76,22 @@ use crate::window::{Window, WindowAttributes};
|
||||
use self::activity::{AndroidApp, ConfigurationRef, Rect};
|
||||
|
||||
/// Additional methods on [`EventLoop`] that are specific to Android.
|
||||
pub trait EventLoopExtAndroid {}
|
||||
pub trait EventLoopExtAndroid {
|
||||
/// Get the [`AndroidApp`] which was used to create this event loop.
|
||||
fn android_app(&self) -> &AndroidApp;
|
||||
}
|
||||
|
||||
impl EventLoopExtAndroid for EventLoop {}
|
||||
impl<T> EventLoopExtAndroid for EventLoop<T> {
|
||||
fn android_app(&self) -> &AndroidApp {
|
||||
&self.event_loop.android_app
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`ActiveEventLoop`] that are specific to Android.
|
||||
pub trait ActiveEventLoopExtAndroid {}
|
||||
pub trait ActiveEventLoopExtAndroid {
|
||||
/// Get the [`AndroidApp`] which was used to create this event loop.
|
||||
fn android_app(&self) -> &AndroidApp;
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to Android.
|
||||
pub trait WindowExtAndroid {
|
||||
@@ -100,7 +110,11 @@ impl WindowExtAndroid for Window {
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtAndroid for ActiveEventLoop {}
|
||||
impl ActiveEventLoopExtAndroid for ActiveEventLoop {
|
||||
fn android_app(&self) -> &AndroidApp {
|
||||
&self.p.app
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`WindowAttributes`] that are specific to Android.
|
||||
pub trait WindowAttributesExtAndroid {}
|
||||
@@ -108,9 +122,9 @@ pub trait WindowAttributesExtAndroid {}
|
||||
impl WindowAttributesExtAndroid for WindowAttributes {}
|
||||
|
||||
pub trait EventLoopBuilderExtAndroid {
|
||||
/// Associates the `AndroidApp` that was passed to `android_main()` with the event loop
|
||||
/// Associates the [`AndroidApp`] that was passed to `android_main()` with the event loop
|
||||
///
|
||||
/// This must be called on Android since the `AndroidApp` is not global state.
|
||||
/// This must be called on Android since the [`AndroidApp`] is not global state.
|
||||
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self;
|
||||
|
||||
/// Calling this will mark the volume keys to be manually handled by the application
|
||||
@@ -119,7 +133,7 @@ pub trait EventLoopBuilderExtAndroid {
|
||||
fn handle_volume_keys(&mut self) -> &mut Self;
|
||||
}
|
||||
|
||||
impl EventLoopBuilderExtAndroid for EventLoopBuilder {
|
||||
impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
|
||||
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self {
|
||||
self.platform_specific.android_app = Some(app);
|
||||
self
|
||||
@@ -147,7 +161,7 @@ impl EventLoopBuilderExtAndroid for EventLoopBuilder {
|
||||
/// depending on the `android_activity` crate, and instead consume the API that
|
||||
/// is re-exported by Winit.
|
||||
///
|
||||
/// For compatibility applications should then import the `AndroidApp` type for
|
||||
/// For compatibility applications should then import the [`AndroidApp`] type for
|
||||
/// their `android_main(app: AndroidApp)` function like:
|
||||
/// ```rust
|
||||
/// #[cfg(target_os = "android")]
|
||||
|
||||
@@ -66,9 +66,22 @@
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::event_loop::EventLoop;
|
||||
use crate::monitor::{MonitorHandle, VideoModeHandle};
|
||||
use crate::window::{Window, WindowAttributes};
|
||||
|
||||
/// Additional methods on [`EventLoop`] that are specific to iOS.
|
||||
pub trait EventLoopExtIOS {
|
||||
/// Returns the [`Idiom`] (phone/tablet/tv/etc) for the current device.
|
||||
fn idiom(&self) -> Idiom;
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopExtIOS for EventLoop<T> {
|
||||
fn idiom(&self) -> Idiom {
|
||||
self.event_loop.idiom()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to iOS.
|
||||
pub trait WindowExtIOS {
|
||||
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
|
||||
@@ -366,6 +379,24 @@ pub enum ValidOrientations {
|
||||
Portrait,
|
||||
}
|
||||
|
||||
/// The device [idiom].
|
||||
///
|
||||
/// [idiom]: https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom?language=objc
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Idiom {
|
||||
Unspecified,
|
||||
|
||||
/// iPhone and iPod touch.
|
||||
Phone,
|
||||
|
||||
/// iPad.
|
||||
Pad,
|
||||
|
||||
/// tvOS and Apple TV.
|
||||
TV,
|
||||
CarPlay,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// The [edges] of a screen.
|
||||
///
|
||||
|
||||
@@ -94,6 +94,12 @@ 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 Window {
|
||||
@@ -166,6 +172,16 @@ impl WindowExtMacOS for Window {
|
||||
fn option_as_alt(&self) -> OptionAsAlt {
|
||||
self.window.maybe_wait_on_main(|w| w.option_as_alt())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_borderless_game(&self, borderless_game: bool) {
|
||||
self.window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_borderless_game(&self) -> bool {
|
||||
self.window.maybe_wait_on_main(|w| w.is_borderless_game())
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to `NSApplicationActivationPolicy`.
|
||||
@@ -216,6 +232,8 @@ 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 {
|
||||
@@ -284,12 +302,21 @@ 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.
|
||||
/// 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.
|
||||
///
|
||||
/// It is set to [`ActivationPolicy::Regular`] by default.
|
||||
/// If unused, the Winit will honor the package manifest.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -338,10 +365,10 @@ pub trait EventLoopBuilderExtMacOS {
|
||||
fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl EventLoopBuilderExtMacOS for EventLoopBuilder {
|
||||
impl<T> EventLoopBuilderExtMacOS for EventLoopBuilder<T> {
|
||||
#[inline]
|
||||
fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self {
|
||||
self.platform_specific.activation_policy = activation_policy;
|
||||
self.platform_specific.activation_policy = Some(activation_policy);
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::event_loop::EventLoop;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
|
||||
|
||||
/// Additional methods on [`EventLoop`] for pumping events within an external event loop
|
||||
pub trait EventLoopExtPumpEvents {
|
||||
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// Pump the `EventLoop` to check for and dispatch pending events.
|
||||
///
|
||||
/// This API is designed to enable applications to integrate Winit into an
|
||||
@@ -48,18 +52,18 @@ pub trait EventLoopExtPumpEvents {
|
||||
/// - `RedrawRequested` events, used to schedule rendering.
|
||||
///
|
||||
/// macOS for example uses a `drawRect` callback to drive rendering
|
||||
/// within applications and expects rendering to be finished before
|
||||
/// the `drawRect` callback returns.
|
||||
/// within applications and expects rendering to be finished before
|
||||
/// the `drawRect` callback returns.
|
||||
///
|
||||
/// For portability it's strongly recommended that applications should
|
||||
/// keep their rendering inside the closure provided to Winit.
|
||||
/// keep their rendering inside the closure provided to Winit.
|
||||
/// - Any lifecycle events, such as `Suspended` / `Resumed`.
|
||||
///
|
||||
/// The handling of these events needs to be synchronized with the
|
||||
/// operating system and it would never be appropriate to buffer a
|
||||
/// notification that your application has been suspended or resumed and
|
||||
/// then handled that later since there would always be a chance that
|
||||
/// other lifecycle events occur while the event is buffered.
|
||||
/// operating system and it would never be appropriate to buffer a
|
||||
/// notification that your application has been suspended or resumed and
|
||||
/// then handled that later since there would always be a chance that
|
||||
/// other lifecycle events occur while the event is buffered.
|
||||
///
|
||||
/// ## Supported Platforms
|
||||
///
|
||||
@@ -70,13 +74,13 @@ pub trait EventLoopExtPumpEvents {
|
||||
///
|
||||
/// ## Unsupported Platforms
|
||||
///
|
||||
/// - **Web:** This API is fundamentally incompatible with the event-based way in which
|
||||
/// Web browsers work because it's not possible to have a long-running external
|
||||
/// loop that would block the browser and there is nothing that can be
|
||||
/// polled to ask for new new events. Events are delivered via callbacks based
|
||||
/// on an event loop that is internal to the browser itself.
|
||||
/// - **Web:** This API is fundamentally incompatible with the event-based way in which Web
|
||||
/// browsers work because it's not possible to have a long-running external loop that would
|
||||
/// block the browser and there is nothing that can be polled to ask for new new events.
|
||||
/// Events are delivered via callbacks based on an event loop that is internal to the browser
|
||||
/// itself.
|
||||
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so
|
||||
/// there's no way to support the same approach to polling as on MacOS.
|
||||
/// there's no way to support the same approach to polling as on MacOS.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
@@ -99,20 +103,34 @@ pub trait EventLoopExtPumpEvents {
|
||||
/// If you render outside of Winit you are likely to see window resizing artifacts
|
||||
/// since MacOS expects applications to render synchronously during any `drawRect`
|
||||
/// callback.
|
||||
fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus;
|
||||
}
|
||||
|
||||
impl EventLoopExtPumpEvents for EventLoop {
|
||||
fn pump_app_events<A: ApplicationHandler>(
|
||||
fn pump_app_events<A: ApplicationHandler<Self::UserEvent>>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
self.event_loop.pump_app_events(timeout, app)
|
||||
#[allow(deprecated)]
|
||||
self.pump_events(timeout, |event, event_loop| {
|
||||
event_loop::dispatch_event_for_app(app, event_loop, event)
|
||||
})
|
||||
}
|
||||
|
||||
/// See [`pump_app_events`].
|
||||
///
|
||||
/// [`pump_app_events`]: Self::pump_app_events
|
||||
#[deprecated = "use EventLoopExtPumpEvents::pump_app_events"]
|
||||
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtPumpEvents for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
||||
{
|
||||
self.event_loop.pump_events(timeout, event_handler)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
|
||||
|
||||
#[cfg(doc)]
|
||||
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
|
||||
|
||||
/// Additional methods on [`EventLoop`] to return control flow to the caller.
|
||||
pub trait EventLoopExtRunOnDemand {
|
||||
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// See [`run_app_on_demand`].
|
||||
///
|
||||
/// [`run_app_on_demand`]: Self::run_app_on_demand
|
||||
#[deprecated = "use EventLoopExtRunOnDemand::run_app_on_demand"]
|
||||
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||
|
||||
/// Run the application with the event loop on the calling thread.
|
||||
///
|
||||
/// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`)
|
||||
@@ -30,7 +42,9 @@ pub trait EventLoopExtRunOnDemand {
|
||||
/// # Caveats
|
||||
/// - This extension isn't available on all platforms, since it's not always possible to return
|
||||
/// to the caller (specifically this is impossible on iOS and Web - though with the Web
|
||||
/// backend it is possible to use `EventLoopExtWebSys::spawn()`[^1] more than once instead).
|
||||
/// backend it is possible to use `EventLoopExtWebSys::spawn()`
|
||||
#[cfg_attr(not(web_platform), doc = "[^1]")]
|
||||
/// more than once instead).
|
||||
/// - No [`Window`] state can be carried between separate runs of the event loop.
|
||||
///
|
||||
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you
|
||||
@@ -53,19 +67,26 @@ pub trait EventLoopExtRunOnDemand {
|
||||
///
|
||||
/// [`exit()`]: ActiveEventLoop::exit()
|
||||
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
|
||||
fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError>;
|
||||
}
|
||||
|
||||
impl EventLoopExtRunOnDemand for EventLoop {
|
||||
fn run_app_on_demand<A: ApplicationHandler>(
|
||||
fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
#[allow(deprecated)]
|
||||
self.run_on_demand(|event, event_loop| {
|
||||
event_loop::dispatch_event_for_app(app, event_loop, event)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
||||
{
|
||||
self.event_loop.window_target().clear_exit();
|
||||
self.event_loop.run_app_on_demand(app)
|
||||
self.event_loop.run_on_demand(event_handler)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ pub trait EventLoopExtWayland {
|
||||
fn is_wayland(&self) -> bool;
|
||||
}
|
||||
|
||||
impl EventLoopExtWayland for EventLoop {
|
||||
impl<T: 'static> EventLoopExtWayland for EventLoop<T> {
|
||||
#[inline]
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.event_loop.is_wayland()
|
||||
@@ -57,7 +57,7 @@ pub trait EventLoopBuilderExtWayland {
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl EventLoopBuilderExtWayland for EventLoopBuilder {
|
||||
impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> {
|
||||
#[inline]
|
||||
fn with_wayland(&mut self) -> &mut Self {
|
||||
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland);
|
||||
|
||||
@@ -55,7 +55,8 @@ use web_sys::HtmlCanvasElement;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::CustomCursorSource;
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
|
||||
#[cfg(web_platform)]
|
||||
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
|
||||
use crate::platform_impl::PlatformCustomCursorSource;
|
||||
@@ -155,6 +156,9 @@ impl WindowAttributesExtWebSys for WindowAttributes {
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to the web.
|
||||
pub trait EventLoopExtWebSys {
|
||||
/// A type provided by the user that can be passed through `Event::UserEvent`.
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// Initializes the winit event loop.
|
||||
///
|
||||
/// Unlike
|
||||
@@ -177,7 +181,15 @@ pub trait EventLoopExtWebSys {
|
||||
doc = "[`run_app()`]: EventLoop::run_app()"
|
||||
)]
|
||||
/// [^1]: `run_app()` is _not_ available on WASM when the target supports `exception-handling`.
|
||||
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A);
|
||||
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, app: A);
|
||||
|
||||
/// See [`spawn_app`].
|
||||
///
|
||||
/// [`spawn_app`]: Self::spawn_app
|
||||
#[deprecated = "use EventLoopExtWebSys::spawn_app"]
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||
|
||||
/// Sets the strategy for [`ControlFlow::Poll`].
|
||||
///
|
||||
@@ -208,9 +220,20 @@ pub trait EventLoopExtWebSys {
|
||||
fn wait_until_strategy(&self) -> WaitUntilStrategy;
|
||||
}
|
||||
|
||||
impl EventLoopExtWebSys for EventLoop {
|
||||
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A) {
|
||||
self.event_loop.spawn_app(app);
|
||||
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, mut app: A) {
|
||||
self.event_loop.spawn(move |event, event_loop| {
|
||||
event_loop::dispatch_event_for_app(&mut app, event_loop, event)
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
||||
{
|
||||
self.event_loop.spawn(event_handler)
|
||||
}
|
||||
|
||||
fn set_poll_strategy(&self, strategy: PollStrategy) {
|
||||
@@ -399,7 +422,7 @@ impl fmt::Display for BadAnimation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Empty => write!(f, "No cursors supplied"),
|
||||
Self::Animation => write!(f, "A supplied cursor is an animtion"),
|
||||
Self::Animation => write!(f, "A supplied cursor is an animation"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -438,3 +461,5 @@ impl Display for CustomCursorError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for CustomCursorError {}
|
||||
|
||||
@@ -227,7 +227,7 @@ pub trait EventLoopBuilderExtWindows {
|
||||
F: FnMut(*const c_void) -> bool + 'static;
|
||||
}
|
||||
|
||||
impl EventLoopBuilderExtWindows for EventLoopBuilder {
|
||||
impl<T> EventLoopBuilderExtWindows for EventLoopBuilder<T> {
|
||||
#[inline]
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
|
||||
self.platform_specific.any_thread = any_thread;
|
||||
|
||||
@@ -81,9 +81,7 @@ pub type XWindow = u32;
|
||||
#[inline]
|
||||
pub fn register_xlib_error_hook(hook: XlibErrorHook) {
|
||||
// Append new hook.
|
||||
unsafe {
|
||||
crate::platform_impl::XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
|
||||
}
|
||||
crate::platform_impl::XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
|
||||
}
|
||||
|
||||
/// Additional methods on [`ActiveEventLoop`] that are specific to X11.
|
||||
@@ -105,7 +103,7 @@ pub trait EventLoopExtX11 {
|
||||
fn is_x11(&self) -> bool;
|
||||
}
|
||||
|
||||
impl EventLoopExtX11 for EventLoop {
|
||||
impl<T: 'static> EventLoopExtX11 for EventLoop<T> {
|
||||
#[inline]
|
||||
fn is_x11(&self) -> bool {
|
||||
!self.event_loop.is_wayland()
|
||||
@@ -124,7 +122,7 @@ pub trait EventLoopBuilderExtX11 {
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl EventLoopBuilderExtX11 for EventLoopBuilder {
|
||||
impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> {
|
||||
#[inline]
|
||||
fn with_x11(&mut self) -> &mut Self {
|
||||
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X);
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::collections::VecDeque;
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
|
||||
@@ -12,13 +12,12 @@ use android_activity::{
|
||||
};
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::{self, Force, InnerSizeWriter, StartCause};
|
||||
use crate::event_loop::{self, ControlFlow, DeviceEvents};
|
||||
use crate::event_loop::{self, ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents};
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::Fullscreen;
|
||||
use crate::window::{
|
||||
@@ -42,6 +41,41 @@ fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
|
||||
a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
|
||||
}
|
||||
|
||||
struct PeekableReceiver<T> {
|
||||
recv: mpsc::Receiver<T>,
|
||||
first: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> PeekableReceiver<T> {
|
||||
pub fn from_recv(recv: mpsc::Receiver<T>) -> Self {
|
||||
Self { recv, first: None }
|
||||
}
|
||||
|
||||
pub fn has_incoming(&mut self) -> bool {
|
||||
if self.first.is_some() {
|
||||
return true;
|
||||
}
|
||||
match self.recv.try_recv() {
|
||||
Ok(v) => {
|
||||
self.first = Some(v);
|
||||
true
|
||||
},
|
||||
Err(mpsc::TryRecvError::Empty) => false,
|
||||
Err(mpsc::TryRecvError::Disconnected) => {
|
||||
warn!("Channel was disconnected when checking incoming");
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_recv(&mut self) -> Result<T, mpsc::TryRecvError> {
|
||||
if let Some(first) = self.first.take() {
|
||||
return Ok(first);
|
||||
}
|
||||
self.recv.try_recv()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SharedFlagSetter {
|
||||
flag: Arc<AtomicBool>,
|
||||
@@ -97,12 +131,13 @@ impl RedrawRequester {
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KeyEventExtra {}
|
||||
|
||||
pub struct EventLoop {
|
||||
android_app: AndroidApp,
|
||||
pub struct EventLoop<T: 'static> {
|
||||
pub(crate) android_app: AndroidApp,
|
||||
window_target: event_loop::ActiveEventLoop,
|
||||
redraw_flag: SharedFlag,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
loop_running: bool, // Dispatched `NewEvents<Init>`
|
||||
user_events_sender: mpsc::Sender<T>,
|
||||
user_events_receiver: PeekableReceiver<T>, // must wake looper whenever something gets sent
|
||||
loop_running: bool, // Dispatched `NewEvents<Init>`
|
||||
running: bool,
|
||||
pending_redraw: bool,
|
||||
cause: StartCause,
|
||||
@@ -122,11 +157,11 @@ impl Default for PlatformSpecificEventLoopAttributes {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub(crate) fn new(
|
||||
attributes: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<Self, EventLoopError> {
|
||||
let proxy_wake_up = Arc::new(AtomicBool::new(false));
|
||||
let (user_events_sender, user_events_receiver) = mpsc::channel();
|
||||
|
||||
let android_app = attributes.android_app.as_ref().expect(
|
||||
"An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on \
|
||||
@@ -149,7 +184,8 @@ impl EventLoop {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
redraw_flag,
|
||||
proxy_wake_up,
|
||||
user_events_sender,
|
||||
user_events_receiver: PeekableReceiver::from_recv(user_events_receiver),
|
||||
loop_running: false,
|
||||
running: false,
|
||||
pending_redraw: false,
|
||||
@@ -159,28 +195,27 @@ impl EventLoop {
|
||||
})
|
||||
}
|
||||
|
||||
fn single_iteration<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
main_event: Option<MainEvent<'_>>,
|
||||
app: &mut A,
|
||||
) {
|
||||
fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootAEL),
|
||||
{
|
||||
trace!("Mainloop iteration");
|
||||
|
||||
let cause = self.cause;
|
||||
let mut pending_redraw = self.pending_redraw;
|
||||
let mut resized = false;
|
||||
|
||||
app.new_events(self.window_target(), cause);
|
||||
callback(event::Event::NewEvents(cause), self.window_target());
|
||||
|
||||
if let Some(event) = main_event {
|
||||
trace!("Handling main event {:?}", event);
|
||||
|
||||
match event {
|
||||
MainEvent::InitWindow { .. } => {
|
||||
app.resumed(self.window_target());
|
||||
callback(event::Event::Resumed, self.window_target());
|
||||
},
|
||||
MainEvent::TerminateWindow { .. } => {
|
||||
app.suspended(self.window_target());
|
||||
callback(event::Event::Suspended, self.window_target());
|
||||
},
|
||||
MainEvent::WindowResized { .. } => resized = true,
|
||||
MainEvent::RedrawNeeded { .. } => pending_redraw = true,
|
||||
@@ -189,15 +224,23 @@ 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(), window_id, event);
|
||||
callback(
|
||||
event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Focused(true),
|
||||
},
|
||||
self.window_target(),
|
||||
);
|
||||
},
|
||||
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(), window_id, event);
|
||||
callback(
|
||||
event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Focused(false),
|
||||
},
|
||||
self.window_target(),
|
||||
);
|
||||
},
|
||||
MainEvent::ConfigChanged { .. } => {
|
||||
let monitor = MonitorHandle::new(self.android_app.clone());
|
||||
@@ -207,19 +250,20 @@ impl EventLoop {
|
||||
let new_inner_size = Arc::new(Mutex::new(
|
||||
MonitorHandle::new(self.android_app.clone()).size(),
|
||||
));
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::ScaleFactorChanged {
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&new_inner_size,
|
||||
)),
|
||||
scale_factor,
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::ScaleFactorChanged {
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&new_inner_size,
|
||||
)),
|
||||
scale_factor,
|
||||
},
|
||||
};
|
||||
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
callback(event, self.window_target());
|
||||
}
|
||||
},
|
||||
MainEvent::LowMemory => {
|
||||
app.memory_warning(self.window_target());
|
||||
callback(event::Event::MemoryWarning, self.window_target());
|
||||
},
|
||||
MainEvent::Start => {
|
||||
// XXX: how to forward this state to applications?
|
||||
@@ -267,7 +311,7 @@ impl EventLoop {
|
||||
match android_app.input_events_iter() {
|
||||
Ok(mut input_iter) => loop {
|
||||
let read_event =
|
||||
input_iter.next(|event| self.handle_input_event(&android_app, event, app));
|
||||
input_iter.next(|event| self.handle_input_event(&android_app, event, callback));
|
||||
|
||||
if !read_event {
|
||||
break;
|
||||
@@ -278,8 +322,11 @@ impl EventLoop {
|
||||
},
|
||||
}
|
||||
|
||||
if self.proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||
app.proxy_wake_up(self.window_target());
|
||||
// Empty the user event buffer
|
||||
{
|
||||
while let Ok(event) = self.user_events_receiver.try_recv() {
|
||||
callback(crate::event::Event::UserEvent(event), self.window_target());
|
||||
}
|
||||
}
|
||||
|
||||
if self.running {
|
||||
@@ -291,32 +338,39 @@ impl EventLoop {
|
||||
} else {
|
||||
PhysicalSize::new(0, 0)
|
||||
};
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::Resized(size);
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Resized(size),
|
||||
};
|
||||
callback(event, self.window_target());
|
||||
}
|
||||
|
||||
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(), window_id, event);
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::RedrawRequested,
|
||||
};
|
||||
callback(event, self.window_target());
|
||||
}
|
||||
}
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
app.about_to_wait(self.window_target());
|
||||
callback(event::Event::AboutToWait, self.window_target());
|
||||
|
||||
self.pending_redraw = pending_redraw;
|
||||
}
|
||||
|
||||
fn handle_input_event<A: ApplicationHandler>(
|
||||
fn handle_input_event<F>(
|
||||
&mut self,
|
||||
android_app: &AndroidApp,
|
||||
event: &InputEvent<'_>,
|
||||
app: &mut A,
|
||||
) -> InputStatus {
|
||||
callback: &mut F,
|
||||
) -> InputStatus
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootAEL),
|
||||
{
|
||||
let mut input_status = InputStatus::Handled;
|
||||
match event {
|
||||
InputEvent::MotionEvent(motion_event) => {
|
||||
@@ -354,16 +408,17 @@ impl EventLoop {
|
||||
"Input event {device_id:?}, {phase:?}, loc={location:?}, \
|
||||
pointer={pointer:?}"
|
||||
);
|
||||
|
||||
let event = event::WindowEvent::Touch(event::Touch {
|
||||
device_id,
|
||||
phase,
|
||||
location,
|
||||
id: pointer.pointer_id() as u64,
|
||||
force: Some(Force::Normalized(pointer.pressure() as f64)),
|
||||
});
|
||||
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id,
|
||||
event: event::WindowEvent::Touch(event::Touch {
|
||||
device_id,
|
||||
phase,
|
||||
location,
|
||||
id: pointer.pointer_id() as u64,
|
||||
force: Some(Force::Normalized(pointer.pressure() as f64)),
|
||||
}),
|
||||
};
|
||||
callback(event, self.window_target());
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -391,22 +446,23 @@ impl EventLoop {
|
||||
&mut self.combining_accent,
|
||||
);
|
||||
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId(key.device_id())),
|
||||
event: event::KeyEvent {
|
||||
state,
|
||||
physical_key: keycodes::to_physical_key(keycode),
|
||||
logical_key: keycodes::to_logical(key_char, keycode),
|
||||
location: keycodes::to_location(keycode),
|
||||
repeat: key.repeat_count() > 0,
|
||||
text: None,
|
||||
platform_specific: KeyEventExtra {},
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId(key.device_id())),
|
||||
event: event::KeyEvent {
|
||||
state,
|
||||
physical_key: keycodes::to_physical_key(keycode),
|
||||
logical_key: keycodes::to_logical(key_char, keycode),
|
||||
location: keycodes::to_location(keycode),
|
||||
repeat: key.repeat_count() > 0,
|
||||
text: None,
|
||||
platform_specific: KeyEventExtra {},
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
is_synthetic: false,
|
||||
};
|
||||
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
callback(event, self.window_target());
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -418,16 +474,19 @@ impl EventLoop {
|
||||
input_status
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(event::Event<T>, &event_loop::ActiveEventLoop),
|
||||
{
|
||||
self.run_on_demand(event_handler)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(event::Event<T>, &event_loop::ActiveEventLoop),
|
||||
{
|
||||
loop {
|
||||
match self.pump_app_events(None, app) {
|
||||
match self.pump_events(None, &mut event_handler) {
|
||||
PumpStatus::Exit(0) => {
|
||||
break Ok(());
|
||||
},
|
||||
@@ -441,11 +500,10 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootAEL),
|
||||
{
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
@@ -456,18 +514,18 @@ impl EventLoop {
|
||||
self.cause = StartCause::Init;
|
||||
|
||||
// run the initial loop iteration
|
||||
self.single_iteration(None, app);
|
||||
self.single_iteration(None, &mut callback);
|
||||
}
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, app);
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
}
|
||||
if self.exiting() {
|
||||
self.loop_running = false;
|
||||
|
||||
app.exiting(self.window_target());
|
||||
callback(event::Event::LoopExiting, self.window_target());
|
||||
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
@@ -475,34 +533,32 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_events_with_timeout<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) {
|
||||
fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootAEL),
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
||||
self.pending_redraw |= self.redraw_flag.get_and_reset();
|
||||
|
||||
timeout = if self.running
|
||||
&& (self.pending_redraw || self.proxy_wake_up.load(Ordering::Relaxed))
|
||||
{
|
||||
// If we already have work to do then we don't want to block on the next poll
|
||||
Some(Duration::ZERO)
|
||||
} else {
|
||||
let control_flow_timeout = match self.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
Some(wait_deadline.saturating_duration_since(start))
|
||||
},
|
||||
timeout =
|
||||
if self.running && (self.pending_redraw || self.user_events_receiver.has_incoming()) {
|
||||
// If we already have work to do then we don't want to block on the next poll
|
||||
Some(Duration::ZERO)
|
||||
} else {
|
||||
let control_flow_timeout = match self.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
Some(wait_deadline.saturating_duration_since(start))
|
||||
},
|
||||
};
|
||||
|
||||
min_timeout(control_flow_timeout, timeout)
|
||||
};
|
||||
|
||||
min_timeout(control_flow_timeout, timeout)
|
||||
};
|
||||
|
||||
let android_app = self.android_app.clone(); // Don't borrow self as part of poll expression
|
||||
android_app.poll_events(timeout, |poll_event| {
|
||||
let app = self.android_app.clone(); // Don't borrow self as part of poll expression
|
||||
app.poll_events(timeout, |poll_event| {
|
||||
let mut main_event = None;
|
||||
|
||||
match poll_event {
|
||||
@@ -517,7 +573,7 @@ impl EventLoop {
|
||||
// We also ignore wake ups while suspended.
|
||||
self.pending_redraw |= self.redraw_flag.get_and_reset();
|
||||
if !self.running
|
||||
|| (!self.pending_redraw && !self.proxy_wake_up.load(Ordering::Relaxed))
|
||||
|| (!self.pending_redraw && !self.user_events_receiver.has_incoming())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -543,7 +599,7 @@ impl EventLoop {
|
||||
},
|
||||
};
|
||||
|
||||
self.single_iteration(main_event, app);
|
||||
self.single_iteration(main_event, &mut callback);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -551,9 +607,9 @@ impl EventLoop {
|
||||
&self.window_target
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy {
|
||||
proxy_wake_up: self.proxy_wake_up.clone(),
|
||||
user_events_sender: self.user_events_sender.clone(),
|
||||
waker: self.android_app.create_waker(),
|
||||
}
|
||||
}
|
||||
@@ -567,21 +623,30 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
user_events_sender: mpsc::Sender<T>,
|
||||
waker: AndroidAppWaker,
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
self.proxy_wake_up.store(true, Ordering::Relaxed);
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
EventLoopProxy {
|
||||
user_events_sender: self.user_events_sender.clone(),
|
||||
waker: self.waker.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed<T>> {
|
||||
self.user_events_sender.send(event).map_err(|err| event_loop::EventLoopClosed(err.0))?;
|
||||
self.waker.wake();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ActiveEventLoop {
|
||||
app: AndroidApp,
|
||||
pub(crate) app: AndroidApp,
|
||||
control_flow: Cell<ControlFlow>,
|
||||
exit: Cell<bool>,
|
||||
redraw_requester: RedrawRequester,
|
||||
@@ -612,6 +677,11 @@ impl ActiveEventLoop {
|
||||
rwh_05::RawDisplayHandle::Android(rwh_05::AndroidDisplayHandle::empty())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn system_theme(&self) -> Option<Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
@@ -838,7 +908,13 @@ impl Window {
|
||||
|
||||
pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
|
||||
|
||||
pub fn set_ime_allowed(&self, _allowed: bool) {}
|
||||
pub fn set_ime_allowed(&self, allowed: bool) {
|
||||
if allowed {
|
||||
self.app.show_soft_input(true);
|
||||
} else {
|
||||
self.app.hide_soft_input(true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![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};
|
||||
@@ -7,8 +8,12 @@ use objc2_foundation::{MainThreadBound, MainThreadMarker, NSObject};
|
||||
|
||||
use super::event_loop::ActiveEventLoop;
|
||||
use super::window_delegate::WindowDelegate;
|
||||
use crate::error::OsError as RootOsError;
|
||||
use crate::window::WindowAttributes;
|
||||
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,
|
||||
};
|
||||
|
||||
pub(crate) struct Window {
|
||||
window: MainThreadBound<Retained<WinitWindow>>,
|
||||
@@ -16,32 +21,20 @@ 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, RootOsError> {
|
||||
) -> Result<Self, RequestError> {
|
||||
let mtm = window_target.mtm;
|
||||
let delegate = autoreleasepool(|_| {
|
||||
WindowDelegate::new(window_target.app_delegate(), attributes, mtm)
|
||||
})?;
|
||||
let delegate =
|
||||
autoreleasepool(|_| WindowDelegate::new(&window_target.app_state, 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,
|
||||
@@ -70,24 +63,274 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(pub usize);
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
// Restore the video mode.
|
||||
if matches!(self.fullscreen(), Some(Fullscreen::Exclusive(_))) {
|
||||
self.set_fullscreen(None);
|
||||
}
|
||||
|
||||
impl WindowId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
Self(0)
|
||||
self.window.get_on_main(|window| autoreleasepool(|_| window.close()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WindowId> for u64 {
|
||||
fn from(window_id: WindowId) -> Self {
|
||||
window_id.0 as u64
|
||||
#[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<u64> for WindowId {
|
||||
fn from(raw_id: u64) -> Self {
|
||||
Self(raw_id as usize)
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +364,6 @@ declare_class!(
|
||||
|
||||
impl WinitWindow {
|
||||
pub(super) fn id(&self) -> WindowId {
|
||||
WindowId(self as *const Self as usize)
|
||||
WindowId::from_raw(self as *const Self as usize)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
//! Apple/Darwin-specific implementations
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod appkit;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
mod uikit;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use self::appkit::*;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub use self::uikit::*;
|
||||
@@ -10,7 +10,7 @@ use std::{fmt, mem, ptr};
|
||||
use core_foundation::base::CFRelease;
|
||||
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
||||
use core_foundation::runloop::{
|
||||
kCFRunLoopDefaultMode, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
||||
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
||||
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
||||
};
|
||||
use objc2::rc::Retained;
|
||||
@@ -40,9 +40,12 @@ macro_rules! bug_assert {
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct HandlePendingUserEvents;
|
||||
|
||||
pub(crate) struct EventLoopHandler {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) handler: Box<dyn FnMut(Event, &RootActiveEventLoop)>,
|
||||
pub(crate) handler: Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop)>,
|
||||
pub(crate) event_loop: RootActiveEventLoop,
|
||||
}
|
||||
|
||||
@@ -56,14 +59,14 @@ impl fmt::Debug for EventLoopHandler {
|
||||
}
|
||||
|
||||
impl EventLoopHandler {
|
||||
fn handle_event(&mut self, event: Event) {
|
||||
fn handle_event(&mut self, event: Event<HandlePendingUserEvents>) {
|
||||
(self.handler)(event, &self.event_loop)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum EventWrapper {
|
||||
StaticEvent(Event),
|
||||
StaticEvent(Event<HandlePendingUserEvents>),
|
||||
ScaleFactorChanged(ScaleFactorChanged),
|
||||
}
|
||||
|
||||
@@ -85,7 +88,7 @@ enum UserCallbackTransitionResult<'a> {
|
||||
},
|
||||
}
|
||||
|
||||
impl Event {
|
||||
impl Event<HandlePendingUserEvents> {
|
||||
fn is_redraw(&self) -> bool {
|
||||
matches!(self, Event::WindowEvent { event: WindowEvent::RedrawRequested, .. })
|
||||
}
|
||||
@@ -144,6 +147,8 @@ 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)]
|
||||
@@ -622,7 +627,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||
}
|
||||
drop(this);
|
||||
|
||||
handler.handle_event(Event::UserWakeUp);
|
||||
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
||||
|
||||
loop {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
@@ -655,7 +660,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||
}
|
||||
}
|
||||
|
||||
handler.handle_event(Event::UserWakeUp);
|
||||
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,7 +807,7 @@ impl EventLoopWaker {
|
||||
wakeup_main_loop,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
CFRunLoopAddTimer(rl, timer, kCFRunLoopDefaultMode);
|
||||
CFRunLoopAddTimer(rl, timer, kCFRunLoopCommonModes);
|
||||
|
||||
EventLoopWaker { timer }
|
||||
}
|
||||
@@ -2,27 +2,28 @@ use std::collections::VecDeque;
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
|
||||
use core_foundation::base::{CFIndex, CFRelease};
|
||||
use core_foundation::runloop::{
|
||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopDefaultMode, kCFRunLoopExit,
|
||||
CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
|
||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopDefaultMode,
|
||||
kCFRunLoopExit, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
|
||||
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
||||
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
use objc2_foundation::{MainThreadMarker, NSString};
|
||||
use objc2_ui_kit::{UIApplication, UIApplicationMain, UIScreen};
|
||||
use objc2_ui_kit::{UIApplication, UIApplicationMain, UIDevice, UIScreen, UIUserInterfaceIdiom};
|
||||
|
||||
use super::app_state::EventLoopHandler;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
|
||||
use crate::window::{CustomCursor, CustomCursorSource};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||
};
|
||||
use crate::platform::ios::Idiom;
|
||||
use crate::platform_impl::ios::app_state::{EventLoopHandler, HandlePendingUserEvents};
|
||||
use crate::window::{CustomCursor, CustomCursorSource, Theme};
|
||||
|
||||
use super::app_delegate::AppDelegate;
|
||||
use super::app_state::AppState;
|
||||
@@ -57,6 +58,11 @@ impl ActiveEventLoop {
|
||||
rwh_05::RawDisplayHandle::UiKit(rwh_05::UiKitDisplayHandle::empty())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn system_theme(&self) -> Option<Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
@@ -107,44 +113,34 @@ impl OwnedDisplayHandle {
|
||||
}
|
||||
}
|
||||
|
||||
fn map_user_event<A: ApplicationHandler>(
|
||||
app: &mut A,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
) -> impl FnMut(Event, &RootActiveEventLoop) + '_ {
|
||||
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);
|
||||
fn map_user_event<T: 'static>(
|
||||
mut handler: impl FnMut(Event<T>, &RootActiveEventLoop),
|
||||
receiver: mpsc::Receiver<T>,
|
||||
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) {
|
||||
move |event, window_target| match event.map_nonuser_event() {
|
||||
Ok(event) => (handler)(event, window_target),
|
||||
Err(_) => {
|
||||
for event in receiver.try_iter() {
|
||||
(handler)(Event::UserEvent(event), window_target);
|
||||
}
|
||||
},
|
||||
Event::Suspended => app.suspended(window_target),
|
||||
Event::Resumed => app.resumed(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 {
|
||||
pub struct EventLoop<T: 'static> {
|
||||
mtm: MainThreadMarker,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
sender: Sender<T>,
|
||||
receiver: Receiver<T>,
|
||||
window_target: RootActiveEventLoop,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
impl EventLoop {
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub(crate) fn new(
|
||||
_: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<EventLoop, EventLoopError> {
|
||||
) -> Result<EventLoop<T>, EventLoopError> {
|
||||
let mtm = MainThreadMarker::new()
|
||||
.expect("On iOS, `EventLoop` must be created on the main thread");
|
||||
|
||||
@@ -157,19 +153,23 @@ impl EventLoop {
|
||||
SINGLETON_INIT = true;
|
||||
}
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
||||
// this line sets up the main run loop before `UIApplicationMain`
|
||||
setup_control_flow_observers();
|
||||
|
||||
let proxy_wake_up = Arc::new(AtomicBool::new(false));
|
||||
|
||||
Ok(EventLoop {
|
||||
mtm,
|
||||
proxy_wake_up,
|
||||
sender,
|
||||
receiver,
|
||||
window_target: RootActiveEventLoop { p: ActiveEventLoop { mtm }, _marker: PhantomData },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(self, app: &mut A) -> ! {
|
||||
pub fn run<F>(self, handler: F) -> !
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
let application: Option<Retained<UIApplication>> =
|
||||
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
|
||||
assert!(
|
||||
@@ -179,12 +179,12 @@ impl EventLoop {
|
||||
`EventLoop::run_app` calls `UIApplicationMain` on iOS",
|
||||
);
|
||||
|
||||
let handler = map_user_event(app, self.proxy_wake_up.clone());
|
||||
let handler = map_user_event(handler, self.receiver);
|
||||
|
||||
let handler = unsafe {
|
||||
std::mem::transmute::<
|
||||
Box<dyn FnMut(Event, &RootActiveEventLoop)>,
|
||||
Box<dyn FnMut(Event, &RootActiveEventLoop)>,
|
||||
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop)>,
|
||||
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop)>,
|
||||
>(Box::new(handler))
|
||||
};
|
||||
|
||||
@@ -212,8 +212,8 @@ impl EventLoop {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
EventLoopProxy::new(self.proxy_wake_up.clone())
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.sender.clone())
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &RootActiveEventLoop {
|
||||
@@ -221,21 +221,35 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy {
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
source: CFRunLoopSourceRef,
|
||||
}
|
||||
|
||||
unsafe impl Send for EventLoopProxy {}
|
||||
unsafe impl Sync for EventLoopProxy {}
|
||||
|
||||
impl Clone for EventLoopProxy {
|
||||
fn clone(&self) -> EventLoopProxy {
|
||||
EventLoopProxy::new(self.proxy_wake_up.clone())
|
||||
// EventLoopExtIOS
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn idiom(&self) -> Idiom {
|
||||
match UIDevice::currentDevice(self.mtm).userInterfaceIdiom() {
|
||||
UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified,
|
||||
UIUserInterfaceIdiom::Phone => Idiom::Phone,
|
||||
UIUserInterfaceIdiom::Pad => Idiom::Pad,
|
||||
UIUserInterfaceIdiom::TV => Idiom::TV,
|
||||
UIUserInterfaceIdiom::CarPlay => Idiom::CarPlay,
|
||||
_ => Idiom::Unspecified,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventLoopProxy {
|
||||
pub struct EventLoopProxy<T> {
|
||||
sender: Sender<T>,
|
||||
source: CFRunLoopSourceRef,
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for EventLoopProxy<T> {}
|
||||
unsafe impl<T: Send> Sync for EventLoopProxy<T> {}
|
||||
|
||||
impl<T> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.sender.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for EventLoopProxy<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
CFRunLoopSourceInvalidate(self.source);
|
||||
@@ -244,8 +258,8 @@ impl Drop for EventLoopProxy {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
fn new(proxy_wake_up: Arc<AtomicBool>) -> EventLoopProxy {
|
||||
impl<T> EventLoopProxy<T> {
|
||||
fn new(sender: Sender<T>) -> EventLoopProxy<T> {
|
||||
unsafe {
|
||||
// just wake up the eventloop
|
||||
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
|
||||
@@ -266,21 +280,22 @@ impl EventLoopProxy {
|
||||
perform: event_loop_proxy_handler,
|
||||
};
|
||||
let source = CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::MAX - 1, &mut context);
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopDefaultMode);
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||
CFRunLoopWakeUp(rl);
|
||||
|
||||
EventLoopProxy { proxy_wake_up, source }
|
||||
EventLoopProxy { sender, source }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wake_up(&self) {
|
||||
self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.sender.send(event).map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?;
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
CFRunLoopSourceSignal(self.source);
|
||||
let rl = CFRunLoopGetMain();
|
||||
CFRunLoopWakeUp(rl);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ pub(crate) use crate::platform_impl::Fullscreen;
|
||||
pub struct DeviceId;
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,9 @@ use objc2::Message;
|
||||
use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker, NSInteger};
|
||||
use objc2_ui_kit::{UIScreen, UIScreenMode};
|
||||
|
||||
use super::app_state;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::monitor::VideoModeHandle as RootVideoModeHandle;
|
||||
use crate::platform_impl::platform::app_state;
|
||||
|
||||
// Workaround for `MainThreadBound` implementing almost no traits
|
||||
#[derive(Debug)]
|
||||
@@ -102,13 +102,20 @@ impl Clone for MonitorHandle {
|
||||
|
||||
impl hash::Hash for MonitorHandle {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
(self as *const Self).hash(state);
|
||||
// SAFETY: Only getting the pointer.
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
Retained::as_ptr(self.ui_screen.get(mtm)).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for MonitorHandle {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
ptr::eq(self, other)
|
||||
// SAFETY: Only getting the pointer.
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
ptr::eq(
|
||||
Retained::as_ptr(self.ui_screen.get(mtm)),
|
||||
Retained::as_ptr(other.ui_screen.get(mtm)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +129,10 @@ impl PartialOrd for MonitorHandle {
|
||||
|
||||
impl Ord for MonitorHandle {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
// SAFETY: Only getting the pointer.
|
||||
// TODO: Make a better ordering
|
||||
(self as *const Self).cmp(&(other as *const Self))
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
Retained::as_ptr(self.ui_screen.get(mtm)).cmp(&Retained::as_ptr(other.ui_screen.get(mtm)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,3 +251,27 @@ pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
||||
#[allow(deprecated)]
|
||||
UIScreen::screens(mtm).into_iter().map(MonitorHandle::new).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use objc2_foundation::NSSet;
|
||||
|
||||
use super::*;
|
||||
|
||||
// Test that UIScreen pointer comparisons are correct.
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn screen_comparisons() {
|
||||
// Test code, doesn't matter that it's not thread safe
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
|
||||
assert!(ptr::eq(&*UIScreen::mainScreen(mtm), &*UIScreen::mainScreen(mtm)));
|
||||
|
||||
let main = UIScreen::mainScreen(mtm);
|
||||
assert!(UIScreen::screens(mtm).iter().any(|screen| ptr::eq(screen, &*main)));
|
||||
|
||||
assert!(unsafe {
|
||||
NSSet::setWithArray(&UIScreen::screens(mtm)).containsObject(&UIScreen::mainScreen(mtm))
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4,19 +4,21 @@ 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};
|
||||
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet, NSString};
|
||||
use objc2_ui_kit::{
|
||||
UICoordinateSpace, UIEvent, UIForceTouchCapability, UIGestureRecognizer,
|
||||
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIPanGestureRecognizer,
|
||||
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer,
|
||||
UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer,
|
||||
UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||
UITextInputTraits, UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||
};
|
||||
|
||||
use super::app_state::{self, EventWrapper};
|
||||
use super::window::WinitUIWindow;
|
||||
use super::DEVICE_ID;
|
||||
use crate::dpi::PhysicalPosition;
|
||||
use crate::event::{Event, Force, Touch, TouchPhase, WindowEvent};
|
||||
use crate::event::{ElementState, Event, Force, KeyEvent, Touch, TouchPhase, WindowEvent};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
|
||||
use crate::platform_impl::platform::DEVICE_ID;
|
||||
use crate::platform_impl::KeyEventExtra;
|
||||
use crate::window::{WindowAttributes, WindowId as RootWindowId};
|
||||
|
||||
pub struct WinitViewState {
|
||||
@@ -314,6 +316,11 @@ 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 {}
|
||||
@@ -324,6 +331,26 @@ 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 {
|
||||
@@ -512,4 +539,69 @@ impl WinitView {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(mtm, touch_events);
|
||||
}
|
||||
|
||||
fn handle_insert_text(&self, text: &NSString) {
|
||||
let window = self.window().unwrap();
|
||||
let window_id = RootWindowId(window.id());
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
// send individual events for each character
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
text.to_string().chars().flat_map(|c| {
|
||||
let text = smol_str::SmolStr::from_iter([c]);
|
||||
// Emit both press and release events
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
event: KeyEvent {
|
||||
text: if state == ElementState::Pressed {
|
||||
Some(text.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
state,
|
||||
location: KeyLocation::Standard,
|
||||
repeat: false,
|
||||
logical_key: Key::Character(text.clone()),
|
||||
physical_key: PhysicalKey::Unidentified(
|
||||
NativeKeyCode::Unidentified,
|
||||
),
|
||||
platform_specific: KeyEventExtra {},
|
||||
},
|
||||
is_synthetic: false,
|
||||
device_id: DEVICE_ID,
|
||||
},
|
||||
})
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn handle_delete_backward(&self) {
|
||||
let window = self.window().unwrap();
|
||||
let window_id = RootWindowId(window.id());
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event: KeyEvent {
|
||||
state,
|
||||
logical_key: Key::Named(NamedKey::Backspace),
|
||||
physical_key: PhysicalKey::Code(KeyCode::Backspace),
|
||||
platform_specific: KeyEventExtra {},
|
||||
repeat: false,
|
||||
location: KeyLocation::Standard,
|
||||
text: None,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,15 @@ use tracing::{debug, warn};
|
||||
use super::app_state::EventWrapper;
|
||||
use super::view::WinitView;
|
||||
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::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event::{Event, WindowEvent};
|
||||
use crate::icon::Icon;
|
||||
use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations};
|
||||
use crate::platform_impl::platform::{
|
||||
app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle,
|
||||
};
|
||||
use crate::window::{
|
||||
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
|
||||
WindowButtons, WindowId as RootWindowId, WindowLevel,
|
||||
@@ -365,12 +367,24 @@ impl Inner {
|
||||
warn!("`Window::set_ime_cursor_area` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_ime_allowed(&self, _allowed: bool) {
|
||||
warn!("`Window::set_ime_allowed` 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_purpose(&self, _purpose: ImePurpose) {
|
||||
warn!("`Window::set_ime_allowed` is ignored on iOS")
|
||||
warn!("`Window::set_ime_purpose` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn focus_window(&self) {
|
||||
@@ -699,7 +713,7 @@ pub struct WindowId {
|
||||
}
|
||||
|
||||
impl WindowId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
WindowId { window: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
@@ -184,7 +184,7 @@ pub struct KeyContext<'a> {
|
||||
scratch_buffer: &'a mut Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a> KeyContext<'a> {
|
||||
impl KeyContext<'_> {
|
||||
pub fn process_key_event(
|
||||
&mut self,
|
||||
keycode: u32,
|
||||
|
||||
@@ -11,8 +11,6 @@ use std::{env, fmt};
|
||||
#[cfg(x11_platform)]
|
||||
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::utils::Lazy;
|
||||
use smol_str::SmolStr;
|
||||
@@ -21,9 +19,12 @@ use smol_str::SmolStr;
|
||||
use self::x11::{X11Error, XConnection, XError, XNotSupported};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event_loop::{AsyncRequestSerial, ControlFlow, DeviceEvents};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootELW, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||
};
|
||||
use crate::icon::Icon;
|
||||
use crate::keyboard::Key;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
|
||||
use crate::window::{
|
||||
@@ -116,6 +117,8 @@ pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported
|
||||
pub enum OsError {
|
||||
Misc(&'static str),
|
||||
#[cfg(x11_platform)]
|
||||
XNotSupported(XNotSupported),
|
||||
#[cfg(x11_platform)]
|
||||
XError(Arc<X11Error>),
|
||||
#[cfg(wayland_platform)]
|
||||
WaylandError(Arc<wayland::WaylandError>),
|
||||
@@ -126,6 +129,8 @@ impl fmt::Display for OsError {
|
||||
match *self {
|
||||
OsError::Misc(e) => _f.pad(e),
|
||||
#[cfg(x11_platform)]
|
||||
OsError::XNotSupported(ref e) => fmt::Display::fmt(e, _f),
|
||||
#[cfg(x11_platform)]
|
||||
OsError::XError(ref e) => fmt::Display::fmt(e, _f),
|
||||
#[cfg(wayland_platform)]
|
||||
OsError::WaylandError(ref e) => fmt::Display::fmt(e, _f),
|
||||
@@ -156,7 +161,7 @@ impl From<u64> for WindowId {
|
||||
}
|
||||
|
||||
impl WindowId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
@@ -170,11 +175,11 @@ pub enum DeviceId {
|
||||
}
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
#[cfg(wayland_platform)]
|
||||
return DeviceId::Wayland(unsafe { wayland::DeviceId::dummy() });
|
||||
return DeviceId::Wayland(wayland::DeviceId::dummy());
|
||||
#[cfg(all(not(wayland_platform), x11_platform))]
|
||||
return DeviceId::X(unsafe { x11::DeviceId::dummy() });
|
||||
return DeviceId::X(x11::DeviceId::dummy());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,18 +647,18 @@ pub(crate) enum PlatformCustomCursor {
|
||||
|
||||
/// Hooks for X11 errors.
|
||||
#[cfg(x11_platform)]
|
||||
pub(crate) static mut XLIB_ERROR_HOOKS: Mutex<Vec<XlibErrorHook>> = Mutex::new(Vec::new());
|
||||
pub(crate) static 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();
|
||||
let xconn_lock = X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner());
|
||||
if let Ok(ref xconn) = *xconn_lock {
|
||||
// Call all the hooks.
|
||||
let mut error_handled = false;
|
||||
for hook in unsafe { XLIB_ERROR_HOOKS.lock() }.unwrap().iter() {
|
||||
for hook in XLIB_ERROR_HOOKS.lock().unwrap().iter() {
|
||||
error_handled |= hook(display as *mut _, event as *mut _);
|
||||
}
|
||||
|
||||
@@ -691,22 +696,27 @@ unsafe extern "C" fn x_error_callback(
|
||||
0
|
||||
}
|
||||
|
||||
pub enum EventLoop {
|
||||
pub enum EventLoop<T: 'static> {
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(Box<wayland::EventLoop>),
|
||||
Wayland(Box<wayland::EventLoop<T>>),
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::EventLoop),
|
||||
X(x11::EventLoop<T>),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum EventLoopProxy {
|
||||
pub enum EventLoopProxy<T: 'static> {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::EventLoopProxy),
|
||||
X(x11::EventLoopProxy<T>),
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(wayland::EventLoopProxy),
|
||||
Wayland(wayland::EventLoopProxy<T>),
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.clone(); as EventLoopProxy)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub(crate) fn new(
|
||||
attributes: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<Self, EventLoopError> {
|
||||
@@ -765,15 +775,17 @@ impl EventLoop {
|
||||
}
|
||||
|
||||
#[cfg(wayland_platform)]
|
||||
fn new_wayland_any_thread() -> Result<EventLoop, EventLoopError> {
|
||||
fn new_wayland_any_thread() -> Result<EventLoop<T>, EventLoopError> {
|
||||
wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp)))
|
||||
}
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
fn new_x11_any_thread() -> Result<EventLoop, EventLoopError> {
|
||||
let xconn = match X11_BACKEND.lock().unwrap().as_ref() {
|
||||
fn new_x11_any_thread() -> Result<EventLoop<T>, EventLoopError> {
|
||||
let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
|
||||
Ok(xconn) => xconn.clone(),
|
||||
Err(_) => return Err(EventLoopError::NotSupported(NotSupportedError::new())),
|
||||
Err(err) => {
|
||||
return Err(EventLoopError::Os(os_error!(OsError::XNotSupported(err.clone()))))
|
||||
},
|
||||
};
|
||||
|
||||
Ok(EventLoop::X(x11::EventLoop::new(xconn)))
|
||||
@@ -789,27 +801,29 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app))
|
||||
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW),
|
||||
{
|
||||
self.run_on_demand(callback)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app_on_demand(app))
|
||||
pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW),
|
||||
{
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(callback))
|
||||
}
|
||||
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_app_events(timeout, app))
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW),
|
||||
{
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &crate::event_loop::ActiveEventLoop {
|
||||
@@ -817,21 +831,21 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsFd for EventLoop {
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_fd())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for EventLoop {
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.wake_up())
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.send_event(event))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -889,6 +903,11 @@ impl ActiveEventLoop {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_rwh_05())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn system_theme(&self) -> Option<Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::io::Result as IOResult;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -13,7 +14,6 @@ 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 as RootOsError};
|
||||
@@ -39,7 +39,7 @@ use super::{logical_to_physical_rounded, DeviceId, WaylandError, WindowId};
|
||||
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
|
||||
|
||||
/// The Wayland event loop.
|
||||
pub struct EventLoop {
|
||||
pub struct EventLoop<T: 'static> {
|
||||
/// Has `run` or `run_on_demand` been called or a call to `pump_events` that starts the loop
|
||||
loop_running: bool,
|
||||
|
||||
@@ -47,8 +47,13 @@ pub struct EventLoop {
|
||||
compositor_updates: Vec<WindowCompositorUpdate>,
|
||||
window_ids: Vec<WindowId>,
|
||||
|
||||
/// Event loop proxy
|
||||
event_loop_proxy: EventLoopProxy,
|
||||
/// Sender of user events.
|
||||
user_events_sender: calloop::channel::Sender<T>,
|
||||
|
||||
// XXX can't remove RefCell out of here, unless we can plumb generics into the `Window`, which
|
||||
// we don't really want, since it'll break public API by a lot.
|
||||
/// Pending events from the user.
|
||||
pending_user_events: Rc<RefCell<Vec<T>>>,
|
||||
|
||||
/// The Wayland dispatcher to has raw access to the queue when needed, such as
|
||||
/// when creating a new window.
|
||||
@@ -65,8 +70,8 @@ pub struct EventLoop {
|
||||
event_loop: calloop::EventLoop<'static, WinitState>,
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
pub fn new() -> Result<EventLoop, EventLoopError> {
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn new() -> Result<EventLoop<T>, EventLoopError> {
|
||||
macro_rules! map_err {
|
||||
($e:expr, $err:expr) => {
|
||||
$e.map_err(|error| os_error!($err(error).into()))
|
||||
@@ -109,12 +114,16 @@ impl EventLoop {
|
||||
)?;
|
||||
|
||||
// Setup the user proxy.
|
||||
let (ping, ping_source) = calloop::ping::make_ping().unwrap();
|
||||
let pending_user_events = Rc::new(RefCell::new(Vec::new()));
|
||||
let pending_user_events_clone = pending_user_events.clone();
|
||||
let (user_events_sender, user_events_channel) = calloop::channel::channel();
|
||||
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;
|
||||
.insert_source(user_events_channel, move |event, _, winit_state: &mut WinitState| {
|
||||
if let calloop::channel::Event::Msg(msg) = event {
|
||||
winit_state.dispatched_events = true;
|
||||
pending_user_events_clone.borrow_mut().push(msg);
|
||||
}
|
||||
})
|
||||
.map_err(|error| error.error);
|
||||
map_err!(result, WaylandError::Calloop)?;
|
||||
@@ -152,7 +161,8 @@ impl EventLoop {
|
||||
window_ids: Vec::new(),
|
||||
connection,
|
||||
wayland_dispatcher,
|
||||
event_loop_proxy: EventLoopProxy::new(ping),
|
||||
user_events_sender,
|
||||
pending_user_events,
|
||||
event_loop,
|
||||
window_target: RootActiveEventLoop {
|
||||
p: PlatformActiveEventLoop::Wayland(window_target),
|
||||
@@ -163,16 +173,12 @@ impl EventLoop {
|
||||
Ok(event_loop)
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
let exit = loop {
|
||||
match self.pump_app_events(None, app) {
|
||||
match self.pump_events(None, &mut event_handler) {
|
||||
PumpStatus::Exit(0) => {
|
||||
break Ok(());
|
||||
},
|
||||
@@ -194,27 +200,26 @@ impl EventLoop {
|
||||
exit
|
||||
}
|
||||
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
// Run the initial loop iteration.
|
||||
self.single_iteration(app, StartCause::Init);
|
||||
self.single_iteration(&mut callback, StartCause::Init);
|
||||
}
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit.
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, app);
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
}
|
||||
if let Some(code) = self.exit_code() {
|
||||
self.loop_running = false;
|
||||
|
||||
app.exiting(&self.window_target);
|
||||
callback(Event::LoopExiting, self.window_target());
|
||||
|
||||
PumpStatus::Exit(code)
|
||||
} else {
|
||||
@@ -222,11 +227,10 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events_with_timeout<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) {
|
||||
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
let cause = loop {
|
||||
let start = Instant::now();
|
||||
|
||||
@@ -288,10 +292,13 @@ impl EventLoop {
|
||||
break cause;
|
||||
};
|
||||
|
||||
self.single_iteration(app, cause);
|
||||
self.single_iteration(&mut callback, cause);
|
||||
}
|
||||
|
||||
fn single_iteration<A: ApplicationHandler>(&mut self, app: &mut A, cause: StartCause) {
|
||||
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
// NOTE currently just indented to simplify the diff
|
||||
|
||||
// We retain these grow-only scratch buffers as part of the EventLoop
|
||||
@@ -302,17 +309,18 @@ impl EventLoop {
|
||||
let mut buffer_sink = std::mem::take(&mut self.buffer_sink);
|
||||
let mut window_ids = std::mem::take(&mut self.window_ids);
|
||||
|
||||
app.new_events(&self.window_target, cause);
|
||||
callback(Event::NewEvents(cause), &self.window_target);
|
||||
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though Wayland
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
if cause == StartCause::Init {
|
||||
app.resumed(&self.window_target);
|
||||
callback(Event::Resumed, &self.window_target);
|
||||
}
|
||||
|
||||
// Indicate user wake up.
|
||||
if self.with_state(|state| mem::take(&mut state.proxy_wake_up)) {
|
||||
app.proxy_wake_up(&self.window_target);
|
||||
// Handle pending user events. We don't need back buffer, since we can't dispatch
|
||||
// user events indirectly via callback to the user.
|
||||
for user_event in self.pending_user_events.borrow_mut().drain(..) {
|
||||
callback(Event::UserEvent(user_event), &self.window_target);
|
||||
}
|
||||
|
||||
// Drain the pending compositor updates.
|
||||
@@ -333,13 +341,18 @@ impl EventLoop {
|
||||
let old_physical_size = 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,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
};
|
||||
|
||||
app.window_event(&self.window_target, root_window_id, event);
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&new_inner_size,
|
||||
)),
|
||||
},
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
@@ -382,14 +395,23 @@ impl EventLoop {
|
||||
size
|
||||
});
|
||||
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
let event = WindowEvent::Resized(physical_size);
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::Resized(physical_size),
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
}
|
||||
|
||||
if compositor_update.close_window {
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
app.window_event(&self.window_target, window_id, WindowEvent::CloseRequested);
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::CloseRequested,
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,15 +420,8 @@ impl EventLoop {
|
||||
buffer_sink.append(&mut state.window_events_sink.lock().unwrap());
|
||||
});
|
||||
for event in buffer_sink.drain() {
|
||||
match event {
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(&self.window_target, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(&self.window_target, device_id, event)
|
||||
},
|
||||
_ => unreachable!("event which is neither device nor window event."),
|
||||
}
|
||||
let event = event.map_nonuser_event().unwrap();
|
||||
callback(event, &self.window_target);
|
||||
}
|
||||
|
||||
// Handle non-synthetic events.
|
||||
@@ -414,15 +429,8 @@ impl EventLoop {
|
||||
buffer_sink.append(&mut state.events_sink);
|
||||
});
|
||||
for event in buffer_sink.drain() {
|
||||
match event {
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(&self.window_target, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(&self.window_target, device_id, event)
|
||||
},
|
||||
_ => unreachable!("event which is neither device nor window event."),
|
||||
}
|
||||
let event = event.map_nonuser_event().unwrap();
|
||||
callback(event, &self.window_target);
|
||||
}
|
||||
|
||||
// Collect the window ids
|
||||
@@ -458,8 +466,10 @@ impl EventLoop {
|
||||
});
|
||||
|
||||
if let Some(event) = event {
|
||||
let window_id = crate::window::WindowId(*window_id);
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
callback(
|
||||
Event::WindowEvent { window_id: crate::window::WindowId(*window_id), event },
|
||||
&self.window_target,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,7 +479,7 @@ impl EventLoop {
|
||||
});
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
app.about_to_wait(&self.window_target);
|
||||
callback(Event::AboutToWait, &self.window_target);
|
||||
|
||||
// Update the window frames and schedule redraws.
|
||||
let mut wake_up = false;
|
||||
@@ -513,8 +523,8 @@ impl EventLoop {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
self.event_loop_proxy.clone()
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.user_events_sender.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -576,13 +586,13 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsFd for EventLoop {
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
self.event_loop.as_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for EventLoop {
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.event_loop.as_raw_fd()
|
||||
}
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
//! An event loop proxy.
|
||||
|
||||
use sctk::reexports::calloop::ping::Ping;
|
||||
use std::sync::mpsc::SendError;
|
||||
|
||||
use sctk::reexports::calloop::channel::Sender;
|
||||
|
||||
use crate::event_loop::EventLoopClosed;
|
||||
|
||||
/// A handle that can be sent across the threads and used to wake up the `EventLoop`.
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
ping: Ping,
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
user_events_sender: Sender<T>,
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn new(ping: Ping) -> Self {
|
||||
Self { ping }
|
||||
}
|
||||
|
||||
pub fn wake_up(&self) {
|
||||
self.ping.ping();
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
EventLoopProxy { user_events_sender: self.user_events_sender.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn new(user_events_sender: Sender<T>) -> Self {
|
||||
Self { user_events_sender }
|
||||
}
|
||||
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.user_events_sender.send(event).map_err(|SendError(error)| EventLoopClosed(error))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use super::{DeviceId, WindowId};
|
||||
/// to the winit's user.
|
||||
#[derive(Default)]
|
||||
pub struct EventSink {
|
||||
pub(crate) window_events: Vec<Event>,
|
||||
pub window_events: Vec<Event<()>>,
|
||||
}
|
||||
|
||||
impl EventSink {
|
||||
@@ -47,7 +47,7 @@ impl EventSink {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn drain(&mut self) -> Drain<'_, Event> {
|
||||
pub fn drain(&mut self) -> Drain<'_, Event<()>> {
|
||||
self.window_events.drain(..)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ impl From<WaylandError> for OsError {
|
||||
pub struct DeviceId;
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ 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::seat::WinitSeatState;
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
use crate::platform_impl::wayland::{self, DeviceId, WindowId};
|
||||
|
||||
@@ -33,7 +32,17 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
) {
|
||||
let seat_state = match state.seats.get_mut(&data.seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => return,
|
||||
None => {
|
||||
warn!("Received keyboard event {event:?} without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
let keyboard_state = match seat_state.keyboard_state.as_mut() {
|
||||
Some(keyboard_state) => keyboard_state,
|
||||
None => {
|
||||
warn!("Received keyboard event {event:?} without keyboard");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
match event {
|
||||
@@ -43,7 +52,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
warn!("non-xkb compatible keymap")
|
||||
},
|
||||
WlKeymapFormat::XkbV1 => {
|
||||
let context = &mut seat_state.keyboard_state.as_mut().unwrap().xkb_context;
|
||||
let context = &mut keyboard_state.xkb_context;
|
||||
context.set_keymap_from_fd(fd, size as usize);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
@@ -67,7 +76,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
};
|
||||
|
||||
// Drop the repeat, if there were any.
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.current_repeat = None;
|
||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(token);
|
||||
@@ -93,7 +101,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
|
||||
// NOTE: we should drop the repeat regardless whethere it was for the present
|
||||
// window of for the window which just went gone.
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.current_repeat = None;
|
||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(token);
|
||||
@@ -128,7 +135,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
keyboard_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
key,
|
||||
@@ -136,7 +143,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
false,
|
||||
);
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
let delay = match keyboard_state.repeat_info {
|
||||
RepeatInfo::Repeat { delay, .. } => delay,
|
||||
RepeatInfo::Disable => return,
|
||||
@@ -163,18 +169,25 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
state.dispatched_events = true;
|
||||
|
||||
let data = wl_keyboard.data::<KeyboardData>().unwrap();
|
||||
let seat_state = state.seats.get_mut(&data.seat.id()).unwrap();
|
||||
let seat_state = match state.seats.get_mut(&data.seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
|
||||
// NOTE: The removed on event source is batched, but key change to
|
||||
// `None` is instant.
|
||||
let repeat_keycode =
|
||||
match seat_state.keyboard_state.as_ref().unwrap().current_repeat {
|
||||
Some(repeat_keycode) => repeat_keycode,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
let keyboard_state = match seat_state.keyboard_state.as_mut() {
|
||||
Some(keyboard_state) => keyboard_state,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
|
||||
// NOTE: The removed on event source is batched, but key change to `None`
|
||||
// is instant.
|
||||
let repeat_keycode = match keyboard_state.current_repeat {
|
||||
Some(repeat_keycode) => repeat_keycode,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
keyboard_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
repeat_keycode,
|
||||
@@ -183,7 +196,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
);
|
||||
|
||||
// NOTE: the gap could change dynamically while repeat is going.
|
||||
match seat_state.keyboard_state.as_ref().unwrap().repeat_info {
|
||||
match keyboard_state.repeat_info {
|
||||
RepeatInfo::Repeat { gap, .. } => TimeoutAction::ToDuration(gap),
|
||||
RepeatInfo::Disable => TimeoutAction::Drop,
|
||||
}
|
||||
@@ -194,7 +207,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
keyboard_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
key,
|
||||
@@ -202,7 +215,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
false,
|
||||
);
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
if keyboard_state.repeat_info != RepeatInfo::Disable
|
||||
&& keyboard_state.xkb_context.keymap_mut().unwrap().key_repeats(key)
|
||||
&& Some(key) == keyboard_state.current_repeat
|
||||
@@ -216,7 +228,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
WlKeyboardEvent::Modifiers {
|
||||
mods_depressed, mods_latched, mods_locked, group, ..
|
||||
} => {
|
||||
let xkb_context = &mut seat_state.keyboard_state.as_mut().unwrap().xkb_context;
|
||||
let xkb_context = &mut keyboard_state.xkb_context;
|
||||
let xkb_state = match xkb_context.state_mut() {
|
||||
Some(state) => state,
|
||||
None => return,
|
||||
@@ -240,7 +252,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
);
|
||||
},
|
||||
WlKeyboardEvent::RepeatInfo { rate, delay } => {
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.repeat_info = if rate == 0 {
|
||||
// Stop the repeat once we get a disable event.
|
||||
keyboard_state.current_repeat = None;
|
||||
@@ -348,7 +359,7 @@ impl KeyboardData {
|
||||
}
|
||||
|
||||
fn key_input(
|
||||
seat_state: &mut WinitSeatState,
|
||||
keyboard_state: &mut KeyboardState,
|
||||
event_sink: &mut EventSink,
|
||||
data: &KeyboardData,
|
||||
keycode: u32,
|
||||
@@ -360,8 +371,6 @@ fn key_input(
|
||||
None => return,
|
||||
};
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
|
||||
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);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ahash::AHashMap;
|
||||
use tracing::warn;
|
||||
|
||||
use sctk::reexports::client::backend::ObjectId;
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
@@ -76,7 +77,13 @@ impl SeatHandler for WinitState {
|
||||
seat: WlSeat,
|
||||
capability: SeatCapability,
|
||||
) {
|
||||
let seat_state = self.seats.get_mut(&seat.id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_seat::new_capability for unknown seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
match capability {
|
||||
SeatCapability::Touch if seat_state.touch.is_none() => {
|
||||
@@ -139,7 +146,13 @@ impl SeatHandler for WinitState {
|
||||
seat: WlSeat,
|
||||
capability: SeatCapability,
|
||||
) {
|
||||
let seat_state = self.seats.get_mut(&seat.id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_seat::remove_capability for unknown seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(text_input) = seat_state.text_input.take() {
|
||||
text_input.destroy();
|
||||
|
||||
@@ -4,6 +4,8 @@ use std::ops::Deref;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use tracing::warn;
|
||||
|
||||
use sctk::reexports::client::delegate_dispatch;
|
||||
use sctk::reexports::client::protocol::wl_pointer::WlPointer;
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
@@ -41,7 +43,21 @@ impl PointerHandler for WinitState {
|
||||
events: &[PointerEvent],
|
||||
) {
|
||||
let seat = pointer.winit_data().seat();
|
||||
let seat_state = self.seats.get(&seat.id()).unwrap();
|
||||
let seat_state = match self.seats.get(&seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received pointer event without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let themed_pointer = match seat_state.pointer.as_ref() {
|
||||
Some(pointer) => pointer,
|
||||
None => {
|
||||
warn!("Received pointer event without pointer");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
|
||||
|
||||
@@ -78,9 +94,7 @@ impl PointerHandler for WinitState {
|
||||
event.position.0,
|
||||
event.position.1,
|
||||
) {
|
||||
if let Some(pointer) = seat_state.pointer.as_ref() {
|
||||
let _ = pointer.set_cursor(connection, icon);
|
||||
}
|
||||
let _ = themed_pointer.set_cursor(connection, icon);
|
||||
}
|
||||
},
|
||||
PointerEventKind::Leave { .. } if parent_surface != surface => {
|
||||
@@ -113,9 +127,7 @@ impl PointerHandler for WinitState {
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::CursorEntered { device_id }, window_id);
|
||||
|
||||
if let Some(pointer) = seat_state.pointer.as_ref().map(Arc::downgrade) {
|
||||
window.pointer_entered(pointer);
|
||||
}
|
||||
window.pointer_entered(Arc::downgrade(themed_pointer));
|
||||
|
||||
// Set the currently focused surface.
|
||||
pointer.winit_data().inner.lock().unwrap().surface = Some(window_id);
|
||||
@@ -126,9 +138,7 @@ impl PointerHandler for WinitState {
|
||||
);
|
||||
},
|
||||
PointerEventKind::Leave { .. } => {
|
||||
if let Some(pointer) = seat_state.pointer.as_ref().map(Arc::downgrade) {
|
||||
window.pointer_left(pointer);
|
||||
}
|
||||
window.pointer_left(Arc::downgrade(themed_pointer));
|
||||
|
||||
// Remove the active surface.
|
||||
pointer.winit_data().inner.lock().unwrap().surface = None;
|
||||
@@ -185,13 +195,13 @@ impl PointerHandler for WinitState {
|
||||
// Mice events have both pixel and discrete delta's at the same time. So prefer
|
||||
// the descrite values if they are present.
|
||||
let delta = if has_discrete_scroll {
|
||||
// XXX Wayland sign convention is the inverse of winit.
|
||||
// NOTE: Wayland sign convention is the inverse of winit.
|
||||
MouseScrollDelta::LineDelta(
|
||||
(-horizontal.discrete) as f32,
|
||||
(-vertical.discrete) as f32,
|
||||
)
|
||||
} else {
|
||||
// XXX Wayland sign convention is the inverse of winit.
|
||||
// NOTE: Wayland sign convention is the inverse of winit.
|
||||
MouseScrollDelta::PixelDelta(
|
||||
LogicalPosition::new(-horizontal.absolute, -vertical.absolute)
|
||||
.to_physical(scale_factor),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! Touch handling.
|
||||
|
||||
use tracing::warn;
|
||||
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
||||
@@ -31,11 +33,16 @@ impl TouchHandler for WinitState {
|
||||
None => return,
|
||||
};
|
||||
|
||||
let location = LogicalPosition::<f64>::from(position);
|
||||
|
||||
let seat_state = self.seats.get_mut(&touch.seat().id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_touch::down without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Update the state of the point.
|
||||
let location = LogicalPosition::<f64>::from(position);
|
||||
seat_state.touch_map.insert(id, TouchPoint { surface, location });
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
@@ -61,7 +68,13 @@ impl TouchHandler for WinitState {
|
||||
_: u32,
|
||||
id: i32,
|
||||
) {
|
||||
let seat_state = self.seats.get_mut(&touch.seat().id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_touch::up without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Remove the touch point.
|
||||
let touch_point = match seat_state.touch_map.remove(&id) {
|
||||
@@ -98,7 +111,13 @@ impl TouchHandler for WinitState {
|
||||
id: i32,
|
||||
position: (f64, f64),
|
||||
) {
|
||||
let seat_state = self.seats.get_mut(&touch.seat().id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_touch::motion without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Remove the touch point.
|
||||
let touch_point = match seat_state.touch_map.get_mut(&id) {
|
||||
@@ -129,7 +148,13 @@ impl TouchHandler for WinitState {
|
||||
}
|
||||
|
||||
fn cancel(&mut self, _: &Connection, _: &QueueHandle<Self>, touch: &WlTouch) {
|
||||
let seat_state = self.seats.get_mut(&touch.seat().id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_touch::cancel without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
for (id, touch_point) in seat_state.touch_map.drain() {
|
||||
let window_id = wayland::make_wid(&touch_point.surface);
|
||||
|
||||
@@ -115,9 +115,6 @@ pub struct WinitState {
|
||||
/// Whether we have dispatched events to the user thus we want to
|
||||
/// send `AboutToWait` and normally wakeup the user.
|
||||
pub dispatched_events: bool,
|
||||
|
||||
/// Whether the user initiated a wake up.
|
||||
pub proxy_wake_up: bool,
|
||||
}
|
||||
|
||||
impl WinitState {
|
||||
@@ -195,7 +192,6 @@ impl WinitState {
|
||||
loop_handle,
|
||||
// Make it true by default.
|
||||
dispatched_events: true,
|
||||
proxy_wake_up: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -343,12 +339,30 @@ impl CompositorHandler for WinitState {
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wayland_client::protocol::wl_surface::WlSurface,
|
||||
_: &WlSurface,
|
||||
_: wayland_client::protocol::wl_output::Transform,
|
||||
) {
|
||||
// TODO(kchibisov) we need to expose it somehow in winit.
|
||||
}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlSurface,
|
||||
_: &WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn surface_leave(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlSurface,
|
||||
_: &WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn scale_factor_changed(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
|
||||
@@ -165,7 +165,7 @@ fn push_display(buffer: &mut Vec<u8>, display: &impl std::fmt::Display) {
|
||||
buffer: &'a mut Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Write for Writer<'a> {
|
||||
impl std::fmt::Write for Writer<'_> {
|
||||
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||
self.buffer.extend_from_slice(s.as_bytes());
|
||||
Ok(())
|
||||
|
||||
@@ -72,9 +72,9 @@ pub struct EventProcessor {
|
||||
}
|
||||
|
||||
impl EventProcessor {
|
||||
pub(crate) fn process_event<F>(&mut self, xev: &mut XEvent, mut callback: F)
|
||||
pub fn process_event<T: 'static, F>(&mut self, xev: &mut XEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
self.process_xevent(xev, &mut callback);
|
||||
|
||||
@@ -129,6 +129,7 @@ impl EventProcessor {
|
||||
/// Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
|
||||
/// along with an extra copy of the KeyRelease events. This also prevents backspace and
|
||||
/// arrow keys from being detected twice.
|
||||
#[must_use]
|
||||
fn filter_event(&mut self, xev: &mut XEvent) -> bool {
|
||||
let wt = Self::window_target(&self.target);
|
||||
unsafe {
|
||||
@@ -139,14 +140,26 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn process_xevent<F>(&mut self, xev: &mut XEvent, mut callback: F)
|
||||
fn process_xevent<T: 'static, F>(&mut self, xev: &mut XEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let event_type = xev.get_type();
|
||||
|
||||
if self.filter_event(xev) {
|
||||
if event_type == xlib::KeyPress || event_type == xlib::KeyRelease {
|
||||
// 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.
|
||||
let filtered = if event_type == xlib::KeyPress || event_type == xlib::KeyRelease {
|
||||
let wt = Self::window_target(&self.target);
|
||||
let ime = wt.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);
|
||||
|
||||
let filtered = forward_to_ime && self.filter_event(xev);
|
||||
if filtered {
|
||||
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
|
||||
@@ -159,6 +172,14 @@ impl EventProcessor {
|
||||
self.xfiltered_modifiers.push_front(xev.serial);
|
||||
}
|
||||
}
|
||||
|
||||
filtered
|
||||
} else {
|
||||
self.filter_event(xev)
|
||||
};
|
||||
|
||||
// Don't process event if it was filtered.
|
||||
if filtered {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -387,9 +408,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn client_message<F>(&mut self, xev: &XClientMessageEvent, mut callback: F)
|
||||
fn client_message<T: 'static, F>(&mut self, xev: &XClientMessageEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
let atoms = wt.xconn.atoms();
|
||||
@@ -552,9 +573,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn selection_notify<F>(&mut self, xev: &XSelectionEvent, mut callback: F)
|
||||
fn selection_notify<T: 'static, F>(&mut self, xev: &XSelectionEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
let atoms = wt.xconn.atoms();
|
||||
@@ -586,9 +607,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_notify<F>(&self, xev: &XConfigureEvent, mut callback: F)
|
||||
fn configure_notify<T: 'static, F>(&self, xev: &XConfigureEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -781,9 +802,9 @@ impl EventProcessor {
|
||||
});
|
||||
}
|
||||
|
||||
fn map_notify<F>(&self, xev: &XMapEvent, mut callback: F)
|
||||
fn map_notify<T: 'static, F>(&self, xev: &XMapEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let window = xev.window as xproto::Window;
|
||||
let window_id = mkwid(window);
|
||||
@@ -799,9 +820,9 @@ impl EventProcessor {
|
||||
callback(&self.target, event);
|
||||
}
|
||||
|
||||
fn destroy_notify<F>(&self, xev: &XDestroyWindowEvent, mut callback: F)
|
||||
fn destroy_notify<T: 'static, F>(&self, xev: &XDestroyWindowEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -823,9 +844,9 @@ impl EventProcessor {
|
||||
callback(&self.target, Event::WindowEvent { window_id, event: WindowEvent::Destroyed });
|
||||
}
|
||||
|
||||
fn property_notify<F>(&mut self, xev: &XPropertyEvent, mut callback: F)
|
||||
fn property_notify<T: 'static, F>(&mut self, xev: &XPropertyEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
let atoms = wt.x_connection().atoms();
|
||||
@@ -838,9 +859,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn visibility_notify<F>(&self, xev: &XVisibilityEvent, mut callback: F)
|
||||
fn visibility_notify<T: 'static, F>(&self, xev: &XVisibilityEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let xwindow = xev.window as xproto::Window;
|
||||
|
||||
@@ -855,9 +876,9 @@ impl EventProcessor {
|
||||
});
|
||||
}
|
||||
|
||||
fn expose<F>(&self, xev: &XExposeEvent, mut callback: F)
|
||||
fn expose<T: 'static, F>(&self, xev: &XExposeEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
// Multiple Expose events may be received for subareas of a window.
|
||||
// We issue `RedrawRequested` only for the last event of such a series.
|
||||
@@ -871,9 +892,13 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn xinput_key_input<F>(&mut self, xev: &mut XKeyEvent, state: ElementState, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
fn xinput_key_input<T: 'static, F>(
|
||||
&mut self,
|
||||
xev: &mut XKeyEvent,
|
||||
state: ElementState,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -985,13 +1010,13 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_synthic_modifier_from_core<F>(
|
||||
fn send_synthic_modifier_from_core<T: 'static, F>(
|
||||
&mut self,
|
||||
window_id: crate::window::WindowId,
|
||||
state: u16,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let keymap = match self.xkb_context.keymap_mut() {
|
||||
Some(keymap) => keymap,
|
||||
@@ -1018,9 +1043,13 @@ impl EventProcessor {
|
||||
callback(&self.target, event);
|
||||
}
|
||||
|
||||
fn xinput2_button_input<F>(&self, event: &XIDeviceEvent, state: ElementState, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
fn xinput2_button_input<T: 'static, F>(
|
||||
&self,
|
||||
event: &XIDeviceEvent,
|
||||
state: ElementState,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
let window_id = mkwid(event.event as xproto::Window);
|
||||
@@ -1071,9 +1100,9 @@ impl EventProcessor {
|
||||
callback(&self.target, event);
|
||||
}
|
||||
|
||||
fn xinput2_mouse_motion<F>(&self, event: &XIDeviceEvent, mut callback: F)
|
||||
fn xinput2_mouse_motion<T: 'static, F>(&self, event: &XIDeviceEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -1149,9 +1178,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn xinput2_mouse_enter<F>(&self, event: &XIEnterEvent, mut callback: F)
|
||||
fn xinput2_mouse_enter<T: 'static, F>(&self, event: &XIEnterEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -1194,9 +1223,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn xinput2_mouse_left<F>(&self, event: &XILeaveEvent, mut callback: F)
|
||||
fn xinput2_mouse_left<T: 'static, F>(&self, event: &XILeaveEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
let window = event.event as xproto::Window;
|
||||
@@ -1217,9 +1246,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn xinput2_focused<F>(&mut self, xev: &XIFocusInEvent, mut callback: F)
|
||||
fn xinput2_focused<T: 'static, F>(&mut self, xev: &XIFocusInEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
let window = xev.event as xproto::Window;
|
||||
@@ -1276,9 +1305,9 @@ impl EventProcessor {
|
||||
callback(&self.target, event);
|
||||
}
|
||||
|
||||
fn xinput2_unfocused<F>(&mut self, xev: &XIFocusOutEvent, mut callback: F)
|
||||
fn xinput2_unfocused<T: 'static, F>(&mut self, xev: &XIFocusOutEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
let window = xev.event as xproto::Window;
|
||||
@@ -1328,9 +1357,13 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn xinput2_touch<F>(&mut self, xev: &XIDeviceEvent, phase: TouchPhase, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
fn xinput2_touch<T: 'static, F>(
|
||||
&mut self,
|
||||
xev: &XIDeviceEvent,
|
||||
phase: TouchPhase,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -1370,9 +1403,13 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn xinput2_raw_button_input<F>(&self, xev: &XIRawEvent, state: ElementState, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
fn xinput2_raw_button_input<T: 'static, F>(
|
||||
&self,
|
||||
xev: &XIRawEvent,
|
||||
state: ElementState,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -1388,9 +1425,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn xinput2_raw_mouse_motion<F>(&self, xev: &XIRawEvent, mut callback: F)
|
||||
fn xinput2_raw_mouse_motion<T: 'static, F>(&self, xev: &XIRawEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -1448,9 +1485,13 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn xinput2_raw_key_input<F>(&mut self, xev: &XIRawEvent, state: ElementState, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
fn xinput2_raw_key_input<T: 'static, F>(
|
||||
&mut self,
|
||||
xev: &XIRawEvent,
|
||||
state: ElementState,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -1470,9 +1511,9 @@ impl EventProcessor {
|
||||
});
|
||||
}
|
||||
|
||||
fn xinput2_hierarchy_changed<F>(&mut self, xev: &XIHierarchyEvent, mut callback: F)
|
||||
fn xinput2_hierarchy_changed<T: 'static, F>(&mut self, xev: &XIHierarchyEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -1497,9 +1538,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn xkb_event<F>(&mut self, xev: &XkbAnyEvent, mut callback: F)
|
||||
fn xkb_event<T: 'static, F>(&mut self, xev: &XkbAnyEvent, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
match xev.xkb_type {
|
||||
@@ -1576,14 +1617,14 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_mods_from_xinput2_event<F>(
|
||||
pub fn update_mods_from_xinput2_event<T: 'static, F>(
|
||||
&mut self,
|
||||
mods: &XIModifierState,
|
||||
group: &XIModifierState,
|
||||
force: bool,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
if let Some(state) = self.xkb_context.state_mut() {
|
||||
state.update_modifiers(
|
||||
@@ -1607,9 +1648,12 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_mods_from_query<F>(&mut self, window_id: crate::window::WindowId, mut callback: F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
fn update_mods_from_query<T: 'static, F>(
|
||||
&mut self,
|
||||
window_id: crate::window::WindowId,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
|
||||
@@ -1638,13 +1682,13 @@ impl EventProcessor {
|
||||
self.send_modifiers(window_id, mods.into(), true, &mut callback)
|
||||
}
|
||||
|
||||
pub(crate) fn update_mods_from_core_event<F>(
|
||||
pub fn update_mods_from_core_event<T: 'static, F>(
|
||||
&mut self,
|
||||
window_id: crate::window::WindowId,
|
||||
state: u16,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let xkb_mask = self.xkb_mod_mask_from_core(state);
|
||||
let xkb_state = match self.xkb_context.state_mut() {
|
||||
@@ -1717,7 +1761,7 @@ impl EventProcessor {
|
||||
///
|
||||
/// The event won't be sent when the `modifiers` match the previously `sent` modifiers value,
|
||||
/// unless `force` is passed. The `force` should be passed when the active window changes.
|
||||
fn send_modifiers<F: FnMut(&RootAEL, Event)>(
|
||||
fn send_modifiers<T: 'static, F: FnMut(&RootAEL, Event<T>)>(
|
||||
&self,
|
||||
window_id: crate::window::WindowId,
|
||||
modifiers: ModifiersState,
|
||||
@@ -1735,14 +1779,14 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_pressed_keys<F>(
|
||||
fn handle_pressed_keys<T: 'static, F>(
|
||||
target: &RootAEL,
|
||||
window_id: crate::window::WindowId,
|
||||
state: ElementState,
|
||||
xkb_context: &mut Context,
|
||||
callback: &mut F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
|
||||
@@ -1777,9 +1821,9 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn process_dpi_change<F>(&self, callback: &mut F)
|
||||
fn process_dpi_change<T: 'static, F>(&self, callback: &mut F)
|
||||
where
|
||||
F: FnMut(&RootAEL, Event),
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
let wt = Self::window_target(&self.target);
|
||||
wt.xconn.reload_database().expect("failed to reload Xft database");
|
||||
|
||||
@@ -226,6 +226,16 @@ 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 {
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{fmt, mem, ptr, slice, str};
|
||||
use std::{fmt, ptr, slice, str};
|
||||
|
||||
use calloop::generic::Generic;
|
||||
use calloop::ping::Ping;
|
||||
@@ -25,10 +25,9 @@ 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, OsError as RootOsError};
|
||||
use crate::event::{Event, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents};
|
||||
use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed};
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::common::xkb::Context;
|
||||
use crate::platform_impl::platform::{min_timeout, WindowId};
|
||||
@@ -81,11 +80,12 @@ impl<T> Clone for WakeSender<T> {
|
||||
}
|
||||
|
||||
impl<T> WakeSender<T> {
|
||||
pub fn send(&self, t: T) {
|
||||
let res = self.sender.send(t);
|
||||
pub fn send(&self, t: T) -> Result<(), EventLoopClosed<T>> {
|
||||
let res = self.sender.send(t).map_err(|e| EventLoopClosed(e.0));
|
||||
if res.is_ok() {
|
||||
self.waker.ping();
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +140,15 @@ pub struct ActiveEventLoop {
|
||||
device_events: Cell<DeviceEvents>,
|
||||
}
|
||||
|
||||
pub struct EventLoop {
|
||||
pub struct EventLoop<T: 'static> {
|
||||
loop_running: bool,
|
||||
event_loop: Loop<'static, EventLoopState>,
|
||||
waker: calloop::ping::Ping,
|
||||
event_processor: EventProcessor,
|
||||
redraw_receiver: PeekableReceiver<WindowId>,
|
||||
user_receiver: PeekableReceiver<T>,
|
||||
activation_receiver: PeekableReceiver<ActivationToken>,
|
||||
event_loop_proxy: EventLoopProxy,
|
||||
user_sender: Sender<T>,
|
||||
|
||||
/// The current state of the event loop.
|
||||
state: EventLoopState,
|
||||
@@ -157,13 +159,20 @@ type ActivationToken = (WindowId, crate::event_loop::AsyncRequestSerial);
|
||||
struct EventLoopState {
|
||||
/// The latest readiness state for the x11 file descriptor
|
||||
x11_readiness: Readiness,
|
||||
|
||||
/// User requested a wake up.
|
||||
proxy_wake_up: bool,
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
pub(crate) fn new(xconn: Arc<XConnection>) -> EventLoop {
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
user_sender: WakeSender<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
EventLoopProxy { user_sender: self.user_sender.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub(crate) fn new(xconn: Arc<XConnection>) -> EventLoop<T> {
|
||||
let root = xconn.default_root().root;
|
||||
let atoms = xconn.atoms();
|
||||
|
||||
@@ -267,16 +276,7 @@ impl EventLoop {
|
||||
let (activation_token_sender, activation_token_channel) = mpsc::channel();
|
||||
|
||||
// Create a channel for sending user events.
|
||||
let (user_waker, user_waker_source) =
|
||||
calloop::ping::make_ping().expect("Failed to create user event loop waker.");
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(user_waker_source, move |_, _, state| {
|
||||
// No extra handling is required, we just need to wake-up.
|
||||
state.proxy_wake_up = true;
|
||||
})
|
||||
.expect("Failed to register the event loop waker source");
|
||||
let event_loop_proxy = EventLoopProxy::new(user_waker);
|
||||
let (user_sender, user_channel) = mpsc::channel();
|
||||
|
||||
let xkb_context =
|
||||
Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap();
|
||||
@@ -357,32 +357,32 @@ impl EventLoop {
|
||||
EventLoop {
|
||||
loop_running: false,
|
||||
event_loop,
|
||||
waker,
|
||||
event_processor,
|
||||
redraw_receiver: PeekableReceiver::from_recv(redraw_channel),
|
||||
activation_receiver: PeekableReceiver::from_recv(activation_token_channel),
|
||||
event_loop_proxy,
|
||||
state: EventLoopState { x11_readiness: Readiness::EMPTY, proxy_wake_up: false },
|
||||
user_receiver: PeekableReceiver::from_recv(user_channel),
|
||||
user_sender,
|
||||
state: EventLoopState { x11_readiness: Readiness::EMPTY },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
self.event_loop_proxy.clone()
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy {
|
||||
user_sender: WakeSender { sender: self.user_sender.clone(), waker: self.waker.clone() },
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn window_target(&self) -> &RootAEL {
|
||||
&self.event_processor.target
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
let exit = loop {
|
||||
match self.pump_app_events(None, app) {
|
||||
match self.pump_events(None, &mut event_handler) {
|
||||
PumpStatus::Exit(0) => {
|
||||
break Ok(());
|
||||
},
|
||||
@@ -407,27 +407,26 @@ impl EventLoop {
|
||||
exit
|
||||
}
|
||||
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
// run the initial loop iteration
|
||||
self.single_iteration(app, StartCause::Init);
|
||||
self.single_iteration(&mut callback, StartCause::Init);
|
||||
}
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit.
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, app);
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
}
|
||||
if let Some(code) = self.exit_code() {
|
||||
self.loop_running = false;
|
||||
|
||||
app.exiting(self.window_target());
|
||||
callback(Event::LoopExiting, self.window_target());
|
||||
|
||||
PumpStatus::Exit(code)
|
||||
} else {
|
||||
@@ -437,15 +436,14 @@ impl EventLoop {
|
||||
|
||||
fn has_pending(&mut self) -> bool {
|
||||
self.event_processor.poll()
|
||||
|| self.state.proxy_wake_up
|
||||
|| self.user_receiver.has_incoming()
|
||||
|| self.redraw_receiver.has_incoming()
|
||||
}
|
||||
|
||||
pub fn poll_events_with_timeout<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) {
|
||||
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
||||
let has_pending = self.has_pending();
|
||||
@@ -503,20 +501,23 @@ impl EventLoop {
|
||||
return;
|
||||
}
|
||||
|
||||
self.single_iteration(app, cause);
|
||||
self.single_iteration(&mut callback, cause);
|
||||
}
|
||||
|
||||
fn single_iteration<A: ApplicationHandler>(&mut self, app: &mut A, cause: StartCause) {
|
||||
app.new_events(&self.event_processor.target, cause);
|
||||
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
callback(Event::NewEvents(cause), &self.event_processor.target);
|
||||
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though X11
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
if cause == StartCause::Init {
|
||||
app.resumed(&self.event_processor.target)
|
||||
callback(Event::Resumed, &self.event_processor.target);
|
||||
}
|
||||
|
||||
// Process all pending events
|
||||
self.drain_events(app);
|
||||
self.drain_events(callback);
|
||||
|
||||
// Empty activation tokens.
|
||||
while let Ok((window_id, serial)) = self.activation_receiver.try_recv() {
|
||||
@@ -526,12 +527,14 @@ impl EventLoop {
|
||||
|
||||
match token {
|
||||
Some(Ok(token)) => {
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
let event = WindowEvent::ActivationTokenDone {
|
||||
serial,
|
||||
token: crate::window::ActivationToken::_new(token),
|
||||
let event = Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::ActivationTokenDone {
|
||||
serial,
|
||||
token: crate::window::ActivationToken::_new(token),
|
||||
},
|
||||
};
|
||||
app.window_event(&self.event_processor.target, window_id, event);
|
||||
callback(event, &self.event_processor.target)
|
||||
},
|
||||
Some(Err(e)) => {
|
||||
tracing::error!("Failed to get activation token: {}", e);
|
||||
@@ -541,8 +544,10 @@ impl EventLoop {
|
||||
}
|
||||
|
||||
// Empty the user event buffer
|
||||
if mem::take(&mut self.state.proxy_wake_up) {
|
||||
app.proxy_wake_up(&self.event_processor.target);
|
||||
{
|
||||
while let Ok(event) = self.user_receiver.try_recv() {
|
||||
callback(Event::UserEvent(event), &self.event_processor.target);
|
||||
}
|
||||
}
|
||||
|
||||
// Empty the redraw requests
|
||||
@@ -555,41 +560,37 @@ impl EventLoop {
|
||||
|
||||
for window_id in windows {
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
app.window_event(
|
||||
callback(
|
||||
Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested },
|
||||
&self.event_processor.target,
|
||||
window_id,
|
||||
WindowEvent::RedrawRequested,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
app.about_to_wait(&self.event_processor.target);
|
||||
{
|
||||
callback(Event::AboutToWait, &self.event_processor.target);
|
||||
}
|
||||
}
|
||||
|
||||
fn drain_events<A: ApplicationHandler>(&mut self, app: &mut A) {
|
||||
fn drain_events<F>(&mut self, callback: &mut F)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
let mut xev = MaybeUninit::uninit();
|
||||
|
||||
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| {
|
||||
self.event_processor.process_event(&mut xev, |window_target, event| {
|
||||
if let Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(wid),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
} = event
|
||||
{
|
||||
let window_target = EventProcessor::window_target(window_target);
|
||||
window_target.redraw_sender.send(wid);
|
||||
window_target.redraw_sender.send(wid).unwrap();
|
||||
} else {
|
||||
match event {
|
||||
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)
|
||||
},
|
||||
_ => unreachable!("event which is neither device nor window event."),
|
||||
}
|
||||
callback(event, window_target);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -616,13 +617,13 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsFd for EventLoop {
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
self.event_loop.as_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for EventLoop {
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.event_loop.as_raw_fd()
|
||||
}
|
||||
@@ -722,9 +723,9 @@ impl ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
self.ping.ping();
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.user_sender.send(event).map_err(|e| EventLoopClosed(e.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,14 +751,14 @@ impl<'a> DeviceInfo<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for DeviceInfo<'a> {
|
||||
impl Drop for DeviceInfo<'_> {
|
||||
fn drop(&mut self) {
|
||||
assert!(!self.info.is_null());
|
||||
unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for DeviceInfo<'a> {
|
||||
impl Deref for DeviceInfo<'_> {
|
||||
type Target = [ffi::XIDeviceInfo];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -770,7 +771,7 @@ pub struct DeviceId(xinput::DeviceId);
|
||||
|
||||
impl DeviceId {
|
||||
#[allow(unused)]
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId(0)
|
||||
}
|
||||
}
|
||||
@@ -808,17 +809,6 @@ impl Drop for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
ping: Ping,
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
fn new(ping: Ping) -> Self {
|
||||
Self { ping }
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic sum error type for X11 errors.
|
||||
#[derive(Debug)]
|
||||
pub enum X11Error {
|
||||
@@ -967,7 +957,7 @@ trait CookieResultExt {
|
||||
fn expect_then_ignore_error(self, msg: &str);
|
||||
}
|
||||
|
||||
impl<'a, E: fmt::Debug> CookieResultExt for Result<VoidCookie<'a>, E> {
|
||||
impl<E: fmt::Debug> CookieResultExt for Result<VoidCookie<'_>, E> {
|
||||
fn expect_then_ignore_error(self, msg: &str) {
|
||||
self.expect(msg).ignore_error()
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ impl XConnection {
|
||||
let info = self
|
||||
.xcb_connection()
|
||||
.extension_information(randr::X11_EXTENSION_NAME)?
|
||||
.ok_or_else(|| X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
|
||||
.ok_or(X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
|
||||
|
||||
// Select input data.
|
||||
let event_mask =
|
||||
|
||||
@@ -19,7 +19,7 @@ impl<'a, T> XSmartPointer<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
impl<T> Deref for XSmartPointer<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
@@ -27,13 +27,13 @@ impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
||||
impl<T> DerefMut for XSmartPointer<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||
impl<T> Drop for XSmartPointer<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
||||
|
||||
@@ -144,12 +144,26 @@ impl UnownedWindow {
|
||||
) -> Result<UnownedWindow, RootOsError> {
|
||||
let xconn = &event_loop.xconn;
|
||||
let atoms = xconn.atoms();
|
||||
|
||||
let screen_id = match window_attrs.platform_specific.x11.screen_id {
|
||||
Some(id) => id,
|
||||
None => xconn.default_screen_index() as c_int,
|
||||
};
|
||||
|
||||
let screen = {
|
||||
let screen_id_usize = usize::try_from(screen_id)
|
||||
.map_err(|_| os_error!(OsError::Misc("screen id must be non-negative")))?;
|
||||
xconn.xcb_connection().setup().roots.get(screen_id_usize).ok_or(os_error!(
|
||||
OsError::Misc("requested screen id not present in server's response")
|
||||
))?
|
||||
};
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
let root = match window_attrs.parent_window.as_ref().map(|handle| handle.0) {
|
||||
Some(rwh_06::RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window,
|
||||
Some(rwh_06::RawWindowHandle::Xcb(handle)) => handle.window.get(),
|
||||
Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"),
|
||||
None => event_loop.root,
|
||||
None => screen.root,
|
||||
};
|
||||
#[cfg(not(feature = "rwh_06"))]
|
||||
let root = event_loop.root;
|
||||
@@ -207,18 +221,10 @@ impl UnownedWindow {
|
||||
dimensions
|
||||
};
|
||||
|
||||
let screen_id = match window_attrs.platform_specific.x11.screen_id {
|
||||
Some(id) => id,
|
||||
None => xconn.default_screen_index() as c_int,
|
||||
};
|
||||
|
||||
// An iterator over all of the visuals combined with their depths.
|
||||
let mut all_visuals = xconn
|
||||
.xcb_connection()
|
||||
.setup()
|
||||
.roots
|
||||
// An iterator over the visuals matching screen id combined with their depths.
|
||||
let mut all_visuals = screen
|
||||
.allowed_depths
|
||||
.iter()
|
||||
.flat_map(|root| &root.allowed_depths)
|
||||
.flat_map(|depth| depth.visuals.iter().map(move |visual| (visual, depth.depth)));
|
||||
|
||||
// creating
|
||||
@@ -484,6 +490,20 @@ impl UnownedWindow {
|
||||
);
|
||||
leap!(result).ignore_error();
|
||||
|
||||
// Select XInput2 events
|
||||
let mask = xinput::XIEventMask::MOTION
|
||||
| xinput::XIEventMask::BUTTON_PRESS
|
||||
| xinput::XIEventMask::BUTTON_RELEASE
|
||||
| xinput::XIEventMask::ENTER
|
||||
| xinput::XIEventMask::LEAVE
|
||||
| xinput::XIEventMask::FOCUS_IN
|
||||
| xinput::XIEventMask::FOCUS_OUT
|
||||
| xinput::XIEventMask::TOUCH_BEGIN
|
||||
| xinput::XIEventMask::TOUCH_UPDATE
|
||||
| xinput::XIEventMask::TOUCH_END;
|
||||
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
|
||||
.ignore_error();
|
||||
|
||||
// Set visibility (map window)
|
||||
if window_attrs.visible {
|
||||
leap!(xconn.xcb_connection().map_window(window.xwindow)).ignore_error();
|
||||
@@ -507,20 +527,6 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// Select XInput2 events
|
||||
let mask = xinput::XIEventMask::MOTION
|
||||
| xinput::XIEventMask::BUTTON_PRESS
|
||||
| xinput::XIEventMask::BUTTON_RELEASE
|
||||
| xinput::XIEventMask::ENTER
|
||||
| xinput::XIEventMask::LEAVE
|
||||
| xinput::XIEventMask::FOCUS_IN
|
||||
| xinput::XIEventMask::FOCUS_OUT
|
||||
| xinput::XIEventMask::TOUCH_BEGIN
|
||||
| xinput::XIEventMask::TOUCH_UPDATE
|
||||
| xinput::XIEventMask::TOUCH_END;
|
||||
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
|
||||
.ignore_error();
|
||||
|
||||
// Try to create input context for the window.
|
||||
if let Some(ime) = event_loop.ime.as_ref() {
|
||||
let result = ime.borrow_mut().create_context(window.xwindow as ffi::Window, false);
|
||||
@@ -876,11 +882,11 @@ impl UnownedWindow {
|
||||
|
||||
/// Refresh the API for the given monitor.
|
||||
#[inline]
|
||||
pub(super) fn refresh_dpi_for_monitor(
|
||||
pub(super) fn refresh_dpi_for_monitor<T: 'static>(
|
||||
&self,
|
||||
new_monitor: &X11MonitorHandle,
|
||||
maybe_prev_scale_factor: Option<f64>,
|
||||
mut callback: impl FnMut(Event),
|
||||
mut callback: impl FnMut(Event<T>),
|
||||
) {
|
||||
// Check if the self is on this monitor
|
||||
let monitor = self.shared_state_lock().last_monitor.clone();
|
||||
@@ -1805,7 +1811,9 @@ impl UnownedWindow {
|
||||
#[inline]
|
||||
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||
let serial = AsyncRequestSerial::get();
|
||||
self.activation_sender.send((self.id(), serial));
|
||||
self.activation_sender
|
||||
.send((self.id(), serial))
|
||||
.expect("activation token channel should never be closed");
|
||||
Ok(serial)
|
||||
}
|
||||
|
||||
@@ -1816,7 +1824,7 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
self.redraw_sender.send(WindowId(self.xwindow as _));
|
||||
self.redraw_sender.send(WindowId(self.xwindow as _)).unwrap();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -5,7 +5,6 @@ use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, N
|
||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::DEVICE_ID;
|
||||
use crate::event::{DeviceEvent, ElementState};
|
||||
|
||||
declare_class!(
|
||||
@@ -58,47 +57,29 @@ fn maybe_dispatch_device_event(delegate: &ApplicationDelegate, event: &NSEvent)
|
||||
let delta_y = unsafe { event.deltaY() } as f64;
|
||||
|
||||
if delta_x != 0.0 {
|
||||
delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Motion {
|
||||
axis: 0,
|
||||
value: delta_x,
|
||||
});
|
||||
});
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Motion { axis: 0, value: delta_x });
|
||||
}
|
||||
|
||||
if delta_y != 0.0 {
|
||||
delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Motion {
|
||||
axis: 1,
|
||||
value: delta_y,
|
||||
});
|
||||
})
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Motion { axis: 1, value: delta_y })
|
||||
}
|
||||
|
||||
if delta_x != 0.0 || delta_y != 0.0 {
|
||||
delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseMotion {
|
||||
delta: (delta_x, delta_y),
|
||||
});
|
||||
delegate.maybe_queue_device_event(DeviceEvent::MouseMotion {
|
||||
delta: (delta_x, delta_y),
|
||||
});
|
||||
}
|
||||
},
|
||||
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
|
||||
let button = unsafe { event.buttonNumber() } as u32;
|
||||
delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
|
||||
button,
|
||||
state: ElementState::Pressed,
|
||||
});
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Button {
|
||||
button: unsafe { event.buttonNumber() } as u32,
|
||||
state: ElementState::Pressed,
|
||||
});
|
||||
},
|
||||
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
||||
let button = unsafe { event.buttonNumber() } as u32;
|
||||
delegate.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
|
||||
button,
|
||||
state: ElementState::Released,
|
||||
});
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Button {
|
||||
button: unsafe { event.buttonNumber() } as u32,
|
||||
state: ElementState::Released,
|
||||
});
|
||||
},
|
||||
_ => (),
|
||||
@@ -1,32 +1,29 @@
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::mem;
|
||||
use std::rc::Weak;
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use objc2::rc::Retained;
|
||||
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
|
||||
use objc2_app_kit::{
|
||||
NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate, NSRunningApplication,
|
||||
};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::event::{StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
use super::event_handler::EventHandler;
|
||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
||||
use super::observer::{EventLoopWaker, RunLoop};
|
||||
use super::{menu, WindowId};
|
||||
use super::{menu, WindowId, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, Event, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct AppState {
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
activation_policy: Option<NSApplicationActivationPolicy>,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
run_loop: RunLoop,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
event_handler: EventHandler,
|
||||
stop_on_launch: Cell<bool>,
|
||||
stop_before_wait: Cell<bool>,
|
||||
@@ -79,14 +76,12 @@ declare_class!(
|
||||
impl ApplicationDelegate {
|
||||
pub(super) fn new(
|
||||
mtm: MainThreadMarker,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
activation_policy: Option<NSApplicationActivationPolicy>,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) -> Retained<Self> {
|
||||
let this = mtm.alloc().set_ivars(AppState {
|
||||
activation_policy,
|
||||
proxy_wake_up,
|
||||
default_menu,
|
||||
activate_ignoring_other_apps,
|
||||
run_loop: RunLoop::main(mtm),
|
||||
@@ -118,7 +113,24 @@ impl ApplicationDelegate {
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
app.setActivationPolicy(self.ivars().activation_policy);
|
||||
// If no activation policy is explicitly provided, do not set it at all
|
||||
// to allow the package manifest to define behavior via LSUIElement.
|
||||
if let Some(activation_policy) = self.ivars().activation_policy {
|
||||
app.setActivationPolicy(activation_policy);
|
||||
} else {
|
||||
// If no activation policy is explicitly provided, and the application
|
||||
// is bundled, do not set the activation policy at all, to allow the
|
||||
// package manifest to define the behavior via LSUIElement.
|
||||
//
|
||||
// See:
|
||||
// - https://github.com/rust-windowing/winit/issues/261
|
||||
// - https://github.com/rust-windowing/winit/issues/3958
|
||||
let is_bundled =
|
||||
unsafe { NSRunningApplication::currentApplication().bundleIdentifier().is_some() };
|
||||
if !is_bundled {
|
||||
app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
|
||||
}
|
||||
}
|
||||
|
||||
window_activation_hack(&app);
|
||||
#[allow(deprecated)]
|
||||
@@ -173,7 +185,7 @@ impl ApplicationDelegate {
|
||||
/// of the given closure.
|
||||
pub fn set_event_handler<R>(
|
||||
&self,
|
||||
handler: &mut dyn ApplicationHandler,
|
||||
handler: impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop),
|
||||
closure: impl FnOnce() -> R,
|
||||
) -> R {
|
||||
self.ivars().event_handler.set(handler, closure)
|
||||
@@ -207,9 +219,7 @@ impl ApplicationDelegate {
|
||||
/// 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) {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.exiting(event_loop);
|
||||
});
|
||||
self.handle_event(Event::LoopExiting);
|
||||
|
||||
self.set_is_running(false);
|
||||
self.set_stop_on_redraw(false);
|
||||
@@ -250,13 +260,26 @@ impl ApplicationDelegate {
|
||||
self.ivars().control_flow.get()
|
||||
}
|
||||
|
||||
pub fn maybe_queue_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||
self.maybe_queue_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||
}
|
||||
|
||||
pub fn handle_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||
self.handle_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||
}
|
||||
|
||||
pub fn maybe_queue_device_event(&self, event: DeviceEvent) {
|
||||
self.maybe_queue_event(Event::DeviceEvent { device_id: DEVICE_ID, event });
|
||||
}
|
||||
|
||||
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.ivars().event_handler.in_use() {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
|
||||
self.handle_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
|
||||
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
|
||||
@@ -278,10 +301,7 @@ impl ApplicationDelegate {
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn maybe_queue_with_handler(
|
||||
&self,
|
||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop) + 'static,
|
||||
) {
|
||||
fn maybe_queue_event(&self, event: Event<HandlePendingUserEvents>) {
|
||||
// Most programmer actions in AppKit (e.g. change window fullscreen, set focused, etc.)
|
||||
// result in an event being queued, and applied at a later point.
|
||||
//
|
||||
@@ -289,31 +309,25 @@ impl ApplicationDelegate {
|
||||
// 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.ivars().event_handler.in_use() {
|
||||
self.with_handler(callback);
|
||||
self.handle_event(event);
|
||||
} else {
|
||||
tracing::debug!("had to queue event since another is currently being handled");
|
||||
tracing::debug!(?event, "had to queue event since another is currently being handled");
|
||||
let this = self.retain();
|
||||
self.ivars().run_loop.queue_closure(move || {
|
||||
this.with_handler(callback);
|
||||
});
|
||||
self.ivars().run_loop.queue_closure(move || this.handle_event(event));
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn with_handler(
|
||||
&self,
|
||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop),
|
||||
) {
|
||||
let event_loop = ActiveEventLoop::new_root(self.retain());
|
||||
self.ivars().event_handler.handle(callback, &event_loop);
|
||||
fn handle_event(&self, event: Event<HandlePendingUserEvents>) {
|
||||
self.ivars().event_handler.handle_event(event, &ActiveEventLoop::new_root(self.retain()))
|
||||
}
|
||||
|
||||
/// dispatch `NewEvents(Init)` + `Resumed`
|
||||
pub fn dispatch_init_events(&self) {
|
||||
self.with_handler(|app, event_loop| app.new_events(event_loop, StartCause::Init));
|
||||
self.handle_event(Event::NewEvents(StartCause::Init));
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though macOS
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
self.with_handler(|app, event_loop| app.resumed(event_loop));
|
||||
self.handle_event(Event::Resumed);
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver after finishing waiting for new events
|
||||
@@ -323,7 +337,8 @@ impl ApplicationDelegate {
|
||||
.upgrade()
|
||||
.expect("The panic info must exist here. This failure indicates a developer error.");
|
||||
|
||||
if panic_info.is_panicking() || !self.is_running() {
|
||||
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
|
||||
if panic_info.is_panicking() || !self.ivars().event_handler.ready() || !self.is_running() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -345,7 +360,7 @@ impl ApplicationDelegate {
|
||||
},
|
||||
};
|
||||
|
||||
self.with_handler(|app, event_loop| app.new_events(event_loop, cause));
|
||||
self.handle_event(Event::NewEvents(cause));
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver before waiting for new events
|
||||
@@ -355,23 +370,24 @@ impl ApplicationDelegate {
|
||||
.upgrade()
|
||||
.expect("The panic info must exist here. This failure indicates a developer error.");
|
||||
|
||||
if panic_info.is_panicking() || !self.is_running() {
|
||||
// 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.ivars().event_handler.ready() || !self.is_running() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.ivars().proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
|
||||
self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
|
||||
}
|
||||
self.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
||||
|
||||
let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut());
|
||||
for window_id in redraw {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
|
||||
self.handle_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
}
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.about_to_wait(event_loop);
|
||||
});
|
||||
|
||||
self.handle_event(Event::AboutToWait);
|
||||
|
||||
if self.exiting() {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
@@ -393,6 +409,9 @@ impl ApplicationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct HandlePendingUserEvents;
|
||||
|
||||
/// Returns the minimum `Option<Instant>`, taking into account that `None`
|
||||
/// equates to an infinite timeout, not a zero timeout (so can't just use
|
||||
/// `Option::min`)
|
||||
@@ -4,7 +4,7 @@ use std::sync::OnceLock;
|
||||
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::Sel;
|
||||
use objc2::{msg_send_id, sel, ClassType};
|
||||
use objc2::{msg_send, msg_send_id, sel, ClassType};
|
||||
use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
|
||||
use objc2_foundation::{
|
||||
ns_string, NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSSize,
|
||||
@@ -66,7 +66,7 @@ pub(crate) fn default_cursor() -> Retained<NSCursor> {
|
||||
|
||||
unsafe fn try_cursor_from_selector(sel: Sel) -> Option<Retained<NSCursor>> {
|
||||
let cls = NSCursor::class();
|
||||
if cls.responds_to(sel) {
|
||||
if msg_send![cls, respondsToSelector: sel] {
|
||||
let cursor: Retained<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] };
|
||||
Some(cursor)
|
||||
} else {
|
||||
@@ -7,12 +7,12 @@ use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSEventType};
|
||||
use objc2_foundation::{run_on_main, NSPoint};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use super::ffi;
|
||||
use crate::event::{ElementState, KeyEvent, Modifiers};
|
||||
use crate::keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
|
||||
PhysicalKey,
|
||||
};
|
||||
use crate::platform_impl::platform::ffi;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
@@ -92,17 +92,12 @@ fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
|
||||
/// Create `KeyEvent` for the given `NSEvent`.
|
||||
///
|
||||
/// This function shouldn't be called when the IME input is in process.
|
||||
pub(crate) fn create_key_event(
|
||||
ns_event: &NSEvent,
|
||||
is_press: bool,
|
||||
is_repeat: bool,
|
||||
key_override: Option<PhysicalKey>,
|
||||
) -> KeyEvent {
|
||||
pub(crate) fn create_key_event(ns_event: &NSEvent, is_press: bool, is_repeat: bool) -> KeyEvent {
|
||||
use ElementState::{Pressed, Released};
|
||||
let state = if is_press { Pressed } else { Released };
|
||||
|
||||
let scancode = unsafe { ns_event.keyCode() };
|
||||
let mut physical_key = key_override.unwrap_or_else(|| scancode_to_physicalkey(scancode as u32));
|
||||
let mut physical_key = scancode_to_physicalkey(scancode as u32);
|
||||
|
||||
// NOTE: The logical key should heed both SHIFT and ALT if possible.
|
||||
// For instance:
|
||||
@@ -111,20 +106,15 @@ pub(crate) fn create_key_event(
|
||||
// * Pressing CTRL SHIFT A: logical key should also be "A"
|
||||
// This is not easy to tease out of `NSEvent`, but we do our best.
|
||||
|
||||
let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() {
|
||||
let characters = unsafe { ns_event.characters() }.map(|s| s.to_string()).unwrap_or_default();
|
||||
let text_with_all_modifiers = if characters.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let characters =
|
||||
unsafe { ns_event.characters() }.map(|s| s.to_string()).unwrap_or_default();
|
||||
if characters.is_empty() {
|
||||
None
|
||||
} else {
|
||||
if matches!(physical_key, PhysicalKey::Unidentified(_)) {
|
||||
// The key may be one of the funky function keys
|
||||
physical_key = extra_function_key_to_code(scancode, &characters);
|
||||
}
|
||||
Some(SmolStr::new(characters))
|
||||
if matches!(physical_key, PhysicalKey::Unidentified(_)) {
|
||||
// The key may be one of the funky function keys
|
||||
physical_key = extra_function_key_to_code(scancode, &characters);
|
||||
}
|
||||
Some(SmolStr::new(characters))
|
||||
};
|
||||
|
||||
let key_from_code = code_to_key(physical_key, scancode);
|
||||
@@ -1,31 +1,32 @@
|
||||
use std::cell::RefCell;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use super::app_state::HandlePendingUserEvents;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
||||
|
||||
#[derive(Default)]
|
||||
struct EventHandlerData {
|
||||
#[allow(clippy::type_complexity)]
|
||||
handler: Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'static>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventHandlerData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EventHandlerData").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct EventHandler {
|
||||
/// This can be in the following states:
|
||||
/// - Not registered by the event loop (None).
|
||||
/// - Present (Some(handler)).
|
||||
/// - Currently executing the handler / in use (RefCell borrowed).
|
||||
inner: RefCell<Option<&'static mut dyn ApplicationHandler>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventHandler {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let state = match self.inner.try_borrow().as_deref() {
|
||||
Ok(Some(_)) => "<available>",
|
||||
Ok(None) => "<not set>",
|
||||
Err(_) => "<in use>",
|
||||
};
|
||||
f.debug_struct("EventHandler").field("state", &state).finish_non_exhaustive()
|
||||
}
|
||||
inner: RefCell<Option<EventHandlerData>>,
|
||||
}
|
||||
|
||||
impl EventHandler {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self { inner: RefCell::new(None) }
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ impl EventHandler {
|
||||
/// from within the closure.
|
||||
pub(crate) fn set<'handler, R>(
|
||||
&self,
|
||||
app: &'handler mut dyn ApplicationHandler,
|
||||
handler: impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'handler,
|
||||
closure: impl FnOnce() -> R,
|
||||
) -> R {
|
||||
// SAFETY: We extend the lifetime of the handler here so that we can
|
||||
@@ -47,9 +48,9 @@ impl EventHandler {
|
||||
// extended beyond `'handler`.
|
||||
let handler = unsafe {
|
||||
mem::transmute::<
|
||||
&'handler mut dyn ApplicationHandler,
|
||||
&'static mut dyn ApplicationHandler,
|
||||
>(app)
|
||||
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'handler>,
|
||||
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'static>,
|
||||
>(Box::new(handler))
|
||||
};
|
||||
|
||||
match self.inner.try_borrow_mut().as_deref_mut() {
|
||||
@@ -57,7 +58,7 @@ impl EventHandler {
|
||||
unreachable!("tried to set handler while another was already set");
|
||||
},
|
||||
Ok(data @ None) => {
|
||||
*data = Some(handler);
|
||||
*data = Some(EventHandlerData { handler });
|
||||
},
|
||||
Err(_) => {
|
||||
unreachable!("tried to set handler that is currently in use");
|
||||
@@ -104,20 +105,24 @@ impl EventHandler {
|
||||
self.inner.try_borrow().is_err()
|
||||
}
|
||||
|
||||
pub(crate) fn handle(
|
||||
pub(crate) fn ready(&self) -> bool {
|
||||
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
|
||||
}
|
||||
|
||||
pub(crate) fn handle_event(
|
||||
&self,
|
||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop),
|
||||
event: Event<HandlePendingUserEvents>,
|
||||
event_loop: &RootActiveEventLoop,
|
||||
) {
|
||||
match self.inner.try_borrow_mut().as_deref_mut() {
|
||||
Ok(Some(user_app)) => {
|
||||
Ok(Some(EventHandlerData { handler })) => {
|
||||
// It is important that we keep the reference borrowed here,
|
||||
// so that `in_use` can properly detect that the handler is
|
||||
// still in use.
|
||||
//
|
||||
// If the handler unwinds, the `RefMut` will ensure that the
|
||||
// handler is no longer borrowed.
|
||||
callback(*user_app, event_loop);
|
||||
(handler)(event, event_loop);
|
||||
},
|
||||
Ok(None) => {
|
||||
// `NSApplication`, our app delegate and this handler are all
|
||||
@@ -6,33 +6,34 @@ use std::os::raw::c_void;
|
||||
use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe};
|
||||
use std::ptr;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use core_foundation::base::{CFIndex, CFRelease};
|
||||
use core_foundation::runloop::{
|
||||
kCFRunLoopDefaultMode, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
|
||||
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
|
||||
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use objc2::rc::{autoreleasepool, Retained};
|
||||
use objc2::runtime::ProtocolObject;
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
use objc2::{msg_send_id, sel, ClassType};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSWindow};
|
||||
use objc2_foundation::{MainThreadMarker, NSObjectProtocol};
|
||||
|
||||
use super::app::WinitApplication;
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::cursor::CustomCursor;
|
||||
use super::app_state::{ApplicationDelegate, HandlePendingUserEvents};
|
||||
use super::event::dummy_event;
|
||||
use super::monitor::{self, MonitorHandle};
|
||||
use super::observer::setup_control_flow_observers;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event_loop::{ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents};
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||
};
|
||||
use crate::platform::macos::ActivationPolicy;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource};
|
||||
use crate::platform_impl::platform::cursor::CustomCursor;
|
||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PanicInfo {
|
||||
@@ -105,6 +106,17 @@ impl ActiveEventLoop {
|
||||
rwh_05::RawDisplayHandle::AppKit(rwh_05::AppKitDisplayHandle::empty())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn system_theme(&self) -> Option<Theme> {
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
|
||||
if app.respondsToSelector(sel!(effectiveAppearance)) {
|
||||
Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
|
||||
} else {
|
||||
Some(Theme::Light)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
@@ -154,7 +166,21 @@ impl ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop {
|
||||
fn map_user_event<T: 'static>(
|
||||
mut handler: impl FnMut(Event<T>, &RootWindowTarget),
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget) {
|
||||
move |event, window_target| match event.map_nonuser_event() {
|
||||
Ok(event) => (handler)(event, window_target),
|
||||
Err(_) => {
|
||||
for event in receiver.try_iter() {
|
||||
(handler)(Event::UserEvent(event), window_target);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
/// Store a reference to the application for convenience.
|
||||
///
|
||||
/// We intentionally don't store `WinitApplication` since we want to have
|
||||
@@ -166,7 +192,9 @@ pub struct EventLoop {
|
||||
/// keep it around here as well.
|
||||
delegate: Retained<ApplicationDelegate>,
|
||||
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
// Event sender and receiver, used for EventLoopProxy.
|
||||
sender: mpsc::Sender<T>,
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
|
||||
window_target: RootWindowTarget,
|
||||
panic_info: Rc<PanicInfo>,
|
||||
@@ -174,22 +202,18 @@ pub struct EventLoop {
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {
|
||||
pub(crate) activation_policy: ActivationPolicy,
|
||||
pub(crate) activation_policy: Option<ActivationPolicy>,
|
||||
pub(crate) default_menu: bool,
|
||||
pub(crate) activate_ignoring_other_apps: bool,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificEventLoopAttributes {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
activation_policy: Default::default(), // Regular
|
||||
default_menu: true,
|
||||
activate_ignoring_other_apps: true,
|
||||
}
|
||||
Self { activation_policy: None, default_menu: true, activate_ignoring_other_apps: true }
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
impl<T> EventLoop<T> {
|
||||
pub(crate) fn new(
|
||||
attributes: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<Self, EventLoopError> {
|
||||
@@ -207,17 +231,14 @@ impl EventLoop {
|
||||
}
|
||||
|
||||
let activation_policy = match attributes.activation_policy {
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular,
|
||||
ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory,
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited,
|
||||
None => None,
|
||||
Some(ActivationPolicy::Regular) => Some(NSApplicationActivationPolicy::Regular),
|
||||
Some(ActivationPolicy::Accessory) => Some(NSApplicationActivationPolicy::Accessory),
|
||||
Some(ActivationPolicy::Prohibited) => Some(NSApplicationActivationPolicy::Prohibited),
|
||||
};
|
||||
|
||||
let proxy_wake_up = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let delegate = ApplicationDelegate::new(
|
||||
mtm,
|
||||
activation_policy,
|
||||
proxy_wake_up.clone(),
|
||||
attributes.default_menu,
|
||||
attributes.activate_ignoring_other_apps,
|
||||
);
|
||||
@@ -229,14 +250,16 @@ impl EventLoop {
|
||||
let panic_info: Rc<PanicInfo> = Default::default();
|
||||
setup_control_flow_observers(mtm, Rc::downgrade(&panic_info));
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
Ok(EventLoop {
|
||||
app,
|
||||
delegate: delegate.clone(),
|
||||
sender,
|
||||
receiver: Rc::new(receiver),
|
||||
window_target: RootWindowTarget {
|
||||
p: ActiveEventLoop { delegate, mtm },
|
||||
_marker: PhantomData,
|
||||
},
|
||||
proxy_wake_up,
|
||||
panic_info,
|
||||
})
|
||||
}
|
||||
@@ -245,19 +268,24 @@ impl EventLoop {
|
||||
&self.window_target
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
pub fn run<F>(mut self, handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget),
|
||||
{
|
||||
self.run_on_demand(handler)
|
||||
}
|
||||
|
||||
// NB: we don't base this on `pump_events` because for `MacOs` we can't support
|
||||
// `pump_events` elegantly (we just ask to run the loop for a "short" amount of
|
||||
// time and so a layered implementation would end up using a lot of CPU due to
|
||||
// redundant wake ups.
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
self.delegate.set_event_handler(app, || {
|
||||
pub fn run_on_demand<F>(&mut self, handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget),
|
||||
{
|
||||
let handler = map_user_event(handler, self.receiver.clone());
|
||||
|
||||
self.delegate.set_event_handler(handler, || {
|
||||
autoreleasepool(|_| {
|
||||
// clear / normalize pump_events state
|
||||
self.delegate.set_wait_timeout(None);
|
||||
@@ -290,12 +318,13 @@ impl EventLoop {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
self.delegate.set_event_handler(app, || {
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget),
|
||||
{
|
||||
let handler = map_user_event(handler, self.receiver.clone());
|
||||
|
||||
self.delegate.set_event_handler(handler, || {
|
||||
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.
|
||||
@@ -359,8 +388,8 @@ impl EventLoop {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
EventLoopProxy::new(self.proxy_wake_up.clone())
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.sender.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,15 +447,15 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy {
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
pub struct EventLoopProxy<T> {
|
||||
sender: mpsc::Sender<T>,
|
||||
source: CFRunLoopSourceRef,
|
||||
}
|
||||
|
||||
unsafe impl Send for EventLoopProxy {}
|
||||
unsafe impl Sync for EventLoopProxy {}
|
||||
unsafe impl<T: Send> Send for EventLoopProxy<T> {}
|
||||
unsafe impl<T: Send> Sync for EventLoopProxy<T> {}
|
||||
|
||||
impl Drop for EventLoopProxy {
|
||||
impl<T> Drop for EventLoopProxy<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
CFRelease(self.source as _);
|
||||
@@ -434,14 +463,14 @@ impl Drop for EventLoopProxy {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for EventLoopProxy {
|
||||
impl<T> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
EventLoopProxy::new(self.proxy_wake_up.clone())
|
||||
EventLoopProxy::new(self.sender.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
fn new(proxy_wake_up: Arc<AtomicBool>) -> Self {
|
||||
impl<T> EventLoopProxy<T> {
|
||||
fn new(sender: mpsc::Sender<T>) -> Self {
|
||||
unsafe {
|
||||
// just wake up the eventloop
|
||||
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
|
||||
@@ -462,20 +491,21 @@ impl EventLoopProxy {
|
||||
perform: event_loop_proxy_handler,
|
||||
};
|
||||
let source = CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::MAX - 1, &mut context);
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopDefaultMode);
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||
CFRunLoopWakeUp(rl);
|
||||
|
||||
EventLoopProxy { proxy_wake_up, source }
|
||||
EventLoopProxy { sender, source }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wake_up(&self) {
|
||||
self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.sender.send(event).map_err(|mpsc::SendError(x)| EventLoopClosed(x))?;
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
CFRunLoopSourceSignal(self.source);
|
||||
let rl = CFRunLoopGetMain();
|
||||
CFRunLoopWakeUp(rl);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ pub(crate) use crate::platform_impl::Fullscreen;
|
||||
pub struct DeviceId;
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ use block2::Block;
|
||||
use core_foundation::base::{CFIndex, CFOptionFlags, CFRelease, CFTypeRef};
|
||||
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
||||
use core_foundation::runloop::{
|
||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopDefaultMode, kCFRunLoopExit,
|
||||
CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddTimer, CFRunLoopGetMain,
|
||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopDefaultMode,
|
||||
kCFRunLoopExit, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddTimer, CFRunLoopGetMain,
|
||||
CFRunLoopObserverCallBack, CFRunLoopObserverContext, CFRunLoopObserverCreate,
|
||||
CFRunLoopObserverRef, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
||||
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, CFRunLoopWakeUp,
|
||||
@@ -129,7 +129,7 @@ impl RunLoop {
|
||||
context,
|
||||
)
|
||||
};
|
||||
unsafe { CFRunLoopAddObserver(self.0, observer, kCFRunLoopDefaultMode) };
|
||||
unsafe { CFRunLoopAddObserver(self.0, observer, kCFRunLoopCommonModes) };
|
||||
}
|
||||
|
||||
/// Submit a closure to run on the main thread as the next step in the run loop, before other
|
||||
@@ -267,7 +267,7 @@ impl EventLoopWaker {
|
||||
wakeup_main_loop,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopDefaultMode);
|
||||
CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes);
|
||||
Self { timer, start_instant: Instant::now(), next_fire_date: None }
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,7 @@ use tracing::trace;
|
||||
|
||||
macro_rules! trace_scope {
|
||||
($s:literal) => {
|
||||
let _crate =
|
||||
$crate::platform_impl::platform::appkit::util::TraceGuard::new(module_path!(), $s);
|
||||
let _crate = $crate::platform_impl::platform::util::TraceGuard::new(module_path!(), $s);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,18 +20,17 @@ 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,
|
||||
scancode_to_physicalkey, KeyEventExtra,
|
||||
};
|
||||
use super::window::WinitWindow;
|
||||
use super::DEVICE_ID;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||
use crate::event::{
|
||||
DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, TouchPhase,
|
||||
DeviceEvent, ElementState, Ime, KeyEvent, 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 {
|
||||
@@ -400,7 +399,7 @@ declare_class!(
|
||||
unsafe { &*string }.to_string()
|
||||
};
|
||||
|
||||
let is_control = string.chars().next().map_or(false, |c| c.is_control());
|
||||
let is_control = string.chars().next().is_some_and(|c| c.is_control());
|
||||
|
||||
// Commit only if we have marked text.
|
||||
if unsafe { self.hasMarkedText() } && self.is_ime_enabled() && !is_control {
|
||||
@@ -483,7 +482,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);
|
||||
let key_event = create_key_event(&event, true, unsafe { event.isARepeat() });
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event: key_event,
|
||||
@@ -506,7 +505,7 @@ declare_class!(
|
||||
) {
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event: create_key_event(&event, false, false, None),
|
||||
event: create_key_event(&event, false, false),
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
@@ -553,7 +552,7 @@ declare_class!(
|
||||
.expect("could not find current event");
|
||||
|
||||
self.update_modifiers(&event, false);
|
||||
let event = create_key_event(&event, true, unsafe { event.isARepeat() }, None);
|
||||
let event = create_key_event(&event, true, unsafe { event.isARepeat() });
|
||||
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
@@ -687,9 +686,7 @@ declare_class!(
|
||||
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
self.ivars().app_delegate.maybe_queue_with_handler(move |app, event_loop|
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseWheel { delta })
|
||||
);
|
||||
self.ivars().app_delegate.maybe_queue_device_event(DeviceEvent::MouseWheel { delta });
|
||||
self.queue_event(WindowEvent::MouseWheel {
|
||||
device_id: DEVICE_ID,
|
||||
delta,
|
||||
@@ -833,10 +830,7 @@ impl WinitView {
|
||||
}
|
||||
|
||||
fn queue_event(&self, event: WindowEvent) {
|
||||
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);
|
||||
});
|
||||
self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event);
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
@@ -939,22 +933,36 @@ impl WinitView {
|
||||
let scancode = unsafe { ns_event.keyCode() };
|
||||
let physical_key = scancode_to_physicalkey(scancode as u32);
|
||||
|
||||
// We'll correct the `is_press` later.
|
||||
let mut event = create_key_event(ns_event, false, false, Some(physical_key));
|
||||
|
||||
let key = code_to_key(physical_key, scancode);
|
||||
let logical_key = code_to_key(physical_key, scancode);
|
||||
// Ignore processing of unknown modifiers because we can't determine whether
|
||||
// it was pressed or release reliably.
|
||||
let Some(event_modifier) = key_to_modifier(&key) else {
|
||||
//
|
||||
// Furthermore, sometimes normal keys are reported inside flagsChanged:, such as
|
||||
// when holding Caps Lock while pressing another key, see:
|
||||
// https://github.com/alacritty/alacritty/issues/8268
|
||||
let Some(event_modifier) = key_to_modifier(&logical_key) else {
|
||||
break 'send_event;
|
||||
};
|
||||
event.physical_key = physical_key;
|
||||
event.logical_key = key.clone();
|
||||
event.location = code_to_location(physical_key);
|
||||
|
||||
let mut event = KeyEvent {
|
||||
location: code_to_location(physical_key),
|
||||
logical_key: logical_key.clone(),
|
||||
physical_key,
|
||||
repeat: false,
|
||||
// We'll correct this later.
|
||||
state: Pressed,
|
||||
text: None,
|
||||
platform_specific: KeyEventExtra {
|
||||
text_with_all_modifiers: None,
|
||||
key_without_modifiers: logical_key.clone(),
|
||||
},
|
||||
};
|
||||
|
||||
let location_mask = ModLocationMask::from_location(event.location);
|
||||
|
||||
let mut phys_mod_state = self.ivars().phys_modifiers.borrow_mut();
|
||||
let phys_mod = phys_mod_state.entry(key).or_insert(ModLocationMask::empty());
|
||||
let phys_mod =
|
||||
phys_mod_state.entry(logical_key).or_insert(ModLocationMask::empty());
|
||||
|
||||
let is_active = current_modifiers.state().contains(event_modifier);
|
||||
let mut events = VecDeque::with_capacity(2);
|
||||
126
src/platform_impl/macos/window.rs
Normal file
126
src/platform_impl/macos/window.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use objc2::rc::{autoreleasepool, Retained};
|
||||
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSResponder, NSWindow};
|
||||
use objc2_foundation::{MainThreadBound, MainThreadMarker, NSObject};
|
||||
|
||||
use super::event_loop::ActiveEventLoop;
|
||||
use super::window_delegate::WindowDelegate;
|
||||
use crate::error::OsError as RootOsError;
|
||||
use crate::window::WindowAttributes;
|
||||
|
||||
pub(crate) struct Window {
|
||||
window: MainThreadBound<Retained<WinitWindow>>,
|
||||
/// The window only keeps a weak reference to this, so we must keep it around here.
|
||||
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, RootOsError> {
|
||||
let mtm = window_target.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,
|
||||
) -> R {
|
||||
self.delegate.get_on_main(|delegate| f(delegate))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub(crate) fn raw_window_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
if let Some(mtm) = MainThreadMarker::new() {
|
||||
Ok(self.delegate.get(mtm).raw_window_handle_rwh_06())
|
||||
} else {
|
||||
Err(rwh_06::HandleError::Unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
#[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::AppKit(rwh_06::AppKitDisplayHandle::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(pub usize);
|
||||
|
||||
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 usize)
|
||||
}
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
pub struct WinitWindow;
|
||||
|
||||
unsafe impl ClassType for WinitWindow {
|
||||
#[inherits(NSResponder, NSObject)]
|
||||
type Super = NSWindow;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitWindow";
|
||||
}
|
||||
|
||||
impl DeclaredClass for WinitWindow {}
|
||||
|
||||
unsafe impl WinitWindow {
|
||||
#[method(canBecomeMainWindow)]
|
||||
fn can_become_main_window(&self) -> bool {
|
||||
trace_scope!("canBecomeMainWindow");
|
||||
true
|
||||
}
|
||||
|
||||
#[method(canBecomeKeyWindow)]
|
||||
fn can_become_key_window(&self) -> bool {
|
||||
trace_scope!("canBecomeKeyWindow");
|
||||
true
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl WinitWindow {
|
||||
pub(super) fn id(&self) -> WindowId {
|
||||
WindowId(self as *const Self as usize)
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ use crate::event::{InnerSizeWriter, WindowEvent};
|
||||
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||
use crate::window::{
|
||||
Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -55,6 +55,7 @@ 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 {
|
||||
@@ -72,6 +73,7 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
accepts_first_mouse: true,
|
||||
tabbing_identifier: None,
|
||||
option_as_alt: Default::default(),
|
||||
borderless_game: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,8 +87,8 @@ pub(crate) struct State {
|
||||
|
||||
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
||||
//
|
||||
// This is expressed in native screen coordinates.
|
||||
previous_position: Cell<Option<NSPoint>>,
|
||||
// This is expressed in desktop coordinates, and flipped to match Winit's coordinate system.
|
||||
previous_position: Cell<NSPoint>,
|
||||
|
||||
// Used to prevent redundant events.
|
||||
previous_scale_factor: Cell<f64>,
|
||||
@@ -120,6 +122,7 @@ 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!(
|
||||
@@ -711,7 +714,7 @@ impl WindowDelegate {
|
||||
let delegate = mtm.alloc().set_ivars(State {
|
||||
app_delegate: app_delegate.retain(),
|
||||
window: window.retain(),
|
||||
previous_position: Cell::new(None),
|
||||
previous_position: Cell::new(flip_window_screen_coordinates(window.frame())),
|
||||
previous_scale_factor: Cell::new(scale_factor),
|
||||
resize_increments: Cell::new(resize_increments),
|
||||
decorations: Cell::new(attrs.decorations),
|
||||
@@ -725,6 +728,7 @@ 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] };
|
||||
|
||||
@@ -807,13 +811,11 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
||||
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);
|
||||
});
|
||||
self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event);
|
||||
}
|
||||
|
||||
fn handle_scale_factor_changed(&self, scale_factor: CGFloat) {
|
||||
let app_delegate = &self.ivars().app_delegate;
|
||||
let window = self.window();
|
||||
|
||||
let content_size = window.contentRectForFrameRect(window.frame()).size;
|
||||
@@ -821,7 +823,7 @@ impl WindowDelegate {
|
||||
|
||||
let suggested_size = content_size.to_physical(scale_factor);
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
self.queue_event(WindowEvent::ScaleFactorChanged {
|
||||
app_delegate.handle_window_event(window.id(), WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
});
|
||||
@@ -833,17 +835,16 @@ impl WindowDelegate {
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
}
|
||||
self.queue_event(WindowEvent::Resized(physical_size));
|
||||
app_delegate.handle_window_event(window.id(), WindowEvent::Resized(physical_size));
|
||||
}
|
||||
|
||||
fn emit_move_event(&self) {
|
||||
let frame = self.window().frame();
|
||||
if self.ivars().previous_position.get() == Some(frame.origin) {
|
||||
let position = flip_window_screen_coordinates(self.window().frame());
|
||||
if self.ivars().previous_position.get() == position {
|
||||
return;
|
||||
}
|
||||
self.ivars().previous_position.set(Some(frame.origin));
|
||||
self.ivars().previous_position.set(position);
|
||||
|
||||
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));
|
||||
@@ -1162,7 +1163,8 @@ impl WindowDelegate {
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let event = NSApplication::sharedApplication(mtm).currentEvent().unwrap();
|
||||
let event =
|
||||
NSApplication::sharedApplication(mtm).currentEvent().ok_or(ExternalError::Ignored)?;
|
||||
self.window().performWindowDragWithEvent(&event);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1411,7 +1413,7 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
match (old_fullscreen, fullscreen) {
|
||||
(None, Some(_)) => {
|
||||
(None, Some(fullscreen)) => {
|
||||
// `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`.
|
||||
@@ -1421,6 +1423,17 @@ 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) => {
|
||||
@@ -1428,13 +1441,7 @@ impl WindowDelegate {
|
||||
toggle_fullscreen(self.window());
|
||||
},
|
||||
(Some(Fullscreen::Exclusive(ref video_mode)), None) => {
|
||||
unsafe {
|
||||
ffi::CGRestorePermanentDisplayConfiguration();
|
||||
assert_eq!(
|
||||
ffi::CGDisplayRelease(video_mode.monitor().native_identifier()),
|
||||
ffi::kCGErrorSuccess
|
||||
);
|
||||
};
|
||||
restore_and_release_display(&video_mode.monitor());
|
||||
toggle_fullscreen(self.window());
|
||||
},
|
||||
(Some(Fullscreen::Borderless(_)), Some(Fullscreen::Exclusive(_))) => {
|
||||
@@ -1465,13 +1472,7 @@ impl WindowDelegate {
|
||||
);
|
||||
app.setPresentationOptions(presentation_options);
|
||||
|
||||
unsafe {
|
||||
ffi::CGRestorePermanentDisplayConfiguration();
|
||||
assert_eq!(
|
||||
ffi::CGDisplayRelease(video_mode.monitor().native_identifier()),
|
||||
ffi::kCGErrorSuccess
|
||||
);
|
||||
};
|
||||
restore_and_release_display(&video_mode.monitor());
|
||||
|
||||
// Restore the normal window level following the Borderless fullscreen
|
||||
// `CGShieldingWindowLevel() + 1` hack.
|
||||
@@ -1615,7 +1616,7 @@ impl WindowDelegate {
|
||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||
let mut window_handle = rwh_04::AppKitHandle::empty();
|
||||
window_handle.ns_window = self.window() as *const WinitWindow as *mut _;
|
||||
window_handle.ns_view = Retained::as_ptr(&self.contentView().unwrap()) as *mut _;
|
||||
window_handle.ns_view = Retained::as_ptr(&self.view()) as *mut _;
|
||||
rwh_04::RawWindowHandle::AppKit(window_handle)
|
||||
}
|
||||
|
||||
@@ -1659,14 +1660,18 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
// Note: We could choose between returning the value of `effectiveAppearance` or
|
||||
// `appearance`, depending on what the user is asking about:
|
||||
// - "how should I render on this particular frame".
|
||||
// - "what is the configuration for this window".
|
||||
//
|
||||
// We choose the latter for consistency with the `set_theme` call, though it might also be
|
||||
// useful to expose the former.
|
||||
Some(appearance_to_theme(unsafe { &*self.window().appearance()? }))
|
||||
unsafe { self.window().appearance() }
|
||||
.map(|appearance| appearance_to_theme(&appearance))
|
||||
.or_else(|| {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
|
||||
if app.respondsToSelector(sel!(effectiveAppearance)) {
|
||||
Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
|
||||
} else {
|
||||
Some(Theme::Light)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_theme(&self, theme: Option<Theme>) {
|
||||
@@ -1691,6 +1696,21 @@ impl WindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
fn restore_and_release_display(monitor: &MonitorHandle) {
|
||||
let available_monitors = monitor::available_monitors();
|
||||
if available_monitors.contains(monitor) {
|
||||
unsafe {
|
||||
ffi::CGRestorePermanentDisplayConfiguration();
|
||||
assert_eq!(ffi::CGDisplayRelease(monitor.native_identifier()), ffi::kCGErrorSuccess);
|
||||
};
|
||||
} else {
|
||||
warn!(
|
||||
monitor = monitor.name(),
|
||||
"Tried to restore exclusive fullscreen on a monitor that is no longer available"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowExtMacOS for WindowDelegate {
|
||||
#[inline]
|
||||
fn simple_fullscreen(&self) -> bool {
|
||||
@@ -1824,6 +1844,14 @@ 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 =
|
||||
@@ -1834,7 +1862,7 @@ fn dark_appearance_name() -> &'static NSString {
|
||||
ns_string!("NSAppearanceNameDarkAqua")
|
||||
}
|
||||
|
||||
fn appearance_to_theme(appearance: &NSAppearance) -> Theme {
|
||||
pub fn appearance_to_theme(appearance: &NSAppearance) -> Theme {
|
||||
let best_match = appearance.bestMatchFromAppearancesWithNames(&NSArray::from_id_slice(&[
|
||||
unsafe { NSAppearanceNameAqua.copy() },
|
||||
dark_appearance_name().copy(),
|
||||
@@ -3,10 +3,12 @@ use crate::window::Fullscreen as RootFullscreen;
|
||||
|
||||
#[cfg(android_platform)]
|
||||
mod android;
|
||||
#[cfg(target_vendor = "apple")]
|
||||
mod apple;
|
||||
#[cfg(ios_platform)]
|
||||
mod ios;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
mod linux;
|
||||
#[cfg(macos_platform)]
|
||||
mod macos;
|
||||
#[cfg(orbital_platform)]
|
||||
mod orbital;
|
||||
#[cfg(web_platform)]
|
||||
@@ -15,17 +17,19 @@ mod web;
|
||||
mod windows;
|
||||
|
||||
#[cfg(android_platform)]
|
||||
use self::android as platform;
|
||||
#[cfg(target_vendor = "apple")]
|
||||
use self::apple as platform;
|
||||
use android as platform;
|
||||
#[cfg(ios_platform)]
|
||||
use ios as platform;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use self::linux as platform;
|
||||
use linux as platform;
|
||||
#[cfg(macos_platform)]
|
||||
use macos as platform;
|
||||
#[cfg(orbital_platform)]
|
||||
use self::orbital as platform;
|
||||
use orbital as platform;
|
||||
#[cfg(web_platform)]
|
||||
use self::web as platform;
|
||||
use web as platform;
|
||||
#[cfg(windows_platform)]
|
||||
use self::windows as platform;
|
||||
use windows as platform;
|
||||
|
||||
pub use self::platform::*;
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ use orbclient::{
|
||||
};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::{self, Ime, Modifiers, StartCause};
|
||||
use crate::event_loop::{self, ControlFlow, DeviceEvents};
|
||||
@@ -21,7 +20,7 @@ use crate::keyboard::{
|
||||
PhysicalKey,
|
||||
};
|
||||
use crate::window::{
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, WindowId as RootWindowId,
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId as RootWindowId,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -272,18 +271,16 @@ impl EventState {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop {
|
||||
pub struct EventLoop<T> {
|
||||
windows: Vec<(Arc<RedoxSocket>, EventState)>,
|
||||
window_target: event_loop::ActiveEventLoop,
|
||||
user_events_sender: mpsc::SyncSender<()>,
|
||||
user_events_receiver: mpsc::Receiver<()>,
|
||||
user_events_sender: mpsc::Sender<T>,
|
||||
user_events_receiver: mpsc::Receiver<T>,
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
|
||||
// NOTE: Create a channel which can hold only one event to automatically _squash_ user
|
||||
// events.
|
||||
let (user_events_sender, user_events_receiver) = mpsc::sync_channel(1);
|
||||
let (user_events_sender, user_events_receiver) = mpsc::channel();
|
||||
|
||||
let event_socket = Arc::new(
|
||||
RedoxSocket::event()
|
||||
@@ -325,13 +322,14 @@ impl EventLoop {
|
||||
})
|
||||
}
|
||||
|
||||
fn process_event<A: ApplicationHandler>(
|
||||
fn process_event<F>(
|
||||
window_id: WindowId,
|
||||
event_option: EventOption,
|
||||
event_state: &mut EventState,
|
||||
window_target: &event_loop::ActiveEventLoop,
|
||||
app: &mut A,
|
||||
) {
|
||||
mut event_handler: F,
|
||||
) where
|
||||
F: FnMut(event::Event<T>),
|
||||
{
|
||||
match event_option {
|
||||
EventOption::Key(KeyEvent { character, scancode, pressed }) => {
|
||||
// Convert scancode
|
||||
@@ -373,128 +371,125 @@ impl EventLoop {
|
||||
key_without_modifiers = logical_key.clone();
|
||||
}
|
||||
|
||||
let window_id = RootWindowId(window_id);
|
||||
let event = event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
event: event::KeyEvent {
|
||||
logical_key,
|
||||
physical_key,
|
||||
location: KeyLocation::Standard,
|
||||
state: element_state(pressed),
|
||||
repeat: false,
|
||||
text,
|
||||
platform_specific: KeyEventExtra {
|
||||
key_without_modifiers,
|
||||
text_with_all_modifiers,
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
event: event::KeyEvent {
|
||||
logical_key,
|
||||
physical_key,
|
||||
location: KeyLocation::Standard,
|
||||
state: element_state(pressed),
|
||||
repeat: false,
|
||||
text,
|
||||
platform_specific: KeyEventExtra {
|
||||
key_without_modifiers,
|
||||
text_with_all_modifiers,
|
||||
},
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
is_synthetic: false,
|
||||
};
|
||||
|
||||
app.window_event(window_target, window_id, event);
|
||||
});
|
||||
|
||||
// If the state of the modifiers has changed, send the event.
|
||||
if modifiers_before != event_state.keyboard {
|
||||
app.window_event(
|
||||
window_target,
|
||||
window_id,
|
||||
event::WindowEvent::ModifiersChanged(event_state.modifiers()),
|
||||
);
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::ModifiersChanged(event_state.modifiers()),
|
||||
})
|
||||
}
|
||||
},
|
||||
EventOption::TextInput(TextInputEvent { character }) => {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Ime(Ime::Preedit("".into(), None)),
|
||||
);
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Ime(Ime::Commit(character.into())),
|
||||
);
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Ime(Ime::Preedit("".into(), None)),
|
||||
});
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Ime(Ime::Commit(character.into())),
|
||||
});
|
||||
},
|
||||
EventOption::Mouse(MouseEvent { x, y }) => {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::CursorMoved {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::CursorMoved {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
position: (x, y).into(),
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => {
|
||||
app.device_event(
|
||||
window_target,
|
||||
event::DeviceId(DeviceId),
|
||||
event::DeviceEvent::MouseMotion { delta: (dx as f64, dy as f64) },
|
||||
);
|
||||
event_handler(event::Event::DeviceEvent {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
event: 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,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::MouseInput {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::MouseInput {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
state,
|
||||
button,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
EventOption::Scroll(ScrollEvent { x, y }) => {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::MouseWheel {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: 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,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::CloseRequested,
|
||||
);
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::CloseRequested,
|
||||
});
|
||||
},
|
||||
EventOption::Focus(FocusEvent { focused }) => {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Focused(focused),
|
||||
);
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Focused(focused),
|
||||
});
|
||||
},
|
||||
EventOption::Move(MoveEvent { x, y }) => {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Moved((x, y).into()),
|
||||
);
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Moved((x, y).into()),
|
||||
});
|
||||
},
|
||||
EventOption::Resize(ResizeEvent { width, height }) => {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Resized((width, height).into()),
|
||||
);
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Resized((width, height).into()),
|
||||
});
|
||||
|
||||
// Acknowledge resize after event loop.
|
||||
event_state.resize_opt = Some((width, height));
|
||||
},
|
||||
// TODO: Screen, Clipboard, Drop
|
||||
EventOption::Hover(HoverEvent { entered }) => {
|
||||
let event = if entered {
|
||||
event::WindowEvent::CursorEntered { device_id: event::DeviceId(DeviceId) }
|
||||
if entered {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::CursorEntered {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
event::WindowEvent::CursorLeft { device_id: event::DeviceId(DeviceId) }
|
||||
};
|
||||
|
||||
app.window_event(window_target, RootWindowId(window_id), event);
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::CursorLeft {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
other => {
|
||||
tracing::warn!("unhandled event: {:?}", other);
|
||||
@@ -502,13 +497,22 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
pub fn run<F>(mut self, mut event_handler_inner: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(event::Event<T>, &event_loop::ActiveEventLoop),
|
||||
{
|
||||
let mut event_handler =
|
||||
move |event: event::Event<T>, window_target: &event_loop::ActiveEventLoop| {
|
||||
event_handler_inner(event, window_target);
|
||||
};
|
||||
|
||||
let mut start_cause = StartCause::Init;
|
||||
|
||||
loop {
|
||||
app.new_events(&self.window_target, start_cause);
|
||||
event_handler(event::Event::NewEvents(start_cause), &self.window_target);
|
||||
|
||||
if start_cause == StartCause::Init {
|
||||
app.resumed(&self.window_target);
|
||||
event_handler(event::Event::Resumed, &self.window_target);
|
||||
}
|
||||
|
||||
// Handle window creates.
|
||||
@@ -524,15 +528,23 @@ 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::Resized((properties.w, properties.h).into());
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
event_handler(
|
||||
event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Resized((properties.w, properties.h).into()),
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
|
||||
// Send moved event on create to indicate first position.
|
||||
let event = event::WindowEvent::Moved((properties.x, properties.y).into());
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
// Send resize event on create to indicate first position.
|
||||
event_handler(
|
||||
event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Moved((properties.x, properties.y).into()),
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
}
|
||||
|
||||
// Handle window destroys.
|
||||
@@ -540,8 +552,14 @@ impl EventLoop {
|
||||
let mut destroys = self.window_target.p.destroys.lock().unwrap();
|
||||
destroys.pop_front()
|
||||
} {
|
||||
let window_id = RootWindowId(destroy_id);
|
||||
app.window_event(&self.window_target, window_id, event::WindowEvent::Destroyed);
|
||||
event_handler(
|
||||
event::Event::WindowEvent {
|
||||
window_id: RootWindowId(destroy_id),
|
||||
event: event::WindowEvent::Destroyed,
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
|
||||
self.windows.retain(|(window, _event_state)| window.fd as u64 != destroy_id.fd);
|
||||
}
|
||||
|
||||
@@ -568,8 +586,7 @@ impl EventLoop {
|
||||
window_id,
|
||||
orbital_event.to_option(),
|
||||
event_state,
|
||||
&self.window_target,
|
||||
app,
|
||||
|event| event_handler(event, &self.window_target),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -596,8 +613,8 @@ impl EventLoop {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
while self.user_events_receiver.try_recv().is_ok() {
|
||||
app.proxy_wake_up(&self.window_target);
|
||||
while let Ok(event) = self.user_events_receiver.try_recv() {
|
||||
event_handler(event::Event::UserEvent(event), &self.window_target);
|
||||
}
|
||||
|
||||
// To avoid deadlocks the redraws lock is not held during event processing.
|
||||
@@ -605,14 +622,16 @@ impl EventLoop {
|
||||
let mut redraws = self.window_target.p.redraws.lock().unwrap();
|
||||
redraws.pop_front()
|
||||
} {
|
||||
app.window_event(
|
||||
event_handler(
|
||||
event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::RedrawRequested,
|
||||
},
|
||||
&self.window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::RedrawRequested,
|
||||
);
|
||||
}
|
||||
|
||||
app.about_to_wait(&self.window_target);
|
||||
event_handler(event::Event::AboutToWait, &self.window_target);
|
||||
|
||||
if self.window_target.p.exiting() {
|
||||
break;
|
||||
@@ -676,7 +695,7 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
app.exiting(&self.window_target);
|
||||
event_handler(event::Event::LoopExiting, &self.window_target);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -685,7 +704,7 @@ impl EventLoop {
|
||||
&self.window_target
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy {
|
||||
user_events_sender: self.user_events_sender.clone(),
|
||||
wake_socket: self.window_target.p.wake_socket.clone(),
|
||||
@@ -693,22 +712,24 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy {
|
||||
user_events_sender: mpsc::SyncSender<()>,
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
user_events_sender: mpsc::Sender<T>,
|
||||
wake_socket: Arc<TimeSocket>,
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
// When we fail to send the event it means that we haven't woken up to read the previous
|
||||
// event.
|
||||
if self.user_events_sender.try_send(()).is_ok() {
|
||||
self.wake_socket.wake().unwrap();
|
||||
}
|
||||
impl<T> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed<T>> {
|
||||
self.user_events_sender
|
||||
.send(event)
|
||||
.map_err(|mpsc::SendError(x)| event_loop::EventLoopClosed(x))?;
|
||||
|
||||
self.wake_socket.wake().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for EventLoopProxy {
|
||||
impl<T> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
user_events_sender: self.user_events_sender.clone(),
|
||||
@@ -717,7 +738,7 @@ impl Clone for EventLoopProxy {
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpin for EventLoopProxy {}
|
||||
impl<T> Unpin for EventLoopProxy<T> {}
|
||||
|
||||
pub struct ActiveEventLoop {
|
||||
control_flow: Cell<ControlFlow>,
|
||||
@@ -754,6 +775,11 @@ impl ActiveEventLoop {
|
||||
rwh_05::RawDisplayHandle::Orbital(rwh_05::OrbitalDisplayHandle::empty())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn system_theme(&self) -> Option<Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
|
||||
@@ -154,7 +154,7 @@ impl<'a> WindowProperties<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for WindowProperties<'a> {
|
||||
impl fmt::Display for WindowProperties<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::future;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{self, RecvError, SendError, TryRecvError};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::task::Poll;
|
||||
|
||||
use super::AtomicWaker;
|
||||
@@ -10,35 +11,54 @@ pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let shared = Arc::new(Shared { closed: AtomicBool::new(false), waker: AtomicWaker::new() });
|
||||
|
||||
let sender = Sender { sender, shared: Arc::clone(&shared) };
|
||||
let receiver = Receiver { receiver, shared };
|
||||
let sender =
|
||||
Sender(Arc::new(SenderInner { sender: Mutex::new(sender), shared: Arc::clone(&shared) }));
|
||||
let receiver = Receiver { receiver: Rc::new(receiver), shared };
|
||||
|
||||
(sender, receiver)
|
||||
}
|
||||
|
||||
pub struct Sender<T> {
|
||||
sender: mpsc::Sender<T>,
|
||||
pub struct Sender<T>(Arc<SenderInner<T>>);
|
||||
|
||||
struct SenderInner<T> {
|
||||
// We need to wrap it into a `Mutex` to make it `Sync`. So the sender can't
|
||||
// be accessed on the main thread, as it could block. Additionally we need
|
||||
// to wrap `Sender` in an `Arc` to make it cloneable on the main thread without
|
||||
// having to block.
|
||||
sender: Mutex<mpsc::Sender<T>>,
|
||||
shared: Arc<Shared>,
|
||||
}
|
||||
|
||||
impl<T> Sender<T> {
|
||||
pub fn send(&self, event: T) -> Result<(), SendError<T>> {
|
||||
self.sender.send(event)?;
|
||||
self.shared.waker.wake();
|
||||
self.0.sender.lock().unwrap().send(event)?;
|
||||
self.0.shared.waker.wake();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Sender<T> {
|
||||
fn drop(&mut self) {
|
||||
impl<T> SenderInner<T> {
|
||||
fn close(&self) {
|
||||
self.shared.closed.store(true, Ordering::Relaxed);
|
||||
self.shared.waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Sender<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(Arc::clone(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for SenderInner<T> {
|
||||
fn drop(&mut self) {
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Receiver<T> {
|
||||
receiver: mpsc::Receiver<T>,
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
shared: Arc<Shared>,
|
||||
}
|
||||
|
||||
@@ -75,6 +95,18 @@ impl<T> Receiver<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Receiver<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { receiver: Rc::clone(&self.receiver), shared: Arc::clone(&self.shared) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Receiver<T> {
|
||||
fn drop(&mut self) {
|
||||
self.shared.closed.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
struct Shared {
|
||||
closed: AtomicBool,
|
||||
waker: AtomicWaker,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use super::super::main_thread::MainThreadMarker;
|
||||
use super::{channel, Receiver, Sender, Wrapper};
|
||||
use std::cell::Ref;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
pub struct Dispatcher<T: 'static>(Wrapper<T, Arc<Sender<Closure<T>>>, Closure<T>>);
|
||||
pub struct Dispatcher<T: 'static>(Wrapper<T, Sender<Closure<T>>, Closure<T>>);
|
||||
|
||||
struct Closure<T>(Box<dyn FnOnce(&T) + Send>);
|
||||
|
||||
@@ -12,8 +11,6 @@ impl<T> Dispatcher<T> {
|
||||
#[track_caller]
|
||||
pub fn new(main_thread: MainThreadMarker, value: T) -> Option<(Self, DispatchRunner<T>)> {
|
||||
let (sender, receiver) = channel::<Closure<T>>();
|
||||
let sender = Arc::new(sender);
|
||||
let receiver = Rc::new(receiver);
|
||||
|
||||
Wrapper::new(
|
||||
main_thread,
|
||||
@@ -24,7 +21,7 @@ impl<T> Dispatcher<T> {
|
||||
closure(value.borrow().as_ref().unwrap())
|
||||
},
|
||||
{
|
||||
let receiver = Rc::clone(&receiver);
|
||||
let receiver = receiver.clone();
|
||||
move |value| async move {
|
||||
while let Ok(Closure(closure)) = receiver.next().await {
|
||||
// SAFETY: The given `Closure` here isn't really `'static`, so we shouldn't
|
||||
@@ -92,8 +89,8 @@ impl<T> Dispatcher<T> {
|
||||
}
|
||||
|
||||
pub struct DispatchRunner<T: 'static> {
|
||||
wrapper: Wrapper<T, Arc<Sender<Closure<T>>>, Closure<T>>,
|
||||
receiver: Rc<Receiver<Closure<T>>>,
|
||||
wrapper: Wrapper<T, Sender<Closure<T>>, Closure<T>>,
|
||||
receiver: Receiver<Closure<T>>,
|
||||
}
|
||||
|
||||
impl<T> DispatchRunner<T> {
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
use std::future;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::num::NonZeroUsize;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll;
|
||||
|
||||
use super::super::main_thread::MainThreadMarker;
|
||||
use super::{AtomicWaker, Wrapper};
|
||||
|
||||
pub struct WakerSpawner<T: 'static>(Wrapper<Handler<T>, Sender, ()>);
|
||||
pub struct WakerSpawner<T: 'static>(Wrapper<Handler<T>, Sender, NonZeroUsize>);
|
||||
|
||||
pub struct Waker<T: 'static>(Wrapper<Handler<T>, Sender, ()>);
|
||||
pub struct Waker<T: 'static>(Wrapper<Handler<T>, Sender, NonZeroUsize>);
|
||||
|
||||
struct Handler<T> {
|
||||
value: T,
|
||||
handler: fn(&T, bool),
|
||||
handler: fn(&T, NonZeroUsize, bool),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -20,9 +21,13 @@ struct Sender(Arc<Inner>);
|
||||
|
||||
impl<T> WakerSpawner<T> {
|
||||
#[track_caller]
|
||||
pub fn new(main_thread: MainThreadMarker, value: T, handler: fn(&T, bool)) -> Option<Self> {
|
||||
pub fn new(
|
||||
main_thread: MainThreadMarker,
|
||||
value: T,
|
||||
handler: fn(&T, NonZeroUsize, bool),
|
||||
) -> Option<Self> {
|
||||
let inner = Arc::new(Inner {
|
||||
awoken: AtomicBool::new(false),
|
||||
counter: AtomicUsize::new(0),
|
||||
waker: AtomicWaker::new(),
|
||||
closed: AtomicBool::new(false),
|
||||
});
|
||||
@@ -34,43 +39,49 @@ impl<T> WakerSpawner<T> {
|
||||
let wrapper = Wrapper::new(
|
||||
main_thread,
|
||||
handler,
|
||||
|handler, _| {
|
||||
|handler, count| {
|
||||
let handler = handler.borrow();
|
||||
let handler = handler.as_ref().unwrap();
|
||||
(handler.handler)(&handler.value, true);
|
||||
(handler.handler)(&handler.value, count, true);
|
||||
},
|
||||
{
|
||||
let inner = Arc::clone(&inner);
|
||||
|
||||
move |handler| async move {
|
||||
while future::poll_fn(|cx| {
|
||||
if inner.awoken.swap(false, Ordering::Relaxed) {
|
||||
Poll::Ready(true)
|
||||
} else {
|
||||
inner.waker.register(cx.waker());
|
||||
while let Some(count) = future::poll_fn(|cx| {
|
||||
let count = inner.counter.swap(0, Ordering::Relaxed);
|
||||
|
||||
if inner.awoken.swap(false, Ordering::Relaxed) {
|
||||
Poll::Ready(true)
|
||||
} else {
|
||||
if inner.closed.load(Ordering::Relaxed) {
|
||||
return Poll::Ready(false);
|
||||
match NonZeroUsize::new(count) {
|
||||
Some(count) => Poll::Ready(Some(count)),
|
||||
None => {
|
||||
inner.waker.register(cx.waker());
|
||||
|
||||
let count = inner.counter.swap(0, Ordering::Relaxed);
|
||||
|
||||
match NonZeroUsize::new(count) {
|
||||
Some(count) => Poll::Ready(Some(count)),
|
||||
None => {
|
||||
if inner.closed.load(Ordering::Relaxed) {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
},
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
.await
|
||||
{
|
||||
let handler = handler.borrow();
|
||||
let handler = handler.as_ref().unwrap();
|
||||
(handler.handler)(&handler.value, false);
|
||||
(handler.handler)(&handler.value, count, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
sender,
|
||||
|inner, _| {
|
||||
inner.0.awoken.store(true, Ordering::Relaxed);
|
||||
inner.0.counter.fetch_add(1, Ordering::Relaxed);
|
||||
inner.0.waker.wake();
|
||||
},
|
||||
)?;
|
||||
@@ -82,13 +93,13 @@ impl<T> WakerSpawner<T> {
|
||||
Waker(self.0.clone())
|
||||
}
|
||||
|
||||
pub fn take(&self) -> bool {
|
||||
pub fn fetch(&self) -> usize {
|
||||
debug_assert!(
|
||||
MainThreadMarker::new().is_some(),
|
||||
"this should only be called from the main thread"
|
||||
);
|
||||
|
||||
self.0.with_sender_data(|inner| inner.0.awoken.swap(false, Ordering::Relaxed))
|
||||
self.0.with_sender_data(|inner| inner.0.counter.swap(0, Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +114,7 @@ impl<T> Drop for WakerSpawner<T> {
|
||||
|
||||
impl<T> Waker<T> {
|
||||
pub fn wake(&self) {
|
||||
self.0.send(())
|
||||
self.0.send(NonZeroUsize::MIN)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +125,7 @@ impl<T> Clone for Waker<T> {
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
awoken: AtomicBool,
|
||||
counter: AtomicUsize,
|
||||
waker: AtomicWaker,
|
||||
closed: AtomicBool,
|
||||
}
|
||||
|
||||
@@ -542,8 +542,8 @@ fn from_rgba(
|
||||
//
|
||||
// We call `createImageBitmap()` before spawning the future,
|
||||
// to not have to clone the image buffer.
|
||||
let mut options = ImageBitmapOptions::new();
|
||||
options.premultiply_alpha(PremultiplyAlpha::None);
|
||||
let options = ImageBitmapOptions::new();
|
||||
options.set_premultiply_alpha(PremultiplyAlpha::None);
|
||||
let bitmap = JsFuture::from(
|
||||
window
|
||||
.create_image_bitmap_with_image_data_and_image_bitmap_options(&image_data, &options)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
pub struct DeviceId(pub i32);
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
||||
@@ -16,30 +16,48 @@ mod window_target;
|
||||
pub(crate) use proxy::EventLoopProxy;
|
||||
pub(crate) use window_target::{ActiveEventLoop, OwnedDisplayHandle};
|
||||
|
||||
pub struct EventLoop {
|
||||
pub struct EventLoop<T: 'static> {
|
||||
elw: RootActiveEventLoop,
|
||||
user_event_sender: Sender<T>,
|
||||
user_event_receiver: Receiver<T>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
impl EventLoop {
|
||||
impl<T> EventLoop<T> {
|
||||
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
|
||||
let (user_event_sender, user_event_receiver) = mpsc::channel();
|
||||
let elw = RootActiveEventLoop { p: ActiveEventLoop::new(), _marker: PhantomData };
|
||||
Ok(EventLoop { elw })
|
||||
Ok(EventLoop { elw, user_event_sender, user_event_receiver })
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(self, app: &mut A) -> ! {
|
||||
pub fn run<F>(self, mut event_handler: F) -> !
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
let target = RootActiveEventLoop { p: self.elw.p.clone(), _marker: PhantomData };
|
||||
|
||||
// SAFETY: Don't use `move` to make sure we leak the `event_handler` and `target`.
|
||||
let handler: Box<dyn FnMut(Event)> = Box::new(|event| handle_event(app, &target, event));
|
||||
|
||||
let handler: Box<dyn FnMut(Event<()>)> = Box::new(|event| {
|
||||
let event = match event.map_nonuser_event() {
|
||||
Ok(event) => event,
|
||||
Err(Event::UserEvent(())) => Event::UserEvent(
|
||||
self.user_event_receiver
|
||||
.try_recv()
|
||||
.expect("handler woken up without user event"),
|
||||
),
|
||||
Err(_) => unreachable!(),
|
||||
};
|
||||
event_handler(event, &target)
|
||||
});
|
||||
// SAFETY: The `transmute` is necessary because `run()` requires `'static`. This is safe
|
||||
// because this function will never return and all resources not cleaned up by the point we
|
||||
// `throw` will leak, making this actually `'static`.
|
||||
let handler = unsafe {
|
||||
std::mem::transmute::<Box<dyn FnMut(Event)>, Box<dyn FnMut(Event) + 'static>>(handler)
|
||||
std::mem::transmute::<Box<dyn FnMut(Event<()>)>, Box<dyn FnMut(Event<()>) + 'static>>(
|
||||
handler,
|
||||
)
|
||||
};
|
||||
self.elw.p.run(handler, false);
|
||||
|
||||
@@ -52,14 +70,31 @@ impl EventLoop {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
pub fn spawn_app<A: ApplicationHandler + 'static>(self, mut app: A) {
|
||||
pub fn spawn<F>(self, mut event_handler: F)
|
||||
where
|
||||
F: 'static + FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
let target = RootActiveEventLoop { p: self.elw.p.clone(), _marker: PhantomData };
|
||||
|
||||
self.elw.p.run(Box::new(move |event| handle_event(&mut app, &target, event)), true);
|
||||
self.elw.p.run(
|
||||
Box::new(move |event| {
|
||||
let event = match event.map_nonuser_event() {
|
||||
Ok(event) => event,
|
||||
Err(Event::UserEvent(())) => Event::UserEvent(
|
||||
self.user_event_receiver
|
||||
.try_recv()
|
||||
.expect("handler woken up without user event"),
|
||||
),
|
||||
Err(_) => unreachable!(),
|
||||
};
|
||||
event_handler(event, &target)
|
||||
}),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
EventLoopProxy::new(self.elw.p.waker())
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.elw.p.waker(), self.user_event_sender.clone())
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &RootActiveEventLoop {
|
||||
@@ -82,17 +117,3 @@ impl EventLoop {
|
||||
self.elw.wait_until_strategy()
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event<A: ApplicationHandler>(app: &mut A, target: &RootActiveEventLoop, event: Event) {
|
||||
match event {
|
||||
Event::NewEvents(cause) => app.new_events(target, cause),
|
||||
Event::WindowEvent { window_id, event } => app.window_event(target, window_id, event),
|
||||
Event::DeviceEvent { device_id, event } => app.device_event(target, device_id, event),
|
||||
Event::UserWakeUp => app.proxy_wake_up(target),
|
||||
Event::Suspended => app.suspended(target),
|
||||
Event::Resumed => app.resumed(target),
|
||||
Event::AboutToWait => app.about_to_wait(target),
|
||||
Event::LoopExiting => app.exiting(target),
|
||||
Event::MemoryWarning => app.memory_warning(target),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
use std::rc::Weak;
|
||||
use std::sync::mpsc::{SendError, Sender};
|
||||
|
||||
use super::runner::Execution;
|
||||
use crate::event_loop::EventLoopClosed;
|
||||
use crate::platform_impl::platform::r#async::Waker;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
runner: Waker<Weak<Execution>>,
|
||||
sender: Sender<T>,
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn new(runner: Waker<Weak<Execution>>) -> Self {
|
||||
Self { runner }
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn new(runner: Waker<Weak<Execution>>, sender: Sender<T>) -> Self {
|
||||
Self { runner, sender }
|
||||
}
|
||||
|
||||
pub fn wake_up(&self) {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.sender.send(event).map_err(|SendError(event)| EventLoopClosed(event))?;
|
||||
self.runner.wake();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { runner: self.runner.clone(), sender: self.sender.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
use std::iter;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::Deref;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Document, KeyboardEvent, PageTransitionEvent, PointerEvent, WheelEvent};
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
use super::super::main_thread::MainThreadMarker;
|
||||
use super::super::DeviceId;
|
||||
use super::backend;
|
||||
@@ -14,20 +26,9 @@ use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawne
|
||||
use crate::platform_impl::platform::window::Inner;
|
||||
use crate::window::WindowId;
|
||||
|
||||
use js_sys::Function;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
use std::rc::{Rc, Weak};
|
||||
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Document, KeyboardEvent, PageTransitionEvent, PointerEvent, WheelEvent};
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
pub struct Shared(Rc<Execution>);
|
||||
|
||||
pub(super) type EventHandler = dyn FnMut(Event);
|
||||
pub(super) type EventHandler = dyn FnMut(Event<()>);
|
||||
|
||||
impl Clone for Shared {
|
||||
fn clone(&self) -> Self {
|
||||
@@ -136,12 +137,13 @@ impl Shared {
|
||||
let document = window.document().expect("Failed to obtain document");
|
||||
|
||||
Shared(Rc::<Execution>::new_cyclic(|weak| {
|
||||
let proxy_spawner = WakerSpawner::new(main_thread, weak.clone(), |runner, local| {
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
Shared(runner).send_proxy_wake_up(local);
|
||||
}
|
||||
})
|
||||
.expect("`EventLoop` has to be created in the main thread");
|
||||
let proxy_spawner =
|
||||
WakerSpawner::new(main_thread, weak.clone(), |runner, count, local| {
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
Shared(runner).send_user_events(count, local)
|
||||
}
|
||||
})
|
||||
.expect("`EventLoop` has to be created in the main thread");
|
||||
|
||||
Execution {
|
||||
main_thread,
|
||||
@@ -201,7 +203,7 @@ impl Shared {
|
||||
// Set the event callback to use for the event loop runner
|
||||
// This the event callback is a fairly thin layer over the user-provided callback that closes
|
||||
// over a RootActiveEventLoop reference
|
||||
pub(crate) fn set_listener(&self, event_handler: Box<EventHandler>) {
|
||||
pub fn set_listener(&self, event_handler: Box<EventHandler>) {
|
||||
{
|
||||
let mut runner = self.0.runner.borrow_mut();
|
||||
assert!(matches!(*runner, RunnerEnum::Pending));
|
||||
@@ -243,21 +245,10 @@ impl Shared {
|
||||
return;
|
||||
}
|
||||
|
||||
let pointer_type = event.pointer_type();
|
||||
|
||||
if pointer_type != "mouse" {
|
||||
return;
|
||||
}
|
||||
|
||||
// chorded button event
|
||||
let device_id = RootDeviceId(DeviceId(event.pointer_id()));
|
||||
|
||||
if let Some(button) = backend::event::mouse_button(&event) {
|
||||
debug_assert_eq!(
|
||||
pointer_type, "mouse",
|
||||
"expect pointer type of a chorded button event to be a mouse"
|
||||
);
|
||||
|
||||
let state = if backend::event::mouse_buttons(&event).contains(button.into()) {
|
||||
ElementState::Pressed
|
||||
} else {
|
||||
@@ -321,10 +312,6 @@ impl Shared {
|
||||
return;
|
||||
}
|
||||
|
||||
if event.pointer_type() != "mouse" {
|
||||
return;
|
||||
}
|
||||
|
||||
let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
|
||||
runner.send_event(Event::DeviceEvent {
|
||||
device_id: RootDeviceId(DeviceId(event.pointer_id())),
|
||||
@@ -344,10 +331,6 @@ impl Shared {
|
||||
return;
|
||||
}
|
||||
|
||||
if event.pointer_type() != "mouse" {
|
||||
return;
|
||||
}
|
||||
|
||||
let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
|
||||
runner.send_event(Event::DeviceEvent {
|
||||
device_id: RootDeviceId(DeviceId(event.pointer_id())),
|
||||
@@ -368,7 +351,7 @@ impl Shared {
|
||||
}
|
||||
|
||||
runner.send_event(Event::DeviceEvent {
|
||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
||||
device_id: RootDeviceId(DeviceId::dummy()),
|
||||
event: DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key: backend::event::key_code(&event),
|
||||
state: ElementState::Pressed,
|
||||
@@ -386,7 +369,7 @@ impl Shared {
|
||||
}
|
||||
|
||||
runner.send_event(Event::DeviceEvent {
|
||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
||||
device_id: RootDeviceId(DeviceId::dummy()),
|
||||
event: DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key: backend::event::key_code(&event),
|
||||
state: ElementState::Released,
|
||||
@@ -464,11 +447,11 @@ impl Shared {
|
||||
self.send_events(iter::once(event));
|
||||
}
|
||||
|
||||
// Add a user event to the event loop runner.
|
||||
// Add a series of user events to the event loop runner
|
||||
//
|
||||
// This will schedule the event loop to wake up instead of waking it up immediately if its not
|
||||
// running.
|
||||
pub(crate) fn send_proxy_wake_up(&self, local: bool) {
|
||||
pub(crate) fn send_user_events(&self, count: NonZeroUsize, local: bool) {
|
||||
// If the event loop is closed, it should discard any new events
|
||||
if self.is_closed() {
|
||||
return;
|
||||
@@ -479,18 +462,14 @@ impl Shared {
|
||||
if let Ok(RunnerEnum::Running(_)) =
|
||||
self.0.runner.try_borrow().as_ref().map(Deref::deref)
|
||||
{
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_name = queueMicrotask)]
|
||||
fn queue_microtask(task: Function);
|
||||
}
|
||||
|
||||
queue_microtask(
|
||||
Closure::once_into_js({
|
||||
self.window().queue_microtask(
|
||||
&Closure::once_into_js({
|
||||
let this = Rc::downgrade(&self.0);
|
||||
move || {
|
||||
if let Some(shared) = this.upgrade() {
|
||||
Shared(shared).send_event(Event::UserWakeUp)
|
||||
Shared(shared).send_events(
|
||||
iter::repeat(Event::UserEvent(())).take(count.get()),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -501,7 +480,7 @@ impl Shared {
|
||||
}
|
||||
}
|
||||
|
||||
self.send_event(Event::UserWakeUp);
|
||||
self.send_events(iter::repeat(Event::UserEvent(())).take(count.get()))
|
||||
}
|
||||
|
||||
// Add a series of events to the event loop runner
|
||||
@@ -515,8 +494,13 @@ impl Shared {
|
||||
// If we can run the event processing right now, or need to queue this and wait for later
|
||||
let mut process_immediately = true;
|
||||
match self.0.runner.try_borrow().as_ref().map(Deref::deref) {
|
||||
// If the runner is attached but not running, we always wake it up.
|
||||
Ok(RunnerEnum::Running(_)) => (),
|
||||
Ok(RunnerEnum::Running(ref runner)) => {
|
||||
// If we're currently polling, queue this and wait for the poll() method to be
|
||||
// called.
|
||||
if let State::Poll { .. } = runner.state {
|
||||
process_immediately = false;
|
||||
}
|
||||
},
|
||||
Ok(RunnerEnum::Pending) => {
|
||||
// The runner still hasn't been attached: queue this event and wait for it to be
|
||||
process_immediately = false;
|
||||
@@ -644,10 +628,8 @@ impl Shared {
|
||||
|
||||
// Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle.
|
||||
events.extend(
|
||||
self.0
|
||||
.proxy_spawner
|
||||
.take()
|
||||
.then_some(Event::UserWakeUp)
|
||||
iter::repeat(Event::UserEvent(()))
|
||||
.take(self.0.proxy_spawner.fetch())
|
||||
.map(EventWrapper::from),
|
||||
);
|
||||
|
||||
@@ -735,7 +717,7 @@ impl Shared {
|
||||
// * The `register_redraw_request` closure.
|
||||
// * The `destroy_fn` closure.
|
||||
if self.0.event_loop_recreation.get() {
|
||||
crate::event_loop::EventLoopBuilder::allow_event_loop_recreation();
|
||||
crate::event_loop::EventLoopBuilder::<()>::allow_event_loop_recreation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,12 +797,12 @@ impl Shared {
|
||||
}
|
||||
|
||||
pub(crate) enum EventWrapper {
|
||||
Event(Event),
|
||||
Event(Event<()>),
|
||||
ScaleChange { canvas: Weak<RefCell<backend::Canvas>>, size: PhysicalSize<u32>, scale: f64 },
|
||||
}
|
||||
|
||||
impl From<Event> for EventWrapper {
|
||||
fn from(value: Event) -> Self {
|
||||
impl From<Event<()>> for EventWrapper {
|
||||
fn from(value: Event<()>) -> Self {
|
||||
Self::Event(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +55,7 @@ impl ActiveEventLoop {
|
||||
Self { runner: runner::Shared::new(), modifiers: ModifiersShared::default() }
|
||||
}
|
||||
|
||||
pub(crate) fn run(
|
||||
&self,
|
||||
event_handler: Box<runner::EventHandler>,
|
||||
event_loop_recreation: bool,
|
||||
) {
|
||||
pub fn run(&self, event_handler: Box<runner::EventHandler>, event_loop_recreation: bool) {
|
||||
self.runner.event_loop_recreation(event_loop_recreation);
|
||||
self.runner.set_listener(event_handler);
|
||||
}
|
||||
@@ -146,7 +142,7 @@ impl ActiveEventLoop {
|
||||
}
|
||||
});
|
||||
|
||||
let device_id = RootDeviceId(unsafe { DeviceId::dummy() });
|
||||
let device_id = RootDeviceId(DeviceId::dummy());
|
||||
|
||||
runner.send_events(
|
||||
iter::once(Event::WindowEvent {
|
||||
@@ -182,7 +178,7 @@ impl ActiveEventLoop {
|
||||
}
|
||||
});
|
||||
|
||||
let device_id = RootDeviceId(unsafe { DeviceId::dummy() });
|
||||
let device_id = RootDeviceId(DeviceId::dummy());
|
||||
|
||||
runner.send_events(
|
||||
iter::once(Event::WindowEvent {
|
||||
@@ -262,21 +258,6 @@ impl ActiveEventLoop {
|
||||
});
|
||||
|
||||
canvas.on_cursor_move(
|
||||
{
|
||||
let runner = self.runner.clone();
|
||||
let has_focus = has_focus.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers| {
|
||||
if has_focus.get() && modifiers.get() != active_modifiers {
|
||||
modifiers.set(active_modifiers);
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
let runner = self.runner.clone();
|
||||
let has_focus = has_focus.clone();
|
||||
@@ -376,20 +357,6 @@ impl ActiveEventLoop {
|
||||
);
|
||||
|
||||
canvas.on_mouse_press(
|
||||
{
|
||||
let runner = self.runner.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers| {
|
||||
if modifiers.get() != active_modifiers {
|
||||
modifiers.set(active_modifiers);
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
let runner = self.runner.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
@@ -454,21 +421,6 @@ impl ActiveEventLoop {
|
||||
);
|
||||
|
||||
canvas.on_mouse_release(
|
||||
{
|
||||
let runner = self.runner.clone();
|
||||
let has_focus = has_focus.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers| {
|
||||
if has_focus.get() && modifiers.get() != active_modifiers {
|
||||
modifiers.set(active_modifiers);
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
let runner = self.runner.clone();
|
||||
let has_focus = has_focus.clone();
|
||||
@@ -609,7 +561,7 @@ impl ActiveEventLoop {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::Resized(new_size),
|
||||
});
|
||||
runner.request_redraw(RootWindowId(id));
|
||||
canvas.request_animation_frame();
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -662,6 +614,16 @@ impl ActiveEventLoop {
|
||||
self.runner.listen_device_events(allowed)
|
||||
}
|
||||
|
||||
pub fn system_theme(&self) -> Option<Theme> {
|
||||
backend::is_dark_mode(self.runner.window()).map(|is_dark_mode| {
|
||||
if is_dark_mode {
|
||||
Theme::Dark
|
||||
} else {
|
||||
Theme::Light
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.runner.set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
@@ -329,51 +329,29 @@ impl Canvas {
|
||||
self.pointer_handler.on_cursor_enter(&self.common, handler)
|
||||
}
|
||||
|
||||
pub fn on_mouse_release<MOD, M, T>(
|
||||
&mut self,
|
||||
modifier_handler: MOD,
|
||||
mouse_handler: M,
|
||||
touch_handler: T,
|
||||
) where
|
||||
MOD: 'static + FnMut(ModifiersState),
|
||||
pub fn on_mouse_release<M, T>(&mut self, mouse_handler: M, touch_handler: T)
|
||||
where
|
||||
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
||||
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
||||
{
|
||||
self.pointer_handler.on_mouse_release(
|
||||
&self.common,
|
||||
modifier_handler,
|
||||
mouse_handler,
|
||||
touch_handler,
|
||||
)
|
||||
self.pointer_handler.on_mouse_release(&self.common, mouse_handler, touch_handler)
|
||||
}
|
||||
|
||||
pub fn on_mouse_press<MOD, M, T>(
|
||||
&mut self,
|
||||
modifier_handler: MOD,
|
||||
mouse_handler: M,
|
||||
touch_handler: T,
|
||||
) where
|
||||
MOD: 'static + FnMut(ModifiersState),
|
||||
pub fn on_mouse_press<M, T>(&mut self, mouse_handler: M, touch_handler: T)
|
||||
where
|
||||
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
||||
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
||||
{
|
||||
self.pointer_handler.on_mouse_press(
|
||||
&self.common,
|
||||
modifier_handler,
|
||||
mouse_handler,
|
||||
touch_handler,
|
||||
Rc::clone(&self.prevent_default),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn on_cursor_move<MOD, M, T, B>(
|
||||
&mut self,
|
||||
modifier_handler: MOD,
|
||||
mouse_handler: M,
|
||||
touch_handler: T,
|
||||
button_handler: B,
|
||||
) where
|
||||
MOD: 'static + FnMut(ModifiersState),
|
||||
pub fn on_cursor_move<M, T, B>(&mut self, mouse_handler: M, touch_handler: T, button_handler: B)
|
||||
where
|
||||
M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
|
||||
T: 'static
|
||||
+ FnMut(ModifiersState, i32, &mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>),
|
||||
@@ -381,7 +359,6 @@ impl Canvas {
|
||||
{
|
||||
self.pointer_handler.on_cursor_move(
|
||||
&self.common,
|
||||
modifier_handler,
|
||||
mouse_handler,
|
||||
touch_handler,
|
||||
button_handler,
|
||||
@@ -483,7 +460,7 @@ impl Canvas {
|
||||
pub(crate) fn handle_scale_change(
|
||||
&self,
|
||||
runner: &super::super::event_loop::runner::Shared,
|
||||
event_handler: impl FnOnce(crate::event::Event),
|
||||
event_handler: impl FnOnce(crate::event::Event<()>),
|
||||
current_size: PhysicalSize<u32>,
|
||||
scale: f64,
|
||||
) {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use crate::dpi::LogicalPosition;
|
||||
use crate::event::{MouseButton, MouseScrollDelta};
|
||||
use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey};
|
||||
|
||||
use dpi::{LogicalPosition, PhysicalPosition, Position};
|
||||
use smol_str::SmolStr;
|
||||
use std::cell::OnceCell;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{KeyboardEvent, MouseEvent, PointerEvent, WheelEvent};
|
||||
|
||||
use super::Engine;
|
||||
|
||||
bitflags::bitflags! {
|
||||
// https://www.w3.org/TR/pointerevents3/#the-buttons-property
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -95,42 +97,48 @@ pub fn mouse_position(event: &MouseEvent) -> LogicalPosition<f64> {
|
||||
LogicalPosition { x: event.offset_x(), y: event.offset_y() }
|
||||
}
|
||||
|
||||
// TODO: Remove this when Firefox supports correct movement values in coalesced events.
|
||||
// TODO: Remove this when Firefox supports correct movement values in coalesced events and browsers
|
||||
// have agreed on what coordinate space `movementX/Y` is using.
|
||||
// See <https://bugzilla.mozilla.org/show_bug.cgi?id=1753724>.
|
||||
pub struct MouseDelta(Option<MouseDeltaInner>);
|
||||
|
||||
pub struct MouseDeltaInner {
|
||||
old_position: LogicalPosition<f64>,
|
||||
old_delta: LogicalPosition<f64>,
|
||||
// See <https://github.com/w3c/pointerlock/issues/42>.
|
||||
pub enum MouseDelta {
|
||||
Chromium,
|
||||
Gecko { old_position: LogicalPosition<f64>, old_delta: LogicalPosition<f64> },
|
||||
Other,
|
||||
}
|
||||
|
||||
impl MouseDelta {
|
||||
pub fn init(window: &web_sys::Window, event: &PointerEvent) -> Self {
|
||||
// Firefox has wrong movement values in coalesced events, we will detect that by checking
|
||||
// for `pointerrawupdate` support. Presumably an implementation of `pointerrawupdate`
|
||||
// should require correct movement values, otherwise uncoalesced events might be broken as
|
||||
// well.
|
||||
Self((!has_pointer_raw_support(window) && has_coalesced_events_support(event)).then(|| {
|
||||
MouseDeltaInner {
|
||||
match super::engine(window) {
|
||||
Some(Engine::Chromium) => Self::Chromium,
|
||||
// Firefox has wrong movement values in coalesced events.
|
||||
Some(Engine::Gecko) if has_coalesced_events_support(event) => Self::Gecko {
|
||||
old_position: mouse_position(event),
|
||||
old_delta: LogicalPosition {
|
||||
x: event.movement_x() as f64,
|
||||
y: event.movement_y() as f64,
|
||||
},
|
||||
}
|
||||
}))
|
||||
old_delta: LogicalPosition::new(
|
||||
event.movement_x() as f64,
|
||||
event.movement_y() as f64,
|
||||
),
|
||||
},
|
||||
_ => Self::Other,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delta(&mut self, event: &MouseEvent) -> LogicalPosition<f64> {
|
||||
if let Some(inner) = &mut self.0 {
|
||||
let new_position = mouse_position(event);
|
||||
let x = new_position.x - inner.old_position.x + inner.old_delta.x;
|
||||
let y = new_position.y - inner.old_position.y + inner.old_delta.y;
|
||||
inner.old_position = new_position;
|
||||
inner.old_delta = LogicalPosition::new(0., 0.);
|
||||
LogicalPosition::new(x, y)
|
||||
} else {
|
||||
LogicalPosition { x: event.movement_x() as f64, y: event.movement_y() as f64 }
|
||||
pub fn delta(&mut self, event: &MouseEvent) -> Position {
|
||||
match self {
|
||||
MouseDelta::Chromium => {
|
||||
PhysicalPosition::new(event.movement_x(), event.movement_y()).into()
|
||||
},
|
||||
MouseDelta::Gecko { old_position, old_delta } => {
|
||||
let new_position = mouse_position(event);
|
||||
let x = new_position.x - old_position.x + old_delta.x;
|
||||
let y = new_position.y - old_position.y + old_delta.y;
|
||||
*old_position = new_position;
|
||||
*old_delta = LogicalPosition::new(0., 0.);
|
||||
LogicalPosition::new(x, y).into()
|
||||
},
|
||||
MouseDelta::Other => {
|
||||
LogicalPosition::new(event.movement_x(), event.movement_y()).into()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,29 +246,6 @@ pub fn pointer_move_event(event: PointerEvent) -> impl Iterator<Item = PointerEv
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove when all browsers implement it correctly.
|
||||
// See <https://github.com/rust-windowing/winit/issues/2875>.
|
||||
pub fn has_pointer_raw_support(window: &web_sys::Window) -> bool {
|
||||
thread_local! {
|
||||
static POINTER_RAW_SUPPORT: OnceCell<bool> = const { OnceCell::new() };
|
||||
}
|
||||
|
||||
POINTER_RAW_SUPPORT.with(|support| {
|
||||
*support.get_or_init(|| {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
type PointerRawSupport;
|
||||
|
||||
#[wasm_bindgen(method, getter, js_name = onpointerrawupdate)]
|
||||
fn has_on_pointerrawupdate(this: &PointerRawSupport) -> JsValue;
|
||||
}
|
||||
|
||||
let support: &PointerRawSupport = window.unchecked_ref();
|
||||
!support.has_on_pointerrawupdate().is_undefined()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Remove when Safari supports `getCoalescedEvents`.
|
||||
// See <https://bugs.webkit.org/show_bug.cgi?id=210454>.
|
||||
pub fn has_coalesced_events_support(event: &PointerEvent) -> bool {
|
||||
|
||||
@@ -9,6 +9,8 @@ mod pointer;
|
||||
mod resize_scaling;
|
||||
mod schedule;
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
pub use self::canvas::{Canvas, Style};
|
||||
pub use self::event::ButtonsState;
|
||||
pub use self::event_handle::EventListenerHandle;
|
||||
@@ -16,8 +18,13 @@ pub use self::resize_scaling::ResizeScaleHandle;
|
||||
pub use self::schedule::Schedule;
|
||||
|
||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||
use js_sys::Array;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use web_sys::{Document, HtmlCanvasElement, PageTransitionEvent, VisibilityState};
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{
|
||||
Document, HtmlCanvasElement, Navigator, PageTransitionEvent, VisibilityState, Window,
|
||||
};
|
||||
|
||||
pub fn throw(msg: &str) {
|
||||
wasm_bindgen::throw_str(msg);
|
||||
@@ -158,3 +165,69 @@ pub fn is_visible(document: &Document) -> bool {
|
||||
}
|
||||
|
||||
pub type RawCanvasType = HtmlCanvasElement;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Engine {
|
||||
Chromium,
|
||||
Gecko,
|
||||
WebKit,
|
||||
}
|
||||
|
||||
pub fn engine(window: &Window) -> Option<Engine> {
|
||||
static ENGINE: OnceLock<Option<Engine>> = OnceLock::new();
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(extends = Navigator)]
|
||||
type NavigatorExt;
|
||||
|
||||
#[wasm_bindgen(method, getter, js_name = userAgentData)]
|
||||
fn user_agent_data(this: &NavigatorExt) -> Option<NavigatorUaData>;
|
||||
|
||||
type NavigatorUaData;
|
||||
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn brands(this: &NavigatorUaData) -> Array;
|
||||
|
||||
type NavigatorUaBrandVersion;
|
||||
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn brand(this: &NavigatorUaBrandVersion) -> String;
|
||||
}
|
||||
|
||||
*ENGINE.get_or_init(|| {
|
||||
let navigator: NavigatorExt = window.navigator().unchecked_into();
|
||||
|
||||
if let Some(data) = navigator.user_agent_data() {
|
||||
for brand in data
|
||||
.brands()
|
||||
.iter()
|
||||
.map(NavigatorUaBrandVersion::unchecked_from_js)
|
||||
.map(|brand| brand.brand())
|
||||
{
|
||||
match brand.as_str() {
|
||||
"Chromium" => return Some(Engine::Chromium),
|
||||
// TODO: verify when Firefox actually implements it.
|
||||
"Gecko" => return Some(Engine::Gecko),
|
||||
// TODO: verify when Safari actually implements it.
|
||||
"WebKit" => return Some(Engine::WebKit),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
} else {
|
||||
let data = navigator.user_agent().ok()?;
|
||||
|
||||
if data.contains("Chrome/") {
|
||||
Some(Engine::Chromium)
|
||||
} else if data.contains("Gecko/") {
|
||||
Some(Engine::Gecko)
|
||||
} else if data.contains("AppleWebKit/") {
|
||||
Some(Engine::WebKit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ impl PointerHandler {
|
||||
// touch events are handled separately
|
||||
// handling them here would produce duplicate mouse events, inconsistent with
|
||||
// other platforms.
|
||||
let pointer_id = (event.pointer_type() == "mouse").then(|| event.pointer_id());
|
||||
let pointer_id = (event.pointer_type() != "touch").then(|| event.pointer_id());
|
||||
|
||||
handler(modifiers, pointer_id);
|
||||
}));
|
||||
@@ -61,20 +61,18 @@ impl PointerHandler {
|
||||
// touch events are handled separately
|
||||
// handling them here would produce duplicate mouse events, inconsistent with
|
||||
// other platforms.
|
||||
let pointer_id = (event.pointer_type() == "mouse").then(|| event.pointer_id());
|
||||
let pointer_id = (event.pointer_type() != "touch").then(|| event.pointer_id());
|
||||
|
||||
handler(modifiers, pointer_id);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_mouse_release<MOD, M, T>(
|
||||
pub fn on_mouse_release<M, T>(
|
||||
&mut self,
|
||||
canvas_common: &Common,
|
||||
mut modifier_handler: MOD,
|
||||
mut mouse_handler: M,
|
||||
mut touch_handler: T,
|
||||
) where
|
||||
MOD: 'static + FnMut(ModifiersState),
|
||||
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
||||
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
||||
{
|
||||
@@ -90,26 +88,23 @@ impl PointerHandler {
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||
Force::Normalized(event.pressure() as f64),
|
||||
),
|
||||
"mouse" => mouse_handler(
|
||||
_ => mouse_handler(
|
||||
modifiers,
|
||||
event.pointer_id(),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||
event::mouse_button(&event).expect("no mouse button released"),
|
||||
),
|
||||
_ => modifier_handler(modifiers),
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_mouse_press<MOD, M, T>(
|
||||
pub fn on_mouse_press<M, T>(
|
||||
&mut self,
|
||||
canvas_common: &Common,
|
||||
mut modifier_handler: MOD,
|
||||
mut mouse_handler: M,
|
||||
mut touch_handler: T,
|
||||
prevent_default: Rc<Cell<bool>>,
|
||||
) where
|
||||
MOD: 'static + FnMut(ModifiersState),
|
||||
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
||||
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
||||
{
|
||||
@@ -125,8 +120,9 @@ impl PointerHandler {
|
||||
}
|
||||
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
let pointer_type = &event.pointer_type();
|
||||
|
||||
match event.pointer_type().as_str() {
|
||||
match pointer_type.as_str() {
|
||||
"touch" => {
|
||||
touch_handler(
|
||||
modifiers,
|
||||
@@ -135,7 +131,7 @@ impl PointerHandler {
|
||||
Force::Normalized(event.pressure() as f64),
|
||||
);
|
||||
},
|
||||
"mouse" => {
|
||||
_ => {
|
||||
mouse_handler(
|
||||
modifiers,
|
||||
event.pointer_id(),
|
||||
@@ -143,27 +139,27 @@ impl PointerHandler {
|
||||
event::mouse_button(&event).expect("no mouse button pressed"),
|
||||
);
|
||||
|
||||
// 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(event.pointer_id());
|
||||
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(event.pointer_id());
|
||||
}
|
||||
},
|
||||
_ => modifier_handler(modifiers),
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_cursor_move<MOD, M, T, B>(
|
||||
pub fn on_cursor_move<M, T, B>(
|
||||
&mut self,
|
||||
canvas_common: &Common,
|
||||
mut modifier_handler: MOD,
|
||||
mut mouse_handler: M,
|
||||
mut touch_handler: T,
|
||||
mut button_handler: B,
|
||||
prevent_default: Rc<Cell<bool>>,
|
||||
) where
|
||||
MOD: 'static + FnMut(ModifiersState),
|
||||
M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
|
||||
T: 'static
|
||||
+ FnMut(ModifiersState, i32, &mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>),
|
||||
@@ -175,23 +171,10 @@ impl PointerHandler {
|
||||
Some(canvas_common.add_event("pointermove", move |event: PointerEvent| {
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
|
||||
let pointer_type = event.pointer_type();
|
||||
|
||||
if let "touch" | "mouse" = pointer_type.as_str() {
|
||||
} else {
|
||||
modifier_handler(modifiers);
|
||||
return;
|
||||
}
|
||||
|
||||
let id = event.pointer_id();
|
||||
|
||||
// chorded button event
|
||||
if let Some(button) = event::mouse_button(&event) {
|
||||
debug_assert_eq!(
|
||||
pointer_type, "mouse",
|
||||
"expect pointer type of a chorded button event to be a mouse"
|
||||
);
|
||||
|
||||
if prevent_default.get() {
|
||||
// prevent text selection
|
||||
event.prevent_default();
|
||||
@@ -212,13 +195,7 @@ impl PointerHandler {
|
||||
|
||||
// pointer move event
|
||||
let scale = super::scale_factor(&window);
|
||||
match pointer_type.as_str() {
|
||||
"mouse" => mouse_handler(
|
||||
modifiers,
|
||||
id,
|
||||
&mut event::pointer_move_event(event)
|
||||
.map(|event| event::mouse_position(&event).to_physical(scale)),
|
||||
),
|
||||
match event.pointer_type().as_str() {
|
||||
"touch" => touch_handler(
|
||||
modifiers,
|
||||
id,
|
||||
@@ -229,7 +206,12 @@ impl PointerHandler {
|
||||
)
|
||||
}),
|
||||
),
|
||||
_ => unreachable!("didn't return early before"),
|
||||
_ => mouse_handler(
|
||||
modifiers,
|
||||
id,
|
||||
&mut event::pointer_move_event(event)
|
||||
.map(|event| event::mouse_position(&event).to_physical(scale)),
|
||||
),
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -139,10 +139,9 @@ impl ResizeScaleInternal {
|
||||
|
||||
// Safari doesn't support `devicePixelContentBoxSize`
|
||||
if has_device_pixel_support() {
|
||||
observer.observe_with_options(
|
||||
canvas,
|
||||
ResizeObserverOptions::new().box_(ResizeObserverBoxOptions::DevicePixelContentBox),
|
||||
);
|
||||
let options = ResizeObserverOptions::new();
|
||||
options.set_box(ResizeObserverBoxOptions::DevicePixelContentBox);
|
||||
observer.observe_with_options(canvas, &options);
|
||||
} else {
|
||||
observer.observe(canvas);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ impl Schedule {
|
||||
let duration = duration
|
||||
.as_secs()
|
||||
.checked_mul(1000)
|
||||
.and_then(|secs| secs.checked_add(duration.subsec_micros().div_ceil(1000).into()))
|
||||
.and_then(|secs| secs.checked_add(duration_millis_ceil(duration).into()))
|
||||
.unwrap_or(u64::MAX);
|
||||
|
||||
options.delay(duration as f64);
|
||||
@@ -140,9 +140,7 @@ impl Schedule {
|
||||
.ok()
|
||||
.and_then(|secs: i32| secs.checked_mul(1000))
|
||||
.and_then(|secs: i32| {
|
||||
let millis: i32 = duration
|
||||
.subsec_micros()
|
||||
.div_ceil(1000)
|
||||
let millis: i32 = duration_millis_ceil(duration)
|
||||
.try_into()
|
||||
.expect("millis are somehow bigger then 1K");
|
||||
secs.checked_add(millis)
|
||||
@@ -191,7 +189,7 @@ impl Schedule {
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(|secs: u32| secs.checked_mul(1000))
|
||||
.and_then(|secs| secs.checked_add(duration.subsec_micros().div_ceil(1000)))
|
||||
.and_then(|secs| secs.checked_add(duration_millis_ceil(duration)))
|
||||
.unwrap_or(u32::MAX);
|
||||
|
||||
WORKER
|
||||
@@ -226,6 +224,20 @@ impl Drop for Schedule {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with `u32::div_ceil()` when we hit Rust v1.73.
|
||||
fn duration_millis_ceil(duration: Duration) -> u32 {
|
||||
let micros = duration.subsec_micros();
|
||||
|
||||
// From <https://doc.rust-lang.org/1.73.0/src/core/num/uint_macros.rs.html#2086-2094>.
|
||||
let d = micros / 1000;
|
||||
let r = micros % 1000;
|
||||
if r > 0 && 1000 > 0 {
|
||||
d + 1
|
||||
} else {
|
||||
d
|
||||
}
|
||||
}
|
||||
|
||||
fn has_scheduler_support(window: &web_sys::Window) -> bool {
|
||||
thread_local! {
|
||||
static SCHEDULER_SUPPORT: OnceCell<bool> = const { OnceCell::new() };
|
||||
@@ -274,8 +286,8 @@ struct ScriptUrl(String);
|
||||
impl ScriptUrl {
|
||||
fn new(script: &str) -> Self {
|
||||
let sequence = Array::of1(&script.into());
|
||||
let mut property = BlobPropertyBag::new();
|
||||
property.type_("text/javascript");
|
||||
let property = BlobPropertyBag::new();
|
||||
property.set_type("text/javascript");
|
||||
let blob = Blob::new_with_str_sequence_and_options(&sequence, &property)
|
||||
.expect("`new Blob()` should never throw");
|
||||
|
||||
|
||||
@@ -431,7 +431,7 @@ impl Drop for Inner {
|
||||
pub struct WindowId(pub(crate) u32);
|
||||
|
||||
impl WindowId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn should_use_dark_mode() -> bool {
|
||||
pub fn should_use_dark_mode() -> bool {
|
||||
should_apps_use_dark_mode() && !is_high_contrast()
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ pub struct FileDropHandlerData {
|
||||
pub interface: IDropTarget,
|
||||
refcount: AtomicUsize,
|
||||
window: HWND,
|
||||
send_event: Box<dyn Fn(Event)>,
|
||||
send_event: Box<dyn Fn(Event<()>)>,
|
||||
cursor_effect: u32,
|
||||
hovered_is_valid: bool, /* If the currently hovered item is not valid there must not be any
|
||||
* `HoveredFileCancelled` emitted */
|
||||
@@ -37,7 +37,7 @@ pub struct FileDropHandler {
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl FileDropHandler {
|
||||
pub(crate) fn new(window: HWND, send_event: Box<dyn Fn(Event)>) -> FileDropHandler {
|
||||
pub fn new(window: HWND, send_event: Box<dyn Fn(Event<()>)>) -> FileDropHandler {
|
||||
let data = Box::new(FileDropHandlerData {
|
||||
interface: IDropTarget { lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl },
|
||||
refcount: AtomicUsize::new(1),
|
||||
@@ -211,7 +211,7 @@ impl FileDropHandler {
|
||||
}
|
||||
|
||||
impl FileDropHandlerData {
|
||||
fn send_event(&self, event: Event) {
|
||||
fn send_event(&self, event: Event<()>) {
|
||||
(self.send_event)(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::c_void;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::windows::io::{AsRawHandle as _, FromRawHandle as _, OwnedHandle, RawHandle};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{mem, panic, ptr};
|
||||
@@ -15,13 +17,18 @@ use std::{mem, panic, ptr};
|
||||
use crate::utils::Lazy;
|
||||
|
||||
use windows_sys::Win32::Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE;
|
||||
use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, POINT, RECT, WPARAM};
|
||||
use windows_sys::Win32::Foundation::{
|
||||
GetLastError, FALSE, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_FAILED, WPARAM,
|
||||
};
|
||||
use windows_sys::Win32::Graphics::Gdi::{
|
||||
GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient,
|
||||
ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE,
|
||||
};
|
||||
use windows_sys::Win32::System::Ole::RevokeDragDrop;
|
||||
use windows_sys::Win32::System::Threading::{GetCurrentThreadId, INFINITE};
|
||||
use windows_sys::Win32::System::Threading::{
|
||||
CreateWaitableTimerExW, GetCurrentThreadId, SetWaitableTimer,
|
||||
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, INFINITE, TIMER_ALL_ACCESS,
|
||||
};
|
||||
use windows_sys::Win32::UI::Controls::{HOVER_DEFAULT, WM_MOUSELEAVE};
|
||||
use windows_sys::Win32::UI::Input::Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW};
|
||||
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
|
||||
@@ -37,15 +44,15 @@ use windows_sys::Win32::UI::Input::Touch::{
|
||||
use windows_sys::Win32::UI::Input::{RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE};
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect, GetCursorPos,
|
||||
GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW,
|
||||
RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos, TranslateMessage, CREATESTRUCTW,
|
||||
GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO,
|
||||
MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, 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,
|
||||
GetMenu, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW,
|
||||
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos, TranslateMessage,
|
||||
CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT,
|
||||
MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
|
||||
PT_TOUCH, QS_ALLEVENTS, 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_INPUT_DEVICE_CHANGE, WM_KEYDOWN,
|
||||
WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
|
||||
WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE,
|
||||
@@ -56,13 +63,12 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
||||
};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::{
|
||||
DeviceEvent, Event, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase, WindowEvent,
|
||||
};
|
||||
use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents};
|
||||
use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::platform::dark_mode::try_theme;
|
||||
@@ -81,16 +87,37 @@ use crate::platform_impl::platform::{
|
||||
raw_input, util, wrap_device_id, Fullscreen, WindowId, DEVICE_ID,
|
||||
};
|
||||
use crate::window::{
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, WindowId as RootWindowId,
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId as RootWindowId,
|
||||
};
|
||||
use runner::EventLoopRunner;
|
||||
use runner::{EventLoopRunner, EventLoopRunnerShared};
|
||||
|
||||
use super::window::set_skip_taskbar;
|
||||
use super::SelectedCursor;
|
||||
|
||||
/// some backends like macos uses an uninhabited `Never` type,
|
||||
/// on windows, `UserEvent`s are also dispatched through the
|
||||
/// WNDPROC callback, and due to the re-entrant nature of the
|
||||
/// callback, recursively delivered events must be queued in a
|
||||
/// buffer, the current implementation put this queue in
|
||||
/// `EventLoopRunner`, which is shared between the event pumping
|
||||
/// loop and the callback. because it's hard to decide from the
|
||||
/// outside whether a event needs to be buffered, I decided not
|
||||
/// use `Event<Never>` for the shared runner state, but use unit
|
||||
/// as a placeholder so user events can be buffered as usual,
|
||||
/// the real `UserEvent` is pulled from the mpsc channel directly
|
||||
/// when the placeholder event is delivered to the event handler
|
||||
pub(crate) struct UserEventPlaceholder;
|
||||
|
||||
// here below, the generic `EventLoopRunnerShared<T>` is replaced with
|
||||
// `EventLoopRunnerShared<UserEventPlaceholder>` so we can get rid
|
||||
// of the generic parameter T in types which don't depend on T.
|
||||
// this is the approach which requires minimum changes to current
|
||||
// backend implementation. it should be considered transitional
|
||||
// and should be refactored and cleaned up eventually, I hope.
|
||||
|
||||
pub(crate) struct WindowData {
|
||||
pub window_state: Arc<Mutex<WindowState>>,
|
||||
pub event_loop_runner: Rc<EventLoopRunner>,
|
||||
pub event_loop_runner: EventLoopRunnerShared<UserEventPlaceholder>,
|
||||
pub key_event_builder: KeyEventBuilder,
|
||||
pub _file_drop_handler: Option<FileDropHandler>,
|
||||
pub userdata_removed: Cell<bool>,
|
||||
@@ -98,7 +125,7 @@ pub(crate) struct WindowData {
|
||||
}
|
||||
|
||||
impl WindowData {
|
||||
fn send_event(&self, event: Event) {
|
||||
fn send_event(&self, event: Event<UserEventPlaceholder>) {
|
||||
self.event_loop_runner.send_event(event);
|
||||
}
|
||||
|
||||
@@ -108,11 +135,11 @@ impl WindowData {
|
||||
}
|
||||
|
||||
struct ThreadMsgTargetData {
|
||||
event_loop_runner: Rc<EventLoopRunner>,
|
||||
event_loop_runner: EventLoopRunnerShared<UserEventPlaceholder>,
|
||||
}
|
||||
|
||||
impl ThreadMsgTargetData {
|
||||
fn send_event(&self, event: Event) {
|
||||
fn send_event(&self, event: Event<UserEventPlaceholder>) {
|
||||
self.event_loop_runner.send_event(event);
|
||||
}
|
||||
}
|
||||
@@ -124,9 +151,15 @@ pub(crate) enum ProcResult {
|
||||
Value(isize),
|
||||
}
|
||||
|
||||
pub struct EventLoop {
|
||||
pub struct EventLoop<T: 'static> {
|
||||
user_event_sender: Sender<T>,
|
||||
user_event_receiver: Receiver<T>,
|
||||
window_target: RootAEL,
|
||||
msg_hook: Option<Box<dyn FnMut(*const c_void) -> bool + 'static>>,
|
||||
// It is a timer used on timed waits.
|
||||
// It is created lazily in case if we have `ControlFlow::WaitUntil`.
|
||||
// Keep it as a field to avoid recreating it on every `ControlFlow::WaitUntil`.
|
||||
high_resolution_timer: Option<OwnedHandle>,
|
||||
}
|
||||
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {
|
||||
@@ -144,10 +177,10 @@ impl Default for PlatformSpecificEventLoopAttributes {
|
||||
pub struct ActiveEventLoop {
|
||||
thread_id: u32,
|
||||
thread_msg_target: HWND,
|
||||
pub(crate) runner_shared: Rc<EventLoopRunner>,
|
||||
pub(crate) runner_shared: EventLoopRunnerShared<UserEventPlaceholder>,
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub(crate) fn new(
|
||||
attributes: &mut PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<Self, EventLoopError> {
|
||||
@@ -170,6 +203,7 @@ impl EventLoop {
|
||||
|
||||
let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target));
|
||||
|
||||
let (user_event_sender, user_event_receiver) = mpsc::channel();
|
||||
insert_event_target_window_data(thread_msg_target, runner_shared.clone());
|
||||
raw_input::register_all_mice_and_keyboards_for_raw_input(
|
||||
thread_msg_target,
|
||||
@@ -177,11 +211,14 @@ impl EventLoop {
|
||||
);
|
||||
|
||||
Ok(EventLoop {
|
||||
user_event_sender,
|
||||
user_event_receiver,
|
||||
window_target: RootAEL {
|
||||
p: ActiveEventLoop { thread_id, thread_msg_target, runner_shared },
|
||||
_marker: PhantomData,
|
||||
},
|
||||
msg_hook: attributes.msg_hook.take(),
|
||||
high_resolution_timer: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -189,43 +226,50 @@ impl EventLoop {
|
||||
&self.window_target
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
self.run_on_demand(event_handler)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
{
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
|
||||
let event_loop_windows_ref = &self.window_target;
|
||||
let user_event_receiver = &self.user_event_receiver;
|
||||
// # Safety
|
||||
// We make sure to call runner.clear_event_handler() before
|
||||
// returning
|
||||
unsafe {
|
||||
runner.set_event_handler(move |event| match event {
|
||||
Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause),
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(event_loop_windows_ref, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(event_loop_windows_ref, device_id, event)
|
||||
},
|
||||
Event::UserWakeUp => app.proxy_wake_up(event_loop_windows_ref),
|
||||
Event::Suspended => app.suspended(event_loop_windows_ref),
|
||||
Event::Resumed => app.resumed(event_loop_windows_ref),
|
||||
Event::AboutToWait => app.about_to_wait(event_loop_windows_ref),
|
||||
Event::LoopExiting => app.exiting(event_loop_windows_ref),
|
||||
Event::MemoryWarning => app.memory_warning(event_loop_windows_ref),
|
||||
runner.set_event_handler(move |event| {
|
||||
// the shared `EventLoopRunner` is not parameterized
|
||||
// `EventLoopProxy::send_event()` calls `PostMessage`
|
||||
// to wakeup and dispatch a placeholder `UserEvent`,
|
||||
// when we received the placeholder event here, the
|
||||
// real UserEvent(T) should already be put in the
|
||||
// mpsc channel and ready to be pulled
|
||||
let event = match event.map_nonuser_event() {
|
||||
Ok(non_user_event) => non_user_event,
|
||||
Err(_user_event_placeholder) => Event::UserEvent(
|
||||
user_event_receiver
|
||||
.try_recv()
|
||||
.expect("user event signaled but not received"),
|
||||
),
|
||||
};
|
||||
event_handler(event, event_loop_windows_ref)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let exit_code = loop {
|
||||
self.wait_and_dispatch_message(None);
|
||||
|
||||
self.wait_for_messages(None);
|
||||
// wait_for_messages calls user application before and after waiting
|
||||
// so it may have decided to exit.
|
||||
if let Some(code) = self.exit_code() {
|
||||
break code;
|
||||
}
|
||||
@@ -252,15 +296,14 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut event_handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
{
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
let event_loop_windows_ref = &self.window_target;
|
||||
// let user_event_receiver = &self.user_event_receiver;
|
||||
let user_event_receiver = &self.user_event_receiver;
|
||||
|
||||
// # Safety
|
||||
// We make sure to call runner.clear_event_handler() before
|
||||
@@ -270,28 +313,26 @@ impl EventLoop {
|
||||
// to leave the runner in an unsound state with an associated
|
||||
// event handler.
|
||||
unsafe {
|
||||
runner.set_event_handler(move |event| match event {
|
||||
Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause),
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(event_loop_windows_ref, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(event_loop_windows_ref, device_id, event)
|
||||
},
|
||||
Event::UserWakeUp => app.proxy_wake_up(event_loop_windows_ref),
|
||||
Event::Suspended => app.suspended(event_loop_windows_ref),
|
||||
Event::Resumed => app.resumed(event_loop_windows_ref),
|
||||
Event::AboutToWait => app.about_to_wait(event_loop_windows_ref),
|
||||
Event::LoopExiting => app.exiting(event_loop_windows_ref),
|
||||
Event::MemoryWarning => app.memory_warning(event_loop_windows_ref),
|
||||
runner.set_event_handler(move |event| {
|
||||
let event = match event.map_nonuser_event() {
|
||||
Ok(non_user_event) => non_user_event,
|
||||
Err(_user_event_placeholder) => Event::UserEvent(
|
||||
user_event_receiver
|
||||
.recv()
|
||||
.expect("user event signaled but not received"),
|
||||
),
|
||||
};
|
||||
event_handler(event, event_loop_windows_ref)
|
||||
});
|
||||
|
||||
runner.wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
self.wait_and_dispatch_message(timeout);
|
||||
|
||||
if self.exit_code().is_none() {
|
||||
self.wait_for_messages(timeout);
|
||||
}
|
||||
// wait_for_messages calls user application before and after waiting
|
||||
// so it may have decided to exit.
|
||||
if self.exit_code().is_none() {
|
||||
self.dispatch_peeked_messages();
|
||||
}
|
||||
@@ -321,101 +362,27 @@ impl EventLoop {
|
||||
status
|
||||
}
|
||||
|
||||
/// Wait for one message and dispatch it, optionally with a timeout
|
||||
fn wait_and_dispatch_message(&mut self, timeout: Option<Duration>) {
|
||||
fn get_msg_with_timeout(msg: &mut MSG, timeout: Option<Duration>) -> PumpStatus {
|
||||
unsafe {
|
||||
// A timeout of None means wait indefinitely (so we don't need to call SetTimer)
|
||||
let timer_id = timeout.map(|timeout| SetTimer(0, 0, dur2timeout(timeout), None));
|
||||
let get_status = GetMessageW(msg, 0, 0, 0);
|
||||
if let Some(timer_id) = timer_id {
|
||||
KillTimer(0, timer_id);
|
||||
}
|
||||
// A return value of 0 implies `WM_QUIT`
|
||||
if get_status == 0 {
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
PumpStatus::Continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the next MSG either via PeekMessage or GetMessage depending on whether the
|
||||
/// requested timeout is `ZERO` (and so we don't want to block)
|
||||
///
|
||||
/// Returns `None` if no MSG was read, else a `Continue` or `Exit` status
|
||||
fn wait_for_msg(msg: &mut MSG, timeout: Option<Duration>) -> Option<PumpStatus> {
|
||||
if timeout == Some(Duration::ZERO) {
|
||||
unsafe {
|
||||
if PeekMessageW(msg, 0, 0, 0, PM_REMOVE) != 0 {
|
||||
Some(PumpStatus::Continue)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Some(get_msg_with_timeout(msg, timeout))
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits until new event messages arrive to be peeked.
|
||||
/// Doesn't peek messages itself.
|
||||
///
|
||||
/// Parameter timeout is optional. This method would wait for the smaller timeout
|
||||
/// between the argument and a timeout from control flow.
|
||||
fn wait_for_messages(&mut self, timeout: Option<Duration>) {
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
|
||||
// We aim to be consistent with the MacOS backend which has a RunLoop
|
||||
// observer that will dispatch AboutToWait when about to wait for
|
||||
// events, and NewEvents after the RunLoop wakes up.
|
||||
//
|
||||
// We emulate similar behaviour by treating `GetMessage` as our wait
|
||||
// We emulate similar behaviour by treating `MsgWaitForMultipleObjectsEx` as our wait
|
||||
// point and wake up point (when it returns) and we drain all other
|
||||
// pending messages via `PeekMessage` until we come back to "wait" via
|
||||
// `GetMessage`
|
||||
// `MsgWaitForMultipleObjectsEx`.
|
||||
//
|
||||
runner.prepare_wait();
|
||||
|
||||
let control_flow_timeout = match runner.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
let start = Instant::now();
|
||||
Some(wait_deadline.saturating_duration_since(start))
|
||||
},
|
||||
};
|
||||
let timeout = min_timeout(control_flow_timeout, timeout);
|
||||
|
||||
// # Safety
|
||||
// The Windows API has no documented requirement for bitwise
|
||||
// initializing a `MSG` struct (it can be uninitialized memory for the C
|
||||
// API) and there's no API to construct or initialize a `MSG`. This
|
||||
// is the simplest way avoid uninitialized memory in Rust
|
||||
let mut msg = unsafe { mem::zeroed() };
|
||||
let msg_status = wait_for_msg(&mut msg, timeout);
|
||||
|
||||
wait_for_messages_impl(&mut self.high_resolution_timer, runner.control_flow(), timeout);
|
||||
// Before we potentially exit, make sure to consistently emit an event for the wake up
|
||||
runner.wakeup();
|
||||
|
||||
match msg_status {
|
||||
None => {}, // No MSG to dispatch
|
||||
Some(PumpStatus::Exit(code)) => {
|
||||
runner.set_exit_code(code);
|
||||
},
|
||||
Some(PumpStatus::Continue) => {
|
||||
unsafe {
|
||||
let handled = if let Some(callback) = self.msg_hook.as_deref_mut() {
|
||||
callback(&mut msg as *mut _ as *mut _)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !handled {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(payload) = runner.take_panic_error() {
|
||||
runner.reset_runner();
|
||||
panic::resume_unwind(payload);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch all queued messages via `PeekMessageW`
|
||||
@@ -434,7 +401,7 @@ impl EventLoop {
|
||||
// initializing a `MSG` struct (it can be uninitialized memory for the C
|
||||
// API) and there's no API to construct or initialize a `MSG`. This
|
||||
// is the simplest way avoid uninitialized memory in Rust
|
||||
let mut msg = unsafe { mem::zeroed() };
|
||||
let mut msg: MSG = unsafe { mem::zeroed() };
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
@@ -468,8 +435,11 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
EventLoopProxy { target_window: self.window_target.p.thread_msg_target }
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy {
|
||||
target_window: self.window_target.p.thread_msg_target,
|
||||
event_send: self.user_event_sender.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_code(&self) -> Option<i32> {
|
||||
@@ -521,6 +491,10 @@ impl ActiveEventLoop {
|
||||
raw_input::register_all_mice_and_keyboards_for_raw_input(self.thread_msg_target, allowed);
|
||||
}
|
||||
|
||||
pub fn system_theme(&self) -> Option<Theme> {
|
||||
Some(if super::dark_mode::should_use_dark_mode() { Theme::Dark } else { Theme::Light })
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.runner_shared.set_control_flow(control_flow)
|
||||
}
|
||||
@@ -590,7 +564,7 @@ impl OwnedDisplayHandle {
|
||||
fn main_thread_id() -> u32 {
|
||||
static mut MAIN_THREAD_ID: u32 = 0;
|
||||
|
||||
/// Function pointer used in CRT initialization section to set the above static field's value.
|
||||
// Function pointer used in CRT initialization section to set the above static field's value.
|
||||
|
||||
// Mark as used so this is not removable.
|
||||
#[used]
|
||||
@@ -641,7 +615,7 @@ fn dur2timeout(dur: Duration) -> u32 {
|
||||
.unwrap_or(INFINITE)
|
||||
}
|
||||
|
||||
impl Drop for EventLoop {
|
||||
impl<T> Drop for EventLoop<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
DestroyWindow(self.window_target.p.thread_msg_target);
|
||||
@@ -649,6 +623,143 @@ impl Drop for EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set upper limit for waiting time to avoid overflows.
|
||||
/// I chose 50 days as a limit because it is used in dur2timeout.
|
||||
const FIFTY_DAYS: Duration = Duration::from_secs(50_u64 * 24 * 60 * 60);
|
||||
/// Waitable timers use 100 ns intervals to indicate due time.
|
||||
/// <https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setwaitabletimer#parameters>
|
||||
/// And there is no point waiting using other ways for such small timings
|
||||
/// because they are even less precise (can overshoot by few ms).
|
||||
const MIN_WAIT: Duration = Duration::from_nanos(100);
|
||||
|
||||
fn create_high_resolution_timer() -> Option<OwnedHandle> {
|
||||
unsafe {
|
||||
let handle: HANDLE = CreateWaitableTimerExW(
|
||||
ptr::null(),
|
||||
ptr::null(),
|
||||
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
|
||||
TIMER_ALL_ACCESS,
|
||||
);
|
||||
// CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported only after
|
||||
// Win10 1803 but it is already default option for rustc
|
||||
// (std uses it to implement `std::thread::sleep`).
|
||||
if handle == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(OwnedHandle::from_raw_handle(handle as *mut c_void))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function should not return error if parameters are valid
|
||||
/// but there is no guarantee about that at MSDN docs
|
||||
/// so we return result of GetLastError if fail.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// timer must be a valid timer handle created by [create_high_resolution_timer].
|
||||
/// timeout divided by 100 nanoseconds must be more than 0 and less than i64::MAX.
|
||||
unsafe fn set_high_resolution_timer(timer: RawHandle, timeout: Duration) -> Result<(), u32> {
|
||||
const INTERVAL_NS: u32 = MIN_WAIT.subsec_nanos();
|
||||
const INTERVALS_IN_SEC: u64 = (Duration::from_secs(1).as_nanos() / INTERVAL_NS as u128) as u64;
|
||||
let intervals_to_wait: u64 =
|
||||
timeout.as_secs() * INTERVALS_IN_SEC + u64::from(timeout.subsec_nanos() / INTERVAL_NS);
|
||||
debug_assert!(intervals_to_wait < i64::MAX as u64, "Must be called with smaller duration",);
|
||||
// Use negative time to indicate relative time.
|
||||
let due_time: i64 = -(intervals_to_wait as i64);
|
||||
unsafe {
|
||||
let set_result = SetWaitableTimer(timer as HANDLE, &due_time, 0, None, ptr::null(), FALSE);
|
||||
if set_result != FALSE {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(GetLastError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation detail of [EventLoop::wait_for_messages].
|
||||
///
|
||||
/// Does actual system-level waiting and doesn't process any messages itself,
|
||||
/// including winits internal notifications about waiting and new messages arrival.
|
||||
fn wait_for_messages_impl(
|
||||
high_resolution_timer: &mut Option<OwnedHandle>,
|
||||
control_flow: ControlFlow,
|
||||
timeout: Option<Duration>,
|
||||
) {
|
||||
let timeout = {
|
||||
let control_flow_timeout = match control_flow {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
let start = Instant::now();
|
||||
Some(wait_deadline.saturating_duration_since(start))
|
||||
},
|
||||
};
|
||||
let timeout = min_timeout(timeout, control_flow_timeout);
|
||||
if timeout == Some(Duration::ZERO) {
|
||||
// Do not wait if we don't have time.
|
||||
return;
|
||||
}
|
||||
// Now we decided to wait so need to do some clamping
|
||||
// to avoid problems with overflow and calling WinAPI with invalid parameters.
|
||||
timeout
|
||||
.map(|t| t.min(FIFTY_DAYS))
|
||||
// If timeout is less than minimally supported by Windows,
|
||||
// increase it to that minimum. Who want less than microsecond delays anyway?
|
||||
.map(|t| t.max(MIN_WAIT))
|
||||
};
|
||||
|
||||
if timeout.is_some() && high_resolution_timer.is_none() {
|
||||
*high_resolution_timer = create_high_resolution_timer();
|
||||
}
|
||||
|
||||
let high_resolution_timer: Option<RawHandle> =
|
||||
high_resolution_timer.as_ref().map(OwnedHandle::as_raw_handle);
|
||||
|
||||
let use_timer: bool;
|
||||
if let (Some(handle), Some(timeout)) = (high_resolution_timer, timeout) {
|
||||
let res = unsafe {
|
||||
// Safety: handle can be Some only if we succeeded in creating high resolution
|
||||
// timer. We properly clamped timeout so it can be used as argument
|
||||
// to timer.
|
||||
set_high_resolution_timer(handle, timeout)
|
||||
};
|
||||
if let Err(error_code) = res {
|
||||
// We successfully got timer but failed to set it?
|
||||
// Should be some bug in our code.
|
||||
tracing::trace!("Failed to set high resolution timer: last error {}", error_code);
|
||||
use_timer = false;
|
||||
} else {
|
||||
use_timer = true;
|
||||
}
|
||||
} else {
|
||||
use_timer = false;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// Either:
|
||||
// 1. User wants to wait indefinely if timeout is not set.
|
||||
// 2. We failed to get and set high resolution timer and we need something instead of it.
|
||||
let wait_duration_ms = timeout.map(dur2timeout).unwrap_or(INFINITE);
|
||||
|
||||
let (num_handles, raw_handles) =
|
||||
if use_timer { (1, [high_resolution_timer.unwrap()]) } else { (0, [ptr::null_mut()]) };
|
||||
|
||||
let result = MsgWaitForMultipleObjectsEx(
|
||||
num_handles,
|
||||
raw_handles.as_ptr() as *const _,
|
||||
wait_duration_ms,
|
||||
QS_ALLEVENTS,
|
||||
MWMO_INPUTAVAILABLE,
|
||||
);
|
||||
if result == WAIT_FAILED {
|
||||
// Well, nothing smart to do in such case.
|
||||
// Treat it as spurious wake up.
|
||||
tracing::warn!("Failed to MsgWaitForMultipleObjectsEx: error code {}", GetLastError(),);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct EventLoopThreadExecutor {
|
||||
thread_id: u32,
|
||||
target_window: HWND,
|
||||
@@ -699,16 +810,27 @@ impl EventLoopThreadExecutor {
|
||||
|
||||
type ThreadExecFn = Box<Box<dyn FnMut()>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
target_window: HWND,
|
||||
event_send: Sender<T>,
|
||||
}
|
||||
unsafe impl<T: Send + 'static> Send for EventLoopProxy<T> {}
|
||||
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { target_window: self.target_window, event_send: self.event_send.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for EventLoopProxy {}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) };
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.event_send
|
||||
.send(event)
|
||||
.map(|result| {
|
||||
unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) };
|
||||
result
|
||||
})
|
||||
.map_err(|e| EventLoopClosed(e.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,7 +962,7 @@ fn create_event_target_window() -> HWND {
|
||||
|
||||
fn insert_event_target_window_data(
|
||||
thread_msg_target: HWND,
|
||||
event_loop_runner: Rc<EventLoopRunner>,
|
||||
event_loop_runner: EventLoopRunnerShared<UserEventPlaceholder>,
|
||||
) {
|
||||
let userdata = ThreadMsgTargetData { event_loop_runner };
|
||||
let input_ptr = Box::into_raw(Box::new(userdata));
|
||||
@@ -2386,7 +2508,7 @@ unsafe extern "system" fn thread_event_target_callback(
|
||||
// user event is still in the mpsc channel and will be pulled
|
||||
// once the placeholder event is delivered to the wrapper
|
||||
// `event_handler`
|
||||
userdata.send_event(Event::UserWakeUp);
|
||||
userdata.send_event(Event::UserEvent(UserEventPlaceholder));
|
||||
0
|
||||
},
|
||||
_ if msg == EXEC_MSG_ID.get() => {
|
||||
@@ -2401,7 +2523,7 @@ unsafe extern "system" fn thread_event_target_callback(
|
||||
if userdata_removed {
|
||||
drop(userdata);
|
||||
} else {
|
||||
Box::into_raw(userdata);
|
||||
Box::leak(userdata);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
use std::{mem, panic};
|
||||
@@ -15,9 +16,11 @@ use crate::window::WindowId;
|
||||
|
||||
use super::ControlFlow;
|
||||
|
||||
type EventHandler = Cell<Option<Box<dyn FnMut(Event)>>>;
|
||||
pub(crate) type EventLoopRunnerShared<T> = Rc<EventLoopRunner<T>>;
|
||||
|
||||
pub(crate) struct EventLoopRunner {
|
||||
type EventHandler<T> = Cell<Option<Box<dyn FnMut(Event<T>)>>>;
|
||||
|
||||
pub(crate) struct EventLoopRunner<T: 'static> {
|
||||
// The event loop's win32 handles
|
||||
pub(super) thread_msg_target: HWND,
|
||||
|
||||
@@ -30,8 +33,8 @@ pub(crate) struct EventLoopRunner {
|
||||
exit: Cell<Option<i32>>,
|
||||
runner_state: Cell<RunnerState>,
|
||||
last_events_cleared: Cell<Instant>,
|
||||
event_handler: EventHandler,
|
||||
event_buffer: RefCell<VecDeque<BufferedEvent>>,
|
||||
event_handler: EventHandler<T>,
|
||||
event_buffer: RefCell<VecDeque<BufferedEvent<T>>>,
|
||||
|
||||
panic_error: Cell<Option<PanicError>>,
|
||||
}
|
||||
@@ -52,13 +55,13 @@ pub(crate) enum RunnerState {
|
||||
Destroyed,
|
||||
}
|
||||
|
||||
enum BufferedEvent {
|
||||
Event(Event),
|
||||
enum BufferedEvent<T: 'static> {
|
||||
Event(Event<T>),
|
||||
ScaleFactorChanged(WindowId, f64, PhysicalSize<u32>),
|
||||
}
|
||||
|
||||
impl EventLoopRunner {
|
||||
pub(crate) fn new(thread_msg_target: HWND) -> EventLoopRunner {
|
||||
impl<T> EventLoopRunner<T> {
|
||||
pub(crate) fn new(thread_msg_target: HWND) -> EventLoopRunner<T> {
|
||||
EventLoopRunner {
|
||||
thread_msg_target,
|
||||
interrupt_msg_dispatch: Cell::new(false),
|
||||
@@ -85,12 +88,13 @@ impl EventLoopRunner {
|
||||
/// undefined behaviour.
|
||||
pub(crate) unsafe fn set_event_handler<F>(&self, f: F)
|
||||
where
|
||||
F: FnMut(Event),
|
||||
F: FnMut(Event<T>),
|
||||
{
|
||||
// Erase closure lifetime.
|
||||
// SAFETY: Caller upholds that the lifetime of the closure is upheld.
|
||||
let f =
|
||||
unsafe { mem::transmute::<Box<dyn FnMut(Event)>, Box<dyn FnMut(Event)>>(Box::new(f)) };
|
||||
let f = unsafe {
|
||||
mem::transmute::<Box<dyn FnMut(Event<T>)>, Box<dyn FnMut(Event<T>)>>(Box::new(f))
|
||||
};
|
||||
let old_event_handler = self.event_handler.replace(Some(f));
|
||||
assert!(old_event_handler.is_none());
|
||||
}
|
||||
@@ -120,7 +124,7 @@ impl EventLoopRunner {
|
||||
}
|
||||
|
||||
/// State retrieval functions.
|
||||
impl EventLoopRunner {
|
||||
impl<T> EventLoopRunner<T> {
|
||||
#[allow(unused)]
|
||||
pub fn thread_msg_target(&self) -> HWND {
|
||||
self.thread_msg_target
|
||||
@@ -162,7 +166,7 @@ impl EventLoopRunner {
|
||||
}
|
||||
|
||||
/// Misc. functions
|
||||
impl EventLoopRunner {
|
||||
impl<T> EventLoopRunner<T> {
|
||||
pub fn catch_unwind<R>(&self, f: impl FnOnce() -> R) -> Option<R> {
|
||||
let panic_error = self.panic_error.take();
|
||||
if panic_error.is_none() {
|
||||
@@ -192,7 +196,7 @@ impl EventLoopRunner {
|
||||
}
|
||||
|
||||
/// Event dispatch functions.
|
||||
impl EventLoopRunner {
|
||||
impl<T> EventLoopRunner<T> {
|
||||
pub(crate) fn prepare_wait(&self) {
|
||||
self.move_state_to(RunnerState::Idle);
|
||||
}
|
||||
@@ -201,7 +205,7 @@ impl EventLoopRunner {
|
||||
self.move_state_to(RunnerState::HandlingMainEvents);
|
||||
}
|
||||
|
||||
pub(crate) fn send_event(&self, event: Event) {
|
||||
pub(crate) fn send_event(&self, event: Event<T>) {
|
||||
if let Event::WindowEvent { event: WindowEvent::RedrawRequested, .. } = event {
|
||||
self.call_event_handler(event);
|
||||
// As a rule, to ensure that `pump_events` can't block an external event loop
|
||||
@@ -222,7 +226,7 @@ impl EventLoopRunner {
|
||||
self.move_state_to(RunnerState::Destroyed);
|
||||
}
|
||||
|
||||
fn call_event_handler(&self, event: Event) {
|
||||
fn call_event_handler(&self, event: Event<T>) {
|
||||
self.catch_unwind(|| {
|
||||
let mut event_handler = self.event_handler.take().expect(
|
||||
"either event handler is re-entrant (likely), or no event handler is registered \
|
||||
@@ -354,8 +358,8 @@ impl EventLoopRunner {
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferedEvent {
|
||||
pub fn from_event(event: Event) -> BufferedEvent {
|
||||
impl<T> BufferedEvent<T> {
|
||||
pub fn from_event(event: Event<T>) -> BufferedEvent<T> {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer },
|
||||
@@ -369,23 +373,23 @@ impl BufferedEvent {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch_event(self, dispatch: impl FnOnce(Event)) {
|
||||
pub fn dispatch_event(self, dispatch: impl FnOnce(Event<T>)) {
|
||||
match self {
|
||||
Self::Event(event) => dispatch(event),
|
||||
Self::ScaleFactorChanged(window_id, scale_factor, new_inner_size) => {
|
||||
let user_new_innner_size = Arc::new(Mutex::new(new_inner_size));
|
||||
let user_new_inner_size = Arc::new(Mutex::new(new_inner_size));
|
||||
dispatch(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&user_new_innner_size,
|
||||
&user_new_inner_size,
|
||||
)),
|
||||
},
|
||||
});
|
||||
let inner_size = *user_new_innner_size.lock().unwrap();
|
||||
let inner_size = *user_new_inner_size.lock().unwrap();
|
||||
|
||||
drop(user_new_innner_size);
|
||||
drop(user_new_inner_size);
|
||||
|
||||
if inner_size != new_inner_size {
|
||||
let window_flags = unsafe {
|
||||
|
||||
@@ -98,7 +98,7 @@ impl KeyEventBuilder {
|
||||
MatchResult::MessagesToDispatch(self.pending.complete_multi(key_events))
|
||||
},
|
||||
WM_KILLFOCUS => {
|
||||
// sythesize keyup events
|
||||
// synthesize keyup events
|
||||
let kbd_state = get_kbd_state();
|
||||
let key_events = Self::synthesize_kbd_state(ElementState::Released, &kbd_state);
|
||||
MatchResult::MessagesToDispatch(self.pending.complete_multi(key_events))
|
||||
@@ -334,11 +334,11 @@ impl KeyEventBuilder {
|
||||
// We are synthesizing the press event for caps-lock first for the following reasons:
|
||||
// 1. If caps-lock is *not* held down but *is* active, then we have to synthesize all
|
||||
// printable keys, respecting the caps-lock state.
|
||||
// 2. If caps-lock is held down, we could choose to sythesize its keypress after every other
|
||||
// key, in which case all other keys *must* be sythesized as if the caps-lock state was
|
||||
// be the opposite of what it currently is.
|
||||
// 2. If caps-lock is held down, we could choose to synthesize its keypress after every
|
||||
// other key, in which case all other keys *must* be sythesized as if the caps-lock state
|
||||
// was be the opposite of what it currently is.
|
||||
// --
|
||||
// For the sake of simplicity we are choosing to always sythesize
|
||||
// For the sake of simplicity we are choosing to always synthesize
|
||||
// caps-lock first, and always use the current caps-lock state
|
||||
// to determine the produced text
|
||||
if is_key_pressed!(VK_CAPITAL) {
|
||||
@@ -556,7 +556,7 @@ impl PartialKeyEventInfo {
|
||||
// We convert dead keys into their character.
|
||||
// The reason for this is that `key_without_modifiers` is designed for key-bindings,
|
||||
// but the US International layout treats `'` (apostrophe) as a dead key and the
|
||||
// reguar US layout treats it a character. In order for a single binding
|
||||
// regular US layout treats it a character. In order for a single binding
|
||||
// configuration to work with both layouts, we forward each dead key as a character.
|
||||
Key::Dead(k) => {
|
||||
if let Some(ch) = k {
|
||||
|
||||
@@ -67,7 +67,7 @@ unsafe impl Sync for PlatformSpecificWindowAttributes {}
|
||||
pub struct DeviceId(u32);
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId(0)
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ unsafe impl Send for WindowId {}
|
||||
unsafe impl Sync for WindowId {}
|
||||
|
||||
impl WindowId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
WindowId(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,39 +263,37 @@ pub fn get_keyboard_physical_key(keyboard: RAWKEYBOARD) -> Option<PhysicalKey> {
|
||||
scancode_to_physicalkey(scancode as u32)
|
||||
};
|
||||
if keyboard.VKey == VK_SHIFT {
|
||||
if let PhysicalKey::Code(code) = physical_key {
|
||||
match code {
|
||||
KeyCode::NumpadDecimal
|
||||
| KeyCode::Numpad0
|
||||
| KeyCode::Numpad1
|
||||
| KeyCode::Numpad2
|
||||
| KeyCode::Numpad3
|
||||
| KeyCode::Numpad4
|
||||
| KeyCode::Numpad5
|
||||
| KeyCode::Numpad6
|
||||
| KeyCode::Numpad7
|
||||
| KeyCode::Numpad8
|
||||
| KeyCode::Numpad9 => {
|
||||
// On Windows, holding the Shift key makes numpad keys behave as if NumLock
|
||||
// wasn't active. The way this is exposed to applications by the system is that
|
||||
// the application receives a fake key release event for the shift key at the
|
||||
// moment when the numpad key is pressed, just before receiving the numpad key
|
||||
// as well.
|
||||
//
|
||||
// The issue is that in the raw device event (here), the fake shift release
|
||||
// event reports the numpad key as the scancode. Unfortunately, the event
|
||||
// doesn't have any information to tell whether it's the
|
||||
// left shift or the right shift that needs to get the fake
|
||||
// release (or press) event so we don't forward this
|
||||
// event to the application at all.
|
||||
//
|
||||
// For more on this, read the article by Raymond Chen, titled:
|
||||
// "The shift key overrides NumLock"
|
||||
// https://devblogs.microsoft.com/oldnewthing/20040906-00/?p=37953
|
||||
return None;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
if let PhysicalKey::Code(
|
||||
KeyCode::NumpadDecimal
|
||||
| KeyCode::Numpad0
|
||||
| KeyCode::Numpad1
|
||||
| KeyCode::Numpad2
|
||||
| KeyCode::Numpad3
|
||||
| KeyCode::Numpad4
|
||||
| KeyCode::Numpad5
|
||||
| KeyCode::Numpad6
|
||||
| KeyCode::Numpad7
|
||||
| KeyCode::Numpad8
|
||||
| KeyCode::Numpad9,
|
||||
) = physical_key
|
||||
{
|
||||
// On Windows, holding the Shift key makes numpad keys behave as if NumLock
|
||||
// wasn't active. The way this is exposed to applications by the system is that
|
||||
// the application receives a fake key release event for the shift key at the
|
||||
// moment when the numpad key is pressed, just before receiving the numpad key
|
||||
// as well.
|
||||
//
|
||||
// The issue is that in the raw device event (here), the fake shift release
|
||||
// event reports the numpad key as the scancode. Unfortunately, the event
|
||||
// doesn't have any information to tell whether it's the
|
||||
// left shift or the right shift that needs to get the fake
|
||||
// release (or press) event so we don't forward this
|
||||
// event to the application at all.
|
||||
//
|
||||
// For more on this, read the article by Raymond Chen, titled:
|
||||
// "The shift key overrides NumLock"
|
||||
// https://devblogs.microsoft.com/oldnewthing/20040906-00/?p=37953
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1129,7 +1129,7 @@ pub(super) struct InitData<'a> {
|
||||
pub window: Option<Window>,
|
||||
}
|
||||
|
||||
impl<'a> InitData<'a> {
|
||||
impl InitData<'_> {
|
||||
unsafe fn create_window(&self, window: HWND) -> Window {
|
||||
// Register for touch events if applicable
|
||||
{
|
||||
@@ -1186,7 +1186,11 @@ impl<'a> InitData<'a> {
|
||||
let file_drop_runner = self.event_loop.runner_shared.clone();
|
||||
let file_drop_handler = FileDropHandler::new(
|
||||
win.window,
|
||||
Box::new(move |event| file_drop_runner.send_event(event)),
|
||||
Box::new(move |event| {
|
||||
if let Ok(e) = event.map_nonuser_event() {
|
||||
file_drop_runner.send_event(e)
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let handler_interface_ptr =
|
||||
|
||||
@@ -71,16 +71,13 @@ pub struct WindowId(pub(crate) platform_impl::WindowId);
|
||||
impl WindowId {
|
||||
/// Returns a dummy id, useful for unit testing.
|
||||
///
|
||||
/// # Safety
|
||||
/// # 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 [`WindowId`].
|
||||
///
|
||||
/// **Passing this into a winit function will result in undefined behavior.**
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
#[allow(unused_unsafe)]
|
||||
WindowId(unsafe { platform_impl::WindowId::dummy() })
|
||||
pub const fn dummy() -> Self {
|
||||
WindowId(platform_impl::WindowId::dummy())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,8 +417,8 @@ impl WindowAttributes {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS**: if `false`, [`NSWindowSharingNone`] is used but doesn't completely
|
||||
/// prevent all apps from reading the window content, for instance, QuickTime.
|
||||
/// - **macOS**: if `false`, [`NSWindowSharingNone`] is used but doesn't completely prevent all
|
||||
/// apps from reading the window content, for instance, QuickTime.
|
||||
/// - **iOS / Android / Web / x11 / Orbital:** Ignored.
|
||||
///
|
||||
/// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone
|
||||
@@ -469,8 +466,8 @@ impl WindowAttributes {
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Windows** : A child window has the WS_CHILD style and is confined
|
||||
/// to the client area of its parent window. For more information, see
|
||||
/// <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
|
||||
/// to the client area of its parent window. For more information, see
|
||||
/// <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
|
||||
/// - **X11**: A child window is confined to the client area of its parent window.
|
||||
/// - **Android / iOS / Wayland / Web:** Unsupported.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
@@ -533,9 +530,9 @@ impl Window {
|
||||
/// provided by XRandR.
|
||||
///
|
||||
/// If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use
|
||||
/// the XRandR scaling method. Generally speaking, you should try to configure the
|
||||
/// standard system variables to do what you want before resorting to
|
||||
/// `WINIT_X11_SCALE_FACTOR`.
|
||||
/// the XRandR scaling method. Generally speaking, you should try to configure the
|
||||
/// standard system variables to do what you want before resorting to
|
||||
/// `WINIT_X11_SCALE_FACTOR`.
|
||||
/// - **Wayland:** The scale factor is suggested by the compositor for each window individually
|
||||
/// by using the wp-fractional-scale protocol if available. Falls back to integer-scale
|
||||
/// factors otherwise.
|
||||
@@ -1277,7 +1274,8 @@ impl Window {
|
||||
///
|
||||
/// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are
|
||||
/// combined.
|
||||
/// - **iOS / Android / Web / Orbital:** Unsupported.
|
||||
/// - **iOS / Android:** This will show / hide the soft keyboard.
|
||||
/// - **Web / Orbital:** Unsupported.
|
||||
/// - **X11**: Enabling IME will disable dead keys reporting during compose.
|
||||
///
|
||||
/// [`Ime`]: crate::event::WindowEvent::Ime
|
||||
@@ -1374,14 +1372,14 @@ impl Window {
|
||||
self.window.maybe_queue_on_main(move |w| w.set_theme(theme))
|
||||
}
|
||||
|
||||
/// Returns the current window theme override.
|
||||
/// Returns the current window theme.
|
||||
///
|
||||
/// Returns `None` if the current theme is set as the system default, or if it cannot be
|
||||
/// determined on the current platform.
|
||||
/// Returns `None` if it cannot be determined on the current platform.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported, returns `None`.
|
||||
/// - **iOS / Android / x11 / Orbital:** Unsupported.
|
||||
/// - **Wayland:** Only returns theme overrides.
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
let _span = tracing::debug_span!("winit::Window::theme",).entered();
|
||||
@@ -1392,8 +1390,8 @@ impl Window {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS**: if `false`, [`NSWindowSharingNone`] is used but doesn't completely
|
||||
/// prevent all apps from reading the window content, for instance, QuickTime.
|
||||
/// - **macOS**: if `false`, [`NSWindowSharingNone`] is used but doesn't completely prevent all
|
||||
/// apps from reading the window content, for instance, QuickTime.
|
||||
/// - **iOS / Android / x11 / Wayland / Web / Orbital:** Unsupported.
|
||||
///
|
||||
/// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone
|
||||
|
||||
@@ -3,11 +3,16 @@ fn needs_send<T: Send>() {}
|
||||
|
||||
#[test]
|
||||
fn event_loop_proxy_send() {
|
||||
needs_send::<winit::event_loop::EventLoopProxy>();
|
||||
#[allow(dead_code)]
|
||||
fn is_send<T: 'static + Send>() {
|
||||
// ensures that `winit::EventLoopProxy<T: Send>` implements `Send`
|
||||
needs_send::<winit::event_loop::EventLoopProxy<T>>();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_send() {
|
||||
// ensures that `winit::Window` implements `Send`
|
||||
needs_send::<winit::window::Window>();
|
||||
}
|
||||
|
||||
@@ -18,6 +23,7 @@ fn window_builder_send() {
|
||||
|
||||
#[test]
|
||||
fn ids_send() {
|
||||
// ensures that the various `..Id` types implement `Send`
|
||||
needs_send::<winit::window::WindowId>();
|
||||
needs_send::<winit::event::DeviceId>();
|
||||
needs_send::<winit::monitor::MonitorHandle>();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user