mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 07:03:15 -04:00
Compare commits
58 Commits
dependabot
...
v0.30.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
8
.github/CODEOWNERS
vendored
8
.github/CODEOWNERS
vendored
@@ -1,6 +1,6 @@
|
|||||||
# Android
|
# Android
|
||||||
/src/platform/android.rs @msiglreith @MarijnS95
|
/src/platform/android.rs @MarijnS95
|
||||||
/src/platform_impl/android @msiglreith @MarijnS95
|
/src/platform_impl/android @MarijnS95
|
||||||
|
|
||||||
# iOS
|
# iOS
|
||||||
/src/platform/ios.rs @madsmtm
|
/src/platform/ios.rs @madsmtm
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
/src/platform_impl/web @daxpedda
|
/src/platform_impl/web @daxpedda
|
||||||
|
|
||||||
# Windows
|
# Windows
|
||||||
/src/platform/windows.rs @msiglreith
|
/src/platform/windows.rs @notgull
|
||||||
/src/platform_impl/windows @msiglreith
|
/src/platform_impl/windows @notgull
|
||||||
|
|
||||||
# Orbital (Redox OS)
|
# Orbital (Redox OS)
|
||||||
/src/platform/orbital.rs @jackpot51
|
/src/platform/orbital.rs @jackpot51
|
||||||
|
|||||||
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
name: Check formatting
|
name: Check formatting
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: taiki-e/checkout-action@v1
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
with:
|
with:
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: taiki-e/checkout-action@v1
|
||||||
- uses: taiki-e/install-action@v2
|
- uses: taiki-e/install-action@v2
|
||||||
with:
|
with:
|
||||||
tool: typos-cli
|
tool: typos-cli
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
CMD: ${{ matrix.platform.cmd }}
|
CMD: ${{ matrix.platform.cmd }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: taiki-e/checkout-action@v1
|
||||||
|
|
||||||
- name: Restore cache of cargo folder
|
- name: Restore cache of cargo folder
|
||||||
# We use `restore` and later `save`, so that we can create the key after
|
# We use `restore` and later `save`, so that we can create the key after
|
||||||
@@ -220,9 +220,24 @@ jobs:
|
|||||||
- { name: 'Windows', target: x86_64-pc-windows-gnu }
|
- { name: 'Windows', target: x86_64-pc-windows-gnu }
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: taiki-e/checkout-action@v1
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
log-level: error
|
log-level: error
|
||||||
arguments: --all-features --target ${{ matrix.platform.target }}
|
arguments: --all-features --target ${{ matrix.platform.target }}
|
||||||
|
|
||||||
|
swc:
|
||||||
|
name: Minimize JavaScript
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: taiki-e/checkout-action@v1
|
||||||
|
- name: Install SWC
|
||||||
|
run: sudo npm i -g @swc/cli
|
||||||
|
- name: Run SWC
|
||||||
|
run: |
|
||||||
|
swc src/platform_impl/web/web_sys/worker.js -o src/platform_impl/web/web_sys/worker.min.js
|
||||||
|
- name: Check for diff
|
||||||
|
run: |
|
||||||
|
[[ -z $(git status -s) ]]
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,8 +3,5 @@ target/
|
|||||||
rls/
|
rls/
|
||||||
.vscode/
|
.vscode/
|
||||||
*~
|
*~
|
||||||
*.wasm
|
|
||||||
*.ts
|
|
||||||
*.js
|
|
||||||
#*#
|
#*#
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
12
.swcrc
Normal file
12
.swcrc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"minify": true,
|
||||||
|
"jsc": {
|
||||||
|
"target": "es2022",
|
||||||
|
"minify": {
|
||||||
|
"compress": {
|
||||||
|
"unused": true
|
||||||
|
},
|
||||||
|
"mangle": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
Cargo.toml
70
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "winit"
|
name = "winit"
|
||||||
version = "0.29.15"
|
version = "0.30.5"
|
||||||
authors = [
|
authors = [
|
||||||
"The winit contributors",
|
"The winit contributors",
|
||||||
"Pierre Krieger <pierre.krieger1708@gmail.com>",
|
"Pierre Krieger <pierre.krieger1708@gmail.com>",
|
||||||
@@ -14,6 +14,7 @@ rust-version.workspace = true
|
|||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
exclude = ["/.cargo"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = [
|
features = [
|
||||||
@@ -70,7 +71,7 @@ rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
|
|||||||
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cfg_aliases = "0.2.0"
|
cfg_aliases = "0.2.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "2"
|
bitflags = "2"
|
||||||
@@ -85,11 +86,11 @@ rwh_06 = { package = "raw-window-handle", version = "0.6", features = [
|
|||||||
], optional = true }
|
], optional = true }
|
||||||
serde = { workspace = true, optional = true }
|
serde = { workspace = true, optional = true }
|
||||||
smol_str = "0.2.0"
|
smol_str = "0.2.0"
|
||||||
tracing = { version = "0.1.40", default_features = false }
|
tracing = { version = "0.1.40", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
image = { version = "0.25.0", default-features = false, features = ["png"] }
|
image = { version = "0.25.0", default-features = false, features = ["png"] }
|
||||||
tracing = { version = "0.1.40", default_features = false, features = ["log"] }
|
tracing = { version = "0.1.40", default-features = false, features = ["log"] }
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
winit = { path = ".", features = ["rwh_05"] }
|
winit = { path = ".", features = ["rwh_05"] }
|
||||||
|
|
||||||
@@ -102,36 +103,41 @@ softbuffer = { version = "0.4.0", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android-activity = "0.5.0"
|
android-activity = "0.6.0"
|
||||||
ndk = { version = "0.8.0", default-features = false }
|
ndk = { version = "0.9.0", default-features = false }
|
||||||
ndk-sys = "0.5.0"
|
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||||
core-foundation = "0.9.3"
|
core-foundation = "0.9.3"
|
||||||
objc2 = "0.5.1"
|
objc2 = "0.5.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
core-graphics = "0.23.1"
|
core-graphics = "0.23.1"
|
||||||
|
block2 = "0.5.1"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
|
[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
|
||||||
version = "0.2.0"
|
version = "0.2.2"
|
||||||
features = [
|
features = [
|
||||||
|
"block2",
|
||||||
"dispatch",
|
"dispatch",
|
||||||
"NSArray",
|
"NSArray",
|
||||||
"NSAttributedString",
|
"NSAttributedString",
|
||||||
"NSData",
|
"NSData",
|
||||||
"NSDictionary",
|
"NSDictionary",
|
||||||
|
"NSDistributedNotificationCenter",
|
||||||
"NSEnumerator",
|
"NSEnumerator",
|
||||||
|
"NSKeyValueObserving",
|
||||||
|
"NSNotification",
|
||||||
"NSObjCRuntime",
|
"NSObjCRuntime",
|
||||||
"NSString",
|
|
||||||
"NSPathUtilities",
|
"NSPathUtilities",
|
||||||
"NSProcessInfo",
|
"NSProcessInfo",
|
||||||
|
"NSRunLoop",
|
||||||
|
"NSString",
|
||||||
"NSThread",
|
"NSThread",
|
||||||
"NSValue",
|
"NSValue",
|
||||||
]
|
]
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies.objc2-app-kit]
|
[target.'cfg(target_os = "macos")'.dependencies.objc2-app-kit]
|
||||||
version = "0.2.0"
|
version = "0.2.2"
|
||||||
features = [
|
features = [
|
||||||
"NSAppearance",
|
"NSAppearance",
|
||||||
"NSApplication",
|
"NSApplication",
|
||||||
@@ -162,7 +168,7 @@ features = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[target.'cfg(target_os = "ios")'.dependencies.objc2-foundation]
|
[target.'cfg(target_os = "ios")'.dependencies.objc2-foundation]
|
||||||
version = "0.2.0"
|
version = "0.2.2"
|
||||||
features = [
|
features = [
|
||||||
"dispatch",
|
"dispatch",
|
||||||
"NSArray",
|
"NSArray",
|
||||||
@@ -175,6 +181,29 @@ features = [
|
|||||||
"NSSet",
|
"NSSet",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "ios")'.dependencies.objc2-ui-kit]
|
||||||
|
version = "0.2.2"
|
||||||
|
features = [
|
||||||
|
"UIApplication",
|
||||||
|
"UIDevice",
|
||||||
|
"UIEvent",
|
||||||
|
"UIGeometry",
|
||||||
|
"UIGestureRecognizer",
|
||||||
|
"UIOrientation",
|
||||||
|
"UIPanGestureRecognizer",
|
||||||
|
"UIPinchGestureRecognizer",
|
||||||
|
"UIResponder",
|
||||||
|
"UIRotationGestureRecognizer",
|
||||||
|
"UIScreen",
|
||||||
|
"UIScreenMode",
|
||||||
|
"UITapGestureRecognizer",
|
||||||
|
"UITouch",
|
||||||
|
"UITraitCollection",
|
||||||
|
"UIView",
|
||||||
|
"UIViewController",
|
||||||
|
"UIWindow",
|
||||||
|
]
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
unicode-segmentation = "1.7.1"
|
unicode-segmentation = "1.7.1"
|
||||||
|
|
||||||
@@ -210,7 +239,7 @@ features = [
|
|||||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.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 }
|
ahash = { version = "0.8.7", features = ["no-rng"], optional = true }
|
||||||
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
||||||
calloop = "0.12.3"
|
calloop = "0.13.0"
|
||||||
libc = "0.2.64"
|
libc = "0.2.64"
|
||||||
memmap2 = { version = "0.9.0", optional = true }
|
memmap2 = { version = "0.9.0", optional = true }
|
||||||
percent-encoding = { version = "2.0", optional = true }
|
percent-encoding = { version = "2.0", optional = true }
|
||||||
@@ -220,18 +249,18 @@ rustix = { version = "0.38.4", default-features = false, features = [
|
|||||||
"thread",
|
"thread",
|
||||||
"process",
|
"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",
|
"calloop",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
sctk-adwaita = { version = "0.9.0", default_features = false, optional = true }
|
sctk-adwaita = { version = "0.10.1", default-features = false, optional = true }
|
||||||
wayland-backend = { version = "0.3.0", default_features = false, features = [
|
wayland-backend = { version = "0.3.5", default-features = false, features = [
|
||||||
"client_system",
|
"client_system",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
wayland-client = { version = "0.31.1", optional = true }
|
wayland-client = { version = "0.31.4", optional = true }
|
||||||
wayland-protocols = { version = "0.31.0", features = [
|
wayland-protocols = { version = "0.32.2", features = [
|
||||||
"staging",
|
"staging",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
wayland-protocols-plasma = { version = "0.2.0", features = [
|
wayland-protocols-plasma = { version = "0.3.2", features = [
|
||||||
"client",
|
"client",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
x11-dl = { version = "2.19.1", optional = true }
|
x11-dl = { version = "2.19.1", optional = true }
|
||||||
@@ -256,6 +285,7 @@ features = [
|
|||||||
'AbortController',
|
'AbortController',
|
||||||
'AbortSignal',
|
'AbortSignal',
|
||||||
'Blob',
|
'Blob',
|
||||||
|
'BlobPropertyBag',
|
||||||
'console',
|
'console',
|
||||||
'CssStyleDeclaration',
|
'CssStyleDeclaration',
|
||||||
'Document',
|
'Document',
|
||||||
@@ -279,6 +309,7 @@ features = [
|
|||||||
'MediaQueryList',
|
'MediaQueryList',
|
||||||
'MessageChannel',
|
'MessageChannel',
|
||||||
'MessagePort',
|
'MessagePort',
|
||||||
|
'Navigator',
|
||||||
'Node',
|
'Node',
|
||||||
'PageTransitionEvent',
|
'PageTransitionEvent',
|
||||||
'PointerEvent',
|
'PointerEvent',
|
||||||
@@ -291,6 +322,7 @@ features = [
|
|||||||
'VisibilityState',
|
'VisibilityState',
|
||||||
'Window',
|
'Window',
|
||||||
'WheelEvent',
|
'WheelEvent',
|
||||||
|
'Worker',
|
||||||
'Url',
|
'Url',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
winit = "0.29.15"
|
winit = "0.30.5"
|
||||||
```
|
```
|
||||||
|
|
||||||
## [Documentation](https://docs.rs/winit)
|
## [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
|
## 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).
|
The maintainers have a meeting every friday at UTC 15. The meeting notes can be found [here](https://hackmd.io/@winit-meetings).
|
||||||
|
|
||||||
|
|||||||
7
build.rs
7
build.rs
@@ -1,10 +1,10 @@
|
|||||||
use cfg_aliases::cfg_aliases;
|
use cfg_aliases::cfg_aliases;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// The script doesn't depend on our code
|
// The script doesn't depend on our code.
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
|
||||||
// Setup cfg aliases
|
// Setup cfg aliases.
|
||||||
cfg_aliases! {
|
cfg_aliases! {
|
||||||
// Systems.
|
// Systems.
|
||||||
android_platform: { target_os = "android" },
|
android_platform: { target_os = "android" },
|
||||||
@@ -21,4 +21,7 @@ fn main() {
|
|||||||
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
|
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
|
||||||
orbital_platform: { redox },
|
orbital_platform: { redox },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Winit defined cfgs.
|
||||||
|
println!("cargo:rustc-check-cfg=cfg(unreleased_changelogs)");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ deny = []
|
|||||||
skip = [
|
skip = [
|
||||||
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this
|
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this
|
||||||
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
|
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
|
||||||
{ name = "libloading" }, # x11rb uses a different version until the next update
|
|
||||||
]
|
]
|
||||||
skip-tree = []
|
skip-tree = []
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ impl Application {
|
|||||||
window.recognize_doubletap_gesture(true);
|
window.recognize_doubletap_gesture(true);
|
||||||
window.recognize_pinch_gesture(true);
|
window.recognize_pinch_gesture(true);
|
||||||
window.recognize_rotation_gesture(true);
|
window.recognize_rotation_gesture(true);
|
||||||
|
window.recognize_pan_gesture(true, 2, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let window_state = WindowState::new(self, window)?;
|
let window_state = WindowState::new(self, window)?;
|
||||||
@@ -211,6 +212,12 @@ impl Application {
|
|||||||
Action::PrintHelp => self.print_help(),
|
Action::PrintHelp => self.print_help(),
|
||||||
#[cfg(macos_platform)]
|
#[cfg(macos_platform)]
|
||||||
Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
|
Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
|
||||||
|
Action::SetTheme(theme) => {
|
||||||
|
window.window.set_theme(theme);
|
||||||
|
// Get the resulting current theme to draw with
|
||||||
|
let actual_theme = theme.or_else(|| window.window.theme()).unwrap_or(Theme::Dark);
|
||||||
|
window.set_draw_theme(actual_theme);
|
||||||
|
},
|
||||||
#[cfg(macos_platform)]
|
#[cfg(macos_platform)]
|
||||||
Action::CreateNewTab => {
|
Action::CreateNewTab => {
|
||||||
let tab_id = window.window.tabbing_identifier();
|
let tab_id = window.window.tabbing_identifier();
|
||||||
@@ -333,7 +340,7 @@ impl ApplicationHandler<UserEvent> for Application {
|
|||||||
},
|
},
|
||||||
WindowEvent::ThemeChanged(theme) => {
|
WindowEvent::ThemeChanged(theme) => {
|
||||||
info!("Theme changed to {theme:?}");
|
info!("Theme changed to {theme:?}");
|
||||||
window.set_theme(theme);
|
window.set_draw_theme(theme);
|
||||||
},
|
},
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
if let Err(err) = window.draw() {
|
if let Err(err) = window.draw() {
|
||||||
@@ -428,6 +435,11 @@ impl ApplicationHandler<UserEvent> for Application {
|
|||||||
info!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
|
info!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
WindowEvent::PanGesture { delta, phase, .. } => {
|
||||||
|
window.panned.x += delta.x;
|
||||||
|
window.panned.y += delta.y;
|
||||||
|
info!("Panned ({delta:?})) (now: {:?}), {phase:?}", window.panned);
|
||||||
|
},
|
||||||
WindowEvent::DoubleTapGesture { .. } => {
|
WindowEvent::DoubleTapGesture { .. } => {
|
||||||
info!("Smart zoom");
|
info!("Smart zoom");
|
||||||
},
|
},
|
||||||
@@ -502,6 +514,8 @@ struct WindowState {
|
|||||||
zoom: f64,
|
zoom: f64,
|
||||||
/// The amount of rotation of the window.
|
/// The amount of rotation of the window.
|
||||||
rotated: f32,
|
rotated: f32,
|
||||||
|
/// The amount of pan of the window.
|
||||||
|
panned: PhysicalPosition<f32>,
|
||||||
|
|
||||||
#[cfg(macos_platform)]
|
#[cfg(macos_platform)]
|
||||||
option_as_alt: OptionAsAlt,
|
option_as_alt: OptionAsAlt,
|
||||||
@@ -547,6 +561,7 @@ impl WindowState {
|
|||||||
modifiers: Default::default(),
|
modifiers: Default::default(),
|
||||||
occluded: Default::default(),
|
occluded: Default::default(),
|
||||||
rotated: Default::default(),
|
rotated: Default::default(),
|
||||||
|
panned: Default::default(),
|
||||||
zoom: Default::default(),
|
zoom: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -724,8 +739,8 @@ impl WindowState {
|
|||||||
self.window.request_redraw();
|
self.window.request_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the theme.
|
/// Change the theme that things are drawn in.
|
||||||
fn set_theme(&mut self, theme: Theme) {
|
fn set_draw_theme(&mut self, theme: Theme) {
|
||||||
self.theme = theme;
|
self.theme = theme;
|
||||||
self.window.request_redraw();
|
self.window.request_redraw();
|
||||||
}
|
}
|
||||||
@@ -875,6 +890,7 @@ enum Action {
|
|||||||
ShowWindowMenu,
|
ShowWindowMenu,
|
||||||
#[cfg(macos_platform)]
|
#[cfg(macos_platform)]
|
||||||
CycleOptionAsAlt,
|
CycleOptionAsAlt,
|
||||||
|
SetTheme(Option<Theme>),
|
||||||
#[cfg(macos_platform)]
|
#[cfg(macos_platform)]
|
||||||
CreateNewTab,
|
CreateNewTab,
|
||||||
RequestResize,
|
RequestResize,
|
||||||
@@ -906,6 +922,9 @@ impl Action {
|
|||||||
Action::ShowWindowMenu => "Show window menu",
|
Action::ShowWindowMenu => "Show window menu",
|
||||||
#[cfg(macos_platform)]
|
#[cfg(macos_platform)]
|
||||||
Action::CycleOptionAsAlt => "Cycle option as alt mode",
|
Action::CycleOptionAsAlt => "Cycle option as alt mode",
|
||||||
|
Action::SetTheme(None) => "Change to the system theme",
|
||||||
|
Action::SetTheme(Some(Theme::Light)) => "Change to a light theme",
|
||||||
|
Action::SetTheme(Some(Theme::Dark)) => "Change to a dark theme",
|
||||||
#[cfg(macos_platform)]
|
#[cfg(macos_platform)]
|
||||||
Action::CreateNewTab => "Create new tab",
|
Action::CreateNewTab => "Create new tab",
|
||||||
Action::RequestResize => "Request a resize",
|
Action::RequestResize => "Request a resize",
|
||||||
@@ -1050,6 +1069,10 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
|
|||||||
Action::AnimationCustomCursor,
|
Action::AnimationCustomCursor,
|
||||||
),
|
),
|
||||||
Binding::new("Z", ModifiersState::CONTROL, Action::ToggleCursorVisibility),
|
Binding::new("Z", ModifiersState::CONTROL, Action::ToggleCursorVisibility),
|
||||||
|
// K.
|
||||||
|
Binding::new("K", ModifiersState::empty(), Action::SetTheme(None)),
|
||||||
|
Binding::new("K", ModifiersState::SUPER, Action::SetTheme(Some(Theme::Light))),
|
||||||
|
Binding::new("K", ModifiersState::CONTROL, Action::SetTheme(Some(Theme::Dark))),
|
||||||
#[cfg(macos_platform)]
|
#[cfg(macos_platform)]
|
||||||
Binding::new("T", ModifiersState::SUPER, Action::CreateNewTab),
|
Binding::new("T", ModifiersState::SUPER, Action::CreateNewTab),
|
||||||
#[cfg(macos_platform)]
|
#[cfg(macos_platform)]
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
|
|||||||
/// Emitted when the event loop is being shut down.
|
/// Emitted when the event loop is being shut down.
|
||||||
///
|
///
|
||||||
/// This is irreversible - if this method is called, it is guaranteed that the event loop
|
/// This is irreversible - if this method is called, it is guaranteed that the event loop
|
||||||
/// will exist right after.
|
/// will exit right after.
|
||||||
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
|
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
let _ = event_loop;
|
let _ = event_loop;
|
||||||
}
|
}
|
||||||
@@ -223,3 +223,117 @@ pub trait ApplicationHandler<T: 'static = ()> {
|
|||||||
let _ = event_loop;
|
let _ = event_loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).resumed(event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
|
||||||
|
(**self).user_event(event_loop, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn window_event(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
window_id: WindowId,
|
||||||
|
event: WindowEvent,
|
||||||
|
) {
|
||||||
|
(**self).window_event(event_loop, window_id, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn device_event(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
device_id: DeviceId,
|
||||||
|
event: DeviceEvent,
|
||||||
|
) {
|
||||||
|
(**self).device_event(event_loop, device_id, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).about_to_wait(event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).suspended(event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).exiting(event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).memory_warning(event_loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).resumed(event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
|
||||||
|
(**self).user_event(event_loop, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn window_event(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
window_id: WindowId,
|
||||||
|
event: WindowEvent,
|
||||||
|
) {
|
||||||
|
(**self).window_event(event_loop, window_id, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn device_event(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
device_id: DeviceId,
|
||||||
|
event: DeviceEvent,
|
||||||
|
) {
|
||||||
|
(**self).device_event(event_loop, device_id, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).about_to_wait(event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).suspended(event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).exiting(event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
(**self).memory_warning(event_loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
// Put the current entry at the top of this page, for discoverability.
|
// Put the current entry at the top of this page, for discoverability.
|
||||||
// See `.cargo/config.toml` for details about `unreleased_changelogs`.
|
// See `.cargo/config.toml` for details about `unreleased_changelogs`.
|
||||||
#![cfg_attr(unreleased_changelogs, doc = include_str!("unreleased.md"))]
|
#![cfg_attr(unreleased_changelogs, doc = include_str!("unreleased.md"))]
|
||||||
#![cfg_attr(not(unreleased_changelogs), doc = include_str!("v0.29.md"))]
|
#![cfg_attr(not(unreleased_changelogs), doc = include_str!("v0.30.md"))]
|
||||||
|
|
||||||
|
#[doc = include_str!("v0.30.md")]
|
||||||
|
pub mod v0_30 {}
|
||||||
|
|
||||||
#[doc = include_str!("v0.29.md")]
|
#[doc = include_str!("v0.29.md")]
|
||||||
pub mod v0_29 {}
|
pub mod v0_29 {}
|
||||||
|
|||||||
@@ -39,222 +39,3 @@ The migration guide could reference other migration examples in the current
|
|||||||
changelog entry.
|
changelog entry.
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Add `OwnedDisplayHandle` type for allowing safe display handle usage outside of
|
|
||||||
trivial cases.
|
|
||||||
- Add `ApplicationHandler<T>` trait which mimics `Event<T>`.
|
|
||||||
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a
|
|
||||||
`CursorIcon` or `CustomCursor`.
|
|
||||||
- Add `Sync` implementation for `EventLoopProxy<T: Send>`.
|
|
||||||
- Add `Window::default_attributes` to get default `WindowAttributes`.
|
|
||||||
- Add `EventLoop::builder` to get `EventLoopBuilder` without export.
|
|
||||||
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
|
|
||||||
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
|
|
||||||
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated
|
|
||||||
cursors from other `CustomCursor`s.
|
|
||||||
- Add `{Active,}EventLoop::create_custom_cursor` to load custom cursor image sources.
|
|
||||||
- Add `ActiveEventLoop::create_window` and `EventLoop::create_window`.
|
|
||||||
- Add `CustomCursor` which could be set via `Window::set_cursor`, implemented on
|
|
||||||
Windows, macOS, X11, Wayland, and Web.
|
|
||||||
- On Web, add to toggle calling `Event.preventDefault()` on `Window`.
|
|
||||||
- On iOS, add `PinchGesture`, `DoubleTapGesture`, and `RotationGesture`
|
|
||||||
- On macOS, add services menu.
|
|
||||||
- On Windows, add `with_title_text_color`, and `with_corner_preference` on
|
|
||||||
`WindowAttributesExtWindows`.
|
|
||||||
- On Windows, implement resize increments.
|
|
||||||
- On Windows, add `AnyThread` API to access window handle off the main thread.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Bump MSRV from `1.65` to `1.70`.
|
|
||||||
- On Wayland, bump `sctk-adwaita` to `0.9.0`, which changed system library
|
|
||||||
crates. This change is a **cascading breaking change**, you must do breaking
|
|
||||||
change as well, even if you don't expose winit.
|
|
||||||
- Rename `TouchpadMagnify` to `PinchGesture`.
|
|
||||||
- Rename `SmartMagnify` to `DoubleTapGesture`.
|
|
||||||
- Rename `TouchpadRotate` to `RotationGesture`.
|
|
||||||
- Rename `EventLoopWindowTarget` to `ActiveEventLoop`.
|
|
||||||
- Rename `platform::x11::XWindowType` to `platform::x11::WindowType`.
|
|
||||||
- Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold
|
|
||||||
static data.
|
|
||||||
- Make `Debug` formatting of `WindowId` more concise.
|
|
||||||
- Move `dpi` types to its own crate, and re-export it from the root crate.
|
|
||||||
- Replace `log` with `tracing`, use `log` feature on `tracing` to restore old
|
|
||||||
behavior.
|
|
||||||
- `EventLoop::with_user_event` now returns `EventLoopBuilder`.
|
|
||||||
- On Web, return `HandleError::Unavailable` when a window handle is not available.
|
|
||||||
- On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
|
|
||||||
- On Web, remove queuing fullscreen request in absence of transient activation.
|
|
||||||
- On iOS, return `HandleError::Unavailable` when a window handle is not available.
|
|
||||||
- On macOS, return `HandleError::Unavailable` when a window handle is not available.
|
|
||||||
- On Windows, remove `WS_CAPTION`, `WS_BORDER`, and `WS_EX_WINDOWEDGE` styles
|
|
||||||
for child windows without decorations.
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Deprecate `EventLoop::run`, use `EventLoop::run_app`.
|
|
||||||
- Deprecate `EventLoopExtRunOnDemand::run_on_demand`, use `EventLoop::run_app_on_demand`.
|
|
||||||
- Deprecate `EventLoopExtPumpEvents::pump_events`, use `EventLoopExtPumpEvents::pump_app_events`.
|
|
||||||
|
|
||||||
The new `app` APIs accept a newly added `ApplicationHandler<T>` instead of
|
|
||||||
`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
|
|
||||||
use winit::event::Event;
|
|
||||||
use winit::event_loop::EventLoop;
|
|
||||||
use winit::window::Window;
|
|
||||||
|
|
||||||
struct MyUserEvent;
|
|
||||||
|
|
||||||
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
|
|
||||||
let window = event_loop.create_window(Window::default_attributes()).unwrap();
|
|
||||||
let mut counter = 0;
|
|
||||||
|
|
||||||
let _ = event_loop.run(move |event, event_loop| {
|
|
||||||
match event {
|
|
||||||
Event::AboutToWait => {
|
|
||||||
window.request_redraw();
|
|
||||||
counter += 1;
|
|
||||||
}
|
|
||||||
Event::WindowEvent { window_id, event } => {
|
|
||||||
// Handle window event.
|
|
||||||
}
|
|
||||||
Event::UserEvent(event) => {
|
|
||||||
// Handle user event.
|
|
||||||
}
|
|
||||||
Event::DeviceEvent { device_id, event } => {
|
|
||||||
// Handle device event.
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
To migrate this code, you should move all the captured values into some
|
|
||||||
newtype `State` and implement `ApplicationHandler` for this type. Finally,
|
|
||||||
we move particular `match event` arms into methods on `ApplicationHandler`,
|
|
||||||
for example:
|
|
||||||
|
|
||||||
```rust,no_run
|
|
||||||
use winit::application::ApplicationHandler;
|
|
||||||
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
|
|
||||||
use winit::event_loop::{EventLoop, ActiveEventLoop};
|
|
||||||
use winit::window::{Window, WindowId};
|
|
||||||
|
|
||||||
struct MyUserEvent;
|
|
||||||
|
|
||||||
struct State {
|
|
||||||
window: Window,
|
|
||||||
counter: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApplicationHandler<MyUserEvent> for State {
|
|
||||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, user_event: MyUserEvent) {
|
|
||||||
// Handle user event.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
// Your application got resumed.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
|
|
||||||
// Handle window event.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
|
|
||||||
// Handle device event.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
self.window.request_redraw();
|
|
||||||
self.counter += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
|
|
||||||
#[allow(deprecated)]
|
|
||||||
let window = event_loop.create_window(Window::default_attributes()).unwrap();
|
|
||||||
let mut state = State { window, counter: 0 };
|
|
||||||
|
|
||||||
let _ = event_loop.run_app(&mut state);
|
|
||||||
```
|
|
||||||
|
|
||||||
Please submit your feedback after migrating in [this issue](https://github.com/rust-windowing/winit/issues/3626).
|
|
||||||
|
|
||||||
- Deprecate `Window::set_cursor_icon`, use `Window::set_cursor`.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Remove `Window::new`, use `ActiveEventLoop::create_window` instead.
|
|
||||||
|
|
||||||
You now have to create your windows inside the actively running event loop
|
|
||||||
(usually the `new_events(cause: StartCause::Init)` or `resumed()` events),
|
|
||||||
and can no longer do it before the application has properly launched.
|
|
||||||
This change is done to fix many long-standing issues on iOS and macOS, and
|
|
||||||
will improve things on Wayland once fully implemented.
|
|
||||||
|
|
||||||
To ease migration, we provide the deprecated `EventLoop::create_window` that
|
|
||||||
will allow you to bypass this restriction in this release.
|
|
||||||
|
|
||||||
Using the migration example from above, you can change your code as follows:
|
|
||||||
|
|
||||||
```rust,no_run
|
|
||||||
use winit::application::ApplicationHandler;
|
|
||||||
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
|
|
||||||
use winit::event_loop::{EventLoop, ActiveEventLoop};
|
|
||||||
use winit::window::{Window, WindowId};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct State {
|
|
||||||
// Use an `Option` to allow the window to not be available until the
|
|
||||||
// application is properly running.
|
|
||||||
window: Option<Window>,
|
|
||||||
counter: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApplicationHandler for State {
|
|
||||||
// This is a common indicator that you can create a window.
|
|
||||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
|
|
||||||
}
|
|
||||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
|
|
||||||
// `unwrap` is fine, the window will always be available when
|
|
||||||
// receiving a window event.
|
|
||||||
let window = self.window.as_ref().unwrap();
|
|
||||||
// Handle window event.
|
|
||||||
}
|
|
||||||
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
|
|
||||||
// Handle window event.
|
|
||||||
}
|
|
||||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
if let Some(window) = self.window.as_ref() {
|
|
||||||
window.request_redraw();
|
|
||||||
self.counter += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let event_loop = EventLoop::new().unwrap();
|
|
||||||
let mut state = State::default();
|
|
||||||
let _ = event_loop.run_app(&mut state);
|
|
||||||
```
|
|
||||||
|
|
||||||
- Remove `Deref` implementation for `EventLoop` that gave `EventLoopWindowTarget`.
|
|
||||||
- Remove `WindowBuilder` in favor of `WindowAttributes`.
|
|
||||||
- Remove Generic parameter `T` from `ActiveEventLoop`.
|
|
||||||
- Remove `EventLoopBuilder::with_user_event`, use `EventLoop::with_user_event`.
|
|
||||||
- Remove Redundant `EventLoopError::AlreadyRunning`.
|
|
||||||
- Remove `WindowAttributes::fullscreen` and expose as field directly.
|
|
||||||
- On X11, remove `platform::x11::XNotSupported` export.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- On Web, fix setting cursor icon overriding cursor visibility.
|
|
||||||
- On Windows, fix cursor not confined to center of window when grabbed and hidden.
|
|
||||||
- On macOS, fix sequence of mouse events being out of order when dragging on the trackpad.
|
|
||||||
- On Wayland, fix decoration glitch on close with some compositors.
|
|
||||||
- On Android, fix a regression introduced in #2748 to allow volume key events to be received again.
|
|
||||||
- On Windows, don't return a valid window handle outside of the GUI thread.
|
|
||||||
|
|||||||
@@ -137,7 +137,7 @@
|
|||||||
|
|
||||||
- On X11, non-resizable windows now have maximize explicitly disabled.
|
- On X11, non-resizable windows now have maximize explicitly disabled.
|
||||||
- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile`
|
- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile`
|
||||||
and `WindowEvent::HoveredFile`.
|
and `WindowEvent::HoveredFile`.
|
||||||
- On Mac, implement `DeviceEvent::Button`.
|
- On Mac, implement `DeviceEvent::Button`.
|
||||||
- Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`.
|
- 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.
|
- 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.
|
||||||
|
|||||||
299
src/changelog/v0.30.md
Normal file
299
src/changelog/v0.30.md
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
## 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
|
||||||
|
|
||||||
|
- On Web, add `EventLoopExtWebSys::(set_)poll_strategy()` to allow setting
|
||||||
|
control flow strategies before starting the event loop.
|
||||||
|
- On Web, add `WaitUntilStrategy`, which allows to set different strategies for
|
||||||
|
`ControlFlow::WaitUntil`. By default the Prioritized Task Scheduling API is
|
||||||
|
used, with a fallback to `setTimeout()` with a trick to circumvent throttling
|
||||||
|
to 4ms. But an option to use a Web worker to schedule the timer is available
|
||||||
|
as well, which commonly prevents any throttling when the window is not focused.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- On macOS, set the window theme on the `NSWindow` instead of application-wide.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- On X11, build on arm platforms.
|
||||||
|
- On macOS, fixed `WindowBuilder::with_theme` not having any effect on the window.
|
||||||
|
|
||||||
|
## 0.30.2
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- On Web, fix `EventLoopProxy::send_event()` triggering event loop immediately
|
||||||
|
when not called from inside the event loop. Now queues a microtask instead.
|
||||||
|
- On Web, stop overwriting default cursor with `CursorIcon::Default`.
|
||||||
|
- On Web, prevent crash when using `InnerSizeWriter::request_inner_size()`.
|
||||||
|
- On macOS, fix not working opacity for entire window.
|
||||||
|
|
||||||
|
## 0.30.1
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- 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`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- On macOS, fix panic on exit when dropping windows outside the event loop.
|
||||||
|
- On macOS, fix window dragging glitches when dragging across a monitor boundary with different scale factor.
|
||||||
|
- On macOS, fix the range in `Ime::Preedit`.
|
||||||
|
- On macOS, use the system's internal mechanisms for queuing events.
|
||||||
|
- On macOS, handle events directly instead of queuing when possible.
|
||||||
|
|
||||||
|
## 0.30.0
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add `OwnedDisplayHandle` type for allowing safe display handle usage outside of
|
||||||
|
trivial cases.
|
||||||
|
- Add `ApplicationHandler<T>` trait which mimics `Event<T>`.
|
||||||
|
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a
|
||||||
|
`CursorIcon` or `CustomCursor`.
|
||||||
|
- Add `Sync` implementation for `EventLoopProxy<T: Send>`.
|
||||||
|
- Add `Window::default_attributes` to get default `WindowAttributes`.
|
||||||
|
- Add `EventLoop::builder` to get `EventLoopBuilder` without export.
|
||||||
|
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
|
||||||
|
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
|
||||||
|
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated
|
||||||
|
cursors from other `CustomCursor`s.
|
||||||
|
- Add `{Active,}EventLoop::create_custom_cursor` to load custom cursor image sources.
|
||||||
|
- Add `ActiveEventLoop::create_window` and `EventLoop::create_window`.
|
||||||
|
- Add `CustomCursor` which could be set via `Window::set_cursor`, implemented on
|
||||||
|
Windows, macOS, X11, Wayland, and Web.
|
||||||
|
- On Web, add to toggle calling `Event.preventDefault()` on `Window`.
|
||||||
|
- On iOS, add `PinchGesture`, `DoubleTapGesture`, `PanGesture` and `RotationGesture`.
|
||||||
|
- on iOS, use `UIGestureRecognizerDelegate` for fine grained control of gesture recognizers.
|
||||||
|
- On macOS, add services menu.
|
||||||
|
- On Windows, add `with_title_text_color`, and `with_corner_preference` on
|
||||||
|
`WindowAttributesExtWindows`.
|
||||||
|
- On Windows, implement resize increments.
|
||||||
|
- On Windows, add `AnyThread` API to access window handle off the main thread.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Bump MSRV from `1.65` to `1.70`.
|
||||||
|
- On Wayland, bump `sctk-adwaita` to `0.9.0`, which changed system library
|
||||||
|
crates. This change is a **cascading breaking change**, you must do breaking
|
||||||
|
change as well, even if you don't expose winit.
|
||||||
|
- Rename `TouchpadMagnify` to `PinchGesture`.
|
||||||
|
- Rename `SmartMagnify` to `DoubleTapGesture`.
|
||||||
|
- Rename `TouchpadRotate` to `RotationGesture`.
|
||||||
|
- Rename `EventLoopWindowTarget` to `ActiveEventLoop`.
|
||||||
|
- Rename `platform::x11::XWindowType` to `platform::x11::WindowType`.
|
||||||
|
- Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold
|
||||||
|
static data.
|
||||||
|
- Make `Debug` formatting of `WindowId` more concise.
|
||||||
|
- Move `dpi` types to its own crate, and re-export it from the root crate.
|
||||||
|
- Replace `log` with `tracing`, use `log` feature on `tracing` to restore old
|
||||||
|
behavior.
|
||||||
|
- `EventLoop::with_user_event` now returns `EventLoopBuilder`.
|
||||||
|
- On Web, return `HandleError::Unavailable` when a window handle is not available.
|
||||||
|
- On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
|
||||||
|
- On Web, remove queuing fullscreen request in absence of transient activation.
|
||||||
|
- On iOS, return `HandleError::Unavailable` when a window handle is not available.
|
||||||
|
- On macOS, return `HandleError::Unavailable` when a window handle is not available.
|
||||||
|
- On Windows, remove `WS_CAPTION`, `WS_BORDER`, and `WS_EX_WINDOWEDGE` styles
|
||||||
|
for child windows without decorations.
|
||||||
|
- On Android, bump `ndk` to `0.9.0` and `android-activity` to `0.6.0`,
|
||||||
|
and remove unused direct dependency on `ndk-sys`.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
- Deprecate `EventLoop::run`, use `EventLoop::run_app`.
|
||||||
|
- Deprecate `EventLoopExtRunOnDemand::run_on_demand`, use `EventLoop::run_app_on_demand`.
|
||||||
|
- Deprecate `EventLoopExtPumpEvents::pump_events`, use `EventLoopExtPumpEvents::pump_app_events`.
|
||||||
|
|
||||||
|
The new `app` APIs accept a newly added `ApplicationHandler<T>` instead of
|
||||||
|
`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
|
||||||
|
use winit::event::Event;
|
||||||
|
use winit::event_loop::EventLoop;
|
||||||
|
use winit::window::Window;
|
||||||
|
|
||||||
|
struct MyUserEvent;
|
||||||
|
|
||||||
|
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
|
||||||
|
let window = event_loop.create_window(Window::default_attributes()).unwrap();
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
let _ = event_loop.run(move |event, event_loop| {
|
||||||
|
match event {
|
||||||
|
Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
Event::WindowEvent { window_id, event } => {
|
||||||
|
// Handle window event.
|
||||||
|
}
|
||||||
|
Event::UserEvent(event) => {
|
||||||
|
// Handle user event.
|
||||||
|
}
|
||||||
|
Event::DeviceEvent { device_id, event } => {
|
||||||
|
// Handle device event.
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
To migrate this code, you should move all the captured values into some
|
||||||
|
newtype `State` and implement `ApplicationHandler` for this type. Finally,
|
||||||
|
we move particular `match event` arms into methods on `ApplicationHandler`,
|
||||||
|
for example:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
use winit::application::ApplicationHandler;
|
||||||
|
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
|
||||||
|
use winit::event_loop::{EventLoop, ActiveEventLoop};
|
||||||
|
use winit::window::{Window, WindowId};
|
||||||
|
|
||||||
|
struct MyUserEvent;
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
window: Window,
|
||||||
|
counter: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler<MyUserEvent> for State {
|
||||||
|
fn user_event(&mut self, event_loop: &ActiveEventLoop, user_event: MyUserEvent) {
|
||||||
|
// Handle user event.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
// Your application got resumed.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
|
||||||
|
// Handle window event.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
|
||||||
|
// Handle device event.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
self.window.request_redraw();
|
||||||
|
self.counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let window = event_loop.create_window(Window::default_attributes()).unwrap();
|
||||||
|
let mut state = State { window, counter: 0 };
|
||||||
|
|
||||||
|
let _ = event_loop.run_app(&mut state);
|
||||||
|
```
|
||||||
|
|
||||||
|
Please submit your feedback after migrating in [this issue](https://github.com/rust-windowing/winit/issues/3626).
|
||||||
|
|
||||||
|
- Deprecate `Window::set_cursor_icon`, use `Window::set_cursor`.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Remove `Window::new`, use `ActiveEventLoop::create_window` instead.
|
||||||
|
|
||||||
|
You now have to create your windows inside the actively running event loop
|
||||||
|
(usually the `new_events(cause: StartCause::Init)` or `resumed()` events),
|
||||||
|
and can no longer do it before the application has properly launched.
|
||||||
|
This change is done to fix many long-standing issues on iOS and macOS, and
|
||||||
|
will improve things on Wayland once fully implemented.
|
||||||
|
|
||||||
|
To ease migration, we provide the deprecated `EventLoop::create_window` that
|
||||||
|
will allow you to bypass this restriction in this release.
|
||||||
|
|
||||||
|
Using the migration example from above, you can change your code as follows:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
use winit::application::ApplicationHandler;
|
||||||
|
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
|
||||||
|
use winit::event_loop::{EventLoop, ActiveEventLoop};
|
||||||
|
use winit::window::{Window, WindowId};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct State {
|
||||||
|
// Use an `Option` to allow the window to not be available until the
|
||||||
|
// application is properly running.
|
||||||
|
window: Option<Window>,
|
||||||
|
counter: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for State {
|
||||||
|
// This is a common indicator that you can create a window.
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
|
||||||
|
}
|
||||||
|
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
|
||||||
|
// `unwrap` is fine, the window will always be available when
|
||||||
|
// receiving a window event.
|
||||||
|
let window = self.window.as_ref().unwrap();
|
||||||
|
// Handle window event.
|
||||||
|
}
|
||||||
|
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
|
||||||
|
// Handle window event.
|
||||||
|
}
|
||||||
|
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
if let Some(window) = self.window.as_ref() {
|
||||||
|
window.request_redraw();
|
||||||
|
self.counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
let mut state = State::default();
|
||||||
|
let _ = event_loop.run_app(&mut state);
|
||||||
|
```
|
||||||
|
|
||||||
|
- Remove `Deref` implementation for `EventLoop` that gave `EventLoopWindowTarget`.
|
||||||
|
- Remove `WindowBuilder` in favor of `WindowAttributes`.
|
||||||
|
- Remove Generic parameter `T` from `ActiveEventLoop`.
|
||||||
|
- Remove `EventLoopBuilder::with_user_event`, use `EventLoop::with_user_event`.
|
||||||
|
- Remove Redundant `EventLoopError::AlreadyRunning`.
|
||||||
|
- Remove `WindowAttributes::fullscreen` and expose as field directly.
|
||||||
|
- On X11, remove `platform::x11::XNotSupported` export.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- On Web, fix setting cursor icon overriding cursor visibility.
|
||||||
|
- On Windows, fix cursor not confined to center of window when grabbed and hidden.
|
||||||
|
- On macOS, fix sequence of mouse events being out of order when dragging on the trackpad.
|
||||||
|
- On Wayland, fix decoration glitch on close with some compositors.
|
||||||
|
- On Android, fix a regression introduced in #2748 to allow volume key events to be received again.
|
||||||
|
- On Windows, don't return a valid window handle outside of the GUI thread.
|
||||||
|
- On macOS, don't set the background color when initializing a window with transparency.
|
||||||
@@ -3,20 +3,20 @@
|
|||||||
- Added event `WindowEvent::HiDPIFactorChanged`.
|
- Added event `WindowEvent::HiDPIFactorChanged`.
|
||||||
- Added method `MonitorId::get_hidpi_factor`.
|
- Added method `MonitorId::get_hidpi_factor`.
|
||||||
- Deprecated `get_inner_size_pixels` and `get_inner_size_points` methods of `Window` in favor of
|
- 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,
|
- **Breaking:** `EventsLoop` is `!Send` and `!Sync` because of platform-dependant constraints,
|
||||||
but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`.
|
but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`.
|
||||||
- `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`.
|
- `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`.
|
||||||
- Rewrite of the wayland backend to use wayland-client-0.11
|
- Rewrite of the wayland backend to use wayland-client-0.11
|
||||||
- Support for dead keys on wayland for keyboard utf8 input
|
- Support for dead keys on wayland for keyboard utf8 input
|
||||||
- Monitor enumeration on Windows is now implemented using `EnumDisplayMonitors` instead of
|
- 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
|
- On Windows added `MonitorIdExt::hmonitor` method
|
||||||
- Impl `Clone` for `EventsLoopProxy`
|
- Impl `Clone` for `EventsLoopProxy`
|
||||||
- `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found
|
- `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found
|
||||||
- Support for touch event on wayland
|
- Support for touch event on wayland
|
||||||
- `WindowEvent`s `MouseMoved`, `MouseEntered`, and `MouseLeft` have been renamed to
|
- `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`.
|
- New `DeviceEvent`s added, `MouseMotion` and `MouseWheel`.
|
||||||
- Send `CursorMoved` event after `CursorEntered` and `Focused` events.
|
- Send `CursorMoved` event after `CursorEntered` and `Focused` events.
|
||||||
- Add support for `ModifiersState`, `MouseMove`, `MouseInput`, `MouseMotion` for emscripten backend.
|
- Add support for `ModifiersState`, `MouseMove`, `MouseInput`, `MouseMotion` for emscripten backend.
|
||||||
|
|||||||
60
src/event.rs
60
src/event.rs
@@ -293,6 +293,19 @@ pub enum WindowEvent {
|
|||||||
phase: TouchPhase,
|
phase: TouchPhase,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// N-finger pan gesture
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - Only available on **iOS**.
|
||||||
|
/// - On iOS, not recognized by default. It must be enabled when needed.
|
||||||
|
PanGesture {
|
||||||
|
device_id: DeviceId,
|
||||||
|
/// Change in pixels of pan gesture from last update.
|
||||||
|
delta: PhysicalPosition<f32>,
|
||||||
|
phase: TouchPhase,
|
||||||
|
},
|
||||||
|
|
||||||
/// Double tap gesture.
|
/// Double tap gesture.
|
||||||
///
|
///
|
||||||
/// On a Mac, smart magnification is triggered by a double tap with two fingers
|
/// On a Mac, smart magnification is triggered by a double tap with two fingers
|
||||||
@@ -322,7 +335,12 @@ pub enum WindowEvent {
|
|||||||
///
|
///
|
||||||
/// - Only available on **macOS** and **iOS**.
|
/// - Only available on **macOS** and **iOS**.
|
||||||
/// - On iOS, not recognized by default. It must be enabled when needed.
|
/// - On iOS, not recognized by default. It must be enabled when needed.
|
||||||
RotationGesture { device_id: DeviceId, delta: f32, phase: TouchPhase },
|
RotationGesture {
|
||||||
|
device_id: DeviceId,
|
||||||
|
/// change in rotation in degrees
|
||||||
|
delta: f32,
|
||||||
|
phase: TouchPhase,
|
||||||
|
},
|
||||||
|
|
||||||
/// Touchpad pressure event.
|
/// Touchpad pressure event.
|
||||||
///
|
///
|
||||||
@@ -371,6 +389,8 @@ pub enum WindowEvent {
|
|||||||
/// Applications might wish to react to this to change the theme of the content of the window
|
/// Applications might wish to react to this to change the theme of the content of the window
|
||||||
/// when the system changes the window theme.
|
/// when the system changes the window theme.
|
||||||
///
|
///
|
||||||
|
/// This only reports a change if the window theme was not overridden by [`Window::set_theme`].
|
||||||
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported.
|
/// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported.
|
||||||
@@ -429,16 +449,13 @@ pub struct DeviceId(pub(crate) platform_impl::DeviceId);
|
|||||||
impl DeviceId {
|
impl DeviceId {
|
||||||
/// Returns a dummy id, useful for unit testing.
|
/// Returns a dummy id, useful for unit testing.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Notes
|
||||||
///
|
///
|
||||||
/// The only guarantee made about the return value of this function is that
|
/// 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.
|
/// 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`.
|
/// No other guarantees are made. This may be equal to a real `DeviceId`.
|
||||||
///
|
pub const fn dummy() -> Self {
|
||||||
/// **Passing this into a winit function will result in undefined behavior.**
|
DeviceId(platform_impl::DeviceId::dummy())
|
||||||
pub const unsafe fn dummy() -> Self {
|
|
||||||
#[allow(unused_unsafe)]
|
|
||||||
DeviceId(unsafe { platform_impl::DeviceId::dummy() })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,11 +532,11 @@ pub struct KeyEvent {
|
|||||||
/// ## Caveats
|
/// ## Caveats
|
||||||
///
|
///
|
||||||
/// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that
|
/// - 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
|
/// - 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
|
/// - 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
|
/// **Because of these caveats, it is important that you provide users with a way to configure
|
||||||
/// most (if not all) keybinds in your application.**
|
/// most (if not all) keybinds in your application.**
|
||||||
@@ -541,8 +558,7 @@ pub struct KeyEvent {
|
|||||||
///
|
///
|
||||||
/// This has two use cases:
|
/// This has two use cases:
|
||||||
/// - Allows querying whether the current input is a Dead key.
|
/// - Allows querying whether the current input is a Dead key.
|
||||||
/// - Allows handling key-bindings on platforms which don't
|
/// - Allows handling key-bindings on platforms which don't support [`key_without_modifiers`].
|
||||||
/// support [`key_without_modifiers`].
|
|
||||||
///
|
///
|
||||||
/// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard
|
/// 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
|
/// shortcuts, **it is important that you provide users with a way to configure your
|
||||||
@@ -550,8 +566,8 @@ pub struct KeyEvent {
|
|||||||
/// incompatible keyboard layout.**
|
/// incompatible keyboard layout.**
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
/// - **Web:** Dead keys might be reported as the real key instead
|
/// - **Web:** Dead keys might be reported as the real key instead of `Dead` depending on the
|
||||||
/// of `Dead` depending on the browser/OS.
|
/// browser/OS.
|
||||||
///
|
///
|
||||||
/// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
|
/// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
|
||||||
pub logical_key: keyboard::Key,
|
pub logical_key: keyboard::Key,
|
||||||
@@ -833,8 +849,8 @@ pub struct Touch {
|
|||||||
///
|
///
|
||||||
/// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**.
|
/// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**.
|
||||||
/// - **Android**: This will never be [None]. If the device doesn't support pressure
|
/// - **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
|
/// 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).
|
/// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE).
|
||||||
pub force: Option<Force>,
|
pub force: Option<Force>,
|
||||||
/// Unique identifier of a finger.
|
/// Unique identifier of a finger.
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
@@ -993,6 +1009,7 @@ impl PartialEq for InnerSizeWriter {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::dpi::PhysicalPosition;
|
||||||
use crate::event;
|
use crate::event;
|
||||||
use std::collections::{BTreeSet, HashSet};
|
use std::collections::{BTreeSet, HashSet};
|
||||||
|
|
||||||
@@ -1000,7 +1017,7 @@ mod tests {
|
|||||||
($closure:expr) => {{
|
($closure:expr) => {{
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut x = $closure;
|
let mut x = $closure;
|
||||||
let did = unsafe { event::DeviceId::dummy() };
|
let did = event::DeviceId::dummy();
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
{
|
{
|
||||||
@@ -1010,7 +1027,7 @@ mod tests {
|
|||||||
use crate::window::WindowId;
|
use crate::window::WindowId;
|
||||||
|
|
||||||
// Mainline events.
|
// Mainline events.
|
||||||
let wid = unsafe { WindowId::dummy() };
|
let wid = WindowId::dummy();
|
||||||
x(UserEvent(()));
|
x(UserEvent(()));
|
||||||
x(NewEvents(event::StartCause::Init));
|
x(NewEvents(event::StartCause::Init));
|
||||||
x(AboutToWait);
|
x(AboutToWait);
|
||||||
@@ -1055,6 +1072,11 @@ mod tests {
|
|||||||
delta: 0.0,
|
delta: 0.0,
|
||||||
phase: event::TouchPhase::Started,
|
phase: event::TouchPhase::Started,
|
||||||
});
|
});
|
||||||
|
with_window_event(PanGesture {
|
||||||
|
device_id: did,
|
||||||
|
delta: PhysicalPosition::<f32>::new(0.0, 0.0),
|
||||||
|
phase: event::TouchPhase::Started,
|
||||||
|
});
|
||||||
with_window_event(TouchpadPressure { device_id: did, pressure: 0.0, stage: 0 });
|
with_window_event(TouchpadPressure { device_id: did, pressure: 0.0, stage: 0 });
|
||||||
with_window_event(AxisMotion { device_id: did, axis: 0, value: 0.0 });
|
with_window_event(AxisMotion { device_id: did, axis: 0, value: 0.0 });
|
||||||
with_window_event(Touch(event::Touch {
|
with_window_event(Touch(event::Touch {
|
||||||
@@ -1134,7 +1156,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
let _ = event::StartCause::Init.clone();
|
let _ = event::StartCause::Init.clone();
|
||||||
|
|
||||||
let did = unsafe { crate::event::DeviceId::dummy() }.clone();
|
let did = crate::event::DeviceId::dummy().clone();
|
||||||
HashSet::new().insert(did);
|
HashSet::new().insert(did);
|
||||||
let mut set = [did, did, did];
|
let mut set = [did, did, did];
|
||||||
set.sort_unstable();
|
set.sort_unstable();
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use crate::error::{EventLoopError, OsError};
|
|||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::monitor::MonitorHandle;
|
use crate::monitor::MonitorHandle;
|
||||||
use crate::platform_impl;
|
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
|
/// Provides a way to retrieve events from the system and from the windows that were registered to
|
||||||
/// the events loop.
|
/// the events loop.
|
||||||
@@ -103,11 +103,14 @@ impl<T> EventLoopBuilder<T> {
|
|||||||
///
|
///
|
||||||
/// [`platform`]: crate::platform
|
/// [`platform`]: crate::platform
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
android,
|
android_platform,
|
||||||
doc = "[`.with_android_app(app)`]: \
|
doc = "[`.with_android_app(app)`]: \
|
||||||
crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
|
crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(not(android), doc = "[`.with_android_app(app)`]: #only-available-on-android")]
|
#[cfg_attr(
|
||||||
|
not(android_platform),
|
||||||
|
doc = "[`.with_android_app(app)`]: #only-available-on-android"
|
||||||
|
)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> {
|
pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> {
|
||||||
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
|
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
|
||||||
@@ -434,6 +437,17 @@ impl ActiveEventLoop {
|
|||||||
self.p.listen_device_events(allowed);
|
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`].
|
/// Sets the [`ControlFlow`].
|
||||||
pub fn set_control_flow(&self, control_flow: ControlFlow) {
|
pub fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||||
self.p.set_control_flow(control_flow)
|
self.p.set_control_flow(control_flow)
|
||||||
|
|||||||
@@ -178,6 +178,10 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
|
||||||
#![allow(clippy::missing_safety_doc)]
|
#![allow(clippy::missing_safety_doc)]
|
||||||
|
|
||||||
|
#[cfg(feature = "rwh_04")]
|
||||||
|
pub use rwh_04 as raw_window_handle_04;
|
||||||
|
#[cfg(feature = "rwh_05")]
|
||||||
|
pub use rwh_05 as raw_window_handle_05;
|
||||||
#[cfg(feature = "rwh_06")]
|
#[cfg(feature = "rwh_06")]
|
||||||
pub use rwh_06 as raw_window_handle;
|
pub use rwh_06 as raw_window_handle;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
//!
|
//!
|
||||||
//! | winit | ndk-glue |
|
//! | winit | ndk-glue |
|
||||||
//! | :---: | :--------------------------: |
|
//! | :---: | :--------------------------: |
|
||||||
|
//! | 0.30 | `android-activity = "0.6"` |
|
||||||
//! | 0.29 | `android-activity = "0.5"` |
|
//! | 0.29 | `android-activity = "0.5"` |
|
||||||
//! | 0.28 | `android-activity = "0.4"` |
|
//! | 0.28 | `android-activity = "0.4"` |
|
||||||
//! | 0.27 | `ndk-glue = "0.7"` |
|
//! | 0.27 | `ndk-glue = "0.7"` |
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building
|
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building
|
||||||
//! with `cargo apk`, then the minimal changes would be:
|
//! with `cargo apk`, then the minimal changes would be:
|
||||||
//! 1. Remove `ndk-glue` from your `Cargo.toml`
|
//! 1. Remove `ndk-glue` from your `Cargo.toml`
|
||||||
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.15",
|
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.5",
|
||||||
//! features = [ "android-native-activity" ] }`
|
//! features = [ "android-native-activity" ] }`
|
||||||
//! 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc
|
//! 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
|
//! macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize
|
||||||
@@ -75,12 +76,22 @@ use crate::window::{Window, WindowAttributes};
|
|||||||
use self::activity::{AndroidApp, ConfigurationRef, Rect};
|
use self::activity::{AndroidApp, ConfigurationRef, Rect};
|
||||||
|
|
||||||
/// Additional methods on [`EventLoop`] that are specific to Android.
|
/// 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<T> EventLoopExtAndroid for EventLoop<T> {}
|
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.
|
/// 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.
|
/// Additional methods on [`Window`] that are specific to Android.
|
||||||
pub trait WindowExtAndroid {
|
pub trait WindowExtAndroid {
|
||||||
@@ -99,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.
|
/// Additional methods on [`WindowAttributes`] that are specific to Android.
|
||||||
pub trait WindowAttributesExtAndroid {}
|
pub trait WindowAttributesExtAndroid {}
|
||||||
@@ -107,9 +122,9 @@ pub trait WindowAttributesExtAndroid {}
|
|||||||
impl WindowAttributesExtAndroid for WindowAttributes {}
|
impl WindowAttributesExtAndroid for WindowAttributes {}
|
||||||
|
|
||||||
pub trait EventLoopBuilderExtAndroid {
|
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;
|
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self;
|
||||||
|
|
||||||
/// Calling this will mark the volume keys to be manually handled by the application
|
/// Calling this will mark the volume keys to be manually handled by the application
|
||||||
@@ -146,7 +161,7 @@ impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
|
|||||||
/// depending on the `android_activity` crate, and instead consume the API that
|
/// depending on the `android_activity` crate, and instead consume the API that
|
||||||
/// is re-exported by Winit.
|
/// 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:
|
/// their `android_main(app: AndroidApp)` function like:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #[cfg(target_os = "android")]
|
/// #[cfg(target_os = "android")]
|
||||||
|
|||||||
@@ -155,6 +155,21 @@ pub trait WindowExtIOS {
|
|||||||
/// The default is to not recognize gestures.
|
/// The default is to not recognize gestures.
|
||||||
fn recognize_pinch_gesture(&self, should_recognize: bool);
|
fn recognize_pinch_gesture(&self, should_recognize: bool);
|
||||||
|
|
||||||
|
/// Sets whether the [`Window`] should recognize pan gestures.
|
||||||
|
///
|
||||||
|
/// The default is to not recognize gestures.
|
||||||
|
/// Installs [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer) onto view
|
||||||
|
///
|
||||||
|
/// Set the minimum number of touches required: [`minimumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-minimumnumberoftouches)
|
||||||
|
///
|
||||||
|
/// Set the maximum number of touches recognized: [`maximumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-maximumnumberoftouches)
|
||||||
|
fn recognize_pan_gesture(
|
||||||
|
&self,
|
||||||
|
should_recognize: bool,
|
||||||
|
minimum_number_of_touches: u8,
|
||||||
|
maximum_number_of_touches: u8,
|
||||||
|
);
|
||||||
|
|
||||||
/// Sets whether the [`Window`] should recognize double tap gestures.
|
/// Sets whether the [`Window`] should recognize double tap gestures.
|
||||||
///
|
///
|
||||||
/// The default is to not recognize gestures.
|
/// The default is to not recognize gestures.
|
||||||
@@ -204,6 +219,22 @@ impl WindowExtIOS for Window {
|
|||||||
self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
|
self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn recognize_pan_gesture(
|
||||||
|
&self,
|
||||||
|
should_recognize: bool,
|
||||||
|
minimum_number_of_touches: u8,
|
||||||
|
maximum_number_of_touches: u8,
|
||||||
|
) {
|
||||||
|
self.window.maybe_queue_on_main(move |w| {
|
||||||
|
w.recognize_pan_gesture(
|
||||||
|
should_recognize,
|
||||||
|
minimum_number_of_touches,
|
||||||
|
maximum_number_of_touches,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn recognize_doubletap_gesture(&self, should_recognize: bool) {
|
fn recognize_doubletap_gesture(&self, should_recognize: bool) {
|
||||||
self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
|
self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
|
||||||
@@ -326,7 +357,7 @@ impl MonitorHandleExtIOS for MonitorHandle {
|
|||||||
fn ui_screen(&self) -> *mut c_void {
|
fn ui_screen(&self) -> *mut c_void {
|
||||||
// SAFETY: The marker is only used to get the pointer of the screen
|
// SAFETY: The marker is only used to get the pointer of the screen
|
||||||
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
|
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
|
||||||
objc2::rc::Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
|
objc2::rc::Retained::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ impl MonitorHandleExtMacOS for MonitorHandle {
|
|||||||
fn ns_screen(&self) -> Option<*mut c_void> {
|
fn ns_screen(&self) -> Option<*mut c_void> {
|
||||||
// SAFETY: We only use the marker to get a pointer
|
// SAFETY: We only use the marker to get a pointer
|
||||||
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
|
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
|
||||||
self.inner.ns_screen(mtm).map(|s| objc2::rc::Id::as_ptr(&s) as _)
|
self.inner.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,18 +52,18 @@ pub trait EventLoopExtPumpEvents {
|
|||||||
/// - `RedrawRequested` events, used to schedule rendering.
|
/// - `RedrawRequested` events, used to schedule rendering.
|
||||||
///
|
///
|
||||||
/// macOS for example uses a `drawRect` callback to drive rendering
|
/// macOS for example uses a `drawRect` callback to drive rendering
|
||||||
/// within applications and expects rendering to be finished before
|
/// within applications and expects rendering to be finished before
|
||||||
/// the `drawRect` callback returns.
|
/// the `drawRect` callback returns.
|
||||||
///
|
///
|
||||||
/// For portability it's strongly recommended that applications should
|
/// 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`.
|
/// - Any lifecycle events, such as `Suspended` / `Resumed`.
|
||||||
///
|
///
|
||||||
/// The handling of these events needs to be synchronized with the
|
/// The handling of these events needs to be synchronized with the
|
||||||
/// operating system and it would never be appropriate to buffer a
|
/// operating system and it would never be appropriate to buffer a
|
||||||
/// notification that your application has been suspended or resumed and
|
/// notification that your application has been suspended or resumed and
|
||||||
/// then handled that later since there would always be a chance that
|
/// then handled that later since there would always be a chance that
|
||||||
/// other lifecycle events occur while the event is buffered.
|
/// other lifecycle events occur while the event is buffered.
|
||||||
///
|
///
|
||||||
/// ## Supported Platforms
|
/// ## Supported Platforms
|
||||||
///
|
///
|
||||||
@@ -74,13 +74,13 @@ pub trait EventLoopExtPumpEvents {
|
|||||||
///
|
///
|
||||||
/// ## Unsupported Platforms
|
/// ## Unsupported Platforms
|
||||||
///
|
///
|
||||||
/// - **Web:** This API is fundamentally incompatible with the event-based way in which
|
/// - **Web:** This API is fundamentally incompatible with the event-based way in which Web
|
||||||
/// Web browsers work because it's not possible to have a long-running external
|
/// browsers work because it's not possible to have a long-running external loop that would
|
||||||
/// loop that would block the browser and there is nothing that can be
|
/// block the browser and there is nothing that can be polled to ask for new new events.
|
||||||
/// polled to ask for new new events. Events are delivered via callbacks based
|
/// Events are delivered via callbacks based on an event loop that is internal to the browser
|
||||||
/// on an event loop that is internal to the browser itself.
|
/// itself.
|
||||||
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so
|
/// - **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
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ pub trait EventLoopExtRunOnDemand {
|
|||||||
/// # Caveats
|
/// # Caveats
|
||||||
/// - This extension isn't available on all platforms, since it's not always possible to return
|
/// - 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
|
/// 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.
|
/// - 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
|
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
//! * `wayland-csd-adwaita` (default).
|
//! * `wayland-csd-adwaita` (default).
|
||||||
//! * `wayland-csd-adwaita-crossfont`.
|
//! * `wayland-csd-adwaita-crossfont`.
|
||||||
//! * `wayland-csd-adwaita-notitle`.
|
//! * `wayland-csd-adwaita-notitle`.
|
||||||
use crate::event_loop::{ActiveEventLoop, EventLoopBuilder};
|
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
|
||||||
use crate::monitor::MonitorHandle;
|
use crate::monitor::MonitorHandle;
|
||||||
use crate::window::{Window, WindowAttributes};
|
use crate::window::{Window, WindowAttributes};
|
||||||
|
|
||||||
@@ -32,6 +32,19 @@ impl ActiveEventLoopExtWayland for ActiveEventLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Additional methods on [`EventLoop`] that are specific to Wayland.
|
||||||
|
pub trait EventLoopExtWayland {
|
||||||
|
/// True if the [`EventLoop`] uses Wayland.
|
||||||
|
fn is_wayland(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> EventLoopExtWayland for EventLoop<T> {
|
||||||
|
#[inline]
|
||||||
|
fn is_wayland(&self) -> bool {
|
||||||
|
self.event_loop.is_wayland()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Additional methods on [`EventLoopBuilder`] that are specific to Wayland.
|
/// Additional methods on [`EventLoopBuilder`] that are specific to Wayland.
|
||||||
pub trait EventLoopBuilderExtWayland {
|
pub trait EventLoopBuilderExtWayland {
|
||||||
/// Force using Wayland.
|
/// Force using Wayland.
|
||||||
|
|||||||
@@ -190,6 +190,34 @@ pub trait EventLoopExtWebSys {
|
|||||||
fn spawn<F>(self, event_handler: F)
|
fn spawn<F>(self, event_handler: F)
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||||
|
|
||||||
|
/// Sets the strategy for [`ControlFlow::Poll`].
|
||||||
|
///
|
||||||
|
/// See [`PollStrategy`].
|
||||||
|
///
|
||||||
|
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
||||||
|
fn set_poll_strategy(&self, strategy: PollStrategy);
|
||||||
|
|
||||||
|
/// Gets the strategy for [`ControlFlow::Poll`].
|
||||||
|
///
|
||||||
|
/// See [`PollStrategy`].
|
||||||
|
///
|
||||||
|
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
||||||
|
fn poll_strategy(&self) -> PollStrategy;
|
||||||
|
|
||||||
|
/// Sets the strategy for [`ControlFlow::WaitUntil`].
|
||||||
|
///
|
||||||
|
/// See [`WaitUntilStrategy`].
|
||||||
|
///
|
||||||
|
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||||
|
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);
|
||||||
|
|
||||||
|
/// Gets the strategy for [`ControlFlow::WaitUntil`].
|
||||||
|
///
|
||||||
|
/// See [`WaitUntilStrategy`].
|
||||||
|
///
|
||||||
|
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||||
|
fn wait_until_strategy(&self) -> WaitUntilStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
||||||
@@ -207,6 +235,22 @@ impl<T> EventLoopExtWebSys for EventLoop<T> {
|
|||||||
{
|
{
|
||||||
self.event_loop.spawn(event_handler)
|
self.event_loop.spawn(event_handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_poll_strategy(&self, strategy: PollStrategy) {
|
||||||
|
self.event_loop.set_poll_strategy(strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_strategy(&self) -> PollStrategy {
|
||||||
|
self.event_loop.poll_strategy()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
|
||||||
|
self.event_loop.set_wait_until_strategy(strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_until_strategy(&self) -> WaitUntilStrategy {
|
||||||
|
self.event_loop.wait_until_strategy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ActiveEventLoopExtWebSys {
|
pub trait ActiveEventLoopExtWebSys {
|
||||||
@@ -224,6 +268,20 @@ pub trait ActiveEventLoopExtWebSys {
|
|||||||
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
||||||
fn poll_strategy(&self) -> PollStrategy;
|
fn poll_strategy(&self) -> PollStrategy;
|
||||||
|
|
||||||
|
/// Sets the strategy for [`ControlFlow::WaitUntil`].
|
||||||
|
///
|
||||||
|
/// See [`WaitUntilStrategy`].
|
||||||
|
///
|
||||||
|
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||||
|
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);
|
||||||
|
|
||||||
|
/// Gets the strategy for [`ControlFlow::WaitUntil`].
|
||||||
|
///
|
||||||
|
/// See [`WaitUntilStrategy`].
|
||||||
|
///
|
||||||
|
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||||
|
fn wait_until_strategy(&self) -> WaitUntilStrategy;
|
||||||
|
|
||||||
/// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the
|
/// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the
|
||||||
/// cursor has completely finished loading.
|
/// cursor has completely finished loading.
|
||||||
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture;
|
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture;
|
||||||
@@ -244,6 +302,16 @@ impl ActiveEventLoopExtWebSys for ActiveEventLoop {
|
|||||||
fn poll_strategy(&self) -> PollStrategy {
|
fn poll_strategy(&self) -> PollStrategy {
|
||||||
self.p.poll_strategy()
|
self.p.poll_strategy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
|
||||||
|
self.p.set_wait_until_strategy(strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn wait_until_strategy(&self) -> WaitUntilStrategy {
|
||||||
|
self.p.wait_until_strategy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Strategy used for [`ControlFlow::Poll`][crate::event_loop::ControlFlow::Poll].
|
/// Strategy used for [`ControlFlow::Poll`][crate::event_loop::ControlFlow::Poll].
|
||||||
@@ -272,6 +340,29 @@ pub enum PollStrategy {
|
|||||||
Scheduler,
|
Scheduler,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Strategy used for [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil].
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub enum WaitUntilStrategy {
|
||||||
|
/// Uses the [Prioritized Task Scheduling API] to queue the next event loop. If not available
|
||||||
|
/// this will fallback to [`setTimeout()`].
|
||||||
|
///
|
||||||
|
/// This strategy is commonly not affected by browser throttling unless the window is not
|
||||||
|
/// focused.
|
||||||
|
///
|
||||||
|
/// This is the default strategy.
|
||||||
|
///
|
||||||
|
/// [Prioritized Task Scheduling API]: https://developer.mozilla.org/en-US/docs/Web/API/Prioritized_Task_Scheduling_API
|
||||||
|
/// [`setTimeout()`]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
|
||||||
|
#[default]
|
||||||
|
Scheduler,
|
||||||
|
/// Equal to [`Scheduler`][Self::Scheduler] but wakes up the event loop from a [worker].
|
||||||
|
///
|
||||||
|
/// This strategy is commonly not affected by browser throttling regardless of window focus.
|
||||||
|
///
|
||||||
|
/// [worker]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
|
||||||
|
Worker,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait CustomCursorExtWebSys {
|
pub trait CustomCursorExtWebSys {
|
||||||
/// Returns if this cursor is an animation.
|
/// Returns if this cursor is an animation.
|
||||||
fn is_animation(&self) -> bool;
|
fn is_animation(&self) -> bool;
|
||||||
@@ -370,3 +461,5 @@ impl Display for CustomCursorError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Error for CustomCursorError {}
|
||||||
|
|||||||
@@ -476,10 +476,10 @@ pub trait WindowAttributesExtWindows {
|
|||||||
/// the menus look. If you use this, it is recommended that you combine it with
|
/// the menus look. If you use this, it is recommended that you combine it with
|
||||||
/// `with_theme(Some(Theme::Light))` to avoid a jarring effect.
|
/// `with_theme(Some(Theme::Light))` to avoid a jarring effect.
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
platform_windows,
|
windows_platform,
|
||||||
doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
|
doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(not(platform_windows), doc = "[`CreateMenu`]: #only-available-on-windows")]
|
#[cfg_attr(not(windows_platform), doc = "[`CreateMenu`]: #only-available-on-windows")]
|
||||||
fn with_menu(self, menu: HMENU) -> Self;
|
fn with_menu(self, menu: HMENU) -> Self;
|
||||||
|
|
||||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::event_loop::{ActiveEventLoop, EventLoopBuilder};
|
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
|
||||||
use crate::monitor::MonitorHandle;
|
use crate::monitor::MonitorHandle;
|
||||||
use crate::window::{Window, WindowAttributes};
|
use crate::window::{Window, WindowAttributes};
|
||||||
|
|
||||||
@@ -99,6 +99,19 @@ impl ActiveEventLoopExtX11 for ActiveEventLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Additional methods on [`EventLoop`] that are specific to X11.
|
||||||
|
pub trait EventLoopExtX11 {
|
||||||
|
/// True if the [`EventLoop`] uses X11.
|
||||||
|
fn is_x11(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> EventLoopExtX11 for EventLoop<T> {
|
||||||
|
#[inline]
|
||||||
|
fn is_x11(&self) -> bool {
|
||||||
|
!self.event_loop.is_wayland()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Additional methods on [`EventLoopBuilder`] that are specific to X11.
|
/// Additional methods on [`EventLoopBuilder`] that are specific to X11.
|
||||||
pub trait EventLoopBuilderExtX11 {
|
pub trait EventLoopBuilderExtX11 {
|
||||||
/// Force using X11.
|
/// Force using X11.
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(android_platform)]
|
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
@@ -134,7 +132,7 @@ impl RedrawRequester {
|
|||||||
pub struct KeyEventExtra {}
|
pub struct KeyEventExtra {}
|
||||||
|
|
||||||
pub struct EventLoop<T: 'static> {
|
pub struct EventLoop<T: 'static> {
|
||||||
android_app: AndroidApp,
|
pub(crate) android_app: AndroidApp,
|
||||||
window_target: event_loop::ActiveEventLoop,
|
window_target: event_loop::ActiveEventLoop,
|
||||||
redraw_flag: SharedFlag,
|
redraw_flag: SharedFlag,
|
||||||
user_events_sender: mpsc::Sender<T>,
|
user_events_sender: mpsc::Sender<T>,
|
||||||
@@ -648,7 +646,7 @@ impl<T> EventLoopProxy<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ActiveEventLoop {
|
pub struct ActiveEventLoop {
|
||||||
app: AndroidApp,
|
pub(crate) app: AndroidApp,
|
||||||
control_flow: Cell<ControlFlow>,
|
control_flow: Cell<ControlFlow>,
|
||||||
exit: Cell<bool>,
|
exit: Cell<bool>,
|
||||||
redraw_requester: RedrawRequester,
|
redraw_requester: RedrawRequester,
|
||||||
@@ -679,6 +677,11 @@ impl ActiveEventLoop {
|
|||||||
rwh_05::RawDisplayHandle::Android(rwh_05::AndroidDisplayHandle::empty())
|
rwh_05::RawDisplayHandle::Android(rwh_05::AndroidDisplayHandle::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn system_theme(&self) -> Option<Theme> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rwh_06")]
|
#[cfg(feature = "rwh_06")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn raw_display_handle_rwh_06(
|
pub fn raw_display_handle_rwh_06(
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
||||||
use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol};
|
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||||
|
use objc2_ui_kit::UIApplication;
|
||||||
|
|
||||||
use super::app_state::{self, EventWrapper};
|
use super::app_state::{self, send_occluded_event_for_all_windows, EventWrapper};
|
||||||
use super::uikit::{UIApplication, UIWindow};
|
use crate::event::Event;
|
||||||
use super::window::WinitUIWindow;
|
|
||||||
use crate::event::{Event, WindowEvent};
|
|
||||||
use crate::window::WindowId as RootWindowId;
|
|
||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
pub struct AppDelegate;
|
pub struct AppDelegate;
|
||||||
@@ -40,34 +38,17 @@ declare_class!(
|
|||||||
|
|
||||||
#[method(applicationWillEnterForeground:)]
|
#[method(applicationWillEnterForeground:)]
|
||||||
fn will_enter_foreground(&self, application: &UIApplication) {
|
fn will_enter_foreground(&self, application: &UIApplication) {
|
||||||
self.send_occluded_event_for_all_windows(application, false);
|
send_occluded_event_for_all_windows(application, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(applicationDidEnterBackground:)]
|
#[method(applicationDidEnterBackground:)]
|
||||||
fn did_enter_background(&self, application: &UIApplication) {
|
fn did_enter_background(&self, application: &UIApplication) {
|
||||||
self.send_occluded_event_for_all_windows(application, true);
|
send_occluded_event_for_all_windows(application, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(applicationWillTerminate:)]
|
#[method(applicationWillTerminate:)]
|
||||||
fn will_terminate(&self, application: &UIApplication) {
|
fn will_terminate(&self, application: &UIApplication) {
|
||||||
let mut events = Vec::new();
|
app_state::terminated(application);
|
||||||
for window in application.windows().iter() {
|
|
||||||
if window.is_kind_of::<WinitUIWindow>() {
|
|
||||||
// SAFETY: We just checked that the window is a `winit` window
|
|
||||||
let window = unsafe {
|
|
||||||
let ptr: *const UIWindow = window;
|
|
||||||
let ptr: *const WinitUIWindow = ptr.cast();
|
|
||||||
&*ptr
|
|
||||||
};
|
|
||||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
|
||||||
window_id: RootWindowId(window.id()),
|
|
||||||
event: WindowEvent::Destroyed,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
|
||||||
app_state::handle_nonuser_events(mtm, events);
|
|
||||||
app_state::terminated(mtm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(applicationDidReceiveMemoryWarning:)]
|
#[method(applicationDidReceiveMemoryWarning:)]
|
||||||
@@ -77,25 +58,3 @@ declare_class!(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
impl AppDelegate {
|
|
||||||
fn send_occluded_event_for_all_windows(&self, application: &UIApplication, occluded: bool) {
|
|
||||||
let mut events = Vec::new();
|
|
||||||
for window in application.windows().iter() {
|
|
||||||
if window.is_kind_of::<WinitUIWindow>() {
|
|
||||||
// SAFETY: We just checked that the window is a `winit` window
|
|
||||||
let window = unsafe {
|
|
||||||
let ptr: *const UIWindow = window;
|
|
||||||
let ptr: *const WinitUIWindow = ptr.cast();
|
|
||||||
&*ptr
|
|
||||||
};
|
|
||||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
|
||||||
window_id: RootWindowId(window.id()),
|
|
||||||
event: WindowEvent::Occluded(occluded),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
|
||||||
app_state::handle_nonuser_events(mtm, events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,14 +13,15 @@ use core_foundation::runloop::{
|
|||||||
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
||||||
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
||||||
};
|
};
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::AnyObject;
|
use objc2::runtime::AnyObject;
|
||||||
use objc2::{msg_send, sel};
|
use objc2::{msg_send, sel};
|
||||||
use objc2_foundation::{
|
use objc2_foundation::{
|
||||||
CGRect, CGSize, MainThreadMarker, NSInteger, NSOperatingSystemVersion, NSProcessInfo,
|
CGRect, CGSize, MainThreadMarker, NSInteger, NSObjectProtocol, NSOperatingSystemVersion,
|
||||||
|
NSProcessInfo,
|
||||||
};
|
};
|
||||||
|
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
|
||||||
|
|
||||||
use super::uikit::UIView;
|
|
||||||
use super::window::WinitUIWindow;
|
use super::window::WinitUIWindow;
|
||||||
use crate::dpi::PhysicalSize;
|
use crate::dpi::PhysicalSize;
|
||||||
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||||
@@ -71,7 +72,7 @@ pub(crate) enum EventWrapper {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ScaleFactorChanged {
|
pub struct ScaleFactorChanged {
|
||||||
pub(super) window: Id<WinitUIWindow>,
|
pub(super) window: Retained<WinitUIWindow>,
|
||||||
pub(super) suggested_size: PhysicalSize<u32>,
|
pub(super) suggested_size: PhysicalSize<u32>,
|
||||||
pub(super) scale_factor: f64,
|
pub(super) scale_factor: f64,
|
||||||
}
|
}
|
||||||
@@ -98,25 +99,25 @@ impl Event<HandlePendingUserEvents> {
|
|||||||
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
|
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
|
||||||
enum AppStateImpl {
|
enum AppStateImpl {
|
||||||
NotLaunched {
|
NotLaunched {
|
||||||
queued_windows: Vec<Id<WinitUIWindow>>,
|
queued_windows: Vec<Retained<WinitUIWindow>>,
|
||||||
queued_events: Vec<EventWrapper>,
|
queued_events: Vec<EventWrapper>,
|
||||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
|
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||||
},
|
},
|
||||||
Launching {
|
Launching {
|
||||||
queued_windows: Vec<Id<WinitUIWindow>>,
|
queued_windows: Vec<Retained<WinitUIWindow>>,
|
||||||
queued_events: Vec<EventWrapper>,
|
queued_events: Vec<EventWrapper>,
|
||||||
queued_handler: EventLoopHandler,
|
queued_handler: EventLoopHandler,
|
||||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
|
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||||
},
|
},
|
||||||
ProcessingEvents {
|
ProcessingEvents {
|
||||||
handler: EventLoopHandler,
|
handler: EventLoopHandler,
|
||||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
|
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||||
active_control_flow: ControlFlow,
|
active_control_flow: ControlFlow,
|
||||||
},
|
},
|
||||||
// special state to deal with reentrancy and prevent mutable aliasing.
|
// special state to deal with reentrancy and prevent mutable aliasing.
|
||||||
InUserCallback {
|
InUserCallback {
|
||||||
queued_events: Vec<EventWrapper>,
|
queued_events: Vec<EventWrapper>,
|
||||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
|
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||||
},
|
},
|
||||||
ProcessingRedraws {
|
ProcessingRedraws {
|
||||||
handler: EventLoopHandler,
|
handler: EventLoopHandler,
|
||||||
@@ -227,7 +228,9 @@ impl AppState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) {
|
fn did_finish_launching_transition(
|
||||||
|
&mut self,
|
||||||
|
) -> (Vec<Retained<WinitUIWindow>>, Vec<EventWrapper>) {
|
||||||
let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
|
let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
|
||||||
AppStateImpl::Launching {
|
AppStateImpl::Launching {
|
||||||
queued_windows,
|
queued_windows,
|
||||||
@@ -343,7 +346,7 @@ impl AppState {
|
|||||||
UserCallbackTransitionResult::Success { handler, active_control_flow, processing_redraws }
|
UserCallbackTransitionResult::Success { handler, active_control_flow, processing_redraws }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> {
|
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
|
||||||
let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
||||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => {
|
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => {
|
||||||
(handler, queued_gpu_redraws, active_control_flow)
|
(handler, queued_gpu_redraws, active_control_flow)
|
||||||
@@ -411,7 +414,7 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id<WinitUIWindow>) {
|
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWindow>) {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let mut this = AppState::get_mut(mtm);
|
||||||
match this.state_mut() {
|
match this.state_mut() {
|
||||||
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } => {
|
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } => {
|
||||||
@@ -431,7 +434,7 @@ pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id<WinitUIWindow>)
|
|||||||
window.makeKeyAndVisible();
|
window.makeKeyAndVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUIWindow>) {
|
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let mut this = AppState::get_mut(mtm);
|
||||||
match this.state_mut() {
|
match this.state_mut() {
|
||||||
&mut AppStateImpl::NotLaunched { ref mut queued_gpu_redraws, .. }
|
&mut AppStateImpl::NotLaunched { ref mut queued_gpu_redraws, .. }
|
||||||
@@ -659,6 +662,28 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, occluded: bool) {
|
||||||
|
let mtm = MainThreadMarker::from(application);
|
||||||
|
|
||||||
|
let mut events = Vec::new();
|
||||||
|
#[allow(deprecated)]
|
||||||
|
for window in application.windows().iter() {
|
||||||
|
if window.is_kind_of::<WinitUIWindow>() {
|
||||||
|
// SAFETY: We just checked that the window is a `winit` window
|
||||||
|
let window = unsafe {
|
||||||
|
let ptr: *const UIWindow = window;
|
||||||
|
let ptr: *const WinitUIWindow = ptr.cast();
|
||||||
|
&*ptr
|
||||||
|
};
|
||||||
|
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
|
window_id: RootWindowId(window.id()),
|
||||||
|
event: WindowEvent::Occluded(occluded),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle_nonuser_events(mtm, events);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let mut this = AppState::get_mut(mtm);
|
||||||
if !this.has_launched() || this.has_terminated() {
|
if !this.has_launched() || this.has_terminated() {
|
||||||
@@ -693,7 +718,27 @@ pub fn handle_events_cleared(mtm: MainThreadMarker) {
|
|||||||
AppState::get_mut(mtm).events_cleared_transition();
|
AppState::get_mut(mtm).events_cleared_transition();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminated(mtm: MainThreadMarker) {
|
pub(crate) fn terminated(application: &UIApplication) {
|
||||||
|
let mtm = MainThreadMarker::from(application);
|
||||||
|
|
||||||
|
let mut events = Vec::new();
|
||||||
|
#[allow(deprecated)]
|
||||||
|
for window in application.windows().iter() {
|
||||||
|
if window.is_kind_of::<WinitUIWindow>() {
|
||||||
|
// SAFETY: We just checked that the window is a `winit` window
|
||||||
|
let window = unsafe {
|
||||||
|
let ptr: *const UIWindow = window;
|
||||||
|
let ptr: *const WinitUIWindow = ptr.cast();
|
||||||
|
&*ptr
|
||||||
|
};
|
||||||
|
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
|
window_id: RootWindowId(window.id()),
|
||||||
|
event: WindowEvent::Destroyed,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle_nonuser_events(mtm, events);
|
||||||
|
|
||||||
let mut this = AppState::get_mut(mtm);
|
let mut this = AppState::get_mut(mtm);
|
||||||
let mut handler = this.terminated_transition();
|
let mut handler = this.terminated_transition();
|
||||||
drop(this);
|
drop(this);
|
||||||
@@ -721,7 +766,7 @@ fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged)
|
|||||||
view.setFrame(new_frame);
|
view.setFrame(new_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Id<UIView>, CGRect) {
|
fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Retained<UIView>, CGRect) {
|
||||||
let view_controller = window.rootViewController().unwrap();
|
let view_controller = window.rootViewController().unwrap();
|
||||||
let view = view_controller.view().unwrap();
|
let view = view_controller.view().unwrap();
|
||||||
let bounds = window.bounds();
|
let bounds = window.bounds();
|
||||||
@@ -753,7 +798,7 @@ impl EventLoopWaker {
|
|||||||
// future, but that gets changed to fire immediately in did_finish_launching
|
// future, but that gets changed to fire immediately in did_finish_launching
|
||||||
let timer = CFRunLoopTimerCreate(
|
let timer = CFRunLoopTimerCreate(
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
std::f64::MAX,
|
f64::MAX,
|
||||||
0.000_000_1,
|
0.000_000_1,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -767,11 +812,11 @@ impl EventLoopWaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MAX) }
|
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, f64::MAX) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&mut self) {
|
fn start(&mut self) {
|
||||||
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MIN) }
|
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, f64::MIN) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_at(&mut self, instant: Instant) {
|
fn start_at(&mut self, instant: Instant) {
|
||||||
@@ -857,23 +902,17 @@ fn meets_requirements(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_version() -> NSOperatingSystemVersion {
|
fn get_version() -> NSOperatingSystemVersion {
|
||||||
unsafe {
|
let process_info = NSProcessInfo::processInfo();
|
||||||
let process_info = NSProcessInfo::processInfo();
|
let atleast_ios_8 = process_info.respondsToSelector(sel!(operatingSystemVersion));
|
||||||
let atleast_ios_8: bool = msg_send![
|
// Winit requires atleast iOS 8 because no one has put the time into supporting earlier os
|
||||||
&process_info,
|
// versions. Older iOS versions are increasingly difficult to test. For example, Xcode 11 does
|
||||||
respondsToSelector: sel!(operatingSystemVersion)
|
// not support debugging on devices with an iOS version of less than 8. Another example, in
|
||||||
];
|
// order to use an iOS simulator older than iOS 8, you must download an older version of Xcode
|
||||||
// winit requires atleast iOS 8 because no one has put the time into supporting earlier os
|
// (<9), and at least Xcode 7 has been tested to not even run on macOS 10.15 - Xcode 8 might?
|
||||||
// versions. Older iOS versions are increasingly difficult to test. For example,
|
//
|
||||||
// Xcode 11 does not support debugging on devices with an iOS version of less than
|
// The minimum required iOS version is likely to grow in the future.
|
||||||
// 8. Another example, in order to use an iOS simulator older than iOS 8, you must
|
assert!(atleast_ios_8, "`winit` requires iOS version 8 or greater");
|
||||||
// download an older version of Xcode (<9), and at least Xcode 7 has been tested to
|
process_info.operatingSystemVersion()
|
||||||
// not even run on macOS 10.15 - Xcode 8 might?
|
|
||||||
//
|
|
||||||
// The minimum required iOS version is likely to grow in the future.
|
|
||||||
assert!(atleast_ios_8, "`winit` requires iOS version 8 or greater");
|
|
||||||
process_info.operatingSystemVersion()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn os_capabilities() -> OSCapabilities {
|
pub fn os_capabilities() -> OSCapabilities {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::ffi::c_void;
|
use std::ffi::{c_char, c_int, c_void};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ptr;
|
use std::ptr::{self, NonNull};
|
||||||
use std::sync::mpsc::{self, Receiver, Sender};
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
|
||||||
use core_foundation::base::{CFIndex, CFRelease};
|
use core_foundation::base::{CFIndex, CFRelease};
|
||||||
@@ -11,8 +11,10 @@ use core_foundation::runloop::{
|
|||||||
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
||||||
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||||
};
|
};
|
||||||
use objc2::ClassType;
|
use objc2::rc::Retained;
|
||||||
|
use objc2::{msg_send_id, ClassType};
|
||||||
use objc2_foundation::{MainThreadMarker, NSString};
|
use objc2_foundation::{MainThreadMarker, NSString};
|
||||||
|
use objc2_ui_kit::{UIApplication, UIApplicationMain, UIDevice, UIScreen, UIUserInterfaceIdiom};
|
||||||
|
|
||||||
use crate::error::EventLoopError;
|
use crate::error::EventLoopError;
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
@@ -20,12 +22,11 @@ use crate::event_loop::{
|
|||||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed,
|
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||||
};
|
};
|
||||||
use crate::platform::ios::Idiom;
|
use crate::platform::ios::Idiom;
|
||||||
use crate::platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents};
|
use crate::platform_impl::ios::app_state::{EventLoopHandler, HandlePendingUserEvents};
|
||||||
use crate::window::{CustomCursor, CustomCursorSource};
|
use crate::window::{CustomCursor, CustomCursorSource, Theme};
|
||||||
|
|
||||||
use super::app_delegate::AppDelegate;
|
use super::app_delegate::AppDelegate;
|
||||||
use super::app_state::AppState;
|
use super::app_state::AppState;
|
||||||
use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen, UIUserInterfaceIdiom};
|
|
||||||
use super::{app_state, monitor, MonitorHandle};
|
use super::{app_state, monitor, MonitorHandle};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -44,7 +45,8 @@ impl ActiveEventLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||||
Some(MonitorHandle::new(UIScreen::main(self.mtm)))
|
#[allow(deprecated)]
|
||||||
|
Some(MonitorHandle::new(UIScreen::mainScreen(self.mtm)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -56,6 +58,11 @@ impl ActiveEventLoop {
|
|||||||
rwh_05::RawDisplayHandle::UiKit(rwh_05::UiKitDisplayHandle::empty())
|
rwh_05::RawDisplayHandle::UiKit(rwh_05::UiKitDisplayHandle::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn system_theme(&self) -> Option<Theme> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rwh_06")]
|
#[cfg(feature = "rwh_06")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn raw_display_handle_rwh_06(
|
pub fn raw_display_handle_rwh_06(
|
||||||
@@ -163,7 +170,8 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
where
|
where
|
||||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||||
{
|
{
|
||||||
let application = UIApplication::shared(self.mtm);
|
let application: Option<Retained<UIApplication>> =
|
||||||
|
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
|
||||||
assert!(
|
assert!(
|
||||||
application.is_none(),
|
application.is_none(),
|
||||||
"\
|
"\
|
||||||
@@ -187,8 +195,19 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
// Ensure application delegate is initialized
|
// Ensure application delegate is initialized
|
||||||
let _ = AppDelegate::class();
|
let _ = AppDelegate::class();
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
// These functions are in crt_externs.h.
|
||||||
|
fn _NSGetArgc() -> *mut c_int;
|
||||||
|
fn _NSGetArgv() -> *mut *mut *mut c_char;
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
UIApplicationMain(0, ptr::null(), None, Some(&NSString::from_str(AppDelegate::NAME)))
|
UIApplicationMain(
|
||||||
|
*_NSGetArgc(),
|
||||||
|
NonNull::new(*_NSGetArgv()).unwrap(),
|
||||||
|
None,
|
||||||
|
Some(&NSString::from_str(AppDelegate::NAME)),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
@@ -205,7 +224,7 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
// EventLoopExtIOS
|
// EventLoopExtIOS
|
||||||
impl<T: 'static> EventLoop<T> {
|
impl<T: 'static> EventLoop<T> {
|
||||||
pub fn idiom(&self) -> Idiom {
|
pub fn idiom(&self) -> Idiom {
|
||||||
match UIDevice::current(self.mtm).userInterfaceIdiom() {
|
match UIDevice::currentDevice(self.mtm).userInterfaceIdiom() {
|
||||||
UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified,
|
UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified,
|
||||||
UIUserInterfaceIdiom::Phone => Idiom::Phone,
|
UIUserInterfaceIdiom::Phone => Idiom::Phone,
|
||||||
UIUserInterfaceIdiom::Pad => Idiom::Pad,
|
UIUserInterfaceIdiom::Pad => Idiom::Pad,
|
||||||
@@ -260,8 +279,7 @@ impl<T> EventLoopProxy<T> {
|
|||||||
cancel: None,
|
cancel: None,
|
||||||
perform: event_loop_proxy_handler,
|
perform: event_loop_proxy_handler,
|
||||||
};
|
};
|
||||||
let source =
|
let source = CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::MAX - 1, &mut context);
|
||||||
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
|
|
||||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||||
CFRunLoopWakeUp(rl);
|
CFRunLoopWakeUp(rl);
|
||||||
|
|
||||||
@@ -344,7 +362,7 @@ fn setup_control_flow_observers() {
|
|||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
kCFRunLoopAfterWaiting,
|
kCFRunLoopAfterWaiting,
|
||||||
1, // repeat = true
|
1, // repeat = true
|
||||||
CFIndex::min_value(),
|
CFIndex::MIN,
|
||||||
control_flow_begin_handler,
|
control_flow_begin_handler,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
);
|
);
|
||||||
@@ -364,7 +382,7 @@ fn setup_control_flow_observers() {
|
|||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
||||||
1, // repeat = true
|
1, // repeat = true
|
||||||
CFIndex::max_value(),
|
CFIndex::MAX,
|
||||||
control_flow_end_handler,
|
control_flow_end_handler,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#![cfg(ios_platform)]
|
|
||||||
#![allow(clippy::let_unit_value)]
|
#![allow(clippy::let_unit_value)]
|
||||||
|
|
||||||
mod app_delegate;
|
mod app_delegate;
|
||||||
mod app_state;
|
mod app_state;
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod monitor;
|
mod monitor;
|
||||||
mod uikit;
|
|
||||||
mod view;
|
mod view;
|
||||||
mod view_controller;
|
mod view_controller;
|
||||||
mod window;
|
mod window;
|
||||||
@@ -34,7 +32,7 @@ pub(crate) use crate::platform_impl::Fullscreen;
|
|||||||
pub struct DeviceId;
|
pub struct DeviceId;
|
||||||
|
|
||||||
impl DeviceId {
|
impl DeviceId {
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
DeviceId
|
DeviceId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,22 +4,22 @@ use std::collections::{BTreeSet, VecDeque};
|
|||||||
use std::{fmt, hash, ptr};
|
use std::{fmt, hash, ptr};
|
||||||
|
|
||||||
use objc2::mutability::IsRetainable;
|
use objc2::mutability::IsRetainable;
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2::Message;
|
use objc2::Message;
|
||||||
use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker, NSInteger};
|
use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker, NSInteger};
|
||||||
|
use objc2_ui_kit::{UIScreen, UIScreenMode};
|
||||||
|
|
||||||
use super::uikit::{UIScreen, UIScreenMode};
|
|
||||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||||
use crate::monitor::VideoModeHandle as RootVideoModeHandle;
|
use crate::monitor::VideoModeHandle as RootVideoModeHandle;
|
||||||
use crate::platform_impl::platform::app_state;
|
use crate::platform_impl::platform::app_state;
|
||||||
|
|
||||||
// Workaround for `MainThreadBound` implementing almost no traits
|
// Workaround for `MainThreadBound` implementing almost no traits
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Id<T>>);
|
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Retained<T>>);
|
||||||
|
|
||||||
impl<T: IsRetainable + Message> Clone for MainThreadBoundDelegateImpls<T> {
|
impl<T: IsRetainable + Message> Clone for MainThreadBoundDelegateImpls<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self(run_on_main(|mtm| MainThreadBound::new(Id::clone(self.0.get(mtm)), mtm)))
|
Self(run_on_main(|mtm| MainThreadBound::new(Retained::clone(self.0.get(mtm)), mtm)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ impl<T: IsRetainable + Message> hash::Hash for MainThreadBoundDelegateImpls<T> {
|
|||||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||||
// SAFETY: Marker only used to get the pointer
|
// SAFETY: Marker only used to get the pointer
|
||||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||||
Id::as_ptr(self.0.get(mtm)).hash(state);
|
Retained::as_ptr(self.0.get(mtm)).hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ impl<T: IsRetainable + Message> PartialEq for MainThreadBoundDelegateImpls<T> {
|
|||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
// SAFETY: Marker only used to get the pointer
|
// SAFETY: Marker only used to get the pointer
|
||||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||||
Id::as_ptr(self.0.get(mtm)) == Id::as_ptr(other.0.get(mtm))
|
Retained::as_ptr(self.0.get(mtm)) == Retained::as_ptr(other.0.get(mtm))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,8 +52,8 @@ pub struct VideoModeHandle {
|
|||||||
|
|
||||||
impl VideoModeHandle {
|
impl VideoModeHandle {
|
||||||
fn new(
|
fn new(
|
||||||
uiscreen: Id<UIScreen>,
|
uiscreen: Retained<UIScreen>,
|
||||||
screen_mode: Id<UIScreenMode>,
|
screen_mode: Retained<UIScreenMode>,
|
||||||
mtm: MainThreadMarker,
|
mtm: MainThreadMarker,
|
||||||
) -> VideoModeHandle {
|
) -> VideoModeHandle {
|
||||||
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
|
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
|
||||||
@@ -83,13 +83,13 @@ impl VideoModeHandle {
|
|||||||
self.monitor.clone()
|
self.monitor.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Id<UIScreenMode> {
|
pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Retained<UIScreenMode> {
|
||||||
self.screen_mode.0.get(mtm)
|
self.screen_mode.0.get(mtm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MonitorHandle {
|
pub struct MonitorHandle {
|
||||||
ui_screen: MainThreadBound<Id<UIScreen>>,
|
ui_screen: MainThreadBound<Retained<UIScreen>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for MonitorHandle {
|
impl Clone for MonitorHandle {
|
||||||
@@ -140,20 +140,22 @@ impl fmt::Debug for MonitorHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MonitorHandle {
|
impl MonitorHandle {
|
||||||
pub(crate) fn new(ui_screen: Id<UIScreen>) -> Self {
|
pub(crate) fn new(ui_screen: Retained<UIScreen>) -> Self {
|
||||||
// Holding `Id<UIScreen>` implies we're on the main thread.
|
// Holding `Retained<UIScreen>` implies we're on the main thread.
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
Self { ui_screen: MainThreadBound::new(ui_screen, mtm) }
|
Self { ui_screen: MainThreadBound::new(ui_screen, mtm) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> Option<String> {
|
pub fn name(&self) -> Option<String> {
|
||||||
run_on_main(|mtm| {
|
run_on_main(|mtm| {
|
||||||
let main = UIScreen::main(mtm);
|
#[allow(deprecated)]
|
||||||
|
let main = UIScreen::mainScreen(mtm);
|
||||||
if *self.ui_screen(mtm) == main {
|
if *self.ui_screen(mtm) == main {
|
||||||
Some("Primary".to_string())
|
Some("Primary".to_string())
|
||||||
} else if *self.ui_screen(mtm) == main.mirroredScreen() {
|
} else if Some(self.ui_screen(mtm)) == main.mirroredScreen().as_ref() {
|
||||||
Some("Mirrored".to_string())
|
Some("Mirrored".to_string())
|
||||||
} else {
|
} else {
|
||||||
|
#[allow(deprecated)]
|
||||||
UIScreen::screens(mtm)
|
UIScreen::screens(mtm)
|
||||||
.iter()
|
.iter()
|
||||||
.position(|rhs| rhs == &**self.ui_screen(mtm))
|
.position(|rhs| rhs == &**self.ui_screen(mtm))
|
||||||
@@ -197,7 +199,7 @@ impl MonitorHandle {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Id<UIScreen> {
|
pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Retained<UIScreen> {
|
||||||
self.ui_screen.get(mtm)
|
self.ui_screen.get(mtm)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,5 +239,6 @@ fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
||||||
|
#[allow(deprecated)]
|
||||||
UIScreen::screens(mtm).into_iter().map(MonitorHandle::new).collect()
|
UIScreen::screens(mtm).into_iter().map(MonitorHandle::new).collect()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
use objc2::rc::Id;
|
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
|
||||||
use objc2_foundation::{CGRect, MainThreadMarker, NSArray, NSObject};
|
|
||||||
|
|
||||||
use super::{UIResponder, UIWindow};
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIApplication;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIApplication {
|
|
||||||
#[inherits(NSObject)]
|
|
||||||
type Super = UIResponder;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIApplication {
|
|
||||||
pub fn shared(_mtm: MainThreadMarker) -> Option<Id<Self>> {
|
|
||||||
unsafe { msg_send_id![Self::class(), sharedApplication] }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn windows(&self) -> Id<NSArray<UIWindow>> {
|
|
||||||
unsafe { msg_send_id![self, windows] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(statusBarFrame)]
|
|
||||||
pub fn statusBarFrame(&self) -> CGRect;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
use objc2::{extern_class, mutability, ClassType};
|
|
||||||
use objc2_foundation::NSObject;
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UICoordinateSpace;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UICoordinateSpace {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
use objc2::encode::{Encode, Encoding};
|
|
||||||
use objc2::rc::Id;
|
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
|
||||||
use objc2_foundation::{MainThreadMarker, NSInteger, NSObject};
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIDevice;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIDevice {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIDevice {
|
|
||||||
pub fn current(_mtm: MainThreadMarker) -> Id<Self> {
|
|
||||||
unsafe { msg_send_id![Self::class(), currentDevice] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(userInterfaceIdiom)]
|
|
||||||
pub fn userInterfaceIdiom(&self) -> UIUserInterfaceIdiom;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct UIUserInterfaceIdiom(NSInteger);
|
|
||||||
|
|
||||||
unsafe impl Encode for UIUserInterfaceIdiom {
|
|
||||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UIUserInterfaceIdiom {
|
|
||||||
pub const Unspecified: UIUserInterfaceIdiom = UIUserInterfaceIdiom(-1);
|
|
||||||
pub const Phone: UIUserInterfaceIdiom = UIUserInterfaceIdiom(0);
|
|
||||||
pub const Pad: UIUserInterfaceIdiom = UIUserInterfaceIdiom(1);
|
|
||||||
pub const TV: UIUserInterfaceIdiom = UIUserInterfaceIdiom(2);
|
|
||||||
pub const CarPlay: UIUserInterfaceIdiom = UIUserInterfaceIdiom(3);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
use objc2::{extern_class, mutability, ClassType};
|
|
||||||
use objc2_foundation::NSObject;
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIEvent;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIEvent {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
use objc2::encode::{Encode, Encoding};
|
|
||||||
use objc2_foundation::NSUInteger;
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct UIRectEdge(pub NSUInteger);
|
|
||||||
|
|
||||||
impl UIRectEdge {
|
|
||||||
pub const NONE: Self = Self(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Encode for UIRectEdge {
|
|
||||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
use objc2::encode::{Encode, Encoding};
|
|
||||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
|
||||||
use objc2_foundation::{CGFloat, NSInteger, NSObject, NSUInteger};
|
|
||||||
|
|
||||||
// https://developer.apple.com/documentation/uikit/uigesturerecognizer
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIGestureRecognizer;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIGestureRecognizer {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIGestureRecognizer {
|
|
||||||
#[method(state)]
|
|
||||||
pub fn state(&self) -> UIGestureRecognizerState;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe impl Encode for UIGestureRecognizer {
|
|
||||||
const ENCODING: Encoding = Encoding::Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://developer.apple.com/documentation/uikit/uigesturerecognizer/state
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct UIGestureRecognizerState(NSInteger);
|
|
||||||
|
|
||||||
unsafe impl Encode for UIGestureRecognizerState {
|
|
||||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl UIGestureRecognizerState {
|
|
||||||
pub const Possible: Self = Self(0);
|
|
||||||
pub const Began: Self = Self(1);
|
|
||||||
pub const Changed: Self = Self(2);
|
|
||||||
pub const Ended: Self = Self(3);
|
|
||||||
pub const Cancelled: Self = Self(4);
|
|
||||||
pub const Failed: Self = Self(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://developer.apple.com/documentation/uikit/uipinchgesturerecognizer
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIPinchGestureRecognizer;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIPinchGestureRecognizer {
|
|
||||||
type Super = UIGestureRecognizer;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIPinchGestureRecognizer {
|
|
||||||
#[method(scale)]
|
|
||||||
pub fn scale(&self) -> CGFloat;
|
|
||||||
|
|
||||||
#[method(velocity)]
|
|
||||||
pub fn velocity(&self) -> CGFloat;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe impl Encode for UIPinchGestureRecognizer {
|
|
||||||
const ENCODING: Encoding = Encoding::Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://developer.apple.com/documentation/uikit/uirotationgesturerecognizer
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIRotationGestureRecognizer;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIRotationGestureRecognizer {
|
|
||||||
type Super = UIGestureRecognizer;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIRotationGestureRecognizer {
|
|
||||||
#[method(rotation)]
|
|
||||||
pub fn rotation(&self) -> CGFloat;
|
|
||||||
|
|
||||||
#[method(velocity)]
|
|
||||||
pub fn velocity(&self) -> CGFloat;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe impl Encode for UIRotationGestureRecognizer {
|
|
||||||
const ENCODING: Encoding = Encoding::Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://developer.apple.com/documentation/uikit/uitapgesturerecognizer
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UITapGestureRecognizer;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UITapGestureRecognizer {
|
|
||||||
type Super = UIGestureRecognizer;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UITapGestureRecognizer {
|
|
||||||
#[method(setNumberOfTapsRequired:)]
|
|
||||||
pub fn setNumberOfTapsRequired(&self, number_of_taps_required: NSUInteger);
|
|
||||||
|
|
||||||
#[method(setNumberOfTouchesRequired:)]
|
|
||||||
pub fn setNumberOfTouchesRequired(&self, number_of_touches_required: NSUInteger);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe impl Encode for UITapGestureRecognizer {
|
|
||||||
const ENCODING: Encoding = Encoding::Object;
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#![allow(non_snake_case)]
|
|
||||||
#![allow(non_upper_case_globals)]
|
|
||||||
|
|
||||||
use std::os::raw::{c_char, c_int};
|
|
||||||
|
|
||||||
use objc2_foundation::NSString;
|
|
||||||
|
|
||||||
mod application;
|
|
||||||
mod coordinate_space;
|
|
||||||
mod device;
|
|
||||||
mod event;
|
|
||||||
mod geometry;
|
|
||||||
mod gesture_recognizer;
|
|
||||||
mod responder;
|
|
||||||
mod screen;
|
|
||||||
mod screen_mode;
|
|
||||||
mod status_bar_style;
|
|
||||||
mod touch;
|
|
||||||
mod trait_collection;
|
|
||||||
mod view;
|
|
||||||
mod view_controller;
|
|
||||||
mod window;
|
|
||||||
|
|
||||||
pub(crate) use self::application::UIApplication;
|
|
||||||
pub(crate) use self::coordinate_space::UICoordinateSpace;
|
|
||||||
pub(crate) use self::device::{UIDevice, UIUserInterfaceIdiom};
|
|
||||||
pub(crate) use self::event::UIEvent;
|
|
||||||
pub(crate) use self::geometry::UIRectEdge;
|
|
||||||
pub(crate) use self::gesture_recognizer::{
|
|
||||||
UIGestureRecognizer, UIGestureRecognizerState, UIPinchGestureRecognizer,
|
|
||||||
UIRotationGestureRecognizer, UITapGestureRecognizer,
|
|
||||||
};
|
|
||||||
pub(crate) use self::responder::UIResponder;
|
|
||||||
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};
|
|
||||||
pub(crate) use self::screen_mode::UIScreenMode;
|
|
||||||
pub(crate) use self::status_bar_style::UIStatusBarStyle;
|
|
||||||
pub(crate) use self::touch::{UITouch, UITouchPhase, UITouchType};
|
|
||||||
pub(crate) use self::trait_collection::{UIForceTouchCapability, UITraitCollection};
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
pub(crate) use self::view::{UIEdgeInsets, UIView};
|
|
||||||
pub(crate) use self::view_controller::{UIInterfaceOrientationMask, UIViewController};
|
|
||||||
pub(crate) use self::window::UIWindow;
|
|
||||||
|
|
||||||
#[link(name = "UIKit", kind = "framework")]
|
|
||||||
extern "C" {
|
|
||||||
pub fn UIApplicationMain(
|
|
||||||
argc: c_int,
|
|
||||||
argv: *const c_char,
|
|
||||||
principalClassName: Option<&NSString>,
|
|
||||||
delegateClassName: Option<&NSString>,
|
|
||||||
) -> c_int;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
use objc2::{extern_class, mutability, ClassType};
|
|
||||||
use objc2_foundation::NSObject;
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIResponder;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIResponder {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
use objc2::encode::{Encode, Encoding};
|
|
||||||
use objc2::rc::Id;
|
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
|
||||||
use objc2_foundation::{CGFloat, CGRect, MainThreadMarker, NSArray, NSInteger, NSObject};
|
|
||||||
|
|
||||||
use super::{UICoordinateSpace, UIScreenMode};
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIScreen;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIScreen {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIScreen {
|
|
||||||
pub fn main(_mtm: MainThreadMarker) -> Id<Self> {
|
|
||||||
unsafe { msg_send_id![Self::class(), mainScreen] }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn screens(_mtm: MainThreadMarker) -> Id<NSArray<Self>> {
|
|
||||||
unsafe { msg_send_id![Self::class(), screens] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(bounds)]
|
|
||||||
pub fn bounds(&self) -> CGRect;
|
|
||||||
|
|
||||||
#[method(scale)]
|
|
||||||
pub fn scale(&self) -> CGFloat;
|
|
||||||
|
|
||||||
#[method(nativeBounds)]
|
|
||||||
pub fn nativeBounds(&self) -> CGRect;
|
|
||||||
|
|
||||||
#[method(nativeScale)]
|
|
||||||
pub fn nativeScale(&self) -> CGFloat;
|
|
||||||
|
|
||||||
#[method(maximumFramesPerSecond)]
|
|
||||||
pub fn maximumFramesPerSecond(&self) -> NSInteger;
|
|
||||||
|
|
||||||
pub fn mirroredScreen(&self) -> Id<Self> {
|
|
||||||
unsafe { msg_send_id![Self::class(), mirroredScreen] }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn preferredMode(&self) -> Option<Id<UIScreenMode>> {
|
|
||||||
unsafe { msg_send_id![self, preferredMode] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(setCurrentMode:)]
|
|
||||||
pub fn setCurrentMode(&self, mode: Option<&UIScreenMode>);
|
|
||||||
|
|
||||||
pub fn availableModes(&self) -> Id<NSArray<UIScreenMode>> {
|
|
||||||
unsafe { msg_send_id![self, availableModes] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(setOverscanCompensation:)]
|
|
||||||
pub fn setOverscanCompensation(&self, overscanCompensation: UIScreenOverscanCompensation);
|
|
||||||
|
|
||||||
pub fn coordinateSpace(&self) -> Id<UICoordinateSpace> {
|
|
||||||
unsafe { msg_send_id![self, coordinateSpace] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct UIScreenOverscanCompensation(NSInteger);
|
|
||||||
|
|
||||||
unsafe impl Encode for UIScreenOverscanCompensation {
|
|
||||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl UIScreenOverscanCompensation {
|
|
||||||
pub const Scale: Self = Self(0);
|
|
||||||
pub const InsetBounds: Self = Self(1);
|
|
||||||
pub const None: Self = Self(2);
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
|
||||||
use objc2_foundation::{CGSize, NSObject};
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIScreenMode;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIScreenMode {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIScreenMode {
|
|
||||||
#[method(size)]
|
|
||||||
pub fn size(&self) -> CGSize;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
use objc2::encode::{Encode, Encoding};
|
|
||||||
use objc2_foundation::NSInteger;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[repr(isize)]
|
|
||||||
pub enum UIStatusBarStyle {
|
|
||||||
#[default]
|
|
||||||
Default = 0,
|
|
||||||
LightContent = 1,
|
|
||||||
DarkContent = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Encode for UIStatusBarStyle {
|
|
||||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
use objc2::encode::{Encode, Encoding};
|
|
||||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
|
||||||
use objc2_foundation::{CGFloat, CGPoint, NSInteger, NSObject};
|
|
||||||
|
|
||||||
use super::UIView;
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UITouch;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UITouch {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UITouch {
|
|
||||||
#[method(locationInView:)]
|
|
||||||
pub fn locationInView(&self, view: Option<&UIView>) -> CGPoint;
|
|
||||||
|
|
||||||
#[method(type)]
|
|
||||||
pub fn type_(&self) -> UITouchType;
|
|
||||||
|
|
||||||
#[method(force)]
|
|
||||||
pub fn force(&self) -> CGFloat;
|
|
||||||
|
|
||||||
#[method(maximumPossibleForce)]
|
|
||||||
pub fn maximumPossibleForce(&self) -> CGFloat;
|
|
||||||
|
|
||||||
#[method(altitudeAngle)]
|
|
||||||
pub fn altitudeAngle(&self) -> CGFloat;
|
|
||||||
|
|
||||||
#[method(phase)]
|
|
||||||
pub fn phase(&self) -> UITouchPhase;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[repr(isize)]
|
|
||||||
pub enum UITouchType {
|
|
||||||
Direct = 0,
|
|
||||||
Indirect,
|
|
||||||
Pencil,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Encode for UITouchType {
|
|
||||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[repr(isize)]
|
|
||||||
pub enum UITouchPhase {
|
|
||||||
Began = 0,
|
|
||||||
Moved,
|
|
||||||
Stationary,
|
|
||||||
Ended,
|
|
||||||
Cancelled,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Encode for UITouchPhase {
|
|
||||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
use objc2::encode::{Encode, Encoding};
|
|
||||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
|
||||||
use objc2_foundation::{NSInteger, NSObject};
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UITraitCollection;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UITraitCollection {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UITraitCollection {
|
|
||||||
#[method(forceTouchCapability)]
|
|
||||||
pub fn forceTouchCapability(&self) -> UIForceTouchCapability;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[repr(isize)]
|
|
||||||
pub enum UIForceTouchCapability {
|
|
||||||
Unknown = 0,
|
|
||||||
Unavailable,
|
|
||||||
Available,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Encode for UIForceTouchCapability {
|
|
||||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
use objc2::encode::{Encode, Encoding};
|
|
||||||
use objc2::rc::Id;
|
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
|
||||||
use objc2_foundation::{CGFloat, CGRect, NSObject};
|
|
||||||
|
|
||||||
use super::{UICoordinateSpace, UIGestureRecognizer, UIResponder, UIViewController};
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIView;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIView {
|
|
||||||
#[inherits(NSObject)]
|
|
||||||
type Super = UIResponder;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIView {
|
|
||||||
#[method(bounds)]
|
|
||||||
pub fn bounds(&self) -> CGRect;
|
|
||||||
|
|
||||||
#[method(setBounds:)]
|
|
||||||
pub fn setBounds(&self, value: CGRect);
|
|
||||||
|
|
||||||
#[method(frame)]
|
|
||||||
pub fn frame(&self) -> CGRect;
|
|
||||||
|
|
||||||
#[method(setFrame:)]
|
|
||||||
pub fn setFrame(&self, value: CGRect);
|
|
||||||
|
|
||||||
#[method(contentScaleFactor)]
|
|
||||||
pub fn contentScaleFactor(&self) -> CGFloat;
|
|
||||||
|
|
||||||
#[method(setContentScaleFactor:)]
|
|
||||||
pub fn setContentScaleFactor(&self, val: CGFloat);
|
|
||||||
|
|
||||||
#[method(setMultipleTouchEnabled:)]
|
|
||||||
pub fn setMultipleTouchEnabled(&self, val: bool);
|
|
||||||
|
|
||||||
pub fn rootViewController(&self) -> Option<Id<UIViewController>> {
|
|
||||||
unsafe { msg_send_id![self, rootViewController] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(setRootViewController:)]
|
|
||||||
pub fn setRootViewController(&self, rootViewController: Option<&UIViewController>);
|
|
||||||
|
|
||||||
#[method(convertRect:toCoordinateSpace:)]
|
|
||||||
pub fn convertRect_toCoordinateSpace(
|
|
||||||
&self,
|
|
||||||
rect: CGRect,
|
|
||||||
coordinateSpace: &UICoordinateSpace,
|
|
||||||
) -> CGRect;
|
|
||||||
|
|
||||||
#[method(convertRect:fromCoordinateSpace:)]
|
|
||||||
pub fn convertRect_fromCoordinateSpace(
|
|
||||||
&self,
|
|
||||||
rect: CGRect,
|
|
||||||
coordinateSpace: &UICoordinateSpace,
|
|
||||||
) -> CGRect;
|
|
||||||
|
|
||||||
#[method(safeAreaInsets)]
|
|
||||||
pub fn safeAreaInsets(&self) -> UIEdgeInsets;
|
|
||||||
|
|
||||||
#[method(setNeedsDisplay)]
|
|
||||||
pub fn setNeedsDisplay(&self);
|
|
||||||
|
|
||||||
#[method(addGestureRecognizer:)]
|
|
||||||
pub fn addGestureRecognizer(&self, gestureRecognizer: &UIGestureRecognizer);
|
|
||||||
|
|
||||||
#[method(removeGestureRecognizer:)]
|
|
||||||
pub fn removeGestureRecognizer(&self, gestureRecognizer: &UIGestureRecognizer);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct UIEdgeInsets {
|
|
||||||
pub top: CGFloat,
|
|
||||||
pub left: CGFloat,
|
|
||||||
pub bottom: CGFloat,
|
|
||||||
pub right: CGFloat,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Encode for UIEdgeInsets {
|
|
||||||
const ENCODING: Encoding = Encoding::Struct("UIEdgeInsets", &[
|
|
||||||
CGFloat::ENCODING,
|
|
||||||
CGFloat::ENCODING,
|
|
||||||
CGFloat::ENCODING,
|
|
||||||
CGFloat::ENCODING,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
use objc2::encode::{Encode, Encoding};
|
|
||||||
use objc2::rc::Id;
|
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
|
||||||
use objc2_foundation::{NSObject, NSUInteger};
|
|
||||||
|
|
||||||
use super::{UIResponder, UIView};
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIViewController;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIViewController {
|
|
||||||
#[inherits(NSObject)]
|
|
||||||
type Super = UIResponder;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIViewController {
|
|
||||||
#[method(attemptRotationToDeviceOrientation)]
|
|
||||||
pub fn attemptRotationToDeviceOrientation();
|
|
||||||
|
|
||||||
#[method(setNeedsStatusBarAppearanceUpdate)]
|
|
||||||
pub fn setNeedsStatusBarAppearanceUpdate(&self);
|
|
||||||
|
|
||||||
#[method(setNeedsUpdateOfHomeIndicatorAutoHidden)]
|
|
||||||
pub fn setNeedsUpdateOfHomeIndicatorAutoHidden(&self);
|
|
||||||
|
|
||||||
#[method(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]
|
|
||||||
pub fn setNeedsUpdateOfScreenEdgesDeferringSystemGestures(&self);
|
|
||||||
|
|
||||||
pub fn view(&self) -> Option<Id<UIView>> {
|
|
||||||
unsafe { msg_send_id![self, view] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(setView:)]
|
|
||||||
pub fn setView(&self, view: Option<&UIView>);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
bitflags::bitflags! {
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct UIInterfaceOrientationMask: NSUInteger {
|
|
||||||
const Portrait = 1 << 1;
|
|
||||||
const PortraitUpsideDown = 1 << 2;
|
|
||||||
const LandscapeRight = 1 << 3;
|
|
||||||
const LandscapeLeft = 1 << 4;
|
|
||||||
const Landscape = Self::LandscapeLeft.bits() | Self::LandscapeRight.bits();
|
|
||||||
const AllButUpsideDown = Self::Landscape.bits() | Self::Portrait.bits();
|
|
||||||
const All = Self::AllButUpsideDown.bits() | Self::PortraitUpsideDown.bits();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Encode for UIInterfaceOrientationMask {
|
|
||||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use objc2::rc::Id;
|
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
|
||||||
use objc2_foundation::NSObject;
|
|
||||||
|
|
||||||
use super::{UIResponder, UIScreen, UIView};
|
|
||||||
|
|
||||||
extern_class!(
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct UIWindow;
|
|
||||||
|
|
||||||
unsafe impl ClassType for UIWindow {
|
|
||||||
#[inherits(UIResponder, NSObject)]
|
|
||||||
type Super = UIView;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
unsafe impl UIWindow {
|
|
||||||
pub fn screen(&self) -> Id<UIScreen> {
|
|
||||||
unsafe { msg_send_id![self, screen] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(setScreen:)]
|
|
||||||
pub fn setScreen(&self, screen: &UIScreen);
|
|
||||||
|
|
||||||
#[method(setHidden:)]
|
|
||||||
pub fn setHidden(&self, flag: bool);
|
|
||||||
|
|
||||||
#[method(makeKeyAndVisible)]
|
|
||||||
pub fn makeKeyAndVisible(&self);
|
|
||||||
|
|
||||||
#[method(isKeyWindow)]
|
|
||||||
pub fn isKeyWindow(&self) -> bool;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
#![allow(clippy::unnecessary_cast)]
|
#![allow(clippy::unnecessary_cast)]
|
||||||
use std::cell::RefCell;
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::AnyClass;
|
use objc2::runtime::{NSObjectProtocol, ProtocolObject};
|
||||||
use objc2::{
|
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||||
declare_class, extern_methods, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
|
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet};
|
||||||
|
use objc2_ui_kit::{
|
||||||
|
UICoordinateSpace, UIEvent, UIForceTouchCapability, UIGestureRecognizer,
|
||||||
|
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIPanGestureRecognizer,
|
||||||
|
UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer,
|
||||||
|
UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||||
};
|
};
|
||||||
use objc2_foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSSet};
|
|
||||||
|
|
||||||
use super::app_state::{self, EventWrapper};
|
use super::app_state::{self, EventWrapper};
|
||||||
use super::uikit::{
|
|
||||||
UIEvent, UIForceTouchCapability, UIGestureRecognizerState, UIPinchGestureRecognizer,
|
|
||||||
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITouch, UITouchPhase,
|
|
||||||
UITouchType, UITraitCollection, UIView,
|
|
||||||
};
|
|
||||||
use super::window::WinitUIWindow;
|
use super::window::WinitUIWindow;
|
||||||
use crate::dpi::PhysicalPosition;
|
use crate::dpi::PhysicalPosition;
|
||||||
use crate::event::{Event, Force, Touch, TouchPhase, WindowEvent};
|
use crate::event::{Event, Force, Touch, TouchPhase, WindowEvent};
|
||||||
@@ -21,9 +20,15 @@ use crate::platform_impl::platform::DEVICE_ID;
|
|||||||
use crate::window::{WindowAttributes, WindowId as RootWindowId};
|
use crate::window::{WindowAttributes, WindowId as RootWindowId};
|
||||||
|
|
||||||
pub struct WinitViewState {
|
pub struct WinitViewState {
|
||||||
pinch_gesture_recognizer: RefCell<Option<Id<UIPinchGestureRecognizer>>>,
|
pinch_gesture_recognizer: RefCell<Option<Retained<UIPinchGestureRecognizer>>>,
|
||||||
doubletap_gesture_recognizer: RefCell<Option<Id<UITapGestureRecognizer>>>,
|
doubletap_gesture_recognizer: RefCell<Option<Retained<UITapGestureRecognizer>>>,
|
||||||
rotation_gesture_recognizer: RefCell<Option<Id<UIRotationGestureRecognizer>>>,
|
rotation_gesture_recognizer: RefCell<Option<Retained<UIRotationGestureRecognizer>>>,
|
||||||
|
pan_gesture_recognizer: RefCell<Option<Retained<UIPanGestureRecognizer>>>,
|
||||||
|
|
||||||
|
// for iOS delta references the start of the Gesture
|
||||||
|
rotation_last_delta: Cell<CGFloat>,
|
||||||
|
pinch_last_delta: Cell<CGFloat>,
|
||||||
|
pan_last_delta: Cell<CGPoint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
@@ -32,7 +37,7 @@ declare_class!(
|
|||||||
unsafe impl ClassType for WinitView {
|
unsafe impl ClassType for WinitView {
|
||||||
#[inherits(UIResponder, NSObject)]
|
#[inherits(UIResponder, NSObject)]
|
||||||
type Super = UIView;
|
type Super = UIView;
|
||||||
type Mutability = mutability::InteriorMutable;
|
type Mutability = mutability::MainThreadOnly;
|
||||||
const NAME: &'static str = "WinitUIView";
|
const NAME: &'static str = "WinitUIView";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,12 +170,23 @@ declare_class!(
|
|||||||
fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) {
|
fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) {
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
|
|
||||||
let phase = match recognizer.state() {
|
let (phase, delta) = match recognizer.state() {
|
||||||
UIGestureRecognizerState::Began => TouchPhase::Started,
|
UIGestureRecognizerState::Began => {
|
||||||
UIGestureRecognizerState::Changed => TouchPhase::Moved,
|
self.ivars().pinch_last_delta.set(recognizer.scale());
|
||||||
UIGestureRecognizerState::Ended => TouchPhase::Ended,
|
(TouchPhase::Started, 0.0)
|
||||||
|
}
|
||||||
|
UIGestureRecognizerState::Changed => {
|
||||||
|
let last_scale: f64 = self.ivars().pinch_last_delta.replace(recognizer.scale());
|
||||||
|
(TouchPhase::Moved, recognizer.scale() - last_scale)
|
||||||
|
}
|
||||||
|
UIGestureRecognizerState::Ended => {
|
||||||
|
let last_scale: f64 = self.ivars().pinch_last_delta.replace(0.0);
|
||||||
|
(TouchPhase::Moved, recognizer.scale() - last_scale)
|
||||||
|
}
|
||||||
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
|
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
|
||||||
TouchPhase::Cancelled
|
self.ivars().rotation_last_delta.set(0.0);
|
||||||
|
// Pass -delta so that action is reversed
|
||||||
|
(TouchPhase::Cancelled, -recognizer.scale())
|
||||||
}
|
}
|
||||||
state => panic!("unexpected recognizer state: {:?}", state),
|
state => panic!("unexpected recognizer state: {:?}", state),
|
||||||
};
|
};
|
||||||
@@ -179,7 +195,7 @@ declare_class!(
|
|||||||
window_id: RootWindowId(window.id()),
|
window_id: RootWindowId(window.id()),
|
||||||
event: WindowEvent::PinchGesture {
|
event: WindowEvent::PinchGesture {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
delta: recognizer.velocity() as _,
|
delta: delta as f64,
|
||||||
phase,
|
phase,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -209,23 +225,88 @@ declare_class!(
|
|||||||
fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) {
|
fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) {
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
|
|
||||||
let phase = match recognizer.state() {
|
let (phase, delta) = match recognizer.state() {
|
||||||
UIGestureRecognizerState::Began => TouchPhase::Started,
|
UIGestureRecognizerState::Began => {
|
||||||
UIGestureRecognizerState::Changed => TouchPhase::Moved,
|
self.ivars().rotation_last_delta.set(0.0);
|
||||||
UIGestureRecognizerState::Ended => TouchPhase::Ended,
|
|
||||||
|
(TouchPhase::Started, 0.0)
|
||||||
|
}
|
||||||
|
UIGestureRecognizerState::Changed => {
|
||||||
|
let last_rotation = self.ivars().rotation_last_delta.replace(recognizer.rotation());
|
||||||
|
|
||||||
|
(TouchPhase::Moved, recognizer.rotation() - last_rotation)
|
||||||
|
}
|
||||||
|
UIGestureRecognizerState::Ended => {
|
||||||
|
let last_rotation = self.ivars().rotation_last_delta.replace(0.0);
|
||||||
|
|
||||||
|
(TouchPhase::Ended, recognizer.rotation() - last_rotation)
|
||||||
|
}
|
||||||
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
|
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
|
||||||
TouchPhase::Cancelled
|
self.ivars().rotation_last_delta.set(0.0);
|
||||||
|
|
||||||
|
// Pass -delta so that action is reversed
|
||||||
|
(TouchPhase::Cancelled, -recognizer.rotation())
|
||||||
}
|
}
|
||||||
state => panic!("unexpected recognizer state: {:?}", state),
|
state => panic!("unexpected recognizer state: {:?}", state),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flip the velocity to match macOS.
|
// Make delta negative to match macos, convert to degrees
|
||||||
let delta = -recognizer.velocity() as _;
|
|
||||||
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
|
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
window_id: RootWindowId(window.id()),
|
window_id: RootWindowId(window.id()),
|
||||||
event: WindowEvent::RotationGesture {
|
event: WindowEvent::RotationGesture {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
delta,
|
delta: -delta.to_degrees() as _,
|
||||||
|
phase,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
|
app_state::handle_nonuser_event(mtm, gesture_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[method(panGesture:)]
|
||||||
|
fn pan_gesture(&self, recognizer: &UIPanGestureRecognizer) {
|
||||||
|
let window = self.window().unwrap();
|
||||||
|
|
||||||
|
let translation = recognizer.translationInView(Some(self));
|
||||||
|
|
||||||
|
let (phase, dx, dy) = match recognizer.state() {
|
||||||
|
UIGestureRecognizerState::Began => {
|
||||||
|
self.ivars().pan_last_delta.set(translation);
|
||||||
|
|
||||||
|
(TouchPhase::Started, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
UIGestureRecognizerState::Changed => {
|
||||||
|
let last_pan: CGPoint = self.ivars().pan_last_delta.replace(translation);
|
||||||
|
|
||||||
|
let dx = translation.x - last_pan.x;
|
||||||
|
let dy = translation.y - last_pan.y;
|
||||||
|
|
||||||
|
(TouchPhase::Moved, dx, dy)
|
||||||
|
}
|
||||||
|
UIGestureRecognizerState::Ended => {
|
||||||
|
let last_pan: CGPoint = self.ivars().pan_last_delta.replace(CGPoint{x:0.0, y:0.0});
|
||||||
|
|
||||||
|
let dx = translation.x - last_pan.x;
|
||||||
|
let dy = translation.y - last_pan.y;
|
||||||
|
|
||||||
|
(TouchPhase::Ended, dx, dy)
|
||||||
|
}
|
||||||
|
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
|
||||||
|
let last_pan: CGPoint = self.ivars().pan_last_delta.replace(CGPoint{x:0.0, y:0.0});
|
||||||
|
|
||||||
|
// Pass -delta so that action is reversed
|
||||||
|
(TouchPhase::Cancelled, -last_pan.x, -last_pan.y)
|
||||||
|
}
|
||||||
|
state => panic!("unexpected recognizer state: {:?}", state),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
|
window_id: RootWindowId(window.id()),
|
||||||
|
event: WindowEvent::PanGesture {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
delta: PhysicalPosition::new(dx as _, dy as _),
|
||||||
phase,
|
phase,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -234,37 +315,34 @@ declare_class!(
|
|||||||
app_state::handle_nonuser_event(mtm, gesture_event);
|
app_state::handle_nonuser_event(mtm, gesture_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
unsafe impl NSObjectProtocol for WinitView {}
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe impl WinitView {
|
unsafe impl UIGestureRecognizerDelegate for WinitView {
|
||||||
fn window(&self) -> Option<Id<WinitUIWindow>> {
|
#[method(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
|
||||||
unsafe { msg_send_id![self, window] }
|
fn should_recognize_simultaneously(&self, _gesture_recognizer: &UIGestureRecognizer, _other_gesture_recognizer: &UIGestureRecognizer) -> bool {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn traitCollection(&self) -> Id<UITraitCollection> {
|
|
||||||
msg_send_id![self, traitCollection]
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Allow the user to customize this
|
|
||||||
#[method(layerClass)]
|
|
||||||
pub(crate) fn layerClass() -> &'static AnyClass;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
impl WinitView {
|
impl WinitView {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
_mtm: MainThreadMarker,
|
mtm: MainThreadMarker,
|
||||||
window_attributes: &WindowAttributes,
|
window_attributes: &WindowAttributes,
|
||||||
frame: CGRect,
|
frame: CGRect,
|
||||||
) -> Id<Self> {
|
) -> Retained<Self> {
|
||||||
let this = Self::alloc().set_ivars(WinitViewState {
|
let this = mtm.alloc().set_ivars(WinitViewState {
|
||||||
pinch_gesture_recognizer: RefCell::new(None),
|
pinch_gesture_recognizer: RefCell::new(None),
|
||||||
doubletap_gesture_recognizer: RefCell::new(None),
|
doubletap_gesture_recognizer: RefCell::new(None),
|
||||||
rotation_gesture_recognizer: RefCell::new(None),
|
rotation_gesture_recognizer: RefCell::new(None),
|
||||||
|
pan_gesture_recognizer: RefCell::new(None),
|
||||||
|
|
||||||
|
rotation_last_delta: Cell::new(0.0),
|
||||||
|
pinch_last_delta: Cell::new(0.0),
|
||||||
|
pan_last_delta: Cell::new(CGPoint { x: 0.0, y: 0.0 }),
|
||||||
});
|
});
|
||||||
let this: Id<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
|
let this: Retained<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
|
||||||
|
|
||||||
this.setMultipleTouchEnabled(true);
|
this.setMultipleTouchEnabled(true);
|
||||||
|
|
||||||
@@ -275,12 +353,23 @@ impl WinitView {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn window(&self) -> Option<Retained<WinitUIWindow>> {
|
||||||
|
// SAFETY: `WinitView`s are always installed in a `WinitUIWindow`
|
||||||
|
(**self).window().map(|window| unsafe { Retained::cast(window) })
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn recognize_pinch_gesture(&self, should_recognize: bool) {
|
pub(crate) fn recognize_pinch_gesture(&self, should_recognize: bool) {
|
||||||
|
let mtm = MainThreadMarker::from(self);
|
||||||
if should_recognize {
|
if should_recognize {
|
||||||
if self.ivars().pinch_gesture_recognizer.borrow().is_none() {
|
if self.ivars().pinch_gesture_recognizer.borrow().is_none() {
|
||||||
let pinch: Id<UIPinchGestureRecognizer> = unsafe {
|
let pinch = unsafe {
|
||||||
msg_send_id![UIPinchGestureRecognizer::alloc(), initWithTarget: self, action: sel!(pinchGesture:)]
|
UIPinchGestureRecognizer::initWithTarget_action(
|
||||||
|
mtm.alloc(),
|
||||||
|
Some(self),
|
||||||
|
Some(sel!(pinchGesture:)),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
pinch.setDelegate(Some(ProtocolObject::from_ref(self)));
|
||||||
self.addGestureRecognizer(&pinch);
|
self.addGestureRecognizer(&pinch);
|
||||||
self.ivars().pinch_gesture_recognizer.replace(Some(pinch));
|
self.ivars().pinch_gesture_recognizer.replace(Some(pinch));
|
||||||
}
|
}
|
||||||
@@ -289,12 +378,45 @@ impl WinitView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn recognize_pan_gesture(
|
||||||
|
&self,
|
||||||
|
should_recognize: bool,
|
||||||
|
minimum_number_of_touches: u8,
|
||||||
|
maximum_number_of_touches: u8,
|
||||||
|
) {
|
||||||
|
let mtm = MainThreadMarker::from(self);
|
||||||
|
if should_recognize {
|
||||||
|
if self.ivars().pan_gesture_recognizer.borrow().is_none() {
|
||||||
|
let pan = unsafe {
|
||||||
|
UIPanGestureRecognizer::initWithTarget_action(
|
||||||
|
mtm.alloc(),
|
||||||
|
Some(self),
|
||||||
|
Some(sel!(panGesture:)),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
pan.setDelegate(Some(ProtocolObject::from_ref(self)));
|
||||||
|
pan.setMinimumNumberOfTouches(minimum_number_of_touches as _);
|
||||||
|
pan.setMaximumNumberOfTouches(maximum_number_of_touches as _);
|
||||||
|
self.addGestureRecognizer(&pan);
|
||||||
|
self.ivars().pan_gesture_recognizer.replace(Some(pan));
|
||||||
|
}
|
||||||
|
} else if let Some(recognizer) = self.ivars().pan_gesture_recognizer.take() {
|
||||||
|
self.removeGestureRecognizer(&recognizer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn recognize_doubletap_gesture(&self, should_recognize: bool) {
|
pub(crate) fn recognize_doubletap_gesture(&self, should_recognize: bool) {
|
||||||
|
let mtm = MainThreadMarker::from(self);
|
||||||
if should_recognize {
|
if should_recognize {
|
||||||
if self.ivars().doubletap_gesture_recognizer.borrow().is_none() {
|
if self.ivars().doubletap_gesture_recognizer.borrow().is_none() {
|
||||||
let tap: Id<UITapGestureRecognizer> = unsafe {
|
let tap = unsafe {
|
||||||
msg_send_id![UITapGestureRecognizer::alloc(), initWithTarget: self, action: sel!(doubleTapGesture:)]
|
UITapGestureRecognizer::initWithTarget_action(
|
||||||
|
mtm.alloc(),
|
||||||
|
Some(self),
|
||||||
|
Some(sel!(doubleTapGesture:)),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
tap.setDelegate(Some(ProtocolObject::from_ref(self)));
|
||||||
tap.setNumberOfTapsRequired(2);
|
tap.setNumberOfTapsRequired(2);
|
||||||
tap.setNumberOfTouchesRequired(1);
|
tap.setNumberOfTouchesRequired(1);
|
||||||
self.addGestureRecognizer(&tap);
|
self.addGestureRecognizer(&tap);
|
||||||
@@ -306,11 +428,17 @@ impl WinitView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn recognize_rotation_gesture(&self, should_recognize: bool) {
|
pub(crate) fn recognize_rotation_gesture(&self, should_recognize: bool) {
|
||||||
|
let mtm = MainThreadMarker::from(self);
|
||||||
if should_recognize {
|
if should_recognize {
|
||||||
if self.ivars().rotation_gesture_recognizer.borrow().is_none() {
|
if self.ivars().rotation_gesture_recognizer.borrow().is_none() {
|
||||||
let rotation: Id<UIRotationGestureRecognizer> = unsafe {
|
let rotation = unsafe {
|
||||||
msg_send_id![UIRotationGestureRecognizer::alloc(), initWithTarget: self, action: sel!(rotationGesture:)]
|
UIRotationGestureRecognizer::initWithTarget_action(
|
||||||
|
mtm.alloc(),
|
||||||
|
Some(self),
|
||||||
|
Some(sel!(rotationGesture:)),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
rotation.setDelegate(Some(ProtocolObject::from_ref(self)));
|
||||||
self.addGestureRecognizer(&rotation);
|
self.addGestureRecognizer(&rotation);
|
||||||
self.ivars().rotation_gesture_recognizer.replace(Some(rotation));
|
self.ivars().rotation_gesture_recognizer.replace(Some(rotation));
|
||||||
}
|
}
|
||||||
@@ -325,9 +453,9 @@ impl WinitView {
|
|||||||
let os_supports_force = app_state::os_capabilities().force_touch;
|
let os_supports_force = app_state::os_capabilities().force_touch;
|
||||||
for touch in touches {
|
for touch in touches {
|
||||||
let logical_location = touch.locationInView(None);
|
let logical_location = touch.locationInView(None);
|
||||||
let touch_type = touch.type_();
|
let touch_type = touch.r#type();
|
||||||
let force = if os_supports_force {
|
let force = if os_supports_force {
|
||||||
let trait_collection = unsafe { self.traitCollection() };
|
let trait_collection = self.traitCollection();
|
||||||
let touch_capability = trait_collection.forceTouchCapability();
|
let touch_capability = trait_collection.forceTouchCapability();
|
||||||
// Both the OS _and_ the device need to be checked for force touch support.
|
// Both the OS _and_ the device need to be checked for force touch support.
|
||||||
if touch_capability == UIForceTouchCapability::Available
|
if touch_capability == UIForceTouchCapability::Available
|
||||||
@@ -360,7 +488,7 @@ impl WinitView {
|
|||||||
// 2 is UITouchPhase::Stationary and is not expected here
|
// 2 is UITouchPhase::Stationary and is not expected here
|
||||||
UITouchPhase::Ended => TouchPhase::Ended,
|
UITouchPhase::Ended => TouchPhase::Ended,
|
||||||
UITouchPhase::Cancelled => TouchPhase::Cancelled,
|
UITouchPhase::Cancelled => TouchPhase::Cancelled,
|
||||||
_ => panic!("unexpected touch phase: {:?}", phase as i32),
|
_ => panic!("unexpected touch phase: {phase:?}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let physical_location = {
|
let physical_location = {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||||
|
use objc2_ui_kit::{
|
||||||
use super::app_state::{self};
|
|
||||||
use super::uikit::{
|
|
||||||
UIDevice, UIInterfaceOrientationMask, UIRectEdge, UIResponder, UIStatusBarStyle,
|
UIDevice, UIInterfaceOrientationMask, UIRectEdge, UIResponder, UIStatusBarStyle,
|
||||||
UIUserInterfaceIdiom, UIView, UIViewController,
|
UIUserInterfaceIdiom, UIView, UIViewController,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::app_state::{self};
|
||||||
use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations};
|
use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations};
|
||||||
use crate::window::WindowAttributes;
|
use crate::window::WindowAttributes;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ declare_class!(
|
|||||||
unsafe impl ClassType for WinitViewController {
|
unsafe impl ClassType for WinitViewController {
|
||||||
#[inherits(UIResponder, NSObject)]
|
#[inherits(UIResponder, NSObject)]
|
||||||
type Super = UIViewController;
|
type Super = UIViewController;
|
||||||
type Mutability = mutability::InteriorMutable;
|
type Mutability = mutability::MainThreadOnly;
|
||||||
const NAME: &'static str = "WinitUIViewController";
|
const NAME: &'static str = "WinitUIViewController";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ impl WinitViewController {
|
|||||||
mtm: MainThreadMarker,
|
mtm: MainThreadMarker,
|
||||||
valid_orientations: ValidOrientations,
|
valid_orientations: ValidOrientations,
|
||||||
) {
|
) {
|
||||||
let mask = match (valid_orientations, UIDevice::current(mtm).userInterfaceIdiom()) {
|
let mask = match (valid_orientations, UIDevice::currentDevice(mtm).userInterfaceIdiom()) {
|
||||||
(ValidOrientations::LandscapeAndPortrait, UIUserInterfaceIdiom::Phone) => {
|
(ValidOrientations::LandscapeAndPortrait, UIUserInterfaceIdiom::Phone) => {
|
||||||
UIInterfaceOrientationMask::AllButUpsideDown
|
UIInterfaceOrientationMask::AllButUpsideDown
|
||||||
},
|
},
|
||||||
@@ -129,23 +129,24 @@ impl WinitViewController {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
self.ivars().supported_orientations.set(mask);
|
self.ivars().supported_orientations.set(mask);
|
||||||
UIViewController::attemptRotationToDeviceOrientation();
|
#[allow(deprecated)]
|
||||||
|
UIViewController::attemptRotationToDeviceOrientation(mtm);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
mtm: MainThreadMarker,
|
mtm: MainThreadMarker,
|
||||||
window_attributes: &WindowAttributes,
|
window_attributes: &WindowAttributes,
|
||||||
view: &UIView,
|
view: &UIView,
|
||||||
) -> Id<Self> {
|
) -> Retained<Self> {
|
||||||
// These are set properly below, we just to set them to something in the meantime.
|
// These are set properly below, we just to set them to something in the meantime.
|
||||||
let this = Self::alloc().set_ivars(ViewControllerState {
|
let this = mtm.alloc().set_ivars(ViewControllerState {
|
||||||
prefers_status_bar_hidden: Cell::new(false),
|
prefers_status_bar_hidden: Cell::new(false),
|
||||||
preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default),
|
preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default),
|
||||||
prefers_home_indicator_auto_hidden: Cell::new(false),
|
prefers_home_indicator_auto_hidden: Cell::new(false),
|
||||||
supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
|
supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
|
||||||
preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::NONE),
|
preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::empty()),
|
||||||
});
|
});
|
||||||
let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
|
let this: Retained<Self> = unsafe { msg_send_id![super(this), init] };
|
||||||
|
|
||||||
this.set_prefers_status_bar_hidden(
|
this.set_prefers_status_bar_hidden(
|
||||||
window_attributes.platform_specific.prefers_status_bar_hidden,
|
window_attributes.platform_specific.prefers_status_bar_hidden,
|
||||||
|
|||||||
@@ -2,16 +2,19 @@
|
|||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::{AnyObject, NSObject};
|
use objc2::runtime::{AnyObject, NSObject};
|
||||||
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass};
|
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||||
use objc2_foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
|
use objc2_foundation::{
|
||||||
|
CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker, NSObjectProtocol,
|
||||||
|
};
|
||||||
|
use objc2_ui_kit::{
|
||||||
|
UIApplication, UICoordinateSpace, UIResponder, UIScreen, UIScreenOverscanCompensation,
|
||||||
|
UIViewController, UIWindow,
|
||||||
|
};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use super::app_state::EventWrapper;
|
use super::app_state::EventWrapper;
|
||||||
use super::uikit::{
|
|
||||||
UIApplication, UIResponder, UIScreen, UIScreenOverscanCompensation, UIViewController, UIWindow,
|
|
||||||
};
|
|
||||||
use super::view::WinitView;
|
use super::view::WinitView;
|
||||||
use super::view_controller::WinitViewController;
|
use super::view_controller::WinitViewController;
|
||||||
use crate::cursor::Cursor;
|
use crate::cursor::Cursor;
|
||||||
@@ -35,7 +38,7 @@ declare_class!(
|
|||||||
unsafe impl ClassType for WinitUIWindow {
|
unsafe impl ClassType for WinitUIWindow {
|
||||||
#[inherits(UIResponder, NSObject)]
|
#[inherits(UIResponder, NSObject)]
|
||||||
type Super = UIWindow;
|
type Super = UIWindow;
|
||||||
type Mutability = mutability::InteriorMutable;
|
type Mutability = mutability::MainThreadOnly;
|
||||||
const NAME: &'static str = "WinitUIWindow";
|
const NAME: &'static str = "WinitUIWindow";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,8 +79,8 @@ impl WinitUIWindow {
|
|||||||
window_attributes: &WindowAttributes,
|
window_attributes: &WindowAttributes,
|
||||||
frame: CGRect,
|
frame: CGRect,
|
||||||
view_controller: &UIViewController,
|
view_controller: &UIViewController,
|
||||||
) -> Id<Self> {
|
) -> Retained<Self> {
|
||||||
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] };
|
let this: Retained<Self> = unsafe { msg_send_id![mtm.alloc(), initWithFrame: frame] };
|
||||||
|
|
||||||
this.setRootViewController(Some(view_controller));
|
this.setRootViewController(Some(view_controller));
|
||||||
|
|
||||||
@@ -104,9 +107,9 @@ impl WinitUIWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Inner {
|
pub struct Inner {
|
||||||
window: Id<WinitUIWindow>,
|
window: Retained<WinitUIWindow>,
|
||||||
view_controller: Id<WinitViewController>,
|
view_controller: Retained<WinitViewController>,
|
||||||
view: Id<WinitView>,
|
view: Retained<WinitView>,
|
||||||
gl_or_metal_backed: bool,
|
gl_or_metal_backed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,7 +397,8 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||||
Some(MonitorHandle::new(UIScreen::main(MainThreadMarker::new().unwrap())))
|
#[allow(deprecated)]
|
||||||
|
Some(MonitorHandle::new(UIScreen::mainScreen(MainThreadMarker::new().unwrap())))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> WindowId {
|
pub fn id(&self) -> WindowId {
|
||||||
@@ -404,18 +408,18 @@ impl Inner {
|
|||||||
#[cfg(feature = "rwh_04")]
|
#[cfg(feature = "rwh_04")]
|
||||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||||
let mut window_handle = rwh_04::UiKitHandle::empty();
|
let mut window_handle = rwh_04::UiKitHandle::empty();
|
||||||
window_handle.ui_window = Id::as_ptr(&self.window) as _;
|
window_handle.ui_window = Retained::as_ptr(&self.window) as _;
|
||||||
window_handle.ui_view = Id::as_ptr(&self.view) as _;
|
window_handle.ui_view = Retained::as_ptr(&self.view) as _;
|
||||||
window_handle.ui_view_controller = Id::as_ptr(&self.view_controller) as _;
|
window_handle.ui_view_controller = Retained::as_ptr(&self.view_controller) as _;
|
||||||
rwh_04::RawWindowHandle::UiKit(window_handle)
|
rwh_04::RawWindowHandle::UiKit(window_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rwh_05")]
|
#[cfg(feature = "rwh_05")]
|
||||||
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
||||||
let mut window_handle = rwh_05::UiKitWindowHandle::empty();
|
let mut window_handle = rwh_05::UiKitWindowHandle::empty();
|
||||||
window_handle.ui_window = Id::as_ptr(&self.window) as _;
|
window_handle.ui_window = Retained::as_ptr(&self.window) as _;
|
||||||
window_handle.ui_view = Id::as_ptr(&self.view) as _;
|
window_handle.ui_view = Retained::as_ptr(&self.view) as _;
|
||||||
window_handle.ui_view_controller = Id::as_ptr(&self.view_controller) as _;
|
window_handle.ui_view_controller = Retained::as_ptr(&self.view_controller) as _;
|
||||||
rwh_05::RawWindowHandle::UiKit(window_handle)
|
rwh_05::RawWindowHandle::UiKit(window_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,11 +431,11 @@ impl Inner {
|
|||||||
#[cfg(feature = "rwh_06")]
|
#[cfg(feature = "rwh_06")]
|
||||||
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
|
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
|
||||||
let mut window_handle = rwh_06::UiKitWindowHandle::new({
|
let mut window_handle = rwh_06::UiKitWindowHandle::new({
|
||||||
let ui_view = Id::as_ptr(&self.view) as _;
|
let ui_view = Retained::as_ptr(&self.view) as _;
|
||||||
std::ptr::NonNull::new(ui_view).expect("Id<T> should never be null")
|
std::ptr::NonNull::new(ui_view).expect("Retained<T> should never be null")
|
||||||
});
|
});
|
||||||
window_handle.ui_view_controller =
|
window_handle.ui_view_controller =
|
||||||
std::ptr::NonNull::new(Id::as_ptr(&self.view_controller) as _);
|
std::ptr::NonNull::new(Retained::as_ptr(&self.view_controller) as _);
|
||||||
rwh_06::RawWindowHandle::UiKit(window_handle)
|
rwh_06::RawWindowHandle::UiKit(window_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,7 +485,8 @@ impl Window {
|
|||||||
|
|
||||||
// TODO: transparency, visible
|
// TODO: transparency, visible
|
||||||
|
|
||||||
let main_screen = UIScreen::main(mtm);
|
#[allow(deprecated)]
|
||||||
|
let main_screen = UIScreen::mainScreen(mtm);
|
||||||
let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
|
let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
|
||||||
let screen = match fullscreen {
|
let screen = match fullscreen {
|
||||||
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm),
|
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm),
|
||||||
@@ -505,12 +510,8 @@ impl Window {
|
|||||||
|
|
||||||
let view = WinitView::new(mtm, &window_attributes, frame);
|
let view = WinitView::new(mtm, &window_attributes, frame);
|
||||||
|
|
||||||
let gl_or_metal_backed = unsafe {
|
let gl_or_metal_backed =
|
||||||
let layer_class = WinitView::layerClass();
|
view.isKindOfClass(class!(CAMetalLayer)) || view.isKindOfClass(class!(CAEAGLLayer));
|
||||||
let is_metal = msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
|
|
||||||
let is_gl = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
|
|
||||||
is_metal || is_gl
|
|
||||||
};
|
|
||||||
|
|
||||||
let view_controller = WinitViewController::new(mtm, &window_attributes, &view);
|
let view_controller = WinitViewController::new(mtm, &window_attributes, &view);
|
||||||
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
|
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
|
||||||
@@ -619,6 +620,19 @@ impl Inner {
|
|||||||
self.view.recognize_pinch_gesture(should_recognize);
|
self.view.recognize_pinch_gesture(should_recognize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn recognize_pan_gesture(
|
||||||
|
&self,
|
||||||
|
should_recognize: bool,
|
||||||
|
minimum_number_of_touches: u8,
|
||||||
|
maximum_number_of_touches: u8,
|
||||||
|
) {
|
||||||
|
self.view.recognize_pan_gesture(
|
||||||
|
should_recognize,
|
||||||
|
minimum_number_of_touches,
|
||||||
|
maximum_number_of_touches,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn recognize_doubletap_gesture(&self, should_recognize: bool) {
|
pub fn recognize_doubletap_gesture(&self, should_recognize: bool) {
|
||||||
self.view.recognize_doubletap_gesture(should_recognize);
|
self.view.recognize_doubletap_gesture(should_recognize);
|
||||||
}
|
}
|
||||||
@@ -661,10 +675,8 @@ impl Inner {
|
|||||||
} else {
|
} else {
|
||||||
let screen_frame = self.rect_to_screen_space(bounds);
|
let screen_frame = self.rect_to_screen_space(bounds);
|
||||||
let status_bar_frame = {
|
let status_bar_frame = {
|
||||||
let app = UIApplication::shared(MainThreadMarker::new().unwrap()).expect(
|
let app = UIApplication::sharedApplication(MainThreadMarker::new().unwrap());
|
||||||
"`Window::get_inner_position` cannot be called before `EventLoop::run_app` on \
|
#[allow(deprecated)]
|
||||||
iOS",
|
|
||||||
);
|
|
||||||
app.statusBarFrame()
|
app.statusBarFrame()
|
||||||
};
|
};
|
||||||
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
|
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
|
||||||
@@ -689,7 +701,7 @@ pub struct WindowId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WindowId {
|
impl WindowId {
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
WindowId { window: std::ptr::null_mut() }
|
WindowId { window: std::ptr::null_mut() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ impl From<u64> for WindowId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WindowId {
|
impl WindowId {
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
Self(0)
|
Self(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,11 +171,11 @@ pub enum DeviceId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceId {
|
impl DeviceId {
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(wayland_platform)]
|
||||||
return DeviceId::Wayland(unsafe { wayland::DeviceId::dummy() });
|
return DeviceId::Wayland(wayland::DeviceId::dummy());
|
||||||
#[cfg(all(not(wayland_platform), x11_platform))]
|
#[cfg(all(not(wayland_platform), x11_platform))]
|
||||||
return DeviceId::X(unsafe { x11::DeviceId::dummy() });
|
return DeviceId::X(x11::DeviceId::dummy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,6 +785,16 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
Ok(EventLoop::X(x11::EventLoop::new(xconn)))
|
Ok(EventLoop::X(x11::EventLoop::new(xconn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_wayland(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
#[cfg(wayland_platform)]
|
||||||
|
EventLoop::Wayland(_) => true,
|
||||||
|
#[cfg(x11_platform)]
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
|
x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
|
||||||
}
|
}
|
||||||
@@ -887,6 +897,11 @@ impl ActiveEventLoop {
|
|||||||
x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_rwh_05())
|
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")]
|
#[cfg(feature = "rwh_06")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn raw_display_handle_rwh_06(
|
pub fn raw_display_handle_rwh_06(
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(wayland_platform)]
|
|
||||||
|
|
||||||
//! Winit's Wayland backend.
|
//! Winit's Wayland backend.
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@@ -68,7 +66,7 @@ impl From<WaylandError> for OsError {
|
|||||||
pub struct DeviceId;
|
pub struct DeviceId;
|
||||||
|
|
||||||
impl DeviceId {
|
impl DeviceId {
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
DeviceId
|
DeviceId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ use crate::keyboard::ModifiersState;
|
|||||||
|
|
||||||
use crate::platform_impl::common::xkb::Context;
|
use crate::platform_impl::common::xkb::Context;
|
||||||
use crate::platform_impl::wayland::event_loop::sink::EventSink;
|
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::state::WinitState;
|
||||||
use crate::platform_impl::wayland::{self, DeviceId, WindowId};
|
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()) {
|
let seat_state = match state.seats.get_mut(&data.seat.id()) {
|
||||||
Some(seat_state) => seat_state,
|
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 {
|
match event {
|
||||||
@@ -43,7 +52,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
warn!("non-xkb compatible keymap")
|
warn!("non-xkb compatible keymap")
|
||||||
},
|
},
|
||||||
WlKeymapFormat::XkbV1 => {
|
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);
|
context.set_keymap_from_fd(fd, size as usize);
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -67,7 +76,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Drop the repeat, if there were any.
|
// Drop the repeat, if there were any.
|
||||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
|
||||||
keyboard_state.current_repeat = None;
|
keyboard_state.current_repeat = None;
|
||||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||||
keyboard_state.loop_handle.remove(token);
|
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
|
// NOTE: we should drop the repeat regardless whethere it was for the present
|
||||||
// window of for the window which just went gone.
|
// window of for the window which just went gone.
|
||||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
|
||||||
keyboard_state.current_repeat = None;
|
keyboard_state.current_repeat = None;
|
||||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||||
keyboard_state.loop_handle.remove(token);
|
keyboard_state.loop_handle.remove(token);
|
||||||
@@ -128,7 +135,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
let key = key + 8;
|
let key = key + 8;
|
||||||
|
|
||||||
key_input(
|
key_input(
|
||||||
seat_state,
|
keyboard_state,
|
||||||
&mut state.events_sink,
|
&mut state.events_sink,
|
||||||
data,
|
data,
|
||||||
key,
|
key,
|
||||||
@@ -136,7 +143,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
|
||||||
let delay = match keyboard_state.repeat_info {
|
let delay = match keyboard_state.repeat_info {
|
||||||
RepeatInfo::Repeat { delay, .. } => delay,
|
RepeatInfo::Repeat { delay, .. } => delay,
|
||||||
RepeatInfo::Disable => return,
|
RepeatInfo::Disable => return,
|
||||||
@@ -163,18 +169,25 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
state.dispatched_events = true;
|
state.dispatched_events = true;
|
||||||
|
|
||||||
let data = wl_keyboard.data::<KeyboardData>().unwrap();
|
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
|
let keyboard_state = match seat_state.keyboard_state.as_mut() {
|
||||||
// `None` is instant.
|
Some(keyboard_state) => keyboard_state,
|
||||||
let repeat_keycode =
|
None => return TimeoutAction::Drop,
|
||||||
match seat_state.keyboard_state.as_ref().unwrap().current_repeat {
|
};
|
||||||
Some(repeat_keycode) => repeat_keycode,
|
|
||||||
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(
|
key_input(
|
||||||
seat_state,
|
keyboard_state,
|
||||||
&mut state.events_sink,
|
&mut state.events_sink,
|
||||||
data,
|
data,
|
||||||
repeat_keycode,
|
repeat_keycode,
|
||||||
@@ -183,7 +196,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// NOTE: the gap could change dynamically while repeat is going.
|
// 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::Repeat { gap, .. } => TimeoutAction::ToDuration(gap),
|
||||||
RepeatInfo::Disable => TimeoutAction::Drop,
|
RepeatInfo::Disable => TimeoutAction::Drop,
|
||||||
}
|
}
|
||||||
@@ -194,7 +207,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
let key = key + 8;
|
let key = key + 8;
|
||||||
|
|
||||||
key_input(
|
key_input(
|
||||||
seat_state,
|
keyboard_state,
|
||||||
&mut state.events_sink,
|
&mut state.events_sink,
|
||||||
data,
|
data,
|
||||||
key,
|
key,
|
||||||
@@ -202,7 +215,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
|
||||||
if keyboard_state.repeat_info != RepeatInfo::Disable
|
if keyboard_state.repeat_info != RepeatInfo::Disable
|
||||||
&& keyboard_state.xkb_context.keymap_mut().unwrap().key_repeats(key)
|
&& keyboard_state.xkb_context.keymap_mut().unwrap().key_repeats(key)
|
||||||
&& Some(key) == keyboard_state.current_repeat
|
&& Some(key) == keyboard_state.current_repeat
|
||||||
@@ -216,7 +228,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
WlKeyboardEvent::Modifiers {
|
WlKeyboardEvent::Modifiers {
|
||||||
mods_depressed, mods_latched, mods_locked, group, ..
|
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() {
|
let xkb_state = match xkb_context.state_mut() {
|
||||||
Some(state) => state,
|
Some(state) => state,
|
||||||
None => return,
|
None => return,
|
||||||
@@ -240,7 +252,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
WlKeyboardEvent::RepeatInfo { rate, delay } => {
|
WlKeyboardEvent::RepeatInfo { rate, delay } => {
|
||||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
|
||||||
keyboard_state.repeat_info = if rate == 0 {
|
keyboard_state.repeat_info = if rate == 0 {
|
||||||
// Stop the repeat once we get a disable event.
|
// Stop the repeat once we get a disable event.
|
||||||
keyboard_state.current_repeat = None;
|
keyboard_state.current_repeat = None;
|
||||||
@@ -348,7 +359,7 @@ impl KeyboardData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn key_input(
|
fn key_input(
|
||||||
seat_state: &mut WinitSeatState,
|
keyboard_state: &mut KeyboardState,
|
||||||
event_sink: &mut EventSink,
|
event_sink: &mut EventSink,
|
||||||
data: &KeyboardData,
|
data: &KeyboardData,
|
||||||
keycode: u32,
|
keycode: u32,
|
||||||
@@ -360,8 +371,6 @@ fn key_input(
|
|||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
|
||||||
|
|
||||||
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
|
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
|
||||||
if let Some(mut key_context) = keyboard_state.xkb_context.key_context() {
|
if let Some(mut key_context) = keyboard_state.xkb_context.key_context() {
|
||||||
let event = key_context.process_key_event(keycode, state, repeat);
|
let event = key_context.process_key_event(keycode, state, repeat);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use sctk::reexports::client::backend::ObjectId;
|
use sctk::reexports::client::backend::ObjectId;
|
||||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||||
@@ -76,7 +77,13 @@ impl SeatHandler for WinitState {
|
|||||||
seat: WlSeat,
|
seat: WlSeat,
|
||||||
capability: SeatCapability,
|
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 {
|
match capability {
|
||||||
SeatCapability::Touch if seat_state.touch.is_none() => {
|
SeatCapability::Touch if seat_state.touch.is_none() => {
|
||||||
@@ -139,7 +146,13 @@ impl SeatHandler for WinitState {
|
|||||||
seat: WlSeat,
|
seat: WlSeat,
|
||||||
capability: SeatCapability,
|
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() {
|
if let Some(text_input) = seat_state.text_input.take() {
|
||||||
text_input.destroy();
|
text_input.destroy();
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ use std::ops::Deref;
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use sctk::reexports::client::delegate_dispatch;
|
use sctk::reexports::client::delegate_dispatch;
|
||||||
use sctk::reexports::client::protocol::wl_pointer::WlPointer;
|
use sctk::reexports::client::protocol::wl_pointer::WlPointer;
|
||||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||||
@@ -41,7 +43,21 @@ impl PointerHandler for WinitState {
|
|||||||
events: &[PointerEvent],
|
events: &[PointerEvent],
|
||||||
) {
|
) {
|
||||||
let seat = pointer.winit_data().seat();
|
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));
|
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.0,
|
||||||
event.position.1,
|
event.position.1,
|
||||||
) {
|
) {
|
||||||
if let Some(pointer) = seat_state.pointer.as_ref() {
|
let _ = themed_pointer.set_cursor(connection, icon);
|
||||||
let _ = pointer.set_cursor(connection, icon);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PointerEventKind::Leave { .. } if parent_surface != surface => {
|
PointerEventKind::Leave { .. } if parent_surface != surface => {
|
||||||
@@ -113,9 +127,7 @@ impl PointerHandler for WinitState {
|
|||||||
self.events_sink
|
self.events_sink
|
||||||
.push_window_event(WindowEvent::CursorEntered { device_id }, window_id);
|
.push_window_event(WindowEvent::CursorEntered { device_id }, window_id);
|
||||||
|
|
||||||
if let Some(pointer) = seat_state.pointer.as_ref().map(Arc::downgrade) {
|
window.pointer_entered(Arc::downgrade(themed_pointer));
|
||||||
window.pointer_entered(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the currently focused surface.
|
// Set the currently focused surface.
|
||||||
pointer.winit_data().inner.lock().unwrap().surface = Some(window_id);
|
pointer.winit_data().inner.lock().unwrap().surface = Some(window_id);
|
||||||
@@ -126,9 +138,7 @@ impl PointerHandler for WinitState {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
PointerEventKind::Leave { .. } => {
|
PointerEventKind::Leave { .. } => {
|
||||||
if let Some(pointer) = seat_state.pointer.as_ref().map(Arc::downgrade) {
|
window.pointer_left(Arc::downgrade(themed_pointer));
|
||||||
window.pointer_left(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the active surface.
|
// Remove the active surface.
|
||||||
pointer.winit_data().inner.lock().unwrap().surface = None;
|
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
|
// Mice events have both pixel and discrete delta's at the same time. So prefer
|
||||||
// the descrite values if they are present.
|
// the descrite values if they are present.
|
||||||
let delta = if has_discrete_scroll {
|
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(
|
MouseScrollDelta::LineDelta(
|
||||||
(-horizontal.discrete) as f32,
|
(-horizontal.discrete) as f32,
|
||||||
(-vertical.discrete) as f32,
|
(-vertical.discrete) as f32,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// XXX Wayland sign convention is the inverse of winit.
|
// NOTE: Wayland sign convention is the inverse of winit.
|
||||||
MouseScrollDelta::PixelDelta(
|
MouseScrollDelta::PixelDelta(
|
||||||
LogicalPosition::new(-horizontal.absolute, -vertical.absolute)
|
LogicalPosition::new(-horizontal.absolute, -vertical.absolute)
|
||||||
.to_physical(scale_factor),
|
.to_physical(scale_factor),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
//! Touch handling.
|
//! Touch handling.
|
||||||
|
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
||||||
@@ -31,11 +33,16 @@ impl TouchHandler for WinitState {
|
|||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let location = LogicalPosition::<f64>::from(position);
|
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
|
||||||
|
Some(seat_state) => seat_state,
|
||||||
let seat_state = self.seats.get_mut(&touch.seat().id()).unwrap();
|
None => {
|
||||||
|
warn!("Received wl_touch::down without seat");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// Update the state of the point.
|
// Update the state of the point.
|
||||||
|
let location = LogicalPosition::<f64>::from(position);
|
||||||
seat_state.touch_map.insert(id, TouchPoint { surface, location });
|
seat_state.touch_map.insert(id, TouchPoint { surface, location });
|
||||||
|
|
||||||
self.events_sink.push_window_event(
|
self.events_sink.push_window_event(
|
||||||
@@ -61,7 +68,13 @@ impl TouchHandler for WinitState {
|
|||||||
_: u32,
|
_: u32,
|
||||||
id: i32,
|
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.
|
// Remove the touch point.
|
||||||
let touch_point = match seat_state.touch_map.remove(&id) {
|
let touch_point = match seat_state.touch_map.remove(&id) {
|
||||||
@@ -98,7 +111,13 @@ impl TouchHandler for WinitState {
|
|||||||
id: i32,
|
id: i32,
|
||||||
position: (f64, f64),
|
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.
|
// Remove the touch point.
|
||||||
let touch_point = match seat_state.touch_map.get_mut(&id) {
|
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) {
|
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() {
|
for (id, touch_point) in seat_state.touch_map.drain() {
|
||||||
let window_id = wayland::make_wid(&touch_point.surface);
|
let window_id = wayland::make_wid(&touch_point.surface);
|
||||||
|
|||||||
@@ -339,12 +339,30 @@ impl CompositorHandler for WinitState {
|
|||||||
&mut self,
|
&mut self,
|
||||||
_: &Connection,
|
_: &Connection,
|
||||||
_: &QueueHandle<Self>,
|
_: &QueueHandle<Self>,
|
||||||
_: &wayland_client::protocol::wl_surface::WlSurface,
|
_: &WlSurface,
|
||||||
_: wayland_client::protocol::wl_output::Transform,
|
_: wayland_client::protocol::wl_output::Transform,
|
||||||
) {
|
) {
|
||||||
// TODO(kchibisov) we need to expose it somehow in winit.
|
// 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(
|
fn scale_factor_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &Connection,
|
_: &Connection,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ pub struct Window {
|
|||||||
/// Source to wake-up the event-loop for window requests.
|
/// Source to wake-up the event-loop for window requests.
|
||||||
event_loop_awakener: calloop::ping::Ping,
|
event_loop_awakener: calloop::ping::Ping,
|
||||||
|
|
||||||
/// The event sink to deliver sythetic events.
|
/// The event sink to deliver synthetic events.
|
||||||
window_events_sink: Arc<Mutex<EventSink>>,
|
window_events_sink: Arc<Mutex<EventSink>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -434,7 +434,7 @@ impl EventProcessor {
|
|||||||
let flags = xev.data.get_long(1);
|
let flags = xev.data.get_long(1);
|
||||||
let version = flags >> 24;
|
let version = flags >> 24;
|
||||||
self.dnd.version = Some(version);
|
self.dnd.version = Some(version);
|
||||||
let has_more_types = flags - (flags & (c_long::max_value() - 1)) == 1;
|
let has_more_types = flags - (flags & (c_long::MAX - 1)) == 1;
|
||||||
if !has_more_types {
|
if !has_more_types {
|
||||||
let type_list = vec![
|
let type_list = vec![
|
||||||
xev.data.get_long(2) as xproto::Atom,
|
xev.data.get_long(2) as xproto::Atom,
|
||||||
@@ -1424,7 +1424,7 @@ impl EventProcessor {
|
|||||||
if !xinput2::XIMaskIsSet(mask, i) {
|
if !xinput2::XIMaskIsSet(mask, i) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let x = unsafe { *value };
|
let x = unsafe { value.read_unaligned() };
|
||||||
|
|
||||||
// We assume that every XInput2 device with analog axes is a pointing device emitting
|
// We assume that every XInput2 device with analog axes is a pointing device emitting
|
||||||
// relative coordinates.
|
// relative coordinates.
|
||||||
@@ -1778,7 +1778,7 @@ impl EventProcessor {
|
|||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send the keys using the sythetic state to not alter the main state.
|
// Send the keys using the synthetic state to not alter the main state.
|
||||||
let mut xkb_state = match XkbState::new_x11(xcb, keymap) {
|
let mut xkb_state = match XkbState::new_x11(xcb, keymap) {
|
||||||
Some(xkb_state) => xkb_state,
|
Some(xkb_state) => xkb_state,
|
||||||
None => return,
|
None => return,
|
||||||
|
|||||||
@@ -158,7 +158,9 @@ struct PreeditCallbacks {
|
|||||||
impl PreeditCallbacks {
|
impl PreeditCallbacks {
|
||||||
pub fn new(client_data: ffi::XPointer) -> PreeditCallbacks {
|
pub fn new(client_data: ffi::XPointer) -> PreeditCallbacks {
|
||||||
let start_callback = create_xim_callback(client_data, unsafe {
|
let start_callback = create_xim_callback(client_data, unsafe {
|
||||||
mem::transmute(preedit_start_callback as usize)
|
mem::transmute::<usize, unsafe extern "C" fn(ffi::XIM, ffi::XPointer, ffi::XPointer)>(
|
||||||
|
preedit_start_callback as usize,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
let done_callback = create_xim_callback(client_data, preedit_done_callback);
|
let done_callback = create_xim_callback(client_data, preedit_done_callback);
|
||||||
let caret_callback = create_xim_callback(client_data, preedit_caret_callback);
|
let caret_callback = create_xim_callback(client_data, preedit_caret_callback);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(x11_platform)]
|
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
@@ -773,7 +771,7 @@ pub struct DeviceId(xinput::DeviceId);
|
|||||||
|
|
||||||
impl DeviceId {
|
impl DeviceId {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
DeviceId(0)
|
DeviceId(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ impl XConnection {
|
|||||||
|
|
||||||
// Position relative to root window.
|
// Position relative to root window.
|
||||||
// With rare exceptions, this is the position of a nested window. Cases where the window
|
// With rare exceptions, this is the position of a nested window. Cases where the window
|
||||||
// isn't nested are outlined in the comments throghout this function, but in addition to
|
// isn't nested are outlined in the comments throughout this function, but in addition to
|
||||||
// that, fullscreen windows often aren't nested.
|
// that, fullscreen windows often aren't nested.
|
||||||
let (inner_y_rel_root, child) = {
|
let (inner_y_rel_root, child) = {
|
||||||
let coords = self
|
let coords = self
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
|
|||||||
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
||||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||||
|
|
||||||
use super::app_delegate::ApplicationDelegate;
|
use super::app_state::ApplicationDelegate;
|
||||||
use super::event::flags_contains;
|
|
||||||
use crate::event::{DeviceEvent, ElementState};
|
use crate::event::{DeviceEvent, ElementState};
|
||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
@@ -32,7 +31,7 @@ declare_class!(
|
|||||||
let event_type = unsafe { event.r#type() };
|
let event_type = unsafe { event.r#type() };
|
||||||
let modifier_flags = unsafe { event.modifierFlags() };
|
let modifier_flags = unsafe { event.modifierFlags() };
|
||||||
if event_type == NSEventType::KeyUp
|
if event_type == NSEventType::KeyUp
|
||||||
&& flags_contains(modifier_flags, NSEventModifierFlags::NSEventModifierFlagCommand)
|
&& modifier_flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand)
|
||||||
{
|
{
|
||||||
if let Some(key_window) = self.keyWindow() {
|
if let Some(key_window) = self.keyWindow() {
|
||||||
key_window.sendEvent(event);
|
key_window.sendEvent(event);
|
||||||
@@ -58,25 +57,27 @@ fn maybe_dispatch_device_event(delegate: &ApplicationDelegate, event: &NSEvent)
|
|||||||
let delta_y = unsafe { event.deltaY() } as f64;
|
let delta_y = unsafe { event.deltaY() } as f64;
|
||||||
|
|
||||||
if delta_x != 0.0 {
|
if delta_x != 0.0 {
|
||||||
delegate.queue_device_event(DeviceEvent::Motion { axis: 0, value: delta_x });
|
delegate.maybe_queue_device_event(DeviceEvent::Motion { axis: 0, value: delta_x });
|
||||||
}
|
}
|
||||||
|
|
||||||
if delta_y != 0.0 {
|
if delta_y != 0.0 {
|
||||||
delegate.queue_device_event(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 {
|
if delta_x != 0.0 || delta_y != 0.0 {
|
||||||
delegate.queue_device_event(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 => {
|
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
|
||||||
delegate.queue_device_event(DeviceEvent::Button {
|
delegate.maybe_queue_device_event(DeviceEvent::Button {
|
||||||
button: unsafe { event.buttonNumber() } as u32,
|
button: unsafe { event.buttonNumber() } as u32,
|
||||||
state: ElementState::Pressed,
|
state: ElementState::Pressed,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
||||||
delegate.queue_device_event(DeviceEvent::Button {
|
delegate.maybe_queue_device_event(DeviceEvent::Button {
|
||||||
button: unsafe { event.buttonNumber() } as u32,
|
button: unsafe { event.buttonNumber() } as u32,
|
||||||
state: ElementState::Released,
|
state: ElementState::Released,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,40 +1,27 @@
|
|||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Weak;
|
use std::rc::Weak;
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::AnyObject;
|
|
||||||
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
|
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
|
||||||
use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol, NSSize};
|
use objc2_foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol};
|
||||||
|
|
||||||
use super::event_handler::EventHandler;
|
use super::event_handler::EventHandler;
|
||||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
||||||
use super::observer::{EventLoopWaker, RunLoop};
|
use super::observer::{EventLoopWaker, RunLoop};
|
||||||
use super::window::WinitWindow;
|
|
||||||
use super::{menu, WindowId, DEVICE_ID};
|
use super::{menu, WindowId, DEVICE_ID};
|
||||||
use crate::dpi::PhysicalSize;
|
use crate::event::{DeviceEvent, Event, StartCause, WindowEvent};
|
||||||
use crate::event::{DeviceEvent, Event, InnerSizeWriter, StartCause, WindowEvent};
|
|
||||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
||||||
use crate::window::WindowId as RootWindowId;
|
use crate::window::WindowId as RootWindowId;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Policy(NSApplicationActivationPolicy);
|
pub(super) struct AppState {
|
||||||
|
activation_policy: NSApplicationActivationPolicy,
|
||||||
impl Default for Policy {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(NSApplicationActivationPolicy::Regular)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub(super) struct State {
|
|
||||||
activation_policy: Policy,
|
|
||||||
default_menu: bool,
|
default_menu: bool,
|
||||||
activate_ignoring_other_apps: bool,
|
activate_ignoring_other_apps: bool,
|
||||||
|
run_loop: RunLoop,
|
||||||
event_handler: EventHandler,
|
event_handler: EventHandler,
|
||||||
stop_on_launch: Cell<bool>,
|
stop_on_launch: Cell<bool>,
|
||||||
stop_before_wait: Cell<bool>,
|
stop_before_wait: Cell<bool>,
|
||||||
@@ -50,8 +37,9 @@ pub(super) struct State {
|
|||||||
waker: RefCell<EventLoopWaker>,
|
waker: RefCell<EventLoopWaker>,
|
||||||
start_time: Cell<Option<Instant>>,
|
start_time: Cell<Option<Instant>>,
|
||||||
wait_timeout: Cell<Option<Instant>>,
|
wait_timeout: Cell<Option<Instant>>,
|
||||||
pending_events: RefCell<VecDeque<QueuedEvent>>,
|
|
||||||
pending_redraw: RefCell<Vec<WindowId>>,
|
pending_redraw: RefCell<Vec<WindowId>>,
|
||||||
|
// NOTE: This is strongly referenced by our `NSWindowDelegate` and our `NSView` subclass, and
|
||||||
|
// as such should be careful to not add fields that, in turn, strongly reference those.
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
@@ -65,62 +53,20 @@ declare_class!(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DeclaredClass for ApplicationDelegate {
|
impl DeclaredClass for ApplicationDelegate {
|
||||||
type Ivars = State;
|
type Ivars = AppState;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl NSObjectProtocol for ApplicationDelegate {}
|
unsafe impl NSObjectProtocol for ApplicationDelegate {}
|
||||||
|
|
||||||
unsafe impl NSApplicationDelegate for ApplicationDelegate {
|
unsafe impl NSApplicationDelegate for ApplicationDelegate {
|
||||||
// Note: This will, globally, only be run once, no matter how many
|
|
||||||
// `EventLoop`s the user creates.
|
|
||||||
#[method(applicationDidFinishLaunching:)]
|
#[method(applicationDidFinishLaunching:)]
|
||||||
fn did_finish_launching(&self, _sender: Option<&AnyObject>) {
|
fn app_did_finish_launching(&self, notification: &NSNotification) {
|
||||||
trace_scope!("applicationDidFinishLaunching:");
|
self.did_finish_launching(notification)
|
||||||
self.ivars().is_launched.set(true);
|
|
||||||
|
|
||||||
let mtm = MainThreadMarker::from(self);
|
|
||||||
let app = NSApplication::sharedApplication(mtm);
|
|
||||||
// We need to delay setting the activation policy and activating the app
|
|
||||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
|
||||||
// menu bar is initially unresponsive on macOS 10.15.
|
|
||||||
app.setActivationPolicy(self.ivars().activation_policy.0);
|
|
||||||
|
|
||||||
window_activation_hack(&app);
|
|
||||||
#[allow(deprecated)]
|
|
||||||
app.activateIgnoringOtherApps(self.ivars().activate_ignoring_other_apps);
|
|
||||||
|
|
||||||
if self.ivars().default_menu {
|
|
||||||
// The menubar initialization should be before the `NewEvents` event, to allow
|
|
||||||
// overriding of the default menu even if it's created
|
|
||||||
menu::initialize(&app);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ivars().waker.borrow_mut().start();
|
|
||||||
|
|
||||||
self.set_is_running(true);
|
|
||||||
self.dispatch_init_events();
|
|
||||||
|
|
||||||
// If the application is being launched via `EventLoop::pump_app_events()` then we'll
|
|
||||||
// want to stop the app once it is launched (and return to the external loop)
|
|
||||||
//
|
|
||||||
// In this case we still want to consider Winit's `EventLoop` to be "running",
|
|
||||||
// so we call `start_running()` above.
|
|
||||||
if self.ivars().stop_on_launch.get() {
|
|
||||||
// Note: the original idea had been to only stop the underlying `RunLoop`
|
|
||||||
// for the app but that didn't work as expected (`-[NSApplication run]`
|
|
||||||
// effectively ignored the attempt to stop the RunLoop and re-started it).
|
|
||||||
//
|
|
||||||
// So we return from `pump_events` by stopping the application.
|
|
||||||
let app = NSApplication::sharedApplication(mtm);
|
|
||||||
stop_app_immediately(&app);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(applicationWillTerminate:)]
|
#[method(applicationWillTerminate:)]
|
||||||
fn will_terminate(&self, _sender: Option<&AnyObject>) {
|
fn app_will_terminate(&self, notification: &NSNotification) {
|
||||||
trace_scope!("applicationWillTerminate:");
|
self.will_terminate(notification)
|
||||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
|
||||||
self.internal_exit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -131,23 +77,86 @@ impl ApplicationDelegate {
|
|||||||
activation_policy: NSApplicationActivationPolicy,
|
activation_policy: NSApplicationActivationPolicy,
|
||||||
default_menu: bool,
|
default_menu: bool,
|
||||||
activate_ignoring_other_apps: bool,
|
activate_ignoring_other_apps: bool,
|
||||||
) -> Id<Self> {
|
) -> Retained<Self> {
|
||||||
let this = mtm.alloc().set_ivars(State {
|
let this = mtm.alloc().set_ivars(AppState {
|
||||||
activation_policy: Policy(activation_policy),
|
activation_policy,
|
||||||
default_menu,
|
default_menu,
|
||||||
activate_ignoring_other_apps,
|
activate_ignoring_other_apps,
|
||||||
..Default::default()
|
run_loop: RunLoop::main(mtm),
|
||||||
|
event_handler: EventHandler::new(),
|
||||||
|
stop_on_launch: Cell::new(false),
|
||||||
|
stop_before_wait: Cell::new(false),
|
||||||
|
stop_after_wait: Cell::new(false),
|
||||||
|
stop_on_redraw: Cell::new(false),
|
||||||
|
is_launched: Cell::new(false),
|
||||||
|
is_running: Cell::new(false),
|
||||||
|
exit: Cell::new(false),
|
||||||
|
control_flow: Cell::new(ControlFlow::default()),
|
||||||
|
waker: RefCell::new(EventLoopWaker::new()),
|
||||||
|
start_time: Cell::new(None),
|
||||||
|
wait_timeout: Cell::new(None),
|
||||||
|
pending_redraw: RefCell::new(vec![]),
|
||||||
});
|
});
|
||||||
unsafe { msg_send_id![super(this), init] }
|
unsafe { msg_send_id![super(this), init] }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(mtm: MainThreadMarker) -> Id<Self> {
|
// NOTE: This will, globally, only be run once, no matter how many
|
||||||
|
// `EventLoop`s the user creates.
|
||||||
|
fn did_finish_launching(&self, _notification: &NSNotification) {
|
||||||
|
trace_scope!("applicationDidFinishLaunching:");
|
||||||
|
self.ivars().is_launched.set(true);
|
||||||
|
|
||||||
|
let mtm = MainThreadMarker::from(self);
|
||||||
|
let app = NSApplication::sharedApplication(mtm);
|
||||||
|
// We need to delay setting the activation policy and activating the app
|
||||||
|
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||||
|
// menu bar is initially unresponsive on macOS 10.15.
|
||||||
|
app.setActivationPolicy(self.ivars().activation_policy);
|
||||||
|
|
||||||
|
window_activation_hack(&app);
|
||||||
|
#[allow(deprecated)]
|
||||||
|
app.activateIgnoringOtherApps(self.ivars().activate_ignoring_other_apps);
|
||||||
|
|
||||||
|
if self.ivars().default_menu {
|
||||||
|
// The menubar initialization should be before the `NewEvents` event, to allow
|
||||||
|
// overriding of the default menu even if it's created
|
||||||
|
menu::initialize(&app);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ivars().waker.borrow_mut().start();
|
||||||
|
|
||||||
|
self.set_is_running(true);
|
||||||
|
self.dispatch_init_events();
|
||||||
|
|
||||||
|
// If the application is being launched via `EventLoop::pump_app_events()` then we'll
|
||||||
|
// want to stop the app once it is launched (and return to the external loop)
|
||||||
|
//
|
||||||
|
// In this case we still want to consider Winit's `EventLoop` to be "running",
|
||||||
|
// so we call `start_running()` above.
|
||||||
|
if self.ivars().stop_on_launch.get() {
|
||||||
|
// NOTE: the original idea had been to only stop the underlying `RunLoop`
|
||||||
|
// for the app but that didn't work as expected (`-[NSApplication run]`
|
||||||
|
// effectively ignored the attempt to stop the RunLoop and re-started it).
|
||||||
|
//
|
||||||
|
// So we return from `pump_events` by stopping the application.
|
||||||
|
let app = NSApplication::sharedApplication(mtm);
|
||||||
|
stop_app_immediately(&app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn will_terminate(&self, _notification: &NSNotification) {
|
||||||
|
trace_scope!("applicationWillTerminate:");
|
||||||
|
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||||
|
self.internal_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(mtm: MainThreadMarker) -> Retained<Self> {
|
||||||
let app = NSApplication::sharedApplication(mtm);
|
let app = NSApplication::sharedApplication(mtm);
|
||||||
let delegate =
|
let delegate =
|
||||||
unsafe { app.delegate() }.expect("a delegate was not configured on the application");
|
unsafe { app.delegate() }.expect("a delegate was not configured on the application");
|
||||||
if delegate.is_kind_of::<Self>() {
|
if delegate.is_kind_of::<Self>() {
|
||||||
// SAFETY: Just checked that the delegate is an instance of `ApplicationDelegate`
|
// SAFETY: Just checked that the delegate is an instance of `ApplicationDelegate`
|
||||||
unsafe { Id::cast(delegate) }
|
unsafe { Retained::cast(delegate) }
|
||||||
} else {
|
} else {
|
||||||
panic!("tried to get a delegate that was not the one Winit has registered")
|
panic!("tried to get a delegate that was not the one Winit has registered")
|
||||||
}
|
}
|
||||||
@@ -188,7 +197,7 @@ impl ApplicationDelegate {
|
|||||||
|
|
||||||
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits.
|
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits.
|
||||||
///
|
///
|
||||||
/// Note: that if the `NSApplication` has been launched then that state is preserved,
|
/// 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.
|
/// and we won't need to re-launch the app if subsequent EventLoops are run.
|
||||||
pub fn internal_exit(&self) {
|
pub fn internal_exit(&self) {
|
||||||
self.handle_event(Event::LoopExiting);
|
self.handle_event(Event::LoopExiting);
|
||||||
@@ -232,28 +241,16 @@ impl ApplicationDelegate {
|
|||||||
self.ivars().control_flow.get()
|
self.ivars().control_flow.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn queue_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
pub fn maybe_queue_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||||
self.ivars()
|
self.maybe_queue_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||||
.pending_events
|
|
||||||
.borrow_mut()
|
|
||||||
.push_back(QueuedEvent::WindowEvent(window_id, event));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn queue_device_event(&self, event: DeviceEvent) {
|
pub fn handle_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||||
self.ivars().pending_events.borrow_mut().push_back(QueuedEvent::DeviceEvent(event));
|
self.handle_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn queue_static_scale_factor_changed_event(
|
pub fn maybe_queue_device_event(&self, event: DeviceEvent) {
|
||||||
&self,
|
self.maybe_queue_event(Event::DeviceEvent { device_id: DEVICE_ID, event });
|
||||||
window: Id<WinitWindow>,
|
|
||||||
suggested_size: PhysicalSize<u32>,
|
|
||||||
scale_factor: f64,
|
|
||||||
) {
|
|
||||||
self.ivars().pending_events.borrow_mut().push_back(QueuedEvent::ScaleFactorChanged {
|
|
||||||
window,
|
|
||||||
suggested_size,
|
|
||||||
scale_factor,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_redraw(&self, window_id: WindowId) {
|
pub fn handle_redraw(&self, window_id: WindowId) {
|
||||||
@@ -281,9 +278,27 @@ impl ApplicationDelegate {
|
|||||||
if !pending_redraw.contains(&window_id) {
|
if !pending_redraw.contains(&window_id) {
|
||||||
pending_redraw.push(window_id);
|
pending_redraw.push(window_id);
|
||||||
}
|
}
|
||||||
unsafe { RunLoop::get() }.wakeup();
|
self.ivars().run_loop.wakeup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
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.
|
||||||
|
//
|
||||||
|
// However, it is not documented which actions do this, and which ones are done immediately,
|
||||||
|
// so to make sure that we don't encounter re-entrancy issues, we first check if we're
|
||||||
|
// currently handling another event, and if we are, we queue the event instead.
|
||||||
|
if !self.ivars().event_handler.in_use() {
|
||||||
|
self.handle_event(event);
|
||||||
|
} else {
|
||||||
|
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.handle_event(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn handle_event(&self, event: Event<HandlePendingUserEvents>) {
|
fn handle_event(&self, event: Event<HandlePendingUserEvents>) {
|
||||||
self.ivars().event_handler.handle_event(event, &ActiveEventLoop::new_root(self.retain()))
|
self.ivars().event_handler.handle_event(event, &ActiveEventLoop::new_root(self.retain()))
|
||||||
}
|
}
|
||||||
@@ -345,47 +360,6 @@ impl ApplicationDelegate {
|
|||||||
|
|
||||||
self.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
self.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
||||||
|
|
||||||
let events = mem::take(&mut *self.ivars().pending_events.borrow_mut());
|
|
||||||
for event in events {
|
|
||||||
match event {
|
|
||||||
QueuedEvent::WindowEvent(window_id, event) => {
|
|
||||||
self.handle_event(Event::WindowEvent {
|
|
||||||
window_id: RootWindowId(window_id),
|
|
||||||
event,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
QueuedEvent::DeviceEvent(event) => {
|
|
||||||
self.handle_event(Event::DeviceEvent { device_id: DEVICE_ID, event });
|
|
||||||
},
|
|
||||||
QueuedEvent::ScaleFactorChanged { window, suggested_size, scale_factor } => {
|
|
||||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
|
||||||
let scale_factor_changed_event = Event::WindowEvent {
|
|
||||||
window_id: RootWindowId(window.id()),
|
|
||||||
event: WindowEvent::ScaleFactorChanged {
|
|
||||||
scale_factor,
|
|
||||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
|
||||||
&new_inner_size,
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
self.handle_event(scale_factor_changed_event);
|
|
||||||
|
|
||||||
let physical_size = *new_inner_size.lock().unwrap();
|
|
||||||
drop(new_inner_size);
|
|
||||||
let logical_size = physical_size.to_logical(scale_factor);
|
|
||||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
|
||||||
window.setContentSize(size);
|
|
||||||
|
|
||||||
let resized_event = Event::WindowEvent {
|
|
||||||
window_id: RootWindowId(window.id()),
|
|
||||||
event: WindowEvent::Resized(physical_size),
|
|
||||||
};
|
|
||||||
self.handle_event(resized_event);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut());
|
let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut());
|
||||||
for window_id in redraw {
|
for window_id in redraw {
|
||||||
self.handle_event(Event::WindowEvent {
|
self.handle_event(Event::WindowEvent {
|
||||||
@@ -416,17 +390,6 @@ impl ApplicationDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) enum QueuedEvent {
|
|
||||||
WindowEvent(WindowId, WindowEvent),
|
|
||||||
DeviceEvent(DeviceEvent),
|
|
||||||
ScaleFactorChanged {
|
|
||||||
window: Id<WinitWindow>,
|
|
||||||
suggested_size: PhysicalSize<u32>,
|
|
||||||
scale_factor: f64,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct HandlePendingUserEvents;
|
pub(crate) struct HandlePendingUserEvents;
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@ use std::ffi::c_uchar;
|
|||||||
use std::slice;
|
use std::slice;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::Sel;
|
use objc2::runtime::Sel;
|
||||||
use objc2::{msg_send_id, sel, ClassType};
|
use objc2::{msg_send_id, sel, ClassType};
|
||||||
use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
|
use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
|
||||||
@@ -15,7 +15,7 @@ use crate::cursor::{CursorImage, OnlyCursorImageSource};
|
|||||||
use crate::window::CursorIcon;
|
use crate::window::CursorIcon;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct CustomCursor(pub(crate) Id<NSCursor>);
|
pub struct CustomCursor(pub(crate) Retained<NSCursor>);
|
||||||
|
|
||||||
// SAFETY: NSCursor is immutable and thread-safe
|
// SAFETY: NSCursor is immutable and thread-safe
|
||||||
// TODO(madsmtm): Put this logic in objc2-app-kit itself
|
// TODO(madsmtm): Put this logic in objc2-app-kit itself
|
||||||
@@ -28,7 +28,7 @@ impl CustomCursor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Id<NSCursor> {
|
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Retained<NSCursor> {
|
||||||
let width = cursor.width;
|
let width = cursor.width;
|
||||||
let height = cursor.height;
|
let height = cursor.height;
|
||||||
|
|
||||||
@@ -60,14 +60,14 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Id<NSCursor> {
|
|||||||
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
|
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_cursor() -> Id<NSCursor> {
|
pub(crate) fn default_cursor() -> Retained<NSCursor> {
|
||||||
NSCursor::arrowCursor()
|
NSCursor::arrowCursor()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn try_cursor_from_selector(sel: Sel) -> Option<Id<NSCursor>> {
|
unsafe fn try_cursor_from_selector(sel: Sel) -> Option<Retained<NSCursor>> {
|
||||||
let cls = NSCursor::class();
|
let cls = NSCursor::class();
|
||||||
if cls.responds_to(sel) {
|
if cls.responds_to(sel) {
|
||||||
let cursor: Id<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] };
|
let cursor: Retained<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] };
|
||||||
Some(cursor)
|
Some(cursor)
|
||||||
} else {
|
} else {
|
||||||
tracing::warn!("cursor `{sel}` appears to be invalid");
|
tracing::warn!("cursor `{sel}` appears to be invalid");
|
||||||
@@ -82,7 +82,7 @@ macro_rules! def_undocumented_cursor {
|
|||||||
)*} => {$(
|
)*} => {$(
|
||||||
$(#[$($m)*])*
|
$(#[$($m)*])*
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn $name() -> Id<NSCursor> {
|
fn $name() -> Retained<NSCursor> {
|
||||||
unsafe { try_cursor_from_selector(sel!($name)).unwrap_or_else(|| default_cursor()) }
|
unsafe { try_cursor_from_selector(sel!($name)).unwrap_or_else(|| default_cursor()) }
|
||||||
}
|
}
|
||||||
)*};
|
)*};
|
||||||
@@ -112,7 +112,7 @@ def_undocumented_cursor!(
|
|||||||
|
|
||||||
// Note that loading `busybutclickable` with this code won't animate
|
// Note that loading `busybutclickable` with this code won't animate
|
||||||
// the frames; instead you'll just get them all in a column.
|
// the frames; instead you'll just get them all in a column.
|
||||||
unsafe fn load_webkit_cursor(name: &NSString) -> Id<NSCursor> {
|
unsafe fn load_webkit_cursor(name: &NSString) -> Retained<NSCursor> {
|
||||||
// Snatch a cursor from WebKit; They fit the style of the native
|
// Snatch a cursor from WebKit; They fit the style of the native
|
||||||
// cursors, and will seem completely standard to macOS users.
|
// cursors, and will seem completely standard to macOS users.
|
||||||
//
|
//
|
||||||
@@ -128,7 +128,7 @@ unsafe fn load_webkit_cursor(name: &NSString) -> Id<NSCursor> {
|
|||||||
|
|
||||||
// TODO: Handle PLists better
|
// TODO: Handle PLists better
|
||||||
let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist"));
|
let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist"));
|
||||||
let info: Id<NSDictionary<NSObject, NSObject>> = unsafe {
|
let info: Retained<NSDictionary<NSObject, NSObject>> = unsafe {
|
||||||
msg_send_id![
|
msg_send_id![
|
||||||
<NSDictionary<NSObject, NSObject>>::class(),
|
<NSDictionary<NSObject, NSObject>>::class(),
|
||||||
dictionaryWithContentsOfFile: &*info_path,
|
dictionaryWithContentsOfFile: &*info_path,
|
||||||
@@ -155,15 +155,15 @@ unsafe fn load_webkit_cursor(name: &NSString) -> Id<NSCursor> {
|
|||||||
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
|
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn webkit_move() -> Id<NSCursor> {
|
fn webkit_move() -> Retained<NSCursor> {
|
||||||
unsafe { load_webkit_cursor(ns_string!("move")) }
|
unsafe { load_webkit_cursor(ns_string!("move")) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn webkit_cell() -> Id<NSCursor> {
|
fn webkit_cell() -> Retained<NSCursor> {
|
||||||
unsafe { load_webkit_cursor(ns_string!("cell")) }
|
unsafe { load_webkit_cursor(ns_string!("cell")) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn invisible_cursor() -> Id<NSCursor> {
|
pub(crate) fn invisible_cursor() -> Retained<NSCursor> {
|
||||||
// 16x16 GIF data for invisible cursor
|
// 16x16 GIF data for invisible cursor
|
||||||
// You can reproduce this via ImageMagick.
|
// You can reproduce this via ImageMagick.
|
||||||
// $ convert -size 16x16 xc:none cursor.gif
|
// $ convert -size 16x16 xc:none cursor.gif
|
||||||
@@ -174,7 +174,7 @@ pub(crate) fn invisible_cursor() -> Id<NSCursor> {
|
|||||||
0xa3, 0x9c, 0xb4, 0xda, 0x8b, 0xb3, 0x3e, 0x05, 0x00, 0x3b,
|
0xa3, 0x9c, 0xb4, 0xda, 0x8b, 0xb3, 0x3e, 0x05, 0x00, 0x3b,
|
||||||
];
|
];
|
||||||
|
|
||||||
fn new_invisible() -> Id<NSCursor> {
|
fn new_invisible() -> Retained<NSCursor> {
|
||||||
// TODO: Consider using `dataWithBytesNoCopy:`
|
// TODO: Consider using `dataWithBytesNoCopy:`
|
||||||
let data = NSData::with_bytes(CURSOR_BYTES);
|
let data = NSData::with_bytes(CURSOR_BYTES);
|
||||||
let image = NSImage::initWithData(NSImage::alloc(), &data).unwrap();
|
let image = NSImage::initWithData(NSImage::alloc(), &data).unwrap();
|
||||||
@@ -187,7 +187,7 @@ pub(crate) fn invisible_cursor() -> Id<NSCursor> {
|
|||||||
CURSOR.get_or_init(|| CustomCursor(new_invisible())).0.clone()
|
CURSOR.get_or_init(|| CustomCursor(new_invisible())).0.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Id<NSCursor> {
|
pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Retained<NSCursor> {
|
||||||
match icon {
|
match icon {
|
||||||
CursorIcon::Default => default_cursor(),
|
CursorIcon::Default => default_cursor(),
|
||||||
CursorIcon::Pointer => NSCursor::pointingHandCursor(),
|
CursorIcon::Pointer => NSCursor::pointingHandCursor(),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::ffi::c_void;
|
|||||||
|
|
||||||
use core_foundation::base::CFRelease;
|
use core_foundation::base::CFRelease;
|
||||||
use core_foundation::data::{CFDataGetBytePtr, CFDataRef};
|
use core_foundation::data::{CFDataGetBytePtr, CFDataRef};
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSEventType};
|
use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSEventType};
|
||||||
use objc2_foundation::{run_on_main, NSPoint};
|
use objc2_foundation::{run_on_main, NSPoint};
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
@@ -133,8 +133,8 @@ pub(crate) fn create_key_event(
|
|||||||
let key_without_modifiers = get_modifierless_char(scancode);
|
let key_without_modifiers = get_modifierless_char(scancode);
|
||||||
|
|
||||||
let modifiers = unsafe { ns_event.modifierFlags() };
|
let modifiers = unsafe { ns_event.modifierFlags() };
|
||||||
let has_ctrl = flags_contains(modifiers, NSEventModifierFlags::NSEventModifierFlagControl);
|
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagControl);
|
||||||
let has_cmd = flags_contains(modifiers, NSEventModifierFlags::NSEventModifierFlagCommand);
|
let has_cmd = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagCommand);
|
||||||
|
|
||||||
let logical_key = match text_with_all_modifiers.as_ref() {
|
let logical_key = match text_with_all_modifiers.as_ref() {
|
||||||
// Only checking for ctrl and cmd here, not checking for alt because we DO want to
|
// Only checking for ctrl and cmd here, not checking for alt because we DO want to
|
||||||
@@ -305,16 +305,12 @@ const NX_DEVICELALTKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x000000
|
|||||||
const NX_DEVICERALTKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000040);
|
const NX_DEVICERALTKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000040);
|
||||||
const NX_DEVICERCTLKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00002000);
|
const NX_DEVICERCTLKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00002000);
|
||||||
|
|
||||||
pub(super) fn flags_contains(flags: NSEventModifierFlags, value: NSEventModifierFlags) -> bool {
|
|
||||||
flags.0 & value.0 == value.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn lalt_pressed(event: &NSEvent) -> bool {
|
pub(super) fn lalt_pressed(event: &NSEvent) -> bool {
|
||||||
flags_contains(unsafe { event.modifierFlags() }, NX_DEVICELALTKEYMASK)
|
unsafe { event.modifierFlags() }.contains(NX_DEVICELALTKEYMASK)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn ralt_pressed(event: &NSEvent) -> bool {
|
pub(super) fn ralt_pressed(event: &NSEvent) -> bool {
|
||||||
flags_contains(unsafe { event.modifierFlags() }, NX_DEVICERALTKEYMASK)
|
unsafe { event.modifierFlags() }.contains(NX_DEVICERALTKEYMASK)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
|
pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
|
||||||
@@ -322,38 +318,33 @@ pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
|
|||||||
let mut state = ModifiersState::empty();
|
let mut state = ModifiersState::empty();
|
||||||
let mut pressed_mods = ModifiersKeys::empty();
|
let mut pressed_mods = ModifiersKeys::empty();
|
||||||
|
|
||||||
state.set(
|
state
|
||||||
ModifiersState::SHIFT,
|
.set(ModifiersState::SHIFT, flags.contains(NSEventModifierFlags::NSEventModifierFlagShift));
|
||||||
flags_contains(flags, NSEventModifierFlags::NSEventModifierFlagShift),
|
pressed_mods.set(ModifiersKeys::LSHIFT, flags.contains(NX_DEVICELSHIFTKEYMASK));
|
||||||
);
|
pressed_mods.set(ModifiersKeys::RSHIFT, flags.contains(NX_DEVICERSHIFTKEYMASK));
|
||||||
pressed_mods.set(ModifiersKeys::LSHIFT, flags_contains(flags, NX_DEVICELSHIFTKEYMASK));
|
|
||||||
pressed_mods.set(ModifiersKeys::RSHIFT, flags_contains(flags, NX_DEVICERSHIFTKEYMASK));
|
|
||||||
|
|
||||||
state.set(
|
state.set(
|
||||||
ModifiersState::CONTROL,
|
ModifiersState::CONTROL,
|
||||||
flags_contains(flags, NSEventModifierFlags::NSEventModifierFlagControl),
|
flags.contains(NSEventModifierFlags::NSEventModifierFlagControl),
|
||||||
);
|
);
|
||||||
pressed_mods.set(ModifiersKeys::LCONTROL, flags_contains(flags, NX_DEVICELCTLKEYMASK));
|
pressed_mods.set(ModifiersKeys::LCONTROL, flags.contains(NX_DEVICELCTLKEYMASK));
|
||||||
pressed_mods.set(ModifiersKeys::RCONTROL, flags_contains(flags, NX_DEVICERCTLKEYMASK));
|
pressed_mods.set(ModifiersKeys::RCONTROL, flags.contains(NX_DEVICERCTLKEYMASK));
|
||||||
|
|
||||||
state.set(
|
state.set(ModifiersState::ALT, flags.contains(NSEventModifierFlags::NSEventModifierFlagOption));
|
||||||
ModifiersState::ALT,
|
pressed_mods.set(ModifiersKeys::LALT, flags.contains(NX_DEVICELALTKEYMASK));
|
||||||
flags_contains(flags, NSEventModifierFlags::NSEventModifierFlagOption),
|
pressed_mods.set(ModifiersKeys::RALT, flags.contains(NX_DEVICERALTKEYMASK));
|
||||||
);
|
|
||||||
pressed_mods.set(ModifiersKeys::LALT, flags_contains(flags, NX_DEVICELALTKEYMASK));
|
|
||||||
pressed_mods.set(ModifiersKeys::RALT, flags_contains(flags, NX_DEVICERALTKEYMASK));
|
|
||||||
|
|
||||||
state.set(
|
state.set(
|
||||||
ModifiersState::SUPER,
|
ModifiersState::SUPER,
|
||||||
flags_contains(flags, NSEventModifierFlags::NSEventModifierFlagCommand),
|
flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand),
|
||||||
);
|
);
|
||||||
pressed_mods.set(ModifiersKeys::LSUPER, flags_contains(flags, NX_DEVICELCMDKEYMASK));
|
pressed_mods.set(ModifiersKeys::LSUPER, flags.contains(NX_DEVICELCMDKEYMASK));
|
||||||
pressed_mods.set(ModifiersKeys::RSUPER, flags_contains(flags, NX_DEVICERCMDKEYMASK));
|
pressed_mods.set(ModifiersKeys::RSUPER, flags.contains(NX_DEVICERCMDKEYMASK));
|
||||||
|
|
||||||
Modifiers { state, pressed_mods }
|
Modifiers { state, pressed_mods }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn dummy_event() -> Option<Id<NSEvent>> {
|
pub(super) fn dummy_event() -> Option<Retained<NSEvent>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2(
|
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2(
|
||||||
NSEventType::ApplicationDefined,
|
NSEventType::ApplicationDefined,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::{fmt, mem};
|
use std::{fmt, mem};
|
||||||
|
|
||||||
use super::app_delegate::HandlePendingUserEvents;
|
use super::app_state::HandlePendingUserEvents;
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ impl fmt::Debug for EventHandlerData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct EventHandler {
|
pub(crate) struct EventHandler {
|
||||||
/// This can be in the following states:
|
/// This can be in the following states:
|
||||||
/// - Not registered by the event loop (None).
|
/// - Not registered by the event loop (None).
|
||||||
@@ -26,6 +26,10 @@ pub(crate) struct EventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EventHandler {
|
impl EventHandler {
|
||||||
|
pub(crate) const fn new() -> Self {
|
||||||
|
Self { inner: RefCell::new(None) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the event loop handler for the duration of the given closure.
|
/// Set the event loop handler for the duration of the given closure.
|
||||||
///
|
///
|
||||||
/// This is similar to using the `scoped-tls` or `scoped-tls-hkt` crates
|
/// This is similar to using the `scoped-tls` or `scoped-tls-hkt` crates
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ use core_foundation::runloop::{
|
|||||||
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
|
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
|
||||||
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||||
};
|
};
|
||||||
use objc2::rc::{autoreleasepool, Id};
|
use objc2::rc::{autoreleasepool, Retained};
|
||||||
use objc2::runtime::ProtocolObject;
|
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_app_kit::{NSApplication, NSApplicationActivationPolicy, NSWindow};
|
||||||
use objc2_foundation::{MainThreadMarker, NSObjectProtocol};
|
use objc2_foundation::{MainThreadMarker, NSObjectProtocol};
|
||||||
|
|
||||||
use super::app::WinitApplication;
|
use super::app::WinitApplication;
|
||||||
use super::app_delegate::{ApplicationDelegate, HandlePendingUserEvents};
|
use super::app_state::{ApplicationDelegate, HandlePendingUserEvents};
|
||||||
use super::event::dummy_event;
|
use super::event::dummy_event;
|
||||||
use super::monitor::{self, MonitorHandle};
|
use super::monitor::{self, MonitorHandle};
|
||||||
use super::observer::setup_control_flow_observers;
|
use super::observer::setup_control_flow_observers;
|
||||||
@@ -33,7 +33,7 @@ use crate::event_loop::{
|
|||||||
use crate::platform::macos::ActivationPolicy;
|
use crate::platform::macos::ActivationPolicy;
|
||||||
use crate::platform::pump_events::PumpStatus;
|
use crate::platform::pump_events::PumpStatus;
|
||||||
use crate::platform_impl::platform::cursor::CustomCursor;
|
use crate::platform_impl::platform::cursor::CustomCursor;
|
||||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource};
|
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PanicInfo {
|
pub struct PanicInfo {
|
||||||
@@ -53,7 +53,7 @@ impl PanicInfo {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overwrites the curret state if the current state is not panicking
|
/// Overwrites the current state if the current state is not panicking
|
||||||
pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) {
|
pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) {
|
||||||
if !self.is_panicking() {
|
if !self.is_panicking() {
|
||||||
self.inner.set(Some(p));
|
self.inner.set(Some(p));
|
||||||
@@ -67,17 +67,21 @@ impl PanicInfo {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ActiveEventLoop {
|
pub struct ActiveEventLoop {
|
||||||
delegate: Id<ApplicationDelegate>,
|
delegate: Retained<ApplicationDelegate>,
|
||||||
pub(super) mtm: MainThreadMarker,
|
pub(super) mtm: MainThreadMarker,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveEventLoop {
|
impl ActiveEventLoop {
|
||||||
pub(super) fn new_root(delegate: Id<ApplicationDelegate>) -> RootWindowTarget {
|
pub(super) fn new_root(delegate: Retained<ApplicationDelegate>) -> RootWindowTarget {
|
||||||
let mtm = MainThreadMarker::from(&*delegate);
|
let mtm = MainThreadMarker::from(&*delegate);
|
||||||
let p = Self { delegate, mtm };
|
let p = Self { delegate, mtm };
|
||||||
RootWindowTarget { p, _marker: PhantomData }
|
RootWindowTarget { p, _marker: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn app_delegate(&self) -> &ApplicationDelegate {
|
||||||
|
&self.delegate
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
|
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
|
||||||
RootCustomCursor { inner: CustomCursor::new(source.inner) }
|
RootCustomCursor { inner: CustomCursor::new(source.inner) }
|
||||||
}
|
}
|
||||||
@@ -102,6 +106,17 @@ impl ActiveEventLoop {
|
|||||||
rwh_05::RawDisplayHandle::AppKit(rwh_05::AppKitDisplayHandle::empty())
|
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")]
|
#[cfg(feature = "rwh_06")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn raw_display_handle_rwh_06(
|
pub fn raw_display_handle_rwh_06(
|
||||||
@@ -133,9 +148,7 @@ impl ActiveEventLoop {
|
|||||||
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
|
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
|
||||||
OwnedDisplayHandle
|
OwnedDisplayHandle
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ActiveEventLoop {
|
|
||||||
pub(crate) fn hide_application(&self) {
|
pub(crate) fn hide_application(&self) {
|
||||||
NSApplication::sharedApplication(self.mtm).hide(None)
|
NSApplication::sharedApplication(self.mtm).hide(None)
|
||||||
}
|
}
|
||||||
@@ -172,12 +185,12 @@ pub struct EventLoop<T: 'static> {
|
|||||||
///
|
///
|
||||||
/// We intentionally don't store `WinitApplication` since we want to have
|
/// We intentionally don't store `WinitApplication` since we want to have
|
||||||
/// the possibility of swapping that out at some point.
|
/// the possibility of swapping that out at some point.
|
||||||
app: Id<NSApplication>,
|
app: Retained<NSApplication>,
|
||||||
/// The application delegate that we've registered.
|
/// The application delegate that we've registered.
|
||||||
///
|
///
|
||||||
/// The delegate is only weakly referenced by NSApplication, so we must
|
/// The delegate is only weakly referenced by NSApplication, so we must
|
||||||
/// keep it around here as well.
|
/// keep it around here as well.
|
||||||
delegate: Id<ApplicationDelegate>,
|
delegate: Retained<ApplicationDelegate>,
|
||||||
|
|
||||||
// Event sender and receiver, used for EventLoopProxy.
|
// Event sender and receiver, used for EventLoopProxy.
|
||||||
sender: mpsc::Sender<T>,
|
sender: mpsc::Sender<T>,
|
||||||
@@ -211,7 +224,7 @@ impl<T> EventLoop<T> {
|
|||||||
let mtm = MainThreadMarker::new()
|
let mtm = MainThreadMarker::new()
|
||||||
.expect("on macOS, `EventLoop` must be created on the main thread!");
|
.expect("on macOS, `EventLoop` must be created on the main thread!");
|
||||||
|
|
||||||
let app: Id<NSApplication> =
|
let app: Retained<NSApplication> =
|
||||||
unsafe { msg_send_id![WinitApplication::class(), sharedApplication] };
|
unsafe { msg_send_id![WinitApplication::class(), sharedApplication] };
|
||||||
|
|
||||||
if !app.is_kind_of::<WinitApplication>() {
|
if !app.is_kind_of::<WinitApplication>() {
|
||||||
@@ -238,7 +251,7 @@ impl<T> EventLoop<T> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let panic_info: Rc<PanicInfo> = Default::default();
|
let panic_info: Rc<PanicInfo> = Default::default();
|
||||||
setup_control_flow_observers(Rc::downgrade(&panic_info));
|
setup_control_flow_observers(mtm, Rc::downgrade(&panic_info));
|
||||||
|
|
||||||
let (sender, receiver) = mpsc::channel();
|
let (sender, receiver) = mpsc::channel();
|
||||||
Ok(EventLoop {
|
Ok(EventLoop {
|
||||||
@@ -480,8 +493,7 @@ impl<T> EventLoopProxy<T> {
|
|||||||
cancel: None,
|
cancel: None,
|
||||||
perform: event_loop_proxy_handler,
|
perform: event_loop_proxy_handler,
|
||||||
};
|
};
|
||||||
let source =
|
let source = CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::MAX - 1, &mut context);
|
||||||
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
|
|
||||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||||
CFRunLoopWakeUp(rl);
|
CFRunLoopWakeUp(rl);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::Sel;
|
use objc2::runtime::Sel;
|
||||||
use objc2::sel;
|
use objc2::sel;
|
||||||
use objc2_app_kit::{NSApplication, NSEventModifierFlags, NSMenu, NSMenuItem};
|
use objc2_app_kit::{NSApplication, NSEventModifierFlags, NSMenu, NSMenuItem};
|
||||||
@@ -48,10 +48,10 @@ pub fn initialize(app: &NSApplication) {
|
|||||||
Some(sel!(hideOtherApplications:)),
|
Some(sel!(hideOtherApplications:)),
|
||||||
Some(KeyEquivalent {
|
Some(KeyEquivalent {
|
||||||
key: ns_string!("h"),
|
key: ns_string!("h"),
|
||||||
masks: Some(NSEventModifierFlags(
|
masks: Some(
|
||||||
NSEventModifierFlags::NSEventModifierFlagOption.0
|
NSEventModifierFlags::NSEventModifierFlagOption
|
||||||
| NSEventModifierFlags::NSEventModifierFlagCommand.0,
|
| NSEventModifierFlags::NSEventModifierFlagCommand,
|
||||||
)),
|
),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ fn menu_item(
|
|||||||
title: &NSString,
|
title: &NSString,
|
||||||
selector: Option<Sel>,
|
selector: Option<Sel>,
|
||||||
key_equivalent: Option<KeyEquivalent<'_>>,
|
key_equivalent: Option<KeyEquivalent<'_>>,
|
||||||
) -> Id<NSMenuItem> {
|
) -> Retained<NSMenuItem> {
|
||||||
let (key, masks) = match key_equivalent {
|
let (key, masks) = match key_equivalent {
|
||||||
Some(ke) => (ke.key, ke.masks),
|
Some(ke) => (ke.key, ke.masks),
|
||||||
None => (ns_string!(""), None),
|
None => (ns_string!(""), None),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod app_delegate;
|
mod app_state;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
mod event;
|
mod event;
|
||||||
mod event_handler;
|
mod event_handler;
|
||||||
@@ -37,7 +37,7 @@ pub(crate) use crate::platform_impl::Fullscreen;
|
|||||||
pub struct DeviceId;
|
pub struct DeviceId;
|
||||||
|
|
||||||
impl DeviceId {
|
impl DeviceId {
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
DeviceId
|
DeviceId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use core_foundation::string::CFString;
|
|||||||
use core_graphics::display::{
|
use core_graphics::display::{
|
||||||
CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode,
|
CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode,
|
||||||
};
|
};
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::AnyObject;
|
use objc2::runtime::AnyObject;
|
||||||
use objc2_app_kit::NSScreen;
|
use objc2_app_kit::NSScreen;
|
||||||
use objc2_foundation::{ns_string, run_on_main, MainThreadMarker, NSNumber, NSPoint, NSRect};
|
use objc2_foundation::{ns_string, run_on_main, MainThreadMarker, NSNumber, NSPoint, NSRect};
|
||||||
@@ -295,7 +295,7 @@ impl MonitorHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option<Id<NSScreen>> {
|
pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option<Retained<NSScreen>> {
|
||||||
let uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) };
|
let uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) };
|
||||||
NSScreen::screens(mtm).into_iter().find(|screen| {
|
NSScreen::screens(mtm).into_iter().find(|screen| {
|
||||||
let other_native_id = get_display_id(screen);
|
let other_native_id = get_display_id(screen);
|
||||||
|
|||||||
@@ -1,21 +1,28 @@
|
|||||||
|
//! Utilities for working with `CFRunLoop`.
|
||||||
|
//!
|
||||||
|
//! See Apple's documentation on Run Loops for details:
|
||||||
|
//! <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html>
|
||||||
|
use std::cell::Cell;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::panic::{AssertUnwindSafe, UnwindSafe};
|
use std::panic::{AssertUnwindSafe, UnwindSafe};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::rc::Weak;
|
use std::rc::Weak;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use core_foundation::base::{CFIndex, CFOptionFlags, CFRelease};
|
use block2::Block;
|
||||||
|
use core_foundation::base::{CFIndex, CFOptionFlags, CFRelease, CFTypeRef};
|
||||||
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
||||||
use core_foundation::runloop::{
|
use core_foundation::runloop::{
|
||||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopExit,
|
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopDefaultMode,
|
||||||
CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddTimer, CFRunLoopGetMain,
|
kCFRunLoopExit, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddTimer, CFRunLoopGetMain,
|
||||||
CFRunLoopObserverCallBack, CFRunLoopObserverContext, CFRunLoopObserverCreate,
|
CFRunLoopObserverCallBack, CFRunLoopObserverContext, CFRunLoopObserverCreate,
|
||||||
CFRunLoopObserverRef, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
CFRunLoopObserverRef, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
||||||
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, CFRunLoopWakeUp,
|
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, CFRunLoopWakeUp,
|
||||||
};
|
};
|
||||||
use objc2_foundation::MainThreadMarker;
|
use objc2_foundation::MainThreadMarker;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
use super::app_delegate::ApplicationDelegate;
|
use super::app_state::ApplicationDelegate;
|
||||||
use super::event_loop::{stop_app_on_panic, PanicInfo};
|
use super::event_loop::{stop_app_on_panic, PanicInfo};
|
||||||
use super::ffi;
|
use super::ffi;
|
||||||
|
|
||||||
@@ -84,10 +91,20 @@ extern "C" fn control_flow_end_handler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct RunLoop(CFRunLoopRef);
|
pub struct RunLoop(CFRunLoopRef);
|
||||||
|
|
||||||
|
impl Default for RunLoop {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(ptr::null_mut())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RunLoop {
|
impl RunLoop {
|
||||||
pub unsafe fn get() -> Self {
|
pub fn main(mtm: MainThreadMarker) -> Self {
|
||||||
|
// SAFETY: We have a MainThreadMarker here, which means we know we're on the main thread, so
|
||||||
|
// scheduling (and scheduling a non-`Send` block) to that thread is allowed.
|
||||||
|
let _ = mtm;
|
||||||
RunLoop(unsafe { CFRunLoopGetMain() })
|
RunLoop(unsafe { CFRunLoopGetMain() })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,9 +131,79 @@ impl RunLoop {
|
|||||||
};
|
};
|
||||||
unsafe { CFRunLoopAddObserver(self.0, observer, kCFRunLoopCommonModes) };
|
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
|
||||||
|
/// event sources are processed.
|
||||||
|
///
|
||||||
|
/// This is used for running event handlers, as those are not allowed to run re-entrantly.
|
||||||
|
///
|
||||||
|
/// # Implementation
|
||||||
|
///
|
||||||
|
/// This queuing could be implemented in the following several ways with subtle differences in
|
||||||
|
/// timing. This list is sorted in rough order in which they are run:
|
||||||
|
///
|
||||||
|
/// 1. Using `CFRunLoopPerformBlock` or `-[NSRunLoop performBlock:]`.
|
||||||
|
///
|
||||||
|
/// 2. Using `-[NSObject performSelectorOnMainThread:withObject:waitUntilDone:]` or wrapping the
|
||||||
|
/// event in `NSEvent` and posting that to `-[NSApplication postEvent:atStart:]` (both
|
||||||
|
/// creates a custom `CFRunLoopSource`, and signals that to wake up the main event loop).
|
||||||
|
///
|
||||||
|
/// a. `atStart = true`.
|
||||||
|
///
|
||||||
|
/// b. `atStart = false`.
|
||||||
|
///
|
||||||
|
/// 3. `dispatch_async` or `dispatch_async_f`. Note that this may appear before 2b, it does not
|
||||||
|
/// respect the ordering that runloop events have.
|
||||||
|
///
|
||||||
|
/// We choose the first one, both for ease-of-implementation, but mostly for consistency, as we
|
||||||
|
/// want the event to be queued in a way that preserves the order the events originally arrived
|
||||||
|
/// in.
|
||||||
|
///
|
||||||
|
/// As an example, let's assume that we receive two events from the user, a mouse click which we
|
||||||
|
/// handled by queuing it, and a window resize which we handled immediately. If we allowed
|
||||||
|
/// AppKit to choose the ordering when queuing the mouse event, it might get put in the back of
|
||||||
|
/// the queue, and the events would appear out of order to the user of Winit. So we must instead
|
||||||
|
/// put the event at the very front of the queue, to be handled as soon as possible after
|
||||||
|
/// handling whatever event it's currently handling.
|
||||||
|
pub fn queue_closure(&self, closure: impl FnOnce() + 'static) {
|
||||||
|
extern "C" {
|
||||||
|
fn CFRunLoopPerformBlock(rl: CFRunLoopRef, mode: CFTypeRef, block: &Block<dyn Fn()>);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert `FnOnce()` to `Block<dyn Fn()>`.
|
||||||
|
let closure = Cell::new(Some(closure));
|
||||||
|
let block = block2::RcBlock::new(move || {
|
||||||
|
if let Some(closure) = closure.take() {
|
||||||
|
closure()
|
||||||
|
} else {
|
||||||
|
error!("tried to execute queued closure on main thread twice");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// There are a few common modes (`kCFRunLoopCommonModes`) defined by Cocoa:
|
||||||
|
// - `NSDefaultRunLoopMode`, alias of `kCFRunLoopDefaultMode`.
|
||||||
|
// - `NSEventTrackingRunLoopMode`, used when mouse-dragging and live-resizing a window.
|
||||||
|
// - `NSModalPanelRunLoopMode`, used when running a modal inside the Winit event loop.
|
||||||
|
// - `NSConnectionReplyMode`: TODO.
|
||||||
|
//
|
||||||
|
// We only want to run event handlers in the default mode, as we support running a blocking
|
||||||
|
// modal inside a Winit event handler (see [#1779]) which outrules the modal panel mode, and
|
||||||
|
// resizing such panel window enters the event tracking run loop mode, so we can't directly
|
||||||
|
// trigger events inside that mode either.
|
||||||
|
//
|
||||||
|
// Any events that are queued while running a modal or when live-resizing will instead wait,
|
||||||
|
// and be delivered to the application afterwards.
|
||||||
|
//
|
||||||
|
// [#1779]: https://github.com/rust-windowing/winit/issues/1779
|
||||||
|
let mode = unsafe { kCFRunLoopDefaultMode as CFTypeRef };
|
||||||
|
|
||||||
|
// SAFETY: The runloop is valid, the mode is a `CFStringRef`, and the block is `'static`.
|
||||||
|
unsafe { CFRunLoopPerformBlock(self.0, mode, &block) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_control_flow_observers(panic_info: Weak<PanicInfo>) {
|
pub fn setup_control_flow_observers(mtm: MainThreadMarker, panic_info: Weak<PanicInfo>) {
|
||||||
|
let run_loop = RunLoop::main(mtm);
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut context = CFRunLoopObserverContext {
|
let mut context = CFRunLoopObserverContext {
|
||||||
info: Weak::into_raw(panic_info) as *mut _,
|
info: Weak::into_raw(panic_info) as *mut _,
|
||||||
@@ -125,16 +212,15 @@ pub fn setup_control_flow_observers(panic_info: Weak<PanicInfo>) {
|
|||||||
release: None,
|
release: None,
|
||||||
copyDescription: None,
|
copyDescription: None,
|
||||||
};
|
};
|
||||||
let run_loop = RunLoop::get();
|
|
||||||
run_loop.add_observer(
|
run_loop.add_observer(
|
||||||
kCFRunLoopAfterWaiting,
|
kCFRunLoopAfterWaiting,
|
||||||
CFIndex::min_value(),
|
CFIndex::MIN,
|
||||||
control_flow_begin_handler,
|
control_flow_begin_handler,
|
||||||
&mut context as *mut _,
|
&mut context as *mut _,
|
||||||
);
|
);
|
||||||
run_loop.add_observer(
|
run_loop.add_observer(
|
||||||
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
||||||
CFIndex::max_value(),
|
CFIndex::MAX,
|
||||||
control_flow_end_handler,
|
control_flow_end_handler,
|
||||||
&mut context as *mut _,
|
&mut context as *mut _,
|
||||||
);
|
);
|
||||||
@@ -165,8 +251,8 @@ impl Drop for EventLoopWaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EventLoopWaker {
|
impl EventLoopWaker {
|
||||||
fn default() -> EventLoopWaker {
|
pub(crate) fn new() -> Self {
|
||||||
extern "C" fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
|
extern "C" fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
|
||||||
unsafe {
|
unsafe {
|
||||||
// Create a timer with a 0.1µs interval (1ns does not work) to mimic polling.
|
// Create a timer with a 0.1µs interval (1ns does not work) to mimic polling.
|
||||||
@@ -174,7 +260,7 @@ impl Default for EventLoopWaker {
|
|||||||
// future, but that gets changed to fire immediately in did_finish_launching
|
// future, but that gets changed to fire immediately in did_finish_launching
|
||||||
let timer = CFRunLoopTimerCreate(
|
let timer = CFRunLoopTimerCreate(
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
std::f64::MAX,
|
f64::MAX,
|
||||||
0.000_000_1,
|
0.000_000_1,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -182,23 +268,21 @@ impl Default for EventLoopWaker {
|
|||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
);
|
);
|
||||||
CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes);
|
CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes);
|
||||||
EventLoopWaker { timer, start_instant: Instant::now(), next_fire_date: None }
|
Self { timer, start_instant: Instant::now(), next_fire_date: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl EventLoopWaker {
|
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) {
|
||||||
if self.next_fire_date.is_some() {
|
if self.next_fire_date.is_some() {
|
||||||
self.next_fire_date = None;
|
self.next_fire_date = None;
|
||||||
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MAX) }
|
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, f64::MAX) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
if self.next_fire_date != Some(self.start_instant) {
|
if self.next_fire_date != Some(self.start_instant) {
|
||||||
self.next_fire_date = Some(self.start_instant);
|
self.next_fire_date = Some(self.start_instant);
|
||||||
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MIN) }
|
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, f64::MIN) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
use objc2_foundation::{NSNotFound, NSRange, NSUInteger};
|
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
pub static EMPTY_RANGE: NSRange = NSRange { location: NSNotFound as NSUInteger, length: 0 };
|
|
||||||
|
|
||||||
macro_rules! trace_scope {
|
macro_rules! trace_scope {
|
||||||
($s:literal) => {
|
($s:literal) => {
|
||||||
let _crate = $crate::platform_impl::platform::util::TraceGuard::new(module_path!(), $s);
|
let _crate = $crate::platform_impl::platform::util::TraceGuard::new(module_path!(), $s);
|
||||||
|
|||||||
@@ -3,29 +3,27 @@ use std::cell::{Cell, RefCell};
|
|||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use objc2::rc::{Id, WeakId};
|
use objc2::rc::{Retained, WeakId};
|
||||||
use objc2::runtime::{AnyObject, Sel};
|
use objc2::runtime::{AnyObject, Sel};
|
||||||
use objc2::{
|
use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||||
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
|
|
||||||
};
|
|
||||||
use objc2_app_kit::{
|
use objc2_app_kit::{
|
||||||
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient,
|
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient,
|
||||||
NSTrackingRectTag, NSView,
|
NSTrackingRectTag, NSView, NSViewFrameDidChangeNotification,
|
||||||
};
|
};
|
||||||
use objc2_foundation::{
|
use objc2_foundation::{
|
||||||
MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying,
|
MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying,
|
||||||
NSMutableAttributedString, NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect, NSSize,
|
NSMutableAttributedString, NSNotFound, NSNotificationCenter, NSObject, NSObjectProtocol,
|
||||||
NSString, NSUInteger,
|
NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::app_delegate::ApplicationDelegate;
|
use super::app_state::ApplicationDelegate;
|
||||||
use super::cursor::{default_cursor, invisible_cursor};
|
use super::cursor::{default_cursor, invisible_cursor};
|
||||||
use super::event::{
|
use super::event::{
|
||||||
code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed,
|
code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed,
|
||||||
scancode_to_physicalkey,
|
scancode_to_physicalkey,
|
||||||
};
|
};
|
||||||
use super::window::WinitWindow;
|
use super::window::WinitWindow;
|
||||||
use super::{util, DEVICE_ID};
|
use super::DEVICE_ID;
|
||||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||||
use crate::event::{
|
use crate::event::{
|
||||||
DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, TouchPhase,
|
DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, TouchPhase,
|
||||||
@@ -37,7 +35,7 @@ use crate::platform::macos::OptionAsAlt;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct CursorState {
|
struct CursorState {
|
||||||
visible: bool,
|
visible: bool,
|
||||||
cursor: Id<NSCursor>,
|
cursor: Retained<NSCursor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CursorState {
|
impl Default for CursorState {
|
||||||
@@ -110,8 +108,11 @@ fn get_left_modifier_code(key: &Key) -> KeyCode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug)]
|
||||||
pub struct ViewState {
|
pub struct ViewState {
|
||||||
|
/// Strong reference to the global application state.
|
||||||
|
app_delegate: Retained<ApplicationDelegate>,
|
||||||
|
|
||||||
cursor_state: RefCell<CursorState>,
|
cursor_state: RefCell<CursorState>,
|
||||||
ime_position: Cell<NSPoint>,
|
ime_position: Cell<NSPoint>,
|
||||||
ime_size: Cell<NSSize>,
|
ime_size: Cell<NSSize>,
|
||||||
@@ -130,7 +131,7 @@ pub struct ViewState {
|
|||||||
/// to the application, even during IME
|
/// to the application, even during IME
|
||||||
forward_key_to_app: Cell<bool>,
|
forward_key_to_app: Cell<bool>,
|
||||||
|
|
||||||
marked_text: RefCell<Id<NSMutableAttributedString>>,
|
marked_text: RefCell<Retained<NSMutableAttributedString>>,
|
||||||
accepts_first_mouse: bool,
|
accepts_first_mouse: bool,
|
||||||
|
|
||||||
// Weak reference because the window keeps a strong reference to the view
|
// Weak reference because the window keeps a strong reference to the view
|
||||||
@@ -199,19 +200,15 @@ declare_class!(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[method(drawRect:)]
|
#[method(drawRect:)]
|
||||||
fn draw_rect(&self, rect: NSRect) {
|
fn draw_rect(&self, _rect: NSRect) {
|
||||||
trace_scope!("drawRect:");
|
trace_scope!("drawRect:");
|
||||||
|
|
||||||
// It's a workaround for https://github.com/rust-windowing/winit/issues/2640, don't replace with `self.window_id()`.
|
// It's a workaround for https://github.com/rust-windowing/winit/issues/2640, don't replace with `self.window_id()`.
|
||||||
if let Some(window) = self.ivars()._ns_window.load() {
|
if let Some(window) = self.ivars()._ns_window.load() {
|
||||||
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
|
self.ivars().app_delegate.handle_redraw(window.id());
|
||||||
app_delegate.handle_redraw(window.id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::let_unit_value)]
|
// This is a direct subclass of NSView, no need to call superclass' drawRect:
|
||||||
unsafe {
|
|
||||||
let _: () = msg_send![super(self), drawRect: rect];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(acceptsFirstResponder)]
|
#[method(acceptsFirstResponder)]
|
||||||
@@ -224,7 +221,7 @@ declare_class!(
|
|||||||
// IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem
|
// IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem
|
||||||
// TODO: Add an API extension for using `NSTouchBar`
|
// TODO: Add an API extension for using `NSTouchBar`
|
||||||
#[method_id(touchBar)]
|
#[method_id(touchBar)]
|
||||||
fn touch_bar(&self) -> Option<Id<NSObject>> {
|
fn touch_bar(&self) -> Option<Retained<NSObject>> {
|
||||||
trace_scope!("touchBar");
|
trace_scope!("touchBar");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -257,33 +254,36 @@ declare_class!(
|
|||||||
if length > 0 {
|
if length > 0 {
|
||||||
NSRange::new(0, length)
|
NSRange::new(0, length)
|
||||||
} else {
|
} else {
|
||||||
util::EMPTY_RANGE
|
// Documented to return `{NSNotFound, 0}` if there is no marked range.
|
||||||
|
NSRange::new(NSNotFound as NSUInteger, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(selectedRange)]
|
#[method(selectedRange)]
|
||||||
fn selected_range(&self) -> NSRange {
|
fn selected_range(&self) -> NSRange {
|
||||||
trace_scope!("selectedRange");
|
trace_scope!("selectedRange");
|
||||||
util::EMPTY_RANGE
|
// Documented to return `{NSNotFound, 0}` if there is no selection.
|
||||||
|
NSRange::new(NSNotFound as NSUInteger, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(setMarkedText:selectedRange:replacementRange:)]
|
#[method(setMarkedText:selectedRange:replacementRange:)]
|
||||||
fn set_marked_text(
|
fn set_marked_text(
|
||||||
&self,
|
&self,
|
||||||
string: &NSObject,
|
string: &NSObject,
|
||||||
_selected_range: NSRange,
|
selected_range: NSRange,
|
||||||
_replacement_range: NSRange,
|
_replacement_range: NSRange,
|
||||||
) {
|
) {
|
||||||
|
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
|
||||||
trace_scope!("setMarkedText:selectedRange:replacementRange:");
|
trace_scope!("setMarkedText:selectedRange:replacementRange:");
|
||||||
|
|
||||||
// SAFETY: This method is guaranteed to get either a `NSString` or a `NSAttributedString`.
|
// SAFETY: This method is guaranteed to get either a `NSString` or a `NSAttributedString`.
|
||||||
let (marked_text, preedit_string) = if string.is_kind_of::<NSAttributedString>() {
|
let (marked_text, string) = if string.is_kind_of::<NSAttributedString>() {
|
||||||
let string: *const NSObject = string;
|
let string: *const NSObject = string;
|
||||||
let string: *const NSAttributedString = string.cast();
|
let string: *const NSAttributedString = string.cast();
|
||||||
let string = unsafe { &*string };
|
let string = unsafe { &*string };
|
||||||
(
|
(
|
||||||
NSMutableAttributedString::from_attributed_nsstring(string),
|
NSMutableAttributedString::from_attributed_nsstring(string),
|
||||||
string.string().to_string(),
|
string.string(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let string: *const NSObject = string;
|
let string: *const NSObject = string;
|
||||||
@@ -291,7 +291,7 @@ declare_class!(
|
|||||||
let string = unsafe { &*string };
|
let string = unsafe { &*string };
|
||||||
(
|
(
|
||||||
NSMutableAttributedString::from_nsstring(string),
|
NSMutableAttributedString::from_nsstring(string),
|
||||||
string.to_string(),
|
string.copy(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -311,16 +311,21 @@ declare_class!(
|
|||||||
self.ivars().ime_state.set(ImeState::Ground);
|
self.ivars().ime_state.set(ImeState::Ground);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty string basically means that there's no preedit, so indicate that by sending
|
let cursor_range = if string.is_empty() {
|
||||||
// `None` cursor range.
|
// An empty string basically means that there's no preedit, so indicate that by
|
||||||
let cursor_range = if preedit_string.is_empty() {
|
// sending a `None` cursor range.
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some((preedit_string.len(), preedit_string.len()))
|
// Convert the selected range from UTF-16 indices to UTF-8 indices.
|
||||||
|
let sub_string_a = unsafe { string.substringToIndex(selected_range.location) };
|
||||||
|
let sub_string_b = unsafe { string.substringToIndex(selected_range.end()) };
|
||||||
|
let lowerbound_utf8 = sub_string_a.len();
|
||||||
|
let upperbound_utf8 = sub_string_b.len();
|
||||||
|
Some((lowerbound_utf8, upperbound_utf8))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send WindowEvent for updating marked text
|
// Send WindowEvent for updating marked text
|
||||||
self.queue_event(WindowEvent::Ime(Ime::Preedit(preedit_string, cursor_range)));
|
self.queue_event(WindowEvent::Ime(Ime::Preedit(string.to_string(), cursor_range)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(unmarkText)]
|
#[method(unmarkText)]
|
||||||
@@ -341,7 +346,7 @@ declare_class!(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[method_id(validAttributesForMarkedText)]
|
#[method_id(validAttributesForMarkedText)]
|
||||||
fn valid_attributes_for_marked_text(&self) -> Id<NSArray<NSAttributedStringKey>> {
|
fn valid_attributes_for_marked_text(&self) -> Retained<NSArray<NSAttributedStringKey>> {
|
||||||
trace_scope!("validAttributesForMarkedText");
|
trace_scope!("validAttributesForMarkedText");
|
||||||
NSArray::new()
|
NSArray::new()
|
||||||
}
|
}
|
||||||
@@ -351,7 +356,7 @@ declare_class!(
|
|||||||
&self,
|
&self,
|
||||||
_range: NSRange,
|
_range: NSRange,
|
||||||
_actual_range: *mut NSRange,
|
_actual_range: *mut NSRange,
|
||||||
) -> Option<Id<NSAttributedString>> {
|
) -> Option<Retained<NSAttributedString>> {
|
||||||
trace_scope!("attributedSubstringForProposedRange:actualRange:");
|
trace_scope!("attributedSubstringForProposedRange:actualRange:");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -380,6 +385,7 @@ declare_class!(
|
|||||||
|
|
||||||
#[method(insertText:replacementRange:)]
|
#[method(insertText:replacementRange:)]
|
||||||
fn insert_text(&self, string: &NSObject, _replacement_range: NSRange) {
|
fn insert_text(&self, string: &NSObject, _replacement_range: NSRange) {
|
||||||
|
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
|
||||||
trace_scope!("insertText:replacementRange:");
|
trace_scope!("insertText:replacementRange:");
|
||||||
|
|
||||||
// SAFETY: This method is guaranteed to get either a `NSString` or a `NSAttributedString`.
|
// SAFETY: This method is guaranteed to get either a `NSString` or a `NSAttributedString`.
|
||||||
@@ -680,7 +686,7 @@ declare_class!(
|
|||||||
|
|
||||||
self.update_modifiers(event, false);
|
self.update_modifiers(event, false);
|
||||||
|
|
||||||
self.queue_device_event(DeviceEvent::MouseWheel { delta });
|
self.ivars().app_delegate.maybe_queue_device_event(DeviceEvent::MouseWheel { delta });
|
||||||
self.queue_event(WindowEvent::MouseWheel {
|
self.queue_event(WindowEvent::MouseWheel {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
delta,
|
delta,
|
||||||
@@ -773,34 +779,40 @@ declare_class!(
|
|||||||
|
|
||||||
impl WinitView {
|
impl WinitView {
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
|
app_delegate: &ApplicationDelegate,
|
||||||
window: &WinitWindow,
|
window: &WinitWindow,
|
||||||
accepts_first_mouse: bool,
|
accepts_first_mouse: bool,
|
||||||
option_as_alt: OptionAsAlt,
|
option_as_alt: OptionAsAlt,
|
||||||
) -> Id<Self> {
|
) -> Retained<Self> {
|
||||||
let mtm = MainThreadMarker::from(window);
|
let mtm = MainThreadMarker::from(window);
|
||||||
let this = mtm.alloc().set_ivars(ViewState {
|
let this = mtm.alloc().set_ivars(ViewState {
|
||||||
|
app_delegate: app_delegate.retain(),
|
||||||
|
cursor_state: Default::default(),
|
||||||
|
ime_position: Default::default(),
|
||||||
|
ime_size: Default::default(),
|
||||||
|
modifiers: Default::default(),
|
||||||
|
phys_modifiers: Default::default(),
|
||||||
|
tracking_rect: Default::default(),
|
||||||
|
ime_state: Default::default(),
|
||||||
|
input_source: Default::default(),
|
||||||
|
ime_allowed: Default::default(),
|
||||||
|
forward_key_to_app: Default::default(),
|
||||||
|
marked_text: Default::default(),
|
||||||
accepts_first_mouse,
|
accepts_first_mouse,
|
||||||
_ns_window: WeakId::new(&window.retain()),
|
_ns_window: WeakId::new(&window.retain()),
|
||||||
option_as_alt: Cell::new(option_as_alt),
|
option_as_alt: Cell::new(option_as_alt),
|
||||||
..Default::default()
|
|
||||||
});
|
});
|
||||||
let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
|
let this: Retained<Self> = unsafe { msg_send_id![super(this), init] };
|
||||||
|
|
||||||
this.setPostsFrameChangedNotifications(true);
|
this.setPostsFrameChangedNotifications(true);
|
||||||
let notification_center: &AnyObject =
|
let notification_center = unsafe { NSNotificationCenter::defaultCenter() };
|
||||||
unsafe { msg_send![class!(NSNotificationCenter), defaultCenter] };
|
|
||||||
// About frame change
|
|
||||||
let frame_did_change_notification_name =
|
|
||||||
NSString::from_str("NSViewFrameDidChangeNotification");
|
|
||||||
#[allow(clippy::let_unit_value)]
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![
|
notification_center.addObserver_selector_name_object(
|
||||||
notification_center,
|
&this,
|
||||||
addObserver: &*this,
|
sel!(frameDidChange:),
|
||||||
selector: sel!(frameDidChange:),
|
Some(NSViewFrameDidChangeNotification),
|
||||||
name: &*frame_did_change_notification_name,
|
Some(&this),
|
||||||
object: &*this,
|
)
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*this.ivars().input_source.borrow_mut() = this.current_input_source();
|
*this.ivars().input_source.borrow_mut() = this.current_input_source();
|
||||||
@@ -808,7 +820,7 @@ impl WinitView {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window(&self) -> Id<WinitWindow> {
|
fn window(&self) -> Retained<WinitWindow> {
|
||||||
// TODO: Simply use `window` property on `NSView`.
|
// TODO: Simply use `window` property on `NSView`.
|
||||||
// That only returns a window _after_ the view has been attached though!
|
// That only returns a window _after_ the view has been attached though!
|
||||||
// (which is incompatible with `frameDidChange:`)
|
// (which is incompatible with `frameDidChange:`)
|
||||||
@@ -818,13 +830,7 @@ impl WinitView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn queue_event(&self, event: WindowEvent) {
|
fn queue_event(&self, event: WindowEvent) {
|
||||||
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
|
self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event);
|
||||||
app_delegate.queue_window_event(self.window().id(), event);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn queue_device_event(&self, event: DeviceEvent) {
|
|
||||||
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
|
|
||||||
app_delegate.queue_device_event(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale_factor(&self) -> f64 {
|
fn scale_factor(&self) -> f64 {
|
||||||
@@ -843,11 +849,11 @@ impl WinitView {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn cursor_icon(&self) -> Id<NSCursor> {
|
pub(super) fn cursor_icon(&self) -> Retained<NSCursor> {
|
||||||
self.ivars().cursor_state.borrow().cursor.clone()
|
self.ivars().cursor_state.borrow().cursor.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_cursor_icon(&self, icon: Id<NSCursor>) {
|
pub(super) fn set_cursor_icon(&self, icon: Retained<NSCursor>) {
|
||||||
let mut cursor_state = self.ivars().cursor_state.borrow_mut();
|
let mut cursor_state = self.ivars().cursor_state.borrow_mut();
|
||||||
cursor_state.cursor = icon;
|
cursor_state.cursor = icon;
|
||||||
}
|
}
|
||||||
@@ -1080,7 +1086,7 @@ fn mouse_button(event: &NSEvent) -> MouseButton {
|
|||||||
// NOTE: to get option as alt working we need to rewrite events
|
// NOTE: to get option as alt working we need to rewrite events
|
||||||
// we're getting from the operating system, which makes it
|
// we're getting from the operating system, which makes it
|
||||||
// impossible to provide such events as extra in `KeyEvent`.
|
// impossible to provide such events as extra in `KeyEvent`.
|
||||||
fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Id<NSEvent> {
|
fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Retained<NSEvent> {
|
||||||
let ev_mods = event_mods(event).state;
|
let ev_mods = event_mods(event).state;
|
||||||
let ignore_alt_characters = match option_as_alt {
|
let ignore_alt_characters = match option_as_alt {
|
||||||
OptionAsAlt::OnlyLeft if lalt_pressed(event) => true,
|
OptionAsAlt::OnlyLeft if lalt_pressed(event) => true,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#![allow(clippy::unnecessary_cast)]
|
#![allow(clippy::unnecessary_cast)]
|
||||||
|
|
||||||
use objc2::rc::{autoreleasepool, Id};
|
use objc2::rc::{autoreleasepool, Retained};
|
||||||
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
||||||
use objc2_app_kit::{NSResponder, NSWindow};
|
use objc2_app_kit::{NSResponder, NSWindow};
|
||||||
use objc2_foundation::{MainThreadBound, MainThreadMarker, NSObject};
|
use objc2_foundation::{MainThreadBound, MainThreadMarker, NSObject};
|
||||||
@@ -11,9 +11,9 @@ use crate::error::OsError as RootOsError;
|
|||||||
use crate::window::WindowAttributes;
|
use crate::window::WindowAttributes;
|
||||||
|
|
||||||
pub(crate) struct Window {
|
pub(crate) struct Window {
|
||||||
window: MainThreadBound<Id<WinitWindow>>,
|
window: MainThreadBound<Retained<WinitWindow>>,
|
||||||
/// The window only keeps a weak reference to this, so we must keep it around here.
|
/// The window only keeps a weak reference to this, so we must keep it around here.
|
||||||
delegate: MainThreadBound<Id<WindowDelegate>>,
|
delegate: MainThreadBound<Retained<WindowDelegate>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Window {
|
impl Drop for Window {
|
||||||
@@ -28,7 +28,9 @@ impl Window {
|
|||||||
attributes: WindowAttributes,
|
attributes: WindowAttributes,
|
||||||
) -> Result<Self, RootOsError> {
|
) -> Result<Self, RootOsError> {
|
||||||
let mtm = window_target.mtm;
|
let mtm = window_target.mtm;
|
||||||
let delegate = autoreleasepool(|_| WindowDelegate::new(attributes, mtm))?;
|
let delegate = autoreleasepool(|_| {
|
||||||
|
WindowDelegate::new(window_target.app_delegate(), attributes, mtm)
|
||||||
|
})?;
|
||||||
Ok(Window {
|
Ok(Window {
|
||||||
window: MainThreadBound::new(delegate.window().retain(), mtm),
|
window: MainThreadBound::new(delegate.window().retain(), mtm),
|
||||||
delegate: MainThreadBound::new(delegate, mtm),
|
delegate: MainThreadBound::new(delegate, mtm),
|
||||||
@@ -72,7 +74,7 @@ impl Window {
|
|||||||
pub struct WindowId(pub usize);
|
pub struct WindowId(pub usize);
|
||||||
|
|
||||||
impl WindowId {
|
impl WindowId {
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
Self(0)
|
Self(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,41 @@
|
|||||||
#![allow(clippy::unnecessary_cast)]
|
#![allow(clippy::unnecessary_cast)]
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use core_graphics::display::{CGDisplay, CGPoint};
|
use core_graphics::display::{CGDisplay, CGPoint};
|
||||||
use monitor::VideoModeHandle;
|
use monitor::VideoModeHandle;
|
||||||
use objc2::rc::{autoreleasepool, Id};
|
use objc2::rc::{autoreleasepool, Retained};
|
||||||
use objc2::runtime::{AnyObject, ProtocolObject};
|
use objc2::runtime::{AnyObject, ProtocolObject};
|
||||||
use objc2::{
|
use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||||
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
|
|
||||||
};
|
|
||||||
use objc2_app_kit::{
|
use objc2_app_kit::{
|
||||||
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSApplication,
|
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization,
|
||||||
NSApplicationPresentationOptions, NSBackingStoreType, NSColor, NSDraggingDestination,
|
NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType,
|
||||||
NSFilenamesPboardType, NSPasteboard, NSRequestUserAttentionType, NSScreen, NSView,
|
NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard,
|
||||||
NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel,
|
NSRequestUserAttentionType, NSScreen, NSView, NSWindowButton, NSWindowDelegate,
|
||||||
NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask,
|
NSWindowFullScreenButton, NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode,
|
||||||
NSWindowTabbingMode, NSWindowTitleVisibility,
|
NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility,
|
||||||
};
|
};
|
||||||
use objc2_foundation::{
|
use objc2_foundation::{
|
||||||
CGFloat, MainThreadMarker, NSArray, NSCopying, NSObject, NSObjectProtocol, NSPoint, NSRect,
|
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSKeyValueChangeKey,
|
||||||
NSSize, NSString,
|
NSKeyValueChangeNewKey, NSKeyValueChangeOldKey, NSKeyValueObservingOptions, NSObject,
|
||||||
|
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
|
||||||
|
NSRect, NSSize, NSString,
|
||||||
};
|
};
|
||||||
|
use tracing::{trace, warn};
|
||||||
|
|
||||||
use super::app_delegate::ApplicationDelegate;
|
use super::app_state::ApplicationDelegate;
|
||||||
use super::cursor::cursor_from_icon;
|
use super::cursor::cursor_from_icon;
|
||||||
use super::monitor::{self, flip_window_screen_coordinates, get_display_id};
|
use super::monitor::{self, flip_window_screen_coordinates, get_display_id};
|
||||||
|
use super::observer::RunLoop;
|
||||||
use super::view::WinitView;
|
use super::view::WinitView;
|
||||||
use super::window::WinitWindow;
|
use super::window::WinitWindow;
|
||||||
use super::{ffi, Fullscreen, MonitorHandle, OsError, WindowId};
|
use super::{ffi, Fullscreen, MonitorHandle, OsError, WindowId};
|
||||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||||
use crate::event::WindowEvent;
|
use crate::event::{InnerSizeWriter, WindowEvent};
|
||||||
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||||
use crate::window::{
|
use crate::window::{
|
||||||
Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||||
@@ -74,9 +78,10 @@ impl Default for PlatformSpecificWindowAttributes {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct State {
|
pub(crate) struct State {
|
||||||
window: Id<WinitWindow>,
|
/// Strong reference to the global application state.
|
||||||
|
app_delegate: Retained<ApplicationDelegate>,
|
||||||
|
|
||||||
current_theme: Cell<Option<Theme>>,
|
window: Retained<WinitWindow>,
|
||||||
|
|
||||||
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
||||||
//
|
//
|
||||||
@@ -183,7 +188,17 @@ declare_class!(
|
|||||||
#[method(windowDidChangeBackingProperties:)]
|
#[method(windowDidChangeBackingProperties:)]
|
||||||
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
|
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidChangeBackingProperties:");
|
trace_scope!("windowDidChangeBackingProperties:");
|
||||||
self.queue_static_scale_factor_changed_event();
|
let scale_factor = self.scale_factor();
|
||||||
|
if scale_factor == self.ivars().previous_scale_factor.get() {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.ivars().previous_scale_factor.set(scale_factor);
|
||||||
|
|
||||||
|
let mtm = MainThreadMarker::from(self);
|
||||||
|
let this = self.retain();
|
||||||
|
RunLoop::main(mtm).queue_closure(move || {
|
||||||
|
this.handle_scale_factor_changed(scale_factor);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[method(windowDidBecomeKey:)]
|
#[method(windowDidBecomeKey:)]
|
||||||
@@ -230,7 +245,7 @@ declare_class!(
|
|||||||
None => {
|
None => {
|
||||||
let current_monitor = self.current_monitor_inner();
|
let current_monitor = self.current_monitor_inner();
|
||||||
*fullscreen = Some(Fullscreen::Borderless(current_monitor));
|
*fullscreen = Some(Fullscreen::Borderless(current_monitor));
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
self.ivars().in_fullscreen_transition.set(true);
|
self.ivars().in_fullscreen_transition.set(true);
|
||||||
}
|
}
|
||||||
@@ -261,11 +276,9 @@ declare_class!(
|
|||||||
let mut options = proposed_options;
|
let mut options = proposed_options;
|
||||||
let fullscreen = self.ivars().fullscreen.borrow();
|
let fullscreen = self.ivars().fullscreen.borrow();
|
||||||
if let Some(Fullscreen::Exclusive(_)) = &*fullscreen {
|
if let Some(Fullscreen::Exclusive(_)) = &*fullscreen {
|
||||||
options = NSApplicationPresentationOptions(
|
options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||||
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen.0
|
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock.0
|
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar.0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
options
|
options
|
||||||
@@ -316,14 +329,12 @@ declare_class!(
|
|||||||
self.ivars().in_fullscreen_transition.set(false);
|
self.ivars().in_fullscreen_transition.set(false);
|
||||||
self.ivars().target_fullscreen.replace(None);
|
self.ivars().target_fullscreen.replace(None);
|
||||||
if self.ivars().initial_fullscreen.get() {
|
if self.ivars().initial_fullscreen.get() {
|
||||||
#[allow(clippy::let_unit_value)]
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![
|
self.window().performSelector_withObject_afterDelay(
|
||||||
self.window(),
|
sel!(toggleFullScreen:),
|
||||||
performSelector: sel!(toggleFullScreen:),
|
None,
|
||||||
withObject: ptr::null::<AnyObject>(),
|
0.5,
|
||||||
afterDelay: 0.5,
|
)
|
||||||
];
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
self.restore_state_from_fullscreen();
|
self.restore_state_from_fullscreen();
|
||||||
@@ -334,8 +345,7 @@ declare_class!(
|
|||||||
#[method(windowDidChangeOcclusionState:)]
|
#[method(windowDidChangeOcclusionState:)]
|
||||||
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
|
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidChangeOcclusionState:");
|
trace_scope!("windowDidChangeOcclusionState:");
|
||||||
let visible = self.window().occlusionState().0 & NSWindowOcclusionState::Visible.0
|
let visible = self.window().occlusionState().contains(NSWindowOcclusionState::Visible);
|
||||||
== NSWindowOcclusionState::Visible.0;
|
|
||||||
self.queue_event(WindowEvent::Occluded(!visible));
|
self.queue_event(WindowEvent::Occluded(!visible));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,11 +369,9 @@ declare_class!(
|
|||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||||
let filenames = pb
|
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
|
||||||
.propertyListForType(unsafe { NSFilenamesPboardType })
|
let filenames: Retained<NSArray<NSString>> = unsafe { Retained::cast(filenames) };
|
||||||
.unwrap();
|
|
||||||
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
|
|
||||||
|
|
||||||
filenames.into_iter().for_each(|file| {
|
filenames.into_iter().for_each(|file| {
|
||||||
let path = PathBuf::from(file.to_string());
|
let path = PathBuf::from(file.to_string());
|
||||||
@@ -387,11 +395,9 @@ declare_class!(
|
|||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||||
let filenames = pb
|
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
|
||||||
.propertyListForType(unsafe { NSFilenamesPboardType })
|
let filenames: Retained<NSArray<NSString>> = unsafe { Retained::cast(filenames) };
|
||||||
.unwrap();
|
|
||||||
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
|
|
||||||
|
|
||||||
filenames.into_iter().for_each(|file| {
|
filenames.into_iter().for_each(|file| {
|
||||||
let path = PathBuf::from(file.to_string());
|
let path = PathBuf::from(file.to_string());
|
||||||
@@ -415,34 +421,71 @@ declare_class!(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Key-Value Observing
|
||||||
unsafe impl WindowDelegate {
|
unsafe impl WindowDelegate {
|
||||||
// Observe theme change
|
#[method(observeValueForKeyPath:ofObject:change:context:)]
|
||||||
#[method(effectiveAppearanceDidChange:)]
|
fn observe_value(
|
||||||
fn effective_appearance_did_change(this: *mut Self, sender: Option<&AnyObject>) {
|
&self,
|
||||||
trace_scope!("effectiveAppearanceDidChange:");
|
key_path: Option<&NSString>,
|
||||||
unsafe {
|
_object: Option<&AnyObject>,
|
||||||
msg_send![
|
change: Option<&NSDictionary<NSKeyValueChangeKey, AnyObject>>,
|
||||||
this,
|
_context: *mut c_void,
|
||||||
performSelectorOnMainThread: sel!(effectiveAppearanceDidChangedOnMainThread:),
|
) {
|
||||||
withObject: sender,
|
trace_scope!("observeValueForKeyPath:ofObject:change:context:");
|
||||||
waitUntilDone: false,
|
// NOTE: We don't _really_ need to check the key path, as there should only be one, but
|
||||||
]
|
// in the future we might want to observe other key paths.
|
||||||
}
|
if key_path == Some(ns_string!("effectiveAppearance")) {
|
||||||
}
|
let change = change.expect("requested a change dictionary in `addObserver`, but none was provided");
|
||||||
|
let old = change.get(unsafe { NSKeyValueChangeOldKey }).expect("requested change dictionary did not contain `NSKeyValueChangeOldKey`");
|
||||||
|
let new = change.get(unsafe { NSKeyValueChangeNewKey }).expect("requested change dictionary did not contain `NSKeyValueChangeNewKey`");
|
||||||
|
|
||||||
#[method(effectiveAppearanceDidChangedOnMainThread:)]
|
// SAFETY: The value of `effectiveAppearance` is `NSAppearance`
|
||||||
fn effective_appearance_did_changed_on_main_thread(&self, _: Option<&AnyObject>) {
|
let old: *const AnyObject = old;
|
||||||
let mtm = MainThreadMarker::from(self);
|
let old: *const NSAppearance = old.cast();
|
||||||
let theme = get_ns_theme(mtm);
|
let old: &NSAppearance = unsafe { &*old };
|
||||||
let old_theme = self.ivars().current_theme.replace(Some(theme));
|
let new: *const AnyObject = new;
|
||||||
if old_theme != Some(theme) {
|
let new: *const NSAppearance = new.cast();
|
||||||
self.queue_event(WindowEvent::ThemeChanged(theme));
|
let new: &NSAppearance = unsafe { &*new };
|
||||||
|
|
||||||
|
trace!(old = %unsafe { old.name() }, new = %unsafe { new.name() }, "effectiveAppearance changed");
|
||||||
|
|
||||||
|
// Ignore the change if the window's theme is customized by the user (since in that
|
||||||
|
// case the `effectiveAppearance` is only emitted upon said customization, and then
|
||||||
|
// it's triggered directly by a user action, and we don't want to emit the event).
|
||||||
|
if unsafe { self.window().appearance() }.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let old = appearance_to_theme(old);
|
||||||
|
let new = appearance_to_theme(new);
|
||||||
|
// Check that the theme changed in Winit's terms (the theme might have changed on
|
||||||
|
// other parameters, such as level of contrast, but the event should not be emitted
|
||||||
|
// in those cases).
|
||||||
|
if old == new {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.queue_event(WindowEvent::ThemeChanged(new));
|
||||||
|
} else {
|
||||||
|
panic!("unknown observed keypath {key_path:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
fn new_window(attrs: &WindowAttributes, mtm: MainThreadMarker) -> Option<Id<WinitWindow>> {
|
impl Drop for WindowDelegate {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self.window().removeObserver_forKeyPath(self, ns_string!("effectiveAppearance"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_window(
|
||||||
|
app_delegate: &ApplicationDelegate,
|
||||||
|
attrs: &WindowAttributes,
|
||||||
|
mtm: MainThreadMarker,
|
||||||
|
) -> Option<Retained<WinitWindow>> {
|
||||||
autoreleasepool(|_| {
|
autoreleasepool(|_| {
|
||||||
let screen = match attrs.fullscreen.clone().map(Into::into) {
|
let screen = match attrs.fullscreen.clone().map(Into::into) {
|
||||||
Some(Fullscreen::Borderless(Some(monitor)))
|
Some(Fullscreen::Borderless(Some(monitor)))
|
||||||
@@ -487,38 +530,38 @@ fn new_window(attrs: &WindowAttributes, mtm: MainThreadMarker) -> Option<Id<Wini
|
|||||||
// if decorations is set to false, ignore pl_attrs
|
// if decorations is set to false, ignore pl_attrs
|
||||||
//
|
//
|
||||||
// if the titlebar is hidden, ignore other pl_attrs
|
// if the titlebar is hidden, ignore other pl_attrs
|
||||||
NSWindowStyleMask::Borderless.0
|
NSWindowStyleMask::Borderless
|
||||||
| NSWindowStyleMask::Resizable.0
|
| NSWindowStyleMask::Resizable
|
||||||
| NSWindowStyleMask::Miniaturizable.0
|
| NSWindowStyleMask::Miniaturizable
|
||||||
} else {
|
} else {
|
||||||
// default case, resizable window with titlebar and titlebar buttons
|
// default case, resizable window with titlebar and titlebar buttons
|
||||||
NSWindowStyleMask::Closable.0
|
NSWindowStyleMask::Closable
|
||||||
| NSWindowStyleMask::Miniaturizable.0
|
| NSWindowStyleMask::Miniaturizable
|
||||||
| NSWindowStyleMask::Resizable.0
|
| NSWindowStyleMask::Resizable
|
||||||
| NSWindowStyleMask::Titled.0
|
| NSWindowStyleMask::Titled
|
||||||
};
|
};
|
||||||
|
|
||||||
if !attrs.resizable {
|
if !attrs.resizable {
|
||||||
masks &= !NSWindowStyleMask::Resizable.0;
|
masks &= !NSWindowStyleMask::Resizable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !attrs.enabled_buttons.contains(WindowButtons::MINIMIZE) {
|
if !attrs.enabled_buttons.contains(WindowButtons::MINIMIZE) {
|
||||||
masks &= !NSWindowStyleMask::Miniaturizable.0;
|
masks &= !NSWindowStyleMask::Miniaturizable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !attrs.enabled_buttons.contains(WindowButtons::CLOSE) {
|
if !attrs.enabled_buttons.contains(WindowButtons::CLOSE) {
|
||||||
masks &= !NSWindowStyleMask::Closable.0;
|
masks &= !NSWindowStyleMask::Closable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if attrs.platform_specific.fullsize_content_view {
|
if attrs.platform_specific.fullsize_content_view {
|
||||||
masks |= NSWindowStyleMask::FullSizeContentView.0;
|
masks |= NSWindowStyleMask::FullSizeContentView;
|
||||||
}
|
}
|
||||||
|
|
||||||
let window: Option<Id<WinitWindow>> = unsafe {
|
let window: Option<Retained<WinitWindow>> = unsafe {
|
||||||
msg_send_id![
|
msg_send_id![
|
||||||
super(mtm.alloc().set_ivars(())),
|
super(mtm.alloc().set_ivars(())),
|
||||||
initWithContentRect: frame,
|
initWithContentRect: frame,
|
||||||
styleMask: NSWindowStyleMask(masks),
|
styleMask: masks,
|
||||||
backing: NSBackingStoreType::NSBackingStoreBuffered,
|
backing: NSBackingStoreType::NSBackingStoreBuffered,
|
||||||
defer: false,
|
defer: false,
|
||||||
]
|
]
|
||||||
@@ -579,6 +622,7 @@ fn new_window(attrs: &WindowAttributes, mtm: MainThreadMarker) -> Option<Id<Wini
|
|||||||
}
|
}
|
||||||
|
|
||||||
let view = WinitView::new(
|
let view = WinitView::new(
|
||||||
|
app_delegate,
|
||||||
&window,
|
&window,
|
||||||
attrs.platform_specific.accepts_first_mouse,
|
attrs.platform_specific.accepts_first_mouse,
|
||||||
attrs.platform_specific.option_as_alt,
|
attrs.platform_specific.option_as_alt,
|
||||||
@@ -605,7 +649,8 @@ fn new_window(attrs: &WindowAttributes, mtm: MainThreadMarker) -> Option<Id<Wini
|
|||||||
|
|
||||||
if attrs.transparent {
|
if attrs.transparent {
|
||||||
window.setOpaque(false);
|
window.setOpaque(false);
|
||||||
window.setBackgroundColor(Some(unsafe { &NSColor::clearColor() }));
|
// See `set_transparent` for details on why we do this.
|
||||||
|
window.setBackgroundColor(unsafe { Some(&NSColor::clearColor()) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// register for drag and drop operations.
|
// register for drag and drop operations.
|
||||||
@@ -619,8 +664,12 @@ fn new_window(attrs: &WindowAttributes, mtm: MainThreadMarker) -> Option<Id<Wini
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WindowDelegate {
|
impl WindowDelegate {
|
||||||
pub fn new(attrs: WindowAttributes, mtm: MainThreadMarker) -> Result<Id<Self>, RootOsError> {
|
pub(super) fn new(
|
||||||
let window = new_window(&attrs, mtm)
|
app_delegate: &ApplicationDelegate,
|
||||||
|
attrs: WindowAttributes,
|
||||||
|
mtm: MainThreadMarker,
|
||||||
|
) -> Result<Retained<Self>, RootOsError> {
|
||||||
|
let window = new_window(app_delegate, &attrs, mtm)
|
||||||
.ok_or_else(|| os_error!(OsError::CreationError("couldn't create `NSWindow`")))?;
|
.ok_or_else(|| os_error!(OsError::CreationError("couldn't create `NSWindow`")))?;
|
||||||
|
|
||||||
#[cfg(feature = "rwh_06")]
|
#[cfg(feature = "rwh_06")]
|
||||||
@@ -628,8 +677,8 @@ impl WindowDelegate {
|
|||||||
Some(rwh_06::RawWindowHandle::AppKit(handle)) => {
|
Some(rwh_06::RawWindowHandle::AppKit(handle)) => {
|
||||||
// SAFETY: Caller ensures the pointer is valid or NULL
|
// SAFETY: Caller ensures the pointer is valid or NULL
|
||||||
// Unwrap is fine, since the pointer comes from `NonNull`.
|
// Unwrap is fine, since the pointer comes from `NonNull`.
|
||||||
let parent_view: Id<NSView> =
|
let parent_view: Retained<NSView> =
|
||||||
unsafe { Id::retain(handle.ns_view.as_ptr().cast()) }.unwrap();
|
unsafe { Retained::retain(handle.ns_view.as_ptr().cast()) }.unwrap();
|
||||||
let parent = parent_view.window().ok_or_else(|| {
|
let parent = parent_view.window().ok_or_else(|| {
|
||||||
os_error!(OsError::CreationError("parent view should be installed in a window"))
|
os_error!(OsError::CreationError("parent view should be installed in a window"))
|
||||||
})?;
|
})?;
|
||||||
@@ -655,14 +704,13 @@ impl WindowDelegate {
|
|||||||
|
|
||||||
let scale_factor = window.backingScaleFactor() as _;
|
let scale_factor = window.backingScaleFactor() as _;
|
||||||
|
|
||||||
let current_theme = match attrs.preferred_theme {
|
if let Some(appearance) = theme_to_appearance(attrs.preferred_theme) {
|
||||||
Some(theme) => Some(theme),
|
unsafe { window.setAppearance(Some(&appearance)) };
|
||||||
None => Some(get_ns_theme(mtm)),
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let delegate = mtm.alloc().set_ivars(State {
|
let delegate = mtm.alloc().set_ivars(State {
|
||||||
|
app_delegate: app_delegate.retain(),
|
||||||
window: window.retain(),
|
window: window.retain(),
|
||||||
current_theme: Cell::new(current_theme),
|
|
||||||
previous_position: Cell::new(None),
|
previous_position: Cell::new(None),
|
||||||
previous_scale_factor: Cell::new(scale_factor),
|
previous_scale_factor: Cell::new(scale_factor),
|
||||||
resize_increments: Cell::new(resize_increments),
|
resize_increments: Cell::new(resize_increments),
|
||||||
@@ -678,25 +726,27 @@ impl WindowDelegate {
|
|||||||
is_simple_fullscreen: Cell::new(false),
|
is_simple_fullscreen: Cell::new(false),
|
||||||
saved_style: Cell::new(None),
|
saved_style: Cell::new(None),
|
||||||
});
|
});
|
||||||
let delegate: Id<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
|
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
|
||||||
|
|
||||||
if scale_factor != 1.0 {
|
if scale_factor != 1.0 {
|
||||||
delegate.queue_static_scale_factor_changed_event();
|
let delegate = delegate.clone();
|
||||||
|
RunLoop::main(mtm).queue_closure(move || {
|
||||||
|
delegate.handle_scale_factor_changed(scale_factor);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
window.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
window.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||||
|
|
||||||
// Enable theme change event
|
// Listen for theme change event.
|
||||||
let notification_center: Id<AnyObject> =
|
//
|
||||||
unsafe { msg_send_id![class!(NSDistributedNotificationCenter), defaultCenter] };
|
// SAFETY: The observer is un-registered in the `Drop` of the delegate.
|
||||||
let notification_name = NSString::from_str("AppleInterfaceThemeChangedNotification");
|
unsafe {
|
||||||
let _: () = unsafe {
|
window.addObserver_forKeyPath_options_context(
|
||||||
msg_send![
|
&delegate,
|
||||||
¬ification_center,
|
ns_string!("effectiveAppearance"),
|
||||||
addObserver: &*delegate,
|
NSKeyValueObservingOptions::NSKeyValueObservingOptionNew
|
||||||
selector: sel!(effectiveAppearanceDidChange:),
|
| NSKeyValueObservingOptions::NSKeyValueObservingOptionOld,
|
||||||
name: &*notification_name,
|
ptr::null_mut(),
|
||||||
object: ptr::null::<AnyObject>(),
|
)
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if attrs.blur {
|
if attrs.blur {
|
||||||
@@ -741,9 +791,9 @@ impl WindowDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(super) fn view(&self) -> Id<WinitView> {
|
pub(super) fn view(&self) -> Retained<WinitView> {
|
||||||
// SAFETY: The view inside WinitWindow is always `WinitView`
|
// SAFETY: The view inside WinitWindow is always `WinitView`
|
||||||
unsafe { Id::cast(self.window().contentView().unwrap()) }
|
unsafe { Retained::cast(self.window().contentView().unwrap()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@@ -757,26 +807,31 @@ impl WindowDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
||||||
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
|
self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event);
|
||||||
app_delegate.queue_window_event(self.window().id(), event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_static_scale_factor_changed_event(&self) {
|
fn handle_scale_factor_changed(&self, scale_factor: CGFloat) {
|
||||||
let scale_factor = self.scale_factor();
|
let app_delegate = &self.ivars().app_delegate;
|
||||||
if scale_factor == self.ivars().previous_scale_factor.get() {
|
let window = self.window();
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.ivars().previous_scale_factor.set(scale_factor);
|
let content_size = window.contentRectForFrameRect(window.frame()).size;
|
||||||
let content_size = self.window().contentRectForFrameRect(self.window().frame()).size;
|
|
||||||
let content_size = LogicalSize::new(content_size.width, content_size.height);
|
let content_size = LogicalSize::new(content_size.width, content_size.height);
|
||||||
|
|
||||||
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
|
let suggested_size = content_size.to_physical(scale_factor);
|
||||||
app_delegate.queue_static_scale_factor_changed_event(
|
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||||
self.window().retain(),
|
app_delegate.handle_window_event(window.id(), WindowEvent::ScaleFactorChanged {
|
||||||
content_size.to_physical(scale_factor),
|
|
||||||
scale_factor,
|
scale_factor,
|
||||||
);
|
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||||
|
});
|
||||||
|
let physical_size = *new_inner_size.lock().unwrap();
|
||||||
|
drop(new_inner_size);
|
||||||
|
|
||||||
|
if physical_size != suggested_size {
|
||||||
|
let logical_size = physical_size.to_logical(scale_factor);
|
||||||
|
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||||
|
window.setContentSize(size);
|
||||||
|
}
|
||||||
|
app_delegate.handle_window_event(window.id(), WindowEvent::Resized(physical_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_move_event(&self) {
|
fn emit_move_event(&self) {
|
||||||
@@ -804,7 +859,23 @@ impl WindowDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_transparent(&self, transparent: bool) {
|
pub fn set_transparent(&self, transparent: bool) {
|
||||||
self.window().setOpaque(!transparent)
|
// This is just a hint for Quartz, it doesn't actually speculate with window alpha.
|
||||||
|
// Providing a wrong value here could result in visual artifacts, when the window is
|
||||||
|
// transparent.
|
||||||
|
self.window().setOpaque(!transparent);
|
||||||
|
|
||||||
|
// AppKit draws the window with a background color by default, which is usually really
|
||||||
|
// nice, but gets in the way when we want to allow the contents of the window to be
|
||||||
|
// transparent, as in that case, the transparent contents will just be drawn on top of
|
||||||
|
// the background color. As such, to allow the window to be transparent, we must also set
|
||||||
|
// the background color to one with an empty alpha channel.
|
||||||
|
let color = if transparent {
|
||||||
|
unsafe { NSColor::clearColor() }
|
||||||
|
} else {
|
||||||
|
unsafe { NSColor::windowBackgroundColor() }
|
||||||
|
};
|
||||||
|
|
||||||
|
self.window().setBackgroundColor(Some(&color));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_blur(&self, blur: bool) {
|
pub fn set_blur(&self, blur: bool) {
|
||||||
@@ -834,8 +905,7 @@ impl WindowDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_redraw(&self) {
|
pub fn request_redraw(&self) {
|
||||||
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
|
self.ivars().app_delegate.queue_redraw(self.window().id());
|
||||||
app_delegate.queue_redraw(self.window().id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -904,8 +974,8 @@ impl WindowDelegate {
|
|||||||
|
|
||||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||||
let dimensions = dimensions.unwrap_or(Size::Logical(LogicalSize {
|
let dimensions = dimensions.unwrap_or(Size::Logical(LogicalSize {
|
||||||
width: std::f32::MAX as f64,
|
width: f32::MAX as f64,
|
||||||
height: std::f32::MAX as f64,
|
height: f32::MAX as f64,
|
||||||
}));
|
}));
|
||||||
let scale_factor = self.scale_factor();
|
let scale_factor = self.scale_factor();
|
||||||
let max_size = dimensions.to_logical::<CGFloat>(scale_factor);
|
let max_size = dimensions.to_logical::<CGFloat>(scale_factor);
|
||||||
@@ -959,13 +1029,13 @@ impl WindowDelegate {
|
|||||||
self.ivars().resizable.set(resizable);
|
self.ivars().resizable.set(resizable);
|
||||||
let fullscreen = self.ivars().fullscreen.borrow().is_some();
|
let fullscreen = self.ivars().fullscreen.borrow().is_some();
|
||||||
if !fullscreen {
|
if !fullscreen {
|
||||||
let mut mask = self.window().styleMask().0;
|
let mut mask = self.window().styleMask();
|
||||||
if resizable {
|
if resizable {
|
||||||
mask |= NSWindowStyleMask::Resizable.0;
|
mask |= NSWindowStyleMask::Resizable;
|
||||||
} else {
|
} else {
|
||||||
mask &= !NSWindowStyleMask::Resizable.0;
|
mask &= !NSWindowStyleMask::Resizable;
|
||||||
}
|
}
|
||||||
self.set_style_mask(NSWindowStyleMask(mask));
|
self.set_style_mask(mask);
|
||||||
}
|
}
|
||||||
// Otherwise, we don't change the mask until we exit fullscreen.
|
// Otherwise, we don't change the mask until we exit fullscreen.
|
||||||
}
|
}
|
||||||
@@ -977,23 +1047,23 @@ impl WindowDelegate {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||||
let mut mask = self.window().styleMask().0;
|
let mut mask = self.window().styleMask();
|
||||||
|
|
||||||
if buttons.contains(WindowButtons::CLOSE) {
|
if buttons.contains(WindowButtons::CLOSE) {
|
||||||
mask |= NSWindowStyleMask::Closable.0;
|
mask |= NSWindowStyleMask::Closable;
|
||||||
} else {
|
} else {
|
||||||
mask &= !NSWindowStyleMask::Closable.0;
|
mask &= !NSWindowStyleMask::Closable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if buttons.contains(WindowButtons::MINIMIZE) {
|
if buttons.contains(WindowButtons::MINIMIZE) {
|
||||||
mask |= NSWindowStyleMask::Miniaturizable.0;
|
mask |= NSWindowStyleMask::Miniaturizable;
|
||||||
} else {
|
} else {
|
||||||
mask &= !NSWindowStyleMask::Miniaturizable.0;
|
mask &= !NSWindowStyleMask::Miniaturizable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This must happen before the button's "enabled" status has been set,
|
// This must happen before the button's "enabled" status has been set,
|
||||||
// hence we do it synchronously.
|
// hence we do it synchronously.
|
||||||
self.set_style_mask(NSWindowStyleMask(mask));
|
self.set_style_mask(mask);
|
||||||
|
|
||||||
// We edit the button directly instead of using `NSResizableWindowMask`,
|
// We edit the button directly instead of using `NSResizableWindowMask`,
|
||||||
// since that mask also affect the resizability of the window (which is
|
// since that mask also affect the resizability of the window (which is
|
||||||
@@ -1114,9 +1184,8 @@ impl WindowDelegate {
|
|||||||
// we make it resizable temporarily.
|
// we make it resizable temporarily.
|
||||||
let curr_mask = self.window().styleMask();
|
let curr_mask = self.window().styleMask();
|
||||||
|
|
||||||
let required =
|
let required = NSWindowStyleMask::Titled | NSWindowStyleMask::Resizable;
|
||||||
NSWindowStyleMask(NSWindowStyleMask::Titled.0 | NSWindowStyleMask::Resizable.0);
|
let needs_temp_mask = !curr_mask.contains(required);
|
||||||
let needs_temp_mask = !mask_contains(curr_mask, required);
|
|
||||||
if needs_temp_mask {
|
if needs_temp_mask {
|
||||||
self.set_style_mask(required);
|
self.set_style_mask(required);
|
||||||
}
|
}
|
||||||
@@ -1133,12 +1202,12 @@ impl WindowDelegate {
|
|||||||
|
|
||||||
fn saved_style(&self) -> NSWindowStyleMask {
|
fn saved_style(&self) -> NSWindowStyleMask {
|
||||||
let base_mask =
|
let base_mask =
|
||||||
self.ivars().saved_style.take().unwrap_or_else(|| self.window().styleMask()).0;
|
self.ivars().saved_style.take().unwrap_or_else(|| self.window().styleMask());
|
||||||
NSWindowStyleMask(if self.ivars().resizable.get() {
|
if self.ivars().resizable.get() {
|
||||||
base_mask | NSWindowStyleMask::Resizable.0
|
base_mask | NSWindowStyleMask::Resizable
|
||||||
} else {
|
} else {
|
||||||
base_mask & !NSWindowStyleMask::Resizable.0
|
base_mask & !NSWindowStyleMask::Resizable
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is called when the window is exiting fullscreen, whether by the
|
/// This is called when the window is exiting fullscreen, whether by the
|
||||||
@@ -1193,7 +1262,7 @@ impl WindowDelegate {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if mask_contains(self.window().styleMask(), NSWindowStyleMask::Resizable) {
|
if self.window().styleMask().contains(NSWindowStyleMask::Resizable) {
|
||||||
// Just use the native zoom if resizable
|
// Just use the native zoom if resizable
|
||||||
self.window().zoom(None);
|
self.window().zoom(None);
|
||||||
} else {
|
} else {
|
||||||
@@ -1345,9 +1414,8 @@ impl WindowDelegate {
|
|||||||
// set a normal style temporarily. The previous state will be
|
// set a normal style temporarily. The previous state will be
|
||||||
// restored in `WindowDelegate::window_did_exit_fullscreen`.
|
// restored in `WindowDelegate::window_did_exit_fullscreen`.
|
||||||
let curr_mask = self.window().styleMask();
|
let curr_mask = self.window().styleMask();
|
||||||
let required =
|
let required = NSWindowStyleMask::Titled | NSWindowStyleMask::Resizable;
|
||||||
NSWindowStyleMask(NSWindowStyleMask::Titled.0 | NSWindowStyleMask::Resizable.0);
|
if !curr_mask.contains(required) {
|
||||||
if !mask_contains(curr_mask, required) {
|
|
||||||
self.set_style_mask(required);
|
self.set_style_mask(required);
|
||||||
self.ivars().saved_style.set(Some(curr_mask));
|
self.ivars().saved_style.set(Some(curr_mask));
|
||||||
}
|
}
|
||||||
@@ -1358,13 +1426,7 @@ impl WindowDelegate {
|
|||||||
toggle_fullscreen(self.window());
|
toggle_fullscreen(self.window());
|
||||||
},
|
},
|
||||||
(Some(Fullscreen::Exclusive(ref video_mode)), None) => {
|
(Some(Fullscreen::Exclusive(ref video_mode)), None) => {
|
||||||
unsafe {
|
restore_and_release_display(&video_mode.monitor());
|
||||||
ffi::CGRestorePermanentDisplayConfiguration();
|
|
||||||
assert_eq!(
|
|
||||||
ffi::CGDisplayRelease(video_mode.monitor().native_identifier()),
|
|
||||||
ffi::kCGErrorSuccess
|
|
||||||
);
|
|
||||||
};
|
|
||||||
toggle_fullscreen(self.window());
|
toggle_fullscreen(self.window());
|
||||||
},
|
},
|
||||||
(Some(Fullscreen::Borderless(_)), Some(Fullscreen::Exclusive(_))) => {
|
(Some(Fullscreen::Borderless(_)), Some(Fullscreen::Exclusive(_))) => {
|
||||||
@@ -1378,11 +1440,10 @@ impl WindowDelegate {
|
|||||||
// delegate in `window:willUseFullScreenPresentationOptions:`.
|
// delegate in `window:willUseFullScreenPresentationOptions:`.
|
||||||
self.ivars().save_presentation_opts.set(Some(app.presentationOptions()));
|
self.ivars().save_presentation_opts.set(Some(app.presentationOptions()));
|
||||||
|
|
||||||
let presentation_options = NSApplicationPresentationOptions(
|
let presentation_options =
|
||||||
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen.0
|
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock.0
|
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar.0,
|
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||||
);
|
|
||||||
app.setPresentationOptions(presentation_options);
|
app.setPresentationOptions(presentation_options);
|
||||||
|
|
||||||
let window_level = unsafe { ffi::CGShieldingWindowLevel() } as NSWindowLevel + 1;
|
let window_level = unsafe { ffi::CGShieldingWindowLevel() } as NSWindowLevel + 1;
|
||||||
@@ -1390,19 +1451,13 @@ impl WindowDelegate {
|
|||||||
},
|
},
|
||||||
(Some(Fullscreen::Exclusive(ref video_mode)), Some(Fullscreen::Borderless(_))) => {
|
(Some(Fullscreen::Exclusive(ref video_mode)), Some(Fullscreen::Borderless(_))) => {
|
||||||
let presentation_options = self.ivars().save_presentation_opts.get().unwrap_or(
|
let presentation_options = self.ivars().save_presentation_opts.get().unwrap_or(
|
||||||
NSApplicationPresentationOptions(NSApplicationPresentationOptions::NSApplicationPresentationFullScreen.0
|
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock.0
|
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
||||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar.0),
|
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar
|
||||||
);
|
);
|
||||||
app.setPresentationOptions(presentation_options);
|
app.setPresentationOptions(presentation_options);
|
||||||
|
|
||||||
unsafe {
|
restore_and_release_display(&video_mode.monitor());
|
||||||
ffi::CGRestorePermanentDisplayConfiguration();
|
|
||||||
assert_eq!(
|
|
||||||
ffi::CGDisplayRelease(video_mode.monitor().native_identifier()),
|
|
||||||
ffi::kCGErrorSuccess
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Restore the normal window level following the Borderless fullscreen
|
// Restore the normal window level following the Borderless fullscreen
|
||||||
// `CGShieldingWindowLevel() + 1` hack.
|
// `CGShieldingWindowLevel() + 1` hack.
|
||||||
@@ -1431,19 +1486,19 @@ impl WindowDelegate {
|
|||||||
|
|
||||||
let new_mask = {
|
let new_mask = {
|
||||||
let mut new_mask = if decorations {
|
let mut new_mask = if decorations {
|
||||||
NSWindowStyleMask::Closable.0
|
NSWindowStyleMask::Closable
|
||||||
| NSWindowStyleMask::Miniaturizable.0
|
| NSWindowStyleMask::Miniaturizable
|
||||||
| NSWindowStyleMask::Resizable.0
|
| NSWindowStyleMask::Resizable
|
||||||
| NSWindowStyleMask::Titled.0
|
| NSWindowStyleMask::Titled
|
||||||
} else {
|
} else {
|
||||||
NSWindowStyleMask::Borderless.0 | NSWindowStyleMask::Resizable.0
|
NSWindowStyleMask::Borderless | NSWindowStyleMask::Resizable
|
||||||
};
|
};
|
||||||
if !resizable {
|
if !resizable {
|
||||||
new_mask &= !NSWindowStyleMask::Resizable.0;
|
new_mask &= !NSWindowStyleMask::Resizable;
|
||||||
}
|
}
|
||||||
new_mask
|
new_mask
|
||||||
};
|
};
|
||||||
self.set_style_mask(NSWindowStyleMask(new_mask));
|
self.set_style_mask(new_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -1546,7 +1601,7 @@ impl WindowDelegate {
|
|||||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||||
let mut window_handle = rwh_04::AppKitHandle::empty();
|
let mut window_handle = rwh_04::AppKitHandle::empty();
|
||||||
window_handle.ns_window = self.window() as *const WinitWindow as *mut _;
|
window_handle.ns_window = self.window() as *const WinitWindow as *mut _;
|
||||||
window_handle.ns_view = Id::as_ptr(&self.contentView().unwrap()) as *mut _;
|
window_handle.ns_view = Retained::as_ptr(&self.view()) as *mut _;
|
||||||
rwh_04::RawWindowHandle::AppKit(window_handle)
|
rwh_04::RawWindowHandle::AppKit(window_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1555,7 +1610,7 @@ impl WindowDelegate {
|
|||||||
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
||||||
let mut window_handle = rwh_05::AppKitWindowHandle::empty();
|
let mut window_handle = rwh_05::AppKitWindowHandle::empty();
|
||||||
window_handle.ns_window = self.window() as *const WinitWindow as *mut _;
|
window_handle.ns_window = self.window() as *const WinitWindow as *mut _;
|
||||||
window_handle.ns_view = Id::as_ptr(&self.view()) as *mut _;
|
window_handle.ns_view = Retained::as_ptr(&self.view()) as *mut _;
|
||||||
rwh_05::RawWindowHandle::AppKit(window_handle)
|
rwh_05::RawWindowHandle::AppKit(window_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1569,8 +1624,8 @@ impl WindowDelegate {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
|
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
|
||||||
let window_handle = rwh_06::AppKitWindowHandle::new({
|
let window_handle = rwh_06::AppKitWindowHandle::new({
|
||||||
let ptr = Id::as_ptr(&self.view()) as *mut _;
|
let ptr = Retained::as_ptr(&self.view()) as *mut _;
|
||||||
std::ptr::NonNull::new(ptr).expect("Id<T> should never be null")
|
std::ptr::NonNull::new(ptr).expect("Retained<T> should never be null")
|
||||||
});
|
});
|
||||||
rwh_06::RawWindowHandle::AppKit(window_handle)
|
rwh_06::RawWindowHandle::AppKit(window_handle)
|
||||||
}
|
}
|
||||||
@@ -1578,26 +1633,34 @@ impl WindowDelegate {
|
|||||||
fn toggle_style_mask(&self, mask: NSWindowStyleMask, on: bool) {
|
fn toggle_style_mask(&self, mask: NSWindowStyleMask, on: bool) {
|
||||||
let current_style_mask = self.window().styleMask();
|
let current_style_mask = self.window().styleMask();
|
||||||
if on {
|
if on {
|
||||||
self.set_style_mask(NSWindowStyleMask(current_style_mask.0 | mask.0));
|
self.set_style_mask(current_style_mask | mask);
|
||||||
} else {
|
} else {
|
||||||
self.set_style_mask(NSWindowStyleMask(current_style_mask.0 & (!mask.0)));
|
self.set_style_mask(current_style_mask & !mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn theme(&self) -> Option<Theme> {
|
|
||||||
self.ivars().current_theme.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn has_focus(&self) -> bool {
|
pub fn has_focus(&self) -> bool {
|
||||||
self.window().isKeyWindow()
|
self.window().isKeyWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn theme(&self) -> Option<Theme> {
|
||||||
|
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>) {
|
pub fn set_theme(&self, theme: Option<Theme>) {
|
||||||
let mtm = MainThreadMarker::from(self);
|
unsafe { self.window().setAppearance(theme_to_appearance(theme).as_deref()) };
|
||||||
set_ns_theme(theme, mtm);
|
|
||||||
self.ivars().current_theme.set(theme.or_else(|| Some(get_ns_theme(mtm))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -1618,6 +1681,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 {
|
impl WindowExtMacOS for WindowDelegate {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simple_fullscreen(&self) -> bool {
|
fn simple_fullscreen(&self) -> bool {
|
||||||
@@ -1653,10 +1731,9 @@ impl WindowExtMacOS for WindowDelegate {
|
|||||||
self.ivars().is_simple_fullscreen.set(true);
|
self.ivars().is_simple_fullscreen.set(true);
|
||||||
|
|
||||||
// Simulate pre-Lion fullscreen by hiding the dock and menu bar
|
// Simulate pre-Lion fullscreen by hiding the dock and menu bar
|
||||||
let presentation_options = NSApplicationPresentationOptions(
|
let presentation_options =
|
||||||
NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock.0
|
NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
||||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar.0,
|
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar;
|
||||||
);
|
|
||||||
app.setPresentationOptions(presentation_options);
|
app.setPresentationOptions(presentation_options);
|
||||||
|
|
||||||
// Hide the titlebar
|
// Hide the titlebar
|
||||||
@@ -1754,43 +1831,42 @@ impl WindowExtMacOS for WindowDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mask_contains(mask: NSWindowStyleMask, value: NSWindowStyleMask) -> bool {
|
|
||||||
mask.0 & value.0 == value.0
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_STANDARD_FRAME: NSRect =
|
const DEFAULT_STANDARD_FRAME: NSRect =
|
||||||
NSRect::new(NSPoint::new(50.0, 50.0), NSSize::new(800.0, 600.0));
|
NSRect::new(NSPoint::new(50.0, 50.0), NSSize::new(800.0, 600.0));
|
||||||
|
|
||||||
pub(super) fn get_ns_theme(mtm: MainThreadMarker) -> Theme {
|
fn dark_appearance_name() -> &'static NSString {
|
||||||
let app = NSApplication::sharedApplication(mtm);
|
// Don't use the static `NSAppearanceNameDarkAqua` to allow linking on macOS < 10.14
|
||||||
let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] };
|
ns_string!("NSAppearanceNameDarkAqua")
|
||||||
if !has_theme {
|
}
|
||||||
return Theme::Light;
|
|
||||||
}
|
pub fn appearance_to_theme(appearance: &NSAppearance) -> Theme {
|
||||||
let appearance = app.effectiveAppearance();
|
let best_match = appearance.bestMatchFromAppearancesWithNames(&NSArray::from_id_slice(&[
|
||||||
let name = appearance
|
unsafe { NSAppearanceNameAqua.copy() },
|
||||||
.bestMatchFromAppearancesWithNames(&NSArray::from_id_slice(&[
|
dark_appearance_name().copy(),
|
||||||
NSString::from_str("NSAppearanceNameAqua"),
|
]));
|
||||||
NSString::from_str("NSAppearanceNameDarkAqua"),
|
if let Some(best_match) = best_match {
|
||||||
]))
|
if *best_match == *dark_appearance_name() {
|
||||||
.unwrap();
|
Theme::Dark
|
||||||
match &*name.to_string() {
|
} else {
|
||||||
"NSAppearanceNameDarkAqua" => Theme::Dark,
|
Theme::Light
|
||||||
_ => Theme::Light,
|
}
|
||||||
|
} else {
|
||||||
|
warn!(?appearance, "failed to determine the theme of the appearance");
|
||||||
|
// Default to light in this case
|
||||||
|
Theme::Light
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ns_theme(theme: Option<Theme>, mtm: MainThreadMarker) {
|
fn theme_to_appearance(theme: Option<Theme>) -> Option<Retained<NSAppearance>> {
|
||||||
let app = NSApplication::sharedApplication(mtm);
|
let appearance = match theme? {
|
||||||
let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] };
|
Theme::Light => unsafe { NSAppearance::appearanceNamed(NSAppearanceNameAqua) },
|
||||||
if has_theme {
|
Theme::Dark => NSAppearance::appearanceNamed(dark_appearance_name()),
|
||||||
let appearance = theme.map(|t| {
|
};
|
||||||
let name = match t {
|
if let Some(appearance) = appearance {
|
||||||
Theme::Dark => NSString::from_str("NSAppearanceNameDarkAqua"),
|
Some(appearance)
|
||||||
Theme::Light => NSString::from_str("NSAppearanceNameAqua"),
|
} else {
|
||||||
};
|
warn!(?theme, "could not find appearance for theme");
|
||||||
NSAppearance::appearanceNamed(&name).unwrap()
|
// Assume system appearance in this case
|
||||||
});
|
None
|
||||||
app.setAppearance(appearance.as_ref().map(|a| a.as_ref()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,35 @@
|
|||||||
use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoModeHandle as RootVideoModeHandle};
|
use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoModeHandle as RootVideoModeHandle};
|
||||||
use crate::window::Fullscreen as RootFullscreen;
|
use crate::window::Fullscreen as RootFullscreen;
|
||||||
|
|
||||||
#[cfg(windows_platform)]
|
|
||||||
#[path = "windows/mod.rs"]
|
|
||||||
mod platform;
|
|
||||||
#[cfg(any(x11_platform, wayland_platform))]
|
|
||||||
#[path = "linux/mod.rs"]
|
|
||||||
mod platform;
|
|
||||||
#[cfg(macos_platform)]
|
|
||||||
#[path = "macos/mod.rs"]
|
|
||||||
mod platform;
|
|
||||||
#[cfg(android_platform)]
|
#[cfg(android_platform)]
|
||||||
#[path = "android/mod.rs"]
|
mod android;
|
||||||
mod platform;
|
|
||||||
#[cfg(ios_platform)]
|
#[cfg(ios_platform)]
|
||||||
#[path = "ios/mod.rs"]
|
mod ios;
|
||||||
mod platform;
|
#[cfg(any(x11_platform, wayland_platform))]
|
||||||
#[cfg(web_platform)]
|
mod linux;
|
||||||
#[path = "web/mod.rs"]
|
#[cfg(macos_platform)]
|
||||||
mod platform;
|
mod macos;
|
||||||
#[cfg(orbital_platform)]
|
#[cfg(orbital_platform)]
|
||||||
#[path = "orbital/mod.rs"]
|
mod orbital;
|
||||||
mod platform;
|
#[cfg(web_platform)]
|
||||||
|
mod web;
|
||||||
|
#[cfg(windows_platform)]
|
||||||
|
mod windows;
|
||||||
|
|
||||||
|
#[cfg(android_platform)]
|
||||||
|
use android as platform;
|
||||||
|
#[cfg(ios_platform)]
|
||||||
|
use ios as platform;
|
||||||
|
#[cfg(any(x11_platform, wayland_platform))]
|
||||||
|
use linux as platform;
|
||||||
|
#[cfg(macos_platform)]
|
||||||
|
use macos as platform;
|
||||||
|
#[cfg(orbital_platform)]
|
||||||
|
use orbital as platform;
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
use web as platform;
|
||||||
|
#[cfg(windows_platform)]
|
||||||
|
use windows as platform;
|
||||||
|
|
||||||
pub use self::platform::*;
|
pub use self::platform::*;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use crate::keyboard::{
|
|||||||
PhysicalKey,
|
PhysicalKey,
|
||||||
};
|
};
|
||||||
use crate::window::{
|
use crate::window::{
|
||||||
CustomCursor as RootCustomCursor, CustomCursorSource, WindowId as RootWindowId,
|
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId as RootWindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -775,6 +775,11 @@ impl ActiveEventLoop {
|
|||||||
rwh_05::RawDisplayHandle::Orbital(rwh_05::OrbitalDisplayHandle::empty())
|
rwh_05::RawDisplayHandle::Orbital(rwh_05::OrbitalDisplayHandle::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn system_theme(&self) -> Option<Theme> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rwh_06")]
|
#[cfg(feature = "rwh_06")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn raw_display_handle_rwh_06(
|
pub fn raw_display_handle_rwh_06(
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ pub struct WindowId {
|
|||||||
|
|
||||||
impl WindowId {
|
impl WindowId {
|
||||||
pub const fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
WindowId { fd: u64::max_value() }
|
WindowId { fd: u64::MAX }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,12 @@ impl<T> Dispatcher<T> {
|
|||||||
// SAFETY: The `transmute` is necessary because `Closure` requires `'static`. This is
|
// SAFETY: The `transmute` is necessary because `Closure` requires `'static`. This is
|
||||||
// safe because this function won't return until `f` has finished executing. See
|
// safe because this function won't return until `f` has finished executing. See
|
||||||
// `Self::new()`.
|
// `Self::new()`.
|
||||||
let closure = Closure(unsafe { std::mem::transmute(closure) });
|
let closure = Closure(unsafe {
|
||||||
|
std::mem::transmute::<
|
||||||
|
Box<dyn FnOnce(&T) + Send>,
|
||||||
|
Box<dyn FnOnce(&T) + Send + 'static>,
|
||||||
|
>(closure)
|
||||||
|
});
|
||||||
|
|
||||||
self.0.send(closure);
|
self.0.send(closure);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::future;
|
use std::future;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
@@ -6,13 +7,13 @@ use std::task::Poll;
|
|||||||
use super::super::main_thread::MainThreadMarker;
|
use super::super::main_thread::MainThreadMarker;
|
||||||
use super::{AtomicWaker, Wrapper};
|
use super::{AtomicWaker, Wrapper};
|
||||||
|
|
||||||
pub struct WakerSpawner<T: 'static>(Wrapper<Handler<T>, Sender, usize>);
|
pub struct WakerSpawner<T: 'static>(Wrapper<Handler<T>, Sender, NonZeroUsize>);
|
||||||
|
|
||||||
pub struct Waker<T: 'static>(Wrapper<Handler<T>, Sender, usize>);
|
pub struct Waker<T: 'static>(Wrapper<Handler<T>, Sender, NonZeroUsize>);
|
||||||
|
|
||||||
struct Handler<T> {
|
struct Handler<T> {
|
||||||
value: T,
|
value: T,
|
||||||
handler: fn(&T, usize),
|
handler: fn(&T, NonZeroUsize, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -20,7 +21,11 @@ struct Sender(Arc<Inner>);
|
|||||||
|
|
||||||
impl<T> WakerSpawner<T> {
|
impl<T> WakerSpawner<T> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn new(main_thread: MainThreadMarker, value: T, handler: fn(&T, usize)) -> Option<Self> {
|
pub fn new(
|
||||||
|
main_thread: MainThreadMarker,
|
||||||
|
value: T,
|
||||||
|
handler: fn(&T, NonZeroUsize, bool),
|
||||||
|
) -> Option<Self> {
|
||||||
let inner = Arc::new(Inner {
|
let inner = Arc::new(Inner {
|
||||||
counter: AtomicUsize::new(0),
|
counter: AtomicUsize::new(0),
|
||||||
waker: AtomicWaker::new(),
|
waker: AtomicWaker::new(),
|
||||||
@@ -37,7 +42,7 @@ impl<T> WakerSpawner<T> {
|
|||||||
|handler, count| {
|
|handler, count| {
|
||||||
let handler = handler.borrow();
|
let handler = handler.borrow();
|
||||||
let handler = handler.as_ref().unwrap();
|
let handler = handler.as_ref().unwrap();
|
||||||
(handler.handler)(&handler.value, count);
|
(handler.handler)(&handler.value, count, true);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
let inner = Arc::clone(&inner);
|
let inner = Arc::clone(&inner);
|
||||||
@@ -46,29 +51,31 @@ impl<T> WakerSpawner<T> {
|
|||||||
while let Some(count) = future::poll_fn(|cx| {
|
while let Some(count) = future::poll_fn(|cx| {
|
||||||
let count = inner.counter.swap(0, Ordering::Relaxed);
|
let count = inner.counter.swap(0, Ordering::Relaxed);
|
||||||
|
|
||||||
if count > 0 {
|
match NonZeroUsize::new(count) {
|
||||||
Poll::Ready(Some(count))
|
Some(count) => Poll::Ready(Some(count)),
|
||||||
} else {
|
None => {
|
||||||
inner.waker.register(cx.waker());
|
inner.waker.register(cx.waker());
|
||||||
|
|
||||||
let count = inner.counter.swap(0, Ordering::Relaxed);
|
let count = inner.counter.swap(0, Ordering::Relaxed);
|
||||||
|
|
||||||
if count > 0 {
|
match NonZeroUsize::new(count) {
|
||||||
Poll::Ready(Some(count))
|
Some(count) => Poll::Ready(Some(count)),
|
||||||
} else {
|
None => {
|
||||||
if inner.closed.load(Ordering::Relaxed) {
|
if inner.closed.load(Ordering::Relaxed) {
|
||||||
return Poll::Ready(None);
|
return Poll::Ready(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
let handler = handler.borrow();
|
let handler = handler.borrow();
|
||||||
let handler = handler.as_ref().unwrap();
|
let handler = handler.as_ref().unwrap();
|
||||||
(handler.handler)(&handler.value, count);
|
(handler.handler)(&handler.value, count, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -107,7 +114,7 @@ impl<T> Drop for WakerSpawner<T> {
|
|||||||
|
|
||||||
impl<T> Waker<T> {
|
impl<T> Waker<T> {
|
||||||
pub fn wake(&self) {
|
pub fn wake(&self) {
|
||||||
self.0.send(1)
|
self.0.send(NonZeroUsize::MIN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -324,7 +324,11 @@ impl Inner {
|
|||||||
match &self.cursor {
|
match &self.cursor {
|
||||||
SelectedCursor::Icon(icon)
|
SelectedCursor::Icon(icon)
|
||||||
| SelectedCursor::Loading { previous: Previous::Icon(icon), .. } => {
|
| SelectedCursor::Loading { previous: Previous::Icon(icon), .. } => {
|
||||||
self.style.set("cursor", icon.name())
|
if let CursorIcon::Default = icon {
|
||||||
|
self.style.remove("cursor")
|
||||||
|
} else {
|
||||||
|
self.style.set("cursor", icon.name())
|
||||||
|
}
|
||||||
},
|
},
|
||||||
SelectedCursor::Loading { previous: Previous::Image(cursor), .. }
|
SelectedCursor::Loading { previous: Previous::Image(cursor), .. }
|
||||||
| SelectedCursor::Image(cursor) => {
|
| SelectedCursor::Image(cursor) => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
pub struct DeviceId(pub i32);
|
pub struct DeviceId(pub i32);
|
||||||
|
|
||||||
impl DeviceId {
|
impl DeviceId {
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
Self(0)
|
Self(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::sync::mpsc::{self, Receiver, Sender};
|
|||||||
use crate::error::EventLoopError;
|
use crate::error::EventLoopError;
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
||||||
|
use crate::platform::web::{ActiveEventLoopExtWebSys, PollStrategy, WaitUntilStrategy};
|
||||||
|
|
||||||
use super::{backend, device, window};
|
use super::{backend, device, window};
|
||||||
|
|
||||||
@@ -53,7 +54,11 @@ impl<T> EventLoop<T> {
|
|||||||
// SAFETY: The `transmute` is necessary because `run()` requires `'static`. This is safe
|
// 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
|
// because this function will never return and all resources not cleaned up by the point we
|
||||||
// `throw` will leak, making this actually `'static`.
|
// `throw` will leak, making this actually `'static`.
|
||||||
let handler = unsafe { std::mem::transmute(handler) };
|
let handler = unsafe {
|
||||||
|
std::mem::transmute::<Box<dyn FnMut(Event<()>)>, Box<dyn FnMut(Event<()>) + 'static>>(
|
||||||
|
handler,
|
||||||
|
)
|
||||||
|
};
|
||||||
self.elw.p.run(handler, false);
|
self.elw.p.run(handler, false);
|
||||||
|
|
||||||
// Throw an exception to break out of Rust execution and use unreachable to tell the
|
// Throw an exception to break out of Rust execution and use unreachable to tell the
|
||||||
@@ -95,4 +100,20 @@ impl<T> EventLoop<T> {
|
|||||||
pub fn window_target(&self) -> &RootActiveEventLoop {
|
pub fn window_target(&self) -> &RootActiveEventLoop {
|
||||||
&self.elw
|
&self.elw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_poll_strategy(&self, strategy: PollStrategy) {
|
||||||
|
self.elw.set_poll_strategy(strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poll_strategy(&self) -> PollStrategy {
|
||||||
|
self.elw.poll_strategy()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
|
||||||
|
self.elw.set_wait_until_strategy(strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_until_strategy(&self) -> WaitUntilStrategy {
|
||||||
|
self.elw.wait_until_strategy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,21 @@ use crate::event::{
|
|||||||
WindowEvent,
|
WindowEvent,
|
||||||
};
|
};
|
||||||
use crate::event_loop::{ControlFlow, DeviceEvents};
|
use crate::event_loop::{ControlFlow, DeviceEvents};
|
||||||
use crate::platform::web::PollStrategy;
|
use crate::platform::web::{PollStrategy, WaitUntilStrategy};
|
||||||
use crate::platform_impl::platform::backend::EventListenerHandle;
|
use crate::platform_impl::platform::backend::EventListenerHandle;
|
||||||
use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
|
use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
|
||||||
use crate::platform_impl::platform::window::Inner;
|
use crate::platform_impl::platform::window::Inner;
|
||||||
use crate::window::WindowId;
|
use crate::window::WindowId;
|
||||||
|
|
||||||
|
use js_sys::Function;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::{HashSet, VecDeque};
|
use std::collections::{HashSet, VecDeque};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use wasm_bindgen::prelude::Closure;
|
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
use web_sys::{Document, KeyboardEvent, PageTransitionEvent, PointerEvent, WheelEvent};
|
use web_sys::{Document, KeyboardEvent, PageTransitionEvent, PointerEvent, WheelEvent};
|
||||||
use web_time::{Duration, Instant};
|
use web_time::{Duration, Instant};
|
||||||
|
|
||||||
@@ -40,6 +43,7 @@ pub struct Execution {
|
|||||||
proxy_spawner: WakerSpawner<Weak<Self>>,
|
proxy_spawner: WakerSpawner<Weak<Self>>,
|
||||||
control_flow: Cell<ControlFlow>,
|
control_flow: Cell<ControlFlow>,
|
||||||
poll_strategy: Cell<PollStrategy>,
|
poll_strategy: Cell<PollStrategy>,
|
||||||
|
wait_until_strategy: Cell<WaitUntilStrategy>,
|
||||||
exit: Cell<bool>,
|
exit: Cell<bool>,
|
||||||
runner: RefCell<RunnerEnum>,
|
runner: RefCell<RunnerEnum>,
|
||||||
suspended: Cell<bool>,
|
suspended: Cell<bool>,
|
||||||
@@ -133,18 +137,20 @@ impl Shared {
|
|||||||
let document = window.document().expect("Failed to obtain document");
|
let document = window.document().expect("Failed to obtain document");
|
||||||
|
|
||||||
Shared(Rc::<Execution>::new_cyclic(|weak| {
|
Shared(Rc::<Execution>::new_cyclic(|weak| {
|
||||||
let proxy_spawner = WakerSpawner::new(main_thread, weak.clone(), |runner, count| {
|
let proxy_spawner =
|
||||||
if let Some(runner) = runner.upgrade() {
|
WakerSpawner::new(main_thread, weak.clone(), |runner, count, local| {
|
||||||
Shared(runner).send_events(iter::repeat(Event::UserEvent(())).take(count))
|
if let Some(runner) = runner.upgrade() {
|
||||||
}
|
Shared(runner).send_user_events(count, local)
|
||||||
})
|
}
|
||||||
.expect("`EventLoop` has to be created in the main thread");
|
})
|
||||||
|
.expect("`EventLoop` has to be created in the main thread");
|
||||||
|
|
||||||
Execution {
|
Execution {
|
||||||
main_thread,
|
main_thread,
|
||||||
proxy_spawner,
|
proxy_spawner,
|
||||||
control_flow: Cell::new(ControlFlow::default()),
|
control_flow: Cell::new(ControlFlow::default()),
|
||||||
poll_strategy: Cell::new(PollStrategy::default()),
|
poll_strategy: Cell::new(PollStrategy::default()),
|
||||||
|
wait_until_strategy: Cell::new(WaitUntilStrategy::default()),
|
||||||
exit: Cell::new(false),
|
exit: Cell::new(false),
|
||||||
runner: RefCell::new(RunnerEnum::Pending),
|
runner: RefCell::new(RunnerEnum::Pending),
|
||||||
suspended: Cell::new(false),
|
suspended: Cell::new(false),
|
||||||
@@ -239,21 +245,10 @@ impl Shared {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pointer_type = event.pointer_type();
|
|
||||||
|
|
||||||
if pointer_type != "mouse" {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// chorded button event
|
// chorded button event
|
||||||
let device_id = RootDeviceId(DeviceId(event.pointer_id()));
|
let device_id = RootDeviceId(DeviceId(event.pointer_id()));
|
||||||
|
|
||||||
if let Some(button) = backend::event::mouse_button(&event) {
|
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()) {
|
let state = if backend::event::mouse_buttons(&event).contains(button.into()) {
|
||||||
ElementState::Pressed
|
ElementState::Pressed
|
||||||
} else {
|
} else {
|
||||||
@@ -317,10 +312,6 @@ impl Shared {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.pointer_type() != "mouse" {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
|
let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
|
||||||
runner.send_event(Event::DeviceEvent {
|
runner.send_event(Event::DeviceEvent {
|
||||||
device_id: RootDeviceId(DeviceId(event.pointer_id())),
|
device_id: RootDeviceId(DeviceId(event.pointer_id())),
|
||||||
@@ -340,10 +331,6 @@ impl Shared {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.pointer_type() != "mouse" {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
|
let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
|
||||||
runner.send_event(Event::DeviceEvent {
|
runner.send_event(Event::DeviceEvent {
|
||||||
device_id: RootDeviceId(DeviceId(event.pointer_id())),
|
device_id: RootDeviceId(DeviceId(event.pointer_id())),
|
||||||
@@ -364,7 +351,7 @@ impl Shared {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runner.send_event(Event::DeviceEvent {
|
runner.send_event(Event::DeviceEvent {
|
||||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
device_id: RootDeviceId(DeviceId::dummy()),
|
||||||
event: DeviceEvent::Key(RawKeyEvent {
|
event: DeviceEvent::Key(RawKeyEvent {
|
||||||
physical_key: backend::event::key_code(&event),
|
physical_key: backend::event::key_code(&event),
|
||||||
state: ElementState::Pressed,
|
state: ElementState::Pressed,
|
||||||
@@ -382,7 +369,7 @@ impl Shared {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runner.send_event(Event::DeviceEvent {
|
runner.send_event(Event::DeviceEvent {
|
||||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
device_id: RootDeviceId(DeviceId::dummy()),
|
||||||
event: DeviceEvent::Key(RawKeyEvent {
|
event: DeviceEvent::Key(RawKeyEvent {
|
||||||
physical_key: backend::event::key_code(&event),
|
physical_key: backend::event::key_code(&event),
|
||||||
state: ElementState::Released,
|
state: ElementState::Released,
|
||||||
@@ -460,6 +447,51 @@ impl Shared {
|
|||||||
self.send_events(iter::once(event));
|
self.send_events(iter::once(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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_user_events(&self, count: NonZeroUsize, local: bool) {
|
||||||
|
// If the event loop is closed, it should discard any new events
|
||||||
|
if self.is_closed() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if local {
|
||||||
|
// If the loop is not running and triggered locally, queue on next microtick.
|
||||||
|
if let Ok(RunnerEnum::Running(ref runner)) =
|
||||||
|
self.0.runner.try_borrow().as_ref().map(Deref::deref)
|
||||||
|
{
|
||||||
|
// If we're currently polling let `send_events` do its job.
|
||||||
|
if !matches!(runner.state, State::Poll { .. }) {
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_name = queueMicrotask)]
|
||||||
|
fn queue_microtask(task: Function);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_microtask(
|
||||||
|
Closure::once_into_js({
|
||||||
|
let this = Rc::downgrade(&self.0);
|
||||||
|
move || {
|
||||||
|
if let Some(shared) = this.upgrade() {
|
||||||
|
Shared(shared).send_events(
|
||||||
|
iter::repeat(Event::UserEvent(())).take(count.get()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unchecked_into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_events(iter::repeat(Event::UserEvent(())).take(count.get()))
|
||||||
|
}
|
||||||
|
|
||||||
// Add a series of events to the event loop runner
|
// Add a series of events to the event loop runner
|
||||||
//
|
//
|
||||||
// It will determine if the event should be immediately sent to the user or buffered for later
|
// It will determine if the event should be immediately sent to the user or buffered for later
|
||||||
@@ -473,7 +505,7 @@ impl Shared {
|
|||||||
match self.0.runner.try_borrow().as_ref().map(Deref::deref) {
|
match self.0.runner.try_borrow().as_ref().map(Deref::deref) {
|
||||||
Ok(RunnerEnum::Running(ref runner)) => {
|
Ok(RunnerEnum::Running(ref runner)) => {
|
||||||
// If we're currently polling, queue this and wait for the poll() method to be
|
// If we're currently polling, queue this and wait for the poll() method to be
|
||||||
// called
|
// called.
|
||||||
if let State::Poll { .. } = runner.state {
|
if let State::Poll { .. } = runner.state {
|
||||||
process_immediately = false;
|
process_immediately = false;
|
||||||
}
|
}
|
||||||
@@ -647,6 +679,7 @@ impl Shared {
|
|||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
_timeout: backend::Schedule::new_with_duration(
|
_timeout: backend::Schedule::new_with_duration(
|
||||||
|
self.wait_until_strategy(),
|
||||||
self.window(),
|
self.window(),
|
||||||
move || cloned.resume_time_reached(start, end),
|
move || cloned.resume_time_reached(start, end),
|
||||||
delay,
|
delay,
|
||||||
@@ -759,6 +792,14 @@ impl Shared {
|
|||||||
self.0.poll_strategy.get()
|
self.0.poll_strategy.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
|
||||||
|
self.0.wait_until_strategy.set(strategy)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn wait_until_strategy(&self) -> WaitUntilStrategy {
|
||||||
|
self.0.wait_until_strategy.get()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
|
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
|
||||||
self.0.proxy_spawner.waker()
|
self.0.proxy_spawner.waker()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use crate::event::{
|
|||||||
};
|
};
|
||||||
use crate::event_loop::{ControlFlow, DeviceEvents};
|
use crate::event_loop::{ControlFlow, DeviceEvents};
|
||||||
use crate::keyboard::ModifiersState;
|
use crate::keyboard::ModifiersState;
|
||||||
use crate::platform::web::{CustomCursorFuture, PollStrategy};
|
use crate::platform::web::{CustomCursorFuture, PollStrategy, WaitUntilStrategy};
|
||||||
use crate::platform_impl::platform::cursor::CustomCursor;
|
use crate::platform_impl::platform::cursor::CustomCursor;
|
||||||
use crate::platform_impl::platform::r#async::Waker;
|
use crate::platform_impl::platform::r#async::Waker;
|
||||||
use crate::window::{
|
use crate::window::{
|
||||||
@@ -142,7 +142,7 @@ impl ActiveEventLoop {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let device_id = RootDeviceId(unsafe { DeviceId::dummy() });
|
let device_id = RootDeviceId(DeviceId::dummy());
|
||||||
|
|
||||||
runner.send_events(
|
runner.send_events(
|
||||||
iter::once(Event::WindowEvent {
|
iter::once(Event::WindowEvent {
|
||||||
@@ -178,7 +178,7 @@ impl ActiveEventLoop {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let device_id = RootDeviceId(unsafe { DeviceId::dummy() });
|
let device_id = RootDeviceId(DeviceId::dummy());
|
||||||
|
|
||||||
runner.send_events(
|
runner.send_events(
|
||||||
iter::once(Event::WindowEvent {
|
iter::once(Event::WindowEvent {
|
||||||
@@ -258,21 +258,6 @@ impl ActiveEventLoop {
|
|||||||
});
|
});
|
||||||
|
|
||||||
canvas.on_cursor_move(
|
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 runner = self.runner.clone();
|
||||||
let has_focus = has_focus.clone();
|
let has_focus = has_focus.clone();
|
||||||
@@ -372,20 +357,6 @@ impl ActiveEventLoop {
|
|||||||
);
|
);
|
||||||
|
|
||||||
canvas.on_mouse_press(
|
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 runner = self.runner.clone();
|
||||||
let modifiers = self.modifiers.clone();
|
let modifiers = self.modifiers.clone();
|
||||||
@@ -450,21 +421,6 @@ impl ActiveEventLoop {
|
|||||||
);
|
);
|
||||||
|
|
||||||
canvas.on_mouse_release(
|
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 runner = self.runner.clone();
|
||||||
let has_focus = has_focus.clone();
|
let has_focus = has_focus.clone();
|
||||||
@@ -605,7 +561,7 @@ impl ActiveEventLoop {
|
|||||||
window_id: RootWindowId(id),
|
window_id: RootWindowId(id),
|
||||||
event: WindowEvent::Resized(new_size),
|
event: WindowEvent::Resized(new_size),
|
||||||
});
|
});
|
||||||
runner.request_redraw(RootWindowId(id));
|
canvas.request_animation_frame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -658,6 +614,16 @@ impl ActiveEventLoop {
|
|||||||
self.runner.listen_device_events(allowed)
|
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) {
|
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||||
self.runner.set_control_flow(control_flow)
|
self.runner.set_control_flow(control_flow)
|
||||||
}
|
}
|
||||||
@@ -682,6 +648,14 @@ impl ActiveEventLoop {
|
|||||||
self.runner.poll_strategy()
|
self.runner.poll_strategy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
|
||||||
|
self.runner.set_wait_until_strategy(strategy)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn wait_until_strategy(&self) -> WaitUntilStrategy {
|
||||||
|
self.runner.wait_until_strategy()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
|
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
|
||||||
self.runner.waker()
|
self.runner.waker()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
// incoming events (from the registered handlers) and ensuring they are passed to the user in a
|
// incoming events (from the registered handlers) and ensuring they are passed to the user in a
|
||||||
// compliant way.
|
// compliant way.
|
||||||
|
|
||||||
// TODO: FP, remove when <https://github.com/rust-lang/rust/issues/121621> is fixed.
|
// TODO: FP, remove when <https://github.com/rust-lang/rust-clippy/issues/12377> is fixed.
|
||||||
#![allow(unknown_lints, non_local_definitions)]
|
#![allow(clippy::empty_docs)]
|
||||||
|
|
||||||
mod r#async;
|
mod r#async;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
@@ -28,11 +28,9 @@ mod event_loop;
|
|||||||
mod keyboard;
|
mod keyboard;
|
||||||
mod main_thread;
|
mod main_thread;
|
||||||
mod monitor;
|
mod monitor;
|
||||||
|
mod web_sys;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
#[path = "web_sys/mod.rs"]
|
|
||||||
mod backend;
|
|
||||||
|
|
||||||
pub use self::device::DeviceId;
|
pub use self::device::DeviceId;
|
||||||
pub use self::error::OsError;
|
pub use self::error::OsError;
|
||||||
pub(crate) use self::event_loop::{
|
pub(crate) use self::event_loop::{
|
||||||
@@ -43,6 +41,7 @@ pub use self::monitor::{MonitorHandle, VideoModeHandle};
|
|||||||
pub use self::window::{PlatformSpecificWindowAttributes, Window, WindowId};
|
pub use self::window::{PlatformSpecificWindowAttributes, Window, WindowId};
|
||||||
|
|
||||||
pub(crate) use self::keyboard::KeyEventExtra;
|
pub(crate) use self::keyboard::KeyEventExtra;
|
||||||
|
use self::web_sys as backend;
|
||||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||||
pub(crate) use crate::platform_impl::Fullscreen;
|
pub(crate) use crate::platform_impl::Fullscreen;
|
||||||
pub(crate) use cursor::{
|
pub(crate) use cursor::{
|
||||||
|
|||||||
@@ -329,51 +329,29 @@ impl Canvas {
|
|||||||
self.pointer_handler.on_cursor_enter(&self.common, handler)
|
self.pointer_handler.on_cursor_enter(&self.common, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_mouse_release<MOD, M, T>(
|
pub fn on_mouse_release<M, T>(&mut self, mouse_handler: M, touch_handler: T)
|
||||||
&mut self,
|
where
|
||||||
modifier_handler: MOD,
|
|
||||||
mouse_handler: M,
|
|
||||||
touch_handler: T,
|
|
||||||
) where
|
|
||||||
MOD: 'static + FnMut(ModifiersState),
|
|
||||||
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
||||||
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
||||||
{
|
{
|
||||||
self.pointer_handler.on_mouse_release(
|
self.pointer_handler.on_mouse_release(&self.common, mouse_handler, touch_handler)
|
||||||
&self.common,
|
|
||||||
modifier_handler,
|
|
||||||
mouse_handler,
|
|
||||||
touch_handler,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_mouse_press<MOD, M, T>(
|
pub fn on_mouse_press<M, T>(&mut self, mouse_handler: M, touch_handler: T)
|
||||||
&mut self,
|
where
|
||||||
modifier_handler: MOD,
|
|
||||||
mouse_handler: M,
|
|
||||||
touch_handler: T,
|
|
||||||
) where
|
|
||||||
MOD: 'static + FnMut(ModifiersState),
|
|
||||||
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
||||||
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
||||||
{
|
{
|
||||||
self.pointer_handler.on_mouse_press(
|
self.pointer_handler.on_mouse_press(
|
||||||
&self.common,
|
&self.common,
|
||||||
modifier_handler,
|
|
||||||
mouse_handler,
|
mouse_handler,
|
||||||
touch_handler,
|
touch_handler,
|
||||||
Rc::clone(&self.prevent_default),
|
Rc::clone(&self.prevent_default),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_cursor_move<MOD, M, T, B>(
|
pub fn on_cursor_move<M, T, B>(&mut self, mouse_handler: M, touch_handler: T, button_handler: B)
|
||||||
&mut self,
|
where
|
||||||
modifier_handler: MOD,
|
|
||||||
mouse_handler: M,
|
|
||||||
touch_handler: T,
|
|
||||||
button_handler: B,
|
|
||||||
) where
|
|
||||||
MOD: 'static + FnMut(ModifiersState),
|
|
||||||
M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
|
M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
|
||||||
T: 'static
|
T: 'static
|
||||||
+ FnMut(ModifiersState, i32, &mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>),
|
+ FnMut(ModifiersState, i32, &mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>),
|
||||||
@@ -381,7 +359,6 @@ impl Canvas {
|
|||||||
{
|
{
|
||||||
self.pointer_handler.on_cursor_move(
|
self.pointer_handler.on_cursor_move(
|
||||||
&self.common,
|
&self.common,
|
||||||
modifier_handler,
|
|
||||||
mouse_handler,
|
mouse_handler,
|
||||||
touch_handler,
|
touch_handler,
|
||||||
button_handler,
|
button_handler,
|
||||||
@@ -427,8 +404,8 @@ impl Canvas {
|
|||||||
|
|
||||||
pub(crate) fn on_resize_scale<S, R>(&mut self, scale_handler: S, size_handler: R)
|
pub(crate) fn on_resize_scale<S, R>(&mut self, scale_handler: S, size_handler: R)
|
||||||
where
|
where
|
||||||
S: 'static + FnMut(PhysicalSize<u32>, f64),
|
S: 'static + Fn(PhysicalSize<u32>, f64),
|
||||||
R: 'static + FnMut(PhysicalSize<u32>),
|
R: 'static + Fn(PhysicalSize<u32>),
|
||||||
{
|
{
|
||||||
self.on_resize_scale = Some(ResizeScaleHandle::new(
|
self.on_resize_scale = Some(ResizeScaleHandle::new(
|
||||||
self.window().clone(),
|
self.window().clone(),
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
use crate::dpi::LogicalPosition;
|
|
||||||
use crate::event::{MouseButton, MouseScrollDelta};
|
use crate::event::{MouseButton, MouseScrollDelta};
|
||||||
use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey};
|
use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey};
|
||||||
|
|
||||||
|
use dpi::{LogicalPosition, PhysicalPosition, Position};
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
use std::cell::OnceCell;
|
use std::cell::OnceCell;
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
use wasm_bindgen::{JsCast, JsValue};
|
use wasm_bindgen::{JsCast, JsValue};
|
||||||
use web_sys::{KeyboardEvent, MouseEvent, PointerEvent, WheelEvent};
|
use web_sys::{KeyboardEvent, MouseEvent, PointerEvent, WheelEvent};
|
||||||
|
|
||||||
|
use super::Engine;
|
||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
// https://www.w3.org/TR/pointerevents3/#the-buttons-property
|
// https://www.w3.org/TR/pointerevents3/#the-buttons-property
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[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() }
|
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>.
|
// See <https://bugzilla.mozilla.org/show_bug.cgi?id=1753724>.
|
||||||
pub struct MouseDelta(Option<MouseDeltaInner>);
|
// See <https://github.com/w3c/pointerlock/issues/42>.
|
||||||
|
pub enum MouseDelta {
|
||||||
pub struct MouseDeltaInner {
|
Chromium,
|
||||||
old_position: LogicalPosition<f64>,
|
Gecko { old_position: LogicalPosition<f64>, old_delta: LogicalPosition<f64> },
|
||||||
old_delta: LogicalPosition<f64>,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MouseDelta {
|
impl MouseDelta {
|
||||||
pub fn init(window: &web_sys::Window, event: &PointerEvent) -> Self {
|
pub fn init(window: &web_sys::Window, event: &PointerEvent) -> Self {
|
||||||
// Firefox has wrong movement values in coalesced events, we will detect that by checking
|
match super::engine(window) {
|
||||||
// for `pointerrawupdate` support. Presumably an implementation of `pointerrawupdate`
|
Some(Engine::Chromium) => Self::Chromium,
|
||||||
// should require correct movement values, otherwise uncoalesced events might be broken as
|
// Firefox has wrong movement values in coalesced events.
|
||||||
// well.
|
Some(Engine::Gecko) if has_coalesced_events_support(event) => Self::Gecko {
|
||||||
Self((!has_pointer_raw_support(window) && has_coalesced_events_support(event)).then(|| {
|
|
||||||
MouseDeltaInner {
|
|
||||||
old_position: mouse_position(event),
|
old_position: mouse_position(event),
|
||||||
old_delta: LogicalPosition {
|
old_delta: LogicalPosition::new(
|
||||||
x: event.movement_x() as f64,
|
event.movement_x() as f64,
|
||||||
y: event.movement_y() as f64,
|
event.movement_y() as f64,
|
||||||
},
|
),
|
||||||
}
|
},
|
||||||
}))
|
_ => Self::Other,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delta(&mut self, event: &MouseEvent) -> LogicalPosition<f64> {
|
pub fn delta(&mut self, event: &MouseEvent) -> Position {
|
||||||
if let Some(inner) = &mut self.0 {
|
match self {
|
||||||
let new_position = mouse_position(event);
|
MouseDelta::Chromium => {
|
||||||
let x = new_position.x - inner.old_position.x + inner.old_delta.x;
|
PhysicalPosition::new(event.movement_x(), event.movement_y()).into()
|
||||||
let y = new_position.y - inner.old_position.y + inner.old_delta.y;
|
},
|
||||||
inner.old_position = new_position;
|
MouseDelta::Gecko { old_position, old_delta } => {
|
||||||
inner.old_delta = LogicalPosition::new(0., 0.);
|
let new_position = mouse_position(event);
|
||||||
LogicalPosition::new(x, y)
|
let x = new_position.x - old_position.x + old_delta.x;
|
||||||
} else {
|
let y = new_position.y - old_position.y + old_delta.y;
|
||||||
LogicalPosition { x: event.movement_x() as f64, y: event.movement_y() as f64 }
|
*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`.
|
// TODO: Remove when Safari supports `getCoalescedEvents`.
|
||||||
// See <https://bugs.webkit.org/show_bug.cgi?id=210454>.
|
// See <https://bugs.webkit.org/show_bug.cgi?id=210454>.
|
||||||
pub fn has_coalesced_events_support(event: &PointerEvent) -> bool {
|
pub fn has_coalesced_events_support(event: &PointerEvent) -> bool {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ mod pointer;
|
|||||||
mod resize_scaling;
|
mod resize_scaling;
|
||||||
mod schedule;
|
mod schedule;
|
||||||
|
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
pub use self::canvas::{Canvas, Style};
|
pub use self::canvas::{Canvas, Style};
|
||||||
pub use self::event::ButtonsState;
|
pub use self::event::ButtonsState;
|
||||||
pub use self::event_handle::EventListenerHandle;
|
pub use self::event_handle::EventListenerHandle;
|
||||||
@@ -16,8 +18,13 @@ pub use self::resize_scaling::ResizeScaleHandle;
|
|||||||
pub use self::schedule::Schedule;
|
pub use self::schedule::Schedule;
|
||||||
|
|
||||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||||
|
use js_sys::Array;
|
||||||
use wasm_bindgen::closure::Closure;
|
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) {
|
pub fn throw(msg: &str) {
|
||||||
wasm_bindgen::throw_str(msg);
|
wasm_bindgen::throw_str(msg);
|
||||||
@@ -158,3 +165,69 @@ pub fn is_visible(document: &Document) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub type RawCanvasType = HtmlCanvasElement;
|
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
|
// touch events are handled separately
|
||||||
// handling them here would produce duplicate mouse events, inconsistent with
|
// handling them here would produce duplicate mouse events, inconsistent with
|
||||||
// other platforms.
|
// 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);
|
handler(modifiers, pointer_id);
|
||||||
}));
|
}));
|
||||||
@@ -61,20 +61,18 @@ impl PointerHandler {
|
|||||||
// touch events are handled separately
|
// touch events are handled separately
|
||||||
// handling them here would produce duplicate mouse events, inconsistent with
|
// handling them here would produce duplicate mouse events, inconsistent with
|
||||||
// other platforms.
|
// 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);
|
handler(modifiers, pointer_id);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_mouse_release<MOD, M, T>(
|
pub fn on_mouse_release<M, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
canvas_common: &Common,
|
canvas_common: &Common,
|
||||||
mut modifier_handler: MOD,
|
|
||||||
mut mouse_handler: M,
|
mut mouse_handler: M,
|
||||||
mut touch_handler: T,
|
mut touch_handler: T,
|
||||||
) where
|
) where
|
||||||
MOD: 'static + FnMut(ModifiersState),
|
|
||||||
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
||||||
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
||||||
{
|
{
|
||||||
@@ -90,26 +88,23 @@ impl PointerHandler {
|
|||||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||||
Force::Normalized(event.pressure() as f64),
|
Force::Normalized(event.pressure() as f64),
|
||||||
),
|
),
|
||||||
"mouse" => mouse_handler(
|
_ => mouse_handler(
|
||||||
modifiers,
|
modifiers,
|
||||||
event.pointer_id(),
|
event.pointer_id(),
|
||||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||||
event::mouse_button(&event).expect("no mouse button released"),
|
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,
|
&mut self,
|
||||||
canvas_common: &Common,
|
canvas_common: &Common,
|
||||||
mut modifier_handler: MOD,
|
|
||||||
mut mouse_handler: M,
|
mut mouse_handler: M,
|
||||||
mut touch_handler: T,
|
mut touch_handler: T,
|
||||||
prevent_default: Rc<Cell<bool>>,
|
prevent_default: Rc<Cell<bool>>,
|
||||||
) where
|
) where
|
||||||
MOD: 'static + FnMut(ModifiersState),
|
|
||||||
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
|
||||||
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
||||||
{
|
{
|
||||||
@@ -125,8 +120,9 @@ impl PointerHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let modifiers = event::mouse_modifiers(&event);
|
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" => {
|
||||||
touch_handler(
|
touch_handler(
|
||||||
modifiers,
|
modifiers,
|
||||||
@@ -135,7 +131,7 @@ impl PointerHandler {
|
|||||||
Force::Normalized(event.pressure() as f64),
|
Force::Normalized(event.pressure() as f64),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
"mouse" => {
|
_ => {
|
||||||
mouse_handler(
|
mouse_handler(
|
||||||
modifiers,
|
modifiers,
|
||||||
event.pointer_id(),
|
event.pointer_id(),
|
||||||
@@ -143,27 +139,27 @@ impl PointerHandler {
|
|||||||
event::mouse_button(&event).expect("no mouse button pressed"),
|
event::mouse_button(&event).expect("no mouse button pressed"),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Error is swallowed here since the error would occur every time the mouse
|
if pointer_type == "mouse" {
|
||||||
// is clicked when the cursor is grabbed, and there
|
// Error is swallowed here since the error would occur every time the
|
||||||
// is probably not a situation where this could
|
// mouse is clicked when the cursor is
|
||||||
// fail, that we care if it fails.
|
// grabbed, and there is probably not a
|
||||||
let _e = canvas.set_pointer_capture(event.pointer_id());
|
// 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,
|
&mut self,
|
||||||
canvas_common: &Common,
|
canvas_common: &Common,
|
||||||
mut modifier_handler: MOD,
|
|
||||||
mut mouse_handler: M,
|
mut mouse_handler: M,
|
||||||
mut touch_handler: T,
|
mut touch_handler: T,
|
||||||
mut button_handler: B,
|
mut button_handler: B,
|
||||||
prevent_default: Rc<Cell<bool>>,
|
prevent_default: Rc<Cell<bool>>,
|
||||||
) where
|
) where
|
||||||
MOD: 'static + FnMut(ModifiersState),
|
|
||||||
M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
|
M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
|
||||||
T: 'static
|
T: 'static
|
||||||
+ FnMut(ModifiersState, i32, &mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>),
|
+ 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| {
|
Some(canvas_common.add_event("pointermove", move |event: PointerEvent| {
|
||||||
let modifiers = event::mouse_modifiers(&event);
|
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();
|
let id = event.pointer_id();
|
||||||
|
|
||||||
// chorded button event
|
// chorded button event
|
||||||
if let Some(button) = event::mouse_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() {
|
if prevent_default.get() {
|
||||||
// prevent text selection
|
// prevent text selection
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
@@ -212,13 +195,7 @@ impl PointerHandler {
|
|||||||
|
|
||||||
// pointer move event
|
// pointer move event
|
||||||
let scale = super::scale_factor(&window);
|
let scale = super::scale_factor(&window);
|
||||||
match pointer_type.as_str() {
|
match event.pointer_type().as_str() {
|
||||||
"mouse" => mouse_handler(
|
|
||||||
modifiers,
|
|
||||||
id,
|
|
||||||
&mut event::pointer_move_event(event)
|
|
||||||
.map(|event| event::mouse_position(&event).to_physical(scale)),
|
|
||||||
),
|
|
||||||
"touch" => touch_handler(
|
"touch" => touch_handler(
|
||||||
modifiers,
|
modifiers,
|
||||||
id,
|
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)),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use super::media_query_handle::MediaQueryListHandle;
|
|||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct ResizeScaleHandle(Rc<RefCell<ResizeScaleInternal>>);
|
pub struct ResizeScaleHandle(Rc<ResizeScaleInternal>);
|
||||||
|
|
||||||
impl ResizeScaleHandle {
|
impl ResizeScaleHandle {
|
||||||
pub(crate) fn new<S, R>(
|
pub(crate) fn new<S, R>(
|
||||||
@@ -28,8 +28,8 @@ impl ResizeScaleHandle {
|
|||||||
resize_handler: R,
|
resize_handler: R,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
S: 'static + FnMut(PhysicalSize<u32>, f64),
|
S: 'static + Fn(PhysicalSize<u32>, f64),
|
||||||
R: 'static + FnMut(PhysicalSize<u32>),
|
R: 'static + Fn(PhysicalSize<u32>),
|
||||||
{
|
{
|
||||||
Self(ResizeScaleInternal::new(
|
Self(ResizeScaleInternal::new(
|
||||||
window,
|
window,
|
||||||
@@ -42,7 +42,7 @@ impl ResizeScaleHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn notify_resize(&self) {
|
pub(crate) fn notify_resize(&self) {
|
||||||
self.0.borrow_mut().notify()
|
self.0.notify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,11 +53,11 @@ struct ResizeScaleInternal {
|
|||||||
document: Document,
|
document: Document,
|
||||||
canvas: HtmlCanvasElement,
|
canvas: HtmlCanvasElement,
|
||||||
style: Style,
|
style: Style,
|
||||||
mql: MediaQueryListHandle,
|
mql: RefCell<MediaQueryListHandle>,
|
||||||
observer: ResizeObserver,
|
observer: ResizeObserver,
|
||||||
_observer_closure: Closure<dyn FnMut(Array, ResizeObserver)>,
|
_observer_closure: Closure<dyn FnMut(Array, ResizeObserver)>,
|
||||||
scale_handler: Box<dyn FnMut(PhysicalSize<u32>, f64)>,
|
scale_handler: Box<dyn Fn(PhysicalSize<u32>, f64)>,
|
||||||
resize_handler: Box<dyn FnMut(PhysicalSize<u32>)>,
|
resize_handler: Box<dyn Fn(PhysicalSize<u32>)>,
|
||||||
notify_scale: Cell<bool>,
|
notify_scale: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,12 +69,12 @@ impl ResizeScaleInternal {
|
|||||||
style: Style,
|
style: Style,
|
||||||
scale_handler: S,
|
scale_handler: S,
|
||||||
resize_handler: R,
|
resize_handler: R,
|
||||||
) -> Rc<RefCell<Self>>
|
) -> Rc<Self>
|
||||||
where
|
where
|
||||||
S: 'static + FnMut(PhysicalSize<u32>, f64),
|
S: 'static + Fn(PhysicalSize<u32>, f64),
|
||||||
R: 'static + FnMut(PhysicalSize<u32>),
|
R: 'static + Fn(PhysicalSize<u32>),
|
||||||
{
|
{
|
||||||
Rc::<RefCell<ResizeScaleInternal>>::new_cyclic(|weak_self| {
|
Rc::<ResizeScaleInternal>::new_cyclic(|weak_self| {
|
||||||
let mql = Self::create_mql(&window, {
|
let mql = Self::create_mql(&window, {
|
||||||
let weak_self = weak_self.clone();
|
let weak_self = weak_self.clone();
|
||||||
move |mql| {
|
move |mql| {
|
||||||
@@ -86,9 +86,7 @@ impl ResizeScaleInternal {
|
|||||||
|
|
||||||
let weak_self = weak_self.clone();
|
let weak_self = weak_self.clone();
|
||||||
let observer_closure = Closure::new(move |entries: Array, _| {
|
let observer_closure = Closure::new(move |entries: Array, _| {
|
||||||
if let Some(rc_self) = weak_self.upgrade() {
|
if let Some(this) = weak_self.upgrade() {
|
||||||
let mut this = rc_self.borrow_mut();
|
|
||||||
|
|
||||||
let size = this.process_entry(entries);
|
let size = this.process_entry(entries);
|
||||||
|
|
||||||
if this.notify_scale.replace(false) {
|
if this.notify_scale.replace(false) {
|
||||||
@@ -101,18 +99,18 @@ impl ResizeScaleInternal {
|
|||||||
});
|
});
|
||||||
let observer = Self::create_observer(&canvas, observer_closure.as_ref());
|
let observer = Self::create_observer(&canvas, observer_closure.as_ref());
|
||||||
|
|
||||||
RefCell::new(Self {
|
Self {
|
||||||
window,
|
window,
|
||||||
document,
|
document,
|
||||||
canvas,
|
canvas,
|
||||||
style,
|
style,
|
||||||
mql,
|
mql: RefCell::new(mql),
|
||||||
observer,
|
observer,
|
||||||
_observer_closure: observer_closure,
|
_observer_closure: observer_closure,
|
||||||
scale_handler: Box::new(scale_handler),
|
scale_handler: Box::new(scale_handler),
|
||||||
resize_handler: Box::new(resize_handler),
|
resize_handler: Box::new(resize_handler),
|
||||||
notify_scale: Cell::new(false),
|
notify_scale: Cell::new(false),
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +150,7 @@ impl ResizeScaleInternal {
|
|||||||
observer
|
observer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notify(&mut self) {
|
fn notify(&self) {
|
||||||
if !self.document.contains(Some(&self.canvas)) || self.style.get("display") == "none" {
|
if !self.document.contains(Some(&self.canvas)) || self.style.get("display") == "none" {
|
||||||
let size = PhysicalSize::new(0, 0);
|
let size = PhysicalSize::new(0, 0);
|
||||||
|
|
||||||
@@ -200,10 +198,9 @@ impl ResizeScaleInternal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_scale(this: Rc<RefCell<Self>>, mql: &MediaQueryList) {
|
fn handle_scale(self: Rc<Self>, mql: &MediaQueryList) {
|
||||||
let weak_self = Rc::downgrade(&this);
|
let weak_self = Rc::downgrade(&self);
|
||||||
let mut this = this.borrow_mut();
|
let scale = super::scale_factor(&self.window);
|
||||||
let scale = super::scale_factor(&this.window);
|
|
||||||
|
|
||||||
// TODO: confirm/reproduce this problem, see:
|
// TODO: confirm/reproduce this problem, see:
|
||||||
// <https://github.com/rust-windowing/winit/issues/2597>.
|
// <https://github.com/rust-windowing/winit/issues/2597>.
|
||||||
@@ -217,15 +214,15 @@ impl ResizeScaleInternal {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_mql = Self::create_mql(&this.window, move |mql| {
|
let new_mql = Self::create_mql(&self.window, move |mql| {
|
||||||
if let Some(rc_self) = weak_self.upgrade() {
|
if let Some(rc_self) = weak_self.upgrade() {
|
||||||
Self::handle_scale(rc_self, mql);
|
Self::handle_scale(rc_self, mql);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.mql = new_mql;
|
self.mql.replace(new_mql);
|
||||||
|
|
||||||
this.notify_scale.set(true);
|
self.notify_scale.set(true);
|
||||||
this.notify();
|
self.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_entry(&self, entries: Array) -> PhysicalSize<u32> {
|
fn process_entry(&self, entries: Array) -> PhysicalSize<u32> {
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
use js_sys::{Function, Object, Promise, Reflect};
|
use js_sys::{Array, Function, Object, Promise, Reflect};
|
||||||
use std::cell::OnceCell;
|
use std::cell::OnceCell;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use wasm_bindgen::closure::Closure;
|
use wasm_bindgen::closure::Closure;
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
use wasm_bindgen::{JsCast, JsValue};
|
use wasm_bindgen::{JsCast, JsValue};
|
||||||
use web_sys::{AbortController, AbortSignal, MessageChannel, MessagePort};
|
use web_sys::{
|
||||||
|
AbortController, AbortSignal, Blob, BlobPropertyBag, MessageChannel, MessagePort, Url, Worker,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::platform::web::PollStrategy;
|
use crate::platform::web::{PollStrategy, WaitUntilStrategy};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Schedule {
|
pub struct Schedule {
|
||||||
@@ -29,6 +31,7 @@ enum Inner {
|
|||||||
port: MessagePort,
|
port: MessagePort,
|
||||||
_timeout_closure: Closure<dyn FnMut()>,
|
_timeout_closure: Closure<dyn FnMut()>,
|
||||||
},
|
},
|
||||||
|
Worker(MessagePort),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Schedule {
|
impl Schedule {
|
||||||
@@ -45,14 +48,24 @@ impl Schedule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_duration<F>(window: &web_sys::Window, f: F, duration: Duration) -> Schedule
|
pub fn new_with_duration<F>(
|
||||||
|
strategy: WaitUntilStrategy,
|
||||||
|
window: &web_sys::Window,
|
||||||
|
f: F,
|
||||||
|
duration: Duration,
|
||||||
|
) -> Schedule
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(),
|
F: 'static + FnMut(),
|
||||||
{
|
{
|
||||||
if has_scheduler_support(window) {
|
match strategy {
|
||||||
Self::new_scheduler(window, f, Some(duration))
|
WaitUntilStrategy::Scheduler => {
|
||||||
} else {
|
if has_scheduler_support(window) {
|
||||||
Self::new_timeout(window.clone(), f, Some(duration))
|
Self::new_scheduler(window, f, Some(duration))
|
||||||
|
} else {
|
||||||
|
Self::new_timeout(window.clone(), f, Some(duration))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
WaitUntilStrategy::Worker => Self::new_worker(f, duration),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +166,44 @@ impl Schedule {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_worker<F>(f: F, duration: Duration) -> Schedule
|
||||||
|
where
|
||||||
|
F: 'static + FnMut(),
|
||||||
|
{
|
||||||
|
thread_local! {
|
||||||
|
static URL: ScriptUrl = ScriptUrl::new(include_str!("worker.min.js"));
|
||||||
|
static WORKER: Worker = URL.with(|url| Worker::new(&url.0)).expect("`new Worker()` is not expected to fail with a local script");
|
||||||
|
}
|
||||||
|
|
||||||
|
let channel = MessageChannel::new().unwrap();
|
||||||
|
let closure = Closure::new(f);
|
||||||
|
let port_1 = channel.port1();
|
||||||
|
port_1.set_onmessage(Some(closure.as_ref().unchecked_ref()));
|
||||||
|
port_1.start();
|
||||||
|
|
||||||
|
// `Duration::as_millis()` always rounds down (because of truncation), we want to round
|
||||||
|
// up instead. This makes sure that the we never wake up **before** the given time.
|
||||||
|
let duration = duration
|
||||||
|
.as_secs()
|
||||||
|
.try_into()
|
||||||
|
.ok()
|
||||||
|
.and_then(|secs: u32| secs.checked_mul(1000))
|
||||||
|
.and_then(|secs| secs.checked_add(duration_millis_ceil(duration)))
|
||||||
|
.unwrap_or(u32::MAX);
|
||||||
|
|
||||||
|
WORKER
|
||||||
|
.with(|worker| {
|
||||||
|
let port_2 = channel.port2();
|
||||||
|
worker.post_message_with_transfer(
|
||||||
|
&Array::of2(&port_2, &duration.into()),
|
||||||
|
&Array::of1(&port_2).into(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.expect("`Worker.postMessage()` is not expected to fail");
|
||||||
|
|
||||||
|
Schedule { _closure: closure, inner: Inner::Worker(port_1) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Schedule {
|
impl Drop for Schedule {
|
||||||
@@ -165,6 +216,10 @@ impl Drop for Schedule {
|
|||||||
port.close();
|
port.close();
|
||||||
port.set_onmessage(None);
|
port.set_onmessage(None);
|
||||||
},
|
},
|
||||||
|
Inner::Worker(port) => {
|
||||||
|
port.close();
|
||||||
|
port.set_onmessage(None);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,6 +281,29 @@ fn has_idle_callback_support(window: &web_sys::Window) -> bool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 blob = Blob::new_with_str_sequence_and_options(&sequence, &property)
|
||||||
|
.expect("`new Blob()` should never throw");
|
||||||
|
|
||||||
|
let url = Url::create_object_url_with_blob(&blob)
|
||||||
|
.expect("`URL.createObjectURL()` should never throw");
|
||||||
|
|
||||||
|
Self(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ScriptUrl {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
Url::revoke_object_url(&self.0).expect("`URL.revokeObjectURL()` should never throw");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
type WindowSupportExt;
|
type WindowSupportExt;
|
||||||
|
|||||||
10
src/platform_impl/web/web_sys/worker.js
Normal file
10
src/platform_impl/web/web_sys/worker.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
onmessage = event => {
|
||||||
|
const [port, timeout] = event.data
|
||||||
|
const f = () => port.postMessage(undefined)
|
||||||
|
|
||||||
|
if ('scheduler' in this) {
|
||||||
|
scheduler.postTask(f, { delay: timeout })
|
||||||
|
} else {
|
||||||
|
setTimeout(f, timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/platform_impl/web/web_sys/worker.min.js
vendored
Normal file
1
src/platform_impl/web/web_sys/worker.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
onmessage=e=>{let[s,t]=e.data,a=()=>s.postMessage(void 0);"scheduler"in this?scheduler.postTask(a,{delay:t}):setTimeout(a,t)};
|
||||||
@@ -431,7 +431,7 @@ impl Drop for Inner {
|
|||||||
pub struct WindowId(pub(crate) u32);
|
pub struct WindowId(pub(crate) u32);
|
||||||
|
|
||||||
impl WindowId {
|
impl WindowId {
|
||||||
pub const unsafe fn dummy() -> Self {
|
pub const fn dummy() -> Self {
|
||||||
Self(0)
|
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()
|
should_apps_use_dark_mode() && !is_high_contrast()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ use crate::platform_impl::platform::{
|
|||||||
raw_input, util, wrap_device_id, Fullscreen, WindowId, DEVICE_ID,
|
raw_input, util, wrap_device_id, Fullscreen, WindowId, DEVICE_ID,
|
||||||
};
|
};
|
||||||
use crate::window::{
|
use crate::window::{
|
||||||
CustomCursor as RootCustomCursor, CustomCursorSource, WindowId as RootWindowId,
|
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId as RootWindowId,
|
||||||
};
|
};
|
||||||
use runner::{EventLoopRunner, EventLoopRunnerShared};
|
use runner::{EventLoopRunner, EventLoopRunnerShared};
|
||||||
|
|
||||||
@@ -550,6 +550,10 @@ impl ActiveEventLoop {
|
|||||||
raw_input::register_all_mice_and_keyboards_for_raw_input(self.thread_msg_target, allowed);
|
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) {
|
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||||
self.runner_shared.set_control_flow(control_flow)
|
self.runner_shared.set_control_flow(control_flow)
|
||||||
}
|
}
|
||||||
@@ -2441,7 +2445,7 @@ unsafe extern "system" fn thread_event_target_callback(
|
|||||||
if userdata_removed {
|
if userdata_removed {
|
||||||
drop(userdata);
|
drop(userdata);
|
||||||
} else {
|
} else {
|
||||||
Box::into_raw(userdata);
|
Box::leak(userdata);
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user