mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
76 Commits
split-exam
...
v0.29.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00b5de0a68 | ||
|
|
80d1e49354 | ||
|
|
07dd45f8e3 | ||
|
|
4e6ce00ec5 | ||
|
|
65c2482d74 | ||
|
|
ba2bfd064f | ||
|
|
08ad3f19e2 | ||
|
|
e3fbfd6792 | ||
|
|
c40af0062b | ||
|
|
511bf53889 | ||
|
|
7451c4b88c | ||
|
|
42ecef7b31 | ||
|
|
5d9ce7f5f4 | ||
|
|
ef5b71d658 | ||
|
|
4ab36f336c | ||
|
|
2791cbd65e | ||
|
|
03bf83f45e | ||
|
|
02870202cb | ||
|
|
c268922def | ||
|
|
61b921c466 | ||
|
|
794d0c1f73 | ||
|
|
8ce58c7053 | ||
|
|
cff9b01052 | ||
|
|
7e9dc147d8 | ||
|
|
d7827b36d3 | ||
|
|
5b90a4e194 | ||
|
|
281077a0d8 | ||
|
|
d21395bb3f | ||
|
|
f69616ac2c | ||
|
|
645b1ff00f | ||
|
|
3925281652 | ||
|
|
3bf0fa9ec8 | ||
|
|
7de2bc7ae6 | ||
|
|
3f44eb1fd9 | ||
|
|
456c735bfe | ||
|
|
973e6ad400 | ||
|
|
07652c76fb | ||
|
|
7d93c34e42 | ||
|
|
a2e1a0ac19 | ||
|
|
f1a64b3155 | ||
|
|
f8ffa314d0 | ||
|
|
e28974bc04 | ||
|
|
93f5f1ac3c | ||
|
|
a02c680a87 | ||
|
|
6bb62d0b13 | ||
|
|
fae4cbd2aa | ||
|
|
7a954c7e08 | ||
|
|
164dce2b8a | ||
|
|
0ba4283c29 | ||
|
|
62b4ba8b50 | ||
|
|
afebe2e7d1 | ||
|
|
0efcfaf5a9 | ||
|
|
d86ce9de9f | ||
|
|
7fa7cea700 | ||
|
|
36ebad3246 | ||
|
|
912c45e9f7 | ||
|
|
e2c71a4422 | ||
|
|
b50d9a0228 | ||
|
|
692f15c49f | ||
|
|
5366694db2 | ||
|
|
7962271faa | ||
|
|
78b5f2feb8 | ||
|
|
4baab2d93e | ||
|
|
79385ecd1f | ||
|
|
8d18043a3c | ||
|
|
297c3f80eb | ||
|
|
1d80005b91 | ||
|
|
fab0f62c5a | ||
|
|
d83188befd | ||
|
|
b9d89e97ed | ||
|
|
5fa4b8f003 | ||
|
|
7a4ce631bd | ||
|
|
8d5f82f0c0 | ||
|
|
08fe32eac3 | ||
|
|
1cddc96a0b | ||
|
|
84ef89eb1c |
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
rust_version: ['1.64.0', stable, nightly]
|
rust_version: ['1.65.0', stable, nightly]
|
||||||
platform:
|
platform:
|
||||||
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
|
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
|
||||||
- { target: x86_64-pc-windows-msvc, os: windows-latest, }
|
- { target: x86_64-pc-windows-msvc, os: windows-latest, }
|
||||||
@@ -116,7 +116,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
if: >
|
if: >
|
||||||
!contains(matrix.platform.target, 'redox') &&
|
!contains(matrix.platform.target, 'redox') &&
|
||||||
matrix.rust_version != '1.64.0'
|
matrix.rust_version != '1.65.0'
|
||||||
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
@@ -126,7 +126,7 @@ jobs:
|
|||||||
!contains(matrix.platform.target, 'ios') &&
|
!contains(matrix.platform.target, 'ios') &&
|
||||||
!contains(matrix.platform.target, 'wasm32') &&
|
!contains(matrix.platform.target, 'wasm32') &&
|
||||||
!contains(matrix.platform.target, 'redox') &&
|
!contains(matrix.platform.target, 'redox') &&
|
||||||
matrix.rust_version != '1.64.0'
|
matrix.rust_version != '1.65.0'
|
||||||
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||||
|
|
||||||
- name: Lint with clippy
|
- name: Lint with clippy
|
||||||
@@ -138,7 +138,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
if: >
|
if: >
|
||||||
!contains(matrix.platform.target, 'redox') &&
|
!contains(matrix.platform.target, 'redox') &&
|
||||||
matrix.rust_version != '1.64.0'
|
matrix.rust_version != '1.65.0'
|
||||||
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
||||||
- name: Run tests with serde enabled
|
- name: Run tests with serde enabled
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -147,5 +147,5 @@ jobs:
|
|||||||
!contains(matrix.platform.target, 'ios') &&
|
!contains(matrix.platform.target, 'ios') &&
|
||||||
!contains(matrix.platform.target, 'wasm32') &&
|
!contains(matrix.platform.target, 'wasm32') &&
|
||||||
!contains(matrix.platform.target, 'redox') &&
|
!contains(matrix.platform.target, 'redox') &&
|
||||||
matrix.rust_version != '1.64.0'
|
matrix.rust_version != '1.65.0'
|
||||||
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
||||||
|
|||||||
65
CHANGELOG.md
65
CHANGELOG.md
@@ -8,6 +8,71 @@ And please only add new entries to the top of this list, right below the `# Unre
|
|||||||
|
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
|
# 0.29.0-beta.1
|
||||||
|
|
||||||
|
- **Breaking:** Bump `ndk` version to `0.8.0-beta.0`, ndk-sys to `v0.5.0-beta.0`, `android-activity` to `0.5.0-beta.1`.
|
||||||
|
- **Breaking:** Bump MSRV from `1.64` to `1.65`.
|
||||||
|
- Make iOS windows usable from other threads.
|
||||||
|
- Reexport `raw-window-handle` in `window` module.
|
||||||
|
- **Breaking:** `WINIT_UNIX_BACKEND` was removed in favor of standard `WAYLAND_DISPLAY` and `DISPLAY` variables.
|
||||||
|
- **Breaking:** `EventLoop::new` and `EventLoopBuilder::build` now return `Result<Self, EventLoopError>`
|
||||||
|
- On X11, set `visual_id` in returned `raw-window-handle`.
|
||||||
|
- **Breaking:** on Wayland, dispatching user created wayland queue won't wake up the loop unless winit has event to send back.
|
||||||
|
- Removed platform-specific extensions that should be retrieved through `raw-window-handle` trait implementations instead:
|
||||||
|
- `platform::windows::HINSTANCE`.
|
||||||
|
- `WindowExtWindows::hinstance`.
|
||||||
|
- `WindowExtWindows::hwnd`.
|
||||||
|
- `WindowExtIOS::ui_window`.
|
||||||
|
- `WindowExtIOS::ui_view_controller`.
|
||||||
|
- `WindowExtIOS::ui_view`.
|
||||||
|
- `WindowExtMacOS::ns_window`.
|
||||||
|
- `WindowExtMacOS::ns_view`.
|
||||||
|
- `EventLoopWindowTargetExtWayland::wayland_display`.
|
||||||
|
- `WindowExtWayland::wayland_surface`.
|
||||||
|
- `WindowExtWayland::wayland_display`.
|
||||||
|
- `WindowExtX11::xlib_window`.
|
||||||
|
- `WindowExtX11::xlib_display`.
|
||||||
|
- `WindowExtX11::xlib_screen_id`.
|
||||||
|
- `WindowExtX11::xcb_connection`.
|
||||||
|
- On Web, use `Window.requestAnimationFrame()` to throttle `RedrawRequested` events.
|
||||||
|
- On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor.
|
||||||
|
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
|
||||||
|
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.
|
||||||
|
- **Breaking:** Remove lifetime parameter from `Event` and `WindowEvent`.
|
||||||
|
- **Breaking:** `ScaleFactorChanged` now contains a writer instead of a reference to update inner size.
|
||||||
|
- On iOS, always wake the event loop when transitioning from `ControlFlow::Poll` to `ControlFlow::Poll`.
|
||||||
|
- **Breaking:** `ActivationTokenDone` event which could be requested with the new `startup_notify` module, see its docs for more.
|
||||||
|
- On Wayland, make double clicking and moving the CSD frame more reliable.
|
||||||
|
- On macOS, add tabbing APIs on `WindowExtMacOS` and `EventLoopWindowTargetExtMacOS`.
|
||||||
|
- **Breaking:** Rename `Window::set_inner_size` to `Window::request_inner_size` and indicate if the size was applied immediately.
|
||||||
|
- On X11, fix false positive flagging of key repeats when pressing different keys with no release between presses.
|
||||||
|
- Implement `PartialOrd` and `Ord` for `Key`, `KeyCode`, `NativeKey`, and `NativeKeyCode`.
|
||||||
|
- Add `ElementState::is_pressed`.
|
||||||
|
- On Web, implement `WindowEvent::Occluded`.
|
||||||
|
- On Web, fix touch location to be as accurate as mouse position.
|
||||||
|
- On Web, account for CSS `padding`, `border`, and `margin` when getting or setting the canvas position.
|
||||||
|
- On Web, add Fullscreen API compatibility for Safari.
|
||||||
|
- On Web, implement `Window::set_(min|max)_inner_size()`.
|
||||||
|
- On Web, fix some `Window` methods using incorrect HTML attributes instead of CSS properties.
|
||||||
|
- On Web, fix some `WindowBuilder` methods doing nothing.
|
||||||
|
- On Web, implement `Window::focus_window()`.
|
||||||
|
- On Web, remove unnecessary `Window::is_dark_mode()`, which was replaced with `Window::theme()`.
|
||||||
|
- On Web, add `WindowBuilderExtWebSys::with_append()` to append the canvas element to the web page on creation.
|
||||||
|
- On Windows, add `drag_resize_window` method support.
|
||||||
|
- **Breaking** `run() ->!` has been replaced by `run() -> Result<(), EventLoopError>` for returning errors without calling `std::process::exit()` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
|
||||||
|
- **Breaking** Removed `EventLoopExtRunReturn` / `run_return` in favor of `EventLoopExtPumpEvents` / `pump_events` and `EventLoopExtRunOnDemand` / `run_ondemand` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
|
||||||
|
- `RedrawRequested` is no longer guaranteed to be emitted after `MainEventsCleared`, it is now platform-specific when the event is emitted after being requested via `redraw_request()`.
|
||||||
|
- On Windows, `RedrawRequested` is now driven by `WM_PAINT` messages which are requested via `redraw_request()`
|
||||||
|
- **Breaking** `LoopDestroyed` renamed to `LoopExiting` ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||||
|
- **Breaking** `RedrawEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||||
|
- **Breaking** `MainEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||||
|
- Added `AboutToWait` event which is emitted when the event loop is about to block and wait for new events ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||||
|
- **Breaking:** `with_x11_visual` now takes the visual ID instead of the bare pointer.
|
||||||
|
- On X11, add a `with_embedded_parent_window` function to the window builder to allow embedding a window into another window.
|
||||||
|
- On iOS, add force data to touch events when using the Apple Pencil.
|
||||||
|
|
||||||
|
# 0.29.0-beta.0
|
||||||
|
|
||||||
- On Web, allow event loops to be recreated with `spawn`.
|
- On Web, allow event loops to be recreated with `spawn`.
|
||||||
- **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone.
|
- **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone.
|
||||||
- On Android, changed default behavior of Android to ignore volume keys letting the operating system handle them.
|
- On Android, changed default behavior of Android to ignore volume keys letting the operating system handle them.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ your description of the issue as detailed as possible:
|
|||||||
|
|
||||||
When making a code contribution to winit, before opening your pull request, please make sure that:
|
When making a code contribution to winit, before opening your pull request, please make sure that:
|
||||||
|
|
||||||
- your patch builds with Winit's minimal supported rust version - Rust 1.64.
|
- your patch builds with Winit's minimal supported rust version - Rust 1.65.
|
||||||
- you tested your modifications on all the platforms impacted, or if not possible detail which platforms
|
- you tested your modifications on all the platforms impacted, or if not possible detail which platforms
|
||||||
were not tested, and what should be tested, so that a maintainer or another contributor can test them
|
were not tested, and what should be tested, so that a maintainer or another contributor can test them
|
||||||
- you updated any relevant documentation in winit
|
- you updated any relevant documentation in winit
|
||||||
|
|||||||
53
Cargo.toml
53
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "winit"
|
name = "winit"
|
||||||
version = "0.28.6"
|
version = "0.29.0-beta.1"
|
||||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||||
description = "Cross-platform window creation library."
|
description = "Cross-platform window creation library."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -10,7 +10,7 @@ readme = "README.md"
|
|||||||
repository = "https://github.com/rust-windowing/winit"
|
repository = "https://github.com/rust-windowing/winit"
|
||||||
documentation = "https://docs.rs/winit"
|
documentation = "https://docs.rs/winit"
|
||||||
categories = ["gui"]
|
categories = ["gui"]
|
||||||
rust-version = "1.64.0"
|
rust-version = "1.65.0"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
@@ -36,7 +36,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||||
x11 = ["x11-dl", "percent-encoding", "xkbcommon-dl/x11"]
|
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
|
||||||
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "sctk", "fnv", "memmap2"]
|
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "sctk", "fnv", "memmap2"]
|
||||||
wayland-dlopen = ["wayland-backend/dlopen"]
|
wayland-dlopen = ["wayland-backend/dlopen"]
|
||||||
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
|
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
|
||||||
@@ -55,7 +55,7 @@ cursor-icon = "1.0.0"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
mint = { version = "0.5.6", optional = true }
|
mint = { version = "0.5.6", optional = true }
|
||||||
once_cell = "1.12"
|
once_cell = "1.12"
|
||||||
raw_window_handle = { package = "raw-window-handle", version = "0.5" }
|
raw_window_handle = { package = "raw-window-handle", version = "0.5", features = ["std"] }
|
||||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||||
smol_str = "0.2.0"
|
smol_str = "0.2.0"
|
||||||
|
|
||||||
@@ -67,18 +67,45 @@ simple_logger = { version = "2.1.0", default_features = false }
|
|||||||
softbuffer = "0.3.0"
|
softbuffer = "0.3.0"
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
# Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995
|
# Coordinate the next winit release android-activity 0.5 release
|
||||||
android-activity = "0.4.0"
|
android-activity = "0.5.0-beta.1"
|
||||||
ndk = "0.7.0"
|
ndk = "0.8.0-beta.0"
|
||||||
ndk-sys = "0.4.0"
|
ndk-sys = "0.5.0-beta.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.3.0-beta.3, <0.3.0-beta.4" # Allow `0.3.0-beta.3.patch-leaks`
|
objc2 = "0.4.1"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
core-graphics = "0.22.3"
|
core-graphics = "0.22.3"
|
||||||
dispatch = "0.2.0"
|
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies.icrate]
|
||||||
|
version = "0.0.4"
|
||||||
|
features = [
|
||||||
|
"dispatch",
|
||||||
|
"Foundation",
|
||||||
|
"Foundation_NSArray",
|
||||||
|
"Foundation_NSAttributedString",
|
||||||
|
"Foundation_NSMutableAttributedString",
|
||||||
|
"Foundation_NSData",
|
||||||
|
"Foundation_NSDictionary",
|
||||||
|
"Foundation_NSString",
|
||||||
|
"Foundation_NSProcessInfo",
|
||||||
|
"Foundation_NSThread",
|
||||||
|
"Foundation_NSNumber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "ios")'.dependencies.icrate]
|
||||||
|
version = "0.0.4"
|
||||||
|
features = [
|
||||||
|
"dispatch",
|
||||||
|
"Foundation",
|
||||||
|
"Foundation_NSArray",
|
||||||
|
"Foundation_NSString",
|
||||||
|
"Foundation_NSProcessInfo",
|
||||||
|
"Foundation_NSThread",
|
||||||
|
"Foundation_NSSet",
|
||||||
|
]
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
unicode-segmentation = "1.7.1"
|
unicode-segmentation = "1.7.1"
|
||||||
@@ -113,6 +140,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]
|
||||||
|
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
||||||
libc = "0.2.64"
|
libc = "0.2.64"
|
||||||
percent-encoding = { version = "2.0", optional = true }
|
percent-encoding = { version = "2.0", optional = true }
|
||||||
fnv = { version = "1.0.3", optional = true }
|
fnv = { version = "1.0.3", optional = true }
|
||||||
@@ -122,7 +150,9 @@ wayland-client = { version = "0.30.0", optional = true }
|
|||||||
wayland-backend = { version = "0.1.0", default_features = false, features = ["client_system"], optional = true }
|
wayland-backend = { version = "0.1.0", default_features = false, features = ["client_system"], optional = true }
|
||||||
wayland-protocols = { version = "0.30.0", features = [ "staging"], optional = true }
|
wayland-protocols = { version = "0.30.0", features = [ "staging"], optional = true }
|
||||||
calloop = "0.10.5"
|
calloop = "0.10.5"
|
||||||
|
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
|
||||||
x11-dl = { version = "2.18.5", optional = true }
|
x11-dl = { version = "2.18.5", optional = true }
|
||||||
|
x11rb = { version = "0.12.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "xinput", "xkb"], optional = true }
|
||||||
xkbcommon-dl = "0.4.0"
|
xkbcommon-dl = "0.4.0"
|
||||||
memmap2 = { version = "0.5.0", optional = true }
|
memmap2 = { version = "0.5.0", optional = true }
|
||||||
|
|
||||||
@@ -145,6 +175,8 @@ features = [
|
|||||||
'FocusEvent',
|
'FocusEvent',
|
||||||
'HtmlCanvasElement',
|
'HtmlCanvasElement',
|
||||||
'HtmlElement',
|
'HtmlElement',
|
||||||
|
'IntersectionObserver',
|
||||||
|
'IntersectionObserverEntry',
|
||||||
'KeyboardEvent',
|
'KeyboardEvent',
|
||||||
'MediaQueryList',
|
'MediaQueryList',
|
||||||
'Node',
|
'Node',
|
||||||
@@ -155,6 +187,7 @@ features = [
|
|||||||
'ResizeObserverEntry',
|
'ResizeObserverEntry',
|
||||||
'ResizeObserverOptions',
|
'ResizeObserverOptions',
|
||||||
'ResizeObserverSize',
|
'ResizeObserverSize',
|
||||||
|
'VisibilityState',
|
||||||
'Window',
|
'Window',
|
||||||
'WheelEvent'
|
'WheelEvent'
|
||||||
]
|
]
|
||||||
|
|||||||
20
FEATURES.md
20
FEATURES.md
@@ -13,7 +13,9 @@ be used to create both games and applications. It supports the following main gr
|
|||||||
- iOS
|
- iOS
|
||||||
- Android
|
- Android
|
||||||
- Web
|
- Web
|
||||||
- via WASM
|
- Chrome
|
||||||
|
- Firefox
|
||||||
|
- Safari 13.1+
|
||||||
|
|
||||||
Most platforms expose capabilities that cannot be meaningfully transposed onto others. Winit does not
|
Most platforms expose capabilities that cannot be meaningfully transposed onto others. Winit does not
|
||||||
aim to support every single feature of every platform, but rather to abstract over the common features
|
aim to support every single feature of every platform, but rather to abstract over the common features
|
||||||
@@ -117,6 +119,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
|||||||
|
|
||||||
## Platform
|
## Platform
|
||||||
### Windows
|
### Windows
|
||||||
|
* Setting the name of the internal window class
|
||||||
* Setting the taskbar icon
|
* Setting the taskbar icon
|
||||||
* Setting the parent window
|
* Setting the parent window
|
||||||
* Setting a menu bar
|
* Setting a menu bar
|
||||||
@@ -143,9 +146,6 @@ If your PR makes notable changes to Winit's features, please update this section
|
|||||||
|
|
||||||
### iOS
|
### iOS
|
||||||
* `winit` has a minimum OS requirement of iOS 8
|
* `winit` has a minimum OS requirement of iOS 8
|
||||||
* Get the `UIWindow` object pointer
|
|
||||||
* Get the `UIViewController` object pointer
|
|
||||||
* Get the `UIView` object pointer
|
|
||||||
* Get the `UIScreen` object pointer
|
* Get the `UIScreen` object pointer
|
||||||
* Setting the `UIView` hidpi factor
|
* Setting the `UIView` hidpi factor
|
||||||
* Valid orientations
|
* Valid orientations
|
||||||
@@ -172,7 +172,7 @@ Legend:
|
|||||||
- ❓: Unknown status
|
- ❓: Unknown status
|
||||||
|
|
||||||
### Windowing
|
### Windowing
|
||||||
|Feature |Windows|MacOS |Linux x11 |Linux Wayland |Android|iOS |WASM |Redox OS|
|
|Feature |Windows|MacOS |Linux x11 |Linux Wayland |Android|iOS |Web |Redox OS|
|
||||||
|-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
|-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||||
|Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |✔️ |✔️ |
|
|Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |✔️ |✔️ |
|
||||||
|Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |
|
|Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |
|
||||||
@@ -192,13 +192,13 @@ Legend:
|
|||||||
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|**N/A** |
|
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|**N/A** |
|
||||||
|
|
||||||
### System information
|
### System information
|
||||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|
||||||
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- | ------ |
|
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- | ------ |
|
||||||
|Monitor list |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|
|Monitor list |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|
||||||
|Video mode query |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|
|Video mode query |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|
||||||
|
|
||||||
### Input handling
|
### Input handling
|
||||||
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|
||||||
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||||
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
||||||
|Mouse set location |✔️ |✔️ |✔️ |✔️(when locked) |**N/A**|**N/A**|**N/A**|**N/A** |
|
|Mouse set location |✔️ |✔️ |✔️ |✔️(when locked) |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||||
@@ -215,19 +215,19 @@ Legend:
|
|||||||
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |**N/A** |
|
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |**N/A** |
|
||||||
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |**N/A** |
|
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |**N/A** |
|
||||||
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
|
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
|
||||||
|Resize with cursor |❌ |❌ |✔️ |❌ |**N/A**|**N/A**|**N/A** |**N/A** |
|
|Resize with cursor |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
|
||||||
|
|
||||||
### Pending API Reworks
|
### Pending API Reworks
|
||||||
Changes in the API that have been agreed upon but aren't implemented across all platforms.
|
Changes in the API that have been agreed upon but aren't implemented across all platforms.
|
||||||
|
|
||||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|
||||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||||
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
|
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
|
||||||
|Event Loop 2.0 ([#459]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |✔️ |
|
|Event Loop 2.0 ([#459]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |✔️ |
|
||||||
|Keyboard Input 2.0 ([#753]) |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
|
|Keyboard Input 2.0 ([#753]) |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
|
||||||
|
|
||||||
### Completed API Reworks
|
### Completed API Reworks
|
||||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|
||||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||||
|
|
||||||
[#165]: https://github.com/rust-windowing/winit/issues/165
|
[#165]: https://github.com/rust-windowing/winit/issues/165
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
winit = "0.28.6"
|
winit = "0.29.0-beta.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
## [Documentation](https://docs.rs/winit)
|
## [Documentation](https://docs.rs/winit)
|
||||||
|
|||||||
4
build.rs
4
build.rs
@@ -8,12 +8,12 @@ fn main() {
|
|||||||
cfg_aliases! {
|
cfg_aliases! {
|
||||||
// Systems.
|
// Systems.
|
||||||
android_platform: { target_os = "android" },
|
android_platform: { target_os = "android" },
|
||||||
wasm_platform: { target_family = "wasm" },
|
wasm_platform: { all(target_family = "wasm", not(target_os = "emscripten")) },
|
||||||
macos_platform: { target_os = "macos" },
|
macos_platform: { target_os = "macos" },
|
||||||
ios_platform: { target_os = "ios" },
|
ios_platform: { target_os = "ios" },
|
||||||
windows_platform: { target_os = "windows" },
|
windows_platform: { target_os = "windows" },
|
||||||
apple: { any(target_os = "ios", target_os = "macos") },
|
apple: { any(target_os = "ios", target_os = "macos") },
|
||||||
free_unix: { all(unix, not(apple), not(android_platform)) },
|
free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) },
|
||||||
redox: { target_os = "redox" },
|
redox: { target_os = "redox" },
|
||||||
|
|
||||||
// Native displays.
|
// Native displays.
|
||||||
|
|||||||
@@ -4,4 +4,9 @@ disallowed-methods = [
|
|||||||
{ path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
|
{ path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||||
{ path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
|
{ path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
|
||||||
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
|
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||||
|
{ path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" },
|
||||||
|
{ path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" },
|
||||||
|
{ path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
|
||||||
|
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
|
||||||
|
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
#[cfg(any(x11_platform, macos_platform, windows_platform))]
|
#[cfg(any(x11_platform, macos_platform, windows_platform))]
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use raw_window_handle::HasRawWindowHandle;
|
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::{LogicalPosition, LogicalSize, Position},
|
dpi::{LogicalPosition, LogicalSize, Position},
|
||||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||||
|
window::raw_window_handle::HasRawWindowHandle,
|
||||||
window::{Window, WindowBuilder, WindowId},
|
window::{Window, WindowBuilder, WindowId},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ fn main() {
|
|||||||
|
|
||||||
let mut windows = HashMap::new();
|
let mut windows = HashMap::new();
|
||||||
|
|
||||||
let event_loop: EventLoop<()> = EventLoop::new();
|
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
|
||||||
let parent_window = WindowBuilder::new()
|
let parent_window = WindowBuilder::new()
|
||||||
.with_title("parent window")
|
.with_title("parent window")
|
||||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||||
@@ -46,7 +46,7 @@ fn main() {
|
|||||||
|
|
||||||
println!("parent window: {parent_window:?})");
|
println!("parent window: {parent_window:?})");
|
||||||
|
|
||||||
event_loop.run(move |event: Event<'_, ()>, event_loop, control_flow| {
|
event_loop.run(move |event: Event<()>, event_loop, control_flow| {
|
||||||
*control_flow = ControlFlow::Wait;
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
if let Event::WindowEvent { event, window_id } = event {
|
if let Event::WindowEvent { event, window_id } = event {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ enum Mode {
|
|||||||
const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
|
const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
|
||||||
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
|
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
|
|
||||||
println!("Press '1' to switch to Wait mode.");
|
println!("Press '1' to switch to Wait mode.");
|
||||||
@@ -36,7 +36,7 @@ fn main() {
|
|||||||
println!("Press 'R' to toggle request_redraw() calls.");
|
println!("Press 'R' to toggle request_redraw() calls.");
|
||||||
println!("Press 'Esc' to close the window.");
|
println!("Press 'Esc' to close the window.");
|
||||||
|
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.")
|
.with_title("Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.")
|
||||||
.build(&event_loop)
|
.build(&event_loop)
|
||||||
@@ -95,18 +95,11 @@ fn main() {
|
|||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
Event::MainEventsCleared => {
|
Event::AboutToWait => {
|
||||||
if request_redraw && !wait_cancelled && !close_requested {
|
if request_redraw && !wait_cancelled && !close_requested {
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
}
|
}
|
||||||
if close_requested {
|
|
||||||
control_flow.set_exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::RedrawRequested(_window_id) => {
|
|
||||||
fill::fill_window(&window);
|
|
||||||
}
|
|
||||||
Event::RedrawEventsCleared => {
|
|
||||||
match mode {
|
match mode {
|
||||||
Mode::Wait => control_flow.set_wait(),
|
Mode::Wait => control_flow.set_wait(),
|
||||||
Mode::WaitUntil => {
|
Mode::WaitUntil => {
|
||||||
@@ -119,8 +112,15 @@ fn main() {
|
|||||||
control_flow.set_poll();
|
control_flow.set_poll();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if close_requested {
|
||||||
|
control_flow.set_exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::RedrawRequested(_window_id) => {
|
||||||
|
fill::fill_window(&window);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||||
window.set_title("A fantastic window!");
|
window.set_title("A fantastic window!");
|
||||||
@@ -54,7 +54,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const CURSORS: &[CursorIcon] = &[
|
const CURSORS: &[CursorIcon] = &[
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
|
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
|
||||||
@@ -73,5 +73,5 @@ fn main() {
|
|||||||
Event::RedrawRequested(_) => fill::fill_window(&window),
|
Event::RedrawRequested(_) => fill::fill_window(&window),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#![allow(clippy::single_match)]
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
#[cfg(not(wasm_platform))]
|
#[cfg(not(wasm_platform))]
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{Event, WindowEvent},
|
event::{Event, WindowEvent},
|
||||||
@@ -18,7 +18,9 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoopBuilder::<CustomEvent>::with_user_event().build();
|
let event_loop = EventLoopBuilder::<CustomEvent>::with_user_event()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
@@ -52,7 +54,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(wasm_platform)]
|
#[cfg(wasm_platform)]
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window_1 = WindowBuilder::new().build(&event_loop).unwrap();
|
let window_1 = WindowBuilder::new().build(&event_loop).unwrap();
|
||||||
let window_2 = WindowBuilder::new().build(&event_loop).unwrap();
|
let window_2 = WindowBuilder::new().build(&event_loop).unwrap();
|
||||||
@@ -69,7 +69,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name_windows(window_id: WindowId, switched: bool, window_1: &Window, window_2: &Window) {
|
fn name_windows(window_id: WindowId, switched: bool, window_1: &Window, window_2: &Window) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#![allow(clippy::single_match)]
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::dpi::PhysicalSize;
|
||||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
use winit::keyboard::Key;
|
use winit::keyboard::Key;
|
||||||
@@ -12,12 +13,14 @@ use winit::platform::macos::WindowExtMacOS;
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let mut decorations = true;
|
let mut decorations = true;
|
||||||
let mut minimized = false;
|
let mut minimized = false;
|
||||||
|
let mut with_min_size = false;
|
||||||
|
let mut with_max_size = false;
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("Hello world!")
|
.with_title("Hello world!")
|
||||||
@@ -46,6 +49,8 @@ fn main() {
|
|||||||
println!("- D\tToggle window decorations");
|
println!("- D\tToggle window decorations");
|
||||||
println!("- X\tMaximize window");
|
println!("- X\tMaximize window");
|
||||||
println!("- Z\tMinimize window");
|
println!("- Z\tMinimize window");
|
||||||
|
println!("- I\tToggle mIn size limit");
|
||||||
|
println!("- A\tToggle mAx size limit");
|
||||||
|
|
||||||
event_loop.run(move |event, elwt, control_flow| {
|
event_loop.run(move |event, elwt, control_flow| {
|
||||||
control_flow.set_wait();
|
control_flow.set_wait();
|
||||||
@@ -120,6 +125,32 @@ fn main() {
|
|||||||
minimized = !minimized;
|
minimized = !minimized;
|
||||||
window.set_minimized(minimized);
|
window.set_minimized(minimized);
|
||||||
}
|
}
|
||||||
|
"i" => {
|
||||||
|
with_min_size = !with_min_size;
|
||||||
|
let min_size = if with_min_size {
|
||||||
|
Some(PhysicalSize::new(100, 100))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
window.set_min_inner_size(min_size);
|
||||||
|
eprintln!(
|
||||||
|
"Min: {with_min_size}: {min_size:?} => {:?}",
|
||||||
|
window.inner_size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"a" => {
|
||||||
|
with_max_size = !with_max_size;
|
||||||
|
let max_size = if with_max_size {
|
||||||
|
Some(PhysicalSize::new(200, 200))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
window.set_max_inner_size(max_size);
|
||||||
|
eprintln!(
|
||||||
|
"Max: {with_max_size}: {max_size:?} => {:?}",
|
||||||
|
window.inner_size()
|
||||||
|
);
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
@@ -131,5 +162,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("Your faithful window")
|
.with_title("Your faithful window")
|
||||||
@@ -87,5 +87,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new()
|
SimpleLogger::new()
|
||||||
.with_level(LevelFilter::Trace)
|
.with_level(LevelFilter::Trace)
|
||||||
.init()
|
.init()
|
||||||
@@ -24,7 +24,7 @@ fn main() {
|
|||||||
println!("Press F2 to toggle IME. See the documentation of `set_ime_allowed` for more info");
|
println!("Press F2 to toggle IME. See the documentation of `set_ime_allowed` for more info");
|
||||||
println!("Press F3 to cycle through IME purposes.");
|
println!("Press F3 to cycle through IME purposes.");
|
||||||
|
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64))
|
.with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64))
|
||||||
@@ -105,5 +105,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
|
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
simple_logger::SimpleLogger::new().init().unwrap();
|
simple_logger::SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_inner_size(LogicalSize::new(400.0, 200.0))
|
.with_inner_size(LogicalSize::new(400.0, 200.0))
|
||||||
@@ -61,5 +61,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use winit::{event_loop::EventLoop, window::WindowBuilder};
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||||
|
|
||||||
if let Some(mon) = window.primary_monitor() {
|
if let Some(mon) = window.primary_monitor() {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("Mouse Wheel events")
|
.with_title("Mouse Wheel events")
|
||||||
@@ -64,5 +64,5 @@ In other words, the deltas indicate the direction in which to move the content (
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#![allow(clippy::single_match)]
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
#[cfg(not(wasm_platform))]
|
#[cfg(not(wasm_platform))]
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
|
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
|
||||||
|
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
@@ -17,7 +17,7 @@ fn main() {
|
|||||||
const WINDOW_SIZE: PhysicalSize<u32> = PhysicalSize::new(600, 400);
|
const WINDOW_SIZE: PhysicalSize<u32> = PhysicalSize::new(600, 400);
|
||||||
|
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
|
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
|
||||||
for _ in 0..WINDOW_COUNT {
|
for _ in 0..WINDOW_COUNT {
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
@@ -137,13 +137,15 @@ fn main() {
|
|||||||
}),
|
}),
|
||||||
"q" => window.request_redraw(),
|
"q" => window.request_redraw(),
|
||||||
"r" => window.set_resizable(state),
|
"r" => window.set_resizable(state),
|
||||||
"s" => window.set_inner_size(match state {
|
"s" => {
|
||||||
true => PhysicalSize::new(
|
let _ = window.request_inner_size(match state {
|
||||||
WINDOW_SIZE.width + 100,
|
true => PhysicalSize::new(
|
||||||
WINDOW_SIZE.height + 100,
|
WINDOW_SIZE.width + 100,
|
||||||
),
|
WINDOW_SIZE.height + 100,
|
||||||
false => WINDOW_SIZE,
|
),
|
||||||
}),
|
false => WINDOW_SIZE,
|
||||||
|
});
|
||||||
|
}
|
||||||
"w" => {
|
"w" => {
|
||||||
if let Size::Physical(size) = WINDOW_SIZE.into() {
|
if let Size::Physical(size) = WINDOW_SIZE.into() {
|
||||||
window
|
window
|
||||||
@@ -193,9 +195,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(tx) = window_senders.get(&window_id) {
|
if let Some(tx) = window_senders.get(&window_id) {
|
||||||
if let Some(event) = event.to_static() {
|
tx.send(event).unwrap();
|
||||||
tx.send(event).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let mut windows = HashMap::new();
|
let mut windows = HashMap::new();
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
@@ -41,5 +41,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#![allow(clippy::single_match)]
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
#[cfg(not(wasm_platform))]
|
#[cfg(not(wasm_platform))]
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
use std::{sync::Arc, thread, time};
|
use std::{sync::Arc, thread, time};
|
||||||
|
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
@@ -15,7 +15,7 @@ fn main() {
|
|||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = {
|
let window = {
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
@@ -49,7 +49,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(wasm_platform)]
|
#[cfg(wasm_platform)]
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let mut resizable = false;
|
let mut resizable = false;
|
||||||
|
|
||||||
@@ -53,5 +53,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
126
examples/startup_notification.rs
Normal file
126
examples/startup_notification.rs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
//! Demonstrates the use of startup notifications on Linux.
|
||||||
|
|
||||||
|
#[cfg(any(x11_platform, wayland_platform))]
|
||||||
|
#[path = "./util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
#[cfg(any(x11_platform, wayland_platform))]
|
||||||
|
mod example {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||||
|
use winit::event_loop::EventLoop;
|
||||||
|
use winit::keyboard::Key;
|
||||||
|
use winit::platform::startup_notify::{
|
||||||
|
EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify,
|
||||||
|
};
|
||||||
|
use winit::window::{Window, WindowBuilder, WindowId};
|
||||||
|
|
||||||
|
pub(super) fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
// Create the event loop and get the activation token.
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
let mut current_token = match event_loop.read_token_from_env() {
|
||||||
|
Some(token) => Some(token),
|
||||||
|
None => {
|
||||||
|
println!("No startup notification token found in environment.");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut windows: HashMap<WindowId, Rc<Window>> = HashMap::new();
|
||||||
|
let mut counter = 0;
|
||||||
|
let mut create_first_window = false;
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt, flow| {
|
||||||
|
match event {
|
||||||
|
Event::Resumed => create_first_window = true,
|
||||||
|
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id,
|
||||||
|
event:
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key,
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
},
|
||||||
|
} => {
|
||||||
|
if logical_key == Key::Character("n".into()) {
|
||||||
|
if let Some(window) = windows.get(&window_id) {
|
||||||
|
// Request a new activation token on this window.
|
||||||
|
// Once we get it we will use it to create a window.
|
||||||
|
window
|
||||||
|
.request_activation_token()
|
||||||
|
.expect("Failed to request activation token.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id,
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
} => {
|
||||||
|
// Remove the window from the map.
|
||||||
|
windows.remove(&window_id);
|
||||||
|
if windows.is_empty() {
|
||||||
|
flow.set_exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::ActivationTokenDone { token, .. },
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
current_token = Some(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::RedrawRequested(id) => {
|
||||||
|
if let Some(window) = windows.get(&id) {
|
||||||
|
super::fill::fill_window(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we've passed the deadline.
|
||||||
|
if current_token.is_some() || create_first_window {
|
||||||
|
// Create the initial window.
|
||||||
|
let window = {
|
||||||
|
let mut builder =
|
||||||
|
WindowBuilder::new().with_title(format!("Window {}", counter));
|
||||||
|
|
||||||
|
if let Some(token) = current_token.take() {
|
||||||
|
println!("Creating a window with token {token:?}");
|
||||||
|
builder = builder.with_activation_token(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rc::new(builder.build(elwt).unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the window to the map.
|
||||||
|
windows.insert(window.id(), window.clone());
|
||||||
|
|
||||||
|
counter += 1;
|
||||||
|
create_first_window = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
flow.set_wait();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(x11_platform, wayland_platform))]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
example::main()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(x11_platform, wayland_platform)))]
|
||||||
|
fn main() {
|
||||||
|
println!("This example is only supported on X11 and Wayland platforms.");
|
||||||
|
}
|
||||||
@@ -11,9 +11,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
@@ -75,5 +75,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
@@ -47,5 +47,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("Touchpad gestures")
|
.with_title("Touchpad gestures")
|
||||||
@@ -47,5 +47,5 @@ fn main() {
|
|||||||
} else if let Event::RedrawRequested(_) = event {
|
} else if let Event::RedrawRequested(_) = event {
|
||||||
fill::fill_window(&window);
|
fill::fill_window(&window);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_decorations(false)
|
.with_decorations(false)
|
||||||
@@ -36,5 +36,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use winit::event_loop::EventLoop;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
let monitor = match event_loop.primary_monitor() {
|
let monitor = match event_loop.primary_monitor() {
|
||||||
Some(monitor) => monitor,
|
Some(monitor) => monitor,
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
@@ -7,13 +7,16 @@ use winit::{
|
|||||||
window::{Fullscreen, WindowBuilder},
|
window::{Fullscreen, WindowBuilder},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() -> Result<(), impl std::error::Error> {
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let builder = WindowBuilder::new().with_title("A fantastic window!");
|
||||||
.with_title("A fantastic window!")
|
#[cfg(wasm_platform)]
|
||||||
.build(&event_loop)
|
let builder = {
|
||||||
.unwrap();
|
use winit::platform::web::WindowBuilderExtWebSys;
|
||||||
|
builder.with_append(true)
|
||||||
|
};
|
||||||
|
let window = builder.build(&event_loop).unwrap();
|
||||||
|
|
||||||
#[cfg(wasm_platform)]
|
#[cfg(wasm_platform)]
|
||||||
let log_list = wasm::insert_canvas_and_create_log_list(&window);
|
let log_list = wasm::insert_canvas_and_create_log_list(&window);
|
||||||
@@ -29,7 +32,7 @@ pub fn main() {
|
|||||||
event: WindowEvent::CloseRequested,
|
event: WindowEvent::CloseRequested,
|
||||||
window_id,
|
window_id,
|
||||||
} if window_id == window.id() => control_flow.set_exit(),
|
} if window_id == window.id() => control_flow.set_exit(),
|
||||||
Event::MainEventsCleared => {
|
Event::AboutToWait => {
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
}
|
}
|
||||||
Event::WindowEvent {
|
Event::WindowEvent {
|
||||||
@@ -53,11 +56,14 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(wasm_platform)]
|
#[cfg(wasm_platform)]
|
||||||
mod wasm {
|
mod wasm {
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
|
use softbuffer::{Surface, SurfaceExtWeb};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use winit::{event::Event, window::Window};
|
use winit::{event::Event, window::Window};
|
||||||
|
|
||||||
@@ -66,24 +72,33 @@ mod wasm {
|
|||||||
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||||
|
|
||||||
#[allow(clippy::main_recursion)]
|
#[allow(clippy::main_recursion)]
|
||||||
super::main();
|
let _ = super::main();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_canvas_and_create_log_list(window: &Window) -> web_sys::Element {
|
pub fn insert_canvas_and_create_log_list(window: &Window) -> web_sys::Element {
|
||||||
use winit::platform::web::WindowExtWebSys;
|
use winit::platform::web::WindowExtWebSys;
|
||||||
|
|
||||||
let canvas = window.canvas().unwrap();
|
let canvas = window.canvas().unwrap();
|
||||||
|
let mut surface = Surface::from_canvas(canvas.clone()).unwrap();
|
||||||
|
surface
|
||||||
|
.resize(
|
||||||
|
NonZeroU32::new(canvas.width()).unwrap(),
|
||||||
|
NonZeroU32::new(canvas.height()).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut buffer = surface.buffer_mut().unwrap();
|
||||||
|
buffer.fill(0xFFF0000);
|
||||||
|
buffer.present().unwrap();
|
||||||
|
|
||||||
let window = web_sys::window().unwrap();
|
let window = web_sys::window().unwrap();
|
||||||
let document = window.document().unwrap();
|
let document = window.document().unwrap();
|
||||||
let body = document.body().unwrap();
|
let body = document.body().unwrap();
|
||||||
|
|
||||||
// Set a background color for the canvas to make it easier to tell where the canvas is for debugging purposes.
|
let style = &canvas.style();
|
||||||
canvas
|
style.set_property("margin", "50px").unwrap();
|
||||||
.style()
|
// Use to test interactions with border and padding.
|
||||||
.set_property("background-color", "crimson")
|
//style.set_property("border", "50px solid black").unwrap();
|
||||||
.unwrap();
|
//style.set_property("padding", "50px").unwrap();
|
||||||
body.append_child(&canvas).unwrap();
|
|
||||||
|
|
||||||
let log_header = document.create_element("h2").unwrap();
|
let log_header = document.create_element("h2").unwrap();
|
||||||
log_header.set_text_content(Some("Event Log"));
|
log_header.set_text_content(Some("Event Log"));
|
||||||
@@ -100,11 +115,25 @@ mod wasm {
|
|||||||
// Getting access to browser logs requires a lot of setup on mobile devices.
|
// Getting access to browser logs requires a lot of setup on mobile devices.
|
||||||
// So we implement this basic logging system into the page to give developers an easy alternative.
|
// So we implement this basic logging system into the page to give developers an easy alternative.
|
||||||
// As a bonus its also kind of handy on desktop.
|
// As a bonus its also kind of handy on desktop.
|
||||||
if let Event::WindowEvent { event, .. } = &event {
|
let event = match event {
|
||||||
|
Event::WindowEvent { event, .. } => Some(format!("{event:?}")),
|
||||||
|
Event::Resumed | Event::Suspended => Some(format!("{event:?}")),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(event) = event {
|
||||||
let window = web_sys::window().unwrap();
|
let window = web_sys::window().unwrap();
|
||||||
let document = window.document().unwrap();
|
let document = window.document().unwrap();
|
||||||
let log = document.create_element("li").unwrap();
|
let log = document.create_element("li").unwrap();
|
||||||
log.set_text_content(Some(&format!("{event:?}")));
|
|
||||||
|
let date = js_sys::Date::new_0();
|
||||||
|
log.set_text_content(Some(&format!(
|
||||||
|
"{:02}:{:02}:{:02}.{:03}: {event}",
|
||||||
|
date.get_hours(),
|
||||||
|
date.get_minutes(),
|
||||||
|
date.get_seconds(),
|
||||||
|
date.get_milliseconds(),
|
||||||
|
)));
|
||||||
|
|
||||||
log_list
|
log_list
|
||||||
.insert_before(&log, log_list.first_child().as_ref())
|
.insert_before(&log, log_list.first_child().as_ref())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ mod wasm {
|
|||||||
dpi::PhysicalSize,
|
dpi::PhysicalSize,
|
||||||
event::{Event, WindowEvent},
|
event::{Event, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
platform::web::WindowBuilderExtWebSys,
|
||||||
window::{Window, WindowBuilder},
|
window::{Window, WindowBuilder},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,13 +31,14 @@ This example demonstrates the desired future functionality which will possibly b
|
|||||||
#[wasm_bindgen(start)]
|
#[wasm_bindgen(start)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
// When running in a non-wasm environment this would set the window size to 100x100.
|
// When running in a non-wasm environment this would set the window size to 100x100.
|
||||||
// However in this example it just sets a default initial size of 100x100 that is immediately overwritten due to the layout + styling of the page.
|
// However in this example it just sets a default initial size of 100x100 that is immediately overwritten due to the layout + styling of the page.
|
||||||
.with_inner_size(PhysicalSize::new(100, 100))
|
.with_inner_size(PhysicalSize::new(100, 100))
|
||||||
|
.with_append(true)
|
||||||
.build(&event_loop)
|
.build(&event_loop)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ This example demonstrates the desired future functionality which will possibly b
|
|||||||
// Render once with the size info we currently have
|
// Render once with the size info we currently have
|
||||||
render_circle(&canvas, window.inner_size());
|
render_circle(&canvas, window.inner_size());
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
let _ = event_loop.run(move |event, _, control_flow| {
|
||||||
*control_flow = ControlFlow::Wait;
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
@@ -72,7 +74,6 @@ This example demonstrates the desired future functionality which will possibly b
|
|||||||
canvas
|
canvas
|
||||||
.style()
|
.style()
|
||||||
.set_css_text("display: block; background-color: crimson; margin: auto; width: 50%; aspect-ratio: 4 / 1;");
|
.set_css_text("display: block; background-color: crimson; margin: auto; width: 50%; aspect-ratio: 4 / 1;");
|
||||||
body.append_child(&canvas).unwrap();
|
|
||||||
|
|
||||||
let explanation = document.create_element("pre").unwrap();
|
let explanation = document.create_element("pre").unwrap();
|
||||||
explanation.set_text_content(Some(EXPLANATION));
|
explanation.set_text_content(Some(EXPLANATION));
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
@@ -29,13 +29,15 @@ fn main() {
|
|||||||
event: WindowEvent::CloseRequested,
|
event: WindowEvent::CloseRequested,
|
||||||
window_id,
|
window_id,
|
||||||
} if window_id == window.id() => control_flow.set_exit(),
|
} if window_id == window.id() => control_flow.set_exit(),
|
||||||
Event::MainEventsCleared => {
|
Event::AboutToWait => {
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
}
|
}
|
||||||
Event::RedrawRequested(_) => {
|
Event::RedrawRequested(_) => {
|
||||||
|
// Notify the windowing system that we'll be presenting to the window.
|
||||||
|
window.pre_present_notify();
|
||||||
fill::fill_window(&window);
|
fill::fill_window(&window);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
@@ -71,5 +71,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
@@ -139,5 +139,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ const BORDER: f64 = 8.0;
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0))
|
.with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0))
|
||||||
@@ -52,6 +52,8 @@ fn main() {
|
|||||||
} => {
|
} => {
|
||||||
if let Some(dir) = cursor_location {
|
if let Some(dir) = cursor_location {
|
||||||
let _res = window.drag_resize_window(dir);
|
let _res = window.drag_resize_window(dir);
|
||||||
|
} else if !window.is_decorated() {
|
||||||
|
let _res = window.drag_window();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput {
|
WindowEvent::KeyboardInput {
|
||||||
@@ -72,7 +74,7 @@ fn main() {
|
|||||||
fill::fill_window(&window);
|
fill::fill_window(&window);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor_direction_icon(resize_direction: Option<ResizeDirection>) -> CursorIcon {
|
fn cursor_direction_icon(resize_direction: Option<ResizeDirection>) -> CursorIcon {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
|
|
||||||
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
|
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
|
||||||
@@ -23,7 +23,7 @@ fn main() {
|
|||||||
|
|
||||||
let icon = load_icon(Path::new(path));
|
let icon = load_icon(Path::new(path));
|
||||||
|
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("An iconic window!")
|
.with_title("An iconic window!")
|
||||||
@@ -48,7 +48,7 @@ fn main() {
|
|||||||
} else if let Event::RedrawRequested(_) = event {
|
} else if let Event::RedrawRequested(_) = event {
|
||||||
fill::fill_window(&window);
|
fill::fill_window(&window);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_icon(path: &Path) -> Icon {
|
fn load_icon(path: &Path) -> Icon {
|
||||||
|
|||||||
90
examples/window_ondemand.rs
Normal file
90
examples/window_ondemand.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
// Limit this example to only compatible platforms.
|
||||||
|
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
|
||||||
|
use winit::{
|
||||||
|
error::EventLoopError,
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
platform::run_ondemand::EventLoopExtRunOnDemand,
|
||||||
|
window::{Window, WindowBuilder, WindowId},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct App {
|
||||||
|
window_id: Option<WindowId>,
|
||||||
|
window: Option<Window>,
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let mut event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
fn run_app(event_loop: &mut EventLoop<()>, idx: usize) -> Result<(), EventLoopError> {
|
||||||
|
let mut app = App::default();
|
||||||
|
|
||||||
|
event_loop.run_ondemand(move |event, event_loop, control_flow| {
|
||||||
|
control_flow.set_wait();
|
||||||
|
println!("Run {idx}: {:?}", event);
|
||||||
|
|
||||||
|
if let Some(window) = &app.window {
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
window_id,
|
||||||
|
} if window.id() == window_id => {
|
||||||
|
println!("--------------------------------------------------------- Window {idx} CloseRequested");
|
||||||
|
app.window = None;
|
||||||
|
}
|
||||||
|
Event::AboutToWait => window.request_redraw(),
|
||||||
|
Event::RedrawRequested(_) => {
|
||||||
|
fill::fill_window(window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
} else if let Some(id) = app.window_id {
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::Destroyed,
|
||||||
|
window_id,
|
||||||
|
} if id == window_id => {
|
||||||
|
println!("--------------------------------------------------------- Window {idx} Destroyed");
|
||||||
|
app.window_id = None;
|
||||||
|
control_flow.set_exit();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
} else if let Event::Resumed = event {
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_title("Fantastic window number one!")
|
||||||
|
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||||
|
.build(event_loop)
|
||||||
|
.unwrap();
|
||||||
|
app.window_id = Some(window.id());
|
||||||
|
app.window = Some(window);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
run_app(&mut event_loop, 1)?;
|
||||||
|
|
||||||
|
println!("--------------------------------------------------------- Finished first loop");
|
||||||
|
println!("--------------------------------------------------------- Waiting 5 seconds");
|
||||||
|
std::thread::sleep(Duration::from_secs(5));
|
||||||
|
|
||||||
|
let ret = run_app(&mut event_loop, 2);
|
||||||
|
println!("--------------------------------------------------------- Finished second loop");
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))]
|
||||||
|
fn main() {
|
||||||
|
println!("This example is not supported on this platform");
|
||||||
|
}
|
||||||
@@ -18,8 +18,8 @@ mod fill;
|
|||||||
/// Prints the keyboard events characters received when option_is_alt is true versus false.
|
/// Prints the keyboard events characters received when option_is_alt is true versus false.
|
||||||
/// A left mouse click will toggle option_is_alt.
|
/// A left mouse click will toggle option_is_alt.
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
@@ -58,7 +58,7 @@ fn main() {
|
|||||||
WindowEvent::KeyboardInput { .. } => println!("KeyboardInput: {event:?}"),
|
WindowEvent::KeyboardInput { .. } => println!("KeyboardInput: {event:?}"),
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
Event::MainEventsCleared => {
|
Event::AboutToWait => {
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
}
|
}
|
||||||
Event::RedrawRequested(_) => {
|
Event::RedrawRequested(_) => {
|
||||||
@@ -66,7 +66,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
|||||||
73
examples/window_pump_events.rs
Normal file
73
examples/window_pump_events.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
// Limit this example to only compatible platforms.
|
||||||
|
#[cfg(any(
|
||||||
|
windows_platform,
|
||||||
|
macos_platform,
|
||||||
|
x11_platform,
|
||||||
|
wayland_platform,
|
||||||
|
android_platform,
|
||||||
|
))]
|
||||||
|
fn main() -> std::process::ExitCode {
|
||||||
|
use std::{process::ExitCode, thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
|
||||||
|
window::WindowBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
let mut event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
'main: loop {
|
||||||
|
let timeout = Some(Duration::ZERO);
|
||||||
|
let status = event_loop.pump_events(timeout, |event, _, control_flow| {
|
||||||
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
|
if let Event::WindowEvent { event, .. } = &event {
|
||||||
|
// Print only Window events to reduce noise
|
||||||
|
println!("{event:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => control_flow.set_exit(),
|
||||||
|
Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
Event::RedrawRequested(_) => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let PumpStatus::Exit(exit_code) = status {
|
||||||
|
break 'main ExitCode::from(exit_code as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep for 1/60 second to simulate application work
|
||||||
|
//
|
||||||
|
// Since `pump_events` doesn't block it will be important to
|
||||||
|
// throttle the loop in the app somehow.
|
||||||
|
println!("Update()");
|
||||||
|
sleep(Duration::from_millis(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(ios_platform, wasm_platform, orbital_platform))]
|
||||||
|
fn main() {
|
||||||
|
println!("This platform doesn't support pump_events.");
|
||||||
|
}
|
||||||
@@ -11,9 +11,9 @@ use winit::{
|
|||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
@@ -54,11 +54,11 @@ fn main() {
|
|||||||
debug!("Had increments: {}", new_increments.is_none());
|
debug!("Had increments: {}", new_increments.is_none());
|
||||||
window.set_resize_increments(new_increments);
|
window.set_resize_increments(new_increments);
|
||||||
}
|
}
|
||||||
Event::MainEventsCleared => window.request_redraw(),
|
Event::AboutToWait => window.request_redraw(),
|
||||||
Event::RedrawRequested(_) => {
|
Event::RedrawRequested(_) => {
|
||||||
fill::fill_window(&window);
|
fill::fill_window(&window);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
#![allow(clippy::single_match)]
|
|
||||||
|
|
||||||
// Limit this example to only compatible platforms.
|
|
||||||
#[cfg(any(
|
|
||||||
windows_platform,
|
|
||||||
macos_platform,
|
|
||||||
x11_platform,
|
|
||||||
wayland_platform,
|
|
||||||
android_platform,
|
|
||||||
orbital_platform,
|
|
||||||
))]
|
|
||||||
fn main() {
|
|
||||||
use std::{thread::sleep, time::Duration};
|
|
||||||
|
|
||||||
use simple_logger::SimpleLogger;
|
|
||||||
use winit::{
|
|
||||||
event::{Event, WindowEvent},
|
|
||||||
event_loop::EventLoop,
|
|
||||||
platform::run_return::EventLoopExtRunReturn,
|
|
||||||
window::WindowBuilder,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
|
||||||
mod fill;
|
|
||||||
|
|
||||||
let mut event_loop = EventLoop::new();
|
|
||||||
|
|
||||||
SimpleLogger::new().init().unwrap();
|
|
||||||
let window = WindowBuilder::new()
|
|
||||||
.with_title("A fantastic window!")
|
|
||||||
.build(&event_loop)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut quit = false;
|
|
||||||
|
|
||||||
while !quit {
|
|
||||||
event_loop.run_return(|event, _, control_flow| {
|
|
||||||
control_flow.set_wait();
|
|
||||||
|
|
||||||
if let Event::WindowEvent { event, .. } = &event {
|
|
||||||
// Print only Window events to reduce noise
|
|
||||||
println!("{event:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
match event {
|
|
||||||
Event::WindowEvent {
|
|
||||||
event: WindowEvent::CloseRequested,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
quit = true;
|
|
||||||
}
|
|
||||||
Event::MainEventsCleared => {
|
|
||||||
control_flow.set_exit();
|
|
||||||
}
|
|
||||||
Event::RedrawRequested(_) => {
|
|
||||||
fill::fill_window(&window);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sleep for 1/60 second to simulate rendering
|
|
||||||
println!("rendering");
|
|
||||||
sleep(Duration::from_millis(16));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(ios_platform, wasm_platform))]
|
|
||||||
fn main() {
|
|
||||||
println!("This platform doesn't support run_return.");
|
|
||||||
}
|
|
||||||
112
examples/window_tabbing.rs
Normal file
112
examples/window_tabbing.rs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use std::{collections::HashMap, num::NonZeroUsize};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::Key,
|
||||||
|
platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS},
|
||||||
|
window::{Window, WindowBuilder},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let mut windows = HashMap::new();
|
||||||
|
let window = Window::new(&event_loop).unwrap();
|
||||||
|
println!("Opened a new window: {:?}", window.id());
|
||||||
|
windows.insert(window.id(), window);
|
||||||
|
|
||||||
|
println!("Press N to open a new window.");
|
||||||
|
|
||||||
|
event_loop.run(move |event, event_loop, control_flow| {
|
||||||
|
control_flow.set_wait();
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent { event, window_id } => {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
println!("Window {window_id:?} has received the signal to close");
|
||||||
|
|
||||||
|
// This drops the window, causing it to close.
|
||||||
|
windows.remove(&window_id);
|
||||||
|
|
||||||
|
if windows.is_empty() {
|
||||||
|
control_flow.set_exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::Resized(_) => {
|
||||||
|
if let Some(window) = windows.get(&window_id) {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
logical_key,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
is_synthetic: false,
|
||||||
|
..
|
||||||
|
} => match logical_key.as_ref() {
|
||||||
|
Key::Character("t") => {
|
||||||
|
let tabbing_id = windows.get(&window_id).unwrap().tabbing_identifier();
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_tabbing_identifier(&tabbing_id)
|
||||||
|
.build(event_loop)
|
||||||
|
.unwrap();
|
||||||
|
println!("Added a new tab: {:?}", window.id());
|
||||||
|
windows.insert(window.id(), window);
|
||||||
|
}
|
||||||
|
Key::Character("w") => {
|
||||||
|
let _ = windows.remove(&window_id);
|
||||||
|
}
|
||||||
|
Key::ArrowRight => {
|
||||||
|
windows.get(&window_id).unwrap().select_next_tab();
|
||||||
|
}
|
||||||
|
Key::ArrowLeft => {
|
||||||
|
windows.get(&window_id).unwrap().select_previous_tab();
|
||||||
|
}
|
||||||
|
Key::Character(ch) => {
|
||||||
|
if let Ok(index) = ch.parse::<NonZeroUsize>() {
|
||||||
|
let index = index.get();
|
||||||
|
// Select the last tab when pressing `9`.
|
||||||
|
let window = windows.get(&window_id).unwrap();
|
||||||
|
if index == 9 {
|
||||||
|
window.select_tab_at_index(window.num_tabs() - 1)
|
||||||
|
} else {
|
||||||
|
window.select_tab_at_index(index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::RedrawRequested(window_id) => {
|
||||||
|
if let Some(window) = windows.get(&window_id) {
|
||||||
|
fill::fill_window(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
fn main() {
|
||||||
|
println!("This example is only supported on MacOS");
|
||||||
|
}
|
||||||
69
examples/x11_embed.rs
Normal file
69
examples/x11_embed.rs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
//! A demonstration of embedding a winit window in an existing X11 application.
|
||||||
|
|
||||||
|
#[cfg(x11_platform)]
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
#[cfg(x11_platform)]
|
||||||
|
mod imple {
|
||||||
|
use super::fill;
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
platform::x11::WindowBuilderExtX11,
|
||||||
|
window::WindowBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// First argument should be a 32-bit X11 window ID.
|
||||||
|
let parent_window_id = std::env::args()
|
||||||
|
.nth(1)
|
||||||
|
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
|
||||||
|
.parse::<u32>()?;
|
||||||
|
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new()?;
|
||||||
|
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_title("An embedded window!")
|
||||||
|
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||||
|
.with_embed_parent_window(parent_window_id)
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
control_flow.set_wait();
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => control_flow.set_exit(),
|
||||||
|
Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
Event::RedrawRequested(_) => {
|
||||||
|
// Notify the windowing system that we'll be presenting to the window.
|
||||||
|
window.pre_present_notify();
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(x11_platform))]
|
||||||
|
mod imple {
|
||||||
|
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
println!("This example is only supported on X11 platforms.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
imple::entry()
|
||||||
|
}
|
||||||
418
src/dpi.rs
418
src/dpi.rs
@@ -581,3 +581,421 @@ impl<P: Pixel> From<LogicalPosition<P>> for Position {
|
|||||||
Position::Logical(position.cast())
|
Position::Logical(position.cast())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::dpi;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
macro_rules! test_pixel_int_impl {
|
||||||
|
($($name:ident => $ty:ty),*) => {$(
|
||||||
|
#[test]
|
||||||
|
fn $name() {
|
||||||
|
use dpi::Pixel;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::from_f64(37.0),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::from_f64(37.4),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::from_f64(37.5),
|
||||||
|
38,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::from_f64(37.9),
|
||||||
|
38,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u8>(37),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u16>(37),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u32>(37),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<i8>(37),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<i16>(37),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<i32>(37),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)*};
|
||||||
|
}
|
||||||
|
|
||||||
|
test_pixel_int_impl! {
|
||||||
|
test_pixel_int_u8 => u8,
|
||||||
|
test_pixel_int_u16 => u16,
|
||||||
|
test_pixel_int_u32 => u32,
|
||||||
|
test_pixel_int_i8 => i8,
|
||||||
|
test_pixel_int_i16 => i16
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assert_approx_eq {
|
||||||
|
($a:expr, $b:expr $(,)?) => {
|
||||||
|
assert!(
|
||||||
|
($a - $b).abs() < 0.001,
|
||||||
|
"{} is not approximately equal to {}",
|
||||||
|
$a,
|
||||||
|
$b
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! test_pixel_float_impl {
|
||||||
|
($($name:ident => $ty:ty),*) => {$(
|
||||||
|
#[test]
|
||||||
|
fn $name() {
|
||||||
|
use dpi::Pixel;
|
||||||
|
|
||||||
|
assert_approx_eq!(
|
||||||
|
<$ty as Pixel>::from_f64(37.0),
|
||||||
|
37.0,
|
||||||
|
);
|
||||||
|
assert_approx_eq!(
|
||||||
|
<$ty as Pixel>::from_f64(37.4),
|
||||||
|
37.4,
|
||||||
|
);
|
||||||
|
assert_approx_eq!(
|
||||||
|
<$ty as Pixel>::from_f64(37.5),
|
||||||
|
37.5,
|
||||||
|
);
|
||||||
|
assert_approx_eq!(
|
||||||
|
<$ty as Pixel>::from_f64(37.9),
|
||||||
|
37.9,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u8>(37.0),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u8>(37.4),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u8>(37.5),
|
||||||
|
38,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u16>(37.0),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u16>(37.4),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u16>(37.5),
|
||||||
|
38,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u32>(37.0),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u32>(37.4),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<u32>(37.5),
|
||||||
|
38,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<i8>(37.0),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<i8>(37.4),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<i8>(37.5),
|
||||||
|
38,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<i16>(37.0),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<i16>(37.4),
|
||||||
|
37,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<$ty as Pixel>::cast::<i16>(37.5),
|
||||||
|
38,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)*};
|
||||||
|
}
|
||||||
|
|
||||||
|
test_pixel_float_impl! {
|
||||||
|
test_pixel_float_f32 => f32,
|
||||||
|
test_pixel_float_f64 => f64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validate_scale_factor() {
|
||||||
|
assert!(dpi::validate_scale_factor(1.0));
|
||||||
|
assert!(dpi::validate_scale_factor(2.0));
|
||||||
|
assert!(dpi::validate_scale_factor(3.0));
|
||||||
|
assert!(dpi::validate_scale_factor(1.5));
|
||||||
|
assert!(dpi::validate_scale_factor(0.5));
|
||||||
|
|
||||||
|
assert!(!dpi::validate_scale_factor(0.0));
|
||||||
|
assert!(!dpi::validate_scale_factor(-1.0));
|
||||||
|
assert!(!dpi::validate_scale_factor(f64::INFINITY));
|
||||||
|
assert!(!dpi::validate_scale_factor(f64::NAN));
|
||||||
|
assert!(!dpi::validate_scale_factor(f64::NEG_INFINITY));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_logical_position() {
|
||||||
|
let log_pos = dpi::LogicalPosition::new(1.0, 2.0);
|
||||||
|
assert_eq!(
|
||||||
|
log_pos.to_physical::<u32>(1.0),
|
||||||
|
dpi::PhysicalPosition::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
log_pos.to_physical::<u32>(2.0),
|
||||||
|
dpi::PhysicalPosition::new(2, 4)
|
||||||
|
);
|
||||||
|
assert_eq!(log_pos.cast::<u32>(), dpi::LogicalPosition::new(1, 2));
|
||||||
|
assert_eq!(
|
||||||
|
log_pos,
|
||||||
|
dpi::LogicalPosition::from_physical(dpi::PhysicalPosition::new(1.0, 2.0), 1.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
log_pos,
|
||||||
|
dpi::LogicalPosition::from_physical(dpi::PhysicalPosition::new(2.0, 4.0), 2.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::LogicalPosition::from((2.0, 2.0)),
|
||||||
|
dpi::LogicalPosition::new(2.0, 2.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::LogicalPosition::from([2.0, 3.0]),
|
||||||
|
dpi::LogicalPosition::new(2.0, 3.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
let x: (f64, f64) = log_pos.into();
|
||||||
|
assert_eq!(x, (1.0, 2.0));
|
||||||
|
let x: [f64; 2] = log_pos.into();
|
||||||
|
assert_eq!(x, [1.0, 2.0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_physical_position() {
|
||||||
|
assert_eq!(
|
||||||
|
dpi::PhysicalPosition::from_logical(dpi::LogicalPosition::new(1.0, 2.0), 1.0),
|
||||||
|
dpi::PhysicalPosition::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::PhysicalPosition::from_logical(dpi::LogicalPosition::new(2.0, 4.0), 0.5),
|
||||||
|
dpi::PhysicalPosition::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::PhysicalPosition::from((2.0, 2.0)),
|
||||||
|
dpi::PhysicalPosition::new(2.0, 2.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::PhysicalPosition::from([2.0, 3.0]),
|
||||||
|
dpi::PhysicalPosition::new(2.0, 3.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
let x: (f64, f64) = dpi::PhysicalPosition::new(1, 2).into();
|
||||||
|
assert_eq!(x, (1.0, 2.0));
|
||||||
|
let x: [f64; 2] = dpi::PhysicalPosition::new(1, 2).into();
|
||||||
|
assert_eq!(x, [1.0, 2.0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_logical_size() {
|
||||||
|
let log_size = dpi::LogicalSize::new(1.0, 2.0);
|
||||||
|
assert_eq!(
|
||||||
|
log_size.to_physical::<u32>(1.0),
|
||||||
|
dpi::PhysicalSize::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
log_size.to_physical::<u32>(2.0),
|
||||||
|
dpi::PhysicalSize::new(2, 4)
|
||||||
|
);
|
||||||
|
assert_eq!(log_size.cast::<u32>(), dpi::LogicalSize::new(1, 2));
|
||||||
|
assert_eq!(
|
||||||
|
log_size,
|
||||||
|
dpi::LogicalSize::from_physical(dpi::PhysicalSize::new(1.0, 2.0), 1.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
log_size,
|
||||||
|
dpi::LogicalSize::from_physical(dpi::PhysicalSize::new(2.0, 4.0), 2.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::LogicalSize::from((2.0, 2.0)),
|
||||||
|
dpi::LogicalSize::new(2.0, 2.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::LogicalSize::from([2.0, 3.0]),
|
||||||
|
dpi::LogicalSize::new(2.0, 3.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
let x: (f64, f64) = log_size.into();
|
||||||
|
assert_eq!(x, (1.0, 2.0));
|
||||||
|
let x: [f64; 2] = log_size.into();
|
||||||
|
assert_eq!(x, [1.0, 2.0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_physical_size() {
|
||||||
|
assert_eq!(
|
||||||
|
dpi::PhysicalSize::from_logical(dpi::LogicalSize::new(1.0, 2.0), 1.0),
|
||||||
|
dpi::PhysicalSize::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::PhysicalSize::from_logical(dpi::LogicalSize::new(2.0, 4.0), 0.5),
|
||||||
|
dpi::PhysicalSize::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::PhysicalSize::from((2.0, 2.0)),
|
||||||
|
dpi::PhysicalSize::new(2.0, 2.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::PhysicalSize::from([2.0, 3.0]),
|
||||||
|
dpi::PhysicalSize::new(2.0, 3.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
let x: (f64, f64) = dpi::PhysicalSize::new(1, 2).into();
|
||||||
|
assert_eq!(x, (1.0, 2.0));
|
||||||
|
let x: [f64; 2] = dpi::PhysicalSize::new(1, 2).into();
|
||||||
|
assert_eq!(x, [1.0, 2.0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_size() {
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Size::new(dpi::PhysicalSize::new(1, 2)),
|
||||||
|
dpi::Size::Physical(dpi::PhysicalSize::new(1, 2))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)),
|
||||||
|
dpi::Size::Logical(dpi::LogicalSize::new(1.0, 2.0))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_logical::<f64>(1.0),
|
||||||
|
dpi::LogicalSize::new(1.0, 2.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_logical::<f64>(2.0),
|
||||||
|
dpi::LogicalSize::new(0.5, 1.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_logical::<f64>(1.0),
|
||||||
|
dpi::LogicalSize::new(1.0, 2.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_physical::<u32>(1.0),
|
||||||
|
dpi::PhysicalSize::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_physical::<u32>(2.0),
|
||||||
|
dpi::PhysicalSize::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_physical::<u32>(1.0),
|
||||||
|
dpi::PhysicalSize::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_physical::<u32>(2.0),
|
||||||
|
dpi::PhysicalSize::new(2, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
let small = dpi::Size::Physical((1, 2).into());
|
||||||
|
let medium = dpi::Size::Logical((3, 4).into());
|
||||||
|
let medium_physical = dpi::Size::new(medium.to_physical::<u32>(1.0));
|
||||||
|
let large = dpi::Size::Physical((5, 6).into());
|
||||||
|
assert_eq!(dpi::Size::clamp(medium, small, large, 1.0), medium_physical);
|
||||||
|
assert_eq!(dpi::Size::clamp(small, medium, large, 1.0), medium_physical);
|
||||||
|
assert_eq!(dpi::Size::clamp(large, small, medium, 1.0), medium_physical);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_position() {
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)),
|
||||||
|
dpi::Position::Physical(dpi::PhysicalPosition::new(1, 2))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)),
|
||||||
|
dpi::Position::Logical(dpi::LogicalPosition::new(1.0, 2.0))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_logical::<f64>(1.0),
|
||||||
|
dpi::LogicalPosition::new(1.0, 2.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_logical::<f64>(2.0),
|
||||||
|
dpi::LogicalPosition::new(0.5, 1.0)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_logical::<f64>(1.0),
|
||||||
|
dpi::LogicalPosition::new(1.0, 2.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_physical::<u32>(1.0),
|
||||||
|
dpi::PhysicalPosition::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_physical::<u32>(2.0),
|
||||||
|
dpi::PhysicalPosition::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(1.0),
|
||||||
|
dpi::PhysicalPosition::new(1, 2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(2.0),
|
||||||
|
dpi::PhysicalPosition::new(2, 4)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eat coverage for the Debug impls et al
|
||||||
|
#[test]
|
||||||
|
fn ensure_attrs_do_not_panic() {
|
||||||
|
let _ = format!("{:?}", dpi::LogicalPosition::<u32>::default().clone());
|
||||||
|
HashSet::new().insert(dpi::LogicalPosition::<u32>::default());
|
||||||
|
|
||||||
|
let _ = format!("{:?}", dpi::PhysicalPosition::<u32>::default().clone());
|
||||||
|
HashSet::new().insert(dpi::PhysicalPosition::<u32>::default());
|
||||||
|
|
||||||
|
let _ = format!("{:?}", dpi::LogicalSize::<u32>::default().clone());
|
||||||
|
HashSet::new().insert(dpi::LogicalSize::<u32>::default());
|
||||||
|
|
||||||
|
let _ = format!("{:?}", dpi::PhysicalSize::<u32>::default().clone());
|
||||||
|
HashSet::new().insert(dpi::PhysicalSize::<u32>::default());
|
||||||
|
|
||||||
|
let _ = format!("{:?}", dpi::Size::Physical((1, 2).into()).clone());
|
||||||
|
let _ = format!("{:?}", dpi::Position::Physical((1, 2).into()).clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
62
src/error.rs
62
src/error.rs
@@ -2,11 +2,14 @@ use std::{error, fmt};
|
|||||||
|
|
||||||
use crate::platform_impl;
|
use crate::platform_impl;
|
||||||
|
|
||||||
/// An error whose cause it outside Winit's control.
|
// TODO: Rename
|
||||||
|
/// An error that may be generated when requesting Winit state
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ExternalError {
|
pub enum ExternalError {
|
||||||
/// The operation is not supported by the backend.
|
/// The operation is not supported by the backend.
|
||||||
NotSupported(NotSupportedError),
|
NotSupported(NotSupportedError),
|
||||||
|
/// The operation was ignored.
|
||||||
|
Ignored,
|
||||||
/// The OS cannot perform the operation.
|
/// The OS cannot perform the operation.
|
||||||
Os(OsError),
|
Os(OsError),
|
||||||
}
|
}
|
||||||
@@ -25,6 +28,27 @@ pub struct OsError {
|
|||||||
error: platform_impl::OsError,
|
error: platform_impl::OsError,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A general error that may occur while running the Winit event loop
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EventLoopError {
|
||||||
|
/// The operation is not supported by the backend.
|
||||||
|
NotSupported(NotSupportedError),
|
||||||
|
/// The OS cannot perform the operation.
|
||||||
|
Os(OsError),
|
||||||
|
/// The event loop can't be re-run while it's already running
|
||||||
|
AlreadyRunning,
|
||||||
|
/// The event loop can't be re-created.
|
||||||
|
RecreationAttempt,
|
||||||
|
/// Application has exit with an error status.
|
||||||
|
ExitFailure(i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OsError> for EventLoopError {
|
||||||
|
fn from(value: OsError) -> Self {
|
||||||
|
Self::Os(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl NotSupportedError {
|
impl NotSupportedError {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -60,6 +84,7 @@ impl fmt::Display for ExternalError {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
match self {
|
match self {
|
||||||
ExternalError::NotSupported(e) => e.fmt(f),
|
ExternalError::NotSupported(e) => e.fmt(f),
|
||||||
|
ExternalError::Ignored => write!(f, "Operation was ignored"),
|
||||||
ExternalError::Os(e) => e.fmt(f),
|
ExternalError::Os(e) => e.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,6 +102,41 @@ impl fmt::Display for NotSupportedError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for EventLoopError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
match self {
|
||||||
|
EventLoopError::AlreadyRunning => write!(f, "EventLoop is already running"),
|
||||||
|
EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
|
||||||
|
EventLoopError::NotSupported(e) => e.fmt(f),
|
||||||
|
EventLoopError::Os(e) => e.fmt(f),
|
||||||
|
EventLoopError::ExitFailure(status) => write!(f, "Exit Failure: {status}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl error::Error for OsError {}
|
impl error::Error for OsError {}
|
||||||
impl error::Error for ExternalError {}
|
impl error::Error for ExternalError {}
|
||||||
impl error::Error for NotSupportedError {}
|
impl error::Error for NotSupportedError {}
|
||||||
|
impl error::Error for EventLoopError {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#![allow(clippy::redundant_clone)]
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Eat attributes for testing
|
||||||
|
#[test]
|
||||||
|
fn ensure_fmt_does_not_panic() {
|
||||||
|
let _ = format!(
|
||||||
|
"{:?}, {}",
|
||||||
|
NotSupportedError::new(),
|
||||||
|
NotSupportedError::new().clone()
|
||||||
|
);
|
||||||
|
let _ = format!(
|
||||||
|
"{:?}, {}",
|
||||||
|
ExternalError::NotSupported(NotSupportedError::new()),
|
||||||
|
ExternalError::NotSupported(NotSupportedError::new())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
639
src/event.rs
639
src/event.rs
@@ -16,17 +16,16 @@
|
|||||||
//! for e in (window events, user events, device events) {
|
//! for e in (window events, user events, device events) {
|
||||||
//! event_handler(e, ..., &mut control_flow);
|
//! event_handler(e, ..., &mut control_flow);
|
||||||
//! }
|
//! }
|
||||||
//! event_handler(MainEventsCleared, ..., &mut control_flow);
|
|
||||||
//!
|
//!
|
||||||
//! for w in (redraw windows) {
|
//! for w in (redraw windows) {
|
||||||
//! event_handler(RedrawRequested(w), ..., &mut control_flow);
|
//! event_handler(RedrawRequested(w), ..., &mut control_flow);
|
||||||
//! }
|
//! }
|
||||||
//! event_handler(RedrawEventsCleared, ..., &mut control_flow);
|
|
||||||
//!
|
//!
|
||||||
|
//! event_handler(AboutToWait, ..., &mut control_flow);
|
||||||
//! start_cause = wait_if_necessary(control_flow);
|
//! start_cause = wait_if_necessary(control_flow);
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! event_handler(LoopDestroyed, ..., &mut control_flow);
|
//! event_handler(LoopExiting, ..., &mut control_flow);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
|
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
|
||||||
@@ -34,27 +33,31 @@
|
|||||||
//!
|
//!
|
||||||
//! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run
|
//! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run
|
||||||
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||||
use smol_str::SmolStr;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::{Mutex, Weak};
|
||||||
#[cfg(not(wasm_platform))]
|
#[cfg(not(wasm_platform))]
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use smol_str::SmolStr;
|
||||||
#[cfg(wasm_platform)]
|
#[cfg(wasm_platform)]
|
||||||
use web_time::Instant;
|
use web_time::Instant;
|
||||||
|
|
||||||
|
use crate::error::ExternalError;
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize},
|
dpi::{PhysicalPosition, PhysicalSize},
|
||||||
|
event_loop::AsyncRequestSerial,
|
||||||
keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState},
|
keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState},
|
||||||
platform_impl,
|
platform_impl,
|
||||||
window::{Theme, WindowId},
|
window::{ActivationToken, Theme, WindowId},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Describes a generic event.
|
/// Describes a generic event.
|
||||||
///
|
///
|
||||||
/// See the module-level docs for more information on the event loop manages each event.
|
/// See the module-level docs for more information on the event loop manages each event.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Event<'a, T: 'static> {
|
pub enum Event<T: 'static> {
|
||||||
/// Emitted when new events arrive from the OS to be processed.
|
/// Emitted when new events arrive from the OS to be processed.
|
||||||
///
|
///
|
||||||
/// This event type is useful as a place to put code that should be done before you start
|
/// This event type is useful as a place to put code that should be done before you start
|
||||||
@@ -66,7 +69,7 @@ pub enum Event<'a, T: 'static> {
|
|||||||
/// Emitted when the OS sends an event to a winit window.
|
/// Emitted when the OS sends an event to a winit window.
|
||||||
WindowEvent {
|
WindowEvent {
|
||||||
window_id: WindowId,
|
window_id: WindowId,
|
||||||
event: WindowEvent<'a>,
|
event: WindowEvent,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Emitted when the OS sends an event to a device.
|
/// Emitted when the OS sends an event to a device.
|
||||||
@@ -84,7 +87,7 @@ pub enum Event<'a, T: 'static> {
|
|||||||
///
|
///
|
||||||
/// Not all platforms support the notion of suspending applications, and there may be no
|
/// Not all platforms support the notion of suspending applications, and there may be no
|
||||||
/// technical way to guarantee being able to emit a `Suspended` event if the OS has
|
/// technical way to guarantee being able to emit a `Suspended` event if the OS has
|
||||||
/// no formal application lifecycle (currently only Android and iOS do). For this reason,
|
/// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason,
|
||||||
/// Winit does not currently try to emit pseudo `Suspended` events before the application
|
/// Winit does not currently try to emit pseudo `Suspended` events before the application
|
||||||
/// quits on platforms without an application lifecycle.
|
/// quits on platforms without an application lifecycle.
|
||||||
///
|
///
|
||||||
@@ -129,7 +132,7 @@ pub enum Event<'a, T: 'static> {
|
|||||||
///
|
///
|
||||||
/// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event
|
/// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event
|
||||||
/// with the property [`persisted`] being true, which means that the page is being
|
/// with the property [`persisted`] being true, which means that the page is being
|
||||||
/// put in the [´bfcache`] (back/forward cache) - an in-memory cache that stores a
|
/// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a
|
||||||
/// complete snapshot of a page (including the JavaScript heap) as the user is
|
/// complete snapshot of a page (including the JavaScript heap) as the user is
|
||||||
/// navigating away.
|
/// navigating away.
|
||||||
///
|
///
|
||||||
@@ -195,7 +198,7 @@ pub enum Event<'a, T: 'static> {
|
|||||||
///
|
///
|
||||||
/// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event
|
/// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event
|
||||||
/// with the property [`persisted`] being true, which means that the page is being
|
/// with the property [`persisted`] being true, which means that the page is being
|
||||||
/// restored from the [´bfcache`] (back/forward cache) - an in-memory cache that
|
/// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
|
||||||
/// stores a complete snapshot of a page (including the JavaScript heap) as the
|
/// stores a complete snapshot of a page (including the JavaScript heap) as the
|
||||||
/// user is navigating away.
|
/// user is navigating away.
|
||||||
///
|
///
|
||||||
@@ -206,121 +209,53 @@ pub enum Event<'a, T: 'static> {
|
|||||||
/// [`Suspended`]: Self::Suspended
|
/// [`Suspended`]: Self::Suspended
|
||||||
Resumed,
|
Resumed,
|
||||||
|
|
||||||
/// Emitted when all of the event loop's input events have been processed and redraw processing
|
/// Emitted when the event loop is about to block and wait for new events.
|
||||||
/// is about to begin.
|
|
||||||
///
|
///
|
||||||
/// This event is useful as a place to put your code that should be run after all
|
/// Most applications shouldn't need to hook into this event since there is no real relationship
|
||||||
/// state-changing events have been handled and you want to do stuff (updating state, performing
|
/// between how often the event loop needs to wake up and the dispatching of any specific events.
|
||||||
/// calculations, etc) that happens as the "main body" of your event loop. If your program only draws
|
///
|
||||||
/// graphics when something changes, it's usually better to do it in response to
|
/// High frequency event sources, such as input devices could potentially lead to lots of wake
|
||||||
/// [`Event::RedrawRequested`](crate::event::Event::RedrawRequested), which gets emitted
|
/// ups and also lots of corresponding `AboutToWait` events.
|
||||||
/// immediately after this event. Programs that draw graphics continuously, like most games,
|
///
|
||||||
/// can render here unconditionally for simplicity.
|
/// This is not an ideal event to drive application rendering from and instead applications
|
||||||
MainEventsCleared,
|
/// should render in response to [`Event::RedrawRequested`](crate::event::Event::RedrawRequested)
|
||||||
|
/// events.
|
||||||
|
AboutToWait,
|
||||||
|
|
||||||
/// Emitted after [`MainEventsCleared`] when a window should be redrawn.
|
/// Emitted when a window should be redrawn.
|
||||||
///
|
///
|
||||||
/// This gets triggered in two scenarios:
|
/// This gets triggered in two scenarios:
|
||||||
/// - The OS has performed an operation that's invalidated the window's contents (such as
|
/// - The OS has performed an operation that's invalidated the window's contents (such as
|
||||||
/// resizing the window).
|
/// resizing the window).
|
||||||
/// - The application has explicitly requested a redraw via [`Window::request_redraw`].
|
/// - The application has explicitly requested a redraw via [`Window::request_redraw`].
|
||||||
///
|
///
|
||||||
/// During each iteration of the event loop, Winit will aggregate duplicate redraw requests
|
/// Winit will aggregate duplicate redraw requests into a single event, to
|
||||||
/// into a single event, to help avoid duplicating rendering work.
|
/// help avoid duplicating rendering work.
|
||||||
///
|
|
||||||
/// Mainly of interest to applications with mostly-static graphics that avoid redrawing unless
|
|
||||||
/// something changes, like most non-game GUIs.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// ## Platform-specific
|
|
||||||
///
|
|
||||||
/// - **macOS / iOS:** Due to implementation difficulties, this will often, but not always, be
|
|
||||||
/// emitted directly inside `drawRect:`, with neither a preceding [`MainEventsCleared`] nor
|
|
||||||
/// subsequent `RedrawEventsCleared`. See [#2640] for work on this.
|
|
||||||
///
|
|
||||||
/// [`MainEventsCleared`]: Self::MainEventsCleared
|
|
||||||
/// [`RedrawEventsCleared`]: Self::RedrawEventsCleared
|
|
||||||
/// [#2640]: https://github.com/rust-windowing/winit/issues/2640
|
|
||||||
RedrawRequested(WindowId),
|
RedrawRequested(WindowId),
|
||||||
|
|
||||||
/// Emitted after all [`RedrawRequested`] events have been processed and control flow is about to
|
|
||||||
/// be taken away from the program. If there are no `RedrawRequested` events, it is emitted
|
|
||||||
/// immediately after `MainEventsCleared`.
|
|
||||||
///
|
|
||||||
/// This event is useful for doing any cleanup or bookkeeping work after all the rendering
|
|
||||||
/// tasks have been completed.
|
|
||||||
///
|
|
||||||
/// [`RedrawRequested`]: Self::RedrawRequested
|
|
||||||
RedrawEventsCleared,
|
|
||||||
|
|
||||||
/// Emitted when the event loop is being shut down.
|
/// Emitted when the event loop is being shut down.
|
||||||
///
|
///
|
||||||
/// This is irreversible - if this event is emitted, it is guaranteed to be the last event that
|
/// This is irreversible - if this event is emitted, it is guaranteed to be the last event that
|
||||||
/// gets emitted. You generally want to treat this as a "do on quit" event.
|
/// gets emitted. You generally want to treat this as a "do on quit" event.
|
||||||
LoopDestroyed,
|
LoopExiting,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> Clone for Event<'static, T> {
|
impl<T> Event<T> {
|
||||||
fn clone(&self) -> Self {
|
|
||||||
use self::Event::*;
|
|
||||||
match self {
|
|
||||||
WindowEvent { window_id, event } => WindowEvent {
|
|
||||||
window_id: *window_id,
|
|
||||||
event: event.clone(),
|
|
||||||
},
|
|
||||||
UserEvent(event) => UserEvent(event.clone()),
|
|
||||||
DeviceEvent { device_id, event } => DeviceEvent {
|
|
||||||
device_id: *device_id,
|
|
||||||
event: event.clone(),
|
|
||||||
},
|
|
||||||
NewEvents(cause) => NewEvents(*cause),
|
|
||||||
MainEventsCleared => MainEventsCleared,
|
|
||||||
RedrawRequested(wid) => RedrawRequested(*wid),
|
|
||||||
RedrawEventsCleared => RedrawEventsCleared,
|
|
||||||
LoopDestroyed => LoopDestroyed,
|
|
||||||
Suspended => Suspended,
|
|
||||||
Resumed => Resumed,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Event<'a, T> {
|
|
||||||
#[allow(clippy::result_large_err)]
|
#[allow(clippy::result_large_err)]
|
||||||
pub fn map_nonuser_event<U>(self) -> Result<Event<'a, U>, Event<'a, T>> {
|
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
|
||||||
use self::Event::*;
|
use self::Event::*;
|
||||||
match self {
|
match self {
|
||||||
UserEvent(_) => Err(self),
|
UserEvent(_) => Err(self),
|
||||||
WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }),
|
WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }),
|
||||||
DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }),
|
DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }),
|
||||||
NewEvents(cause) => Ok(NewEvents(cause)),
|
NewEvents(cause) => Ok(NewEvents(cause)),
|
||||||
MainEventsCleared => Ok(MainEventsCleared),
|
AboutToWait => Ok(AboutToWait),
|
||||||
RedrawRequested(wid) => Ok(RedrawRequested(wid)),
|
RedrawRequested(wid) => Ok(RedrawRequested(wid)),
|
||||||
RedrawEventsCleared => Ok(RedrawEventsCleared),
|
LoopExiting => Ok(LoopExiting),
|
||||||
LoopDestroyed => Ok(LoopDestroyed),
|
|
||||||
Suspended => Ok(Suspended),
|
Suspended => Ok(Suspended),
|
||||||
Resumed => Ok(Resumed),
|
Resumed => Ok(Resumed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the event doesn't contain a reference, turn it into an event with a `'static` lifetime.
|
|
||||||
/// Otherwise, return `None`.
|
|
||||||
pub fn to_static(self) -> Option<Event<'static, T>> {
|
|
||||||
use self::Event::*;
|
|
||||||
match self {
|
|
||||||
WindowEvent { window_id, event } => event
|
|
||||||
.to_static()
|
|
||||||
.map(|event| WindowEvent { window_id, event }),
|
|
||||||
UserEvent(event) => Some(UserEvent(event)),
|
|
||||||
DeviceEvent { device_id, event } => Some(DeviceEvent { device_id, event }),
|
|
||||||
NewEvents(cause) => Some(NewEvents(cause)),
|
|
||||||
MainEventsCleared => Some(MainEventsCleared),
|
|
||||||
RedrawRequested(wid) => Some(RedrawRequested(wid)),
|
|
||||||
RedrawEventsCleared => Some(RedrawEventsCleared),
|
|
||||||
LoopDestroyed => Some(LoopDestroyed),
|
|
||||||
Suspended => Some(Suspended),
|
|
||||||
Resumed => Some(Resumed),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes the reason the event loop is resuming.
|
/// Describes the reason the event loop is resuming.
|
||||||
@@ -354,8 +289,22 @@ pub enum StartCause {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Describes an event from a [`Window`].
|
/// Describes an event from a [`Window`].
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum WindowEvent<'a> {
|
pub enum WindowEvent {
|
||||||
|
/// The activation token was delivered back and now could be used.
|
||||||
|
///
|
||||||
|
#[cfg_attr(
|
||||||
|
not(any(x11_platform, wayland_platfrom)),
|
||||||
|
allow(rustdoc::broken_intra_doc_links)
|
||||||
|
)]
|
||||||
|
/// Delivered in response to [`request_activation_token`].
|
||||||
|
///
|
||||||
|
/// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token
|
||||||
|
ActivationTokenDone {
|
||||||
|
serial: AsyncRequestSerial,
|
||||||
|
token: ActivationToken,
|
||||||
|
},
|
||||||
|
|
||||||
/// The size of the window has changed. Contains the client area's new dimensions.
|
/// The size of the window has changed. Contains the client area's new dimensions.
|
||||||
Resized(PhysicalSize<u32>),
|
Resized(PhysicalSize<u32>),
|
||||||
|
|
||||||
@@ -430,6 +379,14 @@ pub enum WindowEvent<'a> {
|
|||||||
Ime(Ime),
|
Ime(Ime),
|
||||||
|
|
||||||
/// The cursor has moved on the window.
|
/// The cursor has moved on the window.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
|
||||||
|
///
|
||||||
|
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||||
|
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||||
|
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||||
CursorMoved {
|
CursorMoved {
|
||||||
device_id: DeviceId,
|
device_id: DeviceId,
|
||||||
|
|
||||||
@@ -440,9 +397,25 @@ pub enum WindowEvent<'a> {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// The cursor has entered the window.
|
/// The cursor has entered the window.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
|
||||||
|
///
|
||||||
|
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||||
|
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||||
|
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||||
CursorEntered { device_id: DeviceId },
|
CursorEntered { device_id: DeviceId },
|
||||||
|
|
||||||
/// The cursor has left the window.
|
/// The cursor has left the window.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
|
||||||
|
///
|
||||||
|
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||||
|
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||||
|
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||||
CursorLeft { device_id: DeviceId },
|
CursorLeft { device_id: DeviceId },
|
||||||
|
|
||||||
/// A mouse wheel movement or touchpad scroll occurred.
|
/// A mouse wheel movement or touchpad scroll occurred.
|
||||||
@@ -528,7 +501,12 @@ pub enum WindowEvent<'a> {
|
|||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
|
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
|
||||||
/// - **macOS:** Unsupported.
|
/// - **macOS:** Unsupported.
|
||||||
|
///
|
||||||
|
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||||
|
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||||
|
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||||
Touch(Touch),
|
Touch(Touch),
|
||||||
|
|
||||||
/// The window's scale factor has changed.
|
/// The window's scale factor has changed.
|
||||||
@@ -546,7 +524,10 @@ pub enum WindowEvent<'a> {
|
|||||||
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module.
|
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module.
|
||||||
ScaleFactorChanged {
|
ScaleFactorChanged {
|
||||||
scale_factor: f64,
|
scale_factor: f64,
|
||||||
new_inner_size: &'a mut PhysicalSize<u32>,
|
/// Handle to update inner size during scale changes.
|
||||||
|
///
|
||||||
|
/// See [`InnerSizeWriter`] docs for more details.
|
||||||
|
inner_size_writer: InnerSizeWriter,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The system window theme has changed.
|
/// The system window theme has changed.
|
||||||
@@ -565,208 +546,16 @@ pub enum WindowEvent<'a> {
|
|||||||
/// minimised, set invisible, or fully occluded by another window.
|
/// minimised, set invisible, or fully occluded by another window.
|
||||||
///
|
///
|
||||||
/// Platform-specific behavior:
|
/// Platform-specific behavior:
|
||||||
/// - **iOS / Android / Web / Wayland / Windows / Orbital:** Unsupported.
|
///
|
||||||
|
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
|
||||||
|
/// - **iOS / Android / Wayland / Windows / Orbital:** Unsupported.
|
||||||
|
///
|
||||||
|
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||||
|
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||||
|
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||||
Occluded(bool),
|
Occluded(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for WindowEvent<'static> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
use self::WindowEvent::*;
|
|
||||||
return match self {
|
|
||||||
Resized(size) => Resized(*size),
|
|
||||||
Moved(pos) => Moved(*pos),
|
|
||||||
CloseRequested => CloseRequested,
|
|
||||||
Destroyed => Destroyed,
|
|
||||||
DroppedFile(file) => DroppedFile(file.clone()),
|
|
||||||
HoveredFile(file) => HoveredFile(file.clone()),
|
|
||||||
HoveredFileCancelled => HoveredFileCancelled,
|
|
||||||
Focused(f) => Focused(*f),
|
|
||||||
KeyboardInput {
|
|
||||||
device_id,
|
|
||||||
event,
|
|
||||||
is_synthetic,
|
|
||||||
} => KeyboardInput {
|
|
||||||
device_id: *device_id,
|
|
||||||
event: event.clone(),
|
|
||||||
is_synthetic: *is_synthetic,
|
|
||||||
},
|
|
||||||
Ime(preedit_state) => Ime(preedit_state.clone()),
|
|
||||||
ModifiersChanged(modifiers) => ModifiersChanged(*modifiers),
|
|
||||||
CursorMoved {
|
|
||||||
device_id,
|
|
||||||
position,
|
|
||||||
} => CursorMoved {
|
|
||||||
device_id: *device_id,
|
|
||||||
position: *position,
|
|
||||||
},
|
|
||||||
CursorEntered { device_id } => CursorEntered {
|
|
||||||
device_id: *device_id,
|
|
||||||
},
|
|
||||||
CursorLeft { device_id } => CursorLeft {
|
|
||||||
device_id: *device_id,
|
|
||||||
},
|
|
||||||
MouseWheel {
|
|
||||||
device_id,
|
|
||||||
delta,
|
|
||||||
phase,
|
|
||||||
} => MouseWheel {
|
|
||||||
device_id: *device_id,
|
|
||||||
delta: *delta,
|
|
||||||
phase: *phase,
|
|
||||||
},
|
|
||||||
MouseInput {
|
|
||||||
device_id,
|
|
||||||
state,
|
|
||||||
button,
|
|
||||||
} => MouseInput {
|
|
||||||
device_id: *device_id,
|
|
||||||
state: *state,
|
|
||||||
button: *button,
|
|
||||||
},
|
|
||||||
TouchpadMagnify {
|
|
||||||
device_id,
|
|
||||||
delta,
|
|
||||||
phase,
|
|
||||||
} => TouchpadMagnify {
|
|
||||||
device_id: *device_id,
|
|
||||||
delta: *delta,
|
|
||||||
phase: *phase,
|
|
||||||
},
|
|
||||||
SmartMagnify { device_id } => SmartMagnify {
|
|
||||||
device_id: *device_id,
|
|
||||||
},
|
|
||||||
TouchpadRotate {
|
|
||||||
device_id,
|
|
||||||
delta,
|
|
||||||
phase,
|
|
||||||
} => TouchpadRotate {
|
|
||||||
device_id: *device_id,
|
|
||||||
delta: *delta,
|
|
||||||
phase: *phase,
|
|
||||||
},
|
|
||||||
TouchpadPressure {
|
|
||||||
device_id,
|
|
||||||
pressure,
|
|
||||||
stage,
|
|
||||||
} => TouchpadPressure {
|
|
||||||
device_id: *device_id,
|
|
||||||
pressure: *pressure,
|
|
||||||
stage: *stage,
|
|
||||||
},
|
|
||||||
AxisMotion {
|
|
||||||
device_id,
|
|
||||||
axis,
|
|
||||||
value,
|
|
||||||
} => AxisMotion {
|
|
||||||
device_id: *device_id,
|
|
||||||
axis: *axis,
|
|
||||||
value: *value,
|
|
||||||
},
|
|
||||||
Touch(touch) => Touch(*touch),
|
|
||||||
ThemeChanged(theme) => ThemeChanged(*theme),
|
|
||||||
ScaleFactorChanged { .. } => {
|
|
||||||
unreachable!("Static event can't be about scale factor changing")
|
|
||||||
}
|
|
||||||
Occluded(occluded) => Occluded(*occluded),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> WindowEvent<'a> {
|
|
||||||
pub fn to_static(self) -> Option<WindowEvent<'static>> {
|
|
||||||
use self::WindowEvent::*;
|
|
||||||
match self {
|
|
||||||
Resized(size) => Some(Resized(size)),
|
|
||||||
Moved(position) => Some(Moved(position)),
|
|
||||||
CloseRequested => Some(CloseRequested),
|
|
||||||
Destroyed => Some(Destroyed),
|
|
||||||
DroppedFile(file) => Some(DroppedFile(file)),
|
|
||||||
HoveredFile(file) => Some(HoveredFile(file)),
|
|
||||||
HoveredFileCancelled => Some(HoveredFileCancelled),
|
|
||||||
Focused(focused) => Some(Focused(focused)),
|
|
||||||
KeyboardInput {
|
|
||||||
device_id,
|
|
||||||
event,
|
|
||||||
is_synthetic,
|
|
||||||
} => Some(KeyboardInput {
|
|
||||||
device_id,
|
|
||||||
event,
|
|
||||||
is_synthetic,
|
|
||||||
}),
|
|
||||||
ModifiersChanged(modifers) => Some(ModifiersChanged(modifers)),
|
|
||||||
Ime(event) => Some(Ime(event)),
|
|
||||||
CursorMoved {
|
|
||||||
device_id,
|
|
||||||
position,
|
|
||||||
} => Some(CursorMoved {
|
|
||||||
device_id,
|
|
||||||
position,
|
|
||||||
}),
|
|
||||||
CursorEntered { device_id } => Some(CursorEntered { device_id }),
|
|
||||||
CursorLeft { device_id } => Some(CursorLeft { device_id }),
|
|
||||||
MouseWheel {
|
|
||||||
device_id,
|
|
||||||
delta,
|
|
||||||
phase,
|
|
||||||
} => Some(MouseWheel {
|
|
||||||
device_id,
|
|
||||||
delta,
|
|
||||||
phase,
|
|
||||||
}),
|
|
||||||
MouseInput {
|
|
||||||
device_id,
|
|
||||||
state,
|
|
||||||
button,
|
|
||||||
} => Some(MouseInput {
|
|
||||||
device_id,
|
|
||||||
state,
|
|
||||||
button,
|
|
||||||
}),
|
|
||||||
TouchpadMagnify {
|
|
||||||
device_id,
|
|
||||||
delta,
|
|
||||||
phase,
|
|
||||||
} => Some(TouchpadMagnify {
|
|
||||||
device_id,
|
|
||||||
delta,
|
|
||||||
phase,
|
|
||||||
}),
|
|
||||||
SmartMagnify { device_id } => Some(SmartMagnify { device_id }),
|
|
||||||
TouchpadRotate {
|
|
||||||
device_id,
|
|
||||||
delta,
|
|
||||||
phase,
|
|
||||||
} => Some(TouchpadRotate {
|
|
||||||
device_id,
|
|
||||||
delta,
|
|
||||||
phase,
|
|
||||||
}),
|
|
||||||
TouchpadPressure {
|
|
||||||
device_id,
|
|
||||||
pressure,
|
|
||||||
stage,
|
|
||||||
} => Some(TouchpadPressure {
|
|
||||||
device_id,
|
|
||||||
pressure,
|
|
||||||
stage,
|
|
||||||
}),
|
|
||||||
AxisMotion {
|
|
||||||
device_id,
|
|
||||||
axis,
|
|
||||||
value,
|
|
||||||
} => Some(AxisMotion {
|
|
||||||
device_id,
|
|
||||||
axis,
|
|
||||||
value,
|
|
||||||
}),
|
|
||||||
Touch(touch) => Some(Touch(touch)),
|
|
||||||
ThemeChanged(theme) => Some(ThemeChanged(theme)),
|
|
||||||
ScaleFactorChanged { .. } => None,
|
|
||||||
Occluded(occluded) => Some(Occluded(occluded)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Identifier of an input device.
|
/// Identifier of an input device.
|
||||||
///
|
///
|
||||||
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
|
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
|
||||||
@@ -1137,7 +926,12 @@ pub enum TouchPhase {
|
|||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
|
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
|
||||||
/// - **macOS:** Unsupported.
|
/// - **macOS:** Unsupported.
|
||||||
|
///
|
||||||
|
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||||
|
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||||
|
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct Touch {
|
pub struct Touch {
|
||||||
pub device_id: DeviceId,
|
pub device_id: DeviceId,
|
||||||
@@ -1148,7 +942,7 @@ pub struct Touch {
|
|||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - Only available on **iOS** 9.0+ and **Windows** 8+.
|
/// - Only available on **iOS** 9.0+, **Windows** 8+, and **Web**.
|
||||||
pub force: Option<Force>,
|
pub force: Option<Force>,
|
||||||
/// Unique identifier of a finger.
|
/// Unique identifier of a finger.
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
@@ -1225,6 +1019,13 @@ pub enum ElementState {
|
|||||||
Released,
|
Released,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ElementState {
|
||||||
|
/// True if `self == Pressed`.
|
||||||
|
pub fn is_pressed(self) -> bool {
|
||||||
|
self == ElementState::Pressed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Describes a button of a mouse controller.
|
/// Describes a button of a mouse controller.
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
@@ -1268,3 +1069,239 @@ pub enum MouseScrollDelta {
|
|||||||
/// and move the content right and down (to reveal more things left and up).
|
/// and move the content right and down (to reveal more things left and up).
|
||||||
PixelDelta(PhysicalPosition<f64>),
|
PixelDelta(PhysicalPosition<f64>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle to synchroniously change the size of the window from the
|
||||||
|
/// [`WindowEvent`].
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct InnerSizeWriter {
|
||||||
|
pub(crate) new_inner_size: Weak<Mutex<PhysicalSize<u32>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InnerSizeWriter {
|
||||||
|
#[cfg(not(orbital_platform))]
|
||||||
|
pub(crate) fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
|
||||||
|
Self { new_inner_size }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to request inner size which will be set synchroniously on the window.
|
||||||
|
pub fn request_inner_size(
|
||||||
|
&mut self,
|
||||||
|
new_inner_size: PhysicalSize<u32>,
|
||||||
|
) -> Result<(), ExternalError> {
|
||||||
|
if let Some(inner) = self.new_inner_size.upgrade() {
|
||||||
|
*inner.lock().unwrap() = new_inner_size;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ExternalError::Ignored)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for InnerSizeWriter {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.new_inner_size.as_ptr() == other.new_inner_size.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::event;
|
||||||
|
use std::collections::{BTreeSet, HashSet};
|
||||||
|
|
||||||
|
macro_rules! foreach_event {
|
||||||
|
($closure:expr) => {{
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut x = $closure;
|
||||||
|
let did = unsafe { event::DeviceId::dummy() };
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
{
|
||||||
|
use crate::event::{Event::*, Ime::Enabled, WindowEvent::*};
|
||||||
|
use crate::window::WindowId;
|
||||||
|
|
||||||
|
// Mainline events.
|
||||||
|
let wid = unsafe { WindowId::dummy() };
|
||||||
|
x(UserEvent(()));
|
||||||
|
x(NewEvents(event::StartCause::Init));
|
||||||
|
x(RedrawRequested(wid));
|
||||||
|
x(AboutToWait);
|
||||||
|
x(LoopExiting);
|
||||||
|
x(Suspended);
|
||||||
|
x(Resumed);
|
||||||
|
|
||||||
|
// Window events.
|
||||||
|
let with_window_event = |wev| {
|
||||||
|
x(WindowEvent {
|
||||||
|
window_id: wid,
|
||||||
|
event: wev,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
with_window_event(CloseRequested);
|
||||||
|
with_window_event(Destroyed);
|
||||||
|
with_window_event(Focused(true));
|
||||||
|
with_window_event(Moved((0, 0).into()));
|
||||||
|
with_window_event(Resized((0, 0).into()));
|
||||||
|
with_window_event(DroppedFile("x.txt".into()));
|
||||||
|
with_window_event(HoveredFile("x.txt".into()));
|
||||||
|
with_window_event(HoveredFileCancelled);
|
||||||
|
with_window_event(Ime(Enabled));
|
||||||
|
with_window_event(CursorMoved {
|
||||||
|
device_id: did,
|
||||||
|
position: (0, 0).into(),
|
||||||
|
});
|
||||||
|
with_window_event(ModifiersChanged(event::Modifiers::default()));
|
||||||
|
with_window_event(CursorEntered { device_id: did });
|
||||||
|
with_window_event(CursorLeft { device_id: did });
|
||||||
|
with_window_event(MouseWheel {
|
||||||
|
device_id: did,
|
||||||
|
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
|
||||||
|
phase: event::TouchPhase::Started,
|
||||||
|
});
|
||||||
|
with_window_event(MouseInput {
|
||||||
|
device_id: did,
|
||||||
|
state: event::ElementState::Pressed,
|
||||||
|
button: event::MouseButton::Other(0),
|
||||||
|
});
|
||||||
|
with_window_event(TouchpadMagnify {
|
||||||
|
device_id: did,
|
||||||
|
delta: 0.0,
|
||||||
|
phase: event::TouchPhase::Started,
|
||||||
|
});
|
||||||
|
with_window_event(SmartMagnify { device_id: did });
|
||||||
|
with_window_event(TouchpadRotate {
|
||||||
|
device_id: did,
|
||||||
|
delta: 0.0,
|
||||||
|
phase: event::TouchPhase::Started,
|
||||||
|
});
|
||||||
|
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(Touch(event::Touch {
|
||||||
|
device_id: did,
|
||||||
|
phase: event::TouchPhase::Started,
|
||||||
|
location: (0.0, 0.0).into(),
|
||||||
|
id: 0,
|
||||||
|
force: Some(event::Force::Normalized(0.0)),
|
||||||
|
}));
|
||||||
|
with_window_event(ThemeChanged(crate::window::Theme::Light));
|
||||||
|
with_window_event(Occluded(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
{
|
||||||
|
use event::DeviceEvent::*;
|
||||||
|
|
||||||
|
let with_device_event = |dev_ev| {
|
||||||
|
x(event::Event::DeviceEvent {
|
||||||
|
device_id: did,
|
||||||
|
event: dev_ev,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
with_device_event(Added);
|
||||||
|
with_device_event(Removed);
|
||||||
|
with_device_event(MouseMotion {
|
||||||
|
delta: (0.0, 0.0).into(),
|
||||||
|
});
|
||||||
|
with_device_event(MouseWheel {
|
||||||
|
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
|
||||||
|
});
|
||||||
|
with_device_event(Motion {
|
||||||
|
axis: 0,
|
||||||
|
value: 0.0,
|
||||||
|
});
|
||||||
|
with_device_event(Button {
|
||||||
|
button: 0,
|
||||||
|
state: event::ElementState::Pressed,
|
||||||
|
});
|
||||||
|
with_device_event(Text { codepoint: 'a' });
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::redundant_clone)]
|
||||||
|
#[test]
|
||||||
|
fn test_event_clone() {
|
||||||
|
foreach_event!(|event: event::Event<()>| {
|
||||||
|
let event2 = event.clone();
|
||||||
|
assert_eq!(event, event2);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_map_nonuser_event() {
|
||||||
|
foreach_event!(|event: event::Event<()>| {
|
||||||
|
let is_user = matches!(event, event::Event::UserEvent(()));
|
||||||
|
let event2 = event.map_nonuser_event::<()>();
|
||||||
|
if is_user {
|
||||||
|
assert_eq!(event2, Err(event::Event::UserEvent(())));
|
||||||
|
} else {
|
||||||
|
assert!(event2.is_ok());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_force_normalize() {
|
||||||
|
let force = event::Force::Normalized(0.0);
|
||||||
|
assert_eq!(force.normalized(), 0.0);
|
||||||
|
|
||||||
|
let force2 = event::Force::Calibrated {
|
||||||
|
force: 5.0,
|
||||||
|
max_possible_force: 2.5,
|
||||||
|
altitude_angle: None,
|
||||||
|
};
|
||||||
|
assert_eq!(force2.normalized(), 2.0);
|
||||||
|
|
||||||
|
let force3 = event::Force::Calibrated {
|
||||||
|
force: 5.0,
|
||||||
|
max_possible_force: 2.5,
|
||||||
|
altitude_angle: Some(std::f64::consts::PI / 2.0),
|
||||||
|
};
|
||||||
|
assert_eq!(force3.normalized(), 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::clone_on_copy)]
|
||||||
|
#[test]
|
||||||
|
fn ensure_attrs_do_not_panic() {
|
||||||
|
foreach_event!(|event: event::Event<()>| {
|
||||||
|
let _ = format!("{:?}", event);
|
||||||
|
});
|
||||||
|
let _ = event::StartCause::Init.clone();
|
||||||
|
|
||||||
|
let did = unsafe { crate::event::DeviceId::dummy() }.clone();
|
||||||
|
HashSet::new().insert(did);
|
||||||
|
let mut set = [did, did, did];
|
||||||
|
set.sort_unstable();
|
||||||
|
let mut set2 = BTreeSet::new();
|
||||||
|
set2.insert(did);
|
||||||
|
set2.insert(did);
|
||||||
|
|
||||||
|
HashSet::new().insert(event::TouchPhase::Started.clone());
|
||||||
|
HashSet::new().insert(event::MouseButton::Left.clone());
|
||||||
|
HashSet::new().insert(event::Ime::Enabled);
|
||||||
|
|
||||||
|
let _ = event::Touch {
|
||||||
|
device_id: did,
|
||||||
|
phase: event::TouchPhase::Started,
|
||||||
|
location: (0.0, 0.0).into(),
|
||||||
|
id: 0,
|
||||||
|
force: Some(event::Force::Normalized(0.0)),
|
||||||
|
}
|
||||||
|
.clone();
|
||||||
|
let _ = event::Force::Calibrated {
|
||||||
|
force: 0.0,
|
||||||
|
max_possible_force: 0.0,
|
||||||
|
altitude_angle: None,
|
||||||
|
}
|
||||||
|
.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
//! handle events.
|
//! handle events.
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||||
use std::{error, fmt};
|
use std::{error, fmt};
|
||||||
|
|
||||||
use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle};
|
use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle};
|
||||||
@@ -18,6 +18,7 @@ use std::time::{Duration, Instant};
|
|||||||
#[cfg(wasm_platform)]
|
#[cfg(wasm_platform)]
|
||||||
use web_time::{Duration, Instant};
|
use web_time::{Duration, Instant};
|
||||||
|
|
||||||
|
use crate::error::EventLoopError;
|
||||||
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
|
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
|
||||||
|
|
||||||
/// 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
|
||||||
@@ -87,23 +88,23 @@ impl<T> EventLoopBuilder<T> {
|
|||||||
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
|
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
|
||||||
/// and only once per application.***
|
/// and only once per application.***
|
||||||
///
|
///
|
||||||
/// Attempting to create the event loop on a different thread, or multiple event loops in
|
|
||||||
/// the same application, will panic. This restriction isn't
|
|
||||||
/// strictly necessary on all platforms, but is imposed to eliminate any nasty surprises when
|
|
||||||
/// porting to platforms that require it. `EventLoopBuilderExt::any_thread` functions are exposed
|
|
||||||
/// in the relevant [`platform`] module if the target platform supports creating an event loop on
|
|
||||||
/// any thread.
|
|
||||||
///
|
|
||||||
/// Calling this function will result in display backend initialisation.
|
/// Calling this function will result in display backend initialisation.
|
||||||
///
|
///
|
||||||
|
/// ## Panics
|
||||||
|
///
|
||||||
|
/// Attempting to create the event loop off the main thread will panic. This
|
||||||
|
/// restriction isn't strictly necessary on all platforms, but is imposed to
|
||||||
|
/// eliminate any nasty surprises when porting to platforms that require it.
|
||||||
|
/// `EventLoopBuilderExt::any_thread` functions are exposed in the relevant
|
||||||
|
/// [`platform`] module if the target platform supports creating an event
|
||||||
|
/// loop on any thread.
|
||||||
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **Linux:** Backend type can be controlled using an environment variable
|
/// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY`
|
||||||
/// `WINIT_UNIX_BACKEND`. Legal values are `x11` and `wayland`.
|
/// or `DISPLAY` respectively when building the event loop.
|
||||||
/// If it is not set, winit will try to connect to a Wayland connection, and if that fails,
|
/// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling
|
||||||
/// will fall back on X11. If this variable is set with any other value, winit will panic.
|
/// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic.
|
||||||
/// - **Android:** Must be configured with an `AndroidApp` from `android_main()` by calling
|
|
||||||
/// [`.with_android_app(app)`] before calling `.build()`.
|
|
||||||
///
|
///
|
||||||
/// [`platform`]: crate::platform
|
/// [`platform`]: crate::platform
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@@ -115,17 +116,17 @@ impl<T> EventLoopBuilder<T> {
|
|||||||
doc = "[`.with_android_app(app)`]: #only-available-on-android"
|
doc = "[`.with_android_app(app)`]: #only-available-on-android"
|
||||||
)]
|
)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build(&mut self) -> EventLoop<T> {
|
pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> {
|
||||||
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
|
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
|
||||||
panic!("Creating EventLoop multiple times is not supported.");
|
return Err(EventLoopError::RecreationAttempt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Certain platforms accept a mutable reference in their API.
|
// Certain platforms accept a mutable reference in their API.
|
||||||
#[allow(clippy::unnecessary_mut_passed)]
|
#[allow(clippy::unnecessary_mut_passed)]
|
||||||
EventLoop {
|
Ok(EventLoop {
|
||||||
event_loop: platform_impl::EventLoop::new(&mut self.platform_specific),
|
event_loop: platform_impl::EventLoop::new(&mut self.platform_specific)?,
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(wasm_platform)]
|
#[cfg(wasm_platform)]
|
||||||
@@ -148,7 +149,7 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
|
|||||||
|
|
||||||
/// Set by the user callback given to the [`EventLoop::run`] method.
|
/// Set by the user callback given to the [`EventLoop::run`] method.
|
||||||
///
|
///
|
||||||
/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`] is emitted.
|
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
|
||||||
///
|
///
|
||||||
/// Defaults to [`Poll`].
|
/// Defaults to [`Poll`].
|
||||||
///
|
///
|
||||||
@@ -156,7 +157,7 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
|
|||||||
///
|
///
|
||||||
/// Almost every change is persistent between multiple calls to the event loop closure within a
|
/// Almost every change is persistent between multiple calls to the event loop closure within a
|
||||||
/// given run loop. The only exception to this is [`ExitWithCode`] which, once set, cannot be unset.
|
/// given run loop. The only exception to this is [`ExitWithCode`] which, once set, cannot be unset.
|
||||||
/// Changes are **not** persistent between multiple calls to `run_return` - issuing a new call will
|
/// Changes are **not** persistent between multiple calls to `run_ondemand` - issuing a new call will
|
||||||
/// reset the control flow to [`Poll`].
|
/// reset the control flow to [`Poll`].
|
||||||
///
|
///
|
||||||
/// [`ExitWithCode`]: Self::ExitWithCode
|
/// [`ExitWithCode`]: Self::ExitWithCode
|
||||||
@@ -180,7 +181,7 @@ pub enum ControlFlow {
|
|||||||
/// [`Poll`]: Self::Poll
|
/// [`Poll`]: Self::Poll
|
||||||
WaitUntil(Instant),
|
WaitUntil(Instant),
|
||||||
|
|
||||||
/// Send a [`LoopDestroyed`] event and stop the event loop. This variant is *sticky* - once set,
|
/// Send a [`LoopExiting`] event and stop the event loop. This variant is *sticky* - once set,
|
||||||
/// `control_flow` cannot be changed from `ExitWithCode`, and any future attempts to do so will
|
/// `control_flow` cannot be changed from `ExitWithCode`, and any future attempts to do so will
|
||||||
/// result in the `control_flow` parameter being reset to `ExitWithCode`.
|
/// result in the `control_flow` parameter being reset to `ExitWithCode`.
|
||||||
///
|
///
|
||||||
@@ -189,12 +190,12 @@ pub enum ControlFlow {
|
|||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **Android / iOS / WASM:** The supplied exit code is unused.
|
/// - **Android / iOS / Web:** The supplied exit code is unused.
|
||||||
/// - **Unix:** On most Unix-like platforms, only the 8 least significant bits will be used,
|
/// - **Unix:** On most Unix-like platforms, only the 8 least significant bits will be used,
|
||||||
/// which can cause surprises with negative exit values (`-42` would end up as `214`). See
|
/// which can cause surprises with negative exit values (`-42` would end up as `214`). See
|
||||||
/// [`std::process::exit`].
|
/// [`std::process::exit`].
|
||||||
///
|
///
|
||||||
/// [`LoopDestroyed`]: Event::LoopDestroyed
|
/// [`LoopExiting`]: Event::LoopExiting
|
||||||
/// [`Exit`]: ControlFlow::Exit
|
/// [`Exit`]: ControlFlow::Exit
|
||||||
ExitWithCode(i32),
|
ExitWithCode(i32),
|
||||||
}
|
}
|
||||||
@@ -268,42 +269,49 @@ impl EventLoop<()> {
|
|||||||
///
|
///
|
||||||
/// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build
|
/// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> EventLoop<()> {
|
pub fn new() -> Result<EventLoop<()>, EventLoopError> {
|
||||||
EventLoopBuilder::new().build()
|
EventLoopBuilder::new().build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EventLoop<()> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> EventLoop<T> {
|
impl<T> EventLoop<T> {
|
||||||
#[deprecated = "Use `EventLoopBuilder::<T>::with_user_event().build()` instead."]
|
#[deprecated = "Use `EventLoopBuilder::<T>::with_user_event().build()` instead."]
|
||||||
pub fn with_user_event() -> EventLoop<T> {
|
pub fn with_user_event() -> Result<EventLoop<T>, EventLoopError> {
|
||||||
EventLoopBuilder::<T>::with_user_event().build()
|
EventLoopBuilder::<T>::with_user_event().build()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hijacks the calling thread and initializes the winit event loop with the provided
|
/// Runs the event loop in the calling thread and calls the given `event_handler` closure
|
||||||
/// closure. Since the closure is `'static`, it must be a `move` closure if it needs to
|
/// to dispatch any pending events.
|
||||||
|
///
|
||||||
|
/// Since the closure is `'static`, it must be a `move` closure if it needs to
|
||||||
/// access any data from the calling context.
|
/// access any data from the calling context.
|
||||||
///
|
///
|
||||||
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the
|
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the
|
||||||
/// event loop's behavior.
|
/// event loop's behavior.
|
||||||
///
|
///
|
||||||
/// Any values not passed to this function will *not* be dropped.
|
|
||||||
///
|
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **X11 / Wayland:** The program terminates with exit code 1 if the display server
|
/// - **X11 / Wayland:** The program terminates with exit code 1 if the display server
|
||||||
/// disconnects.
|
/// disconnects.
|
||||||
|
/// - **iOS:** Will never return to the caller and so values not passed to this function will
|
||||||
|
/// *not* be dropped before the process exits.
|
||||||
|
/// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript exception
|
||||||
|
/// (that Rust doesn't see) that will also mean that the rest of the function is never executed
|
||||||
|
/// and any values not passed to this function will *not* be dropped.
|
||||||
|
///
|
||||||
|
/// Web applications are recommended to use `spawn()` instead of `run()` to avoid the need
|
||||||
|
/// for the Javascript exception trick, and to make it clearer that the event loop runs
|
||||||
|
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the
|
||||||
|
/// current thread of execution like it does on other platforms.
|
||||||
|
///
|
||||||
|
/// This function won't be available with `target_feature = "exception-handling"`.
|
||||||
///
|
///
|
||||||
/// [`ControlFlow`]: crate::event_loop::ControlFlow
|
/// [`ControlFlow`]: crate::event_loop::ControlFlow
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run<F>(self, event_handler: F) -> !
|
#[cfg(not(all(wasm_platform, target_feature = "exception-handling")))]
|
||||||
|
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
|
F: FnMut(Event<T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
self.event_loop.run(event_handler)
|
self.event_loop.run(event_handler)
|
||||||
}
|
}
|
||||||
@@ -437,3 +445,29 @@ pub enum DeviceEvents {
|
|||||||
/// Never capture device events.
|
/// Never capture device events.
|
||||||
Never,
|
Never,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A unique identifier of the winit's async request.
|
||||||
|
///
|
||||||
|
/// This could be used to identify the async request once it's done
|
||||||
|
/// and a specific action must be taken.
|
||||||
|
///
|
||||||
|
/// One of the handling scenarious could be to maintain a working list
|
||||||
|
/// containing [`AsyncRequestSerial`] and some closure associated with it.
|
||||||
|
/// Then once event is arriving the working list is being traversed and a job
|
||||||
|
/// executed and removed from the list.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct AsyncRequestSerial {
|
||||||
|
serial: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRequestSerial {
|
||||||
|
// TODO(kchibisov) remove `cfg` when the clipboard will be added.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn get() -> Self {
|
||||||
|
static CURRENT_SERIAL: AtomicU64 = AtomicU64::new(0);
|
||||||
|
// NOTE: we rely on wrap around here, while the user may just request
|
||||||
|
// in the loop u64::MAX times that's issue is considered on them.
|
||||||
|
let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
|
||||||
|
Self { serial }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
//
|
//
|
||||||
// --------- END OF W3C SHORT NOTICE ---------------------------------------------------------------
|
// --------- END OF W3C SHORT NOTICE ---------------------------------------------------------------
|
||||||
|
|
||||||
use smol_str::SmolStr;
|
pub use smol_str::SmolStr;
|
||||||
|
|
||||||
/// Contains the platform-native physical key identifier
|
/// Contains the platform-native physical key identifier
|
||||||
///
|
///
|
||||||
@@ -135,7 +135,7 @@ impl std::fmt::Debug for NativeKeyCode {
|
|||||||
/// This enum is primarily used to store raw keysym when Winit doesn't map a given native logical
|
/// This enum is primarily used to store raw keysym when Winit doesn't map a given native logical
|
||||||
/// key identifier to a meaningful [`Key`] variant. This lets you use [`Key`], and let the user
|
/// key identifier to a meaningful [`Key`] variant. This lets you use [`Key`], and let the user
|
||||||
/// define keybinds which work in the presence of identifiers we haven't mapped for you yet.
|
/// define keybinds which work in the presence of identifiers we haven't mapped for you yet.
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum NativeKey {
|
pub enum NativeKey {
|
||||||
Unidentified,
|
Unidentified,
|
||||||
@@ -662,7 +662,7 @@ pub enum KeyCode {
|
|||||||
///
|
///
|
||||||
/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
|
/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum Key<Str = SmolStr> {
|
pub enum Key<Str = SmolStr> {
|
||||||
/// A key string that corresponds to the character typed by the user, taking into account the
|
/// A key string that corresponds to the character typed by the user, taking into account the
|
||||||
|
|||||||
16
src/lib.rs
16
src/lib.rs
@@ -7,7 +7,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use winit::event_loop::EventLoop;
|
//! use winit::event_loop::EventLoop;
|
||||||
//! let event_loop = EventLoop::new();
|
//! let event_loop = EventLoop::new().unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Once this is done there are two ways to create a [`Window`]:
|
//! Once this is done there are two ways to create a [`Window`]:
|
||||||
@@ -32,12 +32,12 @@
|
|||||||
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
|
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
|
||||||
//! will run until the `control_flow` argument given to the closure is set to
|
//! will run until the `control_flow` argument given to the closure is set to
|
||||||
//! [`ControlFlow`]`::`[`ExitWithCode`] (which [`ControlFlow`]`::`[`Exit`] aliases to), at which
|
//! [`ControlFlow`]`::`[`ExitWithCode`] (which [`ControlFlow`]`::`[`Exit`] aliases to), at which
|
||||||
//! point [`Event`]`::`[`LoopDestroyed`] is emitted and the entire program terminates.
|
//! point [`Event`]`::`[`LoopExiting`] is emitted and the entire program terminates.
|
||||||
//!
|
//!
|
||||||
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
|
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
|
||||||
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on
|
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on
|
||||||
//! most other platforms. However, this model can be re-implemented to an extent with
|
//! most other platforms. However, this model can be re-implemented to an extent with
|
||||||
//! [`EventLoopExtRunReturn::run_return`]. See that method's documentation for more reasons about why
|
//! [`EventLoopExtPumpEvents::pump_events`]. See that method's documentation for more reasons about why
|
||||||
//! it's discouraged, beyond compatibility reasons.
|
//! it's discouraged, beyond compatibility reasons.
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
//! window::WindowBuilder,
|
//! window::WindowBuilder,
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! let event_loop = EventLoop::new();
|
//! let event_loop = EventLoop::new().unwrap();
|
||||||
//! let window = WindowBuilder::new().build(&event_loop).unwrap();
|
//! let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||||
//!
|
//!
|
||||||
//! event_loop.run(move |event, _, control_flow| {
|
//! event_loop.run(move |event, _, control_flow| {
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
//! println!("The close button was pressed; stopping");
|
//! println!("The close button was pressed; stopping");
|
||||||
//! control_flow.set_exit();
|
//! control_flow.set_exit();
|
||||||
//! },
|
//! },
|
||||||
//! Event::MainEventsCleared => {
|
//! Event::AboutToWait => {
|
||||||
//! // Application update code.
|
//! // Application update code.
|
||||||
//!
|
//!
|
||||||
//! // Queue a RedrawRequested event.
|
//! // Queue a RedrawRequested event.
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
//! // Redraw the application.
|
//! // Redraw the application.
|
||||||
//! //
|
//! //
|
||||||
//! // It's preferable for applications that do not render continuously to render in
|
//! // It's preferable for applications that do not render continuously to render in
|
||||||
//! // this event rather than in MainEventsCleared, since rendering in here allows
|
//! // this event rather than in AboutToWait, since rendering in here allows
|
||||||
//! // the program to gracefully handle redraws requested by the OS.
|
//! // the program to gracefully handle redraws requested by the OS.
|
||||||
//! },
|
//! },
|
||||||
//! _ => ()
|
//! _ => ()
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
//! window visible only once you're ready to render into it.
|
//! window visible only once you're ready to render into it.
|
||||||
//!
|
//!
|
||||||
//! [`EventLoop`]: event_loop::EventLoop
|
//! [`EventLoop`]: event_loop::EventLoop
|
||||||
//! [`EventLoopExtRunReturn::run_return`]: ./platform/run_return/trait.EventLoopExtRunReturn.html#tymethod.run_return
|
//! [`EventLoopExtPumpEvents::pump_events`]: ./platform/pump_events/trait.EventLoopExtPumpEvents.html#tymethod.pump_events
|
||||||
//! [`EventLoop::new()`]: event_loop::EventLoop::new
|
//! [`EventLoop::new()`]: event_loop::EventLoop::new
|
||||||
//! [event_loop_run]: event_loop::EventLoop::run
|
//! [event_loop_run]: event_loop::EventLoop::run
|
||||||
//! [`ControlFlow`]: event_loop::ControlFlow
|
//! [`ControlFlow`]: event_loop::ControlFlow
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
//! [`WindowEvent`]: event::WindowEvent
|
//! [`WindowEvent`]: event::WindowEvent
|
||||||
//! [`DeviceEvent`]: event::DeviceEvent
|
//! [`DeviceEvent`]: event::DeviceEvent
|
||||||
//! [`UserEvent`]: event::Event::UserEvent
|
//! [`UserEvent`]: event::Event::UserEvent
|
||||||
//! [`LoopDestroyed`]: event::Event::LoopDestroyed
|
//! [`LoopExiting`]: event::Event::LoopExiting
|
||||||
//! [`platform`]: platform
|
//! [`platform`]: platform
|
||||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||||
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
|
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
|
||||||
|
|||||||
@@ -22,27 +22,6 @@ impl<T: 'static> EventLoopExtIOS for EventLoop<T> {
|
|||||||
|
|
||||||
/// Additional methods on [`Window`] that are specific to iOS.
|
/// Additional methods on [`Window`] that are specific to iOS.
|
||||||
pub trait WindowExtIOS {
|
pub trait WindowExtIOS {
|
||||||
/// Returns a pointer to the [`UIWindow`] that is used by this window.
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
|
||||||
///
|
|
||||||
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
|
|
||||||
fn ui_window(&self) -> *mut c_void;
|
|
||||||
|
|
||||||
/// Returns a pointer to the [`UIViewController`] that is used by this window.
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
|
||||||
///
|
|
||||||
/// [`UIViewController`]: https://developer.apple.com/documentation/uikit/uiviewcontroller?language=objc
|
|
||||||
fn ui_view_controller(&self) -> *mut c_void;
|
|
||||||
|
|
||||||
/// Returns a pointer to the [`UIView`] that is used by this window.
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
|
||||||
///
|
|
||||||
/// [`UIView`]: https://developer.apple.com/documentation/uikit/uiview?language=objc
|
|
||||||
fn ui_view(&self) -> *mut c_void;
|
|
||||||
|
|
||||||
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
|
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
|
||||||
///
|
///
|
||||||
/// The default value is device dependent, and it's recommended GLES or Metal applications set
|
/// The default value is device dependent, and it's recommended GLES or Metal applications set
|
||||||
@@ -97,45 +76,35 @@ pub trait WindowExtIOS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WindowExtIOS for Window {
|
impl WindowExtIOS for Window {
|
||||||
#[inline]
|
|
||||||
fn ui_window(&self) -> *mut c_void {
|
|
||||||
self.window.ui_window()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn ui_view_controller(&self) -> *mut c_void {
|
|
||||||
self.window.ui_view_controller()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn ui_view(&self) -> *mut c_void {
|
|
||||||
self.window.ui_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_scale_factor(&self, scale_factor: f64) {
|
fn set_scale_factor(&self, scale_factor: f64) {
|
||||||
self.window.set_scale_factor(scale_factor)
|
self.window
|
||||||
|
.maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
|
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
|
||||||
self.window.set_valid_orientations(valid_orientations)
|
self.window
|
||||||
|
.maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
|
fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
|
||||||
self.window.set_prefers_home_indicator_hidden(hidden)
|
self.window
|
||||||
|
.maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
|
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
|
||||||
self.window
|
self.window.maybe_queue_on_main(move |w| {
|
||||||
.set_preferred_screen_edges_deferring_system_gestures(edges)
|
w.set_preferred_screen_edges_deferring_system_gestures(edges)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
||||||
self.window.set_prefers_status_bar_hidden(hidden)
|
self.window
|
||||||
|
.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,16 +10,6 @@ use crate::{
|
|||||||
|
|
||||||
/// Additional methods on [`Window`] that are specific to MacOS.
|
/// Additional methods on [`Window`] that are specific to MacOS.
|
||||||
pub trait WindowExtMacOS {
|
pub trait WindowExtMacOS {
|
||||||
/// Returns a pointer to the cocoa `NSWindow` that is used by this window.
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
|
||||||
fn ns_window(&self) -> *mut c_void;
|
|
||||||
|
|
||||||
/// Returns a pointer to the cocoa `NSView` that is used by this window.
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
|
||||||
fn ns_view(&self) -> *mut c_void;
|
|
||||||
|
|
||||||
/// Returns whether or not the window is in simple fullscreen mode.
|
/// Returns whether or not the window is in simple fullscreen mode.
|
||||||
fn simple_fullscreen(&self) -> bool;
|
fn simple_fullscreen(&self) -> bool;
|
||||||
|
|
||||||
@@ -38,6 +28,28 @@ pub trait WindowExtMacOS {
|
|||||||
/// Sets whether or not the window has shadow.
|
/// Sets whether or not the window has shadow.
|
||||||
fn set_has_shadow(&self, has_shadow: bool);
|
fn set_has_shadow(&self, has_shadow: bool);
|
||||||
|
|
||||||
|
/// Group windows together by using the same tabbing identifier.
|
||||||
|
///
|
||||||
|
/// <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
|
||||||
|
fn set_tabbing_identifier(&self, identifier: &str);
|
||||||
|
|
||||||
|
/// Returns the window's tabbing identifier.
|
||||||
|
fn tabbing_identifier(&self) -> String;
|
||||||
|
|
||||||
|
/// Select next tab.
|
||||||
|
fn select_next_tab(&self);
|
||||||
|
|
||||||
|
/// Select previous tab.
|
||||||
|
fn select_previous_tab(&self);
|
||||||
|
|
||||||
|
/// Select the tab with the given index.
|
||||||
|
///
|
||||||
|
/// Will no-op when the index is out of bounds.
|
||||||
|
fn select_tab_at_index(&self, index: usize);
|
||||||
|
|
||||||
|
/// Get the number of tabs in the window tab group.
|
||||||
|
fn num_tabs(&self) -> usize;
|
||||||
|
|
||||||
/// Get the window's edit state.
|
/// Get the window's edit state.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@@ -70,54 +82,80 @@ pub trait WindowExtMacOS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WindowExtMacOS for Window {
|
impl WindowExtMacOS for Window {
|
||||||
#[inline]
|
|
||||||
fn ns_window(&self) -> *mut c_void {
|
|
||||||
self.window.ns_window()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn ns_view(&self) -> *mut c_void {
|
|
||||||
self.window.ns_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simple_fullscreen(&self) -> bool {
|
fn simple_fullscreen(&self) -> bool {
|
||||||
self.window.simple_fullscreen()
|
self.window.maybe_wait_on_main(|w| w.simple_fullscreen())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
|
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
|
||||||
self.window.set_simple_fullscreen(fullscreen)
|
self.window
|
||||||
|
.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn has_shadow(&self) -> bool {
|
fn has_shadow(&self) -> bool {
|
||||||
self.window.has_shadow()
|
self.window.maybe_wait_on_main(|w| w.has_shadow())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_has_shadow(&self, has_shadow: bool) {
|
fn set_has_shadow(&self, has_shadow: bool) {
|
||||||
self.window.set_has_shadow(has_shadow)
|
self.window
|
||||||
|
.maybe_queue_on_main(move |w| w.set_has_shadow(has_shadow))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_tabbing_identifier(&self, identifier: &str) {
|
||||||
|
self.window
|
||||||
|
.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn tabbing_identifier(&self) -> String {
|
||||||
|
self.window.maybe_wait_on_main(|w| w.tabbing_identifier())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn select_next_tab(&self) {
|
||||||
|
self.window.maybe_queue_on_main(|w| w.select_next_tab())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn select_previous_tab(&self) {
|
||||||
|
self.window.maybe_queue_on_main(|w| w.select_previous_tab())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn select_tab_at_index(&self, index: usize) {
|
||||||
|
self.window
|
||||||
|
.maybe_queue_on_main(move |w| w.select_tab_at_index(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn num_tabs(&self) -> usize {
|
||||||
|
self.window.maybe_wait_on_main(|w| w.num_tabs())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_document_edited(&self) -> bool {
|
fn is_document_edited(&self) -> bool {
|
||||||
self.window.is_document_edited()
|
self.window.maybe_wait_on_main(|w| w.is_document_edited())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_document_edited(&self, edited: bool) {
|
fn set_document_edited(&self, edited: bool) {
|
||||||
self.window.set_document_edited(edited)
|
self.window
|
||||||
|
.maybe_queue_on_main(move |w| w.set_document_edited(edited))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
|
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
|
||||||
self.window.set_option_as_alt(option_as_alt)
|
self.window
|
||||||
|
.maybe_queue_on_main(move |w| w.set_option_as_alt(option_as_alt))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn option_as_alt(&self) -> OptionAsAlt {
|
fn option_as_alt(&self) -> OptionAsAlt {
|
||||||
self.window.option_as_alt()
|
self.window.maybe_wait_on_main(|w| w.option_as_alt())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +199,10 @@ pub trait WindowBuilderExtMacOS {
|
|||||||
fn with_has_shadow(self, has_shadow: bool) -> WindowBuilder;
|
fn with_has_shadow(self, has_shadow: bool) -> WindowBuilder;
|
||||||
/// Window accepts click-through mouse events.
|
/// Window accepts click-through mouse events.
|
||||||
fn with_accepts_first_mouse(self, accepts_first_mouse: bool) -> WindowBuilder;
|
fn with_accepts_first_mouse(self, accepts_first_mouse: bool) -> WindowBuilder;
|
||||||
|
/// Defines the window tabbing identifier.
|
||||||
|
///
|
||||||
|
/// <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
|
||||||
|
fn with_tabbing_identifier(self, identifier: &str) -> WindowBuilder;
|
||||||
/// Set how the <kbd>Option</kbd> keys are interpreted.
|
/// Set how the <kbd>Option</kbd> keys are interpreted.
|
||||||
///
|
///
|
||||||
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
|
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
|
||||||
@@ -225,6 +267,14 @@ impl WindowBuilderExtMacOS for WindowBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> WindowBuilder {
|
||||||
|
self.platform_specific
|
||||||
|
.tabbing_identifier
|
||||||
|
.replace(tabbing_identifier.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> WindowBuilder {
|
fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> WindowBuilder {
|
||||||
self.platform_specific.option_as_alt = option_as_alt;
|
self.platform_specific.option_as_alt = option_as_alt;
|
||||||
@@ -329,6 +379,12 @@ pub trait EventLoopWindowTargetExtMacOS {
|
|||||||
fn hide_application(&self);
|
fn hide_application(&self);
|
||||||
/// Hide the other applications. In most applications this is typically triggered with Command+Option-H.
|
/// Hide the other applications. In most applications this is typically triggered with Command+Option-H.
|
||||||
fn hide_other_applications(&self);
|
fn hide_other_applications(&self);
|
||||||
|
/// Set whether the system can automatically organize windows into tabs.
|
||||||
|
///
|
||||||
|
/// <https://developer.apple.com/documentation/appkit/nswindow/1646657-allowsautomaticwindowtabbing>
|
||||||
|
fn set_allows_automatic_window_tabbing(&self, enabled: bool);
|
||||||
|
/// Returns whether the system can automatically organize windows into tabs.
|
||||||
|
fn allows_automatic_window_tabbing(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
|
impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
|
||||||
@@ -339,6 +395,14 @@ impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
|
|||||||
fn hide_other_applications(&self) {
|
fn hide_other_applications(&self) {
|
||||||
self.p.hide_other_applications()
|
self.p.hide_other_applications()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
|
||||||
|
self.p.set_allows_automatic_window_tabbing(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allows_automatic_window_tabbing(&self) -> bool {
|
||||||
|
self.p.allows_automatic_window_tabbing()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Option as alt behavior.
|
/// Option as alt behavior.
|
||||||
|
|||||||
@@ -9,9 +9,10 @@
|
|||||||
//! - `windows`
|
//! - `windows`
|
||||||
//! - `web`
|
//! - `web`
|
||||||
//!
|
//!
|
||||||
//! And the following platform-specific module:
|
//! And the following platform-specific modules:
|
||||||
//!
|
//!
|
||||||
//! - `run_return` (available on `windows`, `unix`, `macos`, and `android`)
|
//! - `run_ondemand` (available on `windows`, `unix`, `macos`, `android`)
|
||||||
|
//! - `pump_events` (available on `windows`, `unix`, `macos`, `android`)
|
||||||
//!
|
//!
|
||||||
//! However only the module corresponding to the platform you're compiling to will be available.
|
//! However only the module corresponding to the platform you're compiling to will be available.
|
||||||
|
|
||||||
@@ -23,6 +24,8 @@ pub mod ios;
|
|||||||
pub mod macos;
|
pub mod macos;
|
||||||
#[cfg(orbital_platform)]
|
#[cfg(orbital_platform)]
|
||||||
pub mod orbital;
|
pub mod orbital;
|
||||||
|
#[cfg(any(x11_platform, wayland_platform))]
|
||||||
|
pub mod startup_notify;
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(wayland_platform)]
|
||||||
pub mod wayland;
|
pub mod wayland;
|
||||||
#[cfg(wasm_platform)]
|
#[cfg(wasm_platform)]
|
||||||
@@ -32,14 +35,23 @@ pub mod windows;
|
|||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
pub mod x11;
|
pub mod x11;
|
||||||
|
|
||||||
pub mod modifier_supplement;
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
windows_platform,
|
windows_platform,
|
||||||
macos_platform,
|
macos_platform,
|
||||||
android_platform,
|
android_platform,
|
||||||
x11_platform,
|
x11_platform,
|
||||||
wayland_platform,
|
wayland_platform
|
||||||
orbital_platform
|
|
||||||
))]
|
))]
|
||||||
pub mod run_return;
|
pub mod run_ondemand;
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
windows_platform,
|
||||||
|
macos_platform,
|
||||||
|
android_platform,
|
||||||
|
x11_platform,
|
||||||
|
wayland_platform
|
||||||
|
))]
|
||||||
|
pub mod pump_events;
|
||||||
|
|
||||||
|
pub mod modifier_supplement;
|
||||||
pub mod scancode;
|
pub mod scancode;
|
||||||
|
|||||||
189
src/platform/pump_events.rs
Normal file
189
src/platform/pump_events.rs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
event::Event,
|
||||||
|
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The return status for `pump_events`
|
||||||
|
pub enum PumpStatus {
|
||||||
|
/// Continue running external loop.
|
||||||
|
Continue,
|
||||||
|
/// Exit external loop.
|
||||||
|
Exit(i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Additional methods on [`EventLoop`] for pumping events within an external event loop
|
||||||
|
pub trait EventLoopExtPumpEvents {
|
||||||
|
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||||
|
type UserEvent;
|
||||||
|
|
||||||
|
/// Pump the `EventLoop` to check for and dispatch pending events.
|
||||||
|
///
|
||||||
|
/// This API is designed to enable applications to integrate Winit into an
|
||||||
|
/// external event loop, for platforms that can support this.
|
||||||
|
///
|
||||||
|
/// The given `timeout` limits how long it may block waiting for new events.
|
||||||
|
///
|
||||||
|
/// Passing a `timeout` of `Some(Duration::ZERO)` would ensure your external
|
||||||
|
/// event loop is never blocked but you would likely need to consider how
|
||||||
|
/// to throttle your own external loop.
|
||||||
|
///
|
||||||
|
/// Passing a `timeout` of `None` means that it may wait indefinitely for new
|
||||||
|
/// events before returning control back to the external loop.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # // Copied from examples/window_pump_events.rs
|
||||||
|
/// # #[cfg(any(
|
||||||
|
/// # windows_platform,
|
||||||
|
/// # macos_platform,
|
||||||
|
/// # x11_platform,
|
||||||
|
/// # wayland_platform,
|
||||||
|
/// # android_platform,
|
||||||
|
/// # ))]
|
||||||
|
/// fn main() -> std::process::ExitCode {
|
||||||
|
/// # use std::{process::ExitCode, thread::sleep, time::Duration};
|
||||||
|
/// #
|
||||||
|
/// # use simple_logger::SimpleLogger;
|
||||||
|
/// # use winit::{
|
||||||
|
/// # event::{Event, WindowEvent},
|
||||||
|
/// # event_loop::EventLoop,
|
||||||
|
/// # platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
|
||||||
|
/// # window::WindowBuilder,
|
||||||
|
/// # };
|
||||||
|
/// let mut event_loop = EventLoop::new().unwrap();
|
||||||
|
/// #
|
||||||
|
/// # SimpleLogger::new().init().unwrap();
|
||||||
|
/// let window = WindowBuilder::new()
|
||||||
|
/// .with_title("A fantastic window!")
|
||||||
|
/// .build(&event_loop)
|
||||||
|
/// .unwrap();
|
||||||
|
///
|
||||||
|
/// 'main: loop {
|
||||||
|
/// let timeout = Some(Duration::ZERO);
|
||||||
|
/// let status = event_loop.pump_events(timeout, |event, _, control_flow| {
|
||||||
|
/// # if let Event::WindowEvent { event, .. } = &event {
|
||||||
|
/// # // Print only Window events to reduce noise
|
||||||
|
/// # println!("{event:?}");
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// match event {
|
||||||
|
/// Event::WindowEvent {
|
||||||
|
/// event: WindowEvent::CloseRequested,
|
||||||
|
/// window_id,
|
||||||
|
/// } if window_id == window.id() => control_flow.set_exit(),
|
||||||
|
/// Event::AboutToWait => {
|
||||||
|
/// window.request_redraw();
|
||||||
|
/// }
|
||||||
|
/// _ => (),
|
||||||
|
/// }
|
||||||
|
/// });
|
||||||
|
/// if let PumpStatus::Exit(exit_code) = status {
|
||||||
|
/// break 'main ExitCode::from(exit_code as u8);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Sleep for 1/60 second to simulate application work
|
||||||
|
/// //
|
||||||
|
/// // Since `pump_events` doesn't block it will be important to
|
||||||
|
/// // throttle the loop in the app somehow.
|
||||||
|
/// println!("Update()");
|
||||||
|
/// sleep(Duration::from_millis(16));
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// **Note:** This is not a portable API, and its usage involves a number of
|
||||||
|
/// caveats and trade offs that should be considered before using this API!
|
||||||
|
///
|
||||||
|
/// You almost certainly shouldn't use this API, unless you absolutely know it's
|
||||||
|
/// the only practical option you have.
|
||||||
|
///
|
||||||
|
/// ## Synchronous events
|
||||||
|
///
|
||||||
|
/// Some events _must_ only be handled synchronously via the closure that
|
||||||
|
/// is passed to Winit so that the handler will also be synchronized with
|
||||||
|
/// the window system and operating system.
|
||||||
|
///
|
||||||
|
/// This is because some events are driven by a window system callback
|
||||||
|
/// where the window systems expects the application to have handled the
|
||||||
|
/// event before returning.
|
||||||
|
///
|
||||||
|
/// **These events can not be buffered and handled outside of the closure
|
||||||
|
/// passed to Winit.**
|
||||||
|
///
|
||||||
|
/// As a general rule it is not recommended to ever buffer events to handle
|
||||||
|
/// them outside of the closure passed to Winit since it's difficult to
|
||||||
|
/// provide guarantees about which events are safe to buffer across all
|
||||||
|
/// operating systems.
|
||||||
|
///
|
||||||
|
/// Notable events that will certainly create portability problems if
|
||||||
|
/// buffered and handled outside of Winit include:
|
||||||
|
/// - `RedrawRequested` events, used to schedule rendering.
|
||||||
|
///
|
||||||
|
/// macOS for example uses a `drawRect` callback to drive rendering
|
||||||
|
/// within applications and expects rendering to be finished before
|
||||||
|
/// the `drawRect` callback returns.
|
||||||
|
///
|
||||||
|
/// For portability it's strongly recommended that applications should
|
||||||
|
/// keep their rendering inside the closure provided to Winit.
|
||||||
|
/// - Any lifecycle events, such as `Suspended` / `Resumed`.
|
||||||
|
///
|
||||||
|
/// The handling of these events needs to be synchronized with the
|
||||||
|
/// operating system and it would never be appropriate to buffer a
|
||||||
|
/// notification that your application has been suspended or resumed and
|
||||||
|
/// then handled that later since there would always be a chance that
|
||||||
|
/// other lifecycle events occur while the event is buffered.
|
||||||
|
///
|
||||||
|
/// ## Supported Platforms
|
||||||
|
/// - Windows
|
||||||
|
/// - Linux
|
||||||
|
/// - MacOS
|
||||||
|
/// - Android
|
||||||
|
///
|
||||||
|
/// ## Unsupported Platforms
|
||||||
|
/// - **Web:** This API is fundamentally incompatible with the event-based way in which
|
||||||
|
/// Web browsers work because it's not possible to have a long-running external
|
||||||
|
/// loop that would block the browser and there is nothing that can be
|
||||||
|
/// polled to ask for new new events. Events are delivered via callbacks based
|
||||||
|
/// on an event loop that is internal to the browser itself.
|
||||||
|
/// - **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.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
/// - **Windows**: The implementation will use `PeekMessage` when checking for
|
||||||
|
/// window messages to avoid blocking your external event loop.
|
||||||
|
///
|
||||||
|
/// - **MacOS**: The implementation works in terms of stopping the global `NSApp`
|
||||||
|
/// whenever the application `RunLoop` indicates that it is preparing to block
|
||||||
|
/// and wait for new events.
|
||||||
|
///
|
||||||
|
/// This is very different to the polling APIs that are available on other
|
||||||
|
/// platforms (the lower level polling primitives on MacOS are private
|
||||||
|
/// implementation details for `NSApp` which aren't accessible to application
|
||||||
|
/// developers)
|
||||||
|
///
|
||||||
|
/// It's likely this will be less efficient than polling on other OSs and
|
||||||
|
/// it also means the `NSApp` is stopped while outside of the Winit
|
||||||
|
/// event loop - and that's observable (for example to crates like `rfd`)
|
||||||
|
/// because the `NSApp` is global state.
|
||||||
|
///
|
||||||
|
/// If you render outside of Winit you are likely to see window resizing artifacts
|
||||||
|
/// since MacOS expects applications to render synchronously during any `drawRect`
|
||||||
|
/// callback.
|
||||||
|
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
|
||||||
|
where
|
||||||
|
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EventLoopExtPumpEvents for EventLoop<T> {
|
||||||
|
type UserEvent = T;
|
||||||
|
|
||||||
|
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
|
||||||
|
where
|
||||||
|
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
self.event_loop.pump_events(timeout, event_handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/platform/run_ondemand.rs
Normal file
74
src/platform/run_ondemand.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use crate::{
|
||||||
|
error::EventLoopError,
|
||||||
|
event::Event,
|
||||||
|
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(doc)]
|
||||||
|
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
|
||||||
|
|
||||||
|
/// Additional methods on [`EventLoop`] to return control flow to the caller.
|
||||||
|
pub trait EventLoopExtRunOnDemand {
|
||||||
|
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||||
|
type UserEvent;
|
||||||
|
|
||||||
|
/// Runs the event loop in the calling thread and calls the given `event_handler` closure
|
||||||
|
/// to dispatch any window system events.
|
||||||
|
///
|
||||||
|
/// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures
|
||||||
|
/// and it is possible to return control back to the caller without
|
||||||
|
/// consuming the `EventLoop` (by setting the `control_flow` to [`ControlFlow::Exit`]) and
|
||||||
|
/// so the event loop can be re-run after it has exit.
|
||||||
|
///
|
||||||
|
/// It's expected that each run of the loop will be for orthogonal instantiations of your
|
||||||
|
/// Winit application, but internally each instantiation may re-use some common window
|
||||||
|
/// system resources, such as a display server connection.
|
||||||
|
///
|
||||||
|
/// This API is not designed to run an event loop in bursts that you can exit from and return
|
||||||
|
/// to while maintaining the full state of your application. (If you need something like this
|
||||||
|
/// you can look at the [`EventLoopExtPumpEvents::pump_events()`] API)
|
||||||
|
///
|
||||||
|
/// Each time `run_ondemand` is called the `event_handler` can expect to receive a
|
||||||
|
/// `NewEvents(Init)` and `Resumed` event (even on platforms that have no suspend/resume
|
||||||
|
/// lifecycle) - which can be used to consistently initialize application state.
|
||||||
|
///
|
||||||
|
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the
|
||||||
|
/// event loop's behavior.
|
||||||
|
///
|
||||||
|
/// # Caveats
|
||||||
|
/// - This extension isn't available on all platforms, since it's not always possible to
|
||||||
|
/// return to the caller (specifically this is impossible on iOS and Web - though with
|
||||||
|
/// the Web backend it is possible to use `spawn()` more than once instead).
|
||||||
|
/// - No [`Window`] state can be carried between separate runs of the event loop.
|
||||||
|
///
|
||||||
|
/// You are strongly encouraged to use [`EventLoop::run()`] for portability, unless you specifically need
|
||||||
|
/// the ability to re-run a single event loop more than once
|
||||||
|
///
|
||||||
|
/// # Supported Platforms
|
||||||
|
/// - Windows
|
||||||
|
/// - Linux
|
||||||
|
/// - macOS
|
||||||
|
/// - Android
|
||||||
|
///
|
||||||
|
/// # Unsupported Platforms
|
||||||
|
/// - **Web:** This API is fundamentally incompatible with the event-based way in which
|
||||||
|
/// Web browsers work because it's not possible to have a long-running external
|
||||||
|
/// loop that would block the browser and there is nothing that can be
|
||||||
|
/// polled to ask for new events. Events are delivered via callbacks based
|
||||||
|
/// on an event loop that is internal to the browser itself.
|
||||||
|
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS.
|
||||||
|
fn run_ondemand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||||
|
where
|
||||||
|
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
|
||||||
|
type UserEvent = T;
|
||||||
|
|
||||||
|
fn run_ondemand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||||
|
where
|
||||||
|
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
self.event_loop.run_ondemand(event_handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
event::Event,
|
|
||||||
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Additional methods on [`EventLoop`] to return control flow to the caller.
|
|
||||||
pub trait EventLoopExtRunReturn {
|
|
||||||
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
|
||||||
type UserEvent;
|
|
||||||
|
|
||||||
/// Initializes the `winit` event loop.
|
|
||||||
///
|
|
||||||
/// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures
|
|
||||||
/// and returns control flow to the caller when `control_flow` is set to [`ControlFlow::Exit`].
|
|
||||||
///
|
|
||||||
/// # Caveats
|
|
||||||
///
|
|
||||||
/// Despite its appearance at first glance, this is *not* a perfect replacement for
|
|
||||||
/// `poll_events`. For example, this function will not return on Windows or macOS while a
|
|
||||||
/// window is getting resized, resulting in all application logic outside of the
|
|
||||||
/// `event_handler` closure not running until the resize operation ends. Other OS operations
|
|
||||||
/// may also result in such freezes. This behavior is caused by fundamental limitations in the
|
|
||||||
/// underlying OS APIs, which cannot be hidden by `winit` without severe stability repercussions.
|
|
||||||
///
|
|
||||||
/// You are strongly encouraged to use `run`, unless the use of this is absolutely necessary.
|
|
||||||
///
|
|
||||||
/// ## Platform-specific
|
|
||||||
///
|
|
||||||
/// - **X11 / Wayland:** This function returns `1` upon disconnection from
|
|
||||||
/// the display server.
|
|
||||||
fn run_return<F>(&mut self, event_handler: F) -> i32
|
|
||||||
where
|
|
||||||
F: FnMut(
|
|
||||||
Event<'_, Self::UserEvent>,
|
|
||||||
&EventLoopWindowTarget<Self::UserEvent>,
|
|
||||||
&mut ControlFlow,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> EventLoopExtRunReturn for EventLoop<T> {
|
|
||||||
type UserEvent = T;
|
|
||||||
|
|
||||||
fn run_return<F>(&mut self, event_handler: F) -> i32
|
|
||||||
where
|
|
||||||
F: FnMut(
|
|
||||||
Event<'_, Self::UserEvent>,
|
|
||||||
&EventLoopWindowTarget<Self::UserEvent>,
|
|
||||||
&mut ControlFlow,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
self.event_loop.run_return(event_handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
109
src/platform/startup_notify.rs
Normal file
109
src/platform/startup_notify.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//! Window startup notification to handle window raising.
|
||||||
|
//!
|
||||||
|
//! The [`ActivationToken`] is essential to ensure that your newly
|
||||||
|
//! created window will obtain the focus, otherwise the user could
|
||||||
|
//! be requered to click on the window.
|
||||||
|
//!
|
||||||
|
//! Such token is usually delivered via the environment variable and
|
||||||
|
//! could be read from it with the [`EventLoopExtStartupNotify::read_token_from_env`].
|
||||||
|
//!
|
||||||
|
//! Such token must also be reset after reading it from your environment with
|
||||||
|
//! [`reset_activation_token_env`] otherwise child processes could inherit it.
|
||||||
|
//!
|
||||||
|
//! When starting a new child process with a newly obtained [`ActivationToken`] from
|
||||||
|
//! [`WindowExtStartupNotify::request_activation_token`] the [`set_activation_token_env`]
|
||||||
|
//! must be used to propagate it to the child
|
||||||
|
//!
|
||||||
|
//! To ensure the delivery of such token by other processes to you, the user should
|
||||||
|
//! set `StartupNotify=true` inside the `.desktop` file of their application.
|
||||||
|
//!
|
||||||
|
//! The specification could be found [`here`].
|
||||||
|
//!
|
||||||
|
//! [`here`]: https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use crate::error::NotSupportedError;
|
||||||
|
use crate::event_loop::{AsyncRequestSerial, EventLoopWindowTarget};
|
||||||
|
use crate::window::{ActivationToken, Window, WindowBuilder};
|
||||||
|
|
||||||
|
/// The variable which is used mostly on X11.
|
||||||
|
const X11_VAR: &str = "DESKTOP_STARTUP_ID";
|
||||||
|
|
||||||
|
/// The variable which is used mostly on Wayland.
|
||||||
|
const WAYLAND_VAR: &str = "XDG_ACTIVATION_TOKEN";
|
||||||
|
|
||||||
|
pub trait EventLoopExtStartupNotify {
|
||||||
|
/// Read the token from the environment.
|
||||||
|
///
|
||||||
|
/// It's recommended **to unset** this environment variable for child processes.
|
||||||
|
fn read_token_from_env(&self) -> Option<ActivationToken>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WindowExtStartupNotify {
|
||||||
|
/// Request a new activation token.
|
||||||
|
///
|
||||||
|
/// The token will be delivered inside
|
||||||
|
fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WindowBuilderExtStartupNotify {
|
||||||
|
/// Use this [`ActivationToken`] during window creation.
|
||||||
|
///
|
||||||
|
/// Not using such a token upon a window could make your window not gaining
|
||||||
|
/// focus until the user clicks on the window.
|
||||||
|
fn with_activation_token(self, token: ActivationToken) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EventLoopExtStartupNotify for EventLoopWindowTarget<T> {
|
||||||
|
fn read_token_from_env(&self) -> Option<ActivationToken> {
|
||||||
|
match self.p {
|
||||||
|
#[cfg(wayland_platform)]
|
||||||
|
crate::platform_impl::EventLoopWindowTarget::Wayland(_) => env::var(WAYLAND_VAR),
|
||||||
|
#[cfg(x11_platform)]
|
||||||
|
crate::platform_impl::EventLoopWindowTarget::X(_) => env::var(X11_VAR),
|
||||||
|
}
|
||||||
|
.ok()
|
||||||
|
.map(ActivationToken::_new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowExtStartupNotify for Window {
|
||||||
|
fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||||
|
self.window.request_activation_token()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowBuilderExtStartupNotify for WindowBuilder {
|
||||||
|
fn with_activation_token(mut self, token: ActivationToken) -> Self {
|
||||||
|
self.platform_specific.activation_token = Some(token);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove the activation environment variables from the current process.
|
||||||
|
///
|
||||||
|
/// This is wise to do before running child processes,
|
||||||
|
/// which may not to support the activation token.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// While the function is safe internally, it mutates the global environment
|
||||||
|
/// state for the process, hence unsafe.
|
||||||
|
pub unsafe fn reset_activation_token_env() {
|
||||||
|
env::remove_var(X11_VAR);
|
||||||
|
env::remove_var(WAYLAND_VAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set environment variables responsible for activation token.
|
||||||
|
///
|
||||||
|
/// This could be used before running daemon processes.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// While the function is safe internally, it mutates the global environment
|
||||||
|
/// state for the process, hence unsafe.
|
||||||
|
pub unsafe fn set_activation_token_env(token: ActivationToken) {
|
||||||
|
env::set_var(X11_VAR, &token._token);
|
||||||
|
env::set_var(WAYLAND_VAR, token._token);
|
||||||
|
}
|
||||||
@@ -1,17 +1,10 @@
|
|||||||
use std::os::raw;
|
|
||||||
|
|
||||||
use sctk::reexports::client::Proxy;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
|
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
|
||||||
monitor::MonitorHandle,
|
monitor::MonitorHandle,
|
||||||
window::{Window, WindowBuilder},
|
window::{Window, WindowBuilder},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::platform_impl::{
|
use crate::platform_impl::{ApplicationName, Backend};
|
||||||
ApplicationName, Backend, EventLoopWindowTarget as LinuxEventLoopWindowTarget,
|
|
||||||
Window as LinuxWindow,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use crate::window::Theme;
|
pub use crate::window::Theme;
|
||||||
|
|
||||||
@@ -19,16 +12,6 @@ pub use crate::window::Theme;
|
|||||||
pub trait EventLoopWindowTargetExtWayland {
|
pub trait EventLoopWindowTargetExtWayland {
|
||||||
/// True if the [`EventLoopWindowTarget`] uses Wayland.
|
/// True if the [`EventLoopWindowTarget`] uses Wayland.
|
||||||
fn is_wayland(&self) -> bool;
|
fn is_wayland(&self) -> bool;
|
||||||
|
|
||||||
/// Returns a pointer to the `wl_display` object of wayland that is used by this
|
|
||||||
/// [`EventLoopWindowTarget`].
|
|
||||||
///
|
|
||||||
/// Returns `None` if the [`EventLoop`] doesn't use wayland (if it uses xlib for example).
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the winit [`EventLoop`] is destroyed.
|
|
||||||
///
|
|
||||||
/// [`EventLoop`]: crate::event_loop::EventLoop
|
|
||||||
fn wayland_display(&self) -> Option<*mut raw::c_void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> EventLoopWindowTargetExtWayland for EventLoopWindowTarget<T> {
|
impl<T> EventLoopWindowTargetExtWayland for EventLoopWindowTarget<T> {
|
||||||
@@ -36,17 +19,6 @@ impl<T> EventLoopWindowTargetExtWayland for EventLoopWindowTarget<T> {
|
|||||||
fn is_wayland(&self) -> bool {
|
fn is_wayland(&self) -> bool {
|
||||||
self.p.is_wayland()
|
self.p.is_wayland()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
|
||||||
match self.p {
|
|
||||||
LinuxEventLoopWindowTarget::Wayland(ref p) => {
|
|
||||||
Some(p.connection.display().id().as_ptr() as *mut _)
|
|
||||||
}
|
|
||||||
#[cfg(x11_platform)]
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Additional methods on [`EventLoopBuilder`] that are specific to Wayland.
|
/// Additional methods on [`EventLoopBuilder`] that are specific to Wayland.
|
||||||
@@ -76,41 +48,9 @@ impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Additional methods on [`Window`] that are specific to Wayland.
|
/// Additional methods on [`Window`] that are specific to Wayland.
|
||||||
pub trait WindowExtWayland {
|
pub trait WindowExtWayland {}
|
||||||
/// Returns a pointer to the `wl_surface` object of wayland that is used by this window.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
|
||||||
fn wayland_surface(&self) -> Option<*mut raw::c_void>;
|
|
||||||
|
|
||||||
/// Returns a pointer to the `wl_display` object of wayland that is used by this window.
|
impl WindowExtWayland for Window {}
|
||||||
///
|
|
||||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
|
||||||
fn wayland_display(&self) -> Option<*mut raw::c_void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowExtWayland for Window {
|
|
||||||
#[inline]
|
|
||||||
fn wayland_surface(&self) -> Option<*mut raw::c_void> {
|
|
||||||
match self.window {
|
|
||||||
LinuxWindow::Wayland(ref w) => Some(w.surface().id().as_ptr() as *mut _),
|
|
||||||
#[cfg(x11_platform)]
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
|
||||||
match self.window {
|
|
||||||
LinuxWindow::Wayland(ref w) => Some(w.display().id().as_ptr() as *mut _),
|
|
||||||
#[cfg(x11_platform)]
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Additional methods on [`WindowBuilder`] that are specific to Wayland.
|
/// Additional methods on [`WindowBuilder`] that are specific to Wayland.
|
||||||
pub trait WindowBuilderExtWayland {
|
pub trait WindowBuilderExtWayland {
|
||||||
|
|||||||
@@ -2,24 +2,58 @@
|
|||||||
//! allow end users to determine how the page should be laid out. Use the [`WindowExtWebSys`] trait
|
//! allow end users to determine how the page should be laid out. Use the [`WindowExtWebSys`] trait
|
||||||
//! to retrieve the canvas from the Window. Alternatively, use the [`WindowBuilderExtWebSys`] trait
|
//! to retrieve the canvas from the Window. Alternatively, use the [`WindowBuilderExtWebSys`] trait
|
||||||
//! to provide your own canvas.
|
//! to provide your own canvas.
|
||||||
|
//!
|
||||||
|
//! It is recommended **not** to apply certain CSS properties to the canvas:
|
||||||
|
//! - [`transform`]
|
||||||
|
//! - [`border`]
|
||||||
|
//! - [`padding`]
|
||||||
|
//!
|
||||||
|
//! The following APIs can't take them into account and will therefore provide inaccurate results:
|
||||||
|
//! - [`WindowEvent::Resized`] and [`Window::(set_)inner_size()`]
|
||||||
|
//! - [`WindowEvent::Occluded`]
|
||||||
|
//! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`],
|
||||||
|
//! and [`WindowEvent::Touch`].
|
||||||
|
//! - [`Window::set_outer_position()`]
|
||||||
|
//!
|
||||||
|
//! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
|
||||||
|
//! [`Window::(set_)inner_size()`]: crate::window::Window::inner_size()
|
||||||
|
//! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded
|
||||||
|
//! [`WindowEvent::CursorMoved`]: crate::event::WindowEvent::CursorMoved
|
||||||
|
//! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered
|
||||||
|
//! [`WindowEvent::CursorLeft`]: crate::event::WindowEvent::CursorLeft
|
||||||
|
//! [`WindowEvent::Touch`]: crate::event::WindowEvent::Touch
|
||||||
|
//! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position()
|
||||||
|
//! [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||||
|
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||||
|
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||||
|
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::event_loop::ControlFlow;
|
use crate::event_loop::ControlFlow;
|
||||||
use crate::event_loop::EventLoop;
|
use crate::event_loop::EventLoop;
|
||||||
use crate::event_loop::EventLoopWindowTarget;
|
use crate::event_loop::EventLoopWindowTarget;
|
||||||
use crate::window::WindowBuilder;
|
use crate::window::{Window, WindowBuilder};
|
||||||
|
|
||||||
use web_sys::HtmlCanvasElement;
|
use web_sys::HtmlCanvasElement;
|
||||||
|
|
||||||
pub trait WindowExtWebSys {
|
pub trait WindowExtWebSys {
|
||||||
/// Only returns the canvas if called from inside the window.
|
/// Only returns the canvas if called from inside the window.
|
||||||
fn canvas(&self) -> Option<HtmlCanvasElement>;
|
fn canvas(&self) -> Option<HtmlCanvasElement>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the browser reports the preferred color scheme to be "dark".
|
impl WindowExtWebSys for Window {
|
||||||
fn is_dark_mode(&self) -> bool;
|
#[inline]
|
||||||
|
fn canvas(&self) -> Option<HtmlCanvasElement> {
|
||||||
|
self.window.canvas()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WindowBuilderExtWebSys {
|
pub trait WindowBuilderExtWebSys {
|
||||||
|
/// Pass an [`HtmlCanvasElement`] to be used for this [`Window`](crate::window::Window). If
|
||||||
|
/// [`None`], [`WindowBuilder::build()`] will create one.
|
||||||
|
///
|
||||||
|
/// In any case, the canvas won't be automatically inserted into the web page.
|
||||||
|
///
|
||||||
|
/// [`None`] by default.
|
||||||
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
|
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
|
||||||
|
|
||||||
/// Whether `event.preventDefault` should be automatically called to prevent event propagation
|
/// Whether `event.preventDefault` should be automatically called to prevent event propagation
|
||||||
@@ -30,11 +64,20 @@ pub trait WindowBuilderExtWebSys {
|
|||||||
///
|
///
|
||||||
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
|
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
|
||||||
/// context menu with Shift+Rightclick.
|
/// context menu with Shift+Rightclick.
|
||||||
|
///
|
||||||
|
/// Enabled by default.
|
||||||
fn with_prevent_default(self, prevent_default: bool) -> Self;
|
fn with_prevent_default(self, prevent_default: bool) -> Self;
|
||||||
|
|
||||||
/// Whether the canvas should be focusable using the tab key. This is necessary to capture
|
/// Whether the canvas should be focusable using the tab key. This is necessary to capture
|
||||||
/// canvas keyboard events.
|
/// canvas keyboard events.
|
||||||
|
///
|
||||||
|
/// Enabled by default.
|
||||||
fn with_focusable(self, focusable: bool) -> Self;
|
fn with_focusable(self, focusable: bool) -> Self;
|
||||||
|
|
||||||
|
/// On window creation, append the canvas element to the web page if it isn't already.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
fn with_append(self, append: bool) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowBuilderExtWebSys for WindowBuilder {
|
impl WindowBuilderExtWebSys for WindowBuilder {
|
||||||
@@ -55,6 +98,12 @@ impl WindowBuilderExtWebSys for WindowBuilder {
|
|||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_append(mut self, append: bool) -> Self {
|
||||||
|
self.platform_specific.append = append;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Additional methods on `EventLoop` that are specific to the web.
|
/// Additional methods on `EventLoop` that are specific to the web.
|
||||||
@@ -74,11 +123,7 @@ pub trait EventLoopExtWebSys {
|
|||||||
fn spawn<F>(self, event_handler: F)
|
fn spawn<F>(self, event_handler: F)
|
||||||
where
|
where
|
||||||
F: 'static
|
F: 'static
|
||||||
+ FnMut(
|
+ FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow);
|
||||||
Event<'_, Self::UserEvent>,
|
|
||||||
&EventLoopWindowTarget<Self::UserEvent>,
|
|
||||||
&mut ControlFlow,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
||||||
@@ -87,11 +132,7 @@ impl<T> EventLoopExtWebSys for EventLoop<T> {
|
|||||||
fn spawn<F>(self, event_handler: F)
|
fn spawn<F>(self, event_handler: F)
|
||||||
where
|
where
|
||||||
F: 'static
|
F: 'static
|
||||||
+ FnMut(
|
+ FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow),
|
||||||
Event<'_, Self::UserEvent>,
|
|
||||||
&EventLoopWindowTarget<Self::UserEvent>,
|
|
||||||
&mut ControlFlow,
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
self.event_loop.spawn(event_handler)
|
self.event_loop.spawn(event_handler)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ pub type HWND = isize;
|
|||||||
pub type HMENU = isize;
|
pub type HMENU = isize;
|
||||||
/// Monitor Handle type used by Win32 API
|
/// Monitor Handle type used by Win32 API
|
||||||
pub type HMONITOR = isize;
|
pub type HMONITOR = isize;
|
||||||
/// Instance Handle type used by Win32 API
|
|
||||||
pub type HINSTANCE = isize;
|
|
||||||
|
|
||||||
/// Additional methods on `EventLoop` that are specific to Windows.
|
/// Additional methods on `EventLoop` that are specific to Windows.
|
||||||
pub trait EventLoopBuilderExtWindows {
|
pub trait EventLoopBuilderExtWindows {
|
||||||
@@ -113,13 +111,6 @@ impl<T> EventLoopBuilderExtWindows for EventLoopBuilder<T> {
|
|||||||
|
|
||||||
/// Additional methods on `Window` that are specific to Windows.
|
/// Additional methods on `Window` that are specific to Windows.
|
||||||
pub trait WindowExtWindows {
|
pub trait WindowExtWindows {
|
||||||
/// Returns the HINSTANCE of the window
|
|
||||||
fn hinstance(&self) -> HINSTANCE;
|
|
||||||
/// Returns the native handle that is used by this window.
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the native window was destroyed.
|
|
||||||
fn hwnd(&self) -> HWND;
|
|
||||||
|
|
||||||
/// Enables or disables mouse and keyboard input to the specified window.
|
/// Enables or disables mouse and keyboard input to the specified window.
|
||||||
///
|
///
|
||||||
/// A window must be enabled before it can be activated.
|
/// A window must be enabled before it can be activated.
|
||||||
@@ -148,16 +139,6 @@ pub trait WindowExtWindows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WindowExtWindows for Window {
|
impl WindowExtWindows for Window {
|
||||||
#[inline]
|
|
||||||
fn hinstance(&self) -> HINSTANCE {
|
|
||||||
self.window.hinstance()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn hwnd(&self) -> HWND {
|
|
||||||
self.window.hwnd()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_enable(&self, enabled: bool) {
|
fn set_enable(&self, enabled: bool) {
|
||||||
self.window.set_enable(enabled)
|
self.window.set_enable(enabled)
|
||||||
@@ -223,6 +204,9 @@ pub trait WindowBuilderExtWindows {
|
|||||||
/// Whether show or hide the window icon in the taskbar.
|
/// Whether show or hide the window icon in the taskbar.
|
||||||
fn with_skip_taskbar(self, skip: bool) -> WindowBuilder;
|
fn with_skip_taskbar(self, skip: bool) -> WindowBuilder;
|
||||||
|
|
||||||
|
/// Customize the window class name.
|
||||||
|
fn with_class_name<S: Into<String>>(self, class_name: S) -> WindowBuilder;
|
||||||
|
|
||||||
/// Shows or hides the background drop shadow for undecorated windows.
|
/// Shows or hides the background drop shadow for undecorated windows.
|
||||||
///
|
///
|
||||||
/// The shadow is hidden by default.
|
/// The shadow is hidden by default.
|
||||||
@@ -267,6 +251,12 @@ impl WindowBuilderExtWindows for WindowBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn with_class_name<S: Into<String>>(mut self, class_name: S) -> WindowBuilder {
|
||||||
|
self.platform_specific.class_name = class_name.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_undecorated_shadow(mut self, shadow: bool) -> WindowBuilder {
|
fn with_undecorated_shadow(mut self, shadow: bool) -> WindowBuilder {
|
||||||
self.platform_specific.decoration_shadow = shadow;
|
self.platform_specific.decoration_shadow = shadow;
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
use std::os::raw;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
|
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
|
||||||
monitor::MonitorHandle,
|
monitor::MonitorHandle,
|
||||||
@@ -8,9 +5,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::dpi::Size;
|
use crate::dpi::Size;
|
||||||
use crate::platform_impl::{
|
use crate::platform_impl::{ApplicationName, Backend, XLIB_ERROR_HOOKS};
|
||||||
x11::ffi::XVisualInfo, ApplicationName, Backend, Window as LinuxWindow, XLIB_ERROR_HOOKS,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported};
|
pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported};
|
||||||
|
|
||||||
@@ -22,6 +17,12 @@ pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupport
|
|||||||
pub type XlibErrorHook =
|
pub type XlibErrorHook =
|
||||||
Box<dyn Fn(*mut std::ffi::c_void, *mut std::ffi::c_void) -> bool + Send + Sync>;
|
Box<dyn Fn(*mut std::ffi::c_void, *mut std::ffi::c_void) -> bool + Send + Sync>;
|
||||||
|
|
||||||
|
/// A unique identifer for an X11 visual.
|
||||||
|
pub type XVisualID = u32;
|
||||||
|
|
||||||
|
/// A unique identifier for an X11 window.
|
||||||
|
pub type XWindow = u32;
|
||||||
|
|
||||||
/// Hook to winit's xlib error handling callback.
|
/// Hook to winit's xlib error handling callback.
|
||||||
///
|
///
|
||||||
/// This method is provided as a safe way to handle the errors comming from X11
|
/// This method is provided as a safe way to handle the errors comming from X11
|
||||||
@@ -81,70 +82,14 @@ impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Additional methods on [`Window`] that are specific to X11.
|
/// Additional methods on [`Window`] that are specific to X11.
|
||||||
pub trait WindowExtX11 {
|
pub trait WindowExtX11 {}
|
||||||
/// Returns the ID of the [`Window`] xlib object that is used by this window.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
|
||||||
fn xlib_window(&self) -> Option<raw::c_ulong>;
|
|
||||||
|
|
||||||
/// Returns a pointer to the `Display` object of xlib that is used by this window.
|
impl WindowExtX11 for Window {}
|
||||||
///
|
|
||||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
|
||||||
fn xlib_display(&self) -> Option<*mut raw::c_void>;
|
|
||||||
|
|
||||||
fn xlib_screen_id(&self) -> Option<raw::c_int>;
|
|
||||||
|
|
||||||
/// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
|
||||||
///
|
|
||||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
|
||||||
fn xcb_connection(&self) -> Option<*mut raw::c_void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowExtX11 for Window {
|
|
||||||
#[inline]
|
|
||||||
fn xlib_window(&self) -> Option<raw::c_ulong> {
|
|
||||||
match self.window {
|
|
||||||
LinuxWindow::X(ref w) => Some(w.xlib_window()),
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn xlib_display(&self) -> Option<*mut raw::c_void> {
|
|
||||||
match self.window {
|
|
||||||
LinuxWindow::X(ref w) => Some(w.xlib_display()),
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn xlib_screen_id(&self) -> Option<raw::c_int> {
|
|
||||||
match self.window {
|
|
||||||
LinuxWindow::X(ref w) => Some(w.xlib_screen_id()),
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn xcb_connection(&self) -> Option<*mut raw::c_void> {
|
|
||||||
match self.window {
|
|
||||||
LinuxWindow::X(ref w) => Some(w.xcb_connection()),
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Additional methods on [`WindowBuilder`] that are specific to X11.
|
/// Additional methods on [`WindowBuilder`] that are specific to X11.
|
||||||
pub trait WindowBuilderExtX11 {
|
pub trait WindowBuilderExtX11 {
|
||||||
fn with_x11_visual<T>(self, visual_infos: *const T) -> Self;
|
/// Create this window with a specific X11 visual.
|
||||||
|
fn with_x11_visual(self, visual_id: XVisualID) -> Self;
|
||||||
|
|
||||||
fn with_x11_screen(self, screen_id: i32) -> Self;
|
fn with_x11_screen(self, screen_id: i32) -> Self;
|
||||||
|
|
||||||
@@ -176,21 +121,35 @@ pub trait WindowBuilderExtX11 {
|
|||||||
/// WindowBuilder::new().with_base_size(PhysicalSize::new(400, 200));
|
/// WindowBuilder::new().with_base_size(PhysicalSize::new(400, 200));
|
||||||
/// ```
|
/// ```
|
||||||
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
|
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
|
||||||
|
|
||||||
|
/// Embed this window into another parent window.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use winit::window::WindowBuilder;
|
||||||
|
/// use winit::platform::x11::{XWindow, WindowBuilderExtX11};
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let event_loop = winit::event_loop::EventLoop::new().unwrap();
|
||||||
|
/// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?;
|
||||||
|
/// let window = WindowBuilder::new()
|
||||||
|
/// .with_embed_parent_window(parent_window_id)
|
||||||
|
/// .build(&event_loop)?;
|
||||||
|
/// # Ok(()) }
|
||||||
|
/// ```
|
||||||
|
fn with_embed_parent_window(self, parent_window_id: XWindow) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowBuilderExtX11 for WindowBuilder {
|
impl WindowBuilderExtX11 for WindowBuilder {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_x11_visual<T>(mut self, visual_infos: *const T) -> Self {
|
fn with_x11_visual(mut self, visual_id: XVisualID) -> Self {
|
||||||
{
|
self.platform_specific.x11.visual_id = Some(visual_id);
|
||||||
self.platform_specific.visual_infos =
|
|
||||||
Some(unsafe { ptr::read(visual_infos as *const XVisualInfo) });
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_x11_screen(mut self, screen_id: i32) -> Self {
|
fn with_x11_screen(mut self, screen_id: i32) -> Self {
|
||||||
self.platform_specific.screen_id = Some(screen_id);
|
self.platform_specific.x11.screen_id = Some(screen_id);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,19 +161,25 @@ impl WindowBuilderExtX11 for WindowBuilder {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_override_redirect(mut self, override_redirect: bool) -> Self {
|
fn with_override_redirect(mut self, override_redirect: bool) -> Self {
|
||||||
self.platform_specific.override_redirect = override_redirect;
|
self.platform_specific.x11.override_redirect = override_redirect;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self {
|
fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self {
|
||||||
self.platform_specific.x11_window_types = x11_window_types;
|
self.platform_specific.x11.x11_window_types = x11_window_types;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
|
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
|
||||||
self.platform_specific.base_size = Some(base_size.into());
|
self.platform_specific.x11.base_size = Some(base_size.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self {
|
||||||
|
self.platform_specific.x11.embed_window = Some(parent_window_id);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use android_activity::input::Keycode;
|
use android_activity::{
|
||||||
|
input::{KeyAction, KeyEvent, KeyMapChar, Keycode},
|
||||||
|
AndroidApp,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKey, NativeKeyCode};
|
use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKey, NativeKeyCode};
|
||||||
|
|
||||||
@@ -156,324 +159,405 @@ pub fn to_physical_keycode(keycode: Keycode) -> KeyCode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We need to expose getUnicodeChar via android-activity instead of having
|
/// Tries to map the `key_event` to a `KeyMapChar` containing a unicode character or dead key accent
|
||||||
// a fixed mapping from key codes
|
///
|
||||||
pub fn to_logical(keycode: Keycode, native: NativeKey) -> Key {
|
/// This takes a `KeyEvent` and looks up its corresponding `KeyCharacterMap` and
|
||||||
|
/// uses that to try and map the `key_code` + `meta_state` to a unicode
|
||||||
|
/// character or a dead key that can be combined with the next key press.
|
||||||
|
pub fn character_map_and_combine_key(
|
||||||
|
app: &AndroidApp,
|
||||||
|
key_event: &KeyEvent<'_>,
|
||||||
|
combining_accent: &mut Option<char>,
|
||||||
|
) -> Option<KeyMapChar> {
|
||||||
|
let device_id = key_event.device_id();
|
||||||
|
|
||||||
|
let key_map = match app.device_key_character_map(device_id) {
|
||||||
|
Ok(key_map) => key_map,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to look up `KeyCharacterMap` for device {device_id}: {err:?}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match key_map.get(key_event.key_code(), key_event.meta_state()) {
|
||||||
|
Ok(KeyMapChar::Unicode(unicode)) => {
|
||||||
|
// Only do dead key combining on key down
|
||||||
|
if key_event.action() == KeyAction::Down {
|
||||||
|
let combined_unicode = if let Some(accent) = combining_accent {
|
||||||
|
match key_map.get_dead_char(*accent, unicode) {
|
||||||
|
Ok(Some(key)) => Some(key),
|
||||||
|
Ok(None) => None,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("KeyEvent: Failed to combine 'dead key' accent '{accent}' with '{unicode}': {err:?}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(unicode)
|
||||||
|
};
|
||||||
|
*combining_accent = None;
|
||||||
|
combined_unicode.map(KeyMapChar::Unicode)
|
||||||
|
} else {
|
||||||
|
Some(KeyMapChar::Unicode(unicode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(KeyMapChar::CombiningAccent(accent)) => {
|
||||||
|
if key_event.action() == KeyAction::Down {
|
||||||
|
*combining_accent = Some(accent);
|
||||||
|
}
|
||||||
|
Some(KeyMapChar::CombiningAccent(accent))
|
||||||
|
}
|
||||||
|
Ok(KeyMapChar::None) => {
|
||||||
|
// Leave any combining_accent state in tact (seems to match how other
|
||||||
|
// Android apps work)
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("KeyEvent: Failed to get key map character: {err:?}");
|
||||||
|
*combining_accent = None;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_logical(key_char: Option<KeyMapChar>, keycode: Keycode) -> Key {
|
||||||
use android_activity::input::Keycode::*;
|
use android_activity::input::Keycode::*;
|
||||||
|
|
||||||
match keycode {
|
let native = NativeKey::Android(keycode.into());
|
||||||
Unknown => Key::Unidentified(native),
|
|
||||||
|
|
||||||
// Can be added on demand
|
match key_char {
|
||||||
SoftLeft => Key::Unidentified(native),
|
Some(KeyMapChar::Unicode(c)) => {
|
||||||
SoftRight => Key::Unidentified(native),
|
Key::Character(smol_str::SmolStr::from_iter([c].into_iter()))
|
||||||
|
}
|
||||||
|
Some(KeyMapChar::CombiningAccent(c)) => Key::Dead(Some(c)),
|
||||||
|
None | Some(KeyMapChar::None) => match keycode {
|
||||||
|
// Using `BrowserHome` instead of `GoHome` according to
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
||||||
|
Home => Key::BrowserHome,
|
||||||
|
Back => Key::BrowserBack,
|
||||||
|
Call => Key::Call,
|
||||||
|
Endcall => Key::EndCall,
|
||||||
|
|
||||||
// Using `BrowserHome` instead of `GoHome` according to
|
//-------------------------------------------------------------------------------
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
// These should be redundant because they should have already been matched
|
||||||
Home => Key::BrowserHome,
|
// as `KeyMapChar::Unicode`, but also matched here as a fallback
|
||||||
Back => Key::BrowserBack,
|
Keycode0 => Key::Character("0".into()),
|
||||||
Call => Key::Call,
|
Keycode1 => Key::Character("1".into()),
|
||||||
Endcall => Key::EndCall,
|
Keycode2 => Key::Character("2".into()),
|
||||||
|
Keycode3 => Key::Character("3".into()),
|
||||||
|
Keycode4 => Key::Character("4".into()),
|
||||||
|
Keycode5 => Key::Character("5".into()),
|
||||||
|
Keycode6 => Key::Character("6".into()),
|
||||||
|
Keycode7 => Key::Character("7".into()),
|
||||||
|
Keycode8 => Key::Character("8".into()),
|
||||||
|
Keycode9 => Key::Character("9".into()),
|
||||||
|
Star => Key::Character("*".into()),
|
||||||
|
Pound => Key::Character("#".into()),
|
||||||
|
A => Key::Character("a".into()),
|
||||||
|
B => Key::Character("b".into()),
|
||||||
|
C => Key::Character("c".into()),
|
||||||
|
D => Key::Character("d".into()),
|
||||||
|
E => Key::Character("e".into()),
|
||||||
|
F => Key::Character("f".into()),
|
||||||
|
G => Key::Character("g".into()),
|
||||||
|
H => Key::Character("h".into()),
|
||||||
|
I => Key::Character("i".into()),
|
||||||
|
J => Key::Character("j".into()),
|
||||||
|
K => Key::Character("k".into()),
|
||||||
|
L => Key::Character("l".into()),
|
||||||
|
M => Key::Character("m".into()),
|
||||||
|
N => Key::Character("n".into()),
|
||||||
|
O => Key::Character("o".into()),
|
||||||
|
P => Key::Character("p".into()),
|
||||||
|
Q => Key::Character("q".into()),
|
||||||
|
R => Key::Character("r".into()),
|
||||||
|
S => Key::Character("s".into()),
|
||||||
|
T => Key::Character("t".into()),
|
||||||
|
U => Key::Character("u".into()),
|
||||||
|
V => Key::Character("v".into()),
|
||||||
|
W => Key::Character("w".into()),
|
||||||
|
X => Key::Character("x".into()),
|
||||||
|
Y => Key::Character("y".into()),
|
||||||
|
Z => Key::Character("z".into()),
|
||||||
|
Comma => Key::Character(",".into()),
|
||||||
|
Period => Key::Character(".".into()),
|
||||||
|
Grave => Key::Character("`".into()),
|
||||||
|
Minus => Key::Character("-".into()),
|
||||||
|
Equals => Key::Character("=".into()),
|
||||||
|
LeftBracket => Key::Character("[".into()),
|
||||||
|
RightBracket => Key::Character("]".into()),
|
||||||
|
Backslash => Key::Character("\\".into()),
|
||||||
|
Semicolon => Key::Character(";".into()),
|
||||||
|
Apostrophe => Key::Character("'".into()),
|
||||||
|
Slash => Key::Character("/".into()),
|
||||||
|
At => Key::Character("@".into()),
|
||||||
|
Plus => Key::Character("+".into()),
|
||||||
|
//-------------------------------------------------------------------------------
|
||||||
|
DpadUp => Key::ArrowUp,
|
||||||
|
DpadDown => Key::ArrowDown,
|
||||||
|
DpadLeft => Key::ArrowLeft,
|
||||||
|
DpadRight => Key::ArrowRight,
|
||||||
|
DpadCenter => Key::Enter,
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------
|
VolumeUp => Key::AudioVolumeUp,
|
||||||
// Reporting unidentified, because the specific character is layout dependent.
|
VolumeDown => Key::AudioVolumeDown,
|
||||||
// (I'm not sure though)
|
Power => Key::Power,
|
||||||
Keycode0 => Key::Character("0".into()),
|
Camera => Key::Camera,
|
||||||
Keycode1 => Key::Character("1".into()),
|
Clear => Key::Clear,
|
||||||
Keycode2 => Key::Character("2".into()),
|
|
||||||
Keycode3 => Key::Character("3".into()),
|
|
||||||
Keycode4 => Key::Character("4".into()),
|
|
||||||
Keycode5 => Key::Character("5".into()),
|
|
||||||
Keycode6 => Key::Character("6".into()),
|
|
||||||
Keycode7 => Key::Character("7".into()),
|
|
||||||
Keycode8 => Key::Character("8".into()),
|
|
||||||
Keycode9 => Key::Character("9".into()),
|
|
||||||
Star => Key::Character("*".into()),
|
|
||||||
Pound => Key::Character("#".into()),
|
|
||||||
A => Key::Character("a".into()),
|
|
||||||
B => Key::Character("b".into()),
|
|
||||||
C => Key::Character("c".into()),
|
|
||||||
D => Key::Character("d".into()),
|
|
||||||
E => Key::Character("e".into()),
|
|
||||||
F => Key::Character("f".into()),
|
|
||||||
G => Key::Character("g".into()),
|
|
||||||
H => Key::Character("h".into()),
|
|
||||||
I => Key::Character("i".into()),
|
|
||||||
J => Key::Character("j".into()),
|
|
||||||
K => Key::Character("k".into()),
|
|
||||||
L => Key::Character("l".into()),
|
|
||||||
M => Key::Character("m".into()),
|
|
||||||
N => Key::Character("n".into()),
|
|
||||||
O => Key::Character("o".into()),
|
|
||||||
P => Key::Character("p".into()),
|
|
||||||
Q => Key::Character("q".into()),
|
|
||||||
R => Key::Character("r".into()),
|
|
||||||
S => Key::Character("s".into()),
|
|
||||||
T => Key::Character("t".into()),
|
|
||||||
U => Key::Character("u".into()),
|
|
||||||
V => Key::Character("v".into()),
|
|
||||||
W => Key::Character("w".into()),
|
|
||||||
X => Key::Character("x".into()),
|
|
||||||
Y => Key::Character("y".into()),
|
|
||||||
Z => Key::Character("z".into()),
|
|
||||||
Comma => Key::Character(",".into()),
|
|
||||||
Period => Key::Character(".".into()),
|
|
||||||
Grave => Key::Character("`".into()),
|
|
||||||
Minus => Key::Character("-".into()),
|
|
||||||
Equals => Key::Character("=".into()),
|
|
||||||
LeftBracket => Key::Character("[".into()),
|
|
||||||
RightBracket => Key::Character("]".into()),
|
|
||||||
Backslash => Key::Character("\\".into()),
|
|
||||||
Semicolon => Key::Character(";".into()),
|
|
||||||
Apostrophe => Key::Character("'".into()),
|
|
||||||
Slash => Key::Character("/".into()),
|
|
||||||
At => Key::Character("@".into()),
|
|
||||||
Plus => Key::Character("+".into()),
|
|
||||||
//-------------------------------------------------------------------------------
|
|
||||||
DpadUp => Key::ArrowUp,
|
|
||||||
DpadDown => Key::ArrowDown,
|
|
||||||
DpadLeft => Key::ArrowLeft,
|
|
||||||
DpadRight => Key::ArrowRight,
|
|
||||||
DpadCenter => Key::Enter,
|
|
||||||
|
|
||||||
VolumeUp => Key::AudioVolumeUp,
|
AltLeft => Key::Alt,
|
||||||
VolumeDown => Key::AudioVolumeDown,
|
AltRight => Key::Alt,
|
||||||
Power => Key::Power,
|
ShiftLeft => Key::Shift,
|
||||||
Camera => Key::Camera,
|
ShiftRight => Key::Shift,
|
||||||
Clear => Key::Clear,
|
Tab => Key::Tab,
|
||||||
|
Space => Key::Space,
|
||||||
|
Sym => Key::Symbol,
|
||||||
|
Explorer => Key::LaunchWebBrowser,
|
||||||
|
Envelope => Key::LaunchMail,
|
||||||
|
Enter => Key::Enter,
|
||||||
|
Del => Key::Backspace,
|
||||||
|
|
||||||
AltLeft => Key::Alt,
|
// According to https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_NUM
|
||||||
AltRight => Key::Alt,
|
Num => Key::Alt,
|
||||||
ShiftLeft => Key::Shift,
|
|
||||||
ShiftRight => Key::Shift,
|
|
||||||
Tab => Key::Tab,
|
|
||||||
Space => Key::Space,
|
|
||||||
Sym => Key::Symbol,
|
|
||||||
Explorer => Key::LaunchWebBrowser,
|
|
||||||
Envelope => Key::LaunchMail,
|
|
||||||
Enter => Key::Enter,
|
|
||||||
Del => Key::Backspace,
|
|
||||||
|
|
||||||
// According to https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_NUM
|
Headsethook => Key::HeadsetHook,
|
||||||
Num => Key::Alt,
|
Focus => Key::CameraFocus,
|
||||||
|
|
||||||
Headsethook => Key::HeadsetHook,
|
Notification => Key::Notification,
|
||||||
Focus => Key::CameraFocus,
|
Search => Key::BrowserSearch,
|
||||||
|
MediaPlayPause => Key::MediaPlayPause,
|
||||||
|
MediaStop => Key::MediaStop,
|
||||||
|
MediaNext => Key::MediaTrackNext,
|
||||||
|
MediaPrevious => Key::MediaTrackPrevious,
|
||||||
|
MediaRewind => Key::MediaRewind,
|
||||||
|
MediaFastForward => Key::MediaFastForward,
|
||||||
|
Mute => Key::MicrophoneVolumeMute,
|
||||||
|
PageUp => Key::PageUp,
|
||||||
|
PageDown => Key::PageDown,
|
||||||
|
|
||||||
Menu => Key::Unidentified(native),
|
Escape => Key::Escape,
|
||||||
|
ForwardDel => Key::Delete,
|
||||||
|
CtrlLeft => Key::Control,
|
||||||
|
CtrlRight => Key::Control,
|
||||||
|
CapsLock => Key::CapsLock,
|
||||||
|
ScrollLock => Key::ScrollLock,
|
||||||
|
MetaLeft => Key::Super,
|
||||||
|
MetaRight => Key::Super,
|
||||||
|
Function => Key::Fn,
|
||||||
|
Sysrq => Key::PrintScreen,
|
||||||
|
Break => Key::Pause,
|
||||||
|
MoveHome => Key::Home,
|
||||||
|
MoveEnd => Key::End,
|
||||||
|
Insert => Key::Insert,
|
||||||
|
Forward => Key::BrowserForward,
|
||||||
|
MediaPlay => Key::MediaPlay,
|
||||||
|
MediaPause => Key::MediaPause,
|
||||||
|
MediaClose => Key::MediaClose,
|
||||||
|
MediaEject => Key::Eject,
|
||||||
|
MediaRecord => Key::MediaRecord,
|
||||||
|
F1 => Key::F1,
|
||||||
|
F2 => Key::F2,
|
||||||
|
F3 => Key::F3,
|
||||||
|
F4 => Key::F4,
|
||||||
|
F5 => Key::F5,
|
||||||
|
F6 => Key::F6,
|
||||||
|
F7 => Key::F7,
|
||||||
|
F8 => Key::F8,
|
||||||
|
F9 => Key::F9,
|
||||||
|
F10 => Key::F10,
|
||||||
|
F11 => Key::F11,
|
||||||
|
F12 => Key::F12,
|
||||||
|
NumLock => Key::NumLock,
|
||||||
|
Numpad0 => Key::Character("0".into()),
|
||||||
|
Numpad1 => Key::Character("1".into()),
|
||||||
|
Numpad2 => Key::Character("2".into()),
|
||||||
|
Numpad3 => Key::Character("3".into()),
|
||||||
|
Numpad4 => Key::Character("4".into()),
|
||||||
|
Numpad5 => Key::Character("5".into()),
|
||||||
|
Numpad6 => Key::Character("6".into()),
|
||||||
|
Numpad7 => Key::Character("7".into()),
|
||||||
|
Numpad8 => Key::Character("8".into()),
|
||||||
|
Numpad9 => Key::Character("9".into()),
|
||||||
|
NumpadDivide => Key::Character("/".into()),
|
||||||
|
NumpadMultiply => Key::Character("*".into()),
|
||||||
|
NumpadSubtract => Key::Character("-".into()),
|
||||||
|
NumpadAdd => Key::Character("+".into()),
|
||||||
|
NumpadDot => Key::Character(".".into()),
|
||||||
|
NumpadComma => Key::Character(",".into()),
|
||||||
|
NumpadEnter => Key::Enter,
|
||||||
|
NumpadEquals => Key::Character("=".into()),
|
||||||
|
NumpadLeftParen => Key::Character("(".into()),
|
||||||
|
NumpadRightParen => Key::Character(")".into()),
|
||||||
|
|
||||||
Notification => Key::Notification,
|
VolumeMute => Key::AudioVolumeMute,
|
||||||
Search => Key::BrowserSearch,
|
Info => Key::Info,
|
||||||
MediaPlayPause => Key::MediaPlayPause,
|
ChannelUp => Key::ChannelUp,
|
||||||
MediaStop => Key::MediaStop,
|
ChannelDown => Key::ChannelDown,
|
||||||
MediaNext => Key::MediaTrackNext,
|
ZoomIn => Key::ZoomIn,
|
||||||
MediaPrevious => Key::MediaTrackPrevious,
|
ZoomOut => Key::ZoomOut,
|
||||||
MediaRewind => Key::MediaRewind,
|
Tv => Key::TV,
|
||||||
MediaFastForward => Key::MediaFastForward,
|
Guide => Key::Guide,
|
||||||
Mute => Key::MicrophoneVolumeMute,
|
Dvr => Key::DVR,
|
||||||
PageUp => Key::PageUp,
|
Bookmark => Key::BrowserFavorites,
|
||||||
PageDown => Key::PageDown,
|
Captions => Key::ClosedCaptionToggle,
|
||||||
Pictsymbols => Key::Unidentified(native),
|
Settings => Key::Settings,
|
||||||
SwitchCharset => Key::Unidentified(native),
|
TvPower => Key::TVPower,
|
||||||
|
TvInput => Key::TVInput,
|
||||||
|
StbPower => Key::STBPower,
|
||||||
|
StbInput => Key::STBInput,
|
||||||
|
AvrPower => Key::AVRPower,
|
||||||
|
AvrInput => Key::AVRInput,
|
||||||
|
ProgRed => Key::ColorF0Red,
|
||||||
|
ProgGreen => Key::ColorF1Green,
|
||||||
|
ProgYellow => Key::ColorF2Yellow,
|
||||||
|
ProgBlue => Key::ColorF3Blue,
|
||||||
|
AppSwitch => Key::AppSwitch,
|
||||||
|
LanguageSwitch => Key::GroupNext,
|
||||||
|
MannerMode => Key::MannerMode,
|
||||||
|
Keycode3dMode => Key::TV3DMode,
|
||||||
|
Contacts => Key::LaunchContacts,
|
||||||
|
Calendar => Key::LaunchCalendar,
|
||||||
|
Music => Key::LaunchMusicPlayer,
|
||||||
|
Calculator => Key::LaunchApplication2,
|
||||||
|
ZenkakuHankaku => Key::ZenkakuHankaku,
|
||||||
|
Eisu => Key::Eisu,
|
||||||
|
Muhenkan => Key::NonConvert,
|
||||||
|
Henkan => Key::Convert,
|
||||||
|
KatakanaHiragana => Key::HiraganaKatakana,
|
||||||
|
Kana => Key::KanjiMode,
|
||||||
|
BrightnessDown => Key::BrightnessDown,
|
||||||
|
BrightnessUp => Key::BrightnessUp,
|
||||||
|
MediaAudioTrack => Key::MediaAudioTrack,
|
||||||
|
Sleep => Key::Standby,
|
||||||
|
Wakeup => Key::WakeUp,
|
||||||
|
Pairing => Key::Pairing,
|
||||||
|
MediaTopMenu => Key::MediaTopMenu,
|
||||||
|
LastChannel => Key::MediaLast,
|
||||||
|
TvDataService => Key::TVDataService,
|
||||||
|
VoiceAssist => Key::VoiceDial,
|
||||||
|
TvRadioService => Key::TVRadioService,
|
||||||
|
TvTeletext => Key::Teletext,
|
||||||
|
TvNumberEntry => Key::TVNumberEntry,
|
||||||
|
TvTerrestrialAnalog => Key::TVTerrestrialAnalog,
|
||||||
|
TvTerrestrialDigital => Key::TVTerrestrialDigital,
|
||||||
|
TvSatellite => Key::TVSatellite,
|
||||||
|
TvSatelliteBs => Key::TVSatelliteBS,
|
||||||
|
TvSatelliteCs => Key::TVSatelliteCS,
|
||||||
|
TvSatelliteService => Key::TVSatelliteToggle,
|
||||||
|
TvNetwork => Key::TVNetwork,
|
||||||
|
TvAntennaCable => Key::TVAntennaCable,
|
||||||
|
TvInputHdmi1 => Key::TVInputHDMI1,
|
||||||
|
TvInputHdmi2 => Key::TVInputHDMI2,
|
||||||
|
TvInputHdmi3 => Key::TVInputHDMI3,
|
||||||
|
TvInputHdmi4 => Key::TVInputHDMI4,
|
||||||
|
TvInputComposite1 => Key::TVInputComposite1,
|
||||||
|
TvInputComposite2 => Key::TVInputComposite2,
|
||||||
|
TvInputComponent1 => Key::TVInputComponent1,
|
||||||
|
TvInputComponent2 => Key::TVInputComponent2,
|
||||||
|
TvInputVga1 => Key::TVInputVGA1,
|
||||||
|
TvAudioDescription => Key::TVAudioDescription,
|
||||||
|
TvAudioDescriptionMixUp => Key::TVAudioDescriptionMixUp,
|
||||||
|
TvAudioDescriptionMixDown => Key::TVAudioDescriptionMixDown,
|
||||||
|
TvZoomMode => Key::ZoomToggle,
|
||||||
|
TvContentsMenu => Key::TVContentsMenu,
|
||||||
|
TvMediaContextMenu => Key::TVMediaContext,
|
||||||
|
TvTimerProgramming => Key::TVTimer,
|
||||||
|
Help => Key::Help,
|
||||||
|
NavigatePrevious => Key::NavigatePrevious,
|
||||||
|
NavigateNext => Key::NavigateNext,
|
||||||
|
NavigateIn => Key::NavigateIn,
|
||||||
|
NavigateOut => Key::NavigateOut,
|
||||||
|
MediaSkipForward => Key::MediaSkipForward,
|
||||||
|
MediaSkipBackward => Key::MediaSkipBackward,
|
||||||
|
MediaStepForward => Key::MediaStepForward,
|
||||||
|
MediaStepBackward => Key::MediaStepBackward,
|
||||||
|
Cut => Key::Cut,
|
||||||
|
Copy => Key::Copy,
|
||||||
|
Paste => Key::Paste,
|
||||||
|
Refresh => Key::BrowserRefresh,
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
// Gamepad events should be exposed through a separate API, not
|
// Keycodes that don't have a logical Key mapping
|
||||||
// keyboard events
|
// -----------------------------------------------------------------
|
||||||
ButtonA => Key::Unidentified(native),
|
Unknown => Key::Unidentified(native),
|
||||||
ButtonB => Key::Unidentified(native),
|
|
||||||
ButtonC => Key::Unidentified(native),
|
|
||||||
ButtonX => Key::Unidentified(native),
|
|
||||||
ButtonY => Key::Unidentified(native),
|
|
||||||
ButtonZ => Key::Unidentified(native),
|
|
||||||
ButtonL1 => Key::Unidentified(native),
|
|
||||||
ButtonR1 => Key::Unidentified(native),
|
|
||||||
ButtonL2 => Key::Unidentified(native),
|
|
||||||
ButtonR2 => Key::Unidentified(native),
|
|
||||||
ButtonThumbl => Key::Unidentified(native),
|
|
||||||
ButtonThumbr => Key::Unidentified(native),
|
|
||||||
ButtonStart => Key::Unidentified(native),
|
|
||||||
ButtonSelect => Key::Unidentified(native),
|
|
||||||
ButtonMode => Key::Unidentified(native),
|
|
||||||
// -----------------------------------------------------------------
|
|
||||||
Escape => Key::Escape,
|
|
||||||
ForwardDel => Key::Delete,
|
|
||||||
CtrlLeft => Key::Control,
|
|
||||||
CtrlRight => Key::Control,
|
|
||||||
CapsLock => Key::CapsLock,
|
|
||||||
ScrollLock => Key::ScrollLock,
|
|
||||||
MetaLeft => Key::Super,
|
|
||||||
MetaRight => Key::Super,
|
|
||||||
Function => Key::Fn,
|
|
||||||
Sysrq => Key::PrintScreen,
|
|
||||||
Break => Key::Pause,
|
|
||||||
MoveHome => Key::Home,
|
|
||||||
MoveEnd => Key::End,
|
|
||||||
Insert => Key::Insert,
|
|
||||||
Forward => Key::BrowserForward,
|
|
||||||
MediaPlay => Key::MediaPlay,
|
|
||||||
MediaPause => Key::MediaPause,
|
|
||||||
MediaClose => Key::MediaClose,
|
|
||||||
MediaEject => Key::Eject,
|
|
||||||
MediaRecord => Key::MediaRecord,
|
|
||||||
F1 => Key::F1,
|
|
||||||
F2 => Key::F2,
|
|
||||||
F3 => Key::F3,
|
|
||||||
F4 => Key::F4,
|
|
||||||
F5 => Key::F5,
|
|
||||||
F6 => Key::F6,
|
|
||||||
F7 => Key::F7,
|
|
||||||
F8 => Key::F8,
|
|
||||||
F9 => Key::F9,
|
|
||||||
F10 => Key::F10,
|
|
||||||
F11 => Key::F11,
|
|
||||||
F12 => Key::F12,
|
|
||||||
NumLock => Key::NumLock,
|
|
||||||
Numpad0 => Key::Character("0".into()),
|
|
||||||
Numpad1 => Key::Character("1".into()),
|
|
||||||
Numpad2 => Key::Character("2".into()),
|
|
||||||
Numpad3 => Key::Character("3".into()),
|
|
||||||
Numpad4 => Key::Character("4".into()),
|
|
||||||
Numpad5 => Key::Character("5".into()),
|
|
||||||
Numpad6 => Key::Character("6".into()),
|
|
||||||
Numpad7 => Key::Character("7".into()),
|
|
||||||
Numpad8 => Key::Character("8".into()),
|
|
||||||
Numpad9 => Key::Character("9".into()),
|
|
||||||
NumpadDivide => Key::Character("/".into()),
|
|
||||||
NumpadMultiply => Key::Character("*".into()),
|
|
||||||
NumpadSubtract => Key::Character("-".into()),
|
|
||||||
NumpadAdd => Key::Character("+".into()),
|
|
||||||
NumpadDot => Key::Character(".".into()),
|
|
||||||
NumpadComma => Key::Character(",".into()),
|
|
||||||
NumpadEnter => Key::Enter,
|
|
||||||
NumpadEquals => Key::Character("=".into()),
|
|
||||||
NumpadLeftParen => Key::Character("(".into()),
|
|
||||||
NumpadRightParen => Key::Character(")".into()),
|
|
||||||
|
|
||||||
VolumeMute => Key::AudioVolumeMute,
|
// Can be added on demand
|
||||||
Info => Key::Info,
|
SoftLeft => Key::Unidentified(native),
|
||||||
ChannelUp => Key::ChannelUp,
|
SoftRight => Key::Unidentified(native),
|
||||||
ChannelDown => Key::ChannelDown,
|
|
||||||
ZoomIn => Key::ZoomIn,
|
Menu => Key::Unidentified(native),
|
||||||
ZoomOut => Key::ZoomOut,
|
|
||||||
Tv => Key::TV,
|
Pictsymbols => Key::Unidentified(native),
|
||||||
Window => Key::Unidentified(native),
|
SwitchCharset => Key::Unidentified(native),
|
||||||
Guide => Key::Guide,
|
|
||||||
Dvr => Key::DVR,
|
// -----------------------------------------------------------------
|
||||||
Bookmark => Key::BrowserFavorites,
|
// Gamepad events should be exposed through a separate API, not
|
||||||
Captions => Key::ClosedCaptionToggle,
|
// keyboard events
|
||||||
Settings => Key::Settings,
|
ButtonA => Key::Unidentified(native),
|
||||||
TvPower => Key::TVPower,
|
ButtonB => Key::Unidentified(native),
|
||||||
TvInput => Key::TVInput,
|
ButtonC => Key::Unidentified(native),
|
||||||
StbPower => Key::STBPower,
|
ButtonX => Key::Unidentified(native),
|
||||||
StbInput => Key::STBInput,
|
ButtonY => Key::Unidentified(native),
|
||||||
AvrPower => Key::AVRPower,
|
ButtonZ => Key::Unidentified(native),
|
||||||
AvrInput => Key::AVRInput,
|
ButtonL1 => Key::Unidentified(native),
|
||||||
ProgRed => Key::ColorF0Red,
|
ButtonR1 => Key::Unidentified(native),
|
||||||
ProgGreen => Key::ColorF1Green,
|
ButtonL2 => Key::Unidentified(native),
|
||||||
ProgYellow => Key::ColorF2Yellow,
|
ButtonR2 => Key::Unidentified(native),
|
||||||
ProgBlue => Key::ColorF3Blue,
|
ButtonThumbl => Key::Unidentified(native),
|
||||||
AppSwitch => Key::AppSwitch,
|
ButtonThumbr => Key::Unidentified(native),
|
||||||
Button1 => Key::Unidentified(native),
|
ButtonStart => Key::Unidentified(native),
|
||||||
Button2 => Key::Unidentified(native),
|
ButtonSelect => Key::Unidentified(native),
|
||||||
Button3 => Key::Unidentified(native),
|
ButtonMode => Key::Unidentified(native),
|
||||||
Button4 => Key::Unidentified(native),
|
// -----------------------------------------------------------------
|
||||||
Button5 => Key::Unidentified(native),
|
Window => Key::Unidentified(native),
|
||||||
Button6 => Key::Unidentified(native),
|
|
||||||
Button7 => Key::Unidentified(native),
|
Button1 => Key::Unidentified(native),
|
||||||
Button8 => Key::Unidentified(native),
|
Button2 => Key::Unidentified(native),
|
||||||
Button9 => Key::Unidentified(native),
|
Button3 => Key::Unidentified(native),
|
||||||
Button10 => Key::Unidentified(native),
|
Button4 => Key::Unidentified(native),
|
||||||
Button11 => Key::Unidentified(native),
|
Button5 => Key::Unidentified(native),
|
||||||
Button12 => Key::Unidentified(native),
|
Button6 => Key::Unidentified(native),
|
||||||
Button13 => Key::Unidentified(native),
|
Button7 => Key::Unidentified(native),
|
||||||
Button14 => Key::Unidentified(native),
|
Button8 => Key::Unidentified(native),
|
||||||
Button15 => Key::Unidentified(native),
|
Button9 => Key::Unidentified(native),
|
||||||
Button16 => Key::Unidentified(native),
|
Button10 => Key::Unidentified(native),
|
||||||
LanguageSwitch => Key::GroupNext,
|
Button11 => Key::Unidentified(native),
|
||||||
MannerMode => Key::MannerMode,
|
Button12 => Key::Unidentified(native),
|
||||||
Keycode3dMode => Key::TV3DMode,
|
Button13 => Key::Unidentified(native),
|
||||||
Contacts => Key::LaunchContacts,
|
Button14 => Key::Unidentified(native),
|
||||||
Calendar => Key::LaunchCalendar,
|
Button15 => Key::Unidentified(native),
|
||||||
Music => Key::LaunchMusicPlayer,
|
Button16 => Key::Unidentified(native),
|
||||||
Calculator => Key::LaunchApplication2,
|
|
||||||
ZenkakuHankaku => Key::ZenkakuHankaku,
|
Yen => Key::Unidentified(native),
|
||||||
Eisu => Key::Eisu,
|
Ro => Key::Unidentified(native),
|
||||||
Muhenkan => Key::NonConvert,
|
|
||||||
Henkan => Key::Convert,
|
Assist => Key::Unidentified(native),
|
||||||
KatakanaHiragana => Key::HiraganaKatakana,
|
|
||||||
Yen => Key::Unidentified(native),
|
Keycode11 => Key::Unidentified(native),
|
||||||
Ro => Key::Unidentified(native),
|
Keycode12 => Key::Unidentified(native),
|
||||||
Kana => Key::KanjiMode,
|
|
||||||
Assist => Key::Unidentified(native),
|
StemPrimary => Key::Unidentified(native),
|
||||||
BrightnessDown => Key::BrightnessDown,
|
Stem1 => Key::Unidentified(native),
|
||||||
BrightnessUp => Key::BrightnessUp,
|
Stem2 => Key::Unidentified(native),
|
||||||
MediaAudioTrack => Key::MediaAudioTrack,
|
Stem3 => Key::Unidentified(native),
|
||||||
Sleep => Key::Standby,
|
|
||||||
Wakeup => Key::WakeUp,
|
DpadUpLeft => Key::Unidentified(native),
|
||||||
Pairing => Key::Pairing,
|
DpadDownLeft => Key::Unidentified(native),
|
||||||
MediaTopMenu => Key::MediaTopMenu,
|
DpadUpRight => Key::Unidentified(native),
|
||||||
Keycode11 => Key::Unidentified(native),
|
DpadDownRight => Key::Unidentified(native),
|
||||||
Keycode12 => Key::Unidentified(native),
|
|
||||||
LastChannel => Key::MediaLast,
|
SoftSleep => Key::Unidentified(native),
|
||||||
TvDataService => Key::TVDataService,
|
|
||||||
VoiceAssist => Key::VoiceDial,
|
SystemNavigationUp => Key::Unidentified(native),
|
||||||
TvRadioService => Key::TVRadioService,
|
SystemNavigationDown => Key::Unidentified(native),
|
||||||
TvTeletext => Key::Teletext,
|
SystemNavigationLeft => Key::Unidentified(native),
|
||||||
TvNumberEntry => Key::TVNumberEntry,
|
SystemNavigationRight => Key::Unidentified(native),
|
||||||
TvTerrestrialAnalog => Key::TVTerrestrialAnalog,
|
|
||||||
TvTerrestrialDigital => Key::TVTerrestrialDigital,
|
AllApps => Key::Unidentified(native),
|
||||||
TvSatellite => Key::TVSatellite,
|
ThumbsUp => Key::Unidentified(native),
|
||||||
TvSatelliteBs => Key::TVSatelliteBS,
|
ThumbsDown => Key::Unidentified(native),
|
||||||
TvSatelliteCs => Key::TVSatelliteCS,
|
ProfileSwitch => Key::Unidentified(native),
|
||||||
TvSatelliteService => Key::TVSatelliteToggle,
|
},
|
||||||
TvNetwork => Key::TVNetwork,
|
|
||||||
TvAntennaCable => Key::TVAntennaCable,
|
|
||||||
TvInputHdmi1 => Key::TVInputHDMI1,
|
|
||||||
TvInputHdmi2 => Key::TVInputHDMI2,
|
|
||||||
TvInputHdmi3 => Key::TVInputHDMI3,
|
|
||||||
TvInputHdmi4 => Key::TVInputHDMI4,
|
|
||||||
TvInputComposite1 => Key::TVInputComposite1,
|
|
||||||
TvInputComposite2 => Key::TVInputComposite2,
|
|
||||||
TvInputComponent1 => Key::TVInputComponent1,
|
|
||||||
TvInputComponent2 => Key::TVInputComponent2,
|
|
||||||
TvInputVga1 => Key::TVInputVGA1,
|
|
||||||
TvAudioDescription => Key::TVAudioDescription,
|
|
||||||
TvAudioDescriptionMixUp => Key::TVAudioDescriptionMixUp,
|
|
||||||
TvAudioDescriptionMixDown => Key::TVAudioDescriptionMixDown,
|
|
||||||
TvZoomMode => Key::ZoomToggle,
|
|
||||||
TvContentsMenu => Key::TVContentsMenu,
|
|
||||||
TvMediaContextMenu => Key::TVMediaContext,
|
|
||||||
TvTimerProgramming => Key::TVTimer,
|
|
||||||
Help => Key::Help,
|
|
||||||
NavigatePrevious => Key::NavigatePrevious,
|
|
||||||
NavigateNext => Key::NavigateNext,
|
|
||||||
NavigateIn => Key::NavigateIn,
|
|
||||||
NavigateOut => Key::NavigateOut,
|
|
||||||
StemPrimary => Key::Unidentified(native),
|
|
||||||
Stem1 => Key::Unidentified(native),
|
|
||||||
Stem2 => Key::Unidentified(native),
|
|
||||||
Stem3 => Key::Unidentified(native),
|
|
||||||
DpadUpLeft => Key::Unidentified(native),
|
|
||||||
DpadDownLeft => Key::Unidentified(native),
|
|
||||||
DpadUpRight => Key::Unidentified(native),
|
|
||||||
DpadDownRight => Key::Unidentified(native),
|
|
||||||
MediaSkipForward => Key::MediaSkipForward,
|
|
||||||
MediaSkipBackward => Key::MediaSkipBackward,
|
|
||||||
MediaStepForward => Key::MediaStepForward,
|
|
||||||
MediaStepBackward => Key::MediaStepBackward,
|
|
||||||
SoftSleep => Key::Unidentified(native),
|
|
||||||
Cut => Key::Cut,
|
|
||||||
Copy => Key::Copy,
|
|
||||||
Paste => Key::Paste,
|
|
||||||
SystemNavigationUp => Key::Unidentified(native),
|
|
||||||
SystemNavigationDown => Key::Unidentified(native),
|
|
||||||
SystemNavigationLeft => Key::Unidentified(native),
|
|
||||||
SystemNavigationRight => Key::Unidentified(native),
|
|
||||||
AllApps => Key::Unidentified(native),
|
|
||||||
Refresh => Key::BrowserRefresh,
|
|
||||||
ThumbsUp => Key::Unidentified(native),
|
|
||||||
ThumbsDown => Key::Unidentified(native),
|
|
||||||
ProfileSwitch => Key::Unidentified(native),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::{
|
|||||||
hash::Hash,
|
hash::Hash,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
mpsc, Arc, RwLock,
|
mpsc, Arc, Mutex, RwLock,
|
||||||
},
|
},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
@@ -19,22 +19,31 @@ use raw_window_handle::{
|
|||||||
AndroidDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
|
AndroidDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::platform_impl::Fullscreen;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||||
error,
|
error,
|
||||||
event::{self, StartCause},
|
event::{self, InnerSizeWriter, StartCause},
|
||||||
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
|
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
|
||||||
keyboard::NativeKey,
|
platform::pump_events::PumpStatus,
|
||||||
window::{
|
window::{
|
||||||
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
|
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use crate::{error::EventLoopError, platform_impl::Fullscreen};
|
||||||
|
|
||||||
mod keycodes;
|
mod keycodes;
|
||||||
|
|
||||||
static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));
|
static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));
|
||||||
|
|
||||||
|
/// Returns the minimum `Option<Duration>`, taking into account that `None`
|
||||||
|
/// equates to an infinite timeout, not a zero timeout (so can't just use
|
||||||
|
/// `Option::min`)
|
||||||
|
fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
|
||||||
|
a.map_or(b, |a_timeout| {
|
||||||
|
b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
struct PeekableReceiver<T> {
|
struct PeekableReceiver<T> {
|
||||||
recv: mpsc::Receiver<T>,
|
recv: mpsc::Receiver<T>,
|
||||||
first: Option<T>,
|
first: Option<T>,
|
||||||
@@ -135,8 +144,13 @@ pub struct EventLoop<T: 'static> {
|
|||||||
redraw_flag: SharedFlag,
|
redraw_flag: SharedFlag,
|
||||||
user_events_sender: mpsc::Sender<T>,
|
user_events_sender: mpsc::Sender<T>,
|
||||||
user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent
|
user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent
|
||||||
|
loop_running: bool, // Dispatched `NewEvents<Init>`
|
||||||
running: bool,
|
running: bool,
|
||||||
|
pending_redraw: bool,
|
||||||
|
control_flow: ControlFlow,
|
||||||
|
cause: StartCause,
|
||||||
ignore_volume_keys: bool,
|
ignore_volume_keys: bool,
|
||||||
|
combining_accent: Option<char>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -155,12 +169,12 @@ impl Default for PlatformSpecificEventLoopAttributes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sticky_exit_callback<T, F>(
|
fn sticky_exit_callback<T, F>(
|
||||||
evt: event::Event<'_, T>,
|
evt: event::Event<T>,
|
||||||
target: &RootELW<T>,
|
target: &RootELW<T>,
|
||||||
control_flow: &mut ControlFlow,
|
control_flow: &mut ControlFlow,
|
||||||
callback: &mut F,
|
callback: &mut F,
|
||||||
) where
|
) where
|
||||||
F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
// make ControlFlow::ExitWithCode sticky by providing a dummy
|
// make ControlFlow::ExitWithCode sticky by providing a dummy
|
||||||
// control flow reference if it is already ExitWithCode.
|
// control flow reference if it is already ExitWithCode.
|
||||||
@@ -171,20 +185,16 @@ fn sticky_exit_callback<T, F>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IterationResult {
|
|
||||||
deadline: Option<Instant>,
|
|
||||||
timeout: Option<Duration>,
|
|
||||||
wait_start: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static> EventLoop<T> {
|
impl<T: 'static> EventLoop<T> {
|
||||||
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self {
|
pub(crate) fn new(
|
||||||
|
attributes: &PlatformSpecificEventLoopAttributes,
|
||||||
|
) -> Result<Self, EventLoopError> {
|
||||||
let (user_events_sender, user_events_receiver) = mpsc::channel();
|
let (user_events_sender, user_events_receiver) = mpsc::channel();
|
||||||
|
|
||||||
let android_app = attributes.android_app.as_ref().expect("An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on Android");
|
let android_app = attributes.android_app.as_ref().expect("An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on Android");
|
||||||
let redraw_flag = SharedFlag::new();
|
let redraw_flag = SharedFlag::new();
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
android_app: android_app.clone(),
|
android_app: android_app.clone(),
|
||||||
window_target: event_loop::EventLoopWindowTarget {
|
window_target: event_loop::EventLoopWindowTarget {
|
||||||
p: EventLoopWindowTarget {
|
p: EventLoopWindowTarget {
|
||||||
@@ -200,33 +210,34 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
redraw_flag,
|
redraw_flag,
|
||||||
user_events_sender,
|
user_events_sender,
|
||||||
user_events_receiver: PeekableReceiver::from_recv(user_events_receiver),
|
user_events_receiver: PeekableReceiver::from_recv(user_events_receiver),
|
||||||
|
loop_running: false,
|
||||||
running: false,
|
running: false,
|
||||||
|
pending_redraw: false,
|
||||||
|
control_flow: Default::default(),
|
||||||
|
cause: StartCause::Init,
|
||||||
ignore_volume_keys: attributes.ignore_volume_keys,
|
ignore_volume_keys: attributes.ignore_volume_keys,
|
||||||
}
|
combining_accent: None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn single_iteration<F>(
|
fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
|
||||||
&mut self,
|
|
||||||
control_flow: &mut ControlFlow,
|
|
||||||
main_event: Option<MainEvent<'_>>,
|
|
||||||
pending_redraw: &mut bool,
|
|
||||||
cause: &mut StartCause,
|
|
||||||
callback: &mut F,
|
|
||||||
) -> IterationResult
|
|
||||||
where
|
where
|
||||||
F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
trace!("Mainloop iteration");
|
trace!("Mainloop iteration");
|
||||||
|
|
||||||
|
let cause = self.cause;
|
||||||
|
let mut control_flow = self.control_flow;
|
||||||
|
let mut pending_redraw = self.pending_redraw;
|
||||||
|
let mut resized = false;
|
||||||
|
|
||||||
sticky_exit_callback(
|
sticky_exit_callback(
|
||||||
event::Event::NewEvents(*cause),
|
event::Event::NewEvents(cause),
|
||||||
self.window_target(),
|
self.window_target(),
|
||||||
control_flow,
|
&mut control_flow,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut resized = false;
|
|
||||||
|
|
||||||
if let Some(event) = main_event {
|
if let Some(event) = main_event {
|
||||||
trace!("Handling main event {:?}", event);
|
trace!("Handling main event {:?}", event);
|
||||||
|
|
||||||
@@ -235,7 +246,7 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
sticky_exit_callback(
|
sticky_exit_callback(
|
||||||
event::Event::Resumed,
|
event::Event::Resumed,
|
||||||
self.window_target(),
|
self.window_target(),
|
||||||
control_flow,
|
&mut control_flow,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -243,12 +254,12 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
sticky_exit_callback(
|
sticky_exit_callback(
|
||||||
event::Event::Suspended,
|
event::Event::Suspended,
|
||||||
self.window_target(),
|
self.window_target(),
|
||||||
control_flow,
|
&mut control_flow,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
MainEvent::WindowResized { .. } => resized = true,
|
MainEvent::WindowResized { .. } => resized = true,
|
||||||
MainEvent::RedrawNeeded { .. } => *pending_redraw = true,
|
MainEvent::RedrawNeeded { .. } => pending_redraw = true,
|
||||||
MainEvent::ContentRectChanged { .. } => {
|
MainEvent::ContentRectChanged { .. } => {
|
||||||
warn!("TODO: find a way to notify application of content rect change");
|
warn!("TODO: find a way to notify application of content rect change");
|
||||||
}
|
}
|
||||||
@@ -260,7 +271,7 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
event: event::WindowEvent::Focused(true),
|
event: event::WindowEvent::Focused(true),
|
||||||
},
|
},
|
||||||
self.window_target(),
|
self.window_target(),
|
||||||
control_flow,
|
&mut control_flow,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -272,7 +283,7 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
event: event::WindowEvent::Focused(false),
|
event: event::WindowEvent::Focused(false),
|
||||||
},
|
},
|
||||||
self.window_target(),
|
self.window_target(),
|
||||||
control_flow,
|
&mut control_flow,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -281,15 +292,24 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
let old_scale_factor = monitor.scale_factor();
|
let old_scale_factor = monitor.scale_factor();
|
||||||
let scale_factor = monitor.scale_factor();
|
let scale_factor = monitor.scale_factor();
|
||||||
if (scale_factor - old_scale_factor).abs() < f64::EPSILON {
|
if (scale_factor - old_scale_factor).abs() < f64::EPSILON {
|
||||||
let mut size = MonitorHandle::new(self.android_app.clone()).size();
|
let new_inner_size = Arc::new(Mutex::new(
|
||||||
|
MonitorHandle::new(self.android_app.clone()).size(),
|
||||||
|
));
|
||||||
let event = event::Event::WindowEvent {
|
let event = event::Event::WindowEvent {
|
||||||
window_id: window::WindowId(WindowId),
|
window_id: window::WindowId(WindowId),
|
||||||
event: event::WindowEvent::ScaleFactorChanged {
|
event: event::WindowEvent::ScaleFactorChanged {
|
||||||
new_inner_size: &mut size,
|
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||||
|
&new_inner_size,
|
||||||
|
)),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
sticky_exit_callback(event, self.window_target(), control_flow, callback);
|
sticky_exit_callback(
|
||||||
|
event,
|
||||||
|
self.window_target(),
|
||||||
|
&mut control_flow,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MainEvent::LowMemory => {
|
MainEvent::LowMemory => {
|
||||||
@@ -336,128 +356,25 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
trace!("No main event to handle");
|
trace!("No main event to handle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// temporarily decouple `android_app` from `self` so we aren't holding
|
||||||
|
// a borrow of `self` while iterating
|
||||||
|
let android_app = self.android_app.clone();
|
||||||
|
|
||||||
// Process input events
|
// Process input events
|
||||||
self.android_app.input_events(|event| {
|
match android_app.input_events_iter() {
|
||||||
let mut input_status = InputStatus::Handled;
|
Ok(mut input_iter) => loop {
|
||||||
match event {
|
let read_event = input_iter.next(|event| {
|
||||||
InputEvent::MotionEvent(motion_event) => {
|
self.handle_input_event(&android_app, event, &mut control_flow, callback)
|
||||||
let window_id = window::WindowId(WindowId);
|
});
|
||||||
let device_id = event::DeviceId(DeviceId);
|
|
||||||
|
|
||||||
let phase = match motion_event.action() {
|
if !read_event {
|
||||||
MotionAction::Down | MotionAction::PointerDown => {
|
break;
|
||||||
Some(event::TouchPhase::Started)
|
|
||||||
}
|
|
||||||
MotionAction::Up | MotionAction::PointerUp => {
|
|
||||||
Some(event::TouchPhase::Ended)
|
|
||||||
}
|
|
||||||
MotionAction::Move => Some(event::TouchPhase::Moved),
|
|
||||||
MotionAction::Cancel => {
|
|
||||||
Some(event::TouchPhase::Cancelled)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
None // TODO mouse events
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(phase) = phase {
|
|
||||||
let pointers: Box<
|
|
||||||
dyn Iterator<Item = android_activity::input::Pointer<'_>>,
|
|
||||||
> = match phase {
|
|
||||||
event::TouchPhase::Started
|
|
||||||
| event::TouchPhase::Ended => {
|
|
||||||
Box::new(
|
|
||||||
std::iter::once(motion_event.pointer_at_index(
|
|
||||||
motion_event.pointer_index(),
|
|
||||||
))
|
|
||||||
)
|
|
||||||
},
|
|
||||||
event::TouchPhase::Moved
|
|
||||||
| event::TouchPhase::Cancelled => {
|
|
||||||
Box::new(motion_event.pointers())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for pointer in pointers {
|
|
||||||
let location = PhysicalPosition {
|
|
||||||
x: pointer.x() as _,
|
|
||||||
y: pointer.y() as _,
|
|
||||||
};
|
|
||||||
trace!("Input event {device_id:?}, {phase:?}, loc={location:?}, pointer={pointer:?}");
|
|
||||||
let event = event::Event::WindowEvent {
|
|
||||||
window_id,
|
|
||||||
event: event::WindowEvent::Touch(
|
|
||||||
event::Touch {
|
|
||||||
device_id,
|
|
||||||
phase,
|
|
||||||
location,
|
|
||||||
id: pointer.pointer_id() as u64,
|
|
||||||
force: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
};
|
|
||||||
sticky_exit_callback(
|
|
||||||
event,
|
|
||||||
self.window_target(),
|
|
||||||
control_flow,
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InputEvent::KeyEvent(key) => {
|
|
||||||
match key.key_code() {
|
|
||||||
// Flag keys related to volume as unhandled. While winit does not have a way for applications
|
|
||||||
// to configure what keys to flag as handled, this appears to be a good default until winit
|
|
||||||
// can be configured.
|
|
||||||
Keycode::VolumeUp |
|
|
||||||
Keycode::VolumeDown |
|
|
||||||
Keycode::VolumeMute => {
|
|
||||||
if self.ignore_volume_keys {
|
|
||||||
input_status = InputStatus::Unhandled
|
|
||||||
}
|
|
||||||
},
|
|
||||||
keycode => {
|
|
||||||
let state = match key.action() {
|
|
||||||
KeyAction::Down => event::ElementState::Pressed,
|
|
||||||
KeyAction::Up => event::ElementState::Released,
|
|
||||||
_ => event::ElementState::Released,
|
|
||||||
};
|
|
||||||
|
|
||||||
let native = NativeKey::Android(keycode.into());
|
|
||||||
let logical_key = keycodes::to_logical(keycode, native);
|
|
||||||
// TODO: maybe use getUnicodeChar to get the logical key
|
|
||||||
|
|
||||||
let event = event::Event::WindowEvent {
|
|
||||||
window_id: window::WindowId(WindowId),
|
|
||||||
event: event::WindowEvent::KeyboardInput {
|
|
||||||
device_id: event::DeviceId(DeviceId),
|
|
||||||
event: event::KeyEvent {
|
|
||||||
state,
|
|
||||||
physical_key: keycodes::to_physical_keycode(keycode),
|
|
||||||
logical_key,
|
|
||||||
location: keycodes::to_location(keycode),
|
|
||||||
repeat: key.repeat_count() > 0,
|
|
||||||
text: None,
|
|
||||||
platform_specific: KeyEventExtra {},
|
|
||||||
},
|
|
||||||
is_synthetic: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
sticky_exit_callback(
|
|
||||||
event,
|
|
||||||
self.window_target(),
|
|
||||||
control_flow,
|
|
||||||
callback,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
warn!("Unknown android_activity input event {event:?}")
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to get input events iterator: {err:?}");
|
||||||
}
|
}
|
||||||
input_status
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Empty the user event buffer
|
// Empty the user event buffer
|
||||||
{
|
{
|
||||||
@@ -465,19 +382,12 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
sticky_exit_callback(
|
sticky_exit_callback(
|
||||||
crate::event::Event::UserEvent(event),
|
crate::event::Event::UserEvent(event),
|
||||||
self.window_target(),
|
self.window_target(),
|
||||||
control_flow,
|
&mut control_flow,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sticky_exit_callback(
|
|
||||||
event::Event::MainEventsCleared,
|
|
||||||
self.window_target(),
|
|
||||||
control_flow,
|
|
||||||
callback,
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.running {
|
if self.running {
|
||||||
if resized {
|
if resized {
|
||||||
let size = if let Some(native_window) = self.android_app.native_window().as_ref() {
|
let size = if let Some(native_window) = self.android_app.native_window().as_ref() {
|
||||||
@@ -491,163 +401,290 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
window_id: window::WindowId(WindowId),
|
window_id: window::WindowId(WindowId),
|
||||||
event: event::WindowEvent::Resized(size),
|
event: event::WindowEvent::Resized(size),
|
||||||
};
|
};
|
||||||
sticky_exit_callback(event, self.window_target(), control_flow, callback);
|
sticky_exit_callback(event, self.window_target(), &mut control_flow, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
*pending_redraw |= self.redraw_flag.get_and_reset();
|
pending_redraw |= self.redraw_flag.get_and_reset();
|
||||||
if *pending_redraw {
|
if pending_redraw {
|
||||||
*pending_redraw = false;
|
pending_redraw = false;
|
||||||
let event = event::Event::RedrawRequested(window::WindowId(WindowId));
|
let event = event::Event::RedrawRequested(window::WindowId(WindowId));
|
||||||
sticky_exit_callback(event, self.window_target(), control_flow, callback);
|
sticky_exit_callback(event, self.window_target(), &mut control_flow, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is always the last event we dispatch before poll again
|
||||||
sticky_exit_callback(
|
sticky_exit_callback(
|
||||||
event::Event::RedrawEventsCleared,
|
event::Event::AboutToWait,
|
||||||
self.window_target(),
|
self.window_target(),
|
||||||
control_flow,
|
&mut control_flow,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
|
|
||||||
let start = Instant::now();
|
self.control_flow = control_flow;
|
||||||
let (deadline, timeout);
|
self.pending_redraw = pending_redraw;
|
||||||
|
|
||||||
match control_flow {
|
|
||||||
ControlFlow::ExitWithCode(_) => {
|
|
||||||
deadline = None;
|
|
||||||
timeout = None;
|
|
||||||
}
|
|
||||||
ControlFlow::Poll => {
|
|
||||||
*cause = StartCause::Poll;
|
|
||||||
deadline = None;
|
|
||||||
timeout = Some(Duration::from_millis(0));
|
|
||||||
}
|
|
||||||
ControlFlow::Wait => {
|
|
||||||
*cause = StartCause::WaitCancelled {
|
|
||||||
start,
|
|
||||||
requested_resume: None,
|
|
||||||
};
|
|
||||||
deadline = None;
|
|
||||||
timeout = None;
|
|
||||||
}
|
|
||||||
ControlFlow::WaitUntil(wait_deadline) => {
|
|
||||||
*cause = StartCause::ResumeTimeReached {
|
|
||||||
start,
|
|
||||||
requested_resume: *wait_deadline,
|
|
||||||
};
|
|
||||||
timeout = if *wait_deadline > start {
|
|
||||||
Some(*wait_deadline - start)
|
|
||||||
} else {
|
|
||||||
Some(Duration::from_millis(0))
|
|
||||||
};
|
|
||||||
deadline = Some(*wait_deadline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IterationResult {
|
|
||||||
wait_start: start,
|
|
||||||
deadline,
|
|
||||||
timeout,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<F>(mut self, event_handler: F) -> !
|
fn handle_input_event<F>(
|
||||||
|
&mut self,
|
||||||
|
android_app: &AndroidApp,
|
||||||
|
event: &InputEvent<'_>,
|
||||||
|
control_flow: &mut ControlFlow,
|
||||||
|
callback: &mut F,
|
||||||
|
) -> InputStatus
|
||||||
where
|
where
|
||||||
F: 'static
|
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||||
+ FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
|
|
||||||
{
|
{
|
||||||
let exit_code = self.run_return(event_handler);
|
let mut input_status = InputStatus::Handled;
|
||||||
::std::process::exit(exit_code);
|
match event {
|
||||||
}
|
InputEvent::MotionEvent(motion_event) => {
|
||||||
|
let window_id = window::WindowId(WindowId);
|
||||||
|
let device_id = event::DeviceId(DeviceId);
|
||||||
|
|
||||||
pub fn run_return<F>(&mut self, mut callback: F) -> i32
|
let phase = match motion_event.action() {
|
||||||
where
|
MotionAction::Down | MotionAction::PointerDown => {
|
||||||
F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
Some(event::TouchPhase::Started)
|
||||||
{
|
}
|
||||||
let mut control_flow = ControlFlow::default();
|
MotionAction::Up | MotionAction::PointerUp => Some(event::TouchPhase::Ended),
|
||||||
let mut cause = StartCause::Init;
|
MotionAction::Move => Some(event::TouchPhase::Moved),
|
||||||
let mut pending_redraw = false;
|
MotionAction::Cancel => Some(event::TouchPhase::Cancelled),
|
||||||
|
_ => {
|
||||||
|
None // TODO mouse events
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(phase) = phase {
|
||||||
|
let pointers: Box<dyn Iterator<Item = android_activity::input::Pointer<'_>>> =
|
||||||
|
match phase {
|
||||||
|
event::TouchPhase::Started | event::TouchPhase::Ended => {
|
||||||
|
Box::new(std::iter::once(
|
||||||
|
motion_event.pointer_at_index(motion_event.pointer_index()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
event::TouchPhase::Moved | event::TouchPhase::Cancelled => {
|
||||||
|
Box::new(motion_event.pointers())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// run the initial loop iteration
|
for pointer in pointers {
|
||||||
let mut iter_result = self.single_iteration(
|
let location = PhysicalPosition {
|
||||||
&mut control_flow,
|
x: pointer.x() as _,
|
||||||
None,
|
y: pointer.y() as _,
|
||||||
&mut pending_redraw,
|
};
|
||||||
&mut cause,
|
trace!("Input event {device_id:?}, {phase:?}, loc={location:?}, pointer={pointer:?}");
|
||||||
&mut callback,
|
let event = event::Event::WindowEvent {
|
||||||
);
|
window_id,
|
||||||
|
event: event::WindowEvent::Touch(event::Touch {
|
||||||
let exit_code = loop {
|
device_id,
|
||||||
if let ControlFlow::ExitWithCode(code) = control_flow {
|
phase,
|
||||||
break code;
|
location,
|
||||||
|
id: pointer.pointer_id() as u64,
|
||||||
|
force: None,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
sticky_exit_callback(event, self.window_target(), control_flow, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
InputEvent::KeyEvent(key) => {
|
||||||
let mut timeout = iter_result.timeout;
|
match key.key_code() {
|
||||||
|
// Flag keys related to volume as unhandled. While winit does not have a way for applications
|
||||||
// If we already have work to do then we don't want to block on the next poll...
|
// to configure what keys to flag as handled, this appears to be a good default until winit
|
||||||
pending_redraw |= self.redraw_flag.get_and_reset();
|
// can be configured.
|
||||||
if self.running && (pending_redraw || self.user_events_receiver.has_incoming()) {
|
Keycode::VolumeUp | Keycode::VolumeDown | Keycode::VolumeMute => {
|
||||||
timeout = Some(Duration::from_millis(0))
|
if self.ignore_volume_keys {
|
||||||
}
|
input_status = InputStatus::Unhandled
|
||||||
|
|
||||||
let app = self.android_app.clone(); // Don't borrow self as part of poll expression
|
|
||||||
app.poll_events(timeout, |poll_event| {
|
|
||||||
let mut main_event = None;
|
|
||||||
|
|
||||||
match poll_event {
|
|
||||||
android_activity::PollEvent::Wake => {
|
|
||||||
// In the X11 backend it's noted that too many false-positive wake ups
|
|
||||||
// would cause the event loop to run continuously. They handle this by re-checking
|
|
||||||
// for pending events (assuming they cover all valid reasons for a wake up).
|
|
||||||
//
|
|
||||||
// For now, user_events and redraw_requests are the only reasons to expect
|
|
||||||
// a wake up here so we can ignore the wake up if there are no events/requests.
|
|
||||||
// We also ignore wake ups while suspended.
|
|
||||||
pending_redraw |= self.redraw_flag.get_and_reset();
|
|
||||||
if !self.running
|
|
||||||
|| (!pending_redraw && !self.user_events_receiver.has_incoming())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
android_activity::PollEvent::Timeout => {}
|
keycode => {
|
||||||
android_activity::PollEvent::Main(event) => {
|
let state = match key.action() {
|
||||||
main_event = Some(event);
|
KeyAction::Down => event::ElementState::Pressed,
|
||||||
}
|
KeyAction::Up => event::ElementState::Released,
|
||||||
unknown_event => {
|
_ => event::ElementState::Released,
|
||||||
warn!("Unknown poll event {unknown_event:?} (ignored)");
|
};
|
||||||
|
|
||||||
|
let key_char = keycodes::character_map_and_combine_key(
|
||||||
|
android_app,
|
||||||
|
key,
|
||||||
|
&mut self.combining_accent,
|
||||||
|
);
|
||||||
|
|
||||||
|
let event = event::Event::WindowEvent {
|
||||||
|
window_id: window::WindowId(WindowId),
|
||||||
|
event: event::WindowEvent::KeyboardInput {
|
||||||
|
device_id: event::DeviceId(DeviceId),
|
||||||
|
event: event::KeyEvent {
|
||||||
|
state,
|
||||||
|
physical_key: keycodes::to_physical_keycode(keycode),
|
||||||
|
logical_key: keycodes::to_logical(key_char, keycode),
|
||||||
|
location: keycodes::to_location(keycode),
|
||||||
|
repeat: key.repeat_count() > 0,
|
||||||
|
text: None,
|
||||||
|
platform_specific: KeyEventExtra {},
|
||||||
|
},
|
||||||
|
is_synthetic: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
sticky_exit_callback(event, self.window_target(), control_flow, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
warn!("Unknown android_activity input event {event:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let wait_cancelled = iter_result
|
input_status
|
||||||
.deadline
|
}
|
||||||
.map_or(false, |deadline| Instant::now() < deadline);
|
|
||||||
|
|
||||||
if wait_cancelled {
|
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||||
cause = StartCause::WaitCancelled {
|
where
|
||||||
start: iter_result.wait_start,
|
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
requested_resume: iter_result.deadline,
|
{
|
||||||
};
|
self.run_ondemand(event_handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||||
|
where
|
||||||
|
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
if self.loop_running {
|
||||||
|
return Err(EventLoopError::AlreadyRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.pump_events(None, &mut event_handler) {
|
||||||
|
PumpStatus::Exit(0) => {
|
||||||
|
break Ok(());
|
||||||
}
|
}
|
||||||
|
PumpStatus::Exit(code) => {
|
||||||
|
break Err(EventLoopError::ExitFailure(code));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
iter_result = self.single_iteration(
|
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||||
&mut control_flow,
|
where
|
||||||
main_event,
|
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||||
&mut pending_redraw,
|
{
|
||||||
&mut cause,
|
if !self.loop_running {
|
||||||
&mut callback,
|
self.loop_running = true;
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
sticky_exit_callback(
|
// Reset the internal state for the loop as we start running to
|
||||||
event::Event::LoopDestroyed,
|
// ensure consistent behaviour in case the loop runs and exits more
|
||||||
self.window_target(),
|
// than once
|
||||||
&mut control_flow,
|
self.pending_redraw = false;
|
||||||
&mut callback,
|
self.cause = StartCause::Init;
|
||||||
);
|
self.control_flow = ControlFlow::Poll;
|
||||||
|
|
||||||
exit_code
|
// run the initial loop iteration
|
||||||
|
self.single_iteration(None, &mut callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consider the possibility that the `StartCause::Init` iteration could
|
||||||
|
// request to Exit
|
||||||
|
if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) {
|
||||||
|
self.poll_events_with_timeout(timeout, &mut callback);
|
||||||
|
}
|
||||||
|
if let ControlFlow::ExitWithCode(code) = self.control_flow {
|
||||||
|
self.loop_running = false;
|
||||||
|
|
||||||
|
let mut dummy = self.control_flow;
|
||||||
|
sticky_exit_callback(
|
||||||
|
event::Event::LoopExiting,
|
||||||
|
self.window_target(),
|
||||||
|
&mut dummy,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
|
||||||
|
PumpStatus::Exit(code)
|
||||||
|
} else {
|
||||||
|
PumpStatus::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
|
||||||
|
where
|
||||||
|
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
self.pending_redraw |= self.redraw_flag.get_and_reset();
|
||||||
|
|
||||||
|
timeout =
|
||||||
|
if self.running && (self.pending_redraw || self.user_events_receiver.has_incoming()) {
|
||||||
|
// If we already have work to do then we don't want to block on the next poll
|
||||||
|
Some(Duration::ZERO)
|
||||||
|
} else {
|
||||||
|
let control_flow_timeout = match self.control_flow {
|
||||||
|
ControlFlow::Wait => None,
|
||||||
|
ControlFlow::Poll => Some(Duration::ZERO),
|
||||||
|
ControlFlow::WaitUntil(wait_deadline) => {
|
||||||
|
Some(wait_deadline.saturating_duration_since(start))
|
||||||
|
}
|
||||||
|
// `ExitWithCode()` will be reset to `Poll` before polling
|
||||||
|
ControlFlow::ExitWithCode(_code) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
min_timeout(control_flow_timeout, timeout)
|
||||||
|
};
|
||||||
|
|
||||||
|
let app = self.android_app.clone(); // Don't borrow self as part of poll expression
|
||||||
|
app.poll_events(timeout, |poll_event| {
|
||||||
|
let mut main_event = None;
|
||||||
|
|
||||||
|
match poll_event {
|
||||||
|
android_activity::PollEvent::Wake => {
|
||||||
|
// In the X11 backend it's noted that too many false-positive wake ups
|
||||||
|
// would cause the event loop to run continuously. They handle this by re-checking
|
||||||
|
// for pending events (assuming they cover all valid reasons for a wake up).
|
||||||
|
//
|
||||||
|
// For now, user_events and redraw_requests are the only reasons to expect
|
||||||
|
// a wake up here so we can ignore the wake up if there are no events/requests.
|
||||||
|
// We also ignore wake ups while suspended.
|
||||||
|
self.pending_redraw |= self.redraw_flag.get_and_reset();
|
||||||
|
if !self.running
|
||||||
|
|| (!self.pending_redraw && !self.user_events_receiver.has_incoming())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
android_activity::PollEvent::Timeout => {}
|
||||||
|
android_activity::PollEvent::Main(event) => {
|
||||||
|
main_event = Some(event);
|
||||||
|
}
|
||||||
|
unknown_event => {
|
||||||
|
warn!("Unknown poll event {unknown_event:?} (ignored)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cause = match self.control_flow {
|
||||||
|
ControlFlow::Poll => StartCause::Poll,
|
||||||
|
ControlFlow::Wait => StartCause::WaitCancelled {
|
||||||
|
start,
|
||||||
|
requested_resume: None,
|
||||||
|
},
|
||||||
|
ControlFlow::WaitUntil(deadline) => {
|
||||||
|
if Instant::now() < deadline {
|
||||||
|
StartCause::WaitCancelled {
|
||||||
|
start,
|
||||||
|
requested_resume: Some(deadline),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StartCause::ResumeTimeReached {
|
||||||
|
start,
|
||||||
|
requested_resume: deadline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `ExitWithCode()` will be reset to `Poll` before polling
|
||||||
|
ControlFlow::ExitWithCode(_code) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.single_iteration(main_event, &mut callback);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {
|
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {
|
||||||
@@ -760,6 +797,14 @@ impl Window {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) {
|
||||||
|
f(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Self) -> R + Send) -> R {
|
||||||
|
f(self)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> WindowId {
|
pub fn id(&self) -> WindowId {
|
||||||
WindowId
|
WindowId
|
||||||
}
|
}
|
||||||
@@ -786,6 +831,8 @@ impl Window {
|
|||||||
self.redraw_requester.request_redraw()
|
self.redraw_requester.request_redraw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pre_present_notify(&self) {}
|
||||||
|
|
||||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
|
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
|
||||||
Err(error::NotSupportedError::new())
|
Err(error::NotSupportedError::new())
|
||||||
}
|
}
|
||||||
@@ -802,8 +849,8 @@ impl Window {
|
|||||||
self.outer_size()
|
self.outer_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_inner_size(&self, _size: Size) {
|
pub fn request_inner_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
|
||||||
warn!("Cannot set window size on Android");
|
Some(self.inner_size())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||||
@@ -973,8 +1020,8 @@ pub struct MonitorHandle {
|
|||||||
app: AndroidApp,
|
app: AndroidApp,
|
||||||
}
|
}
|
||||||
impl PartialOrd for MonitorHandle {
|
impl PartialOrd for MonitorHandle {
|
||||||
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
Some(std::cmp::Ordering::Equal)
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Ord for MonitorHandle {
|
impl Ord for MonitorHandle {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use std::{
|
|||||||
mem,
|
mem,
|
||||||
os::raw::c_void,
|
os::raw::c_void,
|
||||||
ptr,
|
ptr,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -15,9 +16,9 @@ use core_foundation::runloop::{
|
|||||||
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
||||||
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
||||||
};
|
};
|
||||||
use objc2::foundation::{CGRect, CGSize, NSInteger, NSProcessInfo};
|
use icrate::Foundation::{CGRect, CGSize, NSInteger, NSOperatingSystemVersion, NSProcessInfo};
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::rc::Id;
|
||||||
use objc2::runtime::Object;
|
use objc2::runtime::AnyObject;
|
||||||
use objc2::{msg_send, sel};
|
use objc2::{msg_send, sel};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
@@ -25,12 +26,9 @@ use super::uikit::UIView;
|
|||||||
use super::view::WinitUIWindow;
|
use super::view::WinitUIWindow;
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::LogicalSize,
|
dpi::LogicalSize,
|
||||||
event::{Event, StartCause, WindowEvent},
|
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
||||||
event_loop::ControlFlow,
|
event_loop::ControlFlow,
|
||||||
platform_impl::platform::{
|
platform_impl::platform::event_loop::{EventHandler, EventProxy, EventWrapper, Never},
|
||||||
event_loop::{EventHandler, EventProxy, EventWrapper, Never},
|
|
||||||
ffi::NSOperatingSystemVersion,
|
|
||||||
},
|
|
||||||
window::WindowId as RootWindowId,
|
window::WindowId as RootWindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,7 +55,7 @@ enum UserCallbackTransitionResult<'a> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event<'static, Never> {
|
impl Event<Never> {
|
||||||
fn is_redraw(&self) -> bool {
|
fn is_redraw(&self) -> bool {
|
||||||
matches!(self, Event::RedrawRequested(_))
|
matches!(self, Event::RedrawRequested(_))
|
||||||
}
|
}
|
||||||
@@ -68,25 +66,25 @@ impl Event<'static, Never> {
|
|||||||
#[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, Shared>>,
|
queued_windows: Vec<Id<WinitUIWindow>>,
|
||||||
queued_events: Vec<EventWrapper>,
|
queued_events: Vec<EventWrapper>,
|
||||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow, Shared>>,
|
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
|
||||||
},
|
},
|
||||||
Launching {
|
Launching {
|
||||||
queued_windows: Vec<Id<WinitUIWindow, Shared>>,
|
queued_windows: Vec<Id<WinitUIWindow>>,
|
||||||
queued_events: Vec<EventWrapper>,
|
queued_events: Vec<EventWrapper>,
|
||||||
queued_event_handler: Box<dyn EventHandler>,
|
queued_event_handler: Box<dyn EventHandler>,
|
||||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow, Shared>>,
|
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
|
||||||
},
|
},
|
||||||
ProcessingEvents {
|
ProcessingEvents {
|
||||||
event_handler: Box<dyn EventHandler>,
|
event_handler: Box<dyn EventHandler>,
|
||||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow, Shared>>,
|
queued_gpu_redraws: HashSet<Id<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, Shared>>,
|
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
|
||||||
},
|
},
|
||||||
ProcessingRedraws {
|
ProcessingRedraws {
|
||||||
event_handler: Box<dyn EventHandler>,
|
event_handler: Box<dyn EventHandler>,
|
||||||
@@ -204,9 +202,7 @@ impl AppState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_finish_launching_transition(
|
fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) {
|
||||||
&mut self,
|
|
||||||
) -> (Vec<Id<WinitUIWindow, Shared>>, Vec<EventWrapper>) {
|
|
||||||
let (windows, events, event_handler, queued_gpu_redraws) = match self.take_state() {
|
let (windows, events, event_handler, queued_gpu_redraws) = match self.take_state() {
|
||||||
AppStateImpl::Launching {
|
AppStateImpl::Launching {
|
||||||
queued_windows,
|
queued_windows,
|
||||||
@@ -363,7 +359,7 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow, Shared>> {
|
fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> {
|
||||||
let (event_handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
let (event_handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
||||||
AppStateImpl::ProcessingEvents {
|
AppStateImpl::ProcessingEvents {
|
||||||
event_handler,
|
event_handler,
|
||||||
@@ -393,9 +389,6 @@ impl AppState {
|
|||||||
|
|
||||||
let new = self.control_flow;
|
let new = self.control_flow;
|
||||||
match (old, new) {
|
match (old, new) {
|
||||||
(ControlFlow::Poll, ControlFlow::Poll) => self.set_state(AppStateImpl::PollFinished {
|
|
||||||
waiting_event_handler,
|
|
||||||
}),
|
|
||||||
(ControlFlow::Wait, ControlFlow::Wait) => {
|
(ControlFlow::Wait, ControlFlow::Wait) => {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
self.set_state(AppStateImpl::Waiting {
|
self.set_state(AppStateImpl::Waiting {
|
||||||
@@ -428,6 +421,7 @@ impl AppState {
|
|||||||
});
|
});
|
||||||
self.waker.start_at(new_instant)
|
self.waker.start_at(new_instant)
|
||||||
}
|
}
|
||||||
|
// Unlike on macOS, handle Poll to Poll transition here to call the waker
|
||||||
(_, ControlFlow::Poll) => {
|
(_, ControlFlow::Poll) => {
|
||||||
self.set_state(AppStateImpl::PollFinished {
|
self.set_state(AppStateImpl::PollFinished {
|
||||||
waiting_event_handler,
|
waiting_event_handler,
|
||||||
@@ -446,17 +440,14 @@ impl AppState {
|
|||||||
fn terminated_transition(&mut self) -> Box<dyn EventHandler> {
|
fn terminated_transition(&mut self) -> Box<dyn EventHandler> {
|
||||||
match self.replace_state(AppStateImpl::Terminated) {
|
match self.replace_state(AppStateImpl::Terminated) {
|
||||||
AppStateImpl::ProcessingEvents { event_handler, .. } => event_handler,
|
AppStateImpl::ProcessingEvents { event_handler, .. } => event_handler,
|
||||||
s => bug!(
|
s => bug!("`LoopExiting` happened while not processing events {:?}", s),
|
||||||
"`LoopDestroyed` happened while not processing events {:?}",
|
|
||||||
s
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread and window is a UIWindow
|
// requires main thread and window is a UIWindow
|
||||||
// retains window
|
// retains window
|
||||||
pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow, Shared>) {
|
pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow>) {
|
||||||
let mut this = AppState::get_mut();
|
let mut this = AppState::get_mut();
|
||||||
match this.state_mut() {
|
match this.state_mut() {
|
||||||
&mut AppStateImpl::NotLaunched {
|
&mut AppStateImpl::NotLaunched {
|
||||||
@@ -479,7 +470,7 @@ pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow, Shared>) {
|
|||||||
|
|
||||||
// requires main thread and window is a UIWindow
|
// requires main thread and window is a UIWindow
|
||||||
// retains window
|
// retains window
|
||||||
pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id<WinitUIWindow, Shared>) {
|
pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id<WinitUIWindow>) {
|
||||||
let mut this = AppState::get_mut();
|
let mut this = AppState::get_mut();
|
||||||
match this.state_mut() {
|
match this.state_mut() {
|
||||||
&mut AppStateImpl::NotLaunched {
|
&mut AppStateImpl::NotLaunched {
|
||||||
@@ -544,7 +535,7 @@ pub unsafe fn did_finish_launching() {
|
|||||||
// completed. This may result in incorrect visual appearance.
|
// completed. This may result in incorrect visual appearance.
|
||||||
// ```
|
// ```
|
||||||
let screen = window.screen();
|
let screen = window.screen();
|
||||||
let _: () = msg_send![&window, setScreen: ptr::null::<Object>()];
|
let _: () = msg_send![&window, setScreen: ptr::null::<AnyObject>()];
|
||||||
window.setScreen(&screen);
|
window.setScreen(&screen);
|
||||||
|
|
||||||
let controller = window.rootViewController();
|
let controller = window.rootViewController();
|
||||||
@@ -758,21 +749,18 @@ pub unsafe fn handle_main_events_cleared() {
|
|||||||
};
|
};
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
// User events are always sent out at the end of the "MainEventLoop"
|
|
||||||
handle_user_events();
|
handle_user_events();
|
||||||
handle_nonuser_event(EventWrapper::StaticEvent(Event::MainEventsCleared));
|
|
||||||
|
|
||||||
let mut this = AppState::get_mut();
|
let mut this = AppState::get_mut();
|
||||||
let mut redraw_events: Vec<EventWrapper> = this
|
let redraw_events: Vec<EventWrapper> = this
|
||||||
.main_events_cleared_transition()
|
.main_events_cleared_transition()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|window| EventWrapper::StaticEvent(Event::RedrawRequested(RootWindowId(window.id()))))
|
.map(|window| EventWrapper::StaticEvent(Event::RedrawRequested(RootWindowId(window.id()))))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
redraw_events.push(EventWrapper::StaticEvent(Event::RedrawEventsCleared));
|
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
handle_nonuser_events(redraw_events);
|
handle_nonuser_events(redraw_events);
|
||||||
|
handle_nonuser_event(EventWrapper::StaticEvent(Event::AboutToWait));
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
// requires main thread
|
||||||
@@ -787,7 +775,7 @@ pub unsafe fn terminated() {
|
|||||||
let mut control_flow = this.control_flow;
|
let mut control_flow = this.control_flow;
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
event_handler.handle_nonuser_event(Event::LoopDestroyed, &mut control_flow)
|
event_handler.handle_nonuser_event(Event::LoopExiting, &mut control_flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_event_proxy(
|
fn handle_event_proxy(
|
||||||
@@ -815,27 +803,27 @@ fn handle_hidpi_proxy(
|
|||||||
mut control_flow: ControlFlow,
|
mut control_flow: ControlFlow,
|
||||||
suggested_size: LogicalSize<f64>,
|
suggested_size: LogicalSize<f64>,
|
||||||
scale_factor: f64,
|
scale_factor: f64,
|
||||||
window: Id<WinitUIWindow, Shared>,
|
window: Id<WinitUIWindow>,
|
||||||
) {
|
) {
|
||||||
let mut size = suggested_size.to_physical(scale_factor);
|
let new_inner_size = Arc::new(Mutex::new(suggested_size.to_physical(scale_factor)));
|
||||||
let new_inner_size = &mut size;
|
|
||||||
let event = Event::WindowEvent {
|
let event = Event::WindowEvent {
|
||||||
window_id: RootWindowId(window.id()),
|
window_id: RootWindowId(window.id()),
|
||||||
event: WindowEvent::ScaleFactorChanged {
|
event: WindowEvent::ScaleFactorChanged {
|
||||||
scale_factor,
|
scale_factor,
|
||||||
new_inner_size,
|
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
event_handler.handle_nonuser_event(event, &mut control_flow);
|
event_handler.handle_nonuser_event(event, &mut control_flow);
|
||||||
let (view, screen_frame) = get_view_and_screen_frame(&window);
|
let (view, screen_frame) = get_view_and_screen_frame(&window);
|
||||||
let physical_size = *new_inner_size;
|
let physical_size = *new_inner_size.lock().unwrap();
|
||||||
|
drop(new_inner_size);
|
||||||
let logical_size = physical_size.to_logical(scale_factor);
|
let logical_size = physical_size.to_logical(scale_factor);
|
||||||
let size = CGSize::new(logical_size.width, logical_size.height);
|
let size = CGSize::new(logical_size.width, logical_size.height);
|
||||||
let new_frame: CGRect = CGRect::new(screen_frame.origin, size);
|
let new_frame: CGRect = CGRect::new(screen_frame.origin, size);
|
||||||
view.setFrame(new_frame);
|
view.setFrame(new_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Id<UIView, Shared>, CGRect) {
|
fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Id<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();
|
||||||
@@ -924,7 +912,7 @@ macro_rules! os_capabilities {
|
|||||||
|
|
||||||
impl From<NSOperatingSystemVersion> for OSCapabilities {
|
impl From<NSOperatingSystemVersion> for OSCapabilities {
|
||||||
fn from(os_version: NSOperatingSystemVersion) -> OSCapabilities {
|
fn from(os_version: NSOperatingSystemVersion) -> OSCapabilities {
|
||||||
$(let $name = os_version.meets_requirements($major, $minor);)*
|
$(let $name = meets_requirements(os_version, $major, $minor);)*
|
||||||
OSCapabilities { $($name,)* os_version, }
|
OSCapabilities { $($name,)* os_version, }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -934,7 +922,7 @@ macro_rules! os_capabilities {
|
|||||||
pub fn $error_name(&self, extra_msg: &str) {
|
pub fn $error_name(&self, extra_msg: &str) {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
concat!("`", $objc_call, "` requires iOS {}.{}+. This device is running iOS {}.{}.{}. {}"),
|
concat!("`", $objc_call, "` requires iOS {}.{}+. This device is running iOS {}.{}.{}. {}"),
|
||||||
$major, $minor, self.os_version.major, self.os_version.minor, self.os_version.patch,
|
$major, $minor, self.os_version.majorVersion, self.os_version.minorVersion, self.os_version.patchVersion,
|
||||||
extra_msg
|
extra_msg
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -962,16 +950,18 @@ os_capabilities! {
|
|||||||
force_touch: 9-0,
|
force_touch: 9-0,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NSOperatingSystemVersion {
|
fn meets_requirements(
|
||||||
fn meets_requirements(&self, required_major: NSInteger, required_minor: NSInteger) -> bool {
|
version: NSOperatingSystemVersion,
|
||||||
(self.major, self.minor) >= (required_major, required_minor)
|
required_major: NSInteger,
|
||||||
}
|
required_minor: NSInteger,
|
||||||
|
) -> bool {
|
||||||
|
(version.majorVersion, version.minorVersion) >= (required_major, required_minor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn os_capabilities() -> OSCapabilities {
|
pub fn os_capabilities() -> OSCapabilities {
|
||||||
static OS_CAPABILITIES: Lazy<OSCapabilities> = Lazy::new(|| {
|
static OS_CAPABILITIES: Lazy<OSCapabilities> = Lazy::new(|| {
|
||||||
let version: NSOperatingSystemVersion = unsafe {
|
let version: NSOperatingSystemVersion = unsafe {
|
||||||
let process_info = NSProcessInfo::process_info();
|
let process_info = NSProcessInfo::processInfo();
|
||||||
let atleast_ios_8: bool = msg_send![
|
let atleast_ios_8: bool = msg_send![
|
||||||
&process_info,
|
&process_info,
|
||||||
respondsToSelector: sel!(operatingSystemVersion)
|
respondsToSelector: sel!(operatingSystemVersion)
|
||||||
@@ -984,7 +974,7 @@ pub fn os_capabilities() -> OSCapabilities {
|
|||||||
//
|
//
|
||||||
// The minimum required iOS version is likely to grow in the future.
|
// The minimum required iOS version is likely to grow in the future.
|
||||||
assert!(atleast_ios_8, "`winit` requires iOS version 8 or greater");
|
assert!(atleast_ios_8, "`winit` requires iOS version 8 or greater");
|
||||||
msg_send![&process_info, operatingSystemVersion]
|
process_info.operatingSystemVersion()
|
||||||
};
|
};
|
||||||
version.into()
|
version.into()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,16 +14,14 @@ use core_foundation::runloop::{
|
|||||||
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
||||||
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||||
};
|
};
|
||||||
use objc2::foundation::{MainThreadMarker, NSString};
|
use icrate::Foundation::{MainThreadMarker, NSString};
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::rc::Id;
|
||||||
use objc2::ClassType;
|
use objc2::ClassType;
|
||||||
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
|
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
|
||||||
|
|
||||||
use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen};
|
|
||||||
use super::view::WinitUIWindow;
|
|
||||||
use super::{app_state, monitor, view, MonitorHandle};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::LogicalSize,
|
dpi::LogicalSize,
|
||||||
|
error::EventLoopError,
|
||||||
event::Event,
|
event::Event,
|
||||||
event_loop::{
|
event_loop::{
|
||||||
ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget,
|
ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget,
|
||||||
@@ -31,16 +29,20 @@ use crate::{
|
|||||||
platform::ios::Idiom,
|
platform::ios::Idiom,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen};
|
||||||
|
use super::view::WinitUIWindow;
|
||||||
|
use super::{app_state, monitor, view, MonitorHandle};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum EventWrapper {
|
pub(crate) enum EventWrapper {
|
||||||
StaticEvent(Event<'static, Never>),
|
StaticEvent(Event<Never>),
|
||||||
EventProxy(EventProxy),
|
EventProxy(EventProxy),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub(crate) enum EventProxy {
|
pub(crate) enum EventProxy {
|
||||||
DpiChangedProxy {
|
DpiChangedProxy {
|
||||||
window: Id<WinitUIWindow, Shared>,
|
window: Id<WinitUIWindow>,
|
||||||
suggested_size: LogicalSize<f64>,
|
suggested_size: LogicalSize<f64>,
|
||||||
scale_factor: f64,
|
scale_factor: f64,
|
||||||
},
|
},
|
||||||
@@ -75,7 +77,9 @@ pub struct EventLoop<T: 'static> {
|
|||||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||||
|
|
||||||
impl<T: 'static> EventLoop<T> {
|
impl<T: 'static> EventLoop<T> {
|
||||||
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> EventLoop<T> {
|
pub(crate) fn new(
|
||||||
|
_: &PlatformSpecificEventLoopAttributes,
|
||||||
|
) -> Result<EventLoop<T>, EventLoopError> {
|
||||||
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
|
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
|
||||||
|
|
||||||
static mut SINGLETON_INIT: bool = false;
|
static mut SINGLETON_INIT: bool = false;
|
||||||
@@ -93,7 +97,7 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
// this line sets up the main run loop before `UIApplicationMain`
|
// this line sets up the main run loop before `UIApplicationMain`
|
||||||
setup_control_flow_observers();
|
setup_control_flow_observers();
|
||||||
|
|
||||||
EventLoop {
|
Ok(EventLoop {
|
||||||
window_target: RootEventLoopWindowTarget {
|
window_target: RootEventLoopWindowTarget {
|
||||||
p: EventLoopWindowTarget {
|
p: EventLoopWindowTarget {
|
||||||
receiver,
|
receiver,
|
||||||
@@ -101,12 +105,12 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
},
|
},
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<F>(self, event_handler: F) -> !
|
pub fn run<F>(self, event_handler: F) -> !
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let application = UIApplication::shared(MainThreadMarker::new().unwrap());
|
let application = UIApplication::shared(MainThreadMarker::new().unwrap());
|
||||||
@@ -116,10 +120,18 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
|
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
|
||||||
Note: `EventLoop::run` calls `UIApplicationMain` on iOS",
|
Note: `EventLoop::run` calls `UIApplicationMain` on iOS",
|
||||||
);
|
);
|
||||||
app_state::will_launch(Box::new(EventLoopHandler {
|
|
||||||
|
let event_handler = std::mem::transmute::<
|
||||||
|
Box<dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow)>,
|
||||||
|
Box<EventHandlerCallback<T>>,
|
||||||
|
>(Box::new(event_handler));
|
||||||
|
|
||||||
|
let handler = EventLoopHandler {
|
||||||
f: event_handler,
|
f: event_handler,
|
||||||
event_loop: self.window_target,
|
event_loop: self.window_target,
|
||||||
}));
|
};
|
||||||
|
|
||||||
|
app_state::will_launch(Box::new(handler));
|
||||||
|
|
||||||
// Ensure application delegate is initialized
|
// Ensure application delegate is initialized
|
||||||
view::WinitApplicationDelegate::class();
|
view::WinitApplicationDelegate::class();
|
||||||
@@ -237,10 +249,10 @@ fn setup_control_flow_observers() {
|
|||||||
|
|
||||||
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
||||||
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end
|
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end
|
||||||
// priority to be 0, in order to send MainEventsCleared before RedrawRequested. This value was
|
// priority to be 0, in order to send AboutToWait before RedrawRequested. This value was
|
||||||
// chosen conservatively to guard against apple using different priorities for their redraw
|
// chosen conservatively to guard against apple using different priorities for their redraw
|
||||||
// observers in different OS's or on different devices. If it so happens that it's too
|
// observers in different OS's or on different devices. If it so happens that it's too
|
||||||
// conservative, the main symptom would be non-redraw events coming in after `MainEventsCleared`.
|
// conservative, the main symptom would be non-redraw events coming in after `AboutToWait`.
|
||||||
//
|
//
|
||||||
// The value of `0x1e8480` was determined by inspecting stack traces and the associated
|
// The value of `0x1e8480` was determined by inspecting stack traces and the associated
|
||||||
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
|
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
|
||||||
@@ -314,17 +326,20 @@ fn setup_control_flow_observers() {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Never {}
|
pub enum Never {}
|
||||||
|
|
||||||
|
type EventHandlerCallback<T> =
|
||||||
|
dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow) + 'static;
|
||||||
|
|
||||||
pub trait EventHandler: Debug {
|
pub trait EventHandler: Debug {
|
||||||
fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow);
|
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow);
|
||||||
fn handle_user_events(&mut self, control_flow: &mut ControlFlow);
|
fn handle_user_events(&mut self, control_flow: &mut ControlFlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EventLoopHandler<F, T: 'static> {
|
struct EventLoopHandler<T: 'static> {
|
||||||
f: F,
|
f: Box<EventHandlerCallback<T>>,
|
||||||
event_loop: RootEventLoopWindowTarget<T>,
|
event_loop: RootEventLoopWindowTarget<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, T: 'static> Debug for EventLoopHandler<F, T> {
|
impl<T: 'static> Debug for EventLoopHandler<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("EventLoopHandler")
|
f.debug_struct("EventLoopHandler")
|
||||||
.field("event_loop", &self.event_loop)
|
.field("event_loop", &self.event_loop)
|
||||||
@@ -332,12 +347,8 @@ impl<F, T: 'static> Debug for EventLoopHandler<F, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, T> EventHandler for EventLoopHandler<F, T>
|
impl<T: 'static> EventHandler for EventLoopHandler<T> {
|
||||||
where
|
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
|
||||||
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
|
||||||
T: 'static,
|
|
||||||
{
|
|
||||||
fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow) {
|
|
||||||
(self.f)(
|
(self.f)(
|
||||||
event.map_nonuser_event().unwrap(),
|
event.map_nonuser_event().unwrap(),
|
||||||
&self.event_loop,
|
&self.event_loop,
|
||||||
|
|||||||
@@ -2,30 +2,11 @@
|
|||||||
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use icrate::Foundation::{NSInteger, NSUInteger};
|
||||||
use objc2::encode::{Encode, Encoding};
|
use objc2::encode::{Encode, Encoding};
|
||||||
use objc2::foundation::{NSInteger, NSUInteger};
|
|
||||||
|
|
||||||
use crate::platform::ios::{Idiom, ScreenEdge};
|
use crate::platform::ios::{Idiom, ScreenEdge};
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct NSOperatingSystemVersion {
|
|
||||||
pub major: NSInteger,
|
|
||||||
pub minor: NSInteger,
|
|
||||||
pub patch: NSInteger,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Encode for NSOperatingSystemVersion {
|
|
||||||
const ENCODING: Encoding = Encoding::Struct(
|
|
||||||
"NSOperatingSystemVersion",
|
|
||||||
&[
|
|
||||||
NSInteger::ENCODING,
|
|
||||||
NSInteger::ENCODING,
|
|
||||||
NSInteger::ENCODING,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct UIUserInterfaceIdiom(NSInteger);
|
pub struct UIUserInterfaceIdiom(NSInteger);
|
||||||
@@ -70,6 +51,10 @@ impl From<UIUserInterfaceIdiom> for Idiom {
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct UIRectEdge(NSUInteger);
|
pub struct UIRectEdge(NSUInteger);
|
||||||
|
|
||||||
|
impl UIRectEdge {
|
||||||
|
pub(crate) const NONE: Self = Self(0);
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl Encode for UIRectEdge {
|
unsafe impl Encode for UIRectEdge {
|
||||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,12 +48,12 @@
|
|||||||
//!
|
//!
|
||||||
//! - applicationDidBecomeActive is Resumed
|
//! - applicationDidBecomeActive is Resumed
|
||||||
//! - applicationWillResignActive is Suspended
|
//! - applicationWillResignActive is Suspended
|
||||||
//! - applicationWillTerminate is LoopDestroyed
|
//! - applicationWillTerminate is LoopExiting
|
||||||
//!
|
//!
|
||||||
//! Keep in mind that after LoopDestroyed event is received every attempt to draw with
|
//! Keep in mind that after LoopExiting event is received every attempt to draw with
|
||||||
//! opengl will result in segfault.
|
//! opengl will result in segfault.
|
||||||
//!
|
//!
|
||||||
//! Also note that app may not receive the LoopDestroyed event if suspended; it might be SIGKILL'ed.
|
//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
|
||||||
|
|
||||||
#![cfg(ios_platform)]
|
#![cfg(ios_platform)]
|
||||||
#![allow(clippy::let_unit_value)]
|
#![allow(clippy::let_unit_value)]
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
// window size/position.
|
// window size/position.
|
||||||
macro_rules! assert_main_thread {
|
macro_rules! assert_main_thread {
|
||||||
($($t:tt)*) => {
|
($($t:tt)*) => {
|
||||||
if !::objc2::foundation::is_main_thread() {
|
if !::icrate::Foundation::is_main_thread() {
|
||||||
panic!($($t)*);
|
panic!($($t)*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -89,7 +89,7 @@ pub(crate) use self::{
|
|||||||
|
|
||||||
use self::uikit::UIScreen;
|
use self::uikit::UIScreen;
|
||||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||||
pub(self) use crate::platform_impl::Fullscreen;
|
pub(crate) use crate::platform_impl::Fullscreen;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct DeviceId {
|
pub struct DeviceId {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use std::{
|
|||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
};
|
};
|
||||||
|
|
||||||
use objc2::foundation::{MainThreadMarker, NSInteger};
|
use icrate::Foundation::{MainThreadMarker, NSInteger};
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::rc::Id;
|
||||||
|
|
||||||
use super::uikit::{UIScreen, UIScreenMode};
|
use super::uikit::{UIScreen, UIScreenMode};
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -18,7 +18,7 @@ use crate::{
|
|||||||
|
|
||||||
// TODO(madsmtm): Remove or refactor this
|
// TODO(madsmtm): Remove or refactor this
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
pub(crate) struct ScreenModeSendSync(pub(crate) Id<UIScreenMode, Shared>);
|
pub(crate) struct ScreenModeSendSync(pub(crate) Id<UIScreenMode>);
|
||||||
|
|
||||||
unsafe impl Send for ScreenModeSendSync {}
|
unsafe impl Send for ScreenModeSendSync {}
|
||||||
unsafe impl Sync for ScreenModeSendSync {}
|
unsafe impl Sync for ScreenModeSendSync {}
|
||||||
@@ -33,7 +33,7 @@ pub struct VideoMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VideoMode {
|
impl VideoMode {
|
||||||
fn new(uiscreen: Id<UIScreen, Shared>, screen_mode: Id<UIScreenMode, Shared>) -> VideoMode {
|
fn new(uiscreen: Id<UIScreen>, screen_mode: Id<UIScreenMode>) -> VideoMode {
|
||||||
assert_main_thread!("`VideoMode` can only be created on the main thread on iOS");
|
assert_main_thread!("`VideoMode` can only be created on the main thread on iOS");
|
||||||
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
|
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
|
||||||
let size = screen_mode.size();
|
let size = screen_mode.size();
|
||||||
@@ -65,7 +65,7 @@ impl VideoMode {
|
|||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Inner {
|
pub struct Inner {
|
||||||
uiscreen: Id<UIScreen, Shared>,
|
uiscreen: Id<UIScreen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
@@ -135,7 +135,7 @@ impl fmt::Debug for MonitorHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MonitorHandle {
|
impl MonitorHandle {
|
||||||
pub(crate) fn new(uiscreen: Id<UIScreen, Shared>) -> Self {
|
pub(crate) fn new(uiscreen: Id<UIScreen>) -> Self {
|
||||||
assert_main_thread!("`MonitorHandle` can only be created on the main thread on iOS");
|
assert_main_thread!("`MonitorHandle` can only be created on the main thread on iOS");
|
||||||
Self {
|
Self {
|
||||||
inner: Inner { uiscreen },
|
inner: Inner { uiscreen },
|
||||||
@@ -182,13 +182,8 @@ impl Inner {
|
|||||||
.uiscreen
|
.uiscreen
|
||||||
.availableModes()
|
.availableModes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mode| {
|
.map(|mode| RootVideoMode {
|
||||||
let mode: *const UIScreenMode = mode;
|
video_mode: VideoMode::new(self.uiscreen.clone(), mode),
|
||||||
let mode = unsafe { Id::retain(mode as *mut UIScreenMode).unwrap() };
|
|
||||||
|
|
||||||
RootVideoMode {
|
|
||||||
video_mode: VideoMode::new(self.uiscreen.clone(), mode),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -222,7 +217,7 @@ fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 {
|
|||||||
|
|
||||||
// MonitorHandleExtIOS
|
// MonitorHandleExtIOS
|
||||||
impl Inner {
|
impl Inner {
|
||||||
pub(crate) fn ui_screen(&self) -> &Id<UIScreen, Shared> {
|
pub(crate) fn ui_screen(&self) -> &Id<UIScreen> {
|
||||||
&self.uiscreen
|
&self.uiscreen
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,10 +232,6 @@ impl Inner {
|
|||||||
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
||||||
UIScreen::screens(mtm)
|
UIScreen::screens(mtm)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|screen| {
|
.map(MonitorHandle::new)
|
||||||
let screen: *const UIScreen = screen;
|
|
||||||
let screen = unsafe { Id::retain(screen as *mut UIScreen).unwrap() };
|
|
||||||
MonitorHandle::new(screen)
|
|
||||||
})
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use objc2::foundation::{CGRect, MainThreadMarker, NSArray, NSObject};
|
use icrate::Foundation::{CGRect, MainThreadMarker, NSArray, NSObject};
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::rc::Id;
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||||
|
|
||||||
use super::{UIResponder, UIWindow};
|
use super::{UIResponder, UIWindow};
|
||||||
|
|
||||||
@@ -11,20 +11,21 @@ extern_class!(
|
|||||||
unsafe impl ClassType for UIApplication {
|
unsafe impl ClassType for UIApplication {
|
||||||
#[inherits(NSObject)]
|
#[inherits(NSObject)]
|
||||||
type Super = UIResponder;
|
type Super = UIResponder;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
extern_methods!(
|
extern_methods!(
|
||||||
unsafe impl UIApplication {
|
unsafe impl UIApplication {
|
||||||
pub fn shared(_mtm: MainThreadMarker) -> Option<Id<Self, Shared>> {
|
pub fn shared(_mtm: MainThreadMarker) -> Option<Id<Self>> {
|
||||||
unsafe { msg_send_id![Self::class(), sharedApplication] }
|
unsafe { msg_send_id![Self::class(), sharedApplication] }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn windows(&self) -> Id<NSArray<UIWindow, Shared>, Shared> {
|
pub fn windows(&self) -> Id<NSArray<UIWindow>> {
|
||||||
unsafe { msg_send_id![self, windows] }
|
unsafe { msg_send_id![self, windows] }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(statusBarFrame)]
|
#[method(statusBarFrame)]
|
||||||
pub fn statusBarFrame(&self) -> CGRect;
|
pub fn statusBarFrame(&self) -> CGRect;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use objc2::foundation::NSObject;
|
use icrate::Foundation::NSObject;
|
||||||
use objc2::{extern_class, ClassType};
|
use objc2::{extern_class, mutability, ClassType};
|
||||||
|
|
||||||
extern_class!(
|
extern_class!(
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
@@ -7,5 +7,6 @@ extern_class!(
|
|||||||
|
|
||||||
unsafe impl ClassType for UICoordinateSpace {
|
unsafe impl ClassType for UICoordinateSpace {
|
||||||
type Super = NSObject;
|
type Super = NSObject;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use objc2::foundation::{MainThreadMarker, NSObject};
|
use icrate::Foundation::{MainThreadMarker, NSObject};
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::rc::Id;
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||||
|
|
||||||
use super::super::ffi::UIUserInterfaceIdiom;
|
use super::super::ffi::UIUserInterfaceIdiom;
|
||||||
|
|
||||||
@@ -10,16 +10,17 @@ extern_class!(
|
|||||||
|
|
||||||
unsafe impl ClassType for UIDevice {
|
unsafe impl ClassType for UIDevice {
|
||||||
type Super = NSObject;
|
type Super = NSObject;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
extern_methods!(
|
extern_methods!(
|
||||||
unsafe impl UIDevice {
|
unsafe impl UIDevice {
|
||||||
pub fn current(_mtm: MainThreadMarker) -> Id<Self, Shared> {
|
pub fn current(_mtm: MainThreadMarker) -> Id<Self> {
|
||||||
unsafe { msg_send_id![Self::class(), currentDevice] }
|
unsafe { msg_send_id![Self::class(), currentDevice] }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(userInterfaceIdiom)]
|
#[method(userInterfaceIdiom)]
|
||||||
pub fn userInterfaceIdiom(&self) -> UIUserInterfaceIdiom;
|
pub fn userInterfaceIdiom(&self) -> UIUserInterfaceIdiom;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use objc2::foundation::NSObject;
|
use icrate::Foundation::NSObject;
|
||||||
use objc2::{extern_class, ClassType};
|
use objc2::{extern_class, mutability, ClassType};
|
||||||
|
|
||||||
extern_class!(
|
extern_class!(
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
@@ -7,5 +7,6 @@ extern_class!(
|
|||||||
|
|
||||||
unsafe impl ClassType for UIEvent {
|
unsafe impl ClassType for UIEvent {
|
||||||
type Super = NSObject;
|
type Super = NSObject;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
use objc2::foundation::NSString;
|
use icrate::Foundation::NSString;
|
||||||
|
|
||||||
mod application;
|
mod application;
|
||||||
mod coordinate_space;
|
mod coordinate_space;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use objc2::foundation::NSObject;
|
use icrate::Foundation::NSObject;
|
||||||
use objc2::{extern_class, ClassType};
|
use objc2::{extern_class, mutability, ClassType};
|
||||||
|
|
||||||
extern_class!(
|
extern_class!(
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
@@ -7,5 +7,6 @@ extern_class!(
|
|||||||
|
|
||||||
unsafe impl ClassType for UIResponder {
|
unsafe impl ClassType for UIResponder {
|
||||||
type Super = NSObject;
|
type Super = NSObject;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSArray, NSInteger, NSObject};
|
||||||
use objc2::encode::{Encode, Encoding};
|
use objc2::encode::{Encode, Encoding};
|
||||||
use objc2::foundation::{CGFloat, CGRect, MainThreadMarker, NSArray, NSInteger, NSObject};
|
use objc2::rc::Id;
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
|
||||||
|
|
||||||
use super::{UICoordinateSpace, UIScreenMode};
|
use super::{UICoordinateSpace, UIScreenMode};
|
||||||
|
|
||||||
@@ -11,53 +11,54 @@ extern_class!(
|
|||||||
|
|
||||||
unsafe impl ClassType for UIScreen {
|
unsafe impl ClassType for UIScreen {
|
||||||
type Super = NSObject;
|
type Super = NSObject;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
extern_methods!(
|
extern_methods!(
|
||||||
unsafe impl UIScreen {
|
unsafe impl UIScreen {
|
||||||
pub fn main(_mtm: MainThreadMarker) -> Id<Self, Shared> {
|
pub fn main(_mtm: MainThreadMarker) -> Id<Self> {
|
||||||
unsafe { msg_send_id![Self::class(), mainScreen] }
|
unsafe { msg_send_id![Self::class(), mainScreen] }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn screens(_mtm: MainThreadMarker) -> Id<NSArray<Self, Shared>, Shared> {
|
pub fn screens(_mtm: MainThreadMarker) -> Id<NSArray<Self>> {
|
||||||
unsafe { msg_send_id![Self::class(), screens] }
|
unsafe { msg_send_id![Self::class(), screens] }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(bounds)]
|
#[method(bounds)]
|
||||||
pub fn bounds(&self) -> CGRect;
|
pub fn bounds(&self) -> CGRect;
|
||||||
|
|
||||||
#[sel(scale)]
|
#[method(scale)]
|
||||||
pub fn scale(&self) -> CGFloat;
|
pub fn scale(&self) -> CGFloat;
|
||||||
|
|
||||||
#[sel(nativeBounds)]
|
#[method(nativeBounds)]
|
||||||
pub fn nativeBounds(&self) -> CGRect;
|
pub fn nativeBounds(&self) -> CGRect;
|
||||||
|
|
||||||
#[sel(nativeScale)]
|
#[method(nativeScale)]
|
||||||
pub fn nativeScale(&self) -> CGFloat;
|
pub fn nativeScale(&self) -> CGFloat;
|
||||||
|
|
||||||
#[sel(maximumFramesPerSecond)]
|
#[method(maximumFramesPerSecond)]
|
||||||
pub fn maximumFramesPerSecond(&self) -> NSInteger;
|
pub fn maximumFramesPerSecond(&self) -> NSInteger;
|
||||||
|
|
||||||
pub fn mirroredScreen(&self) -> Id<Self, Shared> {
|
pub fn mirroredScreen(&self) -> Id<Self> {
|
||||||
unsafe { msg_send_id![Self::class(), mirroredScreen] }
|
unsafe { msg_send_id![Self::class(), mirroredScreen] }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preferredMode(&self) -> Option<Id<UIScreenMode, Shared>> {
|
pub fn preferredMode(&self) -> Option<Id<UIScreenMode>> {
|
||||||
unsafe { msg_send_id![self, preferredMode] }
|
unsafe { msg_send_id![self, preferredMode] }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setCurrentMode:)]
|
#[method(setCurrentMode:)]
|
||||||
pub fn setCurrentMode(&self, mode: Option<&UIScreenMode>);
|
pub fn setCurrentMode(&self, mode: Option<&UIScreenMode>);
|
||||||
|
|
||||||
pub fn availableModes(&self) -> Id<NSArray<UIScreenMode, Shared>, Shared> {
|
pub fn availableModes(&self) -> Id<NSArray<UIScreenMode>> {
|
||||||
unsafe { msg_send_id![self, availableModes] }
|
unsafe { msg_send_id![self, availableModes] }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setOverscanCompensation:)]
|
#[method(setOverscanCompensation:)]
|
||||||
pub fn setOverscanCompensation(&self, overscanCompensation: UIScreenOverscanCompensation);
|
pub fn setOverscanCompensation(&self, overscanCompensation: UIScreenOverscanCompensation);
|
||||||
|
|
||||||
pub fn coordinateSpace(&self) -> Id<UICoordinateSpace, Shared> {
|
pub fn coordinateSpace(&self) -> Id<UICoordinateSpace> {
|
||||||
unsafe { msg_send_id![self, coordinateSpace] }
|
unsafe { msg_send_id![self, coordinateSpace] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use objc2::foundation::{CGSize, NSObject};
|
use icrate::Foundation::{CGSize, NSObject};
|
||||||
use objc2::{extern_class, extern_methods, ClassType};
|
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||||
|
|
||||||
extern_class!(
|
extern_class!(
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
@@ -7,12 +7,13 @@ extern_class!(
|
|||||||
|
|
||||||
unsafe impl ClassType for UIScreenMode {
|
unsafe impl ClassType for UIScreenMode {
|
||||||
type Super = NSObject;
|
type Super = NSObject;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
extern_methods!(
|
extern_methods!(
|
||||||
unsafe impl UIScreenMode {
|
unsafe impl UIScreenMode {
|
||||||
#[sel(size)]
|
#[method(size)]
|
||||||
pub fn size(&self) -> CGSize;
|
pub fn size(&self) -> CGSize;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use icrate::Foundation::{CGFloat, CGPoint, NSInteger, NSObject};
|
||||||
use objc2::encode::{Encode, Encoding};
|
use objc2::encode::{Encode, Encoding};
|
||||||
use objc2::foundation::{CGFloat, CGPoint, NSInteger, NSObject};
|
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||||
use objc2::{extern_class, extern_methods, ClassType};
|
|
||||||
|
|
||||||
use super::UIView;
|
use super::UIView;
|
||||||
|
|
||||||
@@ -10,27 +10,28 @@ extern_class!(
|
|||||||
|
|
||||||
unsafe impl ClassType for UITouch {
|
unsafe impl ClassType for UITouch {
|
||||||
type Super = NSObject;
|
type Super = NSObject;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
extern_methods!(
|
extern_methods!(
|
||||||
unsafe impl UITouch {
|
unsafe impl UITouch {
|
||||||
#[sel(locationInView:)]
|
#[method(locationInView:)]
|
||||||
pub fn locationInView(&self, view: Option<&UIView>) -> CGPoint;
|
pub fn locationInView(&self, view: Option<&UIView>) -> CGPoint;
|
||||||
|
|
||||||
#[sel(type)]
|
#[method(type)]
|
||||||
pub fn type_(&self) -> UITouchType;
|
pub fn type_(&self) -> UITouchType;
|
||||||
|
|
||||||
#[sel(force)]
|
#[method(force)]
|
||||||
pub fn force(&self) -> CGFloat;
|
pub fn force(&self) -> CGFloat;
|
||||||
|
|
||||||
#[sel(maximumPossibleForce)]
|
#[method(maximumPossibleForce)]
|
||||||
pub fn maximumPossibleForce(&self) -> CGFloat;
|
pub fn maximumPossibleForce(&self) -> CGFloat;
|
||||||
|
|
||||||
#[sel(altitudeAngle)]
|
#[method(altitudeAngle)]
|
||||||
pub fn altitudeAngle(&self) -> CGFloat;
|
pub fn altitudeAngle(&self) -> CGFloat;
|
||||||
|
|
||||||
#[sel(phase)]
|
#[method(phase)]
|
||||||
pub fn phase(&self) -> UITouchPhase;
|
pub fn phase(&self) -> UITouchPhase;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use icrate::Foundation::{NSInteger, NSObject};
|
||||||
use objc2::encode::{Encode, Encoding};
|
use objc2::encode::{Encode, Encoding};
|
||||||
use objc2::foundation::{NSInteger, NSObject};
|
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||||
use objc2::{extern_class, extern_methods, ClassType};
|
|
||||||
|
|
||||||
extern_class!(
|
extern_class!(
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
@@ -8,12 +8,13 @@ extern_class!(
|
|||||||
|
|
||||||
unsafe impl ClassType for UITraitCollection {
|
unsafe impl ClassType for UITraitCollection {
|
||||||
type Super = NSObject;
|
type Super = NSObject;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
extern_methods!(
|
extern_methods!(
|
||||||
unsafe impl UITraitCollection {
|
unsafe impl UITraitCollection {
|
||||||
#[sel(forceTouchCapability)]
|
#[method(forceTouchCapability)]
|
||||||
pub fn forceTouchCapability(&self) -> UIForceTouchCapability;
|
pub fn forceTouchCapability(&self) -> UIForceTouchCapability;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
use icrate::Foundation::{CGFloat, CGRect, NSObject};
|
||||||
use objc2::encode::{Encode, Encoding};
|
use objc2::encode::{Encode, Encoding};
|
||||||
use objc2::foundation::{CGFloat, CGRect, NSObject};
|
use objc2::rc::Id;
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
|
||||||
|
|
||||||
use super::{UICoordinateSpace, UIResponder, UIViewController};
|
use super::{UICoordinateSpace, UIResponder, UIViewController};
|
||||||
|
|
||||||
@@ -12,57 +12,58 @@ extern_class!(
|
|||||||
unsafe impl ClassType for UIView {
|
unsafe impl ClassType for UIView {
|
||||||
#[inherits(NSObject)]
|
#[inherits(NSObject)]
|
||||||
type Super = UIResponder;
|
type Super = UIResponder;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
extern_methods!(
|
extern_methods!(
|
||||||
unsafe impl UIView {
|
unsafe impl UIView {
|
||||||
#[sel(bounds)]
|
#[method(bounds)]
|
||||||
pub fn bounds(&self) -> CGRect;
|
pub fn bounds(&self) -> CGRect;
|
||||||
|
|
||||||
#[sel(setBounds:)]
|
#[method(setBounds:)]
|
||||||
pub fn setBounds(&self, value: CGRect);
|
pub fn setBounds(&self, value: CGRect);
|
||||||
|
|
||||||
#[sel(frame)]
|
#[method(frame)]
|
||||||
pub fn frame(&self) -> CGRect;
|
pub fn frame(&self) -> CGRect;
|
||||||
|
|
||||||
#[sel(setFrame:)]
|
#[method(setFrame:)]
|
||||||
pub fn setFrame(&self, value: CGRect);
|
pub fn setFrame(&self, value: CGRect);
|
||||||
|
|
||||||
#[sel(contentScaleFactor)]
|
#[method(contentScaleFactor)]
|
||||||
pub fn contentScaleFactor(&self) -> CGFloat;
|
pub fn contentScaleFactor(&self) -> CGFloat;
|
||||||
|
|
||||||
#[sel(setContentScaleFactor:)]
|
#[method(setContentScaleFactor:)]
|
||||||
pub fn setContentScaleFactor(&self, val: CGFloat);
|
pub fn setContentScaleFactor(&self, val: CGFloat);
|
||||||
|
|
||||||
#[sel(setMultipleTouchEnabled:)]
|
#[method(setMultipleTouchEnabled:)]
|
||||||
pub fn setMultipleTouchEnabled(&self, val: bool);
|
pub fn setMultipleTouchEnabled(&self, val: bool);
|
||||||
|
|
||||||
pub fn rootViewController(&self) -> Option<Id<UIViewController, Shared>> {
|
pub fn rootViewController(&self) -> Option<Id<UIViewController>> {
|
||||||
unsafe { msg_send_id![self, rootViewController] }
|
unsafe { msg_send_id![self, rootViewController] }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setRootViewController:)]
|
#[method(setRootViewController:)]
|
||||||
pub fn setRootViewController(&self, rootViewController: Option<&UIViewController>);
|
pub fn setRootViewController(&self, rootViewController: Option<&UIViewController>);
|
||||||
|
|
||||||
#[sel(convertRect:toCoordinateSpace:)]
|
#[method(convertRect:toCoordinateSpace:)]
|
||||||
pub fn convertRect_toCoordinateSpace(
|
pub fn convertRect_toCoordinateSpace(
|
||||||
&self,
|
&self,
|
||||||
rect: CGRect,
|
rect: CGRect,
|
||||||
coordinateSpace: &UICoordinateSpace,
|
coordinateSpace: &UICoordinateSpace,
|
||||||
) -> CGRect;
|
) -> CGRect;
|
||||||
|
|
||||||
#[sel(convertRect:fromCoordinateSpace:)]
|
#[method(convertRect:fromCoordinateSpace:)]
|
||||||
pub fn convertRect_fromCoordinateSpace(
|
pub fn convertRect_fromCoordinateSpace(
|
||||||
&self,
|
&self,
|
||||||
rect: CGRect,
|
rect: CGRect,
|
||||||
coordinateSpace: &UICoordinateSpace,
|
coordinateSpace: &UICoordinateSpace,
|
||||||
) -> CGRect;
|
) -> CGRect;
|
||||||
|
|
||||||
#[sel(safeAreaInsets)]
|
#[method(safeAreaInsets)]
|
||||||
pub fn safeAreaInsets(&self) -> UIEdgeInsets;
|
pub fn safeAreaInsets(&self) -> UIEdgeInsets;
|
||||||
|
|
||||||
#[sel(setNeedsDisplay)]
|
#[method(setNeedsDisplay)]
|
||||||
pub fn setNeedsDisplay(&self);
|
pub fn setNeedsDisplay(&self);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
use icrate::Foundation::{NSObject, NSUInteger};
|
||||||
use objc2::encode::{Encode, Encoding};
|
use objc2::encode::{Encode, Encoding};
|
||||||
use objc2::foundation::{NSObject, NSUInteger};
|
use objc2::rc::Id;
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
|
||||||
|
|
||||||
use super::{UIResponder, UIView};
|
use super::{UIResponder, UIView};
|
||||||
|
|
||||||
@@ -12,28 +12,29 @@ extern_class!(
|
|||||||
unsafe impl ClassType for UIViewController {
|
unsafe impl ClassType for UIViewController {
|
||||||
#[inherits(NSObject)]
|
#[inherits(NSObject)]
|
||||||
type Super = UIResponder;
|
type Super = UIResponder;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
extern_methods!(
|
extern_methods!(
|
||||||
unsafe impl UIViewController {
|
unsafe impl UIViewController {
|
||||||
#[sel(attemptRotationToDeviceOrientation)]
|
#[method(attemptRotationToDeviceOrientation)]
|
||||||
pub fn attemptRotationToDeviceOrientation();
|
pub fn attemptRotationToDeviceOrientation();
|
||||||
|
|
||||||
#[sel(setNeedsStatusBarAppearanceUpdate)]
|
#[method(setNeedsStatusBarAppearanceUpdate)]
|
||||||
pub fn setNeedsStatusBarAppearanceUpdate(&self);
|
pub fn setNeedsStatusBarAppearanceUpdate(&self);
|
||||||
|
|
||||||
#[sel(setNeedsUpdateOfHomeIndicatorAutoHidden)]
|
#[method(setNeedsUpdateOfHomeIndicatorAutoHidden)]
|
||||||
pub fn setNeedsUpdateOfHomeIndicatorAutoHidden(&self);
|
pub fn setNeedsUpdateOfHomeIndicatorAutoHidden(&self);
|
||||||
|
|
||||||
#[sel(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]
|
#[method(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]
|
||||||
pub fn setNeedsUpdateOfScreenEdgesDeferringSystemGestures(&self);
|
pub fn setNeedsUpdateOfScreenEdgesDeferringSystemGestures(&self);
|
||||||
|
|
||||||
pub fn view(&self) -> Option<Id<UIView, Shared>> {
|
pub fn view(&self) -> Option<Id<UIView>> {
|
||||||
unsafe { msg_send_id![self, view] }
|
unsafe { msg_send_id![self, view] }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setView:)]
|
#[method(setView:)]
|
||||||
pub fn setView(&self, view: Option<&UIView>);
|
pub fn setView(&self, view: Option<&UIView>);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use objc2::foundation::NSObject;
|
use icrate::Foundation::NSObject;
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::rc::Id;
|
||||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||||
|
|
||||||
use super::{UIResponder, UIScreen, UIView};
|
use super::{UIResponder, UIScreen, UIView};
|
||||||
|
|
||||||
@@ -11,25 +11,26 @@ extern_class!(
|
|||||||
unsafe impl ClassType for UIWindow {
|
unsafe impl ClassType for UIWindow {
|
||||||
#[inherits(UIResponder, NSObject)]
|
#[inherits(UIResponder, NSObject)]
|
||||||
type Super = UIView;
|
type Super = UIView;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
extern_methods!(
|
extern_methods!(
|
||||||
unsafe impl UIWindow {
|
unsafe impl UIWindow {
|
||||||
pub fn screen(&self) -> Id<UIScreen, Shared> {
|
pub fn screen(&self) -> Id<UIScreen> {
|
||||||
unsafe { msg_send_id![self, screen] }
|
unsafe { msg_send_id![self, screen] }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setScreen:)]
|
#[method(setScreen:)]
|
||||||
pub fn setScreen(&self, screen: &UIScreen);
|
pub fn setScreen(&self, screen: &UIScreen);
|
||||||
|
|
||||||
#[sel(setHidden:)]
|
#[method(setHidden:)]
|
||||||
pub fn setHidden(&self, flag: bool);
|
pub fn setHidden(&self, flag: bool);
|
||||||
|
|
||||||
#[sel(makeKeyAndVisible)]
|
#[method(makeKeyAndVisible)]
|
||||||
pub fn makeKeyAndVisible(&self);
|
pub fn makeKeyAndVisible(&self);
|
||||||
|
|
||||||
#[sel(isKeyWindow)]
|
#[method(isKeyWindow)]
|
||||||
pub fn isKeyWindow(&self) -> bool;
|
pub fn isKeyWindow(&self) -> bool;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#![allow(clippy::unnecessary_cast)]
|
#![allow(clippy::unnecessary_cast)]
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use objc2::foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSSet};
|
use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet};
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::declare::{Ivar, IvarDrop};
|
||||||
use objc2::runtime::Class;
|
use objc2::rc::Id;
|
||||||
use objc2::{declare_class, extern_methods, msg_send, msg_send_id, ClassType};
|
use objc2::runtime::AnyClass;
|
||||||
|
use objc2::{declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType};
|
||||||
|
|
||||||
use super::uikit::{
|
use super::uikit::{
|
||||||
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIInterfaceOrientationMask,
|
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIInterfaceOrientationMask,
|
||||||
@@ -26,32 +29,28 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
pub(crate) struct WinitView {}
|
pub(crate) struct WinitView;
|
||||||
|
|
||||||
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;
|
||||||
const NAME: &'static str = "WinitUIView";
|
const NAME: &'static str = "WinitUIView";
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl WinitView {
|
unsafe impl WinitView {
|
||||||
#[sel(drawRect:)]
|
#[method(drawRect:)]
|
||||||
fn draw_rect(&self, rect: CGRect) {
|
fn draw_rect(&self, rect: CGRect) {
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
unsafe {
|
unsafe {
|
||||||
app_state::handle_nonuser_events(
|
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawRequested(
|
||||||
std::iter::once(EventWrapper::StaticEvent(Event::RedrawRequested(
|
RootWindowId(window.id()),
|
||||||
RootWindowId(window.id()),
|
)));
|
||||||
)))
|
|
||||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
|
||||||
Event::RedrawEventsCleared,
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
let _: () = unsafe { msg_send![super(self), drawRect: rect] };
|
let _: () = unsafe { msg_send![super(self), drawRect: rect] };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(layoutSubviews)]
|
#[method(layoutSubviews)]
|
||||||
fn layout_subviews(&self) {
|
fn layout_subviews(&self) {
|
||||||
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
|
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
|
||||||
|
|
||||||
@@ -83,7 +82,7 @@ declare_class!(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setContentScaleFactor:)]
|
#[method(setContentScaleFactor:)]
|
||||||
fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) {
|
fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) {
|
||||||
let _: () =
|
let _: () =
|
||||||
unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] };
|
unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] };
|
||||||
@@ -133,22 +132,22 @@ declare_class!(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(touchesBegan:withEvent:)]
|
#[method(touchesBegan:withEvent:)]
|
||||||
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||||
self.handle_touches(touches)
|
self.handle_touches(touches)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(touchesMoved:withEvent:)]
|
#[method(touchesMoved:withEvent:)]
|
||||||
fn touches_moved(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
fn touches_moved(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||||
self.handle_touches(touches)
|
self.handle_touches(touches)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(touchesEnded:withEvent:)]
|
#[method(touchesEnded:withEvent:)]
|
||||||
fn touches_ended(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
fn touches_ended(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||||
self.handle_touches(touches)
|
self.handle_touches(touches)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(touchesCancelled:withEvent:)]
|
#[method(touchesCancelled:withEvent:)]
|
||||||
fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||||
self.handle_touches(touches)
|
self.handle_touches(touches)
|
||||||
}
|
}
|
||||||
@@ -158,17 +157,17 @@ declare_class!(
|
|||||||
extern_methods!(
|
extern_methods!(
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
unsafe impl WinitView {
|
unsafe impl WinitView {
|
||||||
fn window(&self) -> Option<Id<WinitUIWindow, Shared>> {
|
fn window(&self) -> Option<Id<WinitUIWindow>> {
|
||||||
unsafe { msg_send_id![self, window] }
|
unsafe { msg_send_id![self, window] }
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn traitCollection(&self) -> Id<UITraitCollection, Shared> {
|
unsafe fn traitCollection(&self) -> Id<UITraitCollection> {
|
||||||
msg_send_id![self, traitCollection]
|
msg_send_id![self, traitCollection]
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Allow the user to customize this
|
// TODO: Allow the user to customize this
|
||||||
#[sel(layerClass)]
|
#[method(layerClass)]
|
||||||
pub(crate) fn layerClass() -> &'static Class;
|
pub(crate) fn layerClass() -> &'static AnyClass;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -178,9 +177,8 @@ impl WinitView {
|
|||||||
_window_attributes: &WindowAttributes,
|
_window_attributes: &WindowAttributes,
|
||||||
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
||||||
frame: CGRect,
|
frame: CGRect,
|
||||||
) -> Id<Self, Shared> {
|
) -> Id<Self> {
|
||||||
let this: Id<Self, Shared> =
|
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] };
|
||||||
unsafe { msg_send_id![msg_send_id![Self::class(), alloc], initWithFrame: frame] };
|
|
||||||
|
|
||||||
this.setMultipleTouchEnabled(true);
|
this.setMultipleTouchEnabled(true);
|
||||||
|
|
||||||
@@ -203,7 +201,9 @@ impl WinitView {
|
|||||||
let trait_collection = unsafe { self.traitCollection() };
|
let trait_collection = unsafe { 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
|
||||||
|
|| touch_type == UITouchType::Pencil
|
||||||
|
{
|
||||||
let force = touch.force();
|
let force = touch.force();
|
||||||
let max_possible_force = touch.maximumPossibleForce();
|
let max_possible_force = touch.maximumPossibleForce();
|
||||||
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
|
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
|
||||||
@@ -260,102 +260,108 @@ impl WinitView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ViewControllerState {
|
||||||
|
prefers_status_bar_hidden: Cell<bool>,
|
||||||
|
prefers_home_indicator_auto_hidden: Cell<bool>,
|
||||||
|
supported_orientations: Cell<UIInterfaceOrientationMask>,
|
||||||
|
preferred_screen_edges_deferring_system_gestures: Cell<UIRectEdge>,
|
||||||
|
}
|
||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
pub(crate) struct WinitViewController {
|
pub(crate) struct WinitViewController {
|
||||||
_prefers_status_bar_hidden: bool,
|
state: IvarDrop<Box<ViewControllerState>, "_state">,
|
||||||
_prefers_home_indicator_auto_hidden: bool,
|
|
||||||
_supported_orientations: UIInterfaceOrientationMask,
|
|
||||||
_preferred_screen_edges_deferring_system_gestures: UIRectEdge,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod view_controller_ivars;
|
||||||
|
|
||||||
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;
|
||||||
const NAME: &'static str = "WinitUIViewController";
|
const NAME: &'static str = "WinitUIViewController";
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl WinitViewController {
|
unsafe impl WinitViewController {
|
||||||
#[sel(shouldAutorotate)]
|
#[method(init)]
|
||||||
|
unsafe fn init(this: *mut Self) -> Option<NonNull<Self>> {
|
||||||
|
let this: Option<&mut Self> = msg_send![super(this), init];
|
||||||
|
this.map(|this| {
|
||||||
|
// These are set in WinitViewController::new, it's just to set them
|
||||||
|
// to _something_.
|
||||||
|
Ivar::write(
|
||||||
|
&mut this.state,
|
||||||
|
Box::new(ViewControllerState {
|
||||||
|
prefers_status_bar_hidden: Cell::new(false),
|
||||||
|
prefers_home_indicator_auto_hidden: Cell::new(false),
|
||||||
|
supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
|
||||||
|
preferred_screen_edges_deferring_system_gestures: Cell::new(
|
||||||
|
UIRectEdge::NONE,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
NonNull::from(this)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl WinitViewController {
|
||||||
|
#[method(shouldAutorotate)]
|
||||||
fn should_autorotate(&self) -> bool {
|
fn should_autorotate(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl WinitViewController {
|
#[method(prefersStatusBarHidden)]
|
||||||
#[sel(prefersStatusBarHidden)]
|
|
||||||
fn prefers_status_bar_hidden(&self) -> bool {
|
fn prefers_status_bar_hidden(&self) -> bool {
|
||||||
*self._prefers_status_bar_hidden
|
self.state.prefers_status_bar_hidden.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setPrefersStatusBarHidden:)]
|
#[method(prefersHomeIndicatorAutoHidden)]
|
||||||
fn set_prefers_status_bar_hidden(&mut self, val: bool) {
|
|
||||||
*self._prefers_status_bar_hidden = val;
|
|
||||||
self.setNeedsStatusBarAppearanceUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[sel(prefersHomeIndicatorAutoHidden)]
|
|
||||||
fn prefers_home_indicator_auto_hidden(&self) -> bool {
|
fn prefers_home_indicator_auto_hidden(&self) -> bool {
|
||||||
*self._prefers_home_indicator_auto_hidden
|
self.state.prefers_home_indicator_auto_hidden.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setPrefersHomeIndicatorAutoHidden:)]
|
#[method(supportedInterfaceOrientations)]
|
||||||
fn set_prefers_home_indicator_auto_hidden(&mut self, val: bool) {
|
|
||||||
*self._prefers_home_indicator_auto_hidden = val;
|
|
||||||
let os_capabilities = app_state::os_capabilities();
|
|
||||||
if os_capabilities.home_indicator_hidden {
|
|
||||||
self.setNeedsUpdateOfHomeIndicatorAutoHidden();
|
|
||||||
} else {
|
|
||||||
os_capabilities.home_indicator_hidden_err_msg("ignoring")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[sel(supportedInterfaceOrientations)]
|
|
||||||
fn supported_orientations(&self) -> UIInterfaceOrientationMask {
|
fn supported_orientations(&self) -> UIInterfaceOrientationMask {
|
||||||
*self._supported_orientations
|
self.state.supported_orientations.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setSupportedInterfaceOrientations:)]
|
#[method(preferredScreenEdgesDeferringSystemGestures)]
|
||||||
fn set_supported_orientations(&mut self, val: UIInterfaceOrientationMask) {
|
|
||||||
*self._supported_orientations = val;
|
|
||||||
UIViewController::attemptRotationToDeviceOrientation();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[sel(preferredScreenEdgesDeferringSystemGestures)]
|
|
||||||
fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
|
fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
|
||||||
*self._preferred_screen_edges_deferring_system_gestures
|
self.state
|
||||||
|
.preferred_screen_edges_deferring_system_gestures
|
||||||
|
.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(setPreferredScreenEdgesDeferringSystemGestures:)]
|
|
||||||
fn set_preferred_screen_edges_deferring_system_gestures(&mut self, val: UIRectEdge) {
|
|
||||||
*self._preferred_screen_edges_deferring_system_gestures = val;
|
|
||||||
let os_capabilities = app_state::os_capabilities();
|
|
||||||
if os_capabilities.defer_system_gestures {
|
|
||||||
self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures();
|
|
||||||
} else {
|
|
||||||
os_capabilities.defer_system_gestures_err_msg("ignoring")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
extern_methods!(
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe impl WinitViewController {
|
|
||||||
#[sel(setPrefersStatusBarHidden:)]
|
|
||||||
pub(crate) fn setPrefersStatusBarHidden(&self, flag: bool);
|
|
||||||
|
|
||||||
#[sel(setSupportedInterfaceOrientations:)]
|
|
||||||
pub(crate) fn setSupportedInterfaceOrientations(&self, val: UIInterfaceOrientationMask);
|
|
||||||
|
|
||||||
#[sel(setPrefersHomeIndicatorAutoHidden:)]
|
|
||||||
pub(crate) fn setPrefersHomeIndicatorAutoHidden(&self, val: bool);
|
|
||||||
|
|
||||||
#[sel(setPreferredScreenEdgesDeferringSystemGestures:)]
|
|
||||||
pub(crate) fn setPreferredScreenEdgesDeferringSystemGestures(&self, val: UIRectEdge);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
impl WinitViewController {
|
impl WinitViewController {
|
||||||
|
pub(crate) fn set_prefers_status_bar_hidden(&self, val: bool) {
|
||||||
|
self.state.prefers_status_bar_hidden.set(val);
|
||||||
|
self.setNeedsStatusBarAppearanceUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_prefers_home_indicator_auto_hidden(&self, val: bool) {
|
||||||
|
self.state.prefers_home_indicator_auto_hidden.set(val);
|
||||||
|
let os_capabilities = app_state::os_capabilities();
|
||||||
|
if os_capabilities.home_indicator_hidden {
|
||||||
|
self.setNeedsUpdateOfHomeIndicatorAutoHidden();
|
||||||
|
} else {
|
||||||
|
os_capabilities.home_indicator_hidden_err_msg("ignoring")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_preferred_screen_edges_deferring_system_gestures(&self, val: UIRectEdge) {
|
||||||
|
self.state
|
||||||
|
.preferred_screen_edges_deferring_system_gestures
|
||||||
|
.set(val);
|
||||||
|
let os_capabilities = app_state::os_capabilities();
|
||||||
|
if os_capabilities.defer_system_gestures {
|
||||||
|
self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures();
|
||||||
|
} else {
|
||||||
|
os_capabilities.defer_system_gestures_err_msg("ignoring")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn set_supported_interface_orientations(
|
pub(crate) fn set_supported_interface_orientations(
|
||||||
&self,
|
&self,
|
||||||
mtm: MainThreadMarker,
|
mtm: MainThreadMarker,
|
||||||
@@ -378,7 +384,8 @@ impl WinitViewController {
|
|||||||
| UIInterfaceOrientationMask::PortraitUpsideDown
|
| UIInterfaceOrientationMask::PortraitUpsideDown
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.setSupportedInterfaceOrientations(mask);
|
self.state.supported_orientations.set(mask);
|
||||||
|
UIViewController::attemptRotationToDeviceOrientation();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
@@ -386,17 +393,18 @@ impl WinitViewController {
|
|||||||
_window_attributes: &WindowAttributes,
|
_window_attributes: &WindowAttributes,
|
||||||
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
||||||
view: &UIView,
|
view: &UIView,
|
||||||
) -> Id<Self, Shared> {
|
) -> Id<Self> {
|
||||||
let this: Id<Self, Shared> =
|
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), init] };
|
||||||
unsafe { msg_send_id![msg_send_id![Self::class(), alloc], init] };
|
|
||||||
|
|
||||||
this.setPrefersStatusBarHidden(platform_attributes.prefers_status_bar_hidden);
|
this.set_prefers_status_bar_hidden(platform_attributes.prefers_status_bar_hidden);
|
||||||
|
|
||||||
this.set_supported_interface_orientations(mtm, platform_attributes.valid_orientations);
|
this.set_supported_interface_orientations(mtm, platform_attributes.valid_orientations);
|
||||||
|
|
||||||
this.setPrefersHomeIndicatorAutoHidden(platform_attributes.prefers_home_indicator_hidden);
|
this.set_prefers_home_indicator_auto_hidden(
|
||||||
|
platform_attributes.prefers_home_indicator_hidden,
|
||||||
|
);
|
||||||
|
|
||||||
this.setPreferredScreenEdgesDeferringSystemGestures(
|
this.set_preferred_screen_edges_deferring_system_gestures(
|
||||||
platform_attributes
|
platform_attributes
|
||||||
.preferred_screen_edges_deferring_system_gestures
|
.preferred_screen_edges_deferring_system_gestures
|
||||||
.into(),
|
.into(),
|
||||||
@@ -410,15 +418,17 @@ impl WinitViewController {
|
|||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub(crate) struct WinitUIWindow {}
|
pub(crate) struct WinitUIWindow;
|
||||||
|
|
||||||
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;
|
||||||
|
const NAME: &'static str = "WinitUIWindow";
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl WinitUIWindow {
|
unsafe impl WinitUIWindow {
|
||||||
#[sel(becomeKeyWindow)]
|
#[method(becomeKeyWindow)]
|
||||||
fn become_key_window(&self) {
|
fn become_key_window(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
@@ -429,7 +439,7 @@ declare_class!(
|
|||||||
let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
|
let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(resignKeyWindow)]
|
#[method(resignKeyWindow)]
|
||||||
fn resign_key_window(&self) {
|
fn resign_key_window(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
@@ -449,9 +459,8 @@ impl WinitUIWindow {
|
|||||||
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
||||||
frame: CGRect,
|
frame: CGRect,
|
||||||
view_controller: &UIViewController,
|
view_controller: &UIViewController,
|
||||||
) -> Id<Self, Shared> {
|
) -> Id<Self> {
|
||||||
let this: Id<Self, Shared> =
|
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] };
|
||||||
unsafe { msg_send_id![msg_send_id![Self::class(), alloc], initWithFrame: frame] };
|
|
||||||
|
|
||||||
this.setRootViewController(Some(view_controller));
|
this.setRootViewController(Some(view_controller));
|
||||||
|
|
||||||
@@ -478,15 +487,17 @@ impl WinitUIWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare_class!(
|
declare_class!(
|
||||||
pub struct WinitApplicationDelegate {}
|
pub struct WinitApplicationDelegate;
|
||||||
|
|
||||||
unsafe impl ClassType for WinitApplicationDelegate {
|
unsafe impl ClassType for WinitApplicationDelegate {
|
||||||
type Super = NSObject;
|
type Super = NSObject;
|
||||||
|
type Mutability = mutability::InteriorMutable;
|
||||||
|
const NAME: &'static str = "WinitApplicationDelegate";
|
||||||
}
|
}
|
||||||
|
|
||||||
// UIApplicationDelegate protocol
|
// UIApplicationDelegate protocol
|
||||||
unsafe impl WinitApplicationDelegate {
|
unsafe impl WinitApplicationDelegate {
|
||||||
#[sel(application:didFinishLaunchingWithOptions:)]
|
#[method(application:didFinishLaunchingWithOptions:)]
|
||||||
fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
|
fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
app_state::did_finish_launching();
|
app_state::did_finish_launching();
|
||||||
@@ -494,22 +505,23 @@ declare_class!(
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(applicationDidBecomeActive:)]
|
#[method(applicationDidBecomeActive:)]
|
||||||
fn did_become_active(&self, _application: &UIApplication) {
|
fn did_become_active(&self, _application: &UIApplication) {
|
||||||
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
|
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(applicationWillResignActive:)]
|
#[method(applicationWillResignActive:)]
|
||||||
fn will_resign_active(&self, _application: &UIApplication) {
|
fn will_resign_active(&self, _application: &UIApplication) {
|
||||||
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
|
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sel(applicationWillEnterForeground:)]
|
#[method(applicationWillEnterForeground:)]
|
||||||
fn will_enter_foreground(&self, _application: &UIApplication) {}
|
fn will_enter_foreground(&self, _application: &UIApplication) {}
|
||||||
#[sel(applicationDidEnterBackground:)]
|
|
||||||
|
#[method(applicationDidEnterBackground:)]
|
||||||
fn did_enter_background(&self, _application: &UIApplication) {}
|
fn did_enter_background(&self, _application: &UIApplication) {}
|
||||||
|
|
||||||
#[sel(applicationWillTerminate:)]
|
#[method(applicationWillTerminate:)]
|
||||||
fn will_terminate(&self, application: &UIApplication) {
|
fn will_terminate(&self, application: &UIApplication) {
|
||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
for window in application.windows().iter() {
|
for window in application.windows().iter() {
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
#![allow(clippy::unnecessary_cast)]
|
#![allow(clippy::unnecessary_cast)]
|
||||||
|
|
||||||
use std::{
|
use std::collections::VecDeque;
|
||||||
collections::VecDeque,
|
|
||||||
ffi::c_void,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
use objc2::foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadMarker};
|
use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
|
||||||
use objc2::rc::{Id, Shared};
|
use objc2::rc::Id;
|
||||||
use objc2::runtime::Object;
|
use objc2::runtime::AnyObject;
|
||||||
use objc2::{class, msg_send};
|
use objc2::{class, msg_send};
|
||||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, UiKitDisplayHandle, UiKitWindowHandle};
|
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, UiKitDisplayHandle, UiKitWindowHandle};
|
||||||
|
|
||||||
@@ -23,7 +19,6 @@ use crate::{
|
|||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
app_state,
|
app_state,
|
||||||
event_loop::{EventProxy, EventWrapper},
|
event_loop::{EventProxy, EventWrapper},
|
||||||
ffi::UIRectEdge,
|
|
||||||
monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
|
monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
|
||||||
},
|
},
|
||||||
window::{
|
window::{
|
||||||
@@ -33,9 +28,9 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub struct Inner {
|
pub struct Inner {
|
||||||
pub(crate) window: Id<WinitUIWindow, Shared>,
|
window: Id<WinitUIWindow>,
|
||||||
pub(crate) view_controller: Id<WinitViewController, Shared>,
|
view_controller: Id<WinitViewController>,
|
||||||
pub(crate) view: Id<WinitView, Shared>,
|
view: Id<WinitView>,
|
||||||
gl_or_metal_backed: bool,
|
gl_or_metal_backed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,73 +70,65 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pre_present_notify(&self) {}
|
||||||
|
|
||||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||||
unsafe {
|
let safe_area = self.safe_area_screen_space();
|
||||||
let safe_area = self.safe_area_screen_space();
|
let position = LogicalPosition {
|
||||||
let position = LogicalPosition {
|
x: safe_area.origin.x as f64,
|
||||||
x: safe_area.origin.x as f64,
|
y: safe_area.origin.y as f64,
|
||||||
y: safe_area.origin.y as f64,
|
};
|
||||||
};
|
let scale_factor = self.scale_factor();
|
||||||
let scale_factor = self.scale_factor();
|
Ok(position.to_physical(scale_factor))
|
||||||
Ok(position.to_physical(scale_factor))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||||
unsafe {
|
let screen_frame = self.screen_frame();
|
||||||
let screen_frame = self.screen_frame();
|
let position = LogicalPosition {
|
||||||
let position = LogicalPosition {
|
x: screen_frame.origin.x as f64,
|
||||||
x: screen_frame.origin.x as f64,
|
y: screen_frame.origin.y as f64,
|
||||||
y: screen_frame.origin.y as f64,
|
};
|
||||||
};
|
let scale_factor = self.scale_factor();
|
||||||
let scale_factor = self.scale_factor();
|
Ok(position.to_physical(scale_factor))
|
||||||
Ok(position.to_physical(scale_factor))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_outer_position(&self, physical_position: Position) {
|
pub fn set_outer_position(&self, physical_position: Position) {
|
||||||
unsafe {
|
let scale_factor = self.scale_factor();
|
||||||
let scale_factor = self.scale_factor();
|
let position = physical_position.to_logical::<f64>(scale_factor);
|
||||||
let position = physical_position.to_logical::<f64>(scale_factor);
|
let screen_frame = self.screen_frame();
|
||||||
let screen_frame = self.screen_frame();
|
let new_screen_frame = CGRect {
|
||||||
let new_screen_frame = CGRect {
|
origin: CGPoint {
|
||||||
origin: CGPoint {
|
x: position.x as _,
|
||||||
x: position.x as _,
|
y: position.y as _,
|
||||||
y: position.y as _,
|
},
|
||||||
},
|
size: screen_frame.size,
|
||||||
size: screen_frame.size,
|
};
|
||||||
};
|
let bounds = self.rect_from_screen_space(new_screen_frame);
|
||||||
let bounds = self.rect_from_screen_space(new_screen_frame);
|
self.window.setBounds(bounds);
|
||||||
self.window.setBounds(bounds);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||||
unsafe {
|
let scale_factor = self.scale_factor();
|
||||||
let scale_factor = self.scale_factor();
|
let safe_area = self.safe_area_screen_space();
|
||||||
let safe_area = self.safe_area_screen_space();
|
let size = LogicalSize {
|
||||||
let size = LogicalSize {
|
width: safe_area.size.width as f64,
|
||||||
width: safe_area.size.width as f64,
|
height: safe_area.size.height as f64,
|
||||||
height: safe_area.size.height as f64,
|
};
|
||||||
};
|
size.to_physical(scale_factor)
|
||||||
size.to_physical(scale_factor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||||
unsafe {
|
let scale_factor = self.scale_factor();
|
||||||
let scale_factor = self.scale_factor();
|
let screen_frame = self.screen_frame();
|
||||||
let screen_frame = self.screen_frame();
|
let size = LogicalSize {
|
||||||
let size = LogicalSize {
|
width: screen_frame.size.width as f64,
|
||||||
width: screen_frame.size.width as f64,
|
height: screen_frame.size.height as f64,
|
||||||
height: screen_frame.size.height as f64,
|
};
|
||||||
};
|
size.to_physical(scale_factor)
|
||||||
size.to_physical(scale_factor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_inner_size(&self, _size: Size) {
|
pub fn request_inner_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
|
||||||
warn!("not clear what `Window::set_inner_size` means on iOS");
|
Some(self.inner_size())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_min_inner_size(&self, _dimensions: Option<Size>) {
|
pub fn set_min_inner_size(&self, _dimensions: Option<Size>) {
|
||||||
@@ -262,22 +249,20 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||||
unsafe {
|
let monitor = self.current_monitor_inner();
|
||||||
let monitor = self.current_monitor_inner();
|
let uiscreen = monitor.ui_screen();
|
||||||
let uiscreen = monitor.ui_screen();
|
let screen_space_bounds = self.screen_frame();
|
||||||
let screen_space_bounds = self.screen_frame();
|
let screen_bounds = uiscreen.bounds();
|
||||||
let screen_bounds = uiscreen.bounds();
|
|
||||||
|
|
||||||
// TODO: track fullscreen instead of relying on brittle float comparisons
|
// TODO: track fullscreen instead of relying on brittle float comparisons
|
||||||
if screen_space_bounds.origin.x == screen_bounds.origin.x
|
if screen_space_bounds.origin.x == screen_bounds.origin.x
|
||||||
&& screen_space_bounds.origin.y == screen_bounds.origin.y
|
&& screen_space_bounds.origin.y == screen_bounds.origin.y
|
||||||
&& screen_space_bounds.size.width == screen_bounds.size.width
|
&& screen_space_bounds.size.width == screen_bounds.size.width
|
||||||
&& screen_space_bounds.size.height == screen_bounds.size.height
|
&& screen_space_bounds.size.height == screen_bounds.size.height
|
||||||
{
|
{
|
||||||
Some(Fullscreen::Borderless(Some(monitor)))
|
Some(Fullscreen::Borderless(Some(monitor)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,32 +360,7 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
pub inner: Inner,
|
inner: MainThreadBound<Inner>,
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Window {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
assert_main_thread!("`Window::drop` can only be run on the main thread on iOS");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for Window {}
|
|
||||||
unsafe impl Sync for Window {}
|
|
||||||
|
|
||||||
impl Deref for Window {
|
|
||||||
type Target = Inner;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Inner {
|
|
||||||
assert_main_thread!("`Window` methods can only be run on the main thread on iOS");
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Window {
|
|
||||||
fn deref_mut(&mut self) -> &mut Inner {
|
|
||||||
assert_main_thread!("`Window` methods can only be run on the main thread on iOS");
|
|
||||||
&mut self.inner
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
@@ -497,29 +457,29 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let inner = Inner {
|
||||||
|
window,
|
||||||
|
view_controller,
|
||||||
|
view,
|
||||||
|
gl_or_metal_backed,
|
||||||
|
};
|
||||||
Ok(Window {
|
Ok(Window {
|
||||||
inner: Inner {
|
inner: MainThreadBound::new(inner, mtm),
|
||||||
window,
|
|
||||||
view_controller,
|
|
||||||
view,
|
|
||||||
gl_or_metal_backed,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Inner) + Send + 'static) {
|
||||||
|
// For now, don't actually do queuing, since it may be less predictable
|
||||||
|
self.maybe_wait_on_main(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Inner) -> R + Send) -> R {
|
||||||
|
self.inner.get_on_main(|inner, _mtm| f(inner))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowExtIOS
|
// WindowExtIOS
|
||||||
impl Inner {
|
impl Inner {
|
||||||
pub fn ui_window(&self) -> *mut c_void {
|
|
||||||
Id::as_ptr(&self.window) as *mut c_void
|
|
||||||
}
|
|
||||||
pub fn ui_view_controller(&self) -> *mut c_void {
|
|
||||||
Id::as_ptr(&self.view_controller) as *mut c_void
|
|
||||||
}
|
|
||||||
pub fn ui_view(&self) -> *mut c_void {
|
|
||||||
Id::as_ptr(&self.view) as *mut c_void
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_scale_factor(&self, scale_factor: f64) {
|
pub fn set_scale_factor(&self, scale_factor: f64) {
|
||||||
assert!(
|
assert!(
|
||||||
dpi::validate_scale_factor(scale_factor),
|
dpi::validate_scale_factor(scale_factor),
|
||||||
@@ -538,42 +498,37 @@ impl Inner {
|
|||||||
|
|
||||||
pub fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
|
pub fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
|
||||||
self.view_controller
|
self.view_controller
|
||||||
.setPrefersHomeIndicatorAutoHidden(hidden);
|
.set_prefers_home_indicator_auto_hidden(hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
|
pub fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
|
||||||
let edges: UIRectEdge = edges.into();
|
|
||||||
self.view_controller
|
self.view_controller
|
||||||
.setPreferredScreenEdgesDeferringSystemGestures(edges);
|
.set_preferred_screen_edges_deferring_system_gestures(edges.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
pub fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
||||||
self.view_controller.setPrefersStatusBarHidden(hidden);
|
self.view_controller.set_prefers_status_bar_hidden(hidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Inner {
|
impl Inner {
|
||||||
// requires main thread
|
fn screen_frame(&self) -> CGRect {
|
||||||
unsafe fn screen_frame(&self) -> CGRect {
|
|
||||||
self.rect_to_screen_space(self.window.bounds())
|
self.rect_to_screen_space(self.window.bounds())
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
fn rect_to_screen_space(&self, rect: CGRect) -> CGRect {
|
||||||
unsafe fn rect_to_screen_space(&self, rect: CGRect) -> CGRect {
|
|
||||||
let screen_space = self.window.screen().coordinateSpace();
|
let screen_space = self.window.screen().coordinateSpace();
|
||||||
self.window
|
self.window
|
||||||
.convertRect_toCoordinateSpace(rect, &screen_space)
|
.convertRect_toCoordinateSpace(rect, &screen_space)
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
fn rect_from_screen_space(&self, rect: CGRect) -> CGRect {
|
||||||
unsafe fn rect_from_screen_space(&self, rect: CGRect) -> CGRect {
|
|
||||||
let screen_space = self.window.screen().coordinateSpace();
|
let screen_space = self.window.screen().coordinateSpace();
|
||||||
self.window
|
self.window
|
||||||
.convertRect_fromCoordinateSpace(rect, &screen_space)
|
.convertRect_fromCoordinateSpace(rect, &screen_space)
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires main thread
|
fn safe_area_screen_space(&self) -> CGRect {
|
||||||
unsafe fn safe_area_screen_space(&self) -> CGRect {
|
|
||||||
let bounds = self.window.bounds();
|
let bounds = self.window.bounds();
|
||||||
if app_state::os_capabilities().safe_area {
|
if app_state::os_capabilities().safe_area {
|
||||||
let safe_area = self.window.safeAreaInsets();
|
let safe_area = self.window.safeAreaInsets();
|
||||||
@@ -591,7 +546,7 @@ 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_unchecked()).expect(
|
let app = UIApplication::shared(MainThreadMarker::new().unwrap()).expect(
|
||||||
"`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS",
|
"`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS",
|
||||||
);
|
);
|
||||||
app.statusBarFrame()
|
app.statusBarFrame()
|
||||||
@@ -648,8 +603,8 @@ impl From<u64> for WindowId {
|
|||||||
unsafe impl Send for WindowId {}
|
unsafe impl Send for WindowId {}
|
||||||
unsafe impl Sync for WindowId {}
|
unsafe impl Sync for WindowId {}
|
||||||
|
|
||||||
impl From<&Object> for WindowId {
|
impl From<&AnyObject> for WindowId {
|
||||||
fn from(window: &Object) -> WindowId {
|
fn from(window: &AnyObject) -> WindowId {
|
||||||
WindowId {
|
WindowId {
|
||||||
window: window as *const _ as _,
|
window: window as *const _ as _,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,7 +354,6 @@ impl KbdState {
|
|||||||
self.post_init(state, keymap);
|
self.post_init(state, keymap);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
pub fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool {
|
pub fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool {
|
||||||
unsafe { (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode) == 1 }
|
unsafe { (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode) == 1 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,45 +3,45 @@
|
|||||||
#[cfg(all(not(x11_platform), not(wayland_platform)))]
|
#[cfg(all(not(x11_platform), not(wayland_platform)))]
|
||||||
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
use std::sync::Arc;
|
||||||
use std::error::Error;
|
use std::time::Duration;
|
||||||
|
|
||||||
use std::{collections::VecDeque, env, fmt};
|
use std::{collections::VecDeque, env, fmt};
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
use std::{
|
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex};
|
||||||
ffi::CStr,
|
|
||||||
mem::MaybeUninit,
|
|
||||||
os::raw::*,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
#[cfg(x11_platform)]
|
|
||||||
pub use self::x11::XNotSupported;
|
|
||||||
#[cfg(x11_platform)]
|
|
||||||
use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError};
|
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
use crate::platform::x11::XlibErrorHook;
|
use crate::platform::x11::XlibErrorHook;
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError},
|
||||||
event::{Event, KeyEvent},
|
event::{Event, KeyEvent},
|
||||||
event_loop::{ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
event_loop::{
|
||||||
|
AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||||
|
EventLoopWindowTarget as RootELW,
|
||||||
|
},
|
||||||
icon::Icon,
|
icon::Icon,
|
||||||
keyboard::{Key, KeyCode},
|
keyboard::{Key, KeyCode},
|
||||||
platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode},
|
platform::{
|
||||||
|
modifier_supplement::KeyEventExtModifierSupplement, pump_events::PumpStatus,
|
||||||
|
scancode::KeyCodeExtScancode,
|
||||||
|
},
|
||||||
window::{
|
window::{
|
||||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
ActivationToken, CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme,
|
||||||
WindowAttributes, WindowButtons, WindowLevel,
|
UserAttentionType, WindowAttributes, WindowButtons, WindowLevel,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
#[cfg(x11_platform)]
|
||||||
|
pub use x11::XNotSupported;
|
||||||
|
#[cfg(x11_platform)]
|
||||||
|
use x11::{util::WindowType as XWindowType, X11Error, XConnection, XError};
|
||||||
|
|
||||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||||
pub(self) use crate::platform_impl::Fullscreen;
|
pub(crate) use crate::platform_impl::Fullscreen;
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(wayland_platform)]
|
||||||
@@ -49,15 +49,6 @@ pub mod wayland;
|
|||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
pub mod x11;
|
pub mod x11;
|
||||||
|
|
||||||
/// Environment variable specifying which backend should be used on unix platform.
|
|
||||||
///
|
|
||||||
/// Legal values are x11 and wayland. If this variable is set only the named backend
|
|
||||||
/// will be tried by winit. If it is not set, winit will try to connect to a wayland connection,
|
|
||||||
/// and if it fails will fallback on x11.
|
|
||||||
///
|
|
||||||
/// If this variable is set with any other value, winit will panic.
|
|
||||||
const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND";
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub(crate) enum Backend {
|
pub(crate) enum Backend {
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
@@ -87,32 +78,38 @@ impl ApplicationName {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||||
pub name: Option<ApplicationName>,
|
pub name: Option<ApplicationName>,
|
||||||
|
pub activation_token: Option<ActivationToken>,
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
pub visual_infos: Option<XVisualInfo>,
|
pub x11: X11WindowBuilderAttributes,
|
||||||
#[cfg(x11_platform)]
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[cfg(x11_platform)]
|
||||||
|
pub struct X11WindowBuilderAttributes {
|
||||||
|
pub visual_id: Option<x11rb::protocol::xproto::Visualid>,
|
||||||
pub screen_id: Option<i32>,
|
pub screen_id: Option<i32>,
|
||||||
#[cfg(x11_platform)]
|
|
||||||
pub base_size: Option<Size>,
|
pub base_size: Option<Size>,
|
||||||
#[cfg(x11_platform)]
|
|
||||||
pub override_redirect: bool,
|
pub override_redirect: bool,
|
||||||
#[cfg(x11_platform)]
|
|
||||||
pub x11_window_types: Vec<XWindowType>,
|
pub x11_window_types: Vec<XWindowType>,
|
||||||
|
|
||||||
|
/// The parent window to embed this window into.
|
||||||
|
pub embed_window: Option<x11rb::protocol::xproto::Window>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PlatformSpecificWindowBuilderAttributes {
|
impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: None,
|
name: None,
|
||||||
|
activation_token: None,
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
visual_infos: None,
|
x11: X11WindowBuilderAttributes {
|
||||||
#[cfg(x11_platform)]
|
visual_id: None,
|
||||||
screen_id: None,
|
screen_id: None,
|
||||||
#[cfg(x11_platform)]
|
base_size: None,
|
||||||
base_size: None,
|
override_redirect: false,
|
||||||
#[cfg(x11_platform)]
|
x11_window_types: vec![XWindowType::Normal],
|
||||||
override_redirect: false,
|
embed_window: None,
|
||||||
#[cfg(x11_platform)]
|
},
|
||||||
x11_window_types: vec![XWindowType::Normal],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,23 +120,21 @@ pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum OsError {
|
pub enum OsError {
|
||||||
|
Misc(&'static str),
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
XError(XError),
|
XError(Arc<X11Error>),
|
||||||
#[cfg(x11_platform)]
|
|
||||||
XMisc(&'static str),
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(wayland_platform)]
|
||||||
WaylandMisc(&'static str),
|
WaylandError(Arc<wayland::WaylandError>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for OsError {
|
impl fmt::Display for OsError {
|
||||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
|
OsError::Misc(e) => _f.pad(e),
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
OsError::XError(ref e) => _f.pad(&e.description),
|
OsError::XError(ref e) => fmt::Display::fmt(e, _f),
|
||||||
#[cfg(x11_platform)]
|
|
||||||
OsError::XMisc(e) => _f.pad(e),
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(wayland_platform)]
|
||||||
OsError::WaylandMisc(e) => _f.pad(e),
|
OsError::WaylandError(ref e) => fmt::Display::fmt(e, _f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,6 +306,14 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) {
|
||||||
|
f(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Self) -> R + Send) -> R {
|
||||||
|
f(self)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> WindowId {
|
pub fn id(&self) -> WindowId {
|
||||||
match self {
|
match self {
|
||||||
@@ -367,8 +370,13 @@ impl Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_inner_size(&self, size: Size) {
|
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||||
x11_or_wayland!(match self; Window(w) => w.set_inner_size(size))
|
x11_or_wayland!(match self; Window(w) => w.request_inner_size(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||||
|
x11_or_wayland!(match self; Window(w) => w.request_activation_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -541,12 +549,7 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||||
match self {
|
x11_or_wayland!(match self; Window(w) => w.request_user_attention(request_type))
|
||||||
#[cfg(x11_platform)]
|
|
||||||
Window::X(ref w) => w.request_user_attention(request_type),
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
Window::Wayland(ref w) => w.request_user_attention(request_type),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -554,6 +557,11 @@ impl Window {
|
|||||||
x11_or_wayland!(match self; Window(w) => w.request_redraw())
|
x11_or_wayland!(match self; Window(w) => w.request_redraw())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn pre_present_notify(&self) {
|
||||||
|
x11_or_wayland!(match self; Window(w) => w.pre_present_notify())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||||
match self {
|
match self {
|
||||||
@@ -730,7 +738,9 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> EventLoop<T> {
|
impl<T: 'static> EventLoop<T> {
|
||||||
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self {
|
pub(crate) fn new(
|
||||||
|
attributes: &PlatformSpecificEventLoopAttributes,
|
||||||
|
) -> Result<Self, EventLoopError> {
|
||||||
if !attributes.any_thread && !is_main_thread() {
|
if !attributes.any_thread && !is_main_thread() {
|
||||||
panic!(
|
panic!(
|
||||||
"Initializing the event loop outside of the main thread is a significant \
|
"Initializing the event loop outside of the main thread is a significant \
|
||||||
@@ -740,65 +750,26 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(x11_platform)]
|
// NOTE: Wayland first because of X11 could be present under wayland as well.
|
||||||
if attributes.forced_backend == Some(Backend::X) {
|
|
||||||
// TODO: Propagate
|
|
||||||
return EventLoop::new_x11_any_thread().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(wayland_platform)]
|
||||||
if attributes.forced_backend == Some(Backend::Wayland) {
|
if attributes.forced_backend == Some(Backend::Wayland)
|
||||||
// TODO: Propagate
|
|| env::var("WAYLAND_DISPLAY").is_ok()
|
||||||
return EventLoop::new_wayland_any_thread().expect("failed to open Wayland connection");
|
{
|
||||||
|
return EventLoop::new_wayland_any_thread().map_err(Into::into);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) {
|
|
||||||
match env_var.as_str() {
|
|
||||||
"x11" => {
|
|
||||||
// TODO: propagate
|
|
||||||
#[cfg(x11_platform)]
|
|
||||||
return EventLoop::new_x11_any_thread()
|
|
||||||
.expect("Failed to initialize X11 backend");
|
|
||||||
#[cfg(not(x11_platform))]
|
|
||||||
panic!("x11 feature is not enabled")
|
|
||||||
}
|
|
||||||
"wayland" => {
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
return EventLoop::new_wayland_any_thread()
|
|
||||||
.expect("Failed to initialize Wayland backend");
|
|
||||||
#[cfg(not(wayland_platform))]
|
|
||||||
panic!("wayland feature is not enabled");
|
|
||||||
}
|
|
||||||
_ => panic!(
|
|
||||||
"Unknown environment variable value for {BACKEND_PREFERENCE_ENV_VAR}, try one of `x11`,`wayland`",
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
let wayland_err = match EventLoop::new_wayland_any_thread() {
|
|
||||||
Ok(event_loop) => return event_loop,
|
|
||||||
Err(err) => err,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
let x11_err = match EventLoop::new_x11_any_thread() {
|
if attributes.forced_backend == Some(Backend::X) || env::var("DISPLAY").is_ok() {
|
||||||
Ok(event_loop) => return event_loop,
|
return Ok(EventLoop::new_x11_any_thread().unwrap());
|
||||||
Err(err) => err,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(wayland_platform))]
|
Err(EventLoopError::Os(os_error!(OsError::Misc(
|
||||||
let wayland_err = "backend disabled";
|
"neither WAYLAND_DISPLAY nor DISPLAY is set."
|
||||||
#[cfg(not(x11_platform))]
|
))))
|
||||||
let x11_err = "backend disabled";
|
|
||||||
|
|
||||||
panic!(
|
|
||||||
"Failed to initialize any backend! Wayland status: {wayland_err:?} X11 status: {x11_err:?}",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(wayland_platform)]
|
||||||
fn new_wayland_any_thread() -> Result<EventLoop<T>, Box<dyn Error>> {
|
fn new_wayland_any_thread() -> Result<EventLoop<T>, EventLoopError> {
|
||||||
wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp)))
|
wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,18 +787,25 @@ impl<T: 'static> EventLoop<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)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_return<F>(&mut self, callback: F) -> i32
|
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
|
||||||
where
|
where
|
||||||
F: FnMut(crate::event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_return(callback))
|
self.run_ondemand(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<F>(self, callback: F) -> !
|
pub fn run_ondemand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(crate::event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run(callback))
|
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_ondemand(callback))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
|
||||||
|
where
|
||||||
|
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget<T> {
|
pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget<T> {
|
||||||
@@ -907,12 +885,12 @@ impl<T> EventLoopWindowTarget<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sticky_exit_callback<T, F>(
|
fn sticky_exit_callback<T, F>(
|
||||||
evt: Event<'_, T>,
|
evt: Event<T>,
|
||||||
target: &RootELW<T>,
|
target: &RootELW<T>,
|
||||||
control_flow: &mut ControlFlow,
|
control_flow: &mut ControlFlow,
|
||||||
callback: &mut F,
|
callback: &mut F,
|
||||||
) where
|
) where
|
||||||
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
// make ControlFlow::ExitWithCode sticky by providing a dummy
|
// make ControlFlow::ExitWithCode sticky by providing a dummy
|
||||||
// control flow reference if it is already ExitWithCode.
|
// control flow reference if it is already ExitWithCode.
|
||||||
@@ -923,11 +901,18 @@ fn sticky_exit_callback<T, F>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the minimum `Option<Duration>`, taking into account that `None`
|
||||||
|
/// equates to an infinite timeout, not a zero timeout (so can't just use
|
||||||
|
/// `Option::min`)
|
||||||
|
fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
|
||||||
|
a.map_or(b, |a_timeout| {
|
||||||
|
b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn is_main_thread() -> bool {
|
fn is_main_thread() -> bool {
|
||||||
use libc::{c_long, getpid, syscall, SYS_gettid};
|
rustix::thread::gettid() == rustix::process::getpid()
|
||||||
|
|
||||||
unsafe { syscall(SYS_gettid) == getpid() as c_long }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
//! The event-loop routines.
|
//! The event-loop routines.
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::error::Error;
|
|
||||||
use std::io::Result as IOResult;
|
use std::io::Result as IOResult;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::process;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use raw_window_handle::{RawDisplayHandle, WaylandDisplayHandle};
|
use raw_window_handle::{RawDisplayHandle, WaylandDisplayHandle};
|
||||||
|
|
||||||
use sctk::reexports::calloop;
|
use sctk::reexports::calloop;
|
||||||
|
use sctk::reexports::calloop::Error as CalloopError;
|
||||||
use sctk::reexports::client::globals;
|
use sctk::reexports::client::globals;
|
||||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle, WaylandSource};
|
use sctk::reexports::client::{Connection, Proxy, QueueHandle, WaylandSource};
|
||||||
|
|
||||||
use crate::dpi::{LogicalSize, PhysicalSize};
|
use crate::dpi::{LogicalSize, PhysicalSize};
|
||||||
use crate::event::{Event, StartCause, WindowEvent};
|
use crate::error::{EventLoopError, OsError as RootOsError};
|
||||||
|
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||||
use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget};
|
use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget};
|
||||||
|
use crate::platform::pump_events::PumpStatus;
|
||||||
|
use crate::platform_impl::platform::min_timeout;
|
||||||
use crate::platform_impl::platform::sticky_exit_callback;
|
use crate::platform_impl::platform::sticky_exit_callback;
|
||||||
use crate::platform_impl::EventLoopWindowTarget as PlatformEventLoopWindowTarget;
|
use crate::platform_impl::{EventLoopWindowTarget as PlatformEventLoopWindowTarget, OsError};
|
||||||
|
|
||||||
mod proxy;
|
mod proxy;
|
||||||
pub mod sink;
|
pub mod sink;
|
||||||
@@ -29,12 +32,23 @@ pub use proxy::EventLoopProxy;
|
|||||||
use sink::EventSink;
|
use sink::EventSink;
|
||||||
|
|
||||||
use super::state::{WindowCompositorUpdate, WinitState};
|
use super::state::{WindowCompositorUpdate, WinitState};
|
||||||
use super::{DeviceId, WindowId};
|
use super::window::state::FrameCallbackState;
|
||||||
|
use super::{DeviceId, WaylandError, WindowId};
|
||||||
|
|
||||||
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
|
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
|
||||||
|
|
||||||
/// The Wayland event loop.
|
/// The Wayland event loop.
|
||||||
pub struct EventLoop<T: 'static> {
|
pub struct EventLoop<T: 'static> {
|
||||||
|
/// Has `run` or `run_ondemand` been called or a call to `pump_events` that starts the loop
|
||||||
|
loop_running: bool,
|
||||||
|
|
||||||
|
/// The application's latest control_flow state
|
||||||
|
control_flow: ControlFlow,
|
||||||
|
|
||||||
|
buffer_sink: EventSink,
|
||||||
|
compositor_updates: Vec<WindowCompositorUpdate>,
|
||||||
|
window_ids: Vec<WindowId>,
|
||||||
|
|
||||||
/// Sender of user events.
|
/// Sender of user events.
|
||||||
user_events_sender: calloop::channel::Sender<T>,
|
user_events_sender: calloop::channel::Sender<T>,
|
||||||
|
|
||||||
@@ -59,50 +73,93 @@ pub struct EventLoop<T: 'static> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> EventLoop<T> {
|
impl<T: 'static> EventLoop<T> {
|
||||||
pub fn new() -> Result<EventLoop<T>, Box<dyn Error>> {
|
pub fn new() -> Result<EventLoop<T>, EventLoopError> {
|
||||||
let connection = Connection::connect_to_env()?;
|
macro_rules! map_err {
|
||||||
|
($e:expr, $err:expr) => {
|
||||||
|
$e.map_err(|error| os_error!($err(error).into()))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let (globals, mut event_queue) = globals::registry_queue_init(&connection)?;
|
let connection = map_err!(Connection::connect_to_env(), WaylandError::Connection)?;
|
||||||
|
|
||||||
|
let (globals, mut event_queue) = map_err!(
|
||||||
|
globals::registry_queue_init(&connection),
|
||||||
|
WaylandError::Global
|
||||||
|
)?;
|
||||||
let queue_handle = event_queue.handle();
|
let queue_handle = event_queue.handle();
|
||||||
|
|
||||||
let event_loop = calloop::EventLoop::<WinitState>::try_new()?;
|
let event_loop = map_err!(
|
||||||
|
calloop::EventLoop::<WinitState>::try_new(),
|
||||||
|
WaylandError::Calloop
|
||||||
|
)?;
|
||||||
|
|
||||||
let mut winit_state = WinitState::new(&globals, &queue_handle, event_loop.handle())?;
|
let mut winit_state = WinitState::new(&globals, &queue_handle, event_loop.handle())
|
||||||
|
.map_err(|error| os_error!(error))?;
|
||||||
|
|
||||||
// NOTE: do a roundtrip after binding the globals to prevent potential
|
// NOTE: do a roundtrip after binding the globals to prevent potential
|
||||||
// races with the server.
|
// races with the server.
|
||||||
event_queue.roundtrip(&mut winit_state)?;
|
map_err!(
|
||||||
|
event_queue.roundtrip(&mut winit_state),
|
||||||
|
WaylandError::Dispatch
|
||||||
|
)?;
|
||||||
|
|
||||||
// Register Wayland source.
|
// Register Wayland source.
|
||||||
let wayland_source = WaylandSource::new(event_queue)?;
|
let wayland_source = map_err!(WaylandSource::new(event_queue), WaylandError::Wire)?;
|
||||||
let wayland_dispatcher =
|
let wayland_dispatcher =
|
||||||
calloop::Dispatcher::new(wayland_source, |_, queue, winit_state| {
|
calloop::Dispatcher::new(wayland_source, |_, queue, winit_state: &mut WinitState| {
|
||||||
queue.dispatch_pending(winit_state)
|
let result = queue.dispatch_pending(winit_state);
|
||||||
|
if result.is_ok()
|
||||||
|
&& (!winit_state.events_sink.is_empty()
|
||||||
|
|| !winit_state.window_compositor_updates.is_empty())
|
||||||
|
{
|
||||||
|
winit_state.dispatched_events = true;
|
||||||
|
}
|
||||||
|
result
|
||||||
});
|
});
|
||||||
|
|
||||||
event_loop
|
map_err!(
|
||||||
.handle()
|
event_loop
|
||||||
.register_dispatcher(wayland_dispatcher.clone())?;
|
.handle()
|
||||||
|
.register_dispatcher(wayland_dispatcher.clone()),
|
||||||
|
WaylandError::Calloop
|
||||||
|
)?;
|
||||||
|
|
||||||
// Setup the user proxy.
|
// Setup the user proxy.
|
||||||
let pending_user_events = Rc::new(RefCell::new(Vec::new()));
|
let pending_user_events = Rc::new(RefCell::new(Vec::new()));
|
||||||
let pending_user_events_clone = pending_user_events.clone();
|
let pending_user_events_clone = pending_user_events.clone();
|
||||||
let (user_events_sender, user_events_channel) = calloop::channel::channel();
|
let (user_events_sender, user_events_channel) = calloop::channel::channel();
|
||||||
event_loop
|
let result = event_loop
|
||||||
.handle()
|
.handle()
|
||||||
.insert_source(user_events_channel, move |event, _, _| {
|
.insert_source(
|
||||||
if let calloop::channel::Event::Msg(msg) = event {
|
user_events_channel,
|
||||||
pending_user_events_clone.borrow_mut().push(msg);
|
move |event, _, winit_state: &mut WinitState| {
|
||||||
}
|
if let calloop::channel::Event::Msg(msg) = event {
|
||||||
})?;
|
winit_state.dispatched_events = true;
|
||||||
|
pending_user_events_clone.borrow_mut().push(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|error| error.error);
|
||||||
|
map_err!(result, WaylandError::Calloop)?;
|
||||||
|
|
||||||
// An event's loop awakener to wake up for window events from winit's windows.
|
// An event's loop awakener to wake up for window events from winit's windows.
|
||||||
let (event_loop_awakener, event_loop_awakener_source) = calloop::ping::make_ping()?;
|
let (event_loop_awakener, event_loop_awakener_source) = map_err!(
|
||||||
event_loop
|
calloop::ping::make_ping()
|
||||||
|
.map_err(|error| CalloopError::OtherError(Box::new(error).into())),
|
||||||
|
WaylandError::Calloop
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let result = event_loop
|
||||||
.handle()
|
.handle()
|
||||||
.insert_source(event_loop_awakener_source, move |_, _, _| {
|
.insert_source(
|
||||||
// No extra handling is required, we just need to wake-up.
|
event_loop_awakener_source,
|
||||||
})?;
|
move |_, _, winit_state: &mut WinitState| {
|
||||||
|
// Mark that we have something to dispatch.
|
||||||
|
winit_state.dispatched_events = true;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|error| error.error);
|
||||||
|
map_err!(result, WaylandError::Calloop)?;
|
||||||
|
|
||||||
let window_target = EventLoopWindowTarget {
|
let window_target = EventLoopWindowTarget {
|
||||||
connection: connection.clone(),
|
connection: connection.clone(),
|
||||||
@@ -114,6 +171,11 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let event_loop = Self {
|
let event_loop = Self {
|
||||||
|
loop_running: false,
|
||||||
|
control_flow: ControlFlow::default(),
|
||||||
|
compositor_updates: Vec::new(),
|
||||||
|
buffer_sink: EventSink::default(),
|
||||||
|
window_ids: Vec::new(),
|
||||||
connection,
|
connection,
|
||||||
wayland_dispatcher,
|
wayland_dispatcher,
|
||||||
user_events_sender,
|
user_events_sender,
|
||||||
@@ -128,45 +190,87 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
Ok(event_loop)
|
Ok(event_loop)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<F>(mut self, callback: F) -> !
|
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||||
where
|
where
|
||||||
F: FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow) + 'static,
|
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
let exit_code = self.run_return(callback);
|
if self.loop_running {
|
||||||
process::exit(exit_code);
|
return Err(EventLoopError::AlreadyRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
let exit = loop {
|
||||||
|
match self.pump_events(None, &mut event_handler) {
|
||||||
|
PumpStatus::Exit(0) => {
|
||||||
|
break Ok(());
|
||||||
|
}
|
||||||
|
PumpStatus::Exit(code) => {
|
||||||
|
break Err(EventLoopError::ExitFailure(code));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Applications aren't allowed to carry windows between separate
|
||||||
|
// `run_ondemand` calls but if they have only just dropped their
|
||||||
|
// windows we need to make sure those last requests are sent to the
|
||||||
|
// compositor.
|
||||||
|
let _ = self.roundtrip().map_err(EventLoopError::Os);
|
||||||
|
|
||||||
|
exit
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_return<F>(&mut self, mut callback: F) -> i32
|
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||||
where
|
where
|
||||||
F: FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
let mut control_flow = ControlFlow::Poll;
|
if !self.loop_running {
|
||||||
|
self.loop_running = true;
|
||||||
|
|
||||||
// XXX preallocate certian structures to avoid allocating on each loop iteration.
|
// Reset the internal state for the loop as we start running to
|
||||||
let mut window_ids = Vec::<WindowId>::new();
|
// ensure consistent behaviour in case the loop runs and exits more
|
||||||
let mut compositor_updates = Vec::<WindowCompositorUpdate>::new();
|
// than once.
|
||||||
let mut buffer_sink = EventSink::new();
|
self.control_flow = ControlFlow::Poll;
|
||||||
|
|
||||||
callback(
|
// Run the initial loop iteration.
|
||||||
Event::NewEvents(StartCause::Init),
|
self.single_iteration(&mut callback, StartCause::Init);
|
||||||
&self.window_target,
|
}
|
||||||
&mut control_flow,
|
|
||||||
);
|
|
||||||
|
|
||||||
// XXX For consistency all platforms must emit a 'Resumed' event even though Wayland
|
// Consider the possibility that the `StartCause::Init` iteration could
|
||||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
// request to Exit.
|
||||||
callback(Event::Resumed, &self.window_target, &mut control_flow);
|
if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) {
|
||||||
|
self.poll_events_with_timeout(timeout, &mut callback);
|
||||||
|
}
|
||||||
|
if let ControlFlow::ExitWithCode(code) = self.control_flow {
|
||||||
|
self.loop_running = false;
|
||||||
|
|
||||||
// XXX We break on errors from dispatches, since if we've got protocol error
|
let mut dummy = self.control_flow;
|
||||||
// libwayland-client/wayland-rs will inform us anyway, but crashing downstream is not
|
sticky_exit_callback(
|
||||||
// really an option. Instead we inform that the event loop got destroyed. We may
|
Event::LoopExiting,
|
||||||
// communicate an error that something was terminated, but winit doesn't provide us
|
self.window_target(),
|
||||||
// with an API to do that via some event.
|
&mut dummy,
|
||||||
// Still, we set the exit code to the error's OS error code, or to 1 if not possible.
|
&mut callback,
|
||||||
let exit_code = loop {
|
);
|
||||||
// Flush the connection.
|
|
||||||
let _ = self.connection.flush();
|
|
||||||
|
|
||||||
|
PumpStatus::Exit(code)
|
||||||
|
} else {
|
||||||
|
PumpStatus::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
|
||||||
|
where
|
||||||
|
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
let cause = loop {
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
// TODO(rib): remove this workaround and instead make sure that the calloop
|
||||||
|
// WaylandSource correctly implements the cooperative prepare_read protocol
|
||||||
|
// that support multithreaded wayland clients that may all read from the
|
||||||
|
// same socket.
|
||||||
|
//
|
||||||
// During the run of the user callback, some other code monitoring and reading the
|
// During the run of the user callback, some other code monitoring and reading the
|
||||||
// Wayland socket may have been run (mesa for example does this with vsync), if that
|
// Wayland socket may have been run (mesa for example does this with vsync), if that
|
||||||
// is the case, some events may have been enqueued in our event queue.
|
// is the case, some events may have been enqueued in our event queue.
|
||||||
@@ -186,272 +290,321 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match queue.dispatch_pending(state) {
|
match queue.dispatch_pending(state) {
|
||||||
Ok(dispatched) => dispatched > 0,
|
Ok(dispatched) => {
|
||||||
|
state.dispatched_events |= !state.events_sink.is_empty()
|
||||||
|
|| !state.window_compositor_updates.is_empty();
|
||||||
|
dispatched > 0
|
||||||
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
error!("Error dispatching wayland queue: {}", error);
|
error!("Error dispatching wayland queue: {}", error);
|
||||||
break 1;
|
self.control_flow = ControlFlow::ExitWithCode(1);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match control_flow {
|
timeout = if instant_wakeup {
|
||||||
ControlFlow::ExitWithCode(code) => break code,
|
Some(Duration::ZERO)
|
||||||
ControlFlow::Poll => {
|
} else {
|
||||||
// Non-blocking dispatch.
|
let control_flow_timeout = match self.control_flow {
|
||||||
let timeout = Duration::ZERO;
|
ControlFlow::Wait => None,
|
||||||
if let Err(error) = self.loop_dispatch(Some(timeout)) {
|
ControlFlow::Poll => Some(Duration::ZERO),
|
||||||
break error.raw_os_error().unwrap_or(1);
|
ControlFlow::WaitUntil(wait_deadline) => {
|
||||||
|
Some(wait_deadline.saturating_duration_since(start))
|
||||||
}
|
}
|
||||||
|
// This function shouldn't have to handle any requests to exit
|
||||||
|
// the application (there should be no need to poll for events
|
||||||
|
// if the application has requested to exit) so we consider
|
||||||
|
// it a bug in the backend if we ever see `ExitWithCode` here.
|
||||||
|
ControlFlow::ExitWithCode(_code) => unreachable!(),
|
||||||
|
};
|
||||||
|
min_timeout(control_flow_timeout, timeout)
|
||||||
|
};
|
||||||
|
|
||||||
callback(
|
// NOTE Ideally we should flush as the last thing we do before polling
|
||||||
Event::NewEvents(StartCause::Poll),
|
// to wait for events, and this should be done by the calloop
|
||||||
&self.window_target,
|
// WaylandSource but we currently need to flush writes manually.
|
||||||
&mut control_flow,
|
let _ = self.connection.flush();
|
||||||
);
|
|
||||||
}
|
|
||||||
ControlFlow::Wait => {
|
|
||||||
let timeout = if instant_wakeup {
|
|
||||||
Some(Duration::ZERO)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(error) = self.loop_dispatch(timeout) {
|
if let Err(error) = self.loop_dispatch(timeout) {
|
||||||
break error.raw_os_error().unwrap_or(1);
|
// NOTE We exit on errors from dispatches, since if we've got protocol error
|
||||||
}
|
// libwayland-client/wayland-rs will inform us anyway, but crashing downstream is not
|
||||||
|
// really an option. Instead we inform that the event loop got destroyed. We may
|
||||||
callback(
|
// communicate an error that something was terminated, but winit doesn't provide us
|
||||||
Event::NewEvents(StartCause::WaitCancelled {
|
// with an API to do that via some event.
|
||||||
start: Instant::now(),
|
// Still, we set the exit code to the error's OS error code, or to 1 if not possible.
|
||||||
requested_resume: None,
|
let exit_code = error.raw_os_error().unwrap_or(1);
|
||||||
}),
|
self.control_flow = ControlFlow::ExitWithCode(exit_code);
|
||||||
&self.window_target,
|
return;
|
||||||
&mut control_flow,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ControlFlow::WaitUntil(deadline) => {
|
|
||||||
let start = Instant::now();
|
|
||||||
|
|
||||||
// Compute the amount of time we'll block for.
|
|
||||||
let duration = if deadline > start && !instant_wakeup {
|
|
||||||
deadline - start
|
|
||||||
} else {
|
|
||||||
Duration::ZERO
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(error) = self.loop_dispatch(Some(duration)) {
|
|
||||||
break error.raw_os_error().unwrap_or(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let now = Instant::now();
|
|
||||||
|
|
||||||
if now < deadline {
|
|
||||||
callback(
|
|
||||||
Event::NewEvents(StartCause::WaitCancelled {
|
|
||||||
start,
|
|
||||||
requested_resume: Some(deadline),
|
|
||||||
}),
|
|
||||||
&self.window_target,
|
|
||||||
&mut control_flow,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
callback(
|
|
||||||
Event::NewEvents(StartCause::ResumeTimeReached {
|
|
||||||
start,
|
|
||||||
requested_resume: deadline,
|
|
||||||
}),
|
|
||||||
&self.window_target,
|
|
||||||
&mut control_flow,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle pending user events. We don't need back buffer, since we can't dispatch
|
// NB: `StartCause::Init` is handled as a special case and doesn't need
|
||||||
// user events indirectly via callback to the user.
|
// to be considered here
|
||||||
for user_event in self.pending_user_events.borrow_mut().drain(..) {
|
let cause = match self.control_flow {
|
||||||
|
ControlFlow::Poll => StartCause::Poll,
|
||||||
|
ControlFlow::Wait => StartCause::WaitCancelled {
|
||||||
|
start,
|
||||||
|
requested_resume: None,
|
||||||
|
},
|
||||||
|
ControlFlow::WaitUntil(deadline) => {
|
||||||
|
if Instant::now() < deadline {
|
||||||
|
StartCause::WaitCancelled {
|
||||||
|
start,
|
||||||
|
requested_resume: Some(deadline),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StartCause::ResumeTimeReached {
|
||||||
|
start,
|
||||||
|
requested_resume: deadline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This function shouldn't have to handle any requests to exit
|
||||||
|
// the application (there should be no need to poll for events
|
||||||
|
// if the application has requested to exit) so we consider
|
||||||
|
// it a bug in the backend if we ever see `ExitWithCode` here.
|
||||||
|
ControlFlow::ExitWithCode(_code) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reduce spurious wake-ups.
|
||||||
|
let dispatched_events = self.with_state(|state| state.dispatched_events);
|
||||||
|
if matches!(cause, StartCause::WaitCancelled { .. }) && !dispatched_events {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break cause;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.single_iteration(&mut callback, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn single_iteration<F>(&mut self, mut callback: &mut F, cause: StartCause)
|
||||||
|
where
|
||||||
|
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
// NOTE currently just indented to simplify the diff
|
||||||
|
|
||||||
|
let mut control_flow = self.control_flow;
|
||||||
|
|
||||||
|
// We retain these grow-only scratch buffers as part of the EventLoop
|
||||||
|
// for the sake of avoiding lots of reallocs. We take them here to avoid
|
||||||
|
// trying to mutably borrow `self` more than once and we swap them back
|
||||||
|
// when finished.
|
||||||
|
let mut compositor_updates = std::mem::take(&mut self.compositor_updates);
|
||||||
|
let mut buffer_sink = std::mem::take(&mut self.buffer_sink);
|
||||||
|
let mut window_ids = std::mem::take(&mut self.window_ids);
|
||||||
|
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::NewEvents(cause),
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
|
||||||
|
// NB: For consistency all platforms must emit a 'resumed' event even though Wayland
|
||||||
|
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||||
|
if cause == StartCause::Init {
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::Resumed,
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle pending user events. We don't need back buffer, since we can't dispatch
|
||||||
|
// user events indirectly via callback to the user.
|
||||||
|
for user_event in self.pending_user_events.borrow_mut().drain(..) {
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::UserEvent(user_event),
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drain the pending compositor updates.
|
||||||
|
self.with_state(|state| compositor_updates.append(&mut state.window_compositor_updates));
|
||||||
|
|
||||||
|
for mut compositor_update in compositor_updates.drain(..) {
|
||||||
|
let window_id = compositor_update.window_id;
|
||||||
|
if let Some(scale_factor) = compositor_update.scale_factor {
|
||||||
|
let physical_size = self.with_state(|state| {
|
||||||
|
let windows = state.windows.get_mut();
|
||||||
|
let mut window = windows.get(&window_id).unwrap().lock().unwrap();
|
||||||
|
|
||||||
|
// Set the new scale factor.
|
||||||
|
window.set_scale_factor(scale_factor);
|
||||||
|
let window_size = compositor_update.size.unwrap_or(window.inner_size());
|
||||||
|
logical_to_physical_rounded(window_size, scale_factor)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stash the old window size.
|
||||||
|
let old_physical_size = physical_size;
|
||||||
|
|
||||||
|
let new_inner_size = Arc::new(Mutex::new(physical_size));
|
||||||
sticky_exit_callback(
|
sticky_exit_callback(
|
||||||
Event::UserEvent(user_event),
|
Event::WindowEvent {
|
||||||
|
window_id: crate::window::WindowId(window_id),
|
||||||
|
event: WindowEvent::ScaleFactorChanged {
|
||||||
|
scale_factor,
|
||||||
|
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||||
|
&new_inner_size,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
|
||||||
|
let physical_size = *new_inner_size.lock().unwrap();
|
||||||
|
drop(new_inner_size);
|
||||||
|
let new_logical_size = physical_size.to_logical(scale_factor);
|
||||||
|
|
||||||
|
// Resize the window when user altered the size.
|
||||||
|
if old_physical_size != physical_size {
|
||||||
|
self.with_state(|state| {
|
||||||
|
let windows = state.windows.get_mut();
|
||||||
|
let mut window = windows.get(&window_id).unwrap().lock().unwrap();
|
||||||
|
window.resize(new_logical_size);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make it queue resize.
|
||||||
|
compositor_update.size = Some(new_logical_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(size) = compositor_update.size.take() {
|
||||||
|
let physical_size = self.with_state(|state| {
|
||||||
|
let windows = state.windows.get_mut();
|
||||||
|
let window = windows.get(&window_id).unwrap().lock().unwrap();
|
||||||
|
|
||||||
|
let scale_factor = window.scale_factor();
|
||||||
|
let physical_size = logical_to_physical_rounded(size, scale_factor);
|
||||||
|
|
||||||
|
// TODO could probably bring back size reporting optimization.
|
||||||
|
|
||||||
|
// Mark the window as needed a redraw.
|
||||||
|
state
|
||||||
|
.window_requests
|
||||||
|
.get_mut()
|
||||||
|
.get_mut(&window_id)
|
||||||
|
.unwrap()
|
||||||
|
.redraw_requested
|
||||||
|
.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
physical_size
|
||||||
|
});
|
||||||
|
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id: crate::window::WindowId(window_id),
|
||||||
|
event: WindowEvent::Resized(physical_size),
|
||||||
|
},
|
||||||
&self.window_target,
|
&self.window_target,
|
||||||
&mut control_flow,
|
&mut control_flow,
|
||||||
&mut callback,
|
&mut callback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drain the pending compositor updates.
|
if compositor_update.close_window {
|
||||||
self.with_state(|state| {
|
sticky_exit_callback(
|
||||||
compositor_updates.append(&mut state.window_compositor_updates)
|
Event::WindowEvent {
|
||||||
});
|
window_id: crate::window::WindowId(window_id),
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
for mut compositor_update in compositor_updates.drain(..) {
|
},
|
||||||
let window_id = compositor_update.window_id;
|
&self.window_target,
|
||||||
if let Some(scale_factor) = compositor_update.scale_factor {
|
&mut control_flow,
|
||||||
let mut physical_size = self.with_state(|state| {
|
&mut callback,
|
||||||
let windows = state.windows.get_mut();
|
);
|
||||||
let mut window = windows.get(&window_id).unwrap().lock().unwrap();
|
|
||||||
|
|
||||||
// Set the new scale factor.
|
|
||||||
window.set_scale_factor(scale_factor);
|
|
||||||
let window_size = compositor_update.size.unwrap_or(window.inner_size());
|
|
||||||
logical_to_physical_rounded(window_size, scale_factor)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Stash the old window size.
|
|
||||||
let old_physical_size = physical_size;
|
|
||||||
|
|
||||||
sticky_exit_callback(
|
|
||||||
Event::WindowEvent {
|
|
||||||
window_id: crate::window::WindowId(window_id),
|
|
||||||
event: WindowEvent::ScaleFactorChanged {
|
|
||||||
scale_factor,
|
|
||||||
new_inner_size: &mut physical_size,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&self.window_target,
|
|
||||||
&mut control_flow,
|
|
||||||
&mut callback,
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_logical_size = physical_size.to_logical(scale_factor);
|
|
||||||
|
|
||||||
// Resize the window when user altered the size.
|
|
||||||
if old_physical_size != physical_size {
|
|
||||||
self.with_state(|state| {
|
|
||||||
let windows = state.windows.get_mut();
|
|
||||||
let mut window = windows.get(&window_id).unwrap().lock().unwrap();
|
|
||||||
window.resize(new_logical_size);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make it queue resize.
|
|
||||||
compositor_update.size = Some(new_logical_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(size) = compositor_update.size.take() {
|
|
||||||
let physical_size = self.with_state(|state| {
|
|
||||||
let windows = state.windows.get_mut();
|
|
||||||
let window = windows.get(&window_id).unwrap().lock().unwrap();
|
|
||||||
|
|
||||||
let scale_factor = window.scale_factor();
|
|
||||||
let physical_size = logical_to_physical_rounded(size, scale_factor);
|
|
||||||
|
|
||||||
// TODO could probably bring back size reporting optimization.
|
|
||||||
|
|
||||||
// Mark the window as needed a redraw.
|
|
||||||
state
|
|
||||||
.window_requests
|
|
||||||
.get_mut()
|
|
||||||
.get_mut(&window_id)
|
|
||||||
.unwrap()
|
|
||||||
.redraw_requested
|
|
||||||
.store(true, Ordering::Relaxed);
|
|
||||||
|
|
||||||
physical_size
|
|
||||||
});
|
|
||||||
|
|
||||||
sticky_exit_callback(
|
|
||||||
Event::WindowEvent {
|
|
||||||
window_id: crate::window::WindowId(window_id),
|
|
||||||
event: WindowEvent::Resized(physical_size),
|
|
||||||
},
|
|
||||||
&self.window_target,
|
|
||||||
&mut control_flow,
|
|
||||||
&mut callback,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if compositor_update.close_window {
|
|
||||||
sticky_exit_callback(
|
|
||||||
Event::WindowEvent {
|
|
||||||
window_id: crate::window::WindowId(window_id),
|
|
||||||
event: WindowEvent::CloseRequested,
|
|
||||||
},
|
|
||||||
&self.window_target,
|
|
||||||
&mut control_flow,
|
|
||||||
&mut callback,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Push the events directly from the window.
|
// Push the events directly from the window.
|
||||||
self.with_state(|state| {
|
self.with_state(|state| {
|
||||||
buffer_sink.append(&mut state.window_events_sink.lock().unwrap());
|
buffer_sink.append(&mut state.window_events_sink.lock().unwrap());
|
||||||
});
|
});
|
||||||
for event in buffer_sink.drain() {
|
for event in buffer_sink.drain() {
|
||||||
let event = event.map_nonuser_event().unwrap();
|
let event = event.map_nonuser_event().unwrap();
|
||||||
sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback);
|
sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle non-synthetic events.
|
// Handle non-synthetic events.
|
||||||
self.with_state(|state| {
|
self.with_state(|state| {
|
||||||
buffer_sink.append(&mut state.events_sink);
|
buffer_sink.append(&mut state.events_sink);
|
||||||
});
|
});
|
||||||
for event in buffer_sink.drain() {
|
for event in buffer_sink.drain() {
|
||||||
let event = event.map_nonuser_event().unwrap();
|
let event = event.map_nonuser_event().unwrap();
|
||||||
sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback);
|
sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send events cleared.
|
// Collect the window ids
|
||||||
sticky_exit_callback(
|
self.with_state(|state| {
|
||||||
Event::MainEventsCleared,
|
window_ids.extend(state.window_requests.get_mut().keys());
|
||||||
&self.window_target,
|
});
|
||||||
&mut control_flow,
|
|
||||||
&mut callback,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Collect the window ids
|
for window_id in window_ids.drain(..) {
|
||||||
self.with_state(|state| {
|
let request_redraw = self.with_state(|state| {
|
||||||
window_ids.extend(state.window_requests.get_mut().keys());
|
let window_requests = state.window_requests.get_mut();
|
||||||
});
|
if window_requests.get(&window_id).unwrap().take_closed() {
|
||||||
|
mem::drop(window_requests.remove(&window_id));
|
||||||
|
mem::drop(state.windows.get_mut().remove(&window_id));
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
let mut window = state
|
||||||
|
.windows
|
||||||
|
.get_mut()
|
||||||
|
.get_mut(&window_id)
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
for window_id in window_ids.drain(..) {
|
if window.frame_callback_state() == FrameCallbackState::Requested {
|
||||||
let request_redraw = self.with_state(|state| {
|
|
||||||
let window_requests = state.window_requests.get_mut();
|
|
||||||
if window_requests.get(&window_id).unwrap().take_closed() {
|
|
||||||
mem::drop(window_requests.remove(&window_id));
|
|
||||||
mem::drop(state.windows.get_mut().remove(&window_id));
|
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
// Reset the frame callbacks state.
|
||||||
|
window.frame_callback_reset();
|
||||||
let mut redraw_requested = window_requests
|
let mut redraw_requested = window_requests
|
||||||
.get(&window_id)
|
.get(&window_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.take_redraw_requested();
|
.take_redraw_requested();
|
||||||
|
|
||||||
// Redraw the frames while at it.
|
// Redraw the frame while at it.
|
||||||
redraw_requested |= state
|
redraw_requested |= window.refresh_frame();
|
||||||
.windows
|
|
||||||
.get_mut()
|
|
||||||
.get_mut(&window_id)
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.refresh_frame();
|
|
||||||
|
|
||||||
redraw_requested
|
redraw_requested
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if request_redraw {
|
|
||||||
sticky_exit_callback(
|
|
||||||
Event::RedrawRequested(crate::window::WindowId(window_id)),
|
|
||||||
&self.window_target,
|
|
||||||
&mut control_flow,
|
|
||||||
&mut callback,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if request_redraw {
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::RedrawRequested(crate::window::WindowId(window_id)),
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send RedrawEventCleared.
|
// Reset the hint that we've dispatched events.
|
||||||
sticky_exit_callback(
|
self.with_state(|state| {
|
||||||
Event::RedrawEventsCleared,
|
state.dispatched_events = false;
|
||||||
&self.window_target,
|
});
|
||||||
&mut control_flow,
|
|
||||||
&mut callback,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
callback(Event::LoopDestroyed, &self.window_target, &mut control_flow);
|
// This is always the last event we dispatch before poll again
|
||||||
exit_code
|
sticky_exit_callback(
|
||||||
|
Event::AboutToWait,
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.control_flow = control_flow;
|
||||||
|
std::mem::swap(&mut self.compositor_updates, &mut compositor_updates);
|
||||||
|
std::mem::swap(&mut self.buffer_sink, &mut buffer_sink);
|
||||||
|
std::mem::swap(&mut self.window_ids, &mut window_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -486,6 +639,22 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
error.into()
|
error.into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn roundtrip(&mut self) -> Result<usize, RootOsError> {
|
||||||
|
let state = match &mut self.window_target.p {
|
||||||
|
PlatformEventLoopWindowTarget::Wayland(window_target) => window_target.state.get_mut(),
|
||||||
|
#[cfg(feature = "x11")]
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut wayland_source = self.wayland_dispatcher.as_source_mut();
|
||||||
|
let event_queue = wayland_source.queue();
|
||||||
|
event_queue.roundtrip(state).map_err(|error| {
|
||||||
|
os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(
|
||||||
|
error
|
||||||
|
))))
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventLoopWindowTarget<T> {
|
pub struct EventLoopWindowTarget<T> {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use super::{DeviceId, WindowId};
|
|||||||
/// to the winit's user.
|
/// to the winit's user.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct EventSink {
|
pub struct EventSink {
|
||||||
pub window_events: Vec<Event<'static, ()>>,
|
pub window_events: Vec<Event<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventSink {
|
impl EventSink {
|
||||||
@@ -20,6 +20,12 @@ impl EventSink {
|
|||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `true` if there're pending events.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.window_events.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
/// Add new device event to a queue.
|
/// Add new device event to a queue.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn push_device_event(&mut self, event: DeviceEvent, device_id: DeviceId) {
|
pub fn push_device_event(&mut self, event: DeviceEvent, device_id: DeviceId) {
|
||||||
@@ -31,7 +37,7 @@ impl EventSink {
|
|||||||
|
|
||||||
/// Add new window event to a queue.
|
/// Add new window event to a queue.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn push_window_event(&mut self, event: WindowEvent<'static>, window_id: WindowId) {
|
pub fn push_window_event(&mut self, event: WindowEvent, window_id: WindowId) {
|
||||||
self.window_events.push(Event::WindowEvent {
|
self.window_events.push(Event::WindowEvent {
|
||||||
event,
|
event,
|
||||||
window_id: RootWindowId(window_id),
|
window_id: RootWindowId(window_id),
|
||||||
@@ -44,7 +50,7 @@ impl EventSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn drain(&mut self) -> Drain<'_, Event<'static, ()>> {
|
pub fn drain(&mut self) -> Drain<'_, Event<()>> {
|
||||||
self.window_events.drain(..)
|
self.window_events.drain(..)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
|
|
||||||
//! Winit's Wayland backend.
|
//! Winit's Wayland backend.
|
||||||
|
|
||||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
use std::fmt::Display;
|
||||||
use sctk::reexports::client::Proxy;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use crate::platform_impl::platform::WindowId;
|
use sctk::reexports::client::globals::{BindError, GlobalError};
|
||||||
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
|
use sctk::reexports::client::{self, ConnectError, DispatchError, Proxy};
|
||||||
|
|
||||||
|
pub use crate::platform_impl::platform::{OsError, WindowId};
|
||||||
pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
|
pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
|
||||||
pub use output::{MonitorHandle, VideoMode};
|
pub use output::{MonitorHandle, VideoMode};
|
||||||
pub use window::Window;
|
pub use window::Window;
|
||||||
@@ -17,6 +21,46 @@ mod state;
|
|||||||
mod types;
|
mod types;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum WaylandError {
|
||||||
|
/// Error connecting to the socket.
|
||||||
|
Connection(ConnectError),
|
||||||
|
|
||||||
|
/// Error binding the global.
|
||||||
|
Global(GlobalError),
|
||||||
|
|
||||||
|
// Bind error.
|
||||||
|
Bind(BindError),
|
||||||
|
|
||||||
|
/// Error during the dispatching the event queue.
|
||||||
|
Dispatch(DispatchError),
|
||||||
|
|
||||||
|
/// Calloop error.
|
||||||
|
Calloop(calloop::Error),
|
||||||
|
|
||||||
|
/// Wayland
|
||||||
|
Wire(client::backend::WaylandError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for WaylandError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
WaylandError::Connection(error) => error.fmt(f),
|
||||||
|
WaylandError::Global(error) => error.fmt(f),
|
||||||
|
WaylandError::Bind(error) => error.fmt(f),
|
||||||
|
WaylandError::Dispatch(error) => error.fmt(f),
|
||||||
|
WaylandError::Calloop(error) => error.fmt(f),
|
||||||
|
WaylandError::Wire(error) => error.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WaylandError> for OsError {
|
||||||
|
fn from(value: WaylandError) -> Self {
|
||||||
|
Self::WaylandError(Arc::new(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Dummy device id, since Wayland doesn't have device events.
|
/// Dummy device id, since Wayland doesn't have device events.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct DeviceId;
|
pub struct DeviceId;
|
||||||
|
|||||||
@@ -151,6 +151,9 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
|||||||
keyboard_state.repeat_token = keyboard_state
|
keyboard_state.repeat_token = keyboard_state
|
||||||
.loop_handle
|
.loop_handle
|
||||||
.insert_source(timer, move |_, _, state| {
|
.insert_source(timer, move |_, _, state| {
|
||||||
|
// Required to handle the wakeups from the repeat sources.
|
||||||
|
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 = state.seats.get_mut(&data.seat.id()).unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ impl PointerHandler for WinitState {
|
|||||||
if parent_surface != surface =>
|
if parent_surface != surface =>
|
||||||
{
|
{
|
||||||
if let Some(icon) =
|
if let Some(icon) =
|
||||||
window.frame_point_moved(surface, event.position.0, event.position.1)
|
window.frame_point_moved(seat, surface, event.position.0, event.position.1)
|
||||||
{
|
{
|
||||||
if let Some(pointer) = seat_state.pointer.as_ref() {
|
if let Some(pointer) = seat_state.pointer.as_ref() {
|
||||||
let surface = pointer
|
let surface = pointer
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::error::Error;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
@@ -23,6 +23,7 @@ use sctk::shm::{Shm, ShmHandler};
|
|||||||
use sctk::subcompositor::SubcompositorState;
|
use sctk::subcompositor::SubcompositorState;
|
||||||
|
|
||||||
use crate::dpi::LogicalSize;
|
use crate::dpi::LogicalSize;
|
||||||
|
use crate::platform_impl::OsError;
|
||||||
|
|
||||||
use super::event_loop::sink::EventSink;
|
use super::event_loop::sink::EventSink;
|
||||||
use super::output::MonitorHandle;
|
use super::output::MonitorHandle;
|
||||||
@@ -34,7 +35,7 @@ use super::types::wp_fractional_scaling::FractionalScalingManager;
|
|||||||
use super::types::wp_viewporter::ViewporterState;
|
use super::types::wp_viewporter::ViewporterState;
|
||||||
use super::types::xdg_activation::XdgActivationState;
|
use super::types::xdg_activation::XdgActivationState;
|
||||||
use super::window::{WindowRequests, WindowState};
|
use super::window::{WindowRequests, WindowState};
|
||||||
use super::WindowId;
|
use super::{WaylandError, WindowId};
|
||||||
|
|
||||||
/// Winit's Wayland state.
|
/// Winit's Wayland state.
|
||||||
pub struct WinitState {
|
pub struct WinitState {
|
||||||
@@ -104,6 +105,10 @@ pub struct WinitState {
|
|||||||
|
|
||||||
/// Loop handle to re-register event sources, such as keyboard repeat.
|
/// Loop handle to re-register event sources, such as keyboard repeat.
|
||||||
pub loop_handle: LoopHandle<'static, Self>,
|
pub loop_handle: LoopHandle<'static, Self>,
|
||||||
|
|
||||||
|
/// Whether we have dispatched events to the user thus we want to
|
||||||
|
/// send `AboutToWait` and normally wakeup the user.
|
||||||
|
pub dispatched_events: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WinitState {
|
impl WinitState {
|
||||||
@@ -111,14 +116,16 @@ impl WinitState {
|
|||||||
globals: &GlobalList,
|
globals: &GlobalList,
|
||||||
queue_handle: &QueueHandle<Self>,
|
queue_handle: &QueueHandle<Self>,
|
||||||
loop_handle: LoopHandle<'static, WinitState>,
|
loop_handle: LoopHandle<'static, WinitState>,
|
||||||
) -> Result<Self, Box<dyn Error>> {
|
) -> Result<Self, OsError> {
|
||||||
let registry_state = RegistryState::new(globals);
|
let registry_state = RegistryState::new(globals);
|
||||||
let compositor_state = CompositorState::bind(globals, queue_handle)?;
|
let compositor_state =
|
||||||
|
CompositorState::bind(globals, queue_handle).map_err(WaylandError::Bind)?;
|
||||||
let subcompositor_state = SubcompositorState::bind(
|
let subcompositor_state = SubcompositorState::bind(
|
||||||
compositor_state.wl_compositor().clone(),
|
compositor_state.wl_compositor().clone(),
|
||||||
globals,
|
globals,
|
||||||
queue_handle,
|
queue_handle,
|
||||||
)?;
|
)
|
||||||
|
.map_err(WaylandError::Bind)?;
|
||||||
|
|
||||||
let output_state = OutputState::new(globals, queue_handle);
|
let output_state = OutputState::new(globals, queue_handle);
|
||||||
let monitors = output_state.outputs().map(MonitorHandle::new).collect();
|
let monitors = output_state.outputs().map(MonitorHandle::new).collect();
|
||||||
@@ -143,9 +150,9 @@ impl WinitState {
|
|||||||
subcompositor_state: Arc::new(subcompositor_state),
|
subcompositor_state: Arc::new(subcompositor_state),
|
||||||
output_state,
|
output_state,
|
||||||
seat_state,
|
seat_state,
|
||||||
shm: Shm::bind(globals, queue_handle)?,
|
shm: Shm::bind(globals, queue_handle).map_err(WaylandError::Bind)?,
|
||||||
|
|
||||||
xdg_shell: XdgShell::bind(globals, queue_handle)?,
|
xdg_shell: XdgShell::bind(globals, queue_handle).map_err(WaylandError::Bind)?,
|
||||||
xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(),
|
xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(),
|
||||||
|
|
||||||
windows: Default::default(),
|
windows: Default::default(),
|
||||||
@@ -167,6 +174,8 @@ impl WinitState {
|
|||||||
monitors: Arc::new(Mutex::new(monitors)),
|
monitors: Arc::new(Mutex::new(monitors)),
|
||||||
events_sink: EventSink::new(),
|
events_sink: EventSink::new(),
|
||||||
loop_handle,
|
loop_handle,
|
||||||
|
// Make it true by default.
|
||||||
|
dispatched_events: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +330,27 @@ impl CompositorHandler for WinitState {
|
|||||||
self.scale_factor_changed(surface, scale_factor as f64, true)
|
self.scale_factor_changed(surface, scale_factor as f64, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frame(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: u32) {}
|
fn frame(&mut self, _: &Connection, _: &QueueHandle<Self>, surface: &WlSurface, _: u32) {
|
||||||
|
let window_id = super::make_wid(surface);
|
||||||
|
let window = match self.windows.get_mut().get(&window_id) {
|
||||||
|
Some(window) => window,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// In case we have a redraw requested we must indicate the wake up.
|
||||||
|
if self
|
||||||
|
.window_requests
|
||||||
|
.get_mut()
|
||||||
|
.get(&window_id)
|
||||||
|
.unwrap()
|
||||||
|
.redraw_requested
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
{
|
||||||
|
self.dispatched_events = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.lock().unwrap().frame_callback_received();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProvidesRegistryState for WinitState {
|
impl ProvidesRegistryState for WinitState {
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::
|
|||||||
|
|
||||||
use sctk::globals::GlobalData;
|
use sctk::globals::GlobalData;
|
||||||
|
|
||||||
|
use crate::event_loop::AsyncRequestSerial;
|
||||||
use crate::platform_impl::wayland::state::WinitState;
|
use crate::platform_impl::wayland::state::WinitState;
|
||||||
|
use crate::platform_impl::WindowId;
|
||||||
|
use crate::window::ActivationToken;
|
||||||
|
|
||||||
pub struct XdgActivationState {
|
pub struct XdgActivationState {
|
||||||
xdg_activation: XdgActivationV1,
|
xdg_activation: XdgActivationV1,
|
||||||
@@ -62,16 +65,29 @@ impl Dispatch<XdgActivationTokenV1, XdgActivationTokenData, WinitState> for XdgA
|
|||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
state
|
let global = state
|
||||||
.xdg_activation
|
.xdg_activation
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("got xdg_activation event without global.")
|
.expect("got xdg_activation event without global.")
|
||||||
.global()
|
.global();
|
||||||
.activate(token, &data.surface);
|
|
||||||
|
|
||||||
// Mark that no request attention is in process.
|
match data {
|
||||||
if let Some(attention_requested) = data.attention_requested.upgrade() {
|
XdgActivationTokenData::Attention((surface, fence)) => {
|
||||||
attention_requested.store(false, std::sync::atomic::Ordering::Relaxed);
|
global.activate(token, surface);
|
||||||
|
// Mark that no request attention is in process.
|
||||||
|
if let Some(attention_requested) = fence.upgrade() {
|
||||||
|
attention_requested.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XdgActivationTokenData::Obtain((window_id, serial)) => {
|
||||||
|
state.events_sink.push_window_event(
|
||||||
|
crate::event::WindowEvent::ActivationTokenDone {
|
||||||
|
serial: *serial,
|
||||||
|
token: ActivationToken::_new(token),
|
||||||
|
},
|
||||||
|
*window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy.destroy();
|
proxy.destroy();
|
||||||
@@ -79,24 +95,11 @@ impl Dispatch<XdgActivationTokenV1, XdgActivationTokenData, WinitState> for XdgA
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The data associated with the activation request.
|
/// The data associated with the activation request.
|
||||||
pub struct XdgActivationTokenData {
|
pub enum XdgActivationTokenData {
|
||||||
/// The surface we're raising.
|
/// Request user attention for the given surface.
|
||||||
surface: WlSurface,
|
Attention((WlSurface, Weak<AtomicBool>)),
|
||||||
|
/// Get a token to be passed outside of the winit.
|
||||||
/// Flag to throttle attention requests.
|
Obtain((WindowId, AsyncRequestSerial)),
|
||||||
attention_requested: Weak<AtomicBool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XdgActivationTokenData {
|
|
||||||
/// Create a new data.
|
|
||||||
///
|
|
||||||
/// The `attenteion_requested` is marked as `false` on complition.
|
|
||||||
pub fn new(surface: WlSurface, attention_requested: Weak<AtomicBool>) -> Self {
|
|
||||||
Self {
|
|
||||||
surface,
|
|
||||||
attention_requested,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate_dispatch!(WinitState: [ XdgActivationV1: GlobalData] => XdgActivationState);
|
delegate_dispatch!(WinitState: [ XdgActivationV1: GlobalData] => XdgActivationState);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use sctk::shell::WaylandSurface;
|
|||||||
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||||
use crate::event::{Ime, WindowEvent};
|
use crate::event::{Ime, WindowEvent};
|
||||||
|
use crate::event_loop::AsyncRequestSerial;
|
||||||
use crate::platform_impl::{
|
use crate::platform_impl::{
|
||||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError,
|
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError,
|
||||||
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
||||||
@@ -35,9 +36,9 @@ use super::event_loop::sink::EventSink;
|
|||||||
use super::output::MonitorHandle;
|
use super::output::MonitorHandle;
|
||||||
use super::state::WinitState;
|
use super::state::WinitState;
|
||||||
use super::types::xdg_activation::XdgActivationTokenData;
|
use super::types::xdg_activation::XdgActivationTokenData;
|
||||||
use super::{EventLoopWindowTarget, WindowId};
|
use super::{EventLoopWindowTarget, WaylandError, WindowId};
|
||||||
|
|
||||||
mod state;
|
pub(crate) mod state;
|
||||||
|
|
||||||
pub use state::WindowState;
|
pub use state::WindowState;
|
||||||
|
|
||||||
@@ -168,6 +169,14 @@ impl Window {
|
|||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Activate the window when the token is passed.
|
||||||
|
if let (Some(xdg_activation), Some(token)) = (
|
||||||
|
xdg_activation.as_ref(),
|
||||||
|
platform_attributes.activation_token,
|
||||||
|
) {
|
||||||
|
xdg_activation.activate(token._token, &surface);
|
||||||
|
}
|
||||||
|
|
||||||
// XXX Do initial commit.
|
// XXX Do initial commit.
|
||||||
window.commit();
|
window.commit();
|
||||||
|
|
||||||
@@ -196,18 +205,18 @@ impl Window {
|
|||||||
let event_queue = wayland_source.queue();
|
let event_queue = wayland_source.queue();
|
||||||
|
|
||||||
// Do a roundtrip.
|
// Do a roundtrip.
|
||||||
event_queue.roundtrip(&mut state).map_err(|_| {
|
event_queue.roundtrip(&mut state).map_err(|error| {
|
||||||
os_error!(OsError::WaylandMisc(
|
os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(
|
||||||
"failed to do initial roundtrip for the window."
|
error
|
||||||
))
|
))))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// XXX Wait for the initial configure to arrive.
|
// XXX Wait for the initial configure to arrive.
|
||||||
while !window_state.lock().unwrap().is_configured() {
|
while !window_state.lock().unwrap().is_configured() {
|
||||||
event_queue.blocking_dispatch(&mut state).map_err(|_| {
|
event_queue.blocking_dispatch(&mut state).map_err(|error| {
|
||||||
os_error!(OsError::WaylandMisc(
|
os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(
|
||||||
"failed to dispatch queue while waiting for initial configure."
|
error
|
||||||
))
|
))))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,10 +287,23 @@ impl Window {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn request_redraw(&self) {
|
pub fn request_redraw(&self) {
|
||||||
self.window_requests
|
// NOTE: try to not wake up the loop when the event was already scheduled and not yet
|
||||||
|
// processed by the loop, because if at this point the value was `true` it could only
|
||||||
|
// mean that the loop still haven't dispatched the value to the client and will do
|
||||||
|
// eventually, resetting it to `false`.
|
||||||
|
if self
|
||||||
|
.window_requests
|
||||||
.redraw_requested
|
.redraw_requested
|
||||||
.store(true, Ordering::Relaxed);
|
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||||
self.event_loop_awakener.ping();
|
.is_ok()
|
||||||
|
{
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn pre_present_notify(&self) {
|
||||||
|
self.window_state.lock().unwrap().request_frame_callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -292,13 +314,14 @@ impl Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_inner_size(&self, size: Size) {
|
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||||
// TODO should we issue the resize event? I don't think other platforms do so.
|
|
||||||
let mut window_state = self.window_state.lock().unwrap();
|
let mut window_state = self.window_state.lock().unwrap();
|
||||||
let scale_factor = window_state.scale_factor();
|
let scale_factor = window_state.scale_factor();
|
||||||
window_state.resize(size.to_logical::<u32>(scale_factor));
|
window_state.resize(size.to_logical::<u32>(scale_factor));
|
||||||
|
|
||||||
self.request_redraw();
|
self.request_redraw();
|
||||||
|
|
||||||
|
Some(window_state.inner_size().to_physical(scale_factor))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the minimum inner size for the window.
|
/// Set the minimum inner size for the window.
|
||||||
@@ -495,13 +518,31 @@ impl Window {
|
|||||||
|
|
||||||
self.attention_requested.store(true, Ordering::Relaxed);
|
self.attention_requested.store(true, Ordering::Relaxed);
|
||||||
let surface = self.surface().clone();
|
let surface = self.surface().clone();
|
||||||
let data =
|
let data = XdgActivationTokenData::Attention((
|
||||||
XdgActivationTokenData::new(surface.clone(), Arc::downgrade(&self.attention_requested));
|
surface.clone(),
|
||||||
|
Arc::downgrade(&self.attention_requested),
|
||||||
|
));
|
||||||
let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
|
let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
|
||||||
xdg_activation_token.set_surface(&surface);
|
xdg_activation_token.set_surface(&surface);
|
||||||
xdg_activation_token.commit();
|
xdg_activation_token.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||||
|
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||||
|
Some(xdg_activation) => xdg_activation,
|
||||||
|
None => return Err(NotSupportedError::new()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let serial = AsyncRequestSerial::get();
|
||||||
|
|
||||||
|
let data = XdgActivationTokenData::Obtain((self.window_id, serial));
|
||||||
|
let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
|
||||||
|
xdg_activation_token.set_surface(self.surface());
|
||||||
|
xdg_activation_token.commit();
|
||||||
|
|
||||||
|
Ok(serial)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||||
self.window_state.lock().unwrap().set_cursor_grab(mode)
|
self.window_state.lock().unwrap().set_cursor_grab(mode)
|
||||||
@@ -533,9 +574,7 @@ impl Window {
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let region = Region::new(&*self.compositor).map_err(|_| {
|
let region = Region::new(&*self.compositor).map_err(|_| {
|
||||||
ExternalError::Os(os_error!(OsError::WaylandMisc(
|
ExternalError::Os(os_error!(OsError::Misc("failed to set input region.")))
|
||||||
"failed to set input region."
|
|
||||||
)))
|
|
||||||
})?;
|
})?;
|
||||||
region.add(0, 0, 0, 0);
|
region.add(0, 0, 0, 0);
|
||||||
surface.set_input_region(Some(region.wl_region()));
|
surface.set_input_region(Some(region.wl_region()));
|
||||||
@@ -573,11 +612,6 @@ impl Window {
|
|||||||
self.window_state.lock().unwrap().set_ime_purpose(purpose);
|
self.window_state.lock().unwrap().set_ime_purpose(purpose);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn display(&self) -> &WlDisplay {
|
|
||||||
&self.display
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn surface(&self) -> &WlSurface {
|
pub fn surface(&self) -> &WlSurface {
|
||||||
self.window.wl_surface()
|
self.window.wl_surface()
|
||||||
|
|||||||
@@ -125,30 +125,74 @@ pub struct WindowState {
|
|||||||
/// sends `None` for the new size in the configure.
|
/// sends `None` for the new size in the configure.
|
||||||
stateless_size: LogicalSize<u32>,
|
stateless_size: LogicalSize<u32>,
|
||||||
|
|
||||||
|
/// The state of the frame callback.
|
||||||
|
frame_callback_state: FrameCallbackState,
|
||||||
|
|
||||||
viewport: Option<WpViewport>,
|
viewport: Option<WpViewport>,
|
||||||
fractional_scale: Option<WpFractionalScaleV1>,
|
fractional_scale: Option<WpFractionalScaleV1>,
|
||||||
}
|
|
||||||
|
|
||||||
/// The state of the cursor grabs.
|
/// Whether the client side decorations have pending move operations.
|
||||||
#[derive(Clone, Copy)]
|
///
|
||||||
struct GrabState {
|
/// The value is the serial of the event triggered moved.
|
||||||
/// The grab mode requested by the user.
|
has_pending_move: Option<u32>,
|
||||||
user_grab_mode: CursorGrabMode,
|
|
||||||
|
|
||||||
/// The current grab mode.
|
|
||||||
current_grab_mode: CursorGrabMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GrabState {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
user_grab_mode: CursorGrabMode::None,
|
|
||||||
current_grab_mode: CursorGrabMode::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowState {
|
impl WindowState {
|
||||||
|
/// Create new window state.
|
||||||
|
pub fn new(
|
||||||
|
connection: Connection,
|
||||||
|
queue_handle: &QueueHandle<WinitState>,
|
||||||
|
winit_state: &WinitState,
|
||||||
|
size: LogicalSize<u32>,
|
||||||
|
window: Window,
|
||||||
|
theme: Option<Theme>,
|
||||||
|
) -> Self {
|
||||||
|
let compositor = winit_state.compositor_state.clone();
|
||||||
|
let pointer_constraints = winit_state.pointer_constraints.clone();
|
||||||
|
let viewport = winit_state
|
||||||
|
.viewporter_state
|
||||||
|
.as_ref()
|
||||||
|
.map(|state| state.get_viewport(window.wl_surface(), queue_handle));
|
||||||
|
let fractional_scale = winit_state
|
||||||
|
.fractional_scaling_manager
|
||||||
|
.as_ref()
|
||||||
|
.map(|fsm| fsm.fractional_scaling(window.wl_surface(), queue_handle));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
compositor,
|
||||||
|
connection,
|
||||||
|
csd_fails: false,
|
||||||
|
cursor_grab_mode: GrabState::new(),
|
||||||
|
cursor_icon: CursorIcon::Default,
|
||||||
|
cursor_visible: true,
|
||||||
|
decorate: true,
|
||||||
|
fractional_scale,
|
||||||
|
frame: None,
|
||||||
|
frame_callback_state: FrameCallbackState::None,
|
||||||
|
has_focus: false,
|
||||||
|
has_pending_move: None,
|
||||||
|
ime_allowed: false,
|
||||||
|
ime_purpose: ImePurpose::Normal,
|
||||||
|
last_configure: None,
|
||||||
|
max_inner_size: None,
|
||||||
|
min_inner_size: MIN_WINDOW_SIZE,
|
||||||
|
pointer_constraints,
|
||||||
|
pointers: Default::default(),
|
||||||
|
queue_handle: queue_handle.clone(),
|
||||||
|
resizable: true,
|
||||||
|
scale_factor: 1.,
|
||||||
|
shm: winit_state.shm.wl_shm().clone(),
|
||||||
|
size,
|
||||||
|
stateless_size: size,
|
||||||
|
text_inputs: Vec::new(),
|
||||||
|
theme,
|
||||||
|
title: String::default(),
|
||||||
|
transparent: false,
|
||||||
|
viewport,
|
||||||
|
window: ManuallyDrop::new(window),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Apply closure on the given pointer.
|
/// Apply closure on the given pointer.
|
||||||
fn apply_on_poiner<F: Fn(&ThemedPointer<WinitPointerData>, &WinitPointerData)>(
|
fn apply_on_poiner<F: Fn(&ThemedPointer<WinitPointerData>, &WinitPointerData)>(
|
||||||
&self,
|
&self,
|
||||||
@@ -163,6 +207,33 @@ impl WindowState {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current state of the frame callback.
|
||||||
|
pub fn frame_callback_state(&self) -> FrameCallbackState {
|
||||||
|
self.frame_callback_state
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The frame callback was received, but not yet sent to the user.
|
||||||
|
pub fn frame_callback_received(&mut self) {
|
||||||
|
self.frame_callback_state = FrameCallbackState::Received;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the frame callbacks state.
|
||||||
|
pub fn frame_callback_reset(&mut self) {
|
||||||
|
self.frame_callback_state = FrameCallbackState::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request a frame callback if we don't have one for this window in flight.
|
||||||
|
pub fn request_frame_callback(&mut self) {
|
||||||
|
let surface = self.window.wl_surface();
|
||||||
|
match self.frame_callback_state {
|
||||||
|
FrameCallbackState::None | FrameCallbackState::Received => {
|
||||||
|
self.frame_callback_state = FrameCallbackState::Requested;
|
||||||
|
surface.frame(&self.queue_handle, surface.clone());
|
||||||
|
}
|
||||||
|
FrameCallbackState::Requested => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn configure(
|
pub fn configure(
|
||||||
&mut self,
|
&mut self,
|
||||||
configure: WindowConfigure,
|
configure: WindowConfigure,
|
||||||
@@ -279,7 +350,7 @@ impl WindowState {
|
|||||||
FrameAction::Maximize => self.window.set_maximized(),
|
FrameAction::Maximize => self.window.set_maximized(),
|
||||||
FrameAction::UnMaximize => self.window.unset_maximized(),
|
FrameAction::UnMaximize => self.window.unset_maximized(),
|
||||||
FrameAction::Close => WinitState::queue_close(updates, window_id),
|
FrameAction::Close => WinitState::queue_close(updates, window_id),
|
||||||
FrameAction::Move => self.window.move_(seat, serial),
|
FrameAction::Move => self.has_pending_move = Some(serial),
|
||||||
FrameAction::Resize(edge) => self.window.resize(seat, serial, edge),
|
FrameAction::Resize(edge) => self.window.resize(seat, serial, edge),
|
||||||
FrameAction::ShowMenu(x, y) => self.window.show_window_menu(seat, serial, (x, y)),
|
FrameAction::ShowMenu(x, y) => self.window.show_window_menu(seat, serial, (x, y)),
|
||||||
};
|
};
|
||||||
@@ -294,9 +365,26 @@ impl WindowState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move the point over decorations.
|
// Move the point over decorations.
|
||||||
pub fn frame_point_moved(&mut self, surface: &WlSurface, x: f64, y: f64) -> Option<&str> {
|
pub fn frame_point_moved(
|
||||||
|
&mut self,
|
||||||
|
seat: &WlSeat,
|
||||||
|
surface: &WlSurface,
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
) -> Option<&str> {
|
||||||
|
// Take the serial if we had any, so it doesn't stick around.
|
||||||
|
let serial = self.has_pending_move.take();
|
||||||
|
|
||||||
if let Some(frame) = self.frame.as_mut() {
|
if let Some(frame) = self.frame.as_mut() {
|
||||||
frame.click_point_moved(surface, x, y)
|
let cursor = frame.click_point_moved(surface, x, y);
|
||||||
|
// If we have a cursor change, that means that cursor is over the decorations,
|
||||||
|
// so try to apply move.
|
||||||
|
if let Some(serial) = cursor.is_some().then_some(serial).flatten() {
|
||||||
|
self.window.move_(seat, serial);
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
cursor
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -362,66 +450,13 @@ impl WindowState {
|
|||||||
.map(|configure| configure.decoration_mode == DecorationMode::Client)
|
.map(|configure| configure.decoration_mode == DecorationMode::Client)
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
if let Some(frame) = csd.then_some(self.frame.as_ref()).flatten() {
|
if let Some(frame) = csd.then_some(self.frame.as_ref()).flatten() {
|
||||||
frame.is_hidden()
|
!frame.is_hidden()
|
||||||
} else {
|
} else {
|
||||||
// Server side decorations.
|
// Server side decorations.
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new window state.
|
|
||||||
pub fn new(
|
|
||||||
connection: Connection,
|
|
||||||
queue_handle: &QueueHandle<WinitState>,
|
|
||||||
winit_state: &WinitState,
|
|
||||||
size: LogicalSize<u32>,
|
|
||||||
window: Window,
|
|
||||||
theme: Option<Theme>,
|
|
||||||
) -> Self {
|
|
||||||
let compositor = winit_state.compositor_state.clone();
|
|
||||||
let pointer_constraints = winit_state.pointer_constraints.clone();
|
|
||||||
let viewport = winit_state
|
|
||||||
.viewporter_state
|
|
||||||
.as_ref()
|
|
||||||
.map(|state| state.get_viewport(window.wl_surface(), queue_handle));
|
|
||||||
let fractional_scale = winit_state
|
|
||||||
.fractional_scaling_manager
|
|
||||||
.as_ref()
|
|
||||||
.map(|fsm| fsm.fractional_scaling(window.wl_surface(), queue_handle));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
compositor,
|
|
||||||
connection,
|
|
||||||
theme,
|
|
||||||
csd_fails: false,
|
|
||||||
decorate: true,
|
|
||||||
cursor_grab_mode: GrabState::new(),
|
|
||||||
cursor_icon: CursorIcon::Default,
|
|
||||||
cursor_visible: true,
|
|
||||||
fractional_scale,
|
|
||||||
frame: None,
|
|
||||||
has_focus: false,
|
|
||||||
ime_allowed: false,
|
|
||||||
ime_purpose: ImePurpose::Normal,
|
|
||||||
last_configure: None,
|
|
||||||
max_inner_size: None,
|
|
||||||
min_inner_size: MIN_WINDOW_SIZE,
|
|
||||||
pointer_constraints,
|
|
||||||
pointers: Default::default(),
|
|
||||||
queue_handle: queue_handle.clone(),
|
|
||||||
scale_factor: 1.,
|
|
||||||
shm: winit_state.shm.wl_shm().clone(),
|
|
||||||
size,
|
|
||||||
stateless_size: size,
|
|
||||||
text_inputs: Vec::new(),
|
|
||||||
title: String::default(),
|
|
||||||
transparent: false,
|
|
||||||
resizable: true,
|
|
||||||
viewport,
|
|
||||||
window: ManuallyDrop::new(window),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the outer size of the window.
|
/// Get the outer size of the window.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn outer_size(&self) -> LogicalSize<u32> {
|
pub fn outer_size(&self) -> LogicalSize<u32> {
|
||||||
@@ -677,7 +712,7 @@ impl WindowState {
|
|||||||
// Positon can be set only for locked cursor.
|
// Positon can be set only for locked cursor.
|
||||||
if self.cursor_grab_mode.current_grab_mode != CursorGrabMode::Locked {
|
if self.cursor_grab_mode.current_grab_mode != CursorGrabMode::Locked {
|
||||||
return Err(ExternalError::Os(os_error!(
|
return Err(ExternalError::Os(os_error!(
|
||||||
crate::platform_impl::OsError::WaylandMisc(
|
crate::platform_impl::OsError::Misc(
|
||||||
"cursor position can be set only for locked cursor."
|
"cursor position can be set only for locked cursor."
|
||||||
)
|
)
|
||||||
)));
|
)));
|
||||||
@@ -869,6 +904,37 @@ impl Drop for WindowState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The state of the cursor grabs.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct GrabState {
|
||||||
|
/// The grab mode requested by the user.
|
||||||
|
user_grab_mode: CursorGrabMode,
|
||||||
|
|
||||||
|
/// The current grab mode.
|
||||||
|
current_grab_mode: CursorGrabMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GrabState {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
user_grab_mode: CursorGrabMode::None,
|
||||||
|
current_grab_mode: CursorGrabMode::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The state of the frame callback.
|
||||||
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum FrameCallbackState {
|
||||||
|
/// No frame callback was requsted.
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
/// The frame callback was requested, but not yet arrived, the redraw events are throttled.
|
||||||
|
Requested,
|
||||||
|
/// The callback was marked as done, and user could receive redraw requested
|
||||||
|
Received,
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ResizeDirection> for ResizeEdge {
|
impl From<ResizeDirection> for ResizeEdge {
|
||||||
fn from(value: ResizeDirection) -> Self {
|
fn from(value: ResizeDirection) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
|||||||
200
src/platform_impl/linux/x11/activation.rs
Normal file
200
src/platform_impl/linux/x11/activation.rs
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
//! X11 activation handling.
|
||||||
|
//!
|
||||||
|
//! X11 has a "startup notification" specification similar to Wayland's, see this URL:
|
||||||
|
//! <https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt>
|
||||||
|
|
||||||
|
use super::{atoms::*, VoidCookie, X11Error, XConnection};
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||||
|
|
||||||
|
impl XConnection {
|
||||||
|
/// "Request" a new activation token from the server.
|
||||||
|
pub(crate) fn request_activation_token(&self, window_title: &str) -> Result<String, X11Error> {
|
||||||
|
// The specification recommends the format "hostname+pid+"_TIME"+current time"
|
||||||
|
let uname = rustix::system::uname();
|
||||||
|
let pid = rustix::process::getpid();
|
||||||
|
let time = self.timestamp();
|
||||||
|
|
||||||
|
let activation_token = format!(
|
||||||
|
"{}{}_TIME{}",
|
||||||
|
uname.nodename().to_str().unwrap_or("winit"),
|
||||||
|
pid.as_raw_nonzero(),
|
||||||
|
time
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set up the new startup notification.
|
||||||
|
let notification = {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
buffer.extend_from_slice(b"new: ID=");
|
||||||
|
quote_string(&activation_token, &mut buffer);
|
||||||
|
buffer.extend_from_slice(b" NAME=");
|
||||||
|
quote_string(window_title, &mut buffer);
|
||||||
|
buffer.extend_from_slice(b" SCREEN=");
|
||||||
|
push_display(&mut buffer, &self.default_screen_index());
|
||||||
|
|
||||||
|
CString::new(buffer)
|
||||||
|
.map_err(|err| X11Error::InvalidActivationToken(err.into_vec()))?
|
||||||
|
.into_bytes_with_nul()
|
||||||
|
};
|
||||||
|
self.send_message(¬ification)?;
|
||||||
|
|
||||||
|
Ok(activation_token)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finish launching a window with the given startup ID.
|
||||||
|
pub(crate) fn remove_activation_token(
|
||||||
|
&self,
|
||||||
|
window: xproto::Window,
|
||||||
|
startup_id: &str,
|
||||||
|
) -> Result<(), X11Error> {
|
||||||
|
let atoms = self.atoms();
|
||||||
|
|
||||||
|
// Set the _NET_STARTUP_ID property on the window.
|
||||||
|
self.xcb_connection()
|
||||||
|
.change_property(
|
||||||
|
xproto::PropMode::REPLACE,
|
||||||
|
window,
|
||||||
|
atoms[_NET_STARTUP_ID],
|
||||||
|
xproto::AtomEnum::STRING,
|
||||||
|
8,
|
||||||
|
startup_id.len().try_into().unwrap(),
|
||||||
|
startup_id.as_bytes(),
|
||||||
|
)?
|
||||||
|
.check()?;
|
||||||
|
|
||||||
|
// Send the message indicating that the startup is over.
|
||||||
|
let message = {
|
||||||
|
const MESSAGE_ROOT: &str = "remove: ID=";
|
||||||
|
|
||||||
|
let mut buffer = Vec::with_capacity(
|
||||||
|
MESSAGE_ROOT
|
||||||
|
.len()
|
||||||
|
.checked_add(startup_id.len())
|
||||||
|
.and_then(|x| x.checked_add(1))
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
buffer.extend_from_slice(MESSAGE_ROOT.as_bytes());
|
||||||
|
quote_string(startup_id, &mut buffer);
|
||||||
|
CString::new(buffer)
|
||||||
|
.map_err(|err| X11Error::InvalidActivationToken(err.into_vec()))?
|
||||||
|
.into_bytes_with_nul()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.send_message(&message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a startup notification message to the window manager.
|
||||||
|
fn send_message(&self, message: &[u8]) -> Result<(), X11Error> {
|
||||||
|
let atoms = self.atoms();
|
||||||
|
|
||||||
|
// Create a new window to send the message over.
|
||||||
|
let screen = self.default_root();
|
||||||
|
let window = xproto::WindowWrapper::create_window(
|
||||||
|
self.xcb_connection(),
|
||||||
|
screen.root_depth,
|
||||||
|
screen.root,
|
||||||
|
-100,
|
||||||
|
-100,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
xproto::WindowClass::INPUT_OUTPUT,
|
||||||
|
screen.root_visual,
|
||||||
|
&xproto::CreateWindowAux::new()
|
||||||
|
.override_redirect(1)
|
||||||
|
.event_mask(
|
||||||
|
xproto::EventMask::STRUCTURE_NOTIFY | xproto::EventMask::PROPERTY_CHANGE,
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Serialize the messages in 20-byte chunks.
|
||||||
|
let mut message_type = atoms[_NET_STARTUP_INFO_BEGIN];
|
||||||
|
message
|
||||||
|
.chunks(20)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buffer = [0u8; 20];
|
||||||
|
buffer[..chunk.len()].copy_from_slice(chunk);
|
||||||
|
let event =
|
||||||
|
xproto::ClientMessageEvent::new(8, window.window(), message_type, buffer);
|
||||||
|
|
||||||
|
// Set the message type to the continuation atom for the next chunk.
|
||||||
|
message_type = atoms[_NET_STARTUP_INFO];
|
||||||
|
|
||||||
|
event
|
||||||
|
})
|
||||||
|
.try_for_each(|event| {
|
||||||
|
// Send each event in order.
|
||||||
|
self.xcb_connection()
|
||||||
|
.send_event(
|
||||||
|
false,
|
||||||
|
screen.root,
|
||||||
|
xproto::EventMask::PROPERTY_CHANGE,
|
||||||
|
event,
|
||||||
|
)
|
||||||
|
.map(VoidCookie::ignore_error)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quote a literal string as per the startup notification specification.
|
||||||
|
fn quote_string(s: &str, target: &mut Vec<u8>) {
|
||||||
|
let total_len = s.len().checked_add(3).expect("quote string overflow");
|
||||||
|
target.reserve(total_len);
|
||||||
|
|
||||||
|
// Add the opening quote.
|
||||||
|
target.push(b'"');
|
||||||
|
|
||||||
|
// Iterate over the string split by literal quotes.
|
||||||
|
s.as_bytes().split(|&b| b == b'"').for_each(|part| {
|
||||||
|
// Add the part.
|
||||||
|
target.extend_from_slice(part);
|
||||||
|
|
||||||
|
// Escape the quote.
|
||||||
|
target.push(b'\\');
|
||||||
|
target.push(b'"');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Un-escape the last quote.
|
||||||
|
target.remove(target.len() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a `Display` implementation to the buffer.
|
||||||
|
fn push_display(buffer: &mut Vec<u8>, display: &impl std::fmt::Display) {
|
||||||
|
struct Writer<'a> {
|
||||||
|
buffer: &'a mut Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::fmt::Write for Writer<'a> {
|
||||||
|
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||||
|
self.buffer.extend_from_slice(s.as_bytes());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(Writer { buffer }, "{}", display).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn properly_escapes_x11_messages() {
|
||||||
|
let assert_eq = |input: &str, output: &[u8]| {
|
||||||
|
let mut buf = vec![];
|
||||||
|
quote_string(input, &mut buf);
|
||||||
|
assert_eq!(buf, output);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq("", b"\"\"");
|
||||||
|
assert_eq("foo", b"\"foo\"");
|
||||||
|
assert_eq("foo\"bar", b"\"foo\\\"bar\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
116
src/platform_impl/linux/x11/atoms.rs
Normal file
116
src/platform_impl/linux/x11/atoms.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
//! Collects every atom used by the platform implementation.
|
||||||
|
|
||||||
|
use core::ops::Index;
|
||||||
|
|
||||||
|
macro_rules! atom_manager {
|
||||||
|
($($name:ident $(:$lit:literal)?),*) => {
|
||||||
|
x11rb::atom_manager! {
|
||||||
|
/// The atoms used by `winit`
|
||||||
|
pub(crate) Atoms: AtomsCookie {
|
||||||
|
$($name $(:$lit)?,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indices into the `Atoms` struct.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub(crate) enum AtomName {
|
||||||
|
$($name,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtomName {
|
||||||
|
pub(crate) fn atom_from(
|
||||||
|
self,
|
||||||
|
atoms: &Atoms
|
||||||
|
) -> &x11rb::protocol::xproto::Atom {
|
||||||
|
match self {
|
||||||
|
$(AtomName::$name => &atoms.$name,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
atom_manager! {
|
||||||
|
// General Use Atoms
|
||||||
|
CARD32,
|
||||||
|
UTF8_STRING,
|
||||||
|
WM_CHANGE_STATE,
|
||||||
|
WM_CLIENT_MACHINE,
|
||||||
|
WM_DELETE_WINDOW,
|
||||||
|
WM_PROTOCOLS,
|
||||||
|
WM_STATE,
|
||||||
|
XIM_SERVERS,
|
||||||
|
|
||||||
|
// Assorted ICCCM Atoms
|
||||||
|
_NET_WM_ICON,
|
||||||
|
_NET_WM_MOVERESIZE,
|
||||||
|
_NET_WM_NAME,
|
||||||
|
_NET_WM_PID,
|
||||||
|
_NET_WM_PING,
|
||||||
|
_NET_WM_STATE,
|
||||||
|
_NET_WM_STATE_ABOVE,
|
||||||
|
_NET_WM_STATE_BELOW,
|
||||||
|
_NET_WM_STATE_FULLSCREEN,
|
||||||
|
_NET_WM_STATE_HIDDEN,
|
||||||
|
_NET_WM_STATE_MAXIMIZED_HORZ,
|
||||||
|
_NET_WM_STATE_MAXIMIZED_VERT,
|
||||||
|
_NET_WM_WINDOW_TYPE,
|
||||||
|
|
||||||
|
// Activation atoms.
|
||||||
|
_NET_STARTUP_INFO_BEGIN,
|
||||||
|
_NET_STARTUP_INFO,
|
||||||
|
_NET_STARTUP_ID,
|
||||||
|
|
||||||
|
// WM window types.
|
||||||
|
_NET_WM_WINDOW_TYPE_DESKTOP,
|
||||||
|
_NET_WM_WINDOW_TYPE_DOCK,
|
||||||
|
_NET_WM_WINDOW_TYPE_TOOLBAR,
|
||||||
|
_NET_WM_WINDOW_TYPE_MENU,
|
||||||
|
_NET_WM_WINDOW_TYPE_UTILITY,
|
||||||
|
_NET_WM_WINDOW_TYPE_SPLASH,
|
||||||
|
_NET_WM_WINDOW_TYPE_DIALOG,
|
||||||
|
_NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
|
||||||
|
_NET_WM_WINDOW_TYPE_POPUP_MENU,
|
||||||
|
_NET_WM_WINDOW_TYPE_TOOLTIP,
|
||||||
|
_NET_WM_WINDOW_TYPE_NOTIFICATION,
|
||||||
|
_NET_WM_WINDOW_TYPE_COMBO,
|
||||||
|
_NET_WM_WINDOW_TYPE_DND,
|
||||||
|
_NET_WM_WINDOW_TYPE_NORMAL,
|
||||||
|
|
||||||
|
// Drag-N-Drop Atoms
|
||||||
|
XdndAware,
|
||||||
|
XdndEnter,
|
||||||
|
XdndLeave,
|
||||||
|
XdndDrop,
|
||||||
|
XdndPosition,
|
||||||
|
XdndStatus,
|
||||||
|
XdndActionPrivate,
|
||||||
|
XdndSelection,
|
||||||
|
XdndFinished,
|
||||||
|
XdndTypeList,
|
||||||
|
TextUriList: b"text/uri-list",
|
||||||
|
None: b"None",
|
||||||
|
|
||||||
|
// Miscellaneous Atoms
|
||||||
|
_GTK_THEME_VARIANT,
|
||||||
|
_MOTIF_WM_HINTS,
|
||||||
|
_NET_ACTIVE_WINDOW,
|
||||||
|
_NET_CLIENT_LIST,
|
||||||
|
_NET_FRAME_EXTENTS,
|
||||||
|
_NET_SUPPORTED,
|
||||||
|
_NET_SUPPORTING_WM_CHECK,
|
||||||
|
_XEMBED
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<AtomName> for Atoms {
|
||||||
|
type Output = x11rb::protocol::xproto::Atom;
|
||||||
|
|
||||||
|
fn index(&self, index: AtomName) -> &Self::Output {
|
||||||
|
index.atom_from(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use AtomName::*;
|
||||||
|
// Make sure `None` is still defined.
|
||||||
|
pub(crate) use core::option::Option::None;
|
||||||
@@ -7,55 +7,12 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use percent_encoding::percent_decode;
|
use percent_encoding::percent_decode;
|
||||||
|
use x11rb::protocol::xproto::{self, ConnectionExt};
|
||||||
|
|
||||||
use super::{ffi, util, XConnection, XError};
|
use super::{
|
||||||
|
atoms::{AtomName::None as DndNone, *},
|
||||||
#[derive(Debug)]
|
util, CookieResultExt, X11Error, XConnection,
|
||||||
pub(crate) struct DndAtoms {
|
};
|
||||||
pub enter: ffi::Atom,
|
|
||||||
pub leave: ffi::Atom,
|
|
||||||
pub drop: ffi::Atom,
|
|
||||||
pub position: ffi::Atom,
|
|
||||||
pub status: ffi::Atom,
|
|
||||||
pub action_private: ffi::Atom,
|
|
||||||
pub selection: ffi::Atom,
|
|
||||||
pub finished: ffi::Atom,
|
|
||||||
pub type_list: ffi::Atom,
|
|
||||||
pub uri_list: ffi::Atom,
|
|
||||||
pub none: ffi::Atom,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DndAtoms {
|
|
||||||
pub fn new(xconn: &Arc<XConnection>) -> Result<Self, XError> {
|
|
||||||
let names = [
|
|
||||||
b"XdndEnter\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndLeave\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndDrop\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndPosition\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndStatus\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndActionPrivate\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndSelection\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndFinished\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndTypeList\0".as_ptr() as *mut c_char,
|
|
||||||
b"text/uri-list\0".as_ptr() as *mut c_char,
|
|
||||||
b"None\0".as_ptr() as *mut c_char,
|
|
||||||
];
|
|
||||||
let atoms = unsafe { xconn.get_atoms(&names) }?;
|
|
||||||
Ok(DndAtoms {
|
|
||||||
enter: atoms[0],
|
|
||||||
leave: atoms[1],
|
|
||||||
drop: atoms[2],
|
|
||||||
position: atoms[3],
|
|
||||||
status: atoms[4],
|
|
||||||
action_private: atoms[5],
|
|
||||||
selection: atoms[6],
|
|
||||||
finished: atoms[7],
|
|
||||||
type_list: atoms[8],
|
|
||||||
uri_list: atoms[9],
|
|
||||||
none: atoms[10],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum DndState {
|
pub enum DndState {
|
||||||
@@ -86,22 +43,19 @@ impl From<io::Error> for DndDataParseError {
|
|||||||
|
|
||||||
pub(crate) struct Dnd {
|
pub(crate) struct Dnd {
|
||||||
xconn: Arc<XConnection>,
|
xconn: Arc<XConnection>,
|
||||||
pub atoms: DndAtoms,
|
|
||||||
// Populated by XdndEnter event handler
|
// Populated by XdndEnter event handler
|
||||||
pub version: Option<c_long>,
|
pub version: Option<c_long>,
|
||||||
pub type_list: Option<Vec<c_ulong>>,
|
pub type_list: Option<Vec<xproto::Atom>>,
|
||||||
// Populated by XdndPosition event handler
|
// Populated by XdndPosition event handler
|
||||||
pub source_window: Option<c_ulong>,
|
pub source_window: Option<xproto::Window>,
|
||||||
// Populated by SelectionNotify event handler (triggered by XdndPosition event handler)
|
// Populated by SelectionNotify event handler (triggered by XdndPosition event handler)
|
||||||
pub result: Option<Result<Vec<PathBuf>, DndDataParseError>>,
|
pub result: Option<Result<Vec<PathBuf>, DndDataParseError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dnd {
|
impl Dnd {
|
||||||
pub fn new(xconn: Arc<XConnection>) -> Result<Self, XError> {
|
pub fn new(xconn: Arc<XConnection>) -> Result<Self, X11Error> {
|
||||||
let atoms = DndAtoms::new(&xconn)?;
|
|
||||||
Ok(Dnd {
|
Ok(Dnd {
|
||||||
xconn,
|
xconn,
|
||||||
atoms,
|
|
||||||
version: None,
|
version: None,
|
||||||
type_list: None,
|
type_list: None,
|
||||||
source_window: None,
|
source_window: None,
|
||||||
@@ -118,71 +72,85 @@ impl Dnd {
|
|||||||
|
|
||||||
pub unsafe fn send_status(
|
pub unsafe fn send_status(
|
||||||
&self,
|
&self,
|
||||||
this_window: c_ulong,
|
this_window: xproto::Window,
|
||||||
target_window: c_ulong,
|
target_window: xproto::Window,
|
||||||
state: DndState,
|
state: DndState,
|
||||||
) -> Result<(), XError> {
|
) -> Result<(), X11Error> {
|
||||||
|
let atoms = self.xconn.atoms();
|
||||||
let (accepted, action) = match state {
|
let (accepted, action) = match state {
|
||||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
DndState::Accepted => (1, atoms[XdndActionPrivate]),
|
||||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
DndState::Rejected => (0, atoms[DndNone]),
|
||||||
};
|
};
|
||||||
self.xconn
|
self.xconn
|
||||||
.send_client_msg(
|
.send_client_msg(
|
||||||
target_window,
|
target_window,
|
||||||
target_window,
|
target_window,
|
||||||
self.atoms.status,
|
atoms[XdndStatus] as _,
|
||||||
None,
|
None,
|
||||||
[this_window as c_long, accepted, 0, 0, action],
|
[this_window, accepted, 0, 0, action as _],
|
||||||
)
|
)?
|
||||||
.flush()
|
.ignore_error();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn send_finished(
|
pub unsafe fn send_finished(
|
||||||
&self,
|
&self,
|
||||||
this_window: c_ulong,
|
this_window: xproto::Window,
|
||||||
target_window: c_ulong,
|
target_window: xproto::Window,
|
||||||
state: DndState,
|
state: DndState,
|
||||||
) -> Result<(), XError> {
|
) -> Result<(), X11Error> {
|
||||||
|
let atoms = self.xconn.atoms();
|
||||||
let (accepted, action) = match state {
|
let (accepted, action) = match state {
|
||||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
DndState::Accepted => (1, atoms[XdndActionPrivate]),
|
||||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
DndState::Rejected => (0, atoms[DndNone]),
|
||||||
};
|
};
|
||||||
self.xconn
|
self.xconn
|
||||||
.send_client_msg(
|
.send_client_msg(
|
||||||
target_window,
|
target_window,
|
||||||
target_window,
|
target_window,
|
||||||
self.atoms.finished,
|
atoms[XdndFinished] as _,
|
||||||
None,
|
None,
|
||||||
[this_window as c_long, accepted, action, 0, 0],
|
[this_window, accepted, action as _, 0, 0],
|
||||||
)
|
)?
|
||||||
.flush()
|
.ignore_error();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn get_type_list(
|
pub unsafe fn get_type_list(
|
||||||
&self,
|
&self,
|
||||||
source_window: c_ulong,
|
source_window: xproto::Window,
|
||||||
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
|
) -> Result<Vec<xproto::Atom>, util::GetPropertyError> {
|
||||||
self.xconn
|
let atoms = self.xconn.atoms();
|
||||||
.get_property(source_window, self.atoms.type_list, ffi::XA_ATOM)
|
self.xconn.get_property(
|
||||||
|
source_window,
|
||||||
|
atoms[XdndTypeList],
|
||||||
|
xproto::Atom::from(xproto::AtomEnum::ATOM),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) {
|
pub unsafe fn convert_selection(&self, window: xproto::Window, time: xproto::Timestamp) {
|
||||||
(self.xconn.xlib.XConvertSelection)(
|
let atoms = self.xconn.atoms();
|
||||||
self.xconn.display,
|
self.xconn
|
||||||
self.atoms.selection,
|
.xcb_connection()
|
||||||
self.atoms.uri_list,
|
.convert_selection(
|
||||||
self.atoms.selection,
|
window,
|
||||||
window,
|
atoms[XdndSelection],
|
||||||
time,
|
atoms[TextUriList],
|
||||||
);
|
atoms[XdndSelection],
|
||||||
|
time,
|
||||||
|
)
|
||||||
|
.expect_then_ignore_error("Failed to send XdndSelection event")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn read_data(
|
pub unsafe fn read_data(
|
||||||
&self,
|
&self,
|
||||||
window: c_ulong,
|
window: xproto::Window,
|
||||||
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
||||||
|
let atoms = self.xconn.atoms();
|
||||||
self.xconn
|
self.xconn
|
||||||
.get_property(window, self.atoms.selection, self.atoms.uri_list)
|
.get_property(window, atoms[XdndSelection], atoms[TextUriList])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_data(&self, data: &mut [c_uchar]) -> Result<Vec<PathBuf>, DndDataParseError> {
|
pub fn parse_data(&self, data: &mut [c_uchar]) -> Result<Vec<PathBuf>, DndDataParseError> {
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc};
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
use libc::{c_char, c_int, c_long, c_ulong};
|
collections::HashMap,
|
||||||
|
os::raw::{c_char, c_int, c_long, c_ulong},
|
||||||
use super::{
|
rc::Rc,
|
||||||
ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, DndState,
|
slice,
|
||||||
GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, XExtension,
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||||
|
use x11rb::x11_utils::Serialize;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
atoms::*, ffi, get_xtarget, mkdid, mkwid, monitor, util, CookieResultExt, Device, DeviceId,
|
||||||
|
DeviceInfo, Dnd, DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow,
|
||||||
|
WindowId, XExtension,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize},
|
dpi::{PhysicalPosition, PhysicalSize},
|
||||||
event::{DeviceEvent, ElementState, Event, Ime, RawKeyEvent, TouchPhase, WindowEvent},
|
event::{DeviceEvent, ElementState, Event, Ime, RawKeyEvent, TouchPhase, WindowEvent},
|
||||||
@@ -15,6 +23,10 @@ use crate::{
|
|||||||
keyboard::ModifiersState,
|
keyboard::ModifiersState,
|
||||||
platform_impl::platform::common::{keymap, xkb_state::KbdState},
|
platform_impl::platform::common::{keymap, xkb_state::KbdState},
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
|
event::InnerSizeWriter,
|
||||||
|
platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest},
|
||||||
|
};
|
||||||
|
|
||||||
/// The X11 documentation states: "Keycodes lie in the inclusive range `[8, 255]`".
|
/// The X11 documentation states: "Keycodes lie in the inclusive range `[8, 255]`".
|
||||||
const KEYCODE_OFFSET: u8 = 8;
|
const KEYCODE_OFFSET: u8 = 8;
|
||||||
@@ -31,11 +43,14 @@ pub(super) struct EventProcessor<T: 'static> {
|
|||||||
pub(super) kb_state: KbdState,
|
pub(super) kb_state: KbdState,
|
||||||
// Number of touch events currently in progress
|
// Number of touch events currently in progress
|
||||||
pub(super) num_touch: u32,
|
pub(super) num_touch: u32,
|
||||||
// Whether we've got a key release for the key press.
|
// This is the last pressed key that is repeatable (if it hasn't been
|
||||||
pub(super) got_key_release: bool,
|
// released).
|
||||||
|
//
|
||||||
|
// Used to detect key repeats.
|
||||||
|
pub(super) held_key_press: Option<u32>,
|
||||||
pub(super) first_touch: Option<u64>,
|
pub(super) first_touch: Option<u64>,
|
||||||
// Currently focused window belonging to this process
|
// Currently focused window belonging to this process
|
||||||
pub(super) active_window: Option<ffi::Window>,
|
pub(super) active_window: Option<xproto::Window>,
|
||||||
pub(super) is_composing: bool,
|
pub(super) is_composing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +65,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_window<F, Ret>(&self, window_id: ffi::Window, callback: F) -> Option<Ret>
|
pub(crate) fn with_window<F, Ret>(&self, window_id: xproto::Window, callback: F) -> Option<Ret>
|
||||||
where
|
where
|
||||||
F: Fn(&Arc<UnownedWindow>) -> Ret,
|
F: Fn(&Arc<UnownedWindow>) -> Ret,
|
||||||
{
|
{
|
||||||
@@ -74,7 +89,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_exists(&self, window_id: ffi::Window) -> bool {
|
fn window_exists(&self, window_id: xproto::Window) -> bool {
|
||||||
self.with_window(window_id, |_| ()).is_some()
|
self.with_window(window_id, |_| ()).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,9 +129,10 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
|
|
||||||
pub(super) fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
|
pub(super) fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
|
||||||
where
|
where
|
||||||
F: FnMut(Event<'_, T>),
|
F: FnMut(Event<T>),
|
||||||
{
|
{
|
||||||
let wt = get_xtarget(&self.target);
|
let wt = get_xtarget(&self.target);
|
||||||
|
let atoms = wt.x_connection().atoms();
|
||||||
// XFilterEvent tells us when an event has been discarded by the input method.
|
// XFilterEvent tells us when an event has been discarded by the input method.
|
||||||
// Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
|
// Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
|
||||||
// along with an extra copy of the KeyRelease events. This also prevents backspace and
|
// along with an extra copy of the KeyRelease events. This also prevents backspace and
|
||||||
@@ -137,42 +153,57 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
ffi::ClientMessage => {
|
ffi::ClientMessage => {
|
||||||
let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
|
let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
|
||||||
|
|
||||||
let window = client_msg.window;
|
let window = client_msg.window as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
if client_msg.data.get_long(0) as ffi::Atom == wt.wm_delete_window {
|
if client_msg.data.get_long(0) as xproto::Atom == wt.wm_delete_window {
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::CloseRequested,
|
event: WindowEvent::CloseRequested,
|
||||||
});
|
});
|
||||||
} else if client_msg.data.get_long(0) as ffi::Atom == wt.net_wm_ping {
|
} else if client_msg.data.get_long(0) as xproto::Atom == wt.net_wm_ping {
|
||||||
let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut();
|
let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut();
|
||||||
response_msg.window = wt.root;
|
let client_msg = xproto::ClientMessageEvent {
|
||||||
|
response_type: xproto::CLIENT_MESSAGE_EVENT,
|
||||||
|
format: response_msg.format as _,
|
||||||
|
sequence: response_msg.serial as _,
|
||||||
|
window: wt.root,
|
||||||
|
type_: response_msg.message_type as _,
|
||||||
|
data: xproto::ClientMessageData::from({
|
||||||
|
let [a, b, c, d, e]: [c_long; 5] =
|
||||||
|
response_msg.data.as_longs().try_into().unwrap();
|
||||||
|
[a as u32, b as u32, c as u32, d as u32, e as u32]
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
wt.xconn
|
wt.xconn
|
||||||
|
.xcb_connection()
|
||||||
.send_event(
|
.send_event(
|
||||||
|
false,
|
||||||
wt.root,
|
wt.root,
|
||||||
Some(ffi::SubstructureNotifyMask | ffi::SubstructureRedirectMask),
|
xproto::EventMask::SUBSTRUCTURE_NOTIFY
|
||||||
*response_msg,
|
| xproto::EventMask::SUBSTRUCTURE_REDIRECT,
|
||||||
|
client_msg.serialize(),
|
||||||
)
|
)
|
||||||
.queue();
|
.expect_then_ignore_error("Failed to send `ClientMessage` event.");
|
||||||
} else if client_msg.message_type == self.dnd.atoms.enter {
|
} else if client_msg.message_type == atoms[XdndEnter] as c_ulong {
|
||||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
let source_window = client_msg.data.get_long(0) as xproto::Window;
|
||||||
let flags = client_msg.data.get_long(1);
|
let flags = client_msg.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_value() - 1)) == 1;
|
||||||
if !has_more_types {
|
if !has_more_types {
|
||||||
let type_list = vec![
|
let type_list = vec![
|
||||||
client_msg.data.get_long(2) as c_ulong,
|
client_msg.data.get_long(2) as xproto::Atom,
|
||||||
client_msg.data.get_long(3) as c_ulong,
|
client_msg.data.get_long(3) as xproto::Atom,
|
||||||
client_msg.data.get_long(4) as c_ulong,
|
client_msg.data.get_long(4) as xproto::Atom,
|
||||||
];
|
];
|
||||||
self.dnd.type_list = Some(type_list);
|
self.dnd.type_list = Some(type_list);
|
||||||
} else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) }
|
} else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) }
|
||||||
{
|
{
|
||||||
self.dnd.type_list = Some(more_types);
|
self.dnd.type_list = Some(more_types);
|
||||||
}
|
}
|
||||||
} else if client_msg.message_type == self.dnd.atoms.position {
|
} else if client_msg.message_type == atoms[XdndPosition] as c_ulong {
|
||||||
// This event occurs every time the mouse moves while a file's being dragged
|
// This event occurs every time the mouse moves while a file's being dragged
|
||||||
// over our window. We emit HoveredFile in response; while the macOS backend
|
// over our window. We emit HoveredFile in response; while the macOS backend
|
||||||
// does that upon a drag entering, XDND doesn't have access to the actual drop
|
// does that upon a drag entering, XDND doesn't have access to the actual drop
|
||||||
@@ -181,7 +212,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
// supply position updates with `HoveredFile` or another event, implementing
|
// supply position updates with `HoveredFile` or another event, implementing
|
||||||
// that here would be trivial.
|
// that here would be trivial.
|
||||||
|
|
||||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
let source_window = client_msg.data.get_long(0) as xproto::Window;
|
||||||
|
|
||||||
// Equivalent to `(x << shift) | y`
|
// Equivalent to `(x << shift) | y`
|
||||||
// where `shift = mem::size_of::<c_short>() * 8`
|
// where `shift = mem::size_of::<c_short>() * 8`
|
||||||
@@ -199,7 +230,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
//let action = client_msg.data.get_long(4);
|
//let action = client_msg.data.get_long(4);
|
||||||
|
|
||||||
let accepted = if let Some(ref type_list) = self.dnd.type_list {
|
let accepted = if let Some(ref type_list) = self.dnd.type_list {
|
||||||
type_list.contains(&self.dnd.atoms.uri_list)
|
type_list.contains(&atoms[TextUriList])
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
@@ -209,11 +240,15 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
if self.dnd.result.is_none() {
|
if self.dnd.result.is_none() {
|
||||||
let time = if version >= 1 {
|
let time = if version >= 1 {
|
||||||
client_msg.data.get_long(3) as c_ulong
|
client_msg.data.get_long(3) as xproto::Timestamp
|
||||||
} else {
|
} else {
|
||||||
// In version 0, time isn't specified
|
// In version 0, time isn't specified
|
||||||
ffi::CurrentTime
|
x11rb::CURRENT_TIME
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Log this timestamp.
|
||||||
|
wt.xconn.set_timestamp(time);
|
||||||
|
|
||||||
// This results in the `SelectionNotify` event below
|
// This results in the `SelectionNotify` event below
|
||||||
self.dnd.convert_selection(window, time);
|
self.dnd.convert_selection(window, time);
|
||||||
}
|
}
|
||||||
@@ -229,7 +264,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
}
|
}
|
||||||
self.dnd.reset();
|
self.dnd.reset();
|
||||||
}
|
}
|
||||||
} else if client_msg.message_type == self.dnd.atoms.drop {
|
} else if client_msg.message_type == atoms[XdndDrop] as c_ulong {
|
||||||
let (source_window, state) = if let Some(source_window) = self.dnd.source_window
|
let (source_window, state) = if let Some(source_window) = self.dnd.source_window
|
||||||
{
|
{
|
||||||
if let Some(Ok(ref path_list)) = self.dnd.result {
|
if let Some(Ok(ref path_list)) = self.dnd.result {
|
||||||
@@ -244,7 +279,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
} else {
|
} else {
|
||||||
// `source_window` won't be part of our DND state if we already rejected the drop in our
|
// `source_window` won't be part of our DND state if we already rejected the drop in our
|
||||||
// `XdndPosition` handler.
|
// `XdndPosition` handler.
|
||||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
let source_window = client_msg.data.get_long(0) as xproto::Window;
|
||||||
(source_window, DndState::Rejected)
|
(source_window, DndState::Rejected)
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -253,7 +288,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
.expect("Failed to send `XdndFinished` message.");
|
.expect("Failed to send `XdndFinished` message.");
|
||||||
}
|
}
|
||||||
self.dnd.reset();
|
self.dnd.reset();
|
||||||
} else if client_msg.message_type == self.dnd.atoms.leave {
|
} else if client_msg.message_type == atoms[XdndLeave] as c_ulong {
|
||||||
self.dnd.reset();
|
self.dnd.reset();
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
@@ -265,10 +300,13 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
ffi::SelectionNotify => {
|
ffi::SelectionNotify => {
|
||||||
let xsel: &ffi::XSelectionEvent = xev.as_ref();
|
let xsel: &ffi::XSelectionEvent = xev.as_ref();
|
||||||
|
|
||||||
let window = xsel.requestor;
|
let window = xsel.requestor as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
if xsel.property == self.dnd.atoms.selection {
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xsel.time as xproto::Timestamp);
|
||||||
|
|
||||||
|
if xsel.property == atoms[XdndSelection] as c_ulong {
|
||||||
let mut result = None;
|
let mut result = None;
|
||||||
|
|
||||||
// This is where we receive data from drag and drop
|
// This is where we receive data from drag and drop
|
||||||
@@ -291,7 +329,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
|
|
||||||
ffi::ConfigureNotify => {
|
ffi::ConfigureNotify => {
|
||||||
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
||||||
let xwindow = xev.window;
|
let xwindow = xev.window as xproto::Window;
|
||||||
let window_id = mkwid(xwindow);
|
let window_id = mkwid(xwindow);
|
||||||
|
|
||||||
if let Some(window) = self.with_window(xwindow, Arc::clone) {
|
if let Some(window) = self.with_window(xwindow, Arc::clone) {
|
||||||
@@ -402,21 +440,27 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let old_inner_size = PhysicalSize::new(width, height);
|
let old_inner_size = PhysicalSize::new(width, height);
|
||||||
let mut new_inner_size = PhysicalSize::new(new_width, new_height);
|
let new_inner_size = PhysicalSize::new(new_width, new_height);
|
||||||
|
|
||||||
// Unlock shared state to prevent deadlock in callback below
|
// Unlock shared state to prevent deadlock in callback below
|
||||||
drop(shared_state_lock);
|
drop(shared_state_lock);
|
||||||
|
|
||||||
|
let inner_size = Arc::new(Mutex::new(new_inner_size));
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::ScaleFactorChanged {
|
event: WindowEvent::ScaleFactorChanged {
|
||||||
scale_factor: new_scale_factor,
|
scale_factor: new_scale_factor,
|
||||||
new_inner_size: &mut new_inner_size,
|
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||||
|
&inner_size,
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let new_inner_size = *inner_size.lock().unwrap();
|
||||||
|
drop(inner_size);
|
||||||
|
|
||||||
if new_inner_size != old_inner_size {
|
if new_inner_size != old_inner_size {
|
||||||
window.set_inner_size_physical(
|
window.request_inner_size_physical(
|
||||||
new_inner_size.width,
|
new_inner_size.width,
|
||||||
new_inner_size.height,
|
new_inner_size.height,
|
||||||
);
|
);
|
||||||
@@ -440,7 +484,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
// When this finally happens, the event will not be synthetic.
|
// When this finally happens, the event will not be synthetic.
|
||||||
shared_state_lock.dpi_adjusted = None;
|
shared_state_lock.dpi_adjusted = None;
|
||||||
} else {
|
} else {
|
||||||
window.set_inner_size_physical(adjusted_size.0, adjusted_size.1);
|
window.request_inner_size_physical(adjusted_size.0, adjusted_size.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,13 +510,13 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
// effect is that we waste some time trying to query unsupported properties.
|
// effect is that we waste some time trying to query unsupported properties.
|
||||||
wt.xconn.update_cached_wm_info(wt.root);
|
wt.xconn.update_cached_wm_info(wt.root);
|
||||||
|
|
||||||
self.with_window(xev.window, |window| {
|
self.with_window(xev.window as xproto::Window, |window| {
|
||||||
window.invalidate_cached_frame_extents();
|
window.invalidate_cached_frame_extents();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ffi::MapNotify => {
|
ffi::MapNotify => {
|
||||||
let xev: &ffi::XMapEvent = xev.as_ref();
|
let xev: &ffi::XMapEvent = xev.as_ref();
|
||||||
let window = xev.window;
|
let window = xev.window as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
// XXX re-issue the focus state when mapping the window.
|
// XXX re-issue the focus state when mapping the window.
|
||||||
@@ -491,7 +535,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
ffi::DestroyNotify => {
|
ffi::DestroyNotify => {
|
||||||
let xev: &ffi::XDestroyWindowEvent = xev.as_ref();
|
let xev: &ffi::XDestroyWindowEvent = xev.as_ref();
|
||||||
|
|
||||||
let window = xev.window;
|
let window = xev.window as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
// In the event that the window's been destroyed without being dropped first, we
|
// In the event that the window's been destroyed without being dropped first, we
|
||||||
@@ -502,7 +546,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
// context here instead of when dropping the window.
|
// context here instead of when dropping the window.
|
||||||
wt.ime
|
wt.ime
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.remove_context(window)
|
.remove_context(window as ffi::Window)
|
||||||
.expect("Failed to destroy input context");
|
.expect("Failed to destroy input context");
|
||||||
|
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
@@ -513,7 +557,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
|
|
||||||
ffi::VisibilityNotify => {
|
ffi::VisibilityNotify => {
|
||||||
let xev: &ffi::XVisibilityEvent = xev.as_ref();
|
let xev: &ffi::XVisibilityEvent = xev.as_ref();
|
||||||
let xwindow = xev.window;
|
let xwindow = xev.window as xproto::Window;
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id: mkwid(xwindow),
|
window_id: mkwid(xwindow),
|
||||||
event: WindowEvent::Occluded(xev.state == ffi::VisibilityFullyObscured),
|
event: WindowEvent::Occluded(xev.state == ffi::VisibilityFullyObscured),
|
||||||
@@ -529,7 +573,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
// Multiple Expose events may be received for subareas of a window.
|
// Multiple Expose events may be received for subareas of a window.
|
||||||
// We issue `RedrawRequested` only for the last event of such a series.
|
// We issue `RedrawRequested` only for the last event of such a series.
|
||||||
if xev.count == 0 {
|
if xev.count == 0 {
|
||||||
let window = xev.window;
|
let window = xev.window as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
callback(Event::RedrawRequested(window_id));
|
callback(Event::RedrawRequested(window_id));
|
||||||
@@ -539,22 +583,52 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
// Note that in compose/pre-edit sequences, we'll always receive KeyRelease events
|
// Note that in compose/pre-edit sequences, we'll always receive KeyRelease events
|
||||||
ty @ ffi::KeyPress | ty @ ffi::KeyRelease => {
|
ty @ ffi::KeyPress | ty @ ffi::KeyRelease => {
|
||||||
let xkev: &mut ffi::XKeyEvent = xev.as_mut();
|
let xkev: &mut ffi::XKeyEvent = xev.as_mut();
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xkev.time as xproto::Timestamp);
|
||||||
|
|
||||||
let window = match self.active_window {
|
let window = match self.active_window {
|
||||||
Some(window) => window,
|
Some(window) => window,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD.into());
|
||||||
|
|
||||||
let keycode = xkev.keycode as _;
|
let keycode = xkev.keycode as _;
|
||||||
let repeat = ty == ffi::KeyPress && !self.got_key_release;
|
|
||||||
// Update state after the repeat setting.
|
// Update state to track key repeats and determine whether this key was a repeat.
|
||||||
|
//
|
||||||
|
// Note, when a key is held before focusing on this window the first
|
||||||
|
// (non-synthetic) event will not be flagged as a repeat (also note that the
|
||||||
|
// synthetic press event that is generated before this when the window gains focus
|
||||||
|
// will also not be flagged as a repeat).
|
||||||
|
//
|
||||||
|
// Only keys that can repeat should change the held_key_press state since a
|
||||||
|
// continuously held repeatable key may continue repeating after the press of a
|
||||||
|
// non-repeatable key.
|
||||||
|
let repeat = if self.kb_state.key_repeats(keycode) {
|
||||||
|
let is_latest_held = self.held_key_press == Some(keycode);
|
||||||
|
|
||||||
|
if ty == ffi::KeyPress {
|
||||||
|
self.held_key_press = Some(keycode);
|
||||||
|
is_latest_held
|
||||||
|
} else {
|
||||||
|
// Check that the released key is the latest repeatable key that has been
|
||||||
|
// pressed, since repeats will continue for the latest key press if a
|
||||||
|
// different previously pressed key is released.
|
||||||
|
if is_latest_held {
|
||||||
|
self.held_key_press = None;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
let state = if ty == ffi::KeyPress {
|
let state = if ty == ffi::KeyPress {
|
||||||
self.got_key_release = false;
|
|
||||||
ElementState::Pressed
|
ElementState::Pressed
|
||||||
} else {
|
} else {
|
||||||
self.got_key_release = true;
|
|
||||||
ElementState::Released
|
ElementState::Released
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -568,7 +642,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
is_synthetic: false,
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if let Some(ic) = wt.ime.borrow().get_context(window) {
|
} else if let Some(ic) = wt.ime.borrow().get_context(window as ffi::Window) {
|
||||||
let written = wt.xconn.lookup_utf8(ic, xkev);
|
let written = wt.xconn.lookup_utf8(ic, xkev);
|
||||||
if !written.is_empty() {
|
if !written.is_empty() {
|
||||||
let event = Event::WindowEvent {
|
let event = Event::WindowEvent {
|
||||||
@@ -613,8 +687,12 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
match xev.evtype {
|
match xev.evtype {
|
||||||
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
||||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||||
let window_id = mkwid(xev.event);
|
let window_id = mkwid(xev.event as xproto::Window);
|
||||||
let device_id = mkdid(xev.deviceid);
|
let device_id = mkdid(xev.deviceid);
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
if (xev.flags & ffi::XIPointerEmulated) != 0 {
|
if (xev.flags & ffi::XIPointerEmulated) != 0 {
|
||||||
// Deliver multi-touch events instead of emulated mouse events.
|
// Deliver multi-touch events instead of emulated mouse events.
|
||||||
return;
|
return;
|
||||||
@@ -702,11 +780,16 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
}
|
}
|
||||||
ffi::XI_Motion => {
|
ffi::XI_Motion => {
|
||||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
let device_id = mkdid(xev.deviceid);
|
let device_id = mkdid(xev.deviceid);
|
||||||
let window_id = mkwid(xev.event);
|
let window = xev.event as xproto::Window;
|
||||||
|
let window_id = mkwid(window);
|
||||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||||
|
|
||||||
let cursor_moved = self.with_window(xev.event, |window| {
|
let cursor_moved = self.with_window(window, |window| {
|
||||||
let mut shared_state_lock = window.shared_state_lock();
|
let mut shared_state_lock = window.shared_state_lock();
|
||||||
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
||||||
});
|
});
|
||||||
@@ -788,7 +871,11 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
ffi::XI_Enter => {
|
ffi::XI_Enter => {
|
||||||
let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
|
||||||
let window_id = mkwid(xev.event);
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
|
let window = xev.event as xproto::Window;
|
||||||
|
let window_id = mkwid(window);
|
||||||
let device_id = mkdid(xev.deviceid);
|
let device_id = mkdid(xev.deviceid);
|
||||||
|
|
||||||
if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) {
|
if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) {
|
||||||
@@ -809,7 +896,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.window_exists(xev.event) {
|
if self.window_exists(window) {
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: CursorEntered { device_id },
|
event: CursorEntered { device_id },
|
||||||
@@ -828,13 +915,17 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
}
|
}
|
||||||
ffi::XI_Leave => {
|
ffi::XI_Leave => {
|
||||||
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
let window = xev.event as xproto::Window;
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
// Leave, FocusIn, and FocusOut can be received by a window that's already
|
// Leave, FocusIn, and FocusOut can be received by a window that's already
|
||||||
// been destroyed, which the user presumably doesn't want to deal with.
|
// been destroyed, which the user presumably doesn't want to deal with.
|
||||||
let window_closed = !self.window_exists(xev.event);
|
let window_closed = !self.window_exists(window);
|
||||||
if !window_closed {
|
if !window_closed {
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id: mkwid(xev.event),
|
window_id: mkwid(window),
|
||||||
event: CursorLeft {
|
event: CursorLeft {
|
||||||
device_id: mkdid(xev.deviceid),
|
device_id: mkdid(xev.deviceid),
|
||||||
},
|
},
|
||||||
@@ -843,21 +934,25 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
}
|
}
|
||||||
ffi::XI_FocusIn => {
|
ffi::XI_FocusIn => {
|
||||||
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
let window = xev.event as xproto::Window;
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
wt.ime
|
wt.ime
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.focus(xev.event)
|
.focus(xev.event)
|
||||||
.expect("Failed to focus input context");
|
.expect("Failed to focus input context");
|
||||||
|
|
||||||
if self.active_window != Some(xev.event) {
|
if self.active_window != Some(window) {
|
||||||
self.active_window = Some(xev.event);
|
self.active_window = Some(window);
|
||||||
|
|
||||||
wt.update_listen_device_events(true);
|
wt.update_listen_device_events(true);
|
||||||
|
|
||||||
let window_id = mkwid(xev.event);
|
let window_id = mkwid(window);
|
||||||
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
|
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||||
|
|
||||||
if let Some(window) = self.with_window(xev.event, Arc::clone) {
|
if let Some(window) = self.with_window(window, Arc::clone) {
|
||||||
window.shared_state_lock().has_focus = true;
|
window.shared_state_lock().has_focus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -904,7 +999,12 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
}
|
}
|
||||||
ffi::XI_FocusOut => {
|
ffi::XI_FocusOut => {
|
||||||
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||||
if !self.window_exists(xev.event) {
|
let window = xev.event as xproto::Window;
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
|
if !self.window_exists(window) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -913,8 +1013,8 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
.unfocus(xev.event)
|
.unfocus(xev.event)
|
||||||
.expect("Failed to unfocus input context");
|
.expect("Failed to unfocus input context");
|
||||||
|
|
||||||
if self.active_window.take() == Some(xev.event) {
|
if self.active_window.take() == Some(window) {
|
||||||
let window_id = mkwid(xev.event);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
wt.update_listen_device_events(false);
|
wt.update_listen_device_events(false);
|
||||||
|
|
||||||
@@ -926,6 +1026,9 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
&mut self.kb_state,
|
&mut self.kb_state,
|
||||||
&mut callback,
|
&mut callback,
|
||||||
);
|
);
|
||||||
|
// Clear this so detecting key repeats is consistently handled when the
|
||||||
|
// window regains focus.
|
||||||
|
self.held_key_press = None;
|
||||||
|
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
@@ -934,7 +1037,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(window) = self.with_window(xev.event, Arc::clone) {
|
if let Some(window) = self.with_window(window, Arc::clone) {
|
||||||
window.shared_state_lock().has_focus = false;
|
window.shared_state_lock().has_focus = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -947,14 +1050,19 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
|
|
||||||
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
|
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
|
||||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||||
let window_id = mkwid(xev.event);
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
|
let window = xev.event as xproto::Window;
|
||||||
|
let window_id = mkwid(window);
|
||||||
let phase = match xev.evtype {
|
let phase = match xev.evtype {
|
||||||
ffi::XI_TouchBegin => TouchPhase::Started,
|
ffi::XI_TouchBegin => TouchPhase::Started,
|
||||||
ffi::XI_TouchUpdate => TouchPhase::Moved,
|
ffi::XI_TouchUpdate => TouchPhase::Moved,
|
||||||
ffi::XI_TouchEnd => TouchPhase::Ended,
|
ffi::XI_TouchEnd => TouchPhase::Ended,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
if self.window_exists(xev.event) {
|
if self.window_exists(window) {
|
||||||
let id = xev.detail as u64;
|
let id = xev.detail as u64;
|
||||||
let location = PhysicalPosition::new(xev.event_x, xev.event_y);
|
let location = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||||
|
|
||||||
@@ -965,7 +1073,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::CursorMoved {
|
event: WindowEvent::CursorMoved {
|
||||||
device_id: mkdid(util::VIRTUAL_CORE_POINTER),
|
device_id: mkdid(util::VIRTUAL_CORE_POINTER.into()),
|
||||||
position: location.cast(),
|
position: location.cast(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -986,6 +1094,10 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
|
|
||||||
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
|
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
|
||||||
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
if xev.flags & ffi::XIPointerEmulated == 0 {
|
if xev.flags & ffi::XIPointerEmulated == 0 {
|
||||||
callback(Event::DeviceEvent {
|
callback(Event::DeviceEvent {
|
||||||
device_id: mkdid(xev.deviceid),
|
device_id: mkdid(xev.deviceid),
|
||||||
@@ -1003,6 +1115,10 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
|
|
||||||
ffi::XI_RawMotion => {
|
ffi::XI_RawMotion => {
|
||||||
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
let did = mkdid(xev.deviceid);
|
let did = mkdid(xev.deviceid);
|
||||||
|
|
||||||
let mask = unsafe {
|
let mask = unsafe {
|
||||||
@@ -1054,6 +1170,9 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
|
ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
|
||||||
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
let state = match xev.evtype {
|
let state = match xev.evtype {
|
||||||
ffi::XI_RawKeyPress => Pressed,
|
ffi::XI_RawKeyPress => Pressed,
|
||||||
ffi::XI_RawKeyRelease => Released,
|
ffi::XI_RawKeyRelease => Released,
|
||||||
@@ -1078,6 +1197,10 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
|
|
||||||
ffi::XI_HierarchyChanged => {
|
ffi::XI_HierarchyChanged => {
|
||||||
let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
for info in
|
for info in
|
||||||
unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) }
|
unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) }
|
||||||
{
|
{
|
||||||
@@ -1110,6 +1233,10 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
let xev = unsafe {
|
let xev = unsafe {
|
||||||
&*(xev as *const _ as *const ffi::XkbNewKeyboardNotifyEvent)
|
&*(xev as *const _ as *const ffi::XkbNewKeyboardNotifyEvent)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
let keycodes_changed_flag = 0x1;
|
let keycodes_changed_flag = 0x1;
|
||||||
let geometry_changed_flag = 0x1 << 1;
|
let geometry_changed_flag = 0x1 << 1;
|
||||||
|
|
||||||
@@ -1128,6 +1255,9 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
let xev =
|
let xev =
|
||||||
unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) };
|
unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) };
|
||||||
|
|
||||||
|
// Set the timestamp.
|
||||||
|
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||||
|
|
||||||
let prev_mods = self.kb_state.mods_state();
|
let prev_mods = self.kb_state.mods_state();
|
||||||
self.kb_state.update_modifiers(
|
self.kb_state.update_modifiers(
|
||||||
xev.base_mods,
|
xev.base_mods,
|
||||||
@@ -1187,21 +1317,27 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
|
|
||||||
let window_id = crate::window::WindowId(*window_id);
|
let window_id = crate::window::WindowId(*window_id);
|
||||||
let old_inner_size = PhysicalSize::new(width, height);
|
let old_inner_size = PhysicalSize::new(width, height);
|
||||||
let mut new_inner_size =
|
let inner_size = Arc::new(Mutex::new(
|
||||||
PhysicalSize::new(new_width, new_height);
|
PhysicalSize::new(new_width, new_height),
|
||||||
|
));
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::ScaleFactorChanged {
|
event: WindowEvent::ScaleFactorChanged {
|
||||||
scale_factor: new_monitor.scale_factor,
|
scale_factor: new_monitor.scale_factor,
|
||||||
new_inner_size: &mut new_inner_size,
|
inner_size_writer: InnerSizeWriter::new(
|
||||||
|
Arc::downgrade(&inner_size),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let new_inner_size = *inner_size.lock().unwrap();
|
||||||
|
drop(inner_size);
|
||||||
|
|
||||||
if new_inner_size != old_inner_size {
|
if new_inner_size != old_inner_size {
|
||||||
let (new_width, new_height) = new_inner_size.into();
|
let (new_width, new_height) = new_inner_size.into();
|
||||||
window
|
window.request_inner_size_physical(
|
||||||
.set_inner_size_physical(new_width, new_height);
|
new_width, new_height,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1227,7 +1363,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (window, event) = match self.ime_event_receiver.try_recv() {
|
let (window, event) = match self.ime_event_receiver.try_recv() {
|
||||||
Ok((window, event)) => (window, event),
|
Ok((window, event)) => (window as xproto::Window, event),
|
||||||
Err(_) => return,
|
Err(_) => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1278,9 +1414,9 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
kb_state: &mut KbdState,
|
kb_state: &mut KbdState,
|
||||||
callback: &mut F,
|
callback: &mut F,
|
||||||
) where
|
) where
|
||||||
F: FnMut(Event<'_, T>),
|
F: FnMut(Event<T>),
|
||||||
{
|
{
|
||||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD.into());
|
||||||
|
|
||||||
// Update modifiers state and emit key events based on which keys are currently pressed.
|
// Update modifiers state and emit key events based on which keys are currently pressed.
|
||||||
for keycode in wt
|
for keycode in wt
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user