mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
148 Commits
v0.29.0-be
...
notgull/ia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3657449ab | ||
|
|
9382b7238c | ||
|
|
73348f49d7 | ||
|
|
49a315870e | ||
|
|
999a1f8733 | ||
|
|
023ab591b5 | ||
|
|
af32c5c298 | ||
|
|
0c4e725692 | ||
|
|
8671481db4 | ||
|
|
fe893e6690 | ||
|
|
5cfdb3fc42 | ||
|
|
e30f112516 | ||
|
|
f9a975e6e5 | ||
|
|
b938fe9df5 | ||
|
|
b7e3649e8b | ||
|
|
844269d017 | ||
|
|
e41fac825c | ||
|
|
bbeacc46d5 | ||
|
|
61581ebb4f | ||
|
|
93f1000a05 | ||
|
|
1ea41a2ee2 | ||
|
|
42c9b7e40e | ||
|
|
0960635895 | ||
|
|
789a497980 | ||
|
|
fac6110cb6 | ||
|
|
0363be4776 | ||
|
|
f5dd1c008c | ||
|
|
48a1e84906 | ||
|
|
ee0db52ac4 | ||
|
|
c7cf0cfd83 | ||
|
|
8393d98940 | ||
|
|
af247eac0f | ||
|
|
b2b4564a5f | ||
|
|
cb58c49a90 | ||
|
|
ffb46dd61f | ||
|
|
8c8fb39fcd | ||
|
|
2422ea39d0 | ||
|
|
878d832d24 | ||
|
|
e2e01e1fc6 | ||
|
|
c8b685ddbc | ||
|
|
f10ae52385 | ||
|
|
e731041c15 | ||
|
|
9df7fc47a1 | ||
|
|
992aeb0ca0 | ||
|
|
0caba93b51 | ||
|
|
c00c1e9eb7 | ||
|
|
83950acd5a | ||
|
|
b99403b1b9 | ||
|
|
4f0ce7201d | ||
|
|
e648169861 | ||
|
|
8fdd81ecef | ||
|
|
7a2a2341c2 | ||
|
|
d68d9eab38 | ||
|
|
a06ea45c0f | ||
|
|
67b041e231 | ||
|
|
6dfc78fb50 | ||
|
|
477619c0a7 | ||
|
|
5f1a4b65ad | ||
|
|
bb9b629bc3 | ||
|
|
0c8cf94a70 | ||
|
|
1dfca5a395 | ||
|
|
7541220a41 | ||
|
|
7e11912d22 | ||
|
|
86baa1c99a | ||
|
|
d9f04780cc | ||
|
|
67d3fd28f7 | ||
|
|
a3cba838ea | ||
|
|
48abf52aac | ||
|
|
68ef9f707e | ||
|
|
9979441c82 | ||
|
|
309e6aa85a | ||
|
|
8b8556798e | ||
|
|
2d96480a89 | ||
|
|
6caff77abb | ||
|
|
af6c343d0e | ||
|
|
119462795a | ||
|
|
f801c4a00b | ||
|
|
f9758528f6 | ||
|
|
778d70c001 | ||
|
|
dc973883c9 | ||
|
|
2233edb9a0 | ||
|
|
bd2f1e8312 | ||
|
|
e9ebf1e5f4 | ||
|
|
5f7955cb2b | ||
|
|
793c535b01 | ||
|
|
ed26dd58fd | ||
|
|
584aab4cd0 | ||
|
|
8100a6a584 | ||
|
|
cad3277550 | ||
|
|
3c3a863cc9 | ||
|
|
8a7e18aaf0 | ||
|
|
57fad2ce15 | ||
|
|
7a58fe58ce | ||
|
|
38f28d5836 | ||
|
|
189a0080a6 | ||
|
|
b5aa96bea4 | ||
|
|
19e3906369 | ||
|
|
9ac3259a79 | ||
|
|
2b2dd6b65d | ||
|
|
75173118b0 | ||
|
|
e33d2bee6c | ||
|
|
ae7497e18f | ||
|
|
935146d299 | ||
|
|
755c533b08 | ||
|
|
ae9b02e097 | ||
|
|
e6c7cc297d | ||
|
|
b74cee8df1 | ||
|
|
e5eb253698 | ||
|
|
ec11b4877f | ||
|
|
9e46dffcc5 | ||
|
|
7501039d57 | ||
|
|
289ce32d77 | ||
|
|
0d366ffbda | ||
|
|
a6f414d732 | ||
|
|
c47d0846fa | ||
|
|
461efaf99f | ||
|
|
420840278b | ||
|
|
f5e73b0af4 | ||
|
|
f40b5f0dad | ||
|
|
c91402efb9 | ||
|
|
43acf7f42f | ||
|
|
c62e64060b | ||
|
|
f7a84a5b50 | ||
|
|
89aa7cc06e | ||
|
|
b166e1ff13 | ||
|
|
97434d8d80 | ||
|
|
06fb089633 | ||
|
|
4d6dbea74c | ||
|
|
c5941d105f | ||
|
|
b63164645b | ||
|
|
5b5ebc25d8 | ||
|
|
d7ec899d69 | ||
|
|
5379d60e4d | ||
|
|
44e2f95331 | ||
|
|
3b2d1a7643 | ||
|
|
50b17a3907 | ||
|
|
af26f01b95 | ||
|
|
c4d70d75c1 | ||
|
|
db8de03142 | ||
|
|
ff0ce9d065 | ||
|
|
42e492cde8 | ||
|
|
5e0e1e96bc | ||
|
|
bd890e69aa | ||
|
|
bca57ed0b4 | ||
|
|
4652d48105 | ||
|
|
96c0b267e2 | ||
|
|
81fd39485f | ||
|
|
6178acede8 |
284
.github/workflows/ci.yml
vendored
284
.github/workflows/ci.yml
vendored
@@ -6,146 +6,184 @@ on:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
Check_Formatting:
|
||||
fmt:
|
||||
name: Check formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
rust-version: stable
|
||||
components: rustfmt
|
||||
- name: Check Formatting
|
||||
run: cargo +stable fmt --all -- --check
|
||||
|
||||
run: cargo fmt -- --check
|
||||
|
||||
tests:
|
||||
name: Test ${{ matrix.toolchain }} ${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain: [stable, nightly, '1.65.0']
|
||||
platform:
|
||||
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
|
||||
- { name: 'Windows 64bit MSVC', target: x86_64-pc-windows-msvc, os: windows-latest, }
|
||||
- { name: 'Windows 32bit MSVC', target: i686-pc-windows-msvc, os: windows-latest, }
|
||||
- { name: 'Windows 64bit GNU', target: x86_64-pc-windows-gnu, os: windows-latest, host: -x86_64-pc-windows-gnu }
|
||||
- { name: 'Windows 32bit GNU', target: i686-pc-windows-gnu, os: windows-latest, host: -i686-pc-windows-gnu }
|
||||
- { name: 'Linux 32bit', target: i686-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { name: 'Linux 64bit', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
|
||||
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
|
||||
- { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, }
|
||||
- { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, }
|
||||
- { name: 'iOS Aarch64', target: aarch64-apple-ios, os: macos-latest, }
|
||||
- { name: 'web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
|
||||
include:
|
||||
# Android is tested on stable-3
|
||||
- toolchain: '1.69.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity' }
|
||||
- toolchain: 'stable'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity' }
|
||||
- toolchain: 'nightly'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity' }
|
||||
|
||||
|
||||
env:
|
||||
# Set more verbose terminal output
|
||||
CARGO_TERM_VERBOSE: true
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
# Faster compilation and error on warnings
|
||||
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings'
|
||||
RUSTDOCFLAGS: '--deny=warnings'
|
||||
|
||||
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Restore cache of cargo folder
|
||||
# We use `restore` and later `save`, so that we can create the key after
|
||||
# the cache has been downloaded.
|
||||
#
|
||||
# This could be avoided if we added Cargo.lock to the repository.
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
# https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-never-intended-to-be-found
|
||||
restore-keys: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}
|
||||
|
||||
- name: Generate lockfile
|
||||
# Also updates the crates.io index
|
||||
run: cargo generate-lockfile
|
||||
|
||||
- name: Install GCC Multilib
|
||||
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
||||
run: sudo apt-get update && sudo apt-get install gcc-multilib
|
||||
|
||||
- name: Install xbuild
|
||||
uses: taiki-e/install-action@v2
|
||||
if: contains(matrix.platform.target, 'android') || contains(matrix.platform.target, 'ios')
|
||||
with:
|
||||
tool: xbuild@0.2.0
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
|
||||
targets: ${{ matrix.platform.target }}
|
||||
components: clippy
|
||||
|
||||
- name: Install LLVM tools for Android
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
run: sudo apt-get update && sudo apt-get install lld llvm
|
||||
|
||||
- name: Check documentation
|
||||
run: cargo doc --no-deps $OPTIONS --document-private-items
|
||||
|
||||
- name: Build crate
|
||||
run: cargo build $OPTIONS
|
||||
|
||||
- name: Build and package crate for Android
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
run: x build -p android-xbuild-target --platform android --arch arm64
|
||||
|
||||
- name: Build and package crate for iOS
|
||||
if: contains(matrix.platform.target, 'ios')
|
||||
run: x build -p ios-xbuild-target --platform ios --arch arm64
|
||||
|
||||
- name: Build tests
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo test --no-run $OPTIONS
|
||||
|
||||
- name: Run tests
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo test $OPTIONS
|
||||
|
||||
- name: Lint with clippy
|
||||
if: (matrix.toolchain == 'stable') && !contains(matrix.platform.options, '--no-default-features')
|
||||
run: cargo clippy --all-targets $OPTIONS -- -Dwarnings
|
||||
|
||||
- name: Build tests with serde enabled
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo test --no-run $OPTIONS --features serde
|
||||
|
||||
- name: Run tests with serde enabled
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo test $OPTIONS --features serde
|
||||
|
||||
# See restore step above
|
||||
- name: Save cache of cargo folder
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
cargo-deny:
|
||||
name: cargo-deny
|
||||
name: Run cargo-deny on ${{ matrix.platform.name }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# TODO: remove this matrix when https://github.com/EmbarkStudios/cargo-deny/issues/324 is resolved
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- aarch64-apple-ios
|
||||
- aarch64-linux-android
|
||||
- i686-pc-windows-gnu
|
||||
- i686-pc-windows-msvc
|
||||
- i686-unknown-linux-gnu
|
||||
- wasm32-unknown-unknown
|
||||
- x86_64-apple-darwin
|
||||
- x86_64-apple-ios
|
||||
- x86_64-pc-windows-gnu
|
||||
- x86_64-pc-windows-msvc
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-redox
|
||||
- { name: 'Android', target: aarch64-linux-android }
|
||||
- { name: 'iOS', target: aarch64-apple-ios }
|
||||
- { name: 'Linux', target: x86_64-unknown-linux-gnu }
|
||||
- { name: 'macOS', target: x86_64-apple-darwin }
|
||||
- { name: 'Redox OS', target: x86_64-unknown-redox }
|
||||
- { name: 'web', target: wasm32-unknown-unknown }
|
||||
- { name: 'Windows', target: x86_64-pc-windows-gnu }
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
command: check
|
||||
log-level: error
|
||||
arguments: --all-features --target ${{ matrix.platform }}
|
||||
|
||||
tests:
|
||||
name: Tests
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust_version: ['1.65.0', stable, nightly]
|
||||
platform:
|
||||
# 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: i686-pc-windows-msvc, os: windows-latest, }
|
||||
- { target: x86_64-pc-windows-gnu, os: windows-latest, host: -x86_64-pc-windows-gnu }
|
||||
- { target: i686-pc-windows-gnu, os: windows-latest, host: -i686-pc-windows-gnu }
|
||||
- { target: i686-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: x11 }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "wayland,wayland-dlopen" }
|
||||
- { target: aarch64-linux-android, os: ubuntu-latest, options: -p winit, cmd: 'apk --', features: "android-native-activity" }
|
||||
- { target: x86_64-unknown-redox, os: ubuntu-latest, }
|
||||
- { target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { target: x86_64-apple-ios, os: macos-latest, }
|
||||
- { target: aarch64-apple-ios, os: macos-latest, }
|
||||
# We're using Windows rather than Ubuntu to run the wasm tests because caching cargo-web
|
||||
# doesn't currently work on Linux.
|
||||
- { target: wasm32-unknown-unknown, os: windows-latest, }
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
RUSTFLAGS: "-C debuginfo=0 --deny warnings"
|
||||
OPTIONS: ${{ matrix.platform.options }}
|
||||
FEATURES: ${{ format(',{0}', matrix.platform.features ) }}
|
||||
CMD: ${{ matrix.platform.cmd }}
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
# Used to cache cargo-web
|
||||
- name: Cache cargo folder
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ matrix.platform.target }}-cargo-${{ matrix.rust_version }}
|
||||
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
with:
|
||||
rust-version: ${{ matrix.rust_version }}${{ matrix.platform.host }}
|
||||
targets: ${{ matrix.platform.target }}
|
||||
components: clippy
|
||||
|
||||
- name: Install GCC Multilib
|
||||
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
||||
run: sudo apt-get update && sudo apt-get install gcc-multilib
|
||||
- name: Install cargo-apk
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
run: cargo install cargo-apk
|
||||
|
||||
- name: Check documentation
|
||||
shell: bash
|
||||
run: cargo doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES --document-private-items
|
||||
|
||||
- name: Build crate
|
||||
shell: bash
|
||||
run: cargo $CMD build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
|
||||
- name: Build tests
|
||||
shell: bash
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.rust_version != '1.65.0'
|
||||
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.rust_version != '1.65.0'
|
||||
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
|
||||
- name: Lint with clippy
|
||||
shell: bash
|
||||
if: (matrix.rust_version == 'stable') && !contains(matrix.platform.options, '--no-default-features')
|
||||
run: cargo clippy --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings
|
||||
|
||||
- name: Build tests with serde enabled
|
||||
shell: bash
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.rust_version != '1.65.0'
|
||||
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
||||
- name: Run tests with serde enabled
|
||||
shell: bash
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.rust_version != '1.65.0'
|
||||
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
||||
arguments: --all-features --target ${{ matrix.platform.target }}
|
||||
|
||||
34
CHANGELOG.md
34
CHANGELOG.md
@@ -8,7 +8,34 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||
|
||||
# Unreleased
|
||||
|
||||
# 0.29.0-beta.1
|
||||
- Renamed `EventLoopExtRunOnDemand` / `run_ondemand` to `EventLoopExtRunOnDemand` / `run_on_demand`.
|
||||
- Make iOS `MonitorHandle` and `VideoMode` usable from other threads.
|
||||
- On Web, `ControlFlow::WaitUntil` now uses the Prioritized Task Scheduling API. `setTimeout()`, with a trick to circumvent throttling to 4ms, is used as a fallback.
|
||||
- On Web, never return a `MonitorHandle`.
|
||||
- **Breaking:** Move `Event::RedrawRequested` to `WindowEvent::RedrawRequested`.
|
||||
- On macOS, fix crash in `window.set_minimized(false)`.
|
||||
- On Web, enable event propagation and let `DeviceEvent`s appear after `WindowEvent`s.
|
||||
- On Web, take all transient activations on the canvas and window into account to queue a fullscreen request.
|
||||
- On Web, remove any fullscreen requests from the queue when an external fullscreen activation was detected.
|
||||
- On Wayland, fix `TouchPhase::Canceled` being sent for moved events.
|
||||
- Mark `startup_notify` unsafe functions as safe.
|
||||
- Fix a bug where Wayland would be chosen on Linux even if the user specified `with_x11`. (#3058)
|
||||
- **Breaking:** Moved `ControlFlow` to `EventLoopWindowTarget::set_control_flow()` and `EventLoopWindowTarget::control_flow()`.
|
||||
- **Breaking:** Moved `ControlFlow::Exit` to `EventLoopWindowTarget::exit()` and `EventLoopWindowTarget::exiting()` and removed `ControlFlow::ExitWithCode(_)` entirely.
|
||||
- On Web, add `EventLoopWindowTargetExtWebSys` and `PollStrategy`, which allows to set different strategies for `ControlFlow::Poll`. By default the Prioritized Task Scheduling API is used, but an option to use `Window.requestIdleCallback` is available as well. Both use `setTimeout()`, with a trick to circumvent throttling to 4ms, as a fallback.
|
||||
- Implement `PartialOrd` and `Ord` for `MouseButton`.
|
||||
- On X11, fix event loop not waking up on `ControlFlow::Poll` and `ControlFlow::WaitUntil`.
|
||||
- **Breaking:** Change default `ControlFlow` from `Poll` to `Wait`.
|
||||
- **Breaking:** remove `DeviceEvent::Text`.
|
||||
- On Android, fix `DeviceId` to contain device id's.
|
||||
- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now.
|
||||
- On Web, fix `ControlFlow::WaitUntil` to never wake up **before** the given time.
|
||||
- Add `Window::show_window_menu` to request a titlebar/system menu; implemented on Wayland/Windows for now.
|
||||
- On iOS, send events `WindowEvent::Occluded(false)`, `WindowEvent::Occluded(true)` when application enters/leaves foreground.
|
||||
- **Breaking** add `Event::MemoryWarning`; implemented on iOS/Android.
|
||||
- On Wayland, support `Occluded` event with xdg-shell v6
|
||||
|
||||
# 0.29.1-beta
|
||||
|
||||
- **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`.
|
||||
@@ -70,6 +97,7 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||
- **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.
|
||||
- On Android, add force data to touch events.
|
||||
|
||||
# 0.29.0-beta.0
|
||||
|
||||
@@ -154,6 +182,10 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||
- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events
|
||||
on Wayland, X11, Windows, macOS and Web.
|
||||
|
||||
# 0.28.7
|
||||
|
||||
- Fix window size sometimes being invalid when resizing on macOS 14 Sonoma.
|
||||
|
||||
# 0.28.6
|
||||
|
||||
- On macOS, fixed memory leak when getting monitor handle.
|
||||
|
||||
45
Cargo.toml
45
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.29.0-beta.1"
|
||||
version = "0.29.1-beta"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2021"
|
||||
@@ -13,7 +13,7 @@ categories = ["gui"]
|
||||
rust-version = "1.65.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["serde"]
|
||||
features = ["rwh_04", "rwh_05", "rwh_06", "serde"]
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
# These are all tested in CI
|
||||
targets = [
|
||||
@@ -35,9 +35,9 @@ targets = [
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||
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", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"]
|
||||
wayland-dlopen = ["wayland-backend/dlopen"]
|
||||
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
|
||||
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
|
||||
@@ -55,7 +55,9 @@ cursor-icon = "1.0.0"
|
||||
log = "0.4"
|
||||
mint = { version = "0.5.6", optional = true }
|
||||
once_cell = "1.12"
|
||||
raw_window_handle = { package = "raw-window-handle", version = "0.5", features = ["std"] }
|
||||
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
|
||||
rwh_05 = { package = "raw-window-handle", version = "0.5", features = ["std"], optional = true }
|
||||
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
|
||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||
smol_str = "0.2.0"
|
||||
|
||||
@@ -68,16 +70,16 @@ softbuffer = "0.3.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
# Coordinate the next winit release android-activity 0.5 release
|
||||
android-activity = "0.5.0-beta.1"
|
||||
ndk = "0.8.0-beta.0"
|
||||
ndk-sys = "0.5.0-beta.0"
|
||||
android-activity = "=0.5.0-beta.1"
|
||||
ndk = "=0.8.0-beta.0"
|
||||
ndk-sys = "=0.5.0-beta.0"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = "0.4.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-graphics = "0.22.3"
|
||||
core-graphics = "0.23.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.icrate]
|
||||
version = "0.0.4"
|
||||
@@ -140,21 +142,22 @@ features = [
|
||||
]
|
||||
|
||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
|
||||
ahash = { version = "0.8.3", features = ["no-rng"], optional = true }
|
||||
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
||||
calloop = "0.12.3"
|
||||
libc = "0.2.64"
|
||||
memmap2 = { version = "0.9.0", optional = true }
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
fnv = { version = "1.0.3", optional = true }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.17.0", default-features = false, features = ["calloop"], optional = true }
|
||||
sctk-adwaita = { version = "0.6.0", default_features = false, optional = true }
|
||||
wayland-client = { version = "0.30.0", 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 }
|
||||
calloop = "0.10.5"
|
||||
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
|
||||
sctk-adwaita = { version = "0.7.0", default_features = false, optional = true }
|
||||
wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
|
||||
wayland-client = { version = "0.31.1", optional = true }
|
||||
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
|
||||
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], 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 }
|
||||
x11rb = { version = "0.12.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
|
||||
xkbcommon-dl = "0.4.0"
|
||||
memmap2 = { version = "0.5.0", optional = true }
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
orbclient = { version = "0.3.42", default-features = false }
|
||||
@@ -164,6 +167,8 @@ redox_syscall = "0.3"
|
||||
package = "web-sys"
|
||||
version = "0.3.64"
|
||||
features = [
|
||||
'AbortController',
|
||||
'AbortSignal',
|
||||
'console',
|
||||
'CssStyleDeclaration',
|
||||
'Document',
|
||||
@@ -179,6 +184,8 @@ features = [
|
||||
'IntersectionObserverEntry',
|
||||
'KeyboardEvent',
|
||||
'MediaQueryList',
|
||||
'MessageChannel',
|
||||
'MessagePort',
|
||||
'Node',
|
||||
'PageTransitionEvent',
|
||||
'PointerEvent',
|
||||
@@ -205,5 +212,7 @@ web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"xbuild/android-target",
|
||||
"xbuild/ios-target",
|
||||
"run-wasm",
|
||||
]
|
||||
|
||||
@@ -182,6 +182,7 @@ Legend:
|
||||
|Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
||||
|Window resize increments |❌ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|
||||
|Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ |
|
||||
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
@@ -205,7 +206,7 @@ Legend:
|
||||
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |❌ |
|
||||
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|
||||
|Cursor hittest |✔️ |✔️ |❌ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Cursor hittest |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A** |
|
||||
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |
|
||||
|Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |**N/A** |
|
||||
|
||||
49
README.md
49
README.md
@@ -6,14 +6,14 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.29.0-beta.1"
|
||||
winit = "0.29.1-beta"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
For features _within_ the scope of winit, see [FEATURES.md](FEATURES.md).
|
||||
|
||||
For features _outside_ the scope of winit, see [Missing features provided by other crates](https://github.com/rust-windowing/winit/wiki/Missing-features-provided-by-other-crates) in the wiki.
|
||||
For features _outside_ the scope of winit, see [Are we GUI Yet?](https://areweguiyet.com/) and [Are we game yet?](https://arewegameyet.rs/), depending on what kind of project you're looking to do.
|
||||
|
||||
## Contact Us
|
||||
|
||||
@@ -35,22 +35,21 @@ another library.
|
||||
```rust
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let event_loop = EventLoop::new();
|
||||
event_loop.set_control_flow(ControlFlow::Wait);
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
@@ -67,6 +66,35 @@ Winit provides the following features, which can be enabled in your `Cargo.toml`
|
||||
* `wayland` (enabled by default): On Unix platform, compiles with the Wayland backend
|
||||
* `mint`: Enables mint (math interoperability standard types) conversions.
|
||||
|
||||
## MSRV Policy
|
||||
|
||||
The Minimum Supported Rust Version (MSRV) of this crate is **1.65**. Changes to
|
||||
the MSRV will be accompanied by a minor version bump.
|
||||
|
||||
As a **tentative** policy, the upper bound of the MSRV is given by the following
|
||||
formula:
|
||||
|
||||
```
|
||||
min(sid, stable - 3)
|
||||
```
|
||||
|
||||
Where `sid` is the current version of `rustc` provided by [Debian Sid], and
|
||||
`stable` is the latest stable version of Rust. This bound may be broken in the
|
||||
event of a major ecosystem shift or a security vulnerability.
|
||||
|
||||
[Debian Sid]: https://packages.debian.org/sid/rustc
|
||||
|
||||
The exception to this is for the Android platform, where a higher Rust version
|
||||
must be used for certain Android features. In this case, the MSRV will be
|
||||
capped at the latest stable version of Rust minus three. This inconsistency is
|
||||
not reflected in Cargo metadata, as it is not powerful enough to expose this
|
||||
restriction.
|
||||
|
||||
All crates in the [`rust-windowing`] organizations have the
|
||||
same MSRV policy.
|
||||
|
||||
[`rust-windowing`]: https://github.com/rust-windowing
|
||||
|
||||
### Platform-specific usage
|
||||
|
||||
#### Wayland
|
||||
@@ -95,7 +123,7 @@ book].
|
||||
|
||||
#### Android
|
||||
|
||||
The Android backend builds on (and exposes types from) the [`ndk`](https://docs.rs/ndk/0.7.0/ndk/) crate.
|
||||
The Android backend builds on (and exposes types from) the [`ndk`](https://docs.rs/ndk/latest/ndk/) crate.
|
||||
|
||||
Native Android applications need some form of "glue" crate that is responsible
|
||||
for defining the main entry point for your Rust application as well as tracking
|
||||
@@ -114,6 +142,7 @@ clash.
|
||||
|
||||
| winit | ndk-glue |
|
||||
| :---: | :--------------------------: |
|
||||
| 0.29.1-beta | `android-activity = "0.5.0-beta.1"` |
|
||||
| 0.28 | `android-activity = "0.4"` |
|
||||
| 0.27 | `ndk-glue = "0.7"` |
|
||||
| 0.26 | `ndk-glue = "0.5"` |
|
||||
@@ -150,7 +179,7 @@ class. Your application _must_ specify the base class it needs via a feature fla
|
||||
|
||||
For example, add this to Cargo.toml:
|
||||
```toml
|
||||
winit = { version = "0.28", features = [ "android-native-activity" ] }
|
||||
winit = { version = "0.29.1-beta", features = [ "android-native-activity" ] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_logger = "0.11.0"
|
||||
@@ -181,7 +210,7 @@ For more details, refer to these `android-activity` [example applications](https
|
||||
|
||||
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk` then the minimal changes would be:
|
||||
1. Remove `ndk-glue` from your `Cargo.toml`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.28", features = [ "android-native-activity" ] }`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.1-beta", features = [ "android-native-activity" ] }`
|
||||
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
|
||||
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
|
||||
|
||||
|
||||
@@ -9,4 +9,5 @@ disallowed-methods = [
|
||||
{ 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" },
|
||||
{ path = "icrate::AppKit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
|
||||
]
|
||||
|
||||
20
deny.toml
20
deny.toml
@@ -31,16 +31,16 @@ multiple-versions = "deny"
|
||||
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
|
||||
deny = []
|
||||
skip = [
|
||||
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
|
||||
{ name = "nix" }, # differing version - as of 2023-03-02 whis can be solved with `cargo update && cargo update -p calloop --precise 0.10.2`
|
||||
{ name = "memoffset"}, # due to different nix versions.
|
||||
{ name = "memmap2" }, # sctk uses a different version until the next update
|
||||
{ name = "libloading" }, # x11rb uses a different version until the next update
|
||||
{ name = "syn" }, # https://github.com/rust-mobile/ndk/issues/392 and https://github.com/rustwasm/wasm-bindgen/issues/3390
|
||||
{ name = "num_enum"}, # See above ^, waiting for release
|
||||
{ name = "num_enum_derive"},# See above ^, waiting for release
|
||||
{ name = "miniz_oxide"}, # https://github.com/rust-lang/flate2-rs/issues/340
|
||||
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
|
||||
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this
|
||||
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
|
||||
{ name = "libloading" }, # x11rb uses a different version until the next update
|
||||
{ name = "syn" }, # https://github.com/rust-mobile/ndk/issues/392
|
||||
{ name = "num_enum"}, # See above ^, waiting for release
|
||||
{ name = "num_enum_derive"}, # See above ^, waiting for release
|
||||
{ name = "miniz_oxide"}, # https://github.com/rust-lang/flate2-rs/issues/340
|
||||
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
|
||||
{ name = "foreign-types-shared" }, # https://github.com/servo/core-foundation-rs/issues/634
|
||||
{ name = "foreign-types" }, # See above ^, waiting for release
|
||||
]
|
||||
skip-tree = []
|
||||
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
#[cfg(any(x11_platform, macos_platform, windows_platform))]
|
||||
#[cfg(all(
|
||||
feature = "rwh_06",
|
||||
any(x11_platform, macos_platform, windows_platform)
|
||||
))]
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[cfg(any(x11_platform, macos_platform, windows_platform))]
|
||||
#[cfg(all(
|
||||
feature = "rwh_06",
|
||||
any(x11_platform, macos_platform, windows_platform)
|
||||
))]
|
||||
#[allow(deprecated)]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use winit::{
|
||||
dpi::{LogicalPosition, LogicalSize, Position},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||
window::raw_window_handle::HasRawWindowHandle,
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
raw_window_handle::HasRawWindowHandle,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
@@ -19,7 +26,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event_loop: &EventLoopWindowTarget<()>,
|
||||
windows: &mut HashMap<WindowId, Window>,
|
||||
) {
|
||||
let parent = parent.raw_window_handle();
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let mut builder = WindowBuilder::new()
|
||||
.with_title("child window")
|
||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
@@ -46,14 +53,12 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
println!("parent window: {parent_window:?})");
|
||||
|
||||
event_loop.run(move |event: Event<()>, event_loop, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
event_loop.run(move |event: Event<()>, elwt| {
|
||||
if let Event::WindowEvent { event, window_id } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
windows.clear();
|
||||
*control_flow = ControlFlow::Exit;
|
||||
elwt.exit();
|
||||
}
|
||||
WindowEvent::CursorEntered { device_id: _ } => {
|
||||
// On x11, println when the cursor entered in a window even if the child window is created
|
||||
@@ -70,19 +75,23 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
},
|
||||
..
|
||||
} => {
|
||||
spawn_child_window(&parent_window, event_loop, &mut windows);
|
||||
spawn_child_window(&parent_window, elwt, &mut windows);
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if let Event::RedrawRequested(wid) = event {
|
||||
if let Some(window) = windows.get(&wid) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))]
|
||||
#[cfg(not(all(
|
||||
feature = "rwh_06",
|
||||
any(x11_platform, macos_platform, windows_platform)
|
||||
)))]
|
||||
fn main() {
|
||||
panic!("This example is supported only on x11, macOS, and Windows.");
|
||||
panic!("This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature enabled.");
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use web_time as time;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
keyboard::Key,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
@@ -47,7 +47,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
let mut wait_cancelled = false;
|
||||
let mut close_requested = false;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
event_loop.run(move |event, elwt| {
|
||||
use winit::event::StartCause;
|
||||
println!("{event:?}");
|
||||
match event {
|
||||
@@ -93,6 +93,9 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
@@ -101,25 +104,24 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
|
||||
match mode {
|
||||
Mode::Wait => control_flow.set_wait(),
|
||||
Mode::Wait => elwt.set_control_flow(ControlFlow::Wait),
|
||||
Mode::WaitUntil => {
|
||||
if !wait_cancelled {
|
||||
control_flow.set_wait_until(time::Instant::now() + WAIT_TIME);
|
||||
elwt.set_control_flow(ControlFlow::WaitUntil(
|
||||
time::Instant::now() + WAIT_TIME,
|
||||
));
|
||||
}
|
||||
}
|
||||
Mode::Poll => {
|
||||
thread::sleep(POLL_SLEEP_TIME);
|
||||
control_flow.set_poll();
|
||||
elwt.set_control_flow(ControlFlow::Poll);
|
||||
}
|
||||
};
|
||||
|
||||
if close_requested {
|
||||
control_flow.set_exit();
|
||||
elwt.exit();
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(_window_id) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -19,40 +19,33 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let mut cursor_idx = 0;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
|
||||
window.set_cursor_icon(CURSORS[cursor_idx]);
|
||||
if cursor_idx < CURSORS.len() - 1 {
|
||||
cursor_idx += 1;
|
||||
} else {
|
||||
cursor_idx = 0;
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
|
||||
window.set_cursor_icon(CURSORS[cursor_idx]);
|
||||
if cursor_idx < CURSORS.len() - 1 {
|
||||
cursor_idx += 1;
|
||||
} else {
|
||||
cursor_idx = 0;
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
elwt.exit();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
control_flow.set_exit();
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,56 +22,52 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let mut modifiers = ModifiersState::default();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
let result = match key {
|
||||
Key::Escape => {
|
||||
control_flow.set_exit();
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
let result = match key {
|
||||
Key::Escape => {
|
||||
elwt.exit();
|
||||
Ok(())
|
||||
}
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
"g" => window.set_cursor_grab(CursorGrabMode::Confined),
|
||||
"l" => window.set_cursor_grab(CursorGrabMode::Locked),
|
||||
"a" => window.set_cursor_grab(CursorGrabMode::None),
|
||||
"h" => {
|
||||
window.set_cursor_visible(modifiers.shift_key());
|
||||
Ok(())
|
||||
}
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
"g" => window.set_cursor_grab(CursorGrabMode::Confined),
|
||||
"l" => window.set_cursor_grab(CursorGrabMode::Locked),
|
||||
"a" => window.set_cursor_grab(CursorGrabMode::None),
|
||||
"h" => {
|
||||
window.set_cursor_visible(modifiers.shift_key());
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
},
|
||||
_ => Ok(()),
|
||||
};
|
||||
},
|
||||
_ => Ok(()),
|
||||
};
|
||||
|
||||
if let Err(err) = result {
|
||||
println!("error: {err}");
|
||||
}
|
||||
if let Err(err) = result {
|
||||
println!("error: {err}");
|
||||
}
|
||||
WindowEvent::ModifiersChanged(new) => modifiers = new.state(),
|
||||
_ => (),
|
||||
},
|
||||
Event::DeviceEvent { event, .. } => match event {
|
||||
DeviceEvent::MouseMotion { delta } => println!("mouse moved: {delta:?}"),
|
||||
DeviceEvent::Button { button, state } => match state {
|
||||
ElementState::Pressed => println!("mouse button {button} pressed"),
|
||||
ElementState::Released => println!("mouse button {button} released"),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => fill::fill_window(&window),
|
||||
}
|
||||
WindowEvent::ModifiersChanged(new) => modifiers = new.state(),
|
||||
WindowEvent::RedrawRequested => fill::fill_window(&window),
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
Event::DeviceEvent { event, .. } => match event {
|
||||
DeviceEvent::MouseMotion { delta } => println!("mouse moved: {delta:?}"),
|
||||
DeviceEvent::Button { button, state } => match state {
|
||||
ElementState::Pressed => println!("mouse button {button} pressed"),
|
||||
ElementState::Released => println!("mouse button {button} released"),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -40,20 +40,19 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
});
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::UserEvent(event) => println!("user event: {event:?}"),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::UserEvent(event) => println!("user event: {event:?}"),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => elwt.exit(),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,18 +20,16 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let mut switched = false;
|
||||
let mut entered_id = window_2.id();
|
||||
let mut cursor_location = None;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| match event {
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
eprintln!("Switch which window is to be dragged by pressing \"x\".")
|
||||
}
|
||||
Event::WindowEvent { event, window_id } => match event {
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Pressed,
|
||||
button: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::CursorMoved { position, .. } => cursor_location = Some(position),
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
let window = if (window_id == window_1.id() && switched)
|
||||
|| (window_id == window_2.id() && !switched)
|
||||
{
|
||||
@@ -40,7 +38,15 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
&window_1
|
||||
};
|
||||
|
||||
window.drag_window().unwrap()
|
||||
match (button, state) {
|
||||
(MouseButton::Left, ElementState::Pressed) => window.drag_window().unwrap(),
|
||||
(MouseButton::Right, ElementState::Released) => {
|
||||
if let Some(position) = cursor_location {
|
||||
window.show_window_menu(position);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
WindowEvent::CursorEntered { .. } => {
|
||||
entered_id = window_id;
|
||||
@@ -54,20 +60,35 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
..
|
||||
},
|
||||
..
|
||||
} if c == "x" => {
|
||||
switched = !switched;
|
||||
name_windows(entered_id, switched, &window_1, &window_2);
|
||||
println!("Switched!")
|
||||
} => match c.as_str() {
|
||||
"x" => {
|
||||
switched = !switched;
|
||||
name_windows(entered_id, switched, &window_1, &window_2);
|
||||
println!("Switched!")
|
||||
}
|
||||
"d" => {
|
||||
let window = if (window_id == window_1.id() && switched)
|
||||
|| (window_id == window_2.id() && !switched)
|
||||
{
|
||||
&window_2
|
||||
} else {
|
||||
&window_1
|
||||
};
|
||||
|
||||
window.set_decorations(!window.is_decorated());
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if window_id == window_1.id() {
|
||||
fill::fill_window(&window_1);
|
||||
} else if window_id == window_2.id() {
|
||||
fill::fill_window(&window_2);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(wid) => {
|
||||
if wid == window_1.id() {
|
||||
fill::fill_window(&window_1);
|
||||
} else if wid == window_2.id() {
|
||||
fill::fill_window(&window_2);
|
||||
}
|
||||
}
|
||||
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,12 +52,10 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
println!("- I\tToggle mIn size limit");
|
||||
println!("- A\tToggle mAx size limit");
|
||||
|
||||
event_loop.run(move |event, elwt, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
@@ -67,7 +65,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
},
|
||||
..
|
||||
} => match key {
|
||||
Key::Escape => control_flow.set_exit(),
|
||||
Key::Escape => elwt.exit(),
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
@@ -155,12 +153,11 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,70 +22,65 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let mut close_requested = false;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
// `CloseRequested` is sent when the close button on the window is pressed (or
|
||||
// through whatever other mechanisms the window manager provides for closing a
|
||||
// window). If you don't handle this event, the close button won't actually do
|
||||
// anything.
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
// `CloseRequested` is sent when the close button on the window is pressed (or
|
||||
// through whatever other mechanisms the window manager provides for closing a
|
||||
// window). If you don't handle this event, the close button won't actually do
|
||||
// anything.
|
||||
// A common thing to do here is prompt the user if they have unsaved work.
|
||||
// Creating a proper dialog box for that is far beyond the scope of this
|
||||
// example, so here we'll just respond to the Y and N keys.
|
||||
println!("Are you ready to bid your window farewell? [Y/N]");
|
||||
close_requested = true;
|
||||
|
||||
// A common thing to do here is prompt the user if they have unsaved work.
|
||||
// Creating a proper dialog box for that is far beyond the scope of this
|
||||
// example, so here we'll just respond to the Y and N keys.
|
||||
println!("Are you ready to bid your window farewell? [Y/N]");
|
||||
close_requested = true;
|
||||
|
||||
// In applications where you can safely close the window without further
|
||||
// action from the user, this is generally where you'd handle cleanup before
|
||||
// closing the window. How to close the window is detailed in the handler for
|
||||
// the Y key.
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
match key.as_ref() {
|
||||
Key::Character("y") => {
|
||||
if close_requested {
|
||||
// This is where you'll want to do any cleanup you need.
|
||||
println!("Buh-bye!");
|
||||
|
||||
// For a single-window application like this, you'd normally just
|
||||
// break out of the event loop here. If you wanted to keep running the
|
||||
// event loop (i.e. if it's a multi-window application), you need to
|
||||
// drop the window. That closes it, and results in `Destroyed` being
|
||||
// sent.
|
||||
control_flow.set_exit();
|
||||
}
|
||||
}
|
||||
Key::Character("n") => {
|
||||
if close_requested {
|
||||
println!("Your window will continue to stay by your side.");
|
||||
close_requested = false;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
// In applications where you can safely close the window without further
|
||||
// action from the user, this is generally where you'd handle cleanup before
|
||||
// closing the window. How to close the window is detailed in the handler for
|
||||
// the Y key.
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
match key.as_ref() {
|
||||
Key::Character("y") => {
|
||||
if close_requested {
|
||||
// This is where you'll want to do any cleanup you need.
|
||||
println!("Buh-bye!");
|
||||
|
||||
// For a single-window application like this, you'd normally just
|
||||
// break out of the event loop here. If you wanted to keep running the
|
||||
// event loop (i.e. if it's a multi-window application), you need to
|
||||
// drop the window. That closes it, and results in `Destroyed` being
|
||||
// sent.
|
||||
elwt.exit();
|
||||
}
|
||||
}
|
||||
Key::Character("n") => {
|
||||
if close_requested {
|
||||
println!("Your window will continue to stay by your side.");
|
||||
close_requested = false;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
105
examples/ime.rs
105
examples/ime.rs
@@ -5,7 +5,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
event::{ElementState, Event, Ime, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, KeyCode},
|
||||
window::{ImePurpose, WindowBuilder},
|
||||
};
|
||||
@@ -39,71 +39,56 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
let mut cursor_position = PhysicalPosition::new(0.0, 0.0);
|
||||
let mut ime_pos = PhysicalPosition::new(0.0, 0.0);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CursorMoved { position, .. },
|
||||
..
|
||||
} => {
|
||||
cursor_position = position;
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
println!(
|
||||
"Setting ime position to {}, {}",
|
||||
cursor_position.x, cursor_position.y
|
||||
);
|
||||
ime_pos = cursor_position;
|
||||
if may_show_ime {
|
||||
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
cursor_position = position;
|
||||
}
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Ime(event),
|
||||
..
|
||||
} => {
|
||||
println!("{event:?}");
|
||||
may_show_ime = event != Ime::Disabled;
|
||||
if may_show_ime {
|
||||
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => {
|
||||
println!(
|
||||
"Setting ime position to {}, {}",
|
||||
cursor_position.x, cursor_position.y
|
||||
);
|
||||
ime_pos = cursor_position;
|
||||
if may_show_ime {
|
||||
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::KeyboardInput { event, .. },
|
||||
..
|
||||
} => {
|
||||
println!("key: {event:?}");
|
||||
WindowEvent::Ime(event) => {
|
||||
println!("{event:?}");
|
||||
may_show_ime = event != Ime::Disabled;
|
||||
if may_show_ime {
|
||||
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
println!("key: {event:?}");
|
||||
|
||||
if event.state == ElementState::Pressed && event.physical_key == KeyCode::F2 {
|
||||
ime_allowed = !ime_allowed;
|
||||
window.set_ime_allowed(ime_allowed);
|
||||
println!("\nIME allowed: {ime_allowed}\n");
|
||||
if event.state == ElementState::Pressed && event.physical_key == KeyCode::F2 {
|
||||
ime_allowed = !ime_allowed;
|
||||
window.set_ime_allowed(ime_allowed);
|
||||
println!("\nIME allowed: {ime_allowed}\n");
|
||||
}
|
||||
if event.state == ElementState::Pressed && event.logical_key == Key::F3 {
|
||||
ime_purpose = match ime_purpose {
|
||||
ImePurpose::Normal => ImePurpose::Password,
|
||||
ImePurpose::Password => ImePurpose::Terminal,
|
||||
_ => ImePurpose::Normal,
|
||||
};
|
||||
window.set_ime_purpose(ime_purpose);
|
||||
println!("\nIME purpose: {ime_purpose:?}\n");
|
||||
}
|
||||
}
|
||||
if event.state == ElementState::Pressed && event.logical_key == Key::F3 {
|
||||
ime_purpose = match ime_purpose {
|
||||
ImePurpose::Normal => ImePurpose::Password,
|
||||
ImePurpose::Password => ImePurpose::Terminal,
|
||||
_ => ImePurpose::Normal,
|
||||
};
|
||||
window.set_ime_purpose(ime_purpose);
|
||||
println!("\nIME purpose: {ime_purpose:?}\n");
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState},
|
||||
// WARNING: This is not available on all platforms (for example on the web).
|
||||
platform::modifier_supplement::KeyEventExtModifierSupplement,
|
||||
@@ -31,12 +31,10 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let mut modifiers = ModifiersState::default();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::ModifiersChanged(new) => {
|
||||
modifiers = new.state();
|
||||
}
|
||||
@@ -54,12 +52,11 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,12 +34,10 @@ In both cases the example window should move like the content of a scroll area i
|
||||
In other words, the deltas indicate the direction in which to move the content (in this case the window)."
|
||||
);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||
winit::event::MouseScrollDelta::LineDelta(x, y) => {
|
||||
println!("mouse wheel Line Delta: ({x},{y})");
|
||||
@@ -57,12 +55,11 @@ In other words, the deltas indicate the direction in which to move the content (
|
||||
window.set_outer_position(pos)
|
||||
}
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -173,11 +173,10 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
});
|
||||
}
|
||||
event_loop.run(move |event, _event_loop, control_flow| {
|
||||
match !window_senders.is_empty() {
|
||||
true => control_flow.set_wait(),
|
||||
false => control_flow.set_exit(),
|
||||
};
|
||||
event_loop.run(move |event, elwt| {
|
||||
if window_senders.is_empty() {
|
||||
elwt.exit()
|
||||
}
|
||||
match event {
|
||||
Event::WindowEvent { event, window_id } => match event {
|
||||
WindowEvent::CloseRequested
|
||||
|
||||
@@ -26,45 +26,40 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
println!("Press N to open a new window.");
|
||||
|
||||
event_loop.run(move |event, event_loop, control_flow| {
|
||||
control_flow.set_wait();
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, window_id } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
println!("Window {window_id:?} has received the signal to close");
|
||||
|
||||
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);
|
||||
|
||||
// This drops the window, causing it to close.
|
||||
windows.remove(&window_id);
|
||||
|
||||
if windows.is_empty() {
|
||||
control_flow.set_exit();
|
||||
}
|
||||
if windows.is_empty() {
|
||||
elwt.exit();
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
logical_key: Key::Character(c),
|
||||
..
|
||||
},
|
||||
is_synthetic: false,
|
||||
..
|
||||
} if matches!(c.as_ref(), "n" | "N") => {
|
||||
let window = Window::new(event_loop).unwrap();
|
||||
println!("Opened a new window: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
logical_key: Key::Character(c),
|
||||
..
|
||||
},
|
||||
is_synthetic: false,
|
||||
..
|
||||
} if matches!(c.as_ref(), "n" | "N") => {
|
||||
let window = Window::new(elwt).unwrap();
|
||||
println!("Opened a new window: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Event::RedrawRequested(window_id) => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,27 +19,24 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
event_loop.run(move |event, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => {
|
||||
window.request_redraw();
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,17 +33,18 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
});
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
event_loop.run(move |event, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
} => elwt.exit(),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
|
||||
@@ -27,12 +27,10 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
@@ -46,12 +44,11 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
println!("Resizable: {resizable}");
|
||||
window.set_resizable(resizable);
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,60 +32,53 @@ mod example {
|
||||
let mut counter = 0;
|
||||
let mut create_first_window = false;
|
||||
|
||||
event_loop.run(move |event, elwt, flow| {
|
||||
event_loop.run(move |event, elwt| {
|
||||
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 } => match 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;
|
||||
WindowEvent::CloseRequested => {
|
||||
// Remove the window from the map.
|
||||
windows.remove(&window_id);
|
||||
if windows.is_empty() {
|
||||
elwt.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);
|
||||
WindowEvent::ActivationTokenDone { token, .. } => {
|
||||
current_token = Some(token);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
super::fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// See if we've passed the deadline.
|
||||
@@ -109,8 +102,6 @@ mod example {
|
||||
counter += 1;
|
||||
create_first_window = false;
|
||||
}
|
||||
|
||||
flow.set_wait();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{Theme, WindowBuilder},
|
||||
};
|
||||
@@ -27,53 +27,42 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
println!(" (L) Light theme");
|
||||
println!(" (D) Dark theme");
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::ThemeChanged(theme),
|
||||
window_id,
|
||||
..
|
||||
} if window_id == window.id() => {
|
||||
println!("Theme is changed: {theme:?}")
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("A" | "a") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(None);
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { window_id, event } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::ThemeChanged(theme) if window_id == window.id() => {
|
||||
println!("Theme is changed: {theme:?}")
|
||||
}
|
||||
Key::Character("L" | "l") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(Some(Theme::Light));
|
||||
}
|
||||
Key::Character("D" | "d") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(Some(Theme::Dark));
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("A" | "a") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(None);
|
||||
}
|
||||
Key::Character("L" | "l") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(Some(Theme::Light));
|
||||
}
|
||||
Key::Character("D" | "d") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(Some(Theme::Dark));
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use web_time::Instant;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
@@ -27,22 +27,25 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let timer_length = Duration::new(1, 0);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
event_loop.run(move |event, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
control_flow.set_wait_until(Instant::now() + timer_length);
|
||||
elwt.set_control_flow(ControlFlow::WaitUntil(Instant::now() + timer_length));
|
||||
}
|
||||
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
|
||||
control_flow.set_wait_until(Instant::now() + timer_length);
|
||||
elwt.set_control_flow(ControlFlow::WaitUntil(Instant::now() + timer_length));
|
||||
println!("\nTimer\n");
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
} => elwt.exit(),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
@@ -19,12 +19,10 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
println!("Only supported on macOS at the moment.");
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::TouchpadMagnify { delta, .. } => {
|
||||
if delta > 0.0 {
|
||||
println!("Zoomed in {delta}");
|
||||
@@ -42,10 +40,11 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
println!("Rotated clockwise {delta}");
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if let Event::RedrawRequested(_) = event {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,19 +22,17 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
window.set_title("A fantastic window!");
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
event_loop.run(move |event, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
use winit::window::Window;
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
|
||||
pub(super) fn fill_window(window: &Window) {
|
||||
use softbuffer::{Context, Surface};
|
||||
use std::cell::RefCell;
|
||||
@@ -80,7 +80,7 @@ pub(super) fn fill_window(window: &Window) {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
|
||||
pub(super) fn fill_window(_window: &Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ pub fn main() -> Result<(), impl std::error::Error> {
|
||||
#[cfg(wasm_platform)]
|
||||
let log_list = wasm::insert_canvas_and_create_log_list(&window);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
#[cfg(wasm_platform)]
|
||||
wasm::log_event(&log_list, &event);
|
||||
|
||||
@@ -31,7 +29,7 @@ pub fn main() -> Result<(), impl std::error::Error> {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
@@ -65,7 +63,10 @@ mod wasm {
|
||||
|
||||
use softbuffer::{Surface, SurfaceExtWeb};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use winit::{event::Event, window::Window};
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
@@ -116,6 +117,10 @@ mod wasm {
|
||||
// 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.
|
||||
let event = match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => None,
|
||||
Event::WindowEvent { event, .. } => Some(format!("{event:?}")),
|
||||
Event::Resumed | Event::Suspended => Some(format!("{event:?}")),
|
||||
_ => None,
|
||||
|
||||
@@ -12,7 +12,7 @@ mod wasm {
|
||||
use winit::{
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
platform::web::WindowBuilderExtWebSys,
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
@@ -47,18 +47,14 @@ This example demonstrates the desired future functionality which will possibly b
|
||||
// Render once with the size info we currently have
|
||||
render_circle(&canvas, window.inner_size());
|
||||
|
||||
let _ = event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Resized(resize),
|
||||
window_id,
|
||||
} if window_id == window.id() => {
|
||||
render_circle(&canvas, resize);
|
||||
}
|
||||
_ => (),
|
||||
let _ = event_loop.run(move |event, _| match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Resized(resize),
|
||||
window_id,
|
||||
} if window_id == window.id() => {
|
||||
render_circle(&canvas, resize);
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,23 +20,23 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
event_loop.run(move |event, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Notify the windowing system that we'll be presenting to the window.
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
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);
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -31,45 +31,38 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
event_loop.listen_device_events(DeviceEvents::Always);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("F" | "f") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::CLOSE);
|
||||
}
|
||||
Key::Character("G" | "g") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::MAXIMIZE);
|
||||
}
|
||||
Key::Character("H" | "h") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::MINIMIZE);
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { window_id, event } = event {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("F" | "f") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::CLOSE);
|
||||
}
|
||||
Key::Character("G" | "g") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::MAXIMIZE);
|
||||
}
|
||||
Key::Character("H" | "h") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::MINIMIZE);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::CloseRequested if window_id == window.id() => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,9 +38,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
event_loop.listen_device_events(DeviceEvents::Always);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
match event {
|
||||
// This used to use the virtual key, but the new API
|
||||
// only provides the `physical_key` (`Code`).
|
||||
@@ -68,75 +66,71 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Character(key_str),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key_str.as_ref() {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
"e" => {
|
||||
fn area(size: PhysicalSize<u32>) -> u32 {
|
||||
size.width * size.height
|
||||
}
|
||||
Event::WindowEvent { window_id, event } => match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Character(key_str),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key_str.as_ref() {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
"e" => {
|
||||
fn area(size: PhysicalSize<u32>) -> u32 {
|
||||
size.width * size.height
|
||||
}
|
||||
|
||||
let monitor = window.current_monitor().unwrap();
|
||||
if let Some(mode) = monitor
|
||||
.video_modes()
|
||||
.max_by(|a, b| area(a.size()).cmp(&area(b.size())))
|
||||
{
|
||||
window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
|
||||
} else {
|
||||
eprintln!("no video modes available");
|
||||
let monitor = window.current_monitor().unwrap();
|
||||
if let Some(mode) = monitor
|
||||
.video_modes()
|
||||
.max_by(|a, b| area(a.size()).cmp(&area(b.size())))
|
||||
{
|
||||
window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
|
||||
} else {
|
||||
eprintln!("no video modes available");
|
||||
}
|
||||
}
|
||||
}
|
||||
"f" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
let monitor = window.current_monitor();
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
|
||||
"f" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
let monitor = window.current_monitor();
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
|
||||
}
|
||||
}
|
||||
}
|
||||
"p" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||
"p" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||
}
|
||||
}
|
||||
}
|
||||
"m" => {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
"q" => {
|
||||
control_flow.set_exit();
|
||||
}
|
||||
"v" => {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
}
|
||||
"x" => {
|
||||
let is_maximized = window.is_maximized();
|
||||
window.set_maximized(!is_maximized);
|
||||
"m" => {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
"q" => {
|
||||
elwt.exit();
|
||||
}
|
||||
"v" => {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
}
|
||||
"x" => {
|
||||
let is_maximized = window.is_maximized();
|
||||
window.set_maximized(!is_maximized);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::CloseRequested if window_id == window.id() => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{CursorIcon, ResizeDirection, WindowBuilder},
|
||||
};
|
||||
@@ -27,12 +27,12 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
let mut border = false;
|
||||
let mut cursor_location = None;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| match event {
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
eprintln!("Press 'B' to toggle borderless")
|
||||
}
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
if !window.is_decorated() {
|
||||
let new_location =
|
||||
@@ -68,11 +68,12 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
border = !border;
|
||||
window.set_decorations(border);
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::path::Path;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::Event,
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::{Icon, WindowBuilder},
|
||||
};
|
||||
@@ -33,20 +33,16 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
use winit::event::WindowEvent::*;
|
||||
match event {
|
||||
CloseRequested => control_flow.set_exit(),
|
||||
DroppedFile(path) => {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::DroppedFile(path) => {
|
||||
window.set_window_icon(Some(load_icon(&path)));
|
||||
}
|
||||
WindowEvent::RedrawRequested => fill::fill_window(&window),
|
||||
_ => (),
|
||||
}
|
||||
} else if let Event::RedrawRequested(_) = event {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
error::EventLoopError,
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
platform::run_ondemand::EventLoopExtRunOnDemand,
|
||||
platform::run_on_demand::EventLoopExtRunOnDemand,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
@@ -30,8 +30,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
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();
|
||||
event_loop.run_on_demand(move |event, elwt| {
|
||||
println!("Run {idx}: {:?}", event);
|
||||
|
||||
if let Some(window) = &app.window {
|
||||
@@ -44,7 +43,10 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
app.window = None;
|
||||
}
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::RedrawRequested(_) => {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
_ => (),
|
||||
@@ -57,7 +59,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
} if id == window_id => {
|
||||
println!("--------------------------------------------------------- Window {idx} Destroyed");
|
||||
app.window_id = None;
|
||||
control_flow.set_exit();
|
||||
elwt.exit();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@@ -65,7 +67,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Fantastic window number one!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(event_loop)
|
||||
.build(elwt)
|
||||
.unwrap();
|
||||
app.window_id = Some(window.id());
|
||||
app.window = Some(window);
|
||||
@@ -31,41 +31,38 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let mut option_as_alt = window.option_as_alt();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Pressed,
|
||||
button: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
option_as_alt = match option_as_alt {
|
||||
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
|
||||
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
|
||||
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
|
||||
OptionAsAlt::Both => OptionAsAlt::None,
|
||||
};
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Pressed,
|
||||
button: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
option_as_alt = match option_as_alt {
|
||||
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
|
||||
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
|
||||
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
|
||||
OptionAsAlt::Both => OptionAsAlt::None,
|
||||
};
|
||||
|
||||
println!("Received Mouse click, toggling option_as_alt to: {option_as_alt:?}");
|
||||
window.set_option_as_alt(option_as_alt);
|
||||
}
|
||||
WindowEvent::KeyboardInput { .. } => println!("KeyboardInput: {event:?}"),
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
println!("Received Mouse click, toggling option_as_alt to: {option_as_alt:?}");
|
||||
window.set_option_as_alt(option_as_alt);
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
WindowEvent::KeyboardInput { .. } => println!("KeyboardInput: {event:?}"),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ fn main() -> std::process::ExitCode {
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
@@ -32,9 +32,7 @@ fn main() -> std::process::ExitCode {
|
||||
|
||||
'main: loop {
|
||||
let timeout = Some(Duration::ZERO);
|
||||
let status = event_loop.pump_events(timeout, |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
let status = event_loop.pump_events(timeout, |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = &event {
|
||||
// Print only Window events to reduce noise
|
||||
println!("{event:?}");
|
||||
@@ -44,11 +42,14 @@ fn main() -> std::process::ExitCode {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
|
||||
@@ -24,27 +24,18 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let mut has_increments = true;
|
||||
|
||||
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::WindowEvent {
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Space,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
KeyEvent {
|
||||
logical_key: Key::Space,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
window_id,
|
||||
} if window_id == window.id() => {
|
||||
..
|
||||
} => {
|
||||
has_increments = !has_increments;
|
||||
|
||||
let new_increments = match window.resize_increments() {
|
||||
@@ -54,11 +45,13 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
debug!("Had increments: {}", new_increments.is_none());
|
||||
window.set_resize_increments(new_increments);
|
||||
}
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::RedrawRequested(_) => {
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -30,78 +30,73 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
println!("Press N to open a new window.");
|
||||
|
||||
event_loop.run(move |event, event_loop, control_flow| {
|
||||
control_flow.set_wait();
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, window_id } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
println!("Window {window_id:?} has received the signal to close");
|
||||
|
||||
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);
|
||||
|
||||
// This drops the window, causing it to close.
|
||||
windows.remove(&window_id);
|
||||
|
||||
if windows.is_empty() {
|
||||
control_flow.set_exit();
|
||||
}
|
||||
if windows.is_empty() {
|
||||
elwt.exit();
|
||||
}
|
||||
WindowEvent::Resized(_) => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
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(elwt)
|
||||
.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);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Event::RedrawRequested(window_id) => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,18 +32,19 @@ mod imple {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
// Notify the windowing system that we'll be presenting to the window.
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(&window);
|
||||
|
||||
@@ -82,8 +82,7 @@
|
||||
//! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the
|
||||
//! XRandR scaling method. Generally speaking, you should try to configure the standard system
|
||||
//! variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`.
|
||||
//! - **Wayland:** On Wayland, scale factors are set per-screen by the server, and are always
|
||||
//! integers (most often 1 or 2).
|
||||
//! - **Wayland:** Scale factor is suggested by the the compositor.
|
||||
//! - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range
|
||||
//! from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more
|
||||
//! information.
|
||||
|
||||
105
src/event.rs
105
src/event.rs
@@ -7,25 +7,24 @@
|
||||
//! approximate the basic ordering loop of [`EventLoop::run(...)`] like this:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! let mut control_flow = ControlFlow::Poll;
|
||||
//! let mut start_cause = StartCause::Init;
|
||||
//!
|
||||
//! while control_flow != ControlFlow::Exit {
|
||||
//! event_handler(NewEvents(start_cause), ..., &mut control_flow);
|
||||
//! while !elwt.exiting() {
|
||||
//! event_handler(NewEvents(start_cause), elwt);
|
||||
//!
|
||||
//! for e in (window events, user events, device events) {
|
||||
//! event_handler(e, ..., &mut control_flow);
|
||||
//! event_handler(e, elwt);
|
||||
//! }
|
||||
//!
|
||||
//! for w in (redraw windows) {
|
||||
//! event_handler(RedrawRequested(w), ..., &mut control_flow);
|
||||
//! event_handler(RedrawRequested(w), elwt);
|
||||
//! }
|
||||
//!
|
||||
//! event_handler(AboutToWait, ..., &mut control_flow);
|
||||
//! start_cause = wait_if_necessary(control_flow);
|
||||
//! event_handler(AboutToWait, elwt);
|
||||
//! start_cause = wait_if_necessary();
|
||||
//! }
|
||||
//!
|
||||
//! event_handler(LoopExiting, ..., &mut control_flow);
|
||||
//! event_handler(LoopExiting, elwt);
|
||||
//! ```
|
||||
//!
|
||||
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
|
||||
@@ -62,7 +61,7 @@ pub enum Event<T: 'static> {
|
||||
///
|
||||
/// This event type is useful as a place to put code that should be done before you start
|
||||
/// processing events, such as updating frame timing information for benchmarking or checking
|
||||
/// the [`StartCause`][crate::event::StartCause] to see if a timer set by
|
||||
/// the [`StartCause`] to see if a timer set by
|
||||
/// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed.
|
||||
NewEvents(StartCause),
|
||||
|
||||
@@ -218,26 +217,40 @@ pub enum Event<T: 'static> {
|
||||
/// ups and also lots of corresponding `AboutToWait` events.
|
||||
///
|
||||
/// This is not an ideal event to drive application rendering from and instead applications
|
||||
/// should render in response to [`Event::RedrawRequested`](crate::event::Event::RedrawRequested)
|
||||
/// events.
|
||||
/// should render in response to [`WindowEvent::RedrawRequested`] events.
|
||||
AboutToWait,
|
||||
|
||||
/// Emitted when a window should be redrawn.
|
||||
///
|
||||
/// This gets triggered in two scenarios:
|
||||
/// - The OS has performed an operation that's invalidated the window's contents (such as
|
||||
/// resizing the window).
|
||||
/// - The application has explicitly requested a redraw via [`Window::request_redraw`].
|
||||
///
|
||||
/// Winit will aggregate duplicate redraw requests into a single event, to
|
||||
/// help avoid duplicating rendering work.
|
||||
RedrawRequested(WindowId),
|
||||
|
||||
/// 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
|
||||
/// gets emitted. You generally want to treat this as a "do on quit" event.
|
||||
LoopExiting,
|
||||
|
||||
/// Emitted when the application has received a memory warning.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// ### Android
|
||||
///
|
||||
/// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application
|
||||
/// must [release memory] or risk being killed.
|
||||
///
|
||||
/// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory()
|
||||
/// [release memory]: https://developer.android.com/topic/performance/memory#release
|
||||
///
|
||||
/// ### iOS
|
||||
///
|
||||
/// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`]
|
||||
/// callback. The application must free as much memory as possible or risk being terminated, see
|
||||
/// [how to respond to memory warnings].
|
||||
///
|
||||
/// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni
|
||||
/// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings
|
||||
///
|
||||
/// ### Others
|
||||
///
|
||||
/// - **macOS / Wayland / Windows / Orbital:** Unsupported.
|
||||
MemoryWarning,
|
||||
}
|
||||
|
||||
impl<T> Event<T> {
|
||||
@@ -250,10 +263,10 @@ impl<T> Event<T> {
|
||||
DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }),
|
||||
NewEvents(cause) => Ok(NewEvents(cause)),
|
||||
AboutToWait => Ok(AboutToWait),
|
||||
RedrawRequested(wid) => Ok(RedrawRequested(wid)),
|
||||
LoopExiting => Ok(LoopExiting),
|
||||
Suspended => Ok(Suspended),
|
||||
Resumed => Ok(Resumed),
|
||||
MemoryWarning => Ok(MemoryWarning),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,15 +558,39 @@ pub enum WindowEvent {
|
||||
/// This is different to window visibility as it depends on whether the window is closed,
|
||||
/// minimised, set invisible, or fully occluded by another window.
|
||||
///
|
||||
/// Platform-specific behavior:
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// ### iOS
|
||||
///
|
||||
/// On iOS, the `Occluded(false)` event is emitted in response to an [`applicationWillEnterForeground`]
|
||||
/// callback which means the application should start preparing its data. The `Occluded(true)` event is
|
||||
/// emitted in response to an [`applicationDidEnterBackground`] callback which means the application
|
||||
/// should free resources (according to the [iOS application lifecycle]).
|
||||
///
|
||||
/// [`applicationWillEnterForeground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground
|
||||
/// [`applicationDidEnterBackground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground
|
||||
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
|
||||
///
|
||||
/// ### Others
|
||||
///
|
||||
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
|
||||
/// - **iOS / Android / Wayland / Windows / Orbital:** Unsupported.
|
||||
/// - **Android / 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),
|
||||
|
||||
/// Emitted when a window should be redrawn.
|
||||
///
|
||||
/// This gets triggered in two scenarios:
|
||||
/// - The OS has performed an operation that's invalidated the window's contents (such as
|
||||
/// resizing the window).
|
||||
/// - The application has explicitly requested a redraw via [`Window::request_redraw`].
|
||||
///
|
||||
/// Winit will aggregate duplicate redraw requests into a single event, to
|
||||
/// help avoid duplicating rendering work.
|
||||
RedrawRequested,
|
||||
}
|
||||
|
||||
/// Identifier of an input device.
|
||||
@@ -575,7 +612,8 @@ impl DeviceId {
|
||||
///
|
||||
/// **Passing this into a winit function will result in undefined behavior.**
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
DeviceId(platform_impl::DeviceId::dummy())
|
||||
#[allow(unused_unsafe)]
|
||||
DeviceId(unsafe { platform_impl::DeviceId::dummy() })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,10 +659,6 @@ pub enum DeviceEvent {
|
||||
},
|
||||
|
||||
Key(RawKeyEvent),
|
||||
|
||||
Text {
|
||||
codepoint: char,
|
||||
},
|
||||
}
|
||||
|
||||
/// Describes a keyboard input as a raw device event.
|
||||
@@ -674,7 +708,7 @@ pub struct KeyEvent {
|
||||
// Allowing `broken_intra_doc_links` for `logical_key`, because
|
||||
// `key_without_modifiers` is not available on all platforms
|
||||
#[cfg_attr(
|
||||
not(any(target_os = "macos", target_os = "windows", target_os = "linux")),
|
||||
not(any(windows_platform, macos_platform, x11_platform, wayland_platform)),
|
||||
allow(rustdoc::broken_intra_doc_links)
|
||||
)]
|
||||
/// This value is affected by all modifiers except <kbd>Ctrl</kbd>.
|
||||
@@ -942,7 +976,10 @@ pub struct Touch {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - Only available on **iOS** 9.0+, **Windows** 8+, and **Web**.
|
||||
/// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**.
|
||||
/// - **Android**: This will never be [None]. If the device doesn't support pressure
|
||||
/// sensitivity, force will either be 0.0 or 1.0. Also see the
|
||||
/// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE).
|
||||
pub force: Option<Force>,
|
||||
/// Unique identifier of a finger.
|
||||
pub id: u64,
|
||||
@@ -1032,7 +1069,7 @@ impl ElementState {
|
||||
///
|
||||
/// **macOS:** `Back` and `Forward` might not work with all hardware.
|
||||
/// **Orbital:** `Back` and `Forward` are unsupported due to orbital not supporting them.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
@@ -1123,7 +1160,6 @@ mod tests {
|
||||
let wid = unsafe { WindowId::dummy() };
|
||||
x(UserEvent(()));
|
||||
x(NewEvents(event::StartCause::Init));
|
||||
x(RedrawRequested(wid));
|
||||
x(AboutToWait);
|
||||
x(LoopExiting);
|
||||
x(Suspended);
|
||||
@@ -1222,7 +1258,6 @@ mod tests {
|
||||
button: 0,
|
||||
state: event::ElementState::Pressed,
|
||||
});
|
||||
with_device_event(Text { codepoint: 'a' });
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::{error, fmt};
|
||||
|
||||
use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle};
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time::{Duration, Instant};
|
||||
#[cfg(wasm_platform)]
|
||||
@@ -147,28 +146,21 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set by the user callback given to the [`EventLoop::run`] method.
|
||||
/// Set through [`EventLoopWindowTarget::set_control_flow()`].
|
||||
///
|
||||
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
|
||||
///
|
||||
/// Defaults to [`Poll`].
|
||||
/// Defaults to [`Wait`].
|
||||
///
|
||||
/// ## Persistency
|
||||
///
|
||||
/// 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.
|
||||
/// Changes are **not** persistent between multiple calls to `run_ondemand` - issuing a new call will
|
||||
/// reset the control flow to [`Poll`].
|
||||
///
|
||||
/// [`ExitWithCode`]: Self::ExitWithCode
|
||||
/// [`Poll`]: Self::Poll
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
/// [`Wait`]: Self::Wait
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
/// whether or not new events are available to process.
|
||||
Poll,
|
||||
|
||||
/// When the current loop iteration finishes, suspend the thread until another event arrives.
|
||||
#[default]
|
||||
Wait,
|
||||
|
||||
/// When the current loop iteration finishes, suspend the thread until either another event
|
||||
@@ -180,88 +172,22 @@ pub enum ControlFlow {
|
||||
///
|
||||
/// [`Poll`]: Self::Poll
|
||||
WaitUntil(Instant),
|
||||
|
||||
/// 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
|
||||
/// result in the `control_flow` parameter being reset to `ExitWithCode`.
|
||||
///
|
||||
/// The contained number will be used as exit code. The [`Exit`] constant is a shortcut for this
|
||||
/// with exit code 0.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android / iOS / Web:** The supplied exit code is unused.
|
||||
/// - **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
|
||||
/// [`std::process::exit`].
|
||||
///
|
||||
/// [`LoopExiting`]: Event::LoopExiting
|
||||
/// [`Exit`]: ControlFlow::Exit
|
||||
ExitWithCode(i32),
|
||||
}
|
||||
|
||||
impl ControlFlow {
|
||||
/// Alias for [`ExitWithCode`]`(0)`.
|
||||
///
|
||||
/// [`ExitWithCode`]: Self::ExitWithCode
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Exit: Self = Self::ExitWithCode(0);
|
||||
|
||||
/// Sets this to [`Poll`].
|
||||
///
|
||||
/// [`Poll`]: Self::Poll
|
||||
pub fn set_poll(&mut self) {
|
||||
*self = Self::Poll;
|
||||
}
|
||||
|
||||
/// Sets this to [`Wait`].
|
||||
///
|
||||
/// [`Wait`]: Self::Wait
|
||||
pub fn set_wait(&mut self) {
|
||||
*self = Self::Wait;
|
||||
}
|
||||
|
||||
/// Sets this to [`WaitUntil`]`(instant)`.
|
||||
///
|
||||
/// [`WaitUntil`]: Self::WaitUntil
|
||||
pub fn set_wait_until(&mut self, instant: Instant) {
|
||||
*self = Self::WaitUntil(instant);
|
||||
}
|
||||
|
||||
/// Sets this to wait until a timeout has expired.
|
||||
/// Creates a [`ControlFlow`] that waits until a timeout has expired.
|
||||
///
|
||||
/// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
|
||||
/// instead set to [`Wait`].
|
||||
///
|
||||
/// [`WaitUntil`]: Self::WaitUntil
|
||||
/// [`Wait`]: Self::Wait
|
||||
pub fn set_wait_timeout(&mut self, timeout: Duration) {
|
||||
pub fn wait_duration(timeout: Duration) -> Self {
|
||||
match Instant::now().checked_add(timeout) {
|
||||
Some(instant) => self.set_wait_until(instant),
|
||||
None => self.set_wait(),
|
||||
Some(instant) => Self::WaitUntil(instant),
|
||||
None => Self::Wait,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets this to [`ExitWithCode`]`(code)`.
|
||||
///
|
||||
/// [`ExitWithCode`]: Self::ExitWithCode
|
||||
pub fn set_exit_with_code(&mut self, code: i32) {
|
||||
*self = Self::ExitWithCode(code);
|
||||
}
|
||||
|
||||
/// Sets this to [`Exit`].
|
||||
///
|
||||
/// [`Exit`]: Self::Exit
|
||||
pub fn set_exit(&mut self) {
|
||||
*self = Self::Exit;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ControlFlow {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::Poll
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop<()> {
|
||||
@@ -286,13 +212,10 @@ impl<T> EventLoop<T> {
|
||||
/// Since the closure is `'static`, it must be a `move` closure if it needs to
|
||||
/// access any data from the calling context.
|
||||
///
|
||||
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the
|
||||
/// event loop's behavior.
|
||||
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11 / Wayland:** The program terminates with exit code 1 if the display server
|
||||
/// 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
|
||||
@@ -306,12 +229,12 @@ impl<T> EventLoop<T> {
|
||||
///
|
||||
/// This function won't be available with `target_feature = "exception-handling"`.
|
||||
///
|
||||
/// [`ControlFlow`]: crate::event_loop::ControlFlow
|
||||
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow
|
||||
#[inline]
|
||||
#[cfg(not(all(wasm_platform, target_feature = "exception-handling")))]
|
||||
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &EventLoopWindowTarget<T>),
|
||||
{
|
||||
self.event_loop.run(event_handler)
|
||||
}
|
||||
@@ -324,10 +247,18 @@ impl<T> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> HasRawDisplayHandle for EventLoop<T> {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl<T> rwh_06::HasDisplayHandle for EventLoop<T> {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
rwh_06::HasDisplayHandle::display_handle(&**self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
|
||||
/// Returns a [`raw_window_handle::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
self.event_loop.window_target().p.raw_display_handle()
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::HasRawDisplayHandle::raw_display_handle(&**self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +286,7 @@ impl<T> EventLoopWindowTarget<T> {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **Wayland:** Always returns `None`.
|
||||
/// **Wayland / Web:** Always returns `None`.
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
self.p
|
||||
@@ -374,16 +305,49 @@ impl<T> EventLoopWindowTarget<T> {
|
||||
/// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
|
||||
///
|
||||
/// [`DeviceEvent`]: crate::event::DeviceEvent
|
||||
pub fn listen_device_events(&self, _allowed: DeviceEvents) {
|
||||
#[cfg(any(x11_platform, wasm_platform, wayland_platform, windows))]
|
||||
self.p.listen_device_events(_allowed);
|
||||
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
||||
self.p.listen_device_events(allowed);
|
||||
}
|
||||
|
||||
/// Sets the [`ControlFlow`].
|
||||
pub fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.p.set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
/// Gets the current [`ControlFlow`].
|
||||
pub fn control_flow(&self) -> ControlFlow {
|
||||
self.p.control_flow()
|
||||
}
|
||||
|
||||
/// This exits the event loop.
|
||||
///
|
||||
/// See [`LoopExiting`](Event::LoopExiting).
|
||||
pub fn exit(&self) {
|
||||
self.p.exit()
|
||||
}
|
||||
|
||||
/// Returns if the [`EventLoop`] is about to stop.
|
||||
///
|
||||
/// See [`exit()`](Self::exit).
|
||||
pub fn exiting(&self) -> bool {
|
||||
self.p.exiting()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> HasRawDisplayHandle for EventLoopWindowTarget<T> {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.p.raw_display_handle_rwh_06()?;
|
||||
// SAFETY: The display will never be deallocated while the event loop is alive.
|
||||
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoopWindowTarget<T> {
|
||||
/// Returns a [`raw_window_handle::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
self.p.raw_display_handle()
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
self.p.raw_display_handle_rwh_05()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ pub use smol_str::SmolStr;
|
||||
///
|
||||
/// - Correctly match key press and release events.
|
||||
/// - On non-web platforms, support assigning keybinds to virtually any key through a UI.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum NativeKeyCode {
|
||||
Unidentified,
|
||||
@@ -196,7 +196,7 @@ impl std::fmt::Debug for NativeKey {
|
||||
///
|
||||
/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum KeyCode {
|
||||
/// This variant is used when the key cannot be translated to any other variant.
|
||||
|
||||
37
src/lib.rs
37
src/lib.rs
@@ -30,9 +30,7 @@
|
||||
//!
|
||||
//! You can retrieve events by calling [`EventLoop::run`][event_loop_run]. This function will
|
||||
//! 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
|
||||
//! [`ControlFlow`]`::`[`ExitWithCode`] (which [`ControlFlow`]`::`[`Exit`] aliases to), at which
|
||||
//! point [`Event`]`::`[`LoopExiting`] is emitted and the entire program terminates.
|
||||
//! will run until [`exit()`] is used, at which point [`Event`]`::`[`LoopExiting`].
|
||||
//!
|
||||
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
|
||||
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on
|
||||
@@ -44,30 +42,30 @@
|
||||
//! ```no_run
|
||||
//! use winit::{
|
||||
//! event::{Event, WindowEvent},
|
||||
//! event_loop::EventLoop,
|
||||
//! event_loop::{ControlFlow, EventLoop},
|
||||
//! window::WindowBuilder,
|
||||
//! };
|
||||
//!
|
||||
//! let event_loop = EventLoop::new().unwrap();
|
||||
//! let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
//!
|
||||
//! event_loop.run(move |event, _, control_flow| {
|
||||
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
|
||||
//! // dispatched any events. This is ideal for games and similar applications.
|
||||
//! control_flow.set_poll();
|
||||
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
|
||||
//! // dispatched any events. This is ideal for games and similar applications.
|
||||
//! event_loop.set_control_flow(ControlFlow::Poll);
|
||||
//!
|
||||
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
|
||||
//! // This is ideal for non-game applications that only update in response to user
|
||||
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
|
||||
//! control_flow.set_wait();
|
||||
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
|
||||
//! // This is ideal for non-game applications that only update in response to user
|
||||
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
|
||||
//! event_loop.set_control_flow(ControlFlow::Wait);
|
||||
//!
|
||||
//! event_loop.run(move |event, elwt| {
|
||||
//! match event {
|
||||
//! Event::WindowEvent {
|
||||
//! event: WindowEvent::CloseRequested,
|
||||
//! ..
|
||||
//! } => {
|
||||
//! println!("The close button was pressed; stopping");
|
||||
//! control_flow.set_exit();
|
||||
//! elwt.exit();
|
||||
//! },
|
||||
//! Event::AboutToWait => {
|
||||
//! // Application update code.
|
||||
@@ -79,7 +77,10 @@
|
||||
//! // can just render here instead.
|
||||
//! window.request_redraw();
|
||||
//! },
|
||||
//! Event::RedrawRequested(_) => {
|
||||
//! Event::WindowEvent {
|
||||
//! event: WindowEvent::RedrawRequested,
|
||||
//! ..
|
||||
//! } => {
|
||||
//! // Redraw the application.
|
||||
//! //
|
||||
//! // It's preferable for applications that do not render continuously to render in
|
||||
@@ -112,9 +113,7 @@
|
||||
//! [`EventLoopExtPumpEvents::pump_events`]: ./platform/pump_events/trait.EventLoopExtPumpEvents.html#tymethod.pump_events
|
||||
//! [`EventLoop::new()`]: event_loop::EventLoop::new
|
||||
//! [event_loop_run]: event_loop::EventLoop::run
|
||||
//! [`ControlFlow`]: event_loop::ControlFlow
|
||||
//! [`Exit`]: event_loop::ControlFlow::Exit
|
||||
//! [`ExitWithCode`]: event_loop::ControlFlow::ExitWithCode
|
||||
//! [`exit()`]: event_loop::EventLoopWindowTarget::exit
|
||||
//! [`Window`]: window::Window
|
||||
//! [`WindowId`]: window::WindowId
|
||||
//! [`WindowBuilder`]: window::WindowBuilder
|
||||
@@ -134,11 +133,15 @@
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
|
||||
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub use rwh_06 as raw_window_handle;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
@@ -108,20 +108,12 @@ impl MonitorHandle {
|
||||
/// Returns a human-readable name of the monitor.
|
||||
///
|
||||
/// Returns `None` if the monitor doesn't exist anymore.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Always returns None
|
||||
#[inline]
|
||||
pub fn name(&self) -> Option<String> {
|
||||
self.inner.name()
|
||||
}
|
||||
|
||||
/// Returns the monitor's resolution.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Always returns (0,0)
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.inner.size()
|
||||
@@ -129,10 +121,6 @@ impl MonitorHandle {
|
||||
|
||||
/// Returns the top-left corner position of the monitor relative to the larger full
|
||||
/// screen area.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Always returns (0,0)
|
||||
#[inline]
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
self.inner.position()
|
||||
@@ -158,7 +146,6 @@ impl MonitorHandle {
|
||||
///
|
||||
/// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
|
||||
/// - **Android:** Always returns 1.0.
|
||||
/// - **Web:** Always returns 1.0
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.inner.scale_factor()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
use objc2::rc::Id;
|
||||
|
||||
use crate::{
|
||||
@@ -117,7 +118,7 @@ pub trait WindowBuilderExtIOS {
|
||||
///
|
||||
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
|
||||
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
|
||||
fn with_scale_factor(self, scale_factor: f64) -> WindowBuilder;
|
||||
fn with_scale_factor(self, scale_factor: f64) -> Self;
|
||||
|
||||
/// Sets the valid orientations for the [`Window`].
|
||||
///
|
||||
@@ -125,7 +126,7 @@ pub trait WindowBuilderExtIOS {
|
||||
///
|
||||
/// This sets the initial value returned by
|
||||
/// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc).
|
||||
fn with_valid_orientations(self, valid_orientations: ValidOrientations) -> WindowBuilder;
|
||||
fn with_valid_orientations(self, valid_orientations: ValidOrientations) -> Self;
|
||||
|
||||
/// Sets whether the [`Window`] prefers the home indicator hidden.
|
||||
///
|
||||
@@ -135,7 +136,7 @@ pub trait WindowBuilderExtIOS {
|
||||
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc).
|
||||
///
|
||||
/// This only has an effect on iOS 11.0+.
|
||||
fn with_prefers_home_indicator_hidden(self, hidden: bool) -> WindowBuilder;
|
||||
fn with_prefers_home_indicator_hidden(self, hidden: bool) -> Self;
|
||||
|
||||
/// Sets the screen edges for which the system gestures will take a lower priority than the
|
||||
/// application's touch handling.
|
||||
@@ -144,10 +145,7 @@ pub trait WindowBuilderExtIOS {
|
||||
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc).
|
||||
///
|
||||
/// This only has an effect on iOS 11.0+.
|
||||
fn with_preferred_screen_edges_deferring_system_gestures(
|
||||
self,
|
||||
edges: ScreenEdge,
|
||||
) -> WindowBuilder;
|
||||
fn with_preferred_screen_edges_deferring_system_gestures(self, edges: ScreenEdge) -> Self;
|
||||
|
||||
/// Sets whether the [`Window`] prefers the status bar hidden.
|
||||
///
|
||||
@@ -155,40 +153,37 @@ pub trait WindowBuilderExtIOS {
|
||||
///
|
||||
/// This sets the initial value returned by
|
||||
/// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc).
|
||||
fn with_prefers_status_bar_hidden(self, hidden: bool) -> WindowBuilder;
|
||||
fn with_prefers_status_bar_hidden(self, hidden: bool) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtIOS for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_scale_factor(mut self, scale_factor: f64) -> WindowBuilder {
|
||||
fn with_scale_factor(mut self, scale_factor: f64) -> Self {
|
||||
self.platform_specific.scale_factor = Some(scale_factor);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> WindowBuilder {
|
||||
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self {
|
||||
self.platform_specific.valid_orientations = valid_orientations;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> WindowBuilder {
|
||||
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self {
|
||||
self.platform_specific.prefers_home_indicator_hidden = hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_preferred_screen_edges_deferring_system_gestures(
|
||||
mut self,
|
||||
edges: ScreenEdge,
|
||||
) -> WindowBuilder {
|
||||
fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
|
||||
self.platform_specific
|
||||
.preferred_screen_edges_deferring_system_gestures = edges;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> WindowBuilder {
|
||||
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self {
|
||||
self.platform_specific.prefers_status_bar_hidden = hidden;
|
||||
self
|
||||
}
|
||||
@@ -210,7 +205,9 @@ pub trait MonitorHandleExtIOS {
|
||||
impl MonitorHandleExtIOS for MonitorHandle {
|
||||
#[inline]
|
||||
fn ui_screen(&self) -> *mut c_void {
|
||||
Id::as_ptr(self.inner.ui_screen()) as *mut c_void
|
||||
// SAFETY: The marker is only used to get the pointer of the screen
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -183,92 +183,88 @@ pub enum ActivationPolicy {
|
||||
/// - `with_fullsize_content_view`
|
||||
pub trait WindowBuilderExtMacOS {
|
||||
/// Enables click-and-drag behavior for the entire window, not just the titlebar.
|
||||
fn with_movable_by_window_background(self, movable_by_window_background: bool)
|
||||
-> WindowBuilder;
|
||||
fn with_movable_by_window_background(self, movable_by_window_background: bool) -> Self;
|
||||
/// Makes the titlebar transparent and allows the content to appear behind it.
|
||||
fn with_titlebar_transparent(self, titlebar_transparent: bool) -> WindowBuilder;
|
||||
fn with_titlebar_transparent(self, titlebar_transparent: bool) -> Self;
|
||||
/// Hides the window title.
|
||||
fn with_title_hidden(self, title_hidden: bool) -> WindowBuilder;
|
||||
fn with_title_hidden(self, title_hidden: bool) -> Self;
|
||||
/// Hides the window titlebar.
|
||||
fn with_titlebar_hidden(self, titlebar_hidden: bool) -> WindowBuilder;
|
||||
fn with_titlebar_hidden(self, titlebar_hidden: bool) -> Self;
|
||||
/// Hides the window titlebar buttons.
|
||||
fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> WindowBuilder;
|
||||
fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> Self;
|
||||
/// Makes the window content appear behind the titlebar.
|
||||
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
|
||||
fn with_disallow_hidpi(self, disallow_hidpi: bool) -> WindowBuilder;
|
||||
fn with_has_shadow(self, has_shadow: bool) -> WindowBuilder;
|
||||
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> Self;
|
||||
fn with_disallow_hidpi(self, disallow_hidpi: bool) -> Self;
|
||||
fn with_has_shadow(self, has_shadow: bool) -> Self;
|
||||
/// 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) -> Self;
|
||||
/// Defines the window tabbing identifier.
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
|
||||
fn with_tabbing_identifier(self, identifier: &str) -> WindowBuilder;
|
||||
fn with_tabbing_identifier(self, identifier: &str) -> Self;
|
||||
/// Set how the <kbd>Option</kbd> keys are interpreted.
|
||||
///
|
||||
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
|
||||
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> WindowBuilder;
|
||||
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtMacOS for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_movable_by_window_background(
|
||||
mut self,
|
||||
movable_by_window_background: bool,
|
||||
) -> WindowBuilder {
|
||||
fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self {
|
||||
self.platform_specific.movable_by_window_background = movable_by_window_background;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> WindowBuilder {
|
||||
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self {
|
||||
self.platform_specific.titlebar_transparent = titlebar_transparent;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> WindowBuilder {
|
||||
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self {
|
||||
self.platform_specific.titlebar_hidden = titlebar_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> WindowBuilder {
|
||||
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self {
|
||||
self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_title_hidden(mut self, title_hidden: bool) -> WindowBuilder {
|
||||
fn with_title_hidden(mut self, title_hidden: bool) -> Self {
|
||||
self.platform_specific.title_hidden = title_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> WindowBuilder {
|
||||
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self {
|
||||
self.platform_specific.fullsize_content_view = fullsize_content_view;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> WindowBuilder {
|
||||
fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self {
|
||||
self.platform_specific.disallow_hidpi = disallow_hidpi;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_has_shadow(mut self, has_shadow: bool) -> WindowBuilder {
|
||||
fn with_has_shadow(mut self, has_shadow: bool) -> Self {
|
||||
self.platform_specific.has_shadow = has_shadow;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> WindowBuilder {
|
||||
fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self {
|
||||
self.platform_specific.accepts_first_mouse = accepts_first_mouse;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> WindowBuilder {
|
||||
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
|
||||
self.platform_specific
|
||||
.tabbing_identifier
|
||||
.replace(tabbing_identifier.to_string());
|
||||
@@ -276,7 +272,7 @@ impl WindowBuilderExtMacOS for WindowBuilder {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> WindowBuilder {
|
||||
fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self {
|
||||
self.platform_specific.option_as_alt = option_as_alt;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
//!
|
||||
//! And the following platform-specific modules:
|
||||
//!
|
||||
//! - `run_ondemand` (available on `windows`, `unix`, `macos`, `android`)
|
||||
//! - `run_on_demand` (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.
|
||||
@@ -42,7 +42,7 @@ pub mod x11;
|
||||
x11_platform,
|
||||
wayland_platform
|
||||
))]
|
||||
pub mod run_ondemand;
|
||||
pub mod run_on_demand;
|
||||
|
||||
#[cfg(any(
|
||||
windows_platform,
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::time::Duration;
|
||||
|
||||
use crate::{
|
||||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
};
|
||||
|
||||
/// The return status for `pump_events`
|
||||
@@ -63,7 +63,7 @@ pub trait EventLoopExtPumpEvents {
|
||||
///
|
||||
/// 'main: loop {
|
||||
/// let timeout = Some(Duration::ZERO);
|
||||
/// let status = event_loop.pump_events(timeout, |event, _, control_flow| {
|
||||
/// let status = event_loop.pump_events(timeout, |event, elwt| {
|
||||
/// # if let Event::WindowEvent { event, .. } = &event {
|
||||
/// # // Print only Window events to reduce noise
|
||||
/// # println!("{event:?}");
|
||||
@@ -73,7 +73,7 @@ pub trait EventLoopExtPumpEvents {
|
||||
/// Event::WindowEvent {
|
||||
/// event: WindowEvent::CloseRequested,
|
||||
/// window_id,
|
||||
/// } if window_id == window.id() => control_flow.set_exit(),
|
||||
/// } if window_id == window.id() => elwt.exit(),
|
||||
/// Event::AboutToWait => {
|
||||
/// window.request_redraw();
|
||||
/// }
|
||||
@@ -174,7 +174,7 @@ pub trait EventLoopExtPumpEvents {
|
||||
/// 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);
|
||||
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtPumpEvents for EventLoop<T> {
|
||||
@@ -182,7 +182,7 @@ impl<T> EventLoopExtPumpEvents for EventLoop<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),
|
||||
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
|
||||
{
|
||||
self.event_loop.pump_events(timeout, event_handler)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
error::EventLoopError,
|
||||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
};
|
||||
|
||||
#[cfg(doc)]
|
||||
@@ -17,7 +17,7 @@ pub trait EventLoopExtRunOnDemand {
|
||||
///
|
||||
/// 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
|
||||
/// consuming the `EventLoop` (by using [`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
|
||||
@@ -28,12 +28,11 @@ pub trait EventLoopExtRunOnDemand {
|
||||
/// 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
|
||||
/// Each time `run_on_demand` 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.
|
||||
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
|
||||
///
|
||||
/// # Caveats
|
||||
/// - This extension isn't available on all platforms, since it's not always possible to
|
||||
@@ -57,18 +56,21 @@ pub trait EventLoopExtRunOnDemand {
|
||||
/// 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>
|
||||
///
|
||||
/// [`exit()`]: EventLoopWindowTarget::exit
|
||||
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow
|
||||
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow);
|
||||
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn run_ondemand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow),
|
||||
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
|
||||
{
|
||||
self.event_loop.run_ondemand(event_handler)
|
||||
self.event_loop.run_on_demand(event_handler)
|
||||
}
|
||||
}
|
||||
@@ -85,12 +85,7 @@ impl WindowBuilderExtStartupNotify for WindowBuilder {
|
||||
///
|
||||
/// 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() {
|
||||
pub fn reset_activation_token_env() {
|
||||
env::remove_var(X11_VAR);
|
||||
env::remove_var(WAYLAND_VAR);
|
||||
}
|
||||
@@ -98,12 +93,7 @@ pub unsafe fn reset_activation_token_env() {
|
||||
/// 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) {
|
||||
pub fn set_activation_token_env(token: ActivationToken) {
|
||||
env::set_var(X11_VAR, &token._token);
|
||||
env::set_var(WAYLAND_VAR, token._token);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::ControlFlow;
|
||||
use crate::event_loop::EventLoop;
|
||||
use crate::event_loop::EventLoopWindowTarget;
|
||||
use crate::window::{Window, WindowBuilder};
|
||||
@@ -48,8 +47,8 @@ impl WindowExtWebSys for Window {
|
||||
}
|
||||
|
||||
pub trait WindowBuilderExtWebSys {
|
||||
/// Pass an [`HtmlCanvasElement`] to be used for this [`Window`](crate::window::Window). If
|
||||
/// [`None`], [`WindowBuilder::build()`] will create one.
|
||||
/// Pass an [`HtmlCanvasElement`] to be used for this [`Window`]. If [`None`],
|
||||
/// [`WindowBuilder::build()`] will create one.
|
||||
///
|
||||
/// In any case, the canvas won't be automatically inserted into the web page.
|
||||
///
|
||||
@@ -122,8 +121,7 @@ pub trait EventLoopExtWebSys {
|
||||
/// event loop when switching between tabs on a single page application.
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static
|
||||
+ FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow);
|
||||
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
||||
@@ -131,9 +129,62 @@ impl<T> EventLoopExtWebSys for EventLoop<T> {
|
||||
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static
|
||||
+ FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow),
|
||||
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
|
||||
{
|
||||
self.event_loop.spawn(event_handler)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EventLoopWindowTargetExtWebSys {
|
||||
/// Sets the strategy for [`ControlFlow::Poll`].
|
||||
///
|
||||
/// See [`PollStrategy`].
|
||||
///
|
||||
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
||||
fn set_poll_strategy(&self, strategy: PollStrategy);
|
||||
|
||||
/// Gets the strategy for [`ControlFlow::Poll`].
|
||||
///
|
||||
/// See [`PollStrategy`].
|
||||
///
|
||||
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
||||
fn poll_strategy(&self) -> PollStrategy;
|
||||
}
|
||||
|
||||
impl<T> EventLoopWindowTargetExtWebSys for EventLoopWindowTarget<T> {
|
||||
#[inline]
|
||||
fn set_poll_strategy(&self, strategy: PollStrategy) {
|
||||
self.p.set_poll_strategy(strategy);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_strategy(&self) -> PollStrategy {
|
||||
self.p.poll_strategy()
|
||||
}
|
||||
}
|
||||
|
||||
/// Strategy used for [`ControlFlow::Poll`](crate::event_loop::ControlFlow::Poll).
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum PollStrategy {
|
||||
/// Uses [`Window.requestIdleCallback()`] to queue the next event loop. If not available
|
||||
/// this will fallback to [`setTimeout()`].
|
||||
///
|
||||
/// This strategy will wait for the browser to enter an idle period before running and might
|
||||
/// be affected by browser throttling.
|
||||
///
|
||||
/// [`Window.requestIdleCallback()`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
|
||||
/// [`setTimeout()`]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
|
||||
IdleCallback,
|
||||
/// Uses the [Prioritized Task Scheduling API] to queue the next event loop. If not available
|
||||
/// this will fallback to [`setTimeout()`].
|
||||
///
|
||||
/// This strategy will run as fast as possible without disturbing users from interacting with
|
||||
/// the page and is not affected by browser throttling.
|
||||
///
|
||||
/// This is the default strategy.
|
||||
///
|
||||
/// [Prioritized Task Scheduling API]: https://developer.mozilla.org/en-US/docs/Web/API/Prioritized_Task_Scheduling_API
|
||||
/// [`setTimeout()`]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
|
||||
#[default]
|
||||
Scheduler,
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ impl WindowExtWindows for Window {
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
pub trait WindowBuilderExtWindows {
|
||||
/// Set an owner to the window to be created. Can be used to create a dialog box, for example.
|
||||
/// This only works when [`WindowBuilder::with_parent_window`] isn't called or set to `None`.
|
||||
@@ -173,7 +174,7 @@ pub trait WindowBuilderExtWindows {
|
||||
/// - An owned window is hidden when its owner is minimized.
|
||||
///
|
||||
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
|
||||
fn with_owner_window(self, parent: HWND) -> WindowBuilder;
|
||||
fn with_owner_window(self, parent: HWND) -> Self;
|
||||
|
||||
/// Sets a menu on the window to be created.
|
||||
///
|
||||
@@ -185,13 +186,13 @@ pub trait WindowBuilderExtWindows {
|
||||
/// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect.
|
||||
///
|
||||
/// [`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu
|
||||
fn with_menu(self, menu: HMENU) -> WindowBuilder;
|
||||
fn with_menu(self, menu: HMENU) -> Self;
|
||||
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn with_taskbar_icon(self, taskbar_icon: Option<Icon>) -> WindowBuilder;
|
||||
fn with_taskbar_icon(self, taskbar_icon: Option<Icon>) -> Self;
|
||||
|
||||
/// This sets `WS_EX_NOREDIRECTIONBITMAP`.
|
||||
fn with_no_redirection_bitmap(self, flag: bool) -> WindowBuilder;
|
||||
fn with_no_redirection_bitmap(self, flag: bool) -> Self;
|
||||
|
||||
/// Enables or disables drag and drop support (enabled by default). Will interfere with other crates
|
||||
/// that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` instead of
|
||||
@@ -199,66 +200,66 @@ pub trait WindowBuilderExtWindows {
|
||||
/// COM API regardless of this option. Currently only fullscreen mode does that, but there may be more in the future.
|
||||
/// If you need COM API with `COINIT_MULTITHREADED` you must initialize it before calling any winit functions.
|
||||
/// See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information.
|
||||
fn with_drag_and_drop(self, flag: bool) -> WindowBuilder;
|
||||
fn with_drag_and_drop(self, flag: bool) -> Self;
|
||||
|
||||
/// 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) -> Self;
|
||||
|
||||
/// Customize the window class name.
|
||||
fn with_class_name<S: Into<String>>(self, class_name: S) -> WindowBuilder;
|
||||
fn with_class_name<S: Into<String>>(self, class_name: S) -> Self;
|
||||
|
||||
/// Shows or hides the background drop shadow for undecorated windows.
|
||||
///
|
||||
/// The shadow is hidden by default.
|
||||
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
|
||||
fn with_undecorated_shadow(self, shadow: bool) -> WindowBuilder;
|
||||
fn with_undecorated_shadow(self, shadow: bool) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtWindows for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_owner_window(mut self, parent: HWND) -> WindowBuilder {
|
||||
fn with_owner_window(mut self, parent: HWND) -> Self {
|
||||
self.platform_specific.owner = Some(parent);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_menu(mut self, menu: HMENU) -> WindowBuilder {
|
||||
fn with_menu(mut self, menu: HMENU) -> Self {
|
||||
self.platform_specific.menu = Some(menu);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> WindowBuilder {
|
||||
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self {
|
||||
self.platform_specific.taskbar_icon = taskbar_icon;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_no_redirection_bitmap(mut self, flag: bool) -> WindowBuilder {
|
||||
fn with_no_redirection_bitmap(mut self, flag: bool) -> Self {
|
||||
self.platform_specific.no_redirection_bitmap = flag;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_drag_and_drop(mut self, flag: bool) -> WindowBuilder {
|
||||
fn with_drag_and_drop(mut self, flag: bool) -> Self {
|
||||
self.platform_specific.drag_and_drop = flag;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_skip_taskbar(mut self, skip: bool) -> WindowBuilder {
|
||||
fn with_skip_taskbar(mut self, skip: bool) -> Self {
|
||||
self.platform_specific.skip_taskbar = skip;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_class_name<S: Into<String>>(mut self, class_name: S) -> WindowBuilder {
|
||||
fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self {
|
||||
self.platform_specific.class_name = class_name.into();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_undecorated_shadow(mut self, shadow: bool) -> WindowBuilder {
|
||||
fn with_undecorated_shadow(mut self, shadow: bool) -> Self {
|
||||
self.platform_specific.decoration_shadow = shadow;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -226,9 +226,7 @@ pub fn to_logical(key_char: Option<KeyMapChar>, keycode: Keycode) -> Key {
|
||||
let native = NativeKey::Android(keycode.into());
|
||||
|
||||
match key_char {
|
||||
Some(KeyMapChar::Unicode(c)) => {
|
||||
Key::Character(smol_str::SmolStr::from_iter([c].into_iter()))
|
||||
}
|
||||
Some(KeyMapChar::Unicode(c)) => Key::Character(smol_str::SmolStr::from_iter([c])),
|
||||
Some(KeyMapChar::CombiningAccent(c)) => Key::Dead(Some(c)),
|
||||
None | Some(KeyMapChar::None) => match keycode {
|
||||
// Using `BrowserHome` instead of `GoHome` according to
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![cfg(android_platform)]
|
||||
|
||||
use std::{
|
||||
cell::Cell,
|
||||
collections::VecDeque,
|
||||
hash::Hash,
|
||||
sync::{
|
||||
@@ -15,15 +16,12 @@ use android_activity::{
|
||||
AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use raw_window_handle::{
|
||||
AndroidDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
error,
|
||||
event::{self, InnerSizeWriter, StartCause},
|
||||
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
|
||||
event::{self, Force, InnerSizeWriter, StartCause},
|
||||
event_loop::{self, ControlFlow, DeviceEvents, EventLoopWindowTarget as RootELW},
|
||||
platform::pump_events::PumpStatus,
|
||||
window::{
|
||||
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
|
||||
@@ -147,7 +145,6 @@ pub struct EventLoop<T: 'static> {
|
||||
loop_running: bool, // Dispatched `NewEvents<Init>`
|
||||
running: bool,
|
||||
pending_redraw: bool,
|
||||
control_flow: ControlFlow,
|
||||
cause: StartCause,
|
||||
ignore_volume_keys: bool,
|
||||
combining_accent: Option<char>,
|
||||
@@ -168,23 +165,6 @@ impl Default for PlatformSpecificEventLoopAttributes {
|
||||
}
|
||||
}
|
||||
|
||||
fn sticky_exit_callback<T, F>(
|
||||
evt: event::Event<T>,
|
||||
target: &RootELW<T>,
|
||||
control_flow: &mut ControlFlow,
|
||||
callback: &mut F,
|
||||
) where
|
||||
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
{
|
||||
// make ControlFlow::ExitWithCode sticky by providing a dummy
|
||||
// control flow reference if it is already ExitWithCode.
|
||||
if let ControlFlow::ExitWithCode(code) = *control_flow {
|
||||
callback(evt, target, &mut ControlFlow::ExitWithCode(code))
|
||||
} else {
|
||||
callback(evt, target, control_flow)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub(crate) fn new(
|
||||
attributes: &PlatformSpecificEventLoopAttributes,
|
||||
@@ -199,6 +179,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
window_target: event_loop::EventLoopWindowTarget {
|
||||
p: EventLoopWindowTarget {
|
||||
app: android_app.clone(),
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
exit: Cell::new(false),
|
||||
redraw_requester: RedrawRequester::new(
|
||||
&redraw_flag,
|
||||
android_app.create_waker(),
|
||||
@@ -213,7 +195,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
loop_running: false,
|
||||
running: false,
|
||||
pending_redraw: false,
|
||||
control_flow: Default::default(),
|
||||
cause: StartCause::Init,
|
||||
ignore_volume_keys: attributes.ignore_volume_keys,
|
||||
combining_accent: None,
|
||||
@@ -222,41 +203,25 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(event::Event<T>, &RootELW<T>),
|
||||
{
|
||||
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(
|
||||
event::Event::NewEvents(cause),
|
||||
self.window_target(),
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(event::Event::NewEvents(cause), self.window_target());
|
||||
|
||||
if let Some(event) = main_event {
|
||||
trace!("Handling main event {:?}", event);
|
||||
|
||||
match event {
|
||||
MainEvent::InitWindow { .. } => {
|
||||
sticky_exit_callback(
|
||||
event::Event::Resumed,
|
||||
self.window_target(),
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(event::Event::Resumed, self.window_target());
|
||||
}
|
||||
MainEvent::TerminateWindow { .. } => {
|
||||
sticky_exit_callback(
|
||||
event::Event::Suspended,
|
||||
self.window_target(),
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(event::Event::Suspended, self.window_target());
|
||||
}
|
||||
MainEvent::WindowResized { .. } => resized = true,
|
||||
MainEvent::RedrawNeeded { .. } => pending_redraw = true,
|
||||
@@ -265,26 +230,22 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
MainEvent::GainedFocus => {
|
||||
*HAS_FOCUS.write().unwrap() = true;
|
||||
sticky_exit_callback(
|
||||
callback(
|
||||
event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Focused(true),
|
||||
},
|
||||
self.window_target(),
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
MainEvent::LostFocus => {
|
||||
*HAS_FOCUS.write().unwrap() = false;
|
||||
sticky_exit_callback(
|
||||
callback(
|
||||
event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Focused(false),
|
||||
},
|
||||
self.window_target(),
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
MainEvent::ConfigChanged { .. } => {
|
||||
@@ -304,19 +265,11 @@ impl<T: 'static> EventLoop<T> {
|
||||
scale_factor,
|
||||
},
|
||||
};
|
||||
sticky_exit_callback(
|
||||
event,
|
||||
self.window_target(),
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(event, self.window_target());
|
||||
}
|
||||
}
|
||||
MainEvent::LowMemory => {
|
||||
// XXX: how to forward this state to applications?
|
||||
// It seems like ideally winit should support lifecycle and
|
||||
// low-memory events, especially for mobile platforms.
|
||||
warn!("TODO: handle Android LowMemory notification");
|
||||
callback(event::Event::MemoryWarning, self.window_target());
|
||||
}
|
||||
MainEvent::Start => {
|
||||
// XXX: how to forward this state to applications?
|
||||
@@ -363,9 +316,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
// Process input events
|
||||
match android_app.input_events_iter() {
|
||||
Ok(mut input_iter) => loop {
|
||||
let read_event = input_iter.next(|event| {
|
||||
self.handle_input_event(&android_app, event, &mut control_flow, callback)
|
||||
});
|
||||
let read_event =
|
||||
input_iter.next(|event| self.handle_input_event(&android_app, event, callback));
|
||||
|
||||
if !read_event {
|
||||
break;
|
||||
@@ -379,12 +331,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
// Empty the user event buffer
|
||||
{
|
||||
while let Ok(event) = self.user_events_receiver.try_recv() {
|
||||
sticky_exit_callback(
|
||||
crate::event::Event::UserEvent(event),
|
||||
self.window_target(),
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(crate::event::Event::UserEvent(event), self.window_target());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,26 +348,23 @@ impl<T: 'static> EventLoop<T> {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Resized(size),
|
||||
};
|
||||
sticky_exit_callback(event, self.window_target(), &mut control_flow, callback);
|
||||
callback(event, self.window_target());
|
||||
}
|
||||
|
||||
pending_redraw |= self.redraw_flag.get_and_reset();
|
||||
if pending_redraw {
|
||||
pending_redraw = false;
|
||||
let event = event::Event::RedrawRequested(window::WindowId(WindowId));
|
||||
sticky_exit_callback(event, self.window_target(), &mut control_flow, callback);
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::RedrawRequested,
|
||||
};
|
||||
callback(event, self.window_target());
|
||||
}
|
||||
}
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
sticky_exit_callback(
|
||||
event::Event::AboutToWait,
|
||||
self.window_target(),
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(event::Event::AboutToWait, self.window_target());
|
||||
|
||||
self.control_flow = control_flow;
|
||||
self.pending_redraw = pending_redraw;
|
||||
}
|
||||
|
||||
@@ -428,17 +372,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
&mut self,
|
||||
android_app: &AndroidApp,
|
||||
event: &InputEvent<'_>,
|
||||
control_flow: &mut ControlFlow,
|
||||
callback: &mut F,
|
||||
) -> InputStatus
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(event::Event<T>, &RootELW<T>),
|
||||
{
|
||||
let mut input_status = InputStatus::Handled;
|
||||
match event {
|
||||
InputEvent::MotionEvent(motion_event) => {
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let device_id = event::DeviceId(DeviceId);
|
||||
let device_id = event::DeviceId(DeviceId(motion_event.device_id()));
|
||||
|
||||
let phase = match motion_event.action() {
|
||||
MotionAction::Down | MotionAction::PointerDown => {
|
||||
@@ -477,10 +420,10 @@ impl<T: 'static> EventLoop<T> {
|
||||
phase,
|
||||
location,
|
||||
id: pointer.pointer_id() as u64,
|
||||
force: None,
|
||||
force: Some(Force::Normalized(pointer.pressure() as f64)),
|
||||
}),
|
||||
};
|
||||
sticky_exit_callback(event, self.window_target(), control_flow, callback);
|
||||
callback(event, self.window_target());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -510,7 +453,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
device_id: event::DeviceId(DeviceId(key.device_id())),
|
||||
event: event::KeyEvent {
|
||||
state,
|
||||
physical_key: keycodes::to_physical_keycode(keycode),
|
||||
@@ -523,7 +466,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
is_synthetic: false,
|
||||
},
|
||||
};
|
||||
sticky_exit_callback(event, self.window_target(), control_flow, callback);
|
||||
callback(event, self.window_target());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -537,14 +480,14 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>),
|
||||
{
|
||||
self.run_ondemand(event_handler)
|
||||
self.run_on_demand(event_handler)
|
||||
}
|
||||
|
||||
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>),
|
||||
{
|
||||
if self.loop_running {
|
||||
return Err(EventLoopError::AlreadyRunning);
|
||||
@@ -567,7 +510,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(event::Event<T>, &RootELW<T>),
|
||||
{
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
@@ -577,7 +520,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
// than once
|
||||
self.pending_redraw = false;
|
||||
self.cause = StartCause::Init;
|
||||
self.control_flow = ControlFlow::Poll;
|
||||
|
||||
// run the initial loop iteration
|
||||
self.single_iteration(None, &mut callback);
|
||||
@@ -585,21 +527,15 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit
|
||||
if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) {
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
}
|
||||
if let ControlFlow::ExitWithCode(code) = self.control_flow {
|
||||
if self.exiting() {
|
||||
self.loop_running = false;
|
||||
|
||||
let mut dummy = self.control_flow;
|
||||
sticky_exit_callback(
|
||||
event::Event::LoopExiting,
|
||||
self.window_target(),
|
||||
&mut dummy,
|
||||
&mut callback,
|
||||
);
|
||||
callback(event::Event::LoopExiting, self.window_target());
|
||||
|
||||
PumpStatus::Exit(code)
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
PumpStatus::Continue
|
||||
}
|
||||
@@ -607,7 +543,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
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),
|
||||
F: FnMut(event::Event<T>, &RootELW<T>),
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
||||
@@ -618,14 +554,12 @@ impl<T: 'static> EventLoop<T> {
|
||||
// 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 {
|
||||
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)
|
||||
@@ -660,7 +594,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
self.cause = match self.control_flow {
|
||||
self.cause = match self.control_flow() {
|
||||
ControlFlow::Poll => StartCause::Poll,
|
||||
ControlFlow::Wait => StartCause::WaitCancelled {
|
||||
start,
|
||||
@@ -679,8 +613,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// `ExitWithCode()` will be reset to `Poll` before polling
|
||||
ControlFlow::ExitWithCode(_code) => unreachable!(),
|
||||
};
|
||||
|
||||
self.single_iteration(main_event, &mut callback);
|
||||
@@ -697,6 +629,14 @@ impl<T: 'static> EventLoop<T> {
|
||||
waker: self.android_app.create_waker(),
|
||||
}
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
self.window_target.p.control_flow()
|
||||
}
|
||||
|
||||
fn exiting(&self) -> bool {
|
||||
self.window_target.p.exiting()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
@@ -725,6 +665,8 @@ impl<T> EventLoopProxy<T> {
|
||||
|
||||
pub struct EventLoopWindowTarget<T: 'static> {
|
||||
app: AndroidApp,
|
||||
control_flow: Cell<ControlFlow>,
|
||||
exit: Cell<bool>,
|
||||
redraw_requester: RedrawRequester,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
@@ -740,8 +682,39 @@ impl<T: 'static> EventLoopWindowTarget<T> {
|
||||
v
|
||||
}
|
||||
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
RawDisplayHandle::Android(AndroidDisplayHandle::empty())
|
||||
#[inline]
|
||||
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::RawDisplayHandle::Android(rwh_05::AndroidDisplayHandle::empty())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::Android(
|
||||
rwh_06::AndroidDisplayHandle::new(),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.control_flow.set(control_flow)
|
||||
}
|
||||
|
||||
pub(crate) fn control_flow(&self) -> ControlFlow {
|
||||
self.control_flow.get()
|
||||
}
|
||||
|
||||
pub(crate) fn exit(&self) {
|
||||
self.exit.set(true)
|
||||
}
|
||||
|
||||
pub(crate) fn exiting(&self) -> bool {
|
||||
self.exit.get()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,11 +740,11 @@ impl From<u64> for WindowId {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct DeviceId;
|
||||
pub struct DeviceId(i32);
|
||||
|
||||
impl DeviceId {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId
|
||||
DeviceId(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,6 +844,8 @@ impl Window {
|
||||
|
||||
pub fn set_transparent(&self, _transparent: bool) {}
|
||||
|
||||
pub fn set_blur(&self, _blur: bool) {}
|
||||
|
||||
pub fn set_visible(&self, _visibility: bool) {}
|
||||
|
||||
pub fn is_visible(&self) -> Option<bool> {
|
||||
@@ -960,13 +935,30 @@ impl Window {
|
||||
))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(
|
||||
error::NotSupportedError::new(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
#[cfg(feature = "rwh_04")]
|
||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||
if let Some(native_window) = self.app.native_window().as_ref() {
|
||||
let mut handle = rwh_04::AndroidNdkHandle::empty();
|
||||
handle.a_native_window = native_window.ptr().as_ptr() as *mut _;
|
||||
rwh_04::RawWindowHandle::AndroidNdk(handle)
|
||||
} else {
|
||||
panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
||||
use rwh_05::HasRawWindowHandle;
|
||||
|
||||
if let Some(native_window) = self.app.native_window().as_ref() {
|
||||
native_window.raw_window_handle()
|
||||
} else {
|
||||
@@ -974,8 +966,29 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
RawDisplayHandle::Android(AndroidDisplayHandle::empty())
|
||||
#[cfg(feature = "rwh_05")]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::RawDisplayHandle::Android(rwh_05::AndroidDisplayHandle::empty())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
if let Some(native_window) = self.app.native_window().as_ref() {
|
||||
let handle = rwh_06::AndroidNdkWindowHandle::new(native_window.ptr().cast());
|
||||
Ok(rwh_06::RawWindowHandle::AndroidNdk(handle))
|
||||
} else {
|
||||
log::error!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
|
||||
Err(rwh_06::HandleError::Unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::Android(
|
||||
rwh_06::AndroidDisplayHandle::new(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn config(&self) -> ConfigurationRef {
|
||||
@@ -992,6 +1005,8 @@ impl Window {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
pub fn has_focus(&self) -> bool {
|
||||
*HAS_FOCUS.read().unwrap()
|
||||
}
|
||||
|
||||
@@ -16,19 +16,21 @@ use core_foundation::runloop::{
|
||||
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
||||
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
||||
};
|
||||
use icrate::Foundation::{CGRect, CGSize, NSInteger, NSOperatingSystemVersion, NSProcessInfo};
|
||||
use icrate::Foundation::{
|
||||
CGRect, CGSize, MainThreadMarker, NSInteger, NSOperatingSystemVersion, NSProcessInfo,
|
||||
};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{msg_send, sel};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::event_loop::{EventHandler, Never};
|
||||
use super::uikit::UIView;
|
||||
use super::view::WinitUIWindow;
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
||||
event_loop::ControlFlow,
|
||||
platform_impl::platform::event_loop::{EventHandler, EventProxy, EventWrapper, Never},
|
||||
window::WindowId as RootWindowId,
|
||||
};
|
||||
|
||||
@@ -44,6 +46,19 @@ macro_rules! bug_assert {
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventWrapper {
|
||||
StaticEvent(Event<Never>),
|
||||
ScaleFactorChanged(ScaleFactorChanged),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScaleFactorChanged {
|
||||
pub(super) window: Id<WinitUIWindow>,
|
||||
pub(super) suggested_size: PhysicalSize<u32>,
|
||||
pub(super) scale_factor: f64,
|
||||
}
|
||||
|
||||
enum UserCallbackTransitionResult<'a> {
|
||||
Success {
|
||||
event_handler: Box<dyn EventHandler>,
|
||||
@@ -57,7 +72,13 @@ enum UserCallbackTransitionResult<'a> {
|
||||
|
||||
impl Event<Never> {
|
||||
fn is_redraw(&self) -> bool {
|
||||
matches!(self, Event::RedrawRequested(_))
|
||||
matches!(
|
||||
self,
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +121,7 @@ enum AppStateImpl {
|
||||
Terminated,
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
pub(crate) struct AppState {
|
||||
// This should never be `None`, except for briefly during a state transition.
|
||||
app_state: Option<AppStateImpl>,
|
||||
control_flow: ControlFlow,
|
||||
@@ -108,24 +129,18 @@ struct AppState {
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
// requires main thread
|
||||
unsafe fn get_mut() -> RefMut<'static, AppState> {
|
||||
pub(crate) fn get_mut(_mtm: MainThreadMarker) -> RefMut<'static, AppState> {
|
||||
// basically everything in UIKit requires the main thread, so it's pointless to use the
|
||||
// std::sync APIs.
|
||||
// must be mut because plain `static` requires `Sync`
|
||||
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
assert_main_thread!(
|
||||
"bug in winit: `AppState::get_mut()` can only be called on the main thread"
|
||||
);
|
||||
}
|
||||
let mut guard = APP_STATE.borrow_mut();
|
||||
let mut guard = unsafe { APP_STATE.borrow_mut() };
|
||||
if guard.is_none() {
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
unsafe fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
|
||||
let waker = EventLoopWaker::new(CFRunLoopGetMain());
|
||||
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
|
||||
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() });
|
||||
**guard = Some(AppState {
|
||||
app_state: Some(AppStateImpl::NotLaunched {
|
||||
queued_windows: Vec::new(),
|
||||
@@ -136,7 +151,7 @@ impl AppState {
|
||||
waker,
|
||||
});
|
||||
}
|
||||
init_guard(&mut guard)
|
||||
init_guard(&mut guard);
|
||||
}
|
||||
RefMut::map(guard, |state| state.as_mut().unwrap())
|
||||
}
|
||||
@@ -219,7 +234,7 @@ impl AppState {
|
||||
};
|
||||
self.set_state(AppStateImpl::ProcessingEvents {
|
||||
event_handler,
|
||||
active_control_flow: ControlFlow::Poll,
|
||||
active_control_flow: self.control_flow,
|
||||
queued_gpu_redraws,
|
||||
});
|
||||
(windows, events)
|
||||
@@ -275,7 +290,6 @@ impl AppState {
|
||||
};
|
||||
(waiting_event_handler, event)
|
||||
}
|
||||
(ControlFlow::ExitWithCode(_), _) => bug!("unexpected `ControlFlow` `Exit`"),
|
||||
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
|
||||
};
|
||||
|
||||
@@ -428,12 +442,6 @@ impl AppState {
|
||||
});
|
||||
self.waker.start()
|
||||
}
|
||||
(_, ControlFlow::ExitWithCode(_)) => {
|
||||
// https://developer.apple.com/library/archive/qa/qa1561/_index.html
|
||||
// it is not possible to quit an iOS app gracefully and programatically
|
||||
warn!("`ControlFlow::Exit` ignored on iOS");
|
||||
self.control_flow = old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,12 +451,18 @@ impl AppState {
|
||||
s => bug!("`LoopExiting` happened while not processing events {:?}", s),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) {
|
||||
self.control_flow = control_flow;
|
||||
}
|
||||
|
||||
pub(crate) fn control_flow(&self) -> ControlFlow {
|
||||
self.control_flow
|
||||
}
|
||||
}
|
||||
|
||||
// requires main thread and window is a UIWindow
|
||||
// retains window
|
||||
pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow>) {
|
||||
let mut this = AppState::get_mut();
|
||||
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id<WinitUIWindow>) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::NotLaunched {
|
||||
ref mut queued_windows,
|
||||
@@ -468,10 +482,8 @@ pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow>) {
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
// requires main thread and window is a UIWindow
|
||||
// retains window
|
||||
pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id<WinitUIWindow>) {
|
||||
let mut this = AppState::get_mut();
|
||||
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUIWindow>) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::NotLaunched {
|
||||
ref mut queued_gpu_redraws,
|
||||
@@ -500,24 +512,17 @@ pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id<WinitUIWindow>) {
|
||||
}
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
pub unsafe fn will_launch(queued_event_handler: Box<dyn EventHandler>) {
|
||||
AppState::get_mut().will_launch_transition(queued_event_handler)
|
||||
pub fn will_launch(mtm: MainThreadMarker, queued_event_handler: Box<dyn EventHandler>) {
|
||||
AppState::get_mut(mtm).will_launch_transition(queued_event_handler)
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
pub unsafe fn did_finish_launching() {
|
||||
let mut this = AppState::get_mut();
|
||||
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let windows = match this.state_mut() {
|
||||
AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows),
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
|
||||
// start waking up the event loop now!
|
||||
bug_assert!(
|
||||
this.control_flow == ControlFlow::Poll,
|
||||
"unexpectedly not setup to `Poll` on launch!"
|
||||
);
|
||||
this.waker.start();
|
||||
|
||||
// have to drop RefMut because the window setup code below can trigger new events
|
||||
@@ -535,7 +540,7 @@ pub unsafe fn did_finish_launching() {
|
||||
// completed. This may result in incorrect visual appearance.
|
||||
// ```
|
||||
let screen = window.screen();
|
||||
let _: () = msg_send![&window, setScreen: ptr::null::<AnyObject>()];
|
||||
let _: () = unsafe { msg_send![&window, setScreen: ptr::null::<AnyObject>()] };
|
||||
window.setScreen(&screen);
|
||||
|
||||
let controller = window.rootViewController();
|
||||
@@ -545,13 +550,13 @@ pub unsafe fn did_finish_launching() {
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
let (windows, events) = AppState::get_mut().did_finish_launching_transition();
|
||||
let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition();
|
||||
|
||||
let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents(
|
||||
StartCause::Init,
|
||||
)))
|
||||
.chain(events);
|
||||
handle_nonuser_events(events);
|
||||
handle_nonuser_events(mtm, events);
|
||||
|
||||
// the above window dance hack, could possibly trigger new windows to be created.
|
||||
// we can just set those windows up normally, as they were created after didFinishLaunching
|
||||
@@ -560,27 +565,27 @@ pub unsafe fn did_finish_launching() {
|
||||
}
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
// AppState::did_finish_launching handles the special transition `Init`
|
||||
pub unsafe fn handle_wakeup_transition() {
|
||||
let mut this = AppState::get_mut();
|
||||
pub fn handle_wakeup_transition(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let wakeup_event = match this.wakeup_transition() {
|
||||
None => return,
|
||||
Some(wakeup_event) => wakeup_event,
|
||||
};
|
||||
drop(this);
|
||||
|
||||
handle_nonuser_event(wakeup_event)
|
||||
handle_nonuser_event(mtm, wakeup_event)
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
pub(crate) unsafe fn handle_nonuser_event(event: EventWrapper) {
|
||||
handle_nonuser_events(std::iter::once(event))
|
||||
pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) {
|
||||
handle_nonuser_events(mtm, std::iter::once(event))
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(events: I) {
|
||||
let mut this = AppState::get_mut();
|
||||
pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
mtm: MainThreadMarker,
|
||||
events: I,
|
||||
) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let (mut event_handler, active_control_flow, processing_redraws) =
|
||||
match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
||||
@@ -593,7 +598,6 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
|
||||
processing_redraws,
|
||||
} => (event_handler, active_control_flow, processing_redraws),
|
||||
};
|
||||
let mut control_flow = this.control_flow;
|
||||
drop(this);
|
||||
|
||||
for wrapper in events {
|
||||
@@ -607,16 +611,16 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
|
||||
event
|
||||
);
|
||||
}
|
||||
event_handler.handle_nonuser_event(event, &mut control_flow)
|
||||
event_handler.handle_nonuser_event(event)
|
||||
}
|
||||
EventWrapper::EventProxy(proxy) => {
|
||||
handle_event_proxy(&mut event_handler, control_flow, proxy)
|
||||
EventWrapper::ScaleFactorChanged(event) => {
|
||||
handle_hidpi_proxy(&mut event_handler, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut this = AppState::get_mut();
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let queued_events = match this.state_mut() {
|
||||
&mut AppStateImpl::InUserCallback {
|
||||
ref mut queued_events,
|
||||
@@ -648,7 +652,6 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
|
||||
active_control_flow,
|
||||
}
|
||||
});
|
||||
this.control_flow = control_flow;
|
||||
break;
|
||||
}
|
||||
drop(this);
|
||||
@@ -664,20 +667,18 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
|
||||
event
|
||||
);
|
||||
}
|
||||
event_handler.handle_nonuser_event(event, &mut control_flow)
|
||||
event_handler.handle_nonuser_event(event)
|
||||
}
|
||||
EventWrapper::EventProxy(proxy) => {
|
||||
handle_event_proxy(&mut event_handler, control_flow, proxy)
|
||||
EventWrapper::ScaleFactorChanged(event) => {
|
||||
handle_hidpi_proxy(&mut event_handler, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
unsafe fn handle_user_events() {
|
||||
let mut this = AppState::get_mut();
|
||||
let mut control_flow = this.control_flow;
|
||||
fn handle_user_events(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let (mut event_handler, active_control_flow, processing_redraws) =
|
||||
match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
|
||||
@@ -694,10 +695,10 @@ unsafe fn handle_user_events() {
|
||||
}
|
||||
drop(this);
|
||||
|
||||
event_handler.handle_user_events(&mut control_flow);
|
||||
event_handler.handle_user_events();
|
||||
|
||||
loop {
|
||||
let mut this = AppState::get_mut();
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let queued_events = match this.state_mut() {
|
||||
&mut AppStateImpl::InUserCallback {
|
||||
ref mut queued_events,
|
||||
@@ -718,28 +719,24 @@ unsafe fn handle_user_events() {
|
||||
queued_gpu_redraws,
|
||||
active_control_flow,
|
||||
});
|
||||
this.control_flow = control_flow;
|
||||
break;
|
||||
}
|
||||
drop(this);
|
||||
|
||||
for wrapper in queued_events {
|
||||
match wrapper {
|
||||
EventWrapper::StaticEvent(event) => {
|
||||
event_handler.handle_nonuser_event(event, &mut control_flow)
|
||||
}
|
||||
EventWrapper::EventProxy(proxy) => {
|
||||
handle_event_proxy(&mut event_handler, control_flow, proxy)
|
||||
EventWrapper::StaticEvent(event) => event_handler.handle_nonuser_event(event),
|
||||
EventWrapper::ScaleFactorChanged(event) => {
|
||||
handle_hidpi_proxy(&mut event_handler, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
event_handler.handle_user_events(&mut control_flow);
|
||||
event_handler.handle_user_events();
|
||||
}
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
pub unsafe fn handle_main_events_cleared() {
|
||||
let mut this = AppState::get_mut();
|
||||
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
if !this.has_launched() {
|
||||
return;
|
||||
}
|
||||
@@ -749,63 +746,44 @@ pub unsafe fn handle_main_events_cleared() {
|
||||
};
|
||||
drop(this);
|
||||
|
||||
handle_user_events();
|
||||
handle_user_events(mtm);
|
||||
|
||||
let mut this = AppState::get_mut();
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let redraw_events: Vec<EventWrapper> = this
|
||||
.main_events_cleared_transition()
|
||||
.into_iter()
|
||||
.map(|window| EventWrapper::StaticEvent(Event::RedrawRequested(RootWindowId(window.id()))))
|
||||
.map(|window| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
drop(this);
|
||||
|
||||
handle_nonuser_events(redraw_events);
|
||||
handle_nonuser_event(EventWrapper::StaticEvent(Event::AboutToWait));
|
||||
handle_nonuser_events(mtm, redraw_events);
|
||||
handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::AboutToWait));
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
pub unsafe fn handle_events_cleared() {
|
||||
AppState::get_mut().events_cleared_transition();
|
||||
pub fn handle_events_cleared(mtm: MainThreadMarker) {
|
||||
AppState::get_mut(mtm).events_cleared_transition();
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
pub unsafe fn terminated() {
|
||||
let mut this = AppState::get_mut();
|
||||
pub fn terminated(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let mut event_handler = this.terminated_transition();
|
||||
let mut control_flow = this.control_flow;
|
||||
drop(this);
|
||||
|
||||
event_handler.handle_nonuser_event(Event::LoopExiting, &mut control_flow)
|
||||
event_handler.handle_nonuser_event(Event::LoopExiting)
|
||||
}
|
||||
|
||||
fn handle_event_proxy(
|
||||
event_handler: &mut Box<dyn EventHandler>,
|
||||
control_flow: ControlFlow,
|
||||
proxy: EventProxy,
|
||||
) {
|
||||
match proxy {
|
||||
EventProxy::DpiChangedProxy {
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
window,
|
||||
} => handle_hidpi_proxy(
|
||||
event_handler,
|
||||
control_flow,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
window,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_hidpi_proxy(
|
||||
event_handler: &mut Box<dyn EventHandler>,
|
||||
mut control_flow: ControlFlow,
|
||||
suggested_size: LogicalSize<f64>,
|
||||
scale_factor: f64,
|
||||
window: Id<WinitUIWindow>,
|
||||
) {
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size.to_physical(scale_factor)));
|
||||
fn handle_hidpi_proxy(event_handler: &mut Box<dyn EventHandler>, event: ScaleFactorChanged) {
|
||||
let ScaleFactorChanged {
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
window,
|
||||
} = event;
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
let event = Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
@@ -813,7 +791,7 @@ fn handle_hidpi_proxy(
|
||||
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);
|
||||
let (view, screen_frame) = get_view_and_screen_frame(&window);
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
|
||||
@@ -15,61 +15,81 @@ use core_foundation::runloop::{
|
||||
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use icrate::Foundation::{MainThreadMarker, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::ClassType;
|
||||
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
error::EventLoopError,
|
||||
event::Event,
|
||||
event_loop::{
|
||||
ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget,
|
||||
ControlFlow, DeviceEvents, EventLoopClosed,
|
||||
EventLoopWindowTarget as RootEventLoopWindowTarget,
|
||||
},
|
||||
platform::ios::Idiom,
|
||||
};
|
||||
|
||||
use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen};
|
||||
use super::view::WinitUIWindow;
|
||||
use super::{app_state, monitor, view, MonitorHandle};
|
||||
use super::{
|
||||
app_state::AppState,
|
||||
uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum EventWrapper {
|
||||
StaticEvent(Event<Never>),
|
||||
EventProxy(EventProxy),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) enum EventProxy {
|
||||
DpiChangedProxy {
|
||||
window: Id<WinitUIWindow>,
|
||||
suggested_size: LogicalSize<f64>,
|
||||
scale_factor: f64,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct EventLoopWindowTarget<T: 'static> {
|
||||
receiver: Receiver<T>,
|
||||
sender_to_clone: Sender<T>,
|
||||
pub(super) mtm: MainThreadMarker,
|
||||
p: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopWindowTarget<T> {
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
monitor::uiscreens(MainThreadMarker::new().unwrap())
|
||||
monitor::uiscreens(self.mtm)
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(MonitorHandle::new(UIScreen::main(
|
||||
MainThreadMarker::new().unwrap(),
|
||||
)))
|
||||
Some(MonitorHandle::new(UIScreen::main(self.mtm)))
|
||||
}
|
||||
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
RawDisplayHandle::UiKit(UiKitDisplayHandle::empty())
|
||||
#[inline]
|
||||
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::RawDisplayHandle::UiKit(rwh_05::UiKitDisplayHandle::empty())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::UiKit(
|
||||
rwh_06::UiKitDisplayHandle::new(),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
AppState::get_mut(self.mtm).set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
pub(crate) fn control_flow(&self) -> ControlFlow {
|
||||
AppState::get_mut(self.mtm).control_flow()
|
||||
}
|
||||
|
||||
pub(crate) fn exit(&self) {
|
||||
// https://developer.apple.com/library/archive/qa/qa1561/_index.html
|
||||
// it is not possible to quit an iOS app gracefully and programatically
|
||||
warn!("`ControlFlow::Exit` ignored on iOS");
|
||||
}
|
||||
|
||||
pub(crate) fn exiting(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
mtm: MainThreadMarker,
|
||||
sender: Sender<T>,
|
||||
receiver: Receiver<T>,
|
||||
window_target: RootEventLoopWindowTarget<T>,
|
||||
}
|
||||
|
||||
@@ -80,7 +100,8 @@ impl<T: 'static> 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");
|
||||
let mtm = MainThreadMarker::new()
|
||||
.expect("On iOS, `EventLoop` must be created on the main thread");
|
||||
|
||||
static mut SINGLETON_INIT: bool = false;
|
||||
unsafe {
|
||||
@@ -92,16 +113,19 @@ impl<T: 'static> EventLoop<T> {
|
||||
SINGLETON_INIT = true;
|
||||
}
|
||||
|
||||
let (sender_to_clone, receiver) = mpsc::channel();
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
||||
// this line sets up the main run loop before `UIApplicationMain`
|
||||
setup_control_flow_observers();
|
||||
|
||||
Ok(EventLoop {
|
||||
mtm,
|
||||
sender,
|
||||
receiver,
|
||||
window_target: RootEventLoopWindowTarget {
|
||||
p: EventLoopWindowTarget {
|
||||
receiver,
|
||||
sender_to_clone,
|
||||
mtm,
|
||||
p: PhantomData,
|
||||
},
|
||||
_marker: PhantomData,
|
||||
},
|
||||
@@ -110,10 +134,10 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
pub fn run<F>(self, event_handler: F) -> !
|
||||
where
|
||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
|
||||
{
|
||||
unsafe {
|
||||
let application = UIApplication::shared(MainThreadMarker::new().unwrap());
|
||||
let application = UIApplication::shared(self.mtm);
|
||||
assert!(
|
||||
application.is_none(),
|
||||
"\
|
||||
@@ -122,16 +146,17 @@ impl<T: 'static> EventLoop<T> {
|
||||
);
|
||||
|
||||
let event_handler = std::mem::transmute::<
|
||||
Box<dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow)>,
|
||||
Box<dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>)>,
|
||||
Box<EventHandlerCallback<T>>,
|
||||
>(Box::new(event_handler));
|
||||
|
||||
let handler = EventLoopHandler {
|
||||
f: event_handler,
|
||||
receiver: self.receiver,
|
||||
event_loop: self.window_target,
|
||||
};
|
||||
|
||||
app_state::will_launch(Box::new(handler));
|
||||
app_state::will_launch(self.mtm, Box::new(handler));
|
||||
|
||||
// Ensure application delegate is initialized
|
||||
view::WinitApplicationDelegate::class();
|
||||
@@ -147,7 +172,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.window_target.p.sender_to_clone.clone())
|
||||
EventLoopProxy::new(self.sender.clone())
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
|
||||
@@ -158,9 +183,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
// EventLoopExtIOS
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn idiom(&self) -> Idiom {
|
||||
UIDevice::current(MainThreadMarker::new().unwrap())
|
||||
.userInterfaceIdiom()
|
||||
.into()
|
||||
UIDevice::current(self.mtm).userInterfaceIdiom().into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,12 +261,11 @@ fn setup_control_flow_observers() {
|
||||
activity: CFRunLoopActivity,
|
||||
_: *mut c_void,
|
||||
) {
|
||||
unsafe {
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(mtm),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,13 +285,12 @@ fn setup_control_flow_observers() {
|
||||
activity: CFRunLoopActivity,
|
||||
_: *mut c_void,
|
||||
) {
|
||||
unsafe {
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(),
|
||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm),
|
||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,13 +300,12 @@ fn setup_control_flow_observers() {
|
||||
activity: CFRunLoopActivity,
|
||||
_: *mut c_void,
|
||||
) {
|
||||
unsafe {
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(),
|
||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm),
|
||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,16 +346,16 @@ fn setup_control_flow_observers() {
|
||||
#[derive(Debug)]
|
||||
pub enum Never {}
|
||||
|
||||
type EventHandlerCallback<T> =
|
||||
dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow) + 'static;
|
||||
type EventHandlerCallback<T> = dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>) + 'static;
|
||||
|
||||
pub trait EventHandler: Debug {
|
||||
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_nonuser_event(&mut self, event: Event<Never>);
|
||||
fn handle_user_events(&mut self);
|
||||
}
|
||||
|
||||
struct EventLoopHandler<T: 'static> {
|
||||
f: Box<EventHandlerCallback<T>>,
|
||||
receiver: Receiver<T>,
|
||||
event_loop: RootEventLoopWindowTarget<T>,
|
||||
}
|
||||
|
||||
@@ -348,17 +368,13 @@ impl<T: 'static> Debug for EventLoopHandler<T> {
|
||||
}
|
||||
|
||||
impl<T: 'static> EventHandler for EventLoopHandler<T> {
|
||||
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
|
||||
(self.f)(
|
||||
event.map_nonuser_event().unwrap(),
|
||||
&self.event_loop,
|
||||
control_flow,
|
||||
);
|
||||
fn handle_nonuser_event(&mut self, event: Event<Never>) {
|
||||
(self.f)(event.map_nonuser_event().unwrap(), &self.event_loop);
|
||||
}
|
||||
|
||||
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
|
||||
for event in self.event_loop.p.receiver.try_iter() {
|
||||
(self.f)(Event::UserEvent(event), &self.event_loop, control_flow);
|
||||
fn handle_user_events(&mut self) {
|
||||
for event in self.receiver.try_iter() {
|
||||
(self.f)(Event::UserEvent(event), &self.event_loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,17 +58,6 @@
|
||||
#![cfg(ios_platform)]
|
||||
#![allow(clippy::let_unit_value)]
|
||||
|
||||
// TODO: (mtak-) UIKit requires main thread for virtually all function/method calls. This could be
|
||||
// worked around in the future by using GCD (grand central dispatch) and/or caching of values like
|
||||
// window size/position.
|
||||
macro_rules! assert_main_thread {
|
||||
($($t:tt)*) => {
|
||||
if !::icrate::Foundation::is_main_thread() {
|
||||
panic!($($t)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod app_state;
|
||||
mod event_loop;
|
||||
mod ffi;
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
use std::{
|
||||
collections::{BTreeSet, VecDeque},
|
||||
fmt,
|
||||
ops::{Deref, DerefMut},
|
||||
fmt, hash, ptr,
|
||||
};
|
||||
|
||||
use icrate::Foundation::{MainThreadMarker, NSInteger};
|
||||
use icrate::Foundation::{MainThreadBound, MainThreadMarker, NSInteger};
|
||||
use objc2::mutability::IsRetainable;
|
||||
use objc2::rc::Id;
|
||||
|
||||
use super::uikit::{UIScreen, UIScreenMode};
|
||||
@@ -16,32 +16,59 @@ use crate::{
|
||||
platform_impl::platform::app_state,
|
||||
};
|
||||
|
||||
// TODO(madsmtm): Remove or refactor this
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub(crate) struct ScreenModeSendSync(pub(crate) Id<UIScreenMode>);
|
||||
// Workaround for `MainThreadBound` implementing almost no traits
|
||||
#[derive(Debug)]
|
||||
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Id<T>>);
|
||||
|
||||
unsafe impl Send for ScreenModeSendSync {}
|
||||
unsafe impl Sync for ScreenModeSendSync {}
|
||||
impl<T: IsRetainable> Clone for MainThreadBoundDelegateImpls<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(
|
||||
self.0
|
||||
.get_on_main(|inner, mtm| MainThreadBound::new(Id::clone(inner), mtm)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IsRetainable> hash::Hash for MainThreadBoundDelegateImpls<T> {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
// SAFETY: Marker only used to get the pointer
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
Id::as_ptr(self.0.get(mtm)).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IsRetainable> PartialEq for MainThreadBoundDelegateImpls<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// SAFETY: Marker only used to get the pointer
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
Id::as_ptr(self.0.get(mtm)) == Id::as_ptr(other.0.get(mtm))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IsRetainable> Eq for MainThreadBoundDelegateImpls<T> {}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub struct VideoMode {
|
||||
pub(crate) size: (u32, u32),
|
||||
pub(crate) bit_depth: u16,
|
||||
pub(crate) refresh_rate_millihertz: u32,
|
||||
pub(crate) screen_mode: ScreenModeSendSync,
|
||||
screen_mode: MainThreadBoundDelegateImpls<UIScreenMode>,
|
||||
pub(crate) monitor: MonitorHandle,
|
||||
}
|
||||
|
||||
impl 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");
|
||||
fn new(
|
||||
uiscreen: Id<UIScreen>,
|
||||
screen_mode: Id<UIScreenMode>,
|
||||
mtm: MainThreadMarker,
|
||||
) -> VideoMode {
|
||||
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
|
||||
let size = screen_mode.size();
|
||||
VideoMode {
|
||||
size: (size.width as u32, size.height as u32),
|
||||
bit_depth: 32,
|
||||
refresh_rate_millihertz,
|
||||
screen_mode: ScreenModeSendSync(screen_mode),
|
||||
screen_mode: MainThreadBoundDelegateImpls(MainThreadBound::new(screen_mode, mtm)),
|
||||
monitor: MonitorHandle::new(uiscreen),
|
||||
}
|
||||
}
|
||||
@@ -61,18 +88,40 @@ impl VideoMode {
|
||||
pub fn monitor(&self) -> MonitorHandle {
|
||||
self.monitor.clone()
|
||||
}
|
||||
|
||||
pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Id<UIScreenMode> {
|
||||
self.screen_mode.0.get(mtm)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Inner {
|
||||
uiscreen: Id<UIScreen>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MonitorHandle {
|
||||
inner: Inner,
|
||||
ui_screen: MainThreadBound<Id<UIScreen>>,
|
||||
}
|
||||
|
||||
impl Clone for MonitorHandle {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
ui_screen: self
|
||||
.ui_screen
|
||||
.get_on_main(|inner, mtm| MainThreadBound::new(inner.clone(), mtm)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for MonitorHandle {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
(self as *const Self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for MonitorHandle {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
ptr::eq(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for MonitorHandle {}
|
||||
|
||||
impl PartialOrd for MonitorHandle {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
@@ -86,31 +135,6 @@ impl Ord for MonitorHandle {
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MonitorHandle {
|
||||
type Target = Inner;
|
||||
|
||||
fn deref(&self) -> &Inner {
|
||||
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS");
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MonitorHandle {
|
||||
fn deref_mut(&mut self) -> &mut Inner {
|
||||
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS");
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for MonitorHandle {}
|
||||
unsafe impl Sync for MonitorHandle {}
|
||||
|
||||
impl Drop for MonitorHandle {
|
||||
fn drop(&mut self) {
|
||||
assert_main_thread!("`MonitorHandle` can only be dropped on the main thread on iOS");
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MonitorHandle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO: Do this using the proper fmt API
|
||||
@@ -135,59 +159,80 @@ impl fmt::Debug for MonitorHandle {
|
||||
}
|
||||
|
||||
impl MonitorHandle {
|
||||
pub(crate) fn new(uiscreen: Id<UIScreen>) -> Self {
|
||||
assert_main_thread!("`MonitorHandle` can only be created on the main thread on iOS");
|
||||
pub(crate) fn new(ui_screen: Id<UIScreen>) -> Self {
|
||||
// Holding `Id<UIScreen>` implies we're on the main thread.
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
Self {
|
||||
inner: Inner { uiscreen },
|
||||
ui_screen: MainThreadBound::new(ui_screen, mtm),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
pub fn name(&self) -> Option<String> {
|
||||
let main = UIScreen::main(MainThreadMarker::new().unwrap());
|
||||
if self.uiscreen == main {
|
||||
Some("Primary".to_string())
|
||||
} else if self.uiscreen == main.mirroredScreen() {
|
||||
Some("Mirrored".to_string())
|
||||
} else {
|
||||
UIScreen::screens(MainThreadMarker::new().unwrap())
|
||||
.iter()
|
||||
.position(|rhs| rhs == &*self.uiscreen)
|
||||
.map(|idx| idx.to_string())
|
||||
}
|
||||
self.ui_screen.get_on_main(|ui_screen, mtm| {
|
||||
let main = UIScreen::main(mtm);
|
||||
if *ui_screen == main {
|
||||
Some("Primary".to_string())
|
||||
} else if *ui_screen == main.mirroredScreen() {
|
||||
Some("Mirrored".to_string())
|
||||
} else {
|
||||
UIScreen::screens(mtm)
|
||||
.iter()
|
||||
.position(|rhs| rhs == &**ui_screen)
|
||||
.map(|idx| idx.to_string())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
let bounds = self.uiscreen.nativeBounds();
|
||||
let bounds = self
|
||||
.ui_screen
|
||||
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
|
||||
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
|
||||
}
|
||||
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
let bounds = self.uiscreen.nativeBounds();
|
||||
let bounds = self
|
||||
.ui_screen
|
||||
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
|
||||
(bounds.origin.x as f64, bounds.origin.y as f64).into()
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.uiscreen.nativeScale() as f64
|
||||
self.ui_screen
|
||||
.get_on_main(|ui_screen, _| ui_screen.nativeScale()) as f64
|
||||
}
|
||||
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
Some(refresh_rate_millihertz(&self.uiscreen))
|
||||
Some(
|
||||
self.ui_screen
|
||||
.get_on_main(|ui_screen, _| refresh_rate_millihertz(ui_screen)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
||||
// Use Ord impl of RootVideoMode
|
||||
let modes: BTreeSet<_> = self
|
||||
.uiscreen
|
||||
.availableModes()
|
||||
.into_iter()
|
||||
.map(|mode| RootVideoMode {
|
||||
video_mode: VideoMode::new(self.uiscreen.clone(), mode),
|
||||
})
|
||||
.collect();
|
||||
self.ui_screen.get_on_main(|ui_screen, mtm| {
|
||||
// Use Ord impl of RootVideoMode
|
||||
|
||||
modes.into_iter().map(|mode| mode.video_mode)
|
||||
let modes: BTreeSet<_> = ui_screen
|
||||
.availableModes()
|
||||
.into_iter()
|
||||
.map(|mode| RootVideoMode {
|
||||
video_mode: VideoMode::new(ui_screen.clone(), mode, mtm),
|
||||
})
|
||||
.collect();
|
||||
|
||||
modes.into_iter().map(|mode| mode.video_mode)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Id<UIScreen> {
|
||||
self.ui_screen.get(mtm)
|
||||
}
|
||||
|
||||
pub fn preferred_video_mode(&self) -> VideoMode {
|
||||
self.ui_screen.get_on_main(|ui_screen, mtm| {
|
||||
VideoMode::new(ui_screen.clone(), ui_screen.preferredMode().unwrap(), mtm)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,20 +260,6 @@ fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 {
|
||||
refresh_rate_millihertz as u32 * 1000
|
||||
}
|
||||
|
||||
// MonitorHandleExtIOS
|
||||
impl Inner {
|
||||
pub(crate) fn ui_screen(&self) -> &Id<UIScreen> {
|
||||
&self.uiscreen
|
||||
}
|
||||
|
||||
pub fn preferred_video_mode(&self) -> VideoMode {
|
||||
VideoMode::new(
|
||||
self.uiscreen.clone(),
|
||||
self.uiscreen.preferredMode().unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
||||
UIScreen::screens(mtm)
|
||||
.into_iter()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use objc2::rc::Id;
|
||||
use objc2::runtime::AnyClass;
|
||||
use objc2::{declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType};
|
||||
|
||||
use super::app_state::{self, EventWrapper};
|
||||
use super::uikit::{
|
||||
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIInterfaceOrientationMask,
|
||||
UIResponder, UITouch, UITouchPhase, UITouchType, UITraitCollection, UIView, UIViewController,
|
||||
@@ -19,8 +20,6 @@ use crate::{
|
||||
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
|
||||
platform::ios::ValidOrientations,
|
||||
platform_impl::platform::{
|
||||
app_state,
|
||||
event_loop::{EventProxy, EventWrapper},
|
||||
ffi::{UIRectEdge, UIUserInterfaceIdiom},
|
||||
window::PlatformSpecificWindowBuilderAttributes,
|
||||
DeviceId, Fullscreen,
|
||||
@@ -41,17 +40,21 @@ declare_class!(
|
||||
unsafe impl WinitView {
|
||||
#[method(drawRect:)]
|
||||
fn draw_rect(&self, rect: CGRect) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let window = self.window().unwrap();
|
||||
unsafe {
|
||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawRequested(
|
||||
RootWindowId(window.id()),
|
||||
)));
|
||||
}
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
}),
|
||||
);
|
||||
let _: () = unsafe { msg_send![super(self), drawRect: rect] };
|
||||
}
|
||||
|
||||
#[method(layoutSubviews)]
|
||||
fn layout_subviews(&self) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
|
||||
|
||||
let window = self.window().unwrap();
|
||||
@@ -74,16 +77,18 @@ declare_class!(
|
||||
self.setFrame(window_bounds);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::Resized(size),
|
||||
}));
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
#[method(setContentScaleFactor:)]
|
||||
fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let _: () =
|
||||
unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] };
|
||||
|
||||
@@ -111,25 +116,26 @@ declare_class!(
|
||||
let screen_space = screen.coordinateSpace();
|
||||
let screen_frame = self.convertRect_toCoordinateSpace(bounds, &screen_space);
|
||||
let size = crate::dpi::LogicalSize {
|
||||
width: screen_frame.size.width as _,
|
||||
height: screen_frame.size.height as _,
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
};
|
||||
let window_id = RootWindowId(window.id());
|
||||
unsafe {
|
||||
app_state::handle_nonuser_events(
|
||||
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
std::iter::once(EventWrapper::ScaleFactorChanged(
|
||||
app_state::ScaleFactorChanged {
|
||||
window,
|
||||
scale_factor,
|
||||
suggested_size: size,
|
||||
}))
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
}
|
||||
suggested_size: size.to_physical(scale_factor),
|
||||
},
|
||||
))
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
#[method(touchesBegan:withEvent:)]
|
||||
@@ -254,9 +260,8 @@ impl WinitView {
|
||||
}),
|
||||
}));
|
||||
}
|
||||
unsafe {
|
||||
app_state::handle_nonuser_events(touch_events);
|
||||
}
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(mtm, touch_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,23 +435,27 @@ declare_class!(
|
||||
unsafe impl WinitUIWindow {
|
||||
#[method(becomeKeyWindow)]
|
||||
fn become_key_window(&self) {
|
||||
unsafe {
|
||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(self.id()),
|
||||
event: WindowEvent::Focused(true),
|
||||
}));
|
||||
}
|
||||
}),
|
||||
);
|
||||
let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
|
||||
}
|
||||
|
||||
#[method(resignKeyWindow)]
|
||||
fn resign_key_window(&self) {
|
||||
unsafe {
|
||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(self.id()),
|
||||
event: WindowEvent::Focused(false),
|
||||
}));
|
||||
}
|
||||
}),
|
||||
);
|
||||
let _: () = unsafe { msg_send![super(self), resignKeyWindow] };
|
||||
}
|
||||
}
|
||||
@@ -454,7 +463,7 @@ declare_class!(
|
||||
|
||||
impl WinitUIWindow {
|
||||
pub(crate) fn new(
|
||||
_mtm: MainThreadMarker,
|
||||
mtm: MainThreadMarker,
|
||||
window_attributes: &WindowAttributes,
|
||||
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
||||
frame: CGRect,
|
||||
@@ -467,12 +476,12 @@ impl WinitUIWindow {
|
||||
match window_attributes.fullscreen.clone().map(Into::into) {
|
||||
Some(Fullscreen::Exclusive(ref video_mode)) => {
|
||||
let monitor = video_mode.monitor();
|
||||
let screen = monitor.ui_screen();
|
||||
screen.setCurrentMode(Some(&video_mode.screen_mode.0));
|
||||
let screen = monitor.ui_screen(mtm);
|
||||
screen.setCurrentMode(Some(video_mode.screen_mode(mtm)));
|
||||
this.setScreen(screen);
|
||||
}
|
||||
Some(Fullscreen::Borderless(Some(ref monitor))) => {
|
||||
let screen = monitor.ui_screen();
|
||||
let screen = monitor.ui_screen(mtm);
|
||||
this.setScreen(screen);
|
||||
}
|
||||
_ => (),
|
||||
@@ -499,27 +508,31 @@ declare_class!(
|
||||
unsafe impl WinitApplicationDelegate {
|
||||
#[method(application:didFinishLaunchingWithOptions:)]
|
||||
fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
|
||||
unsafe {
|
||||
app_state::did_finish_launching();
|
||||
}
|
||||
app_state::did_finish_launching(MainThreadMarker::new().unwrap());
|
||||
true
|
||||
}
|
||||
|
||||
#[method(applicationDidBecomeActive:)]
|
||||
fn did_become_active(&self, _application: &UIApplication) {
|
||||
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed))
|
||||
}
|
||||
|
||||
#[method(applicationWillResignActive:)]
|
||||
fn will_resign_active(&self, _application: &UIApplication) {
|
||||
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended))
|
||||
}
|
||||
|
||||
#[method(applicationWillEnterForeground:)]
|
||||
fn will_enter_foreground(&self, _application: &UIApplication) {}
|
||||
fn will_enter_foreground(&self, application: &UIApplication) {
|
||||
self.send_occluded_event_for_all_windows(application, false);
|
||||
}
|
||||
|
||||
#[method(applicationDidEnterBackground:)]
|
||||
fn did_enter_background(&self, _application: &UIApplication) {}
|
||||
fn did_enter_background(&self, application: &UIApplication) {
|
||||
self.send_occluded_event_for_all_windows(application, true);
|
||||
}
|
||||
|
||||
#[method(applicationWillTerminate:)]
|
||||
fn will_terminate(&self, application: &UIApplication) {
|
||||
@@ -538,10 +551,37 @@ declare_class!(
|
||||
}));
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
app_state::handle_nonuser_events(events);
|
||||
app_state::terminated();
|
||||
}
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(mtm, events);
|
||||
app_state::terminated(mtm);
|
||||
}
|
||||
|
||||
#[method(applicationDidReceiveMemoryWarning:)]
|
||||
fn did_receive_memory_warning(&self, _application: &UIApplication) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning))
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl WinitApplicationDelegate {
|
||||
fn send_occluded_event_for_all_windows(&self, application: &UIApplication, occluded: bool) {
|
||||
let mut events = Vec::new();
|
||||
for window in application.windows().iter() {
|
||||
if window.is_kind_of::<WinitUIWindow>() {
|
||||
// SAFETY: We just checked that the window is a `winit` window
|
||||
let window = unsafe {
|
||||
let ptr: *const UIWindow = window;
|
||||
let ptr: *const WinitUIWindow = ptr.cast();
|
||||
&*ptr
|
||||
};
|
||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::Occluded(occluded),
|
||||
}));
|
||||
}
|
||||
}
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(mtm, events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, Main
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{class, msg_send};
|
||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, UiKitDisplayHandle, UiKitWindowHandle};
|
||||
|
||||
use super::app_state::EventWrapper;
|
||||
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
|
||||
use super::view::{WinitUIWindow, WinitView, WinitViewController};
|
||||
use crate::{
|
||||
@@ -17,9 +17,7 @@ use crate::{
|
||||
icon::Icon,
|
||||
platform::ios::{ScreenEdge, ValidOrientations},
|
||||
platform_impl::platform::{
|
||||
app_state,
|
||||
event_loop::{EventProxy, EventWrapper},
|
||||
monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
|
||||
app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
|
||||
},
|
||||
window::{
|
||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
@@ -43,6 +41,10 @@ impl Inner {
|
||||
debug!("`Window::set_transparent` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_blur(&self, _blur: bool) {
|
||||
debug!("`Window::set_blur` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
self.window.setHidden(!visible)
|
||||
}
|
||||
@@ -53,20 +55,19 @@ impl Inner {
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
unsafe {
|
||||
if self.gl_or_metal_backed {
|
||||
// `setNeedsDisplay` does nothing on UIViews which are directly backed by CAEAGLLayer or CAMetalLayer.
|
||||
// Ordinarily the OS sets up a bunch of UIKit state before calling drawRect: on a UIView, but when using
|
||||
// raw or gl/metal for drawing this work is completely avoided.
|
||||
//
|
||||
// The docs for `setNeedsDisplay` don't mention `CAMetalLayer`; however, this has been confirmed via
|
||||
// testing.
|
||||
//
|
||||
// https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc
|
||||
app_state::queue_gl_or_metal_redraw(self.window.clone());
|
||||
} else {
|
||||
self.view.setNeedsDisplay();
|
||||
}
|
||||
if self.gl_or_metal_backed {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
// `setNeedsDisplay` does nothing on UIViews which are directly backed by CAEAGLLayer or CAMetalLayer.
|
||||
// Ordinarily the OS sets up a bunch of UIKit state before calling drawRect: on a UIView, but when using
|
||||
// raw or gl/metal for drawing this work is completely avoided.
|
||||
//
|
||||
// The docs for `setNeedsDisplay` don't mention `CAMetalLayer`; however, this has been confirmed via
|
||||
// testing.
|
||||
//
|
||||
// https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc
|
||||
app_state::queue_gl_or_metal_redraw(mtm, self.window.clone());
|
||||
} else {
|
||||
self.view.setNeedsDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +197,9 @@ impl Inner {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
@@ -219,14 +223,17 @@ impl Inner {
|
||||
}
|
||||
|
||||
pub(crate) fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let uiscreen = match &monitor {
|
||||
Some(Fullscreen::Exclusive(video_mode)) => {
|
||||
let uiscreen = video_mode.monitor.ui_screen();
|
||||
uiscreen.setCurrentMode(Some(&video_mode.screen_mode.0));
|
||||
let uiscreen = video_mode.monitor.ui_screen(mtm);
|
||||
uiscreen.setCurrentMode(Some(video_mode.screen_mode(mtm)));
|
||||
uiscreen.clone()
|
||||
}
|
||||
Some(Fullscreen::Borderless(Some(monitor))) => monitor.ui_screen().clone(),
|
||||
Some(Fullscreen::Borderless(None)) => self.current_monitor_inner().ui_screen().clone(),
|
||||
Some(Fullscreen::Borderless(Some(monitor))) => monitor.ui_screen(mtm).clone(),
|
||||
Some(Fullscreen::Borderless(None)) => {
|
||||
self.current_monitor_inner().ui_screen(mtm).clone()
|
||||
}
|
||||
None => {
|
||||
warn!("`Window::set_fullscreen(None)` ignored on iOS");
|
||||
return;
|
||||
@@ -249,8 +256,9 @@ impl Inner {
|
||||
}
|
||||
|
||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let monitor = self.current_monitor_inner();
|
||||
let uiscreen = monitor.ui_screen();
|
||||
let uiscreen = monitor.ui_screen(mtm);
|
||||
let screen_space_bounds = self.screen_frame();
|
||||
let screen_bounds = uiscreen.bounds();
|
||||
|
||||
@@ -323,16 +331,47 @@ impl Inner {
|
||||
self.window.id()
|
||||
}
|
||||
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let mut window_handle = UiKitWindowHandle::empty();
|
||||
#[cfg(feature = "rwh_04")]
|
||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||
let mut window_handle = rwh_04::UiKitHandle::empty();
|
||||
window_handle.ui_window = Id::as_ptr(&self.window) as _;
|
||||
window_handle.ui_view = Id::as_ptr(&self.view) as _;
|
||||
window_handle.ui_view_controller = Id::as_ptr(&self.view_controller) as _;
|
||||
RawWindowHandle::UiKit(window_handle)
|
||||
rwh_04::RawWindowHandle::UiKit(window_handle)
|
||||
}
|
||||
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
RawDisplayHandle::UiKit(UiKitDisplayHandle::empty())
|
||||
#[cfg(feature = "rwh_05")]
|
||||
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
||||
let mut window_handle = rwh_05::UiKitWindowHandle::empty();
|
||||
window_handle.ui_window = Id::as_ptr(&self.window) as _;
|
||||
window_handle.ui_view = Id::as_ptr(&self.view) as _;
|
||||
window_handle.ui_view_controller = Id::as_ptr(&self.view_controller) as _;
|
||||
rwh_05::RawWindowHandle::UiKit(window_handle)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::RawDisplayHandle::UiKit(rwh_05::UiKitDisplayHandle::empty())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
let mut window_handle = rwh_06::UiKitWindowHandle::new({
|
||||
let ui_view = Id::as_ptr(&self.view) as _;
|
||||
std::ptr::NonNull::new(ui_view).expect("Id<T> should never be null")
|
||||
});
|
||||
window_handle.ui_view_controller =
|
||||
std::ptr::NonNull::new(Id::as_ptr(&self.view_controller) as _);
|
||||
Ok(rwh_06::RawWindowHandle::UiKit(window_handle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::UiKit(
|
||||
rwh_06::UiKitDisplayHandle::new(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
@@ -340,6 +379,8 @@ impl Inner {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
pub fn has_focus(&self) -> bool {
|
||||
self.window.isKeyWindow()
|
||||
}
|
||||
@@ -365,11 +406,11 @@ pub struct Window {
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new<T>(
|
||||
_event_loop: &EventLoopWindowTarget<T>,
|
||||
event_loop: &EventLoopWindowTarget<T>,
|
||||
window_attributes: WindowAttributes,
|
||||
platform_attributes: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Window, RootOsError> {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let mtm = event_loop.mtm;
|
||||
|
||||
if window_attributes.min_inner_size.is_some() {
|
||||
warn!("`WindowAttributes::min_inner_size` is ignored on iOS");
|
||||
@@ -383,8 +424,8 @@ impl Window {
|
||||
let main_screen = UIScreen::main(mtm);
|
||||
let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
|
||||
let screen = match fullscreen {
|
||||
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(),
|
||||
Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(),
|
||||
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm),
|
||||
Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm),
|
||||
Some(Fullscreen::Borderless(None)) | None => &main_screen,
|
||||
};
|
||||
|
||||
@@ -424,7 +465,7 @@ impl Window {
|
||||
&view_controller,
|
||||
);
|
||||
|
||||
unsafe { app_state::set_key_window(&window) };
|
||||
app_state::set_key_window(mtm, &window);
|
||||
|
||||
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
|
||||
// event on window creation if the DPI factor != 1.0
|
||||
@@ -436,25 +477,26 @@ impl Window {
|
||||
let screen_space = screen.coordinateSpace();
|
||||
let screen_frame = view.convertRect_toCoordinateSpace(bounds, &screen_space);
|
||||
let size = crate::dpi::LogicalSize {
|
||||
width: screen_frame.size.width as _,
|
||||
height: screen_frame.size.height as _,
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
};
|
||||
let window_id = RootWindowId(window.id());
|
||||
unsafe {
|
||||
app_state::handle_nonuser_events(
|
||||
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
std::iter::once(EventWrapper::ScaleFactorChanged(
|
||||
app_state::ScaleFactorChanged {
|
||||
window: window.clone(),
|
||||
scale_factor,
|
||||
suggested_size: size,
|
||||
}))
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
}
|
||||
suggested_size: size.to_physical(scale_factor),
|
||||
},
|
||||
))
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
let inner = Inner {
|
||||
|
||||
@@ -13,7 +13,7 @@ use xkbcommon_dl::{
|
||||
XkbCommonCompose,
|
||||
};
|
||||
#[cfg(feature = "wayland")]
|
||||
use {memmap2::MmapOptions, wayland_backend::io_lifetimes::OwnedFd};
|
||||
use {memmap2::MmapOptions, std::os::unix::io::OwnedFd};
|
||||
#[cfg(feature = "x11")]
|
||||
use {x11_dl::xlib_xcb::xcb_connection_t, xkbcommon_dl::x11::xkbcommon_x11_handle};
|
||||
|
||||
@@ -250,37 +250,43 @@ impl KbdState {
|
||||
.unwrap_or_else(|| "C".into());
|
||||
let locale = CString::new(locale.into_vec()).unwrap();
|
||||
|
||||
let compose_table = (XKBCH.xkb_compose_table_new_from_locale)(
|
||||
self.xkb_context,
|
||||
locale.as_ptr(),
|
||||
ffi::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
|
||||
);
|
||||
let compose_table = unsafe {
|
||||
(XKBCH.xkb_compose_table_new_from_locale)(
|
||||
self.xkb_context,
|
||||
locale.as_ptr(),
|
||||
ffi::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
|
||||
)
|
||||
};
|
||||
|
||||
if compose_table.is_null() {
|
||||
// init of compose table failed, continue without compose
|
||||
return;
|
||||
}
|
||||
|
||||
let compose_state = (XKBCH.xkb_compose_state_new)(
|
||||
compose_table,
|
||||
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
|
||||
);
|
||||
let compose_state = unsafe {
|
||||
(XKBCH.xkb_compose_state_new)(
|
||||
compose_table,
|
||||
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
|
||||
)
|
||||
};
|
||||
|
||||
if compose_state.is_null() {
|
||||
// init of compose state failed, continue without compose
|
||||
(XKBCH.xkb_compose_table_unref)(compose_table);
|
||||
unsafe { (XKBCH.xkb_compose_table_unref)(compose_table) };
|
||||
return;
|
||||
}
|
||||
|
||||
let compose_state_2 = (XKBCH.xkb_compose_state_new)(
|
||||
compose_table,
|
||||
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
|
||||
);
|
||||
let compose_state_2 = unsafe {
|
||||
(XKBCH.xkb_compose_state_new)(
|
||||
compose_table,
|
||||
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
|
||||
)
|
||||
};
|
||||
|
||||
if compose_state_2.is_null() {
|
||||
// init of compose state failed, continue without compose
|
||||
(XKBCH.xkb_compose_table_unref)(compose_table);
|
||||
(XKBCH.xkb_compose_state_unref)(compose_state);
|
||||
unsafe { (XKBCH.xkb_compose_table_unref)(compose_table) };
|
||||
unsafe { (XKBCH.xkb_compose_state_unref)(compose_state) };
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -296,62 +302,71 @@ impl KbdState {
|
||||
}
|
||||
|
||||
unsafe fn de_init(&mut self) {
|
||||
(XKBH.xkb_state_unref)(self.xkb_state);
|
||||
unsafe { (XKBH.xkb_state_unref)(self.xkb_state) };
|
||||
self.xkb_state = ptr::null_mut();
|
||||
(XKBH.xkb_keymap_unref)(self.xkb_keymap);
|
||||
unsafe { (XKBH.xkb_keymap_unref)(self.xkb_keymap) };
|
||||
self.xkb_keymap = ptr::null_mut();
|
||||
}
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
pub unsafe fn init_with_x11_keymap(&mut self) {
|
||||
if !self.xkb_keymap.is_null() {
|
||||
self.de_init();
|
||||
unsafe { self.de_init() };
|
||||
}
|
||||
|
||||
// TODO: Support keyboards other than the "virtual core keyboard device".
|
||||
self.core_keyboard_id = (XKBXH.xkb_x11_get_core_keyboard_device_id)(self.xcb_connection);
|
||||
let keymap = (XKBXH.xkb_x11_keymap_new_from_device)(
|
||||
self.xkb_context,
|
||||
self.xcb_connection,
|
||||
self.core_keyboard_id,
|
||||
xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
);
|
||||
self.core_keyboard_id =
|
||||
unsafe { (XKBXH.xkb_x11_get_core_keyboard_device_id)(self.xcb_connection) };
|
||||
let keymap = unsafe {
|
||||
(XKBXH.xkb_x11_keymap_new_from_device)(
|
||||
self.xkb_context,
|
||||
self.xcb_connection,
|
||||
self.core_keyboard_id,
|
||||
xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
)
|
||||
};
|
||||
if keymap.is_null() {
|
||||
panic!("Failed to get keymap from X11 server.");
|
||||
}
|
||||
|
||||
let state = (XKBXH.xkb_x11_state_new_from_device)(
|
||||
keymap,
|
||||
self.xcb_connection,
|
||||
self.core_keyboard_id,
|
||||
);
|
||||
self.post_init(state, keymap);
|
||||
let state = unsafe {
|
||||
(XKBXH.xkb_x11_state_new_from_device)(
|
||||
keymap,
|
||||
self.xcb_connection,
|
||||
self.core_keyboard_id,
|
||||
)
|
||||
};
|
||||
unsafe { self.post_init(state, keymap) };
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
pub unsafe fn init_with_fd(&mut self, fd: OwnedFd, size: usize) {
|
||||
if !self.xkb_keymap.is_null() {
|
||||
self.de_init();
|
||||
unsafe { self.de_init() };
|
||||
}
|
||||
|
||||
let map = MmapOptions::new()
|
||||
.len(size)
|
||||
.map_copy_read_only(&fd)
|
||||
.unwrap();
|
||||
let map = unsafe {
|
||||
MmapOptions::new()
|
||||
.len(size)
|
||||
.map_copy_read_only(&fd)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let keymap = (XKBH.xkb_keymap_new_from_string)(
|
||||
self.xkb_context,
|
||||
map.as_ptr() as *const _,
|
||||
ffi::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
);
|
||||
let keymap = unsafe {
|
||||
(XKBH.xkb_keymap_new_from_string)(
|
||||
self.xkb_context,
|
||||
map.as_ptr() as *const _,
|
||||
ffi::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
)
|
||||
};
|
||||
|
||||
if keymap.is_null() {
|
||||
panic!("Received invalid keymap from compositor.");
|
||||
}
|
||||
|
||||
let state = (XKBH.xkb_state_new)(keymap);
|
||||
self.post_init(state, keymap);
|
||||
let state = unsafe { (XKBH.xkb_state_new)(keymap) };
|
||||
unsafe { self.post_init(state, keymap) };
|
||||
}
|
||||
|
||||
pub fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool {
|
||||
|
||||
@@ -11,7 +11,6 @@ use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex};
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
use once_cell::sync::Lazy;
|
||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
@@ -19,7 +18,7 @@ use crate::platform::x11::XlibErrorHook;
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
event::{Event, KeyEvent},
|
||||
event::KeyEvent,
|
||||
event_loop::{
|
||||
AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||
EventLoopWindowTarget as RootELW,
|
||||
@@ -178,9 +177,9 @@ pub enum DeviceId {
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
#[cfg(wayland_platform)]
|
||||
return DeviceId::Wayland(wayland::DeviceId::dummy());
|
||||
return DeviceId::Wayland(unsafe { wayland::DeviceId::dummy() });
|
||||
#[cfg(all(not(wayland_platform), x11_platform))]
|
||||
return DeviceId::X(x11::DeviceId::dummy());
|
||||
return DeviceId::X(unsafe { x11::DeviceId::dummy() });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +282,7 @@ impl VideoMode {
|
||||
|
||||
#[inline]
|
||||
pub fn monitor(&self) -> MonitorHandle {
|
||||
x11_or_wayland!(match self; VideoMode(m) => m.monitor())
|
||||
x11_or_wayland!(match self; VideoMode(m) => m.monitor(); as MonitorHandle)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,12 +315,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
match self {
|
||||
#[cfg(wayland_platform)]
|
||||
Self::Wayland(window) => window.id(),
|
||||
#[cfg(x11_platform)]
|
||||
Self::X(window) => window.id(),
|
||||
}
|
||||
x11_or_wayland!(match self; Window(w) => w.id())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -334,6 +328,11 @@ impl Window {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_transparent(transparent));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_blur(&self, blur: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_blur(blur));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_visible(visible))
|
||||
@@ -444,6 +443,11 @@ impl Window {
|
||||
x11_or_wayland!(match self; Window(window) => window.drag_resize_window(direction))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, position: Position) {
|
||||
x11_or_wayland!(match self; Window(w) => w.show_window_menu(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_cursor_hittest(hittest))
|
||||
@@ -500,23 +504,13 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_level(&self, _level: WindowLevel) {
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Window::X(ref w) => w.set_window_level(_level),
|
||||
#[cfg(wayland_platform)]
|
||||
Window::Wayland(_) => (),
|
||||
}
|
||||
pub fn set_window_level(&self, level: WindowLevel) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_window_level(level))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _window_icon: Option<Icon>) {
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Window::X(ref w) => w.set_window_icon(_window_icon),
|
||||
#[cfg(wayland_platform)]
|
||||
Window::Wayland(_) => (),
|
||||
}
|
||||
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_window_icon(window_icon.map(|icon| icon.inner)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -541,12 +535,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Window::X(ref w) => w.focus_window(),
|
||||
#[cfg(wayland_platform)]
|
||||
Window::Wayland(_) => (),
|
||||
}
|
||||
x11_or_wayland!(match self; Window(w) => w.focus_window())
|
||||
}
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_user_attention(request_type))
|
||||
@@ -564,18 +553,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Window::X(ref window) => {
|
||||
let current_monitor = MonitorHandle::X(window.current_monitor());
|
||||
Some(current_monitor)
|
||||
}
|
||||
#[cfg(wayland_platform)]
|
||||
Window::Wayland(ref window) => {
|
||||
let current_monitor = MonitorHandle::Wayland(window.current_monitor()?);
|
||||
Some(current_monitor)
|
||||
}
|
||||
}
|
||||
Some(x11_or_wayland!(match self; Window(w) => w.current_monitor()?; as MonitorHandle))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -598,25 +576,39 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Window::X(ref window) => {
|
||||
let primary_monitor = MonitorHandle::X(window.primary_monitor());
|
||||
Some(primary_monitor)
|
||||
}
|
||||
#[cfg(wayland_platform)]
|
||||
Window::Wayland(ref window) => window.primary_monitor(),
|
||||
}
|
||||
Some(x11_or_wayland!(match self; Window(w) => w.primary_monitor()?; as MonitorHandle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_04")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
x11_or_wayland!(match self; Window(window) => window.raw_window_handle())
|
||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||
x11_or_wayland!(match self; Window(window) => window.raw_window_handle_rwh_04())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
x11_or_wayland!(match self; Window(window) => window.raw_display_handle())
|
||||
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
||||
x11_or_wayland!(match self; Window(window) => window.raw_window_handle_rwh_05())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
x11_or_wayland!(match self; Window(window) => window.raw_display_handle_rwh_05())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.raw_window_handle_rwh_06())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.raw_display_handle_rwh_06())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -629,6 +621,10 @@ impl Window {
|
||||
x11_or_wayland!(match self; Window(window) => window.theme())
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, protected: bool) {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_content_protected(protected))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
x11_or_wayland!(match self; Window(window) => window.has_focus())
|
||||
@@ -684,26 +680,31 @@ unsafe extern "C" fn x_error_callback(
|
||||
if let Ok(ref xconn) = *xconn_lock {
|
||||
// Call all the hooks.
|
||||
let mut error_handled = false;
|
||||
for hook in XLIB_ERROR_HOOKS.lock().unwrap().iter() {
|
||||
for hook in unsafe { XLIB_ERROR_HOOKS.lock() }.unwrap().iter() {
|
||||
error_handled |= hook(display as *mut _, event as *mut _);
|
||||
}
|
||||
|
||||
// `assume_init` is safe here because the array consists of `MaybeUninit` values,
|
||||
// which do not require initialization.
|
||||
let mut buf: [MaybeUninit<c_char>; 1024] = MaybeUninit::uninit().assume_init();
|
||||
(xconn.xlib.XGetErrorText)(
|
||||
display,
|
||||
(*event).error_code as c_int,
|
||||
buf.as_mut_ptr() as *mut c_char,
|
||||
buf.len() as c_int,
|
||||
);
|
||||
let description = CStr::from_ptr(buf.as_ptr() as *const c_char).to_string_lossy();
|
||||
let mut buf: [MaybeUninit<c_char>; 1024] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
unsafe {
|
||||
(xconn.xlib.XGetErrorText)(
|
||||
display,
|
||||
(*event).error_code as c_int,
|
||||
buf.as_mut_ptr() as *mut c_char,
|
||||
buf.len() as c_int,
|
||||
)
|
||||
};
|
||||
let description =
|
||||
unsafe { CStr::from_ptr(buf.as_ptr() as *const c_char) }.to_string_lossy();
|
||||
|
||||
let error = XError {
|
||||
description: description.into_owned(),
|
||||
error_code: (*event).error_code,
|
||||
request_code: (*event).request_code,
|
||||
minor_code: (*event).minor_code,
|
||||
let error = unsafe {
|
||||
XError {
|
||||
description: description.into_owned(),
|
||||
error_code: (*event).error_code,
|
||||
request_code: (*event).request_code,
|
||||
minor_code: (*event).minor_code,
|
||||
}
|
||||
};
|
||||
|
||||
// Don't log error.
|
||||
@@ -751,21 +752,34 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
|
||||
// NOTE: Wayland first because of X11 could be present under wayland as well.
|
||||
#[cfg(wayland_platform)]
|
||||
if attributes.forced_backend == Some(Backend::Wayland)
|
||||
|| env::var("WAYLAND_DISPLAY").is_ok()
|
||||
{
|
||||
return EventLoop::new_wayland_any_thread().map_err(Into::into);
|
||||
}
|
||||
let backend = match (
|
||||
attributes.forced_backend,
|
||||
env::var("WAYLAND_DISPLAY").is_ok(),
|
||||
env::var("DISPLAY").is_ok(),
|
||||
) {
|
||||
// User is forcing a backend.
|
||||
(Some(backend), _, _) => backend,
|
||||
// Wayland is present.
|
||||
#[cfg(wayland_platform)]
|
||||
(None, true, _) => Backend::Wayland,
|
||||
// X11 is present.
|
||||
#[cfg(x11_platform)]
|
||||
(None, _, true) => Backend::X,
|
||||
// No backend is present.
|
||||
_ => {
|
||||
return Err(EventLoopError::Os(os_error!(OsError::Misc(
|
||||
"neither WAYLAND_DISPLAY nor DISPLAY is set."
|
||||
))));
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
if attributes.forced_backend == Some(Backend::X) || env::var("DISPLAY").is_ok() {
|
||||
return Ok(EventLoop::new_x11_any_thread().unwrap());
|
||||
// Create the display based on the backend.
|
||||
match backend {
|
||||
#[cfg(wayland_platform)]
|
||||
Backend::Wayland => EventLoop::new_wayland_any_thread().map_err(Into::into),
|
||||
#[cfg(x11_platform)]
|
||||
Backend::X => Ok(EventLoop::new_x11_any_thread().unwrap()),
|
||||
}
|
||||
|
||||
Err(EventLoopError::Os(os_error!(OsError::Misc(
|
||||
"neither WAYLAND_DISPLAY nor DISPLAY is set."
|
||||
))))
|
||||
}
|
||||
|
||||
#[cfg(wayland_platform)]
|
||||
@@ -789,21 +803,21 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(crate::event::Event<T>, &RootELW<T>),
|
||||
{
|
||||
self.run_ondemand(callback)
|
||||
self.run_on_demand(callback)
|
||||
}
|
||||
|
||||
pub fn run_ondemand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
|
||||
pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(crate::event::Event<T>, &RootELW<T>),
|
||||
{
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_ondemand(callback))
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(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),
|
||||
F: FnMut(crate::event::Event<T>, &RootELW<T>),
|
||||
{
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
|
||||
}
|
||||
@@ -843,61 +857,63 @@ impl<T> EventLoopWindowTarget<T> {
|
||||
#[cfg(wayland_platform)]
|
||||
EventLoopWindowTarget::Wayland(ref evlp) => evlp
|
||||
.available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorHandle::Wayland)
|
||||
.collect(),
|
||||
#[cfg(x11_platform)]
|
||||
EventLoopWindowTarget::X(ref evlp) => evlp
|
||||
.x_connection()
|
||||
.available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorHandle::X)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
match *self {
|
||||
#[cfg(wayland_platform)]
|
||||
EventLoopWindowTarget::Wayland(ref evlp) => evlp.primary_monitor(),
|
||||
#[cfg(x11_platform)]
|
||||
EventLoopWindowTarget::X(ref evlp) => {
|
||||
let primary_monitor = MonitorHandle::X(evlp.x_connection().primary_monitor());
|
||||
Some(primary_monitor)
|
||||
evlp.available_monitors().map(MonitorHandle::X).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn listen_device_events(&self, _allowed: DeviceEvents) {
|
||||
match *self {
|
||||
#[cfg(wayland_platform)]
|
||||
EventLoopWindowTarget::Wayland(_) => (),
|
||||
#[cfg(x11_platform)]
|
||||
EventLoopWindowTarget::X(ref evlp) => evlp.set_listen_device_events(_allowed),
|
||||
}
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(
|
||||
x11_or_wayland!(match self; EventLoopWindowTarget(evlp) => evlp.primary_monitor()?; as MonitorHandle),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle())
|
||||
#[inline]
|
||||
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.listen_device_events(allowed))
|
||||
}
|
||||
}
|
||||
|
||||
fn sticky_exit_callback<T, F>(
|
||||
evt: Event<T>,
|
||||
target: &RootELW<T>,
|
||||
control_flow: &mut ControlFlow,
|
||||
callback: &mut F,
|
||||
) where
|
||||
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
{
|
||||
// make ControlFlow::ExitWithCode sticky by providing a dummy
|
||||
// control flow reference if it is already ExitWithCode.
|
||||
if let ControlFlow::ExitWithCode(code) = *control_flow {
|
||||
callback(evt, target, &mut ControlFlow::ExitWithCode(code))
|
||||
} else {
|
||||
callback(evt, target, control_flow)
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_rwh_05())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_rwh_06())
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.set_control_flow(control_flow))
|
||||
}
|
||||
|
||||
pub(crate) fn control_flow(&self) -> ControlFlow {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.control_flow())
|
||||
}
|
||||
|
||||
pub(crate) fn exit(&self) {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.exit())
|
||||
}
|
||||
|
||||
pub(crate) fn exiting(&self) -> bool {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.exiting())
|
||||
}
|
||||
|
||||
fn set_exit_code(&self, code: i32) {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code))
|
||||
}
|
||||
|
||||
fn exit_code(&self) -> Option<i32> {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.exit_code())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! The event-loop routines.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::io::Result as IOResult;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
@@ -9,20 +9,20 @@ use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use raw_window_handle::{RawDisplayHandle, WaylandDisplayHandle};
|
||||
|
||||
use sctk::reexports::calloop;
|
||||
use sctk::reexports::calloop::Error as CalloopError;
|
||||
use sctk::reexports::calloop_wayland_source::WaylandSource;
|
||||
use sctk::reexports::client::globals;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle, WaylandSource};
|
||||
use sctk::reexports::client::{Connection, QueueHandle};
|
||||
|
||||
use crate::dpi::{LogicalSize, PhysicalSize};
|
||||
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, DeviceEvents, 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::{EventLoopWindowTarget as PlatformEventLoopWindowTarget, OsError};
|
||||
|
||||
mod proxy;
|
||||
@@ -39,12 +39,9 @@ type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>,
|
||||
|
||||
/// The Wayland event loop.
|
||||
pub struct EventLoop<T: 'static> {
|
||||
/// Has `run` or `run_ondemand` been called or a call to `pump_events` that starts the loop
|
||||
/// Has `run` or `run_on_demand` 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>,
|
||||
@@ -104,7 +101,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
)?;
|
||||
|
||||
// Register Wayland source.
|
||||
let wayland_source = map_err!(WaylandSource::new(event_queue), WaylandError::Wire)?;
|
||||
let wayland_source = WaylandSource::new(connection.clone(), event_queue);
|
||||
let wayland_dispatcher =
|
||||
calloop::Dispatcher::new(wayland_source, |_, queue, winit_state: &mut WinitState| {
|
||||
let result = queue.dispatch_pending(winit_state);
|
||||
@@ -166,13 +163,14 @@ impl<T: 'static> EventLoop<T> {
|
||||
wayland_dispatcher: wayland_dispatcher.clone(),
|
||||
event_loop_awakener,
|
||||
queue_handle,
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
exit: Cell::new(None),
|
||||
state: RefCell::new(winit_state),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
let event_loop = Self {
|
||||
loop_running: false,
|
||||
control_flow: ControlFlow::default(),
|
||||
compositor_updates: Vec::new(),
|
||||
buffer_sink: EventSink::default(),
|
||||
window_ids: Vec::new(),
|
||||
@@ -190,9 +188,9 @@ impl<T: 'static> EventLoop<T> {
|
||||
Ok(event_loop)
|
||||
}
|
||||
|
||||
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
|
||||
{
|
||||
if self.loop_running {
|
||||
return Err(EventLoopError::AlreadyRunning);
|
||||
@@ -213,7 +211,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
};
|
||||
|
||||
// Applications aren't allowed to carry windows between separate
|
||||
// `run_ondemand` calls but if they have only just dropped their
|
||||
// `run_on_demand` calls but if they have only just dropped their
|
||||
// windows we need to make sure those last requests are sent to the
|
||||
// compositor.
|
||||
let _ = self.roundtrip().map_err(EventLoopError::Os);
|
||||
@@ -223,35 +221,24 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
|
||||
{
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
// Reset the internal state for the loop as we start running to
|
||||
// ensure consistent behaviour in case the loop runs and exits more
|
||||
// than once.
|
||||
self.control_flow = ControlFlow::Poll;
|
||||
|
||||
// Run the initial loop iteration.
|
||||
self.single_iteration(&mut callback, StartCause::Init);
|
||||
}
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit.
|
||||
if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) {
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
}
|
||||
if let ControlFlow::ExitWithCode(code) = self.control_flow {
|
||||
if let Some(code) = self.exit_code() {
|
||||
self.loop_running = false;
|
||||
|
||||
let mut dummy = self.control_flow;
|
||||
sticky_exit_callback(
|
||||
Event::LoopExiting,
|
||||
self.window_target(),
|
||||
&mut dummy,
|
||||
&mut callback,
|
||||
);
|
||||
callback(Event::LoopExiting, self.window_target());
|
||||
|
||||
PumpStatus::Exit(code)
|
||||
} else {
|
||||
@@ -261,62 +248,18 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
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),
|
||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
|
||||
{
|
||||
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
|
||||
// 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.
|
||||
//
|
||||
// If some messages are there, the event loop needs to behave as if it was instantly
|
||||
// woken up by messages arriving from the Wayland socket, to avoid delaying the
|
||||
// dispatch of these events until we're woken up again.
|
||||
let instant_wakeup = {
|
||||
let mut wayland_source = self.wayland_dispatcher.as_source_mut();
|
||||
let queue = wayland_source.queue();
|
||||
let state = match &mut self.window_target.p {
|
||||
PlatformEventLoopWindowTarget::Wayland(window_target) => {
|
||||
window_target.state.get_mut()
|
||||
}
|
||||
#[cfg(x11_platform)]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match queue.dispatch_pending(state) {
|
||||
Ok(dispatched) => {
|
||||
state.dispatched_events |= !state.events_sink.is_empty()
|
||||
|| !state.window_compositor_updates.is_empty();
|
||||
dispatched > 0
|
||||
}
|
||||
Err(error) => {
|
||||
error!("Error dispatching wayland queue: {}", error);
|
||||
self.control_flow = ControlFlow::ExitWithCode(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
timeout = if instant_wakeup {
|
||||
Some(Duration::ZERO)
|
||||
} else {
|
||||
let control_flow_timeout = match self.control_flow {
|
||||
timeout = {
|
||||
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))
|
||||
}
|
||||
// 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)
|
||||
};
|
||||
@@ -324,7 +267,13 @@ impl<T: 'static> EventLoop<T> {
|
||||
// NOTE Ideally we should flush as the last thing we do before polling
|
||||
// to wait for events, and this should be done by the calloop
|
||||
// WaylandSource but we currently need to flush writes manually.
|
||||
let _ = self.connection.flush();
|
||||
//
|
||||
// Checking for flush error is essential to perform an exit with error, since
|
||||
// once we have a protocol error, we could get stuck retrying...
|
||||
if self.connection.flush().is_err() {
|
||||
self.set_exit_code(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(error) = self.loop_dispatch(timeout) {
|
||||
// NOTE We exit on errors from dispatches, since if we've got protocol error
|
||||
@@ -334,13 +283,13 @@ impl<T: 'static> EventLoop<T> {
|
||||
// with an API to do that via some event.
|
||||
// Still, we set the exit code to the error's OS error code, or to 1 if not possible.
|
||||
let exit_code = error.raw_os_error().unwrap_or(1);
|
||||
self.control_flow = ControlFlow::ExitWithCode(exit_code);
|
||||
self.set_exit_code(exit_code);
|
||||
return;
|
||||
}
|
||||
|
||||
// NB: `StartCause::Init` is handled as a special case and doesn't need
|
||||
// to be considered here
|
||||
let cause = match self.control_flow {
|
||||
let cause = match self.control_flow() {
|
||||
ControlFlow::Poll => StartCause::Poll,
|
||||
ControlFlow::Wait => StartCause::WaitCancelled {
|
||||
start,
|
||||
@@ -359,11 +308,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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.
|
||||
@@ -378,14 +322,12 @@ impl<T: 'static> EventLoop<T> {
|
||||
self.single_iteration(&mut callback, cause);
|
||||
}
|
||||
|
||||
fn single_iteration<F>(&mut self, mut callback: &mut F, cause: StartCause)
|
||||
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
|
||||
{
|
||||
// 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
|
||||
@@ -394,33 +336,18 @@ impl<T: 'static> EventLoop<T> {
|
||||
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,
|
||||
);
|
||||
callback(Event::NewEvents(cause), &self.window_target);
|
||||
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though Wayland
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
if cause == StartCause::Init {
|
||||
sticky_exit_callback(
|
||||
Event::Resumed,
|
||||
&self.window_target,
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(Event::Resumed, &self.window_target);
|
||||
}
|
||||
|
||||
// Handle pending user events. We don't need back buffer, since we can't dispatch
|
||||
// user events indirectly via callback to the user.
|
||||
for user_event in self.pending_user_events.borrow_mut().drain(..) {
|
||||
sticky_exit_callback(
|
||||
Event::UserEvent(user_event),
|
||||
&self.window_target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
callback(Event::UserEvent(user_event), &self.window_target);
|
||||
}
|
||||
|
||||
// Drain the pending compositor updates.
|
||||
@@ -443,7 +370,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
let old_physical_size = physical_size;
|
||||
|
||||
let new_inner_size = Arc::new(Mutex::new(physical_size));
|
||||
sticky_exit_callback(
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
@@ -454,8 +381,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
},
|
||||
},
|
||||
&self.window_target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
@@ -497,26 +422,22 @@ impl<T: 'static> EventLoop<T> {
|
||||
physical_size
|
||||
});
|
||||
|
||||
sticky_exit_callback(
|
||||
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(
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::CloseRequested,
|
||||
},
|
||||
&self.window_target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -527,7 +448,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
for event in buffer_sink.drain() {
|
||||
let event = event.map_nonuser_event().unwrap();
|
||||
sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback);
|
||||
callback(event, &self.window_target);
|
||||
}
|
||||
|
||||
// Handle non-synthetic events.
|
||||
@@ -536,7 +457,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
for event in buffer_sink.drain() {
|
||||
let event = event.map_nonuser_event().unwrap();
|
||||
sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback);
|
||||
callback(event, &self.window_target);
|
||||
}
|
||||
|
||||
// Collect the window ids
|
||||
@@ -579,11 +500,12 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
|
||||
if request_redraw {
|
||||
sticky_exit_callback(
|
||||
Event::RedrawRequested(crate::window::WindowId(window_id)),
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
},
|
||||
&self.window_target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -594,14 +516,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
sticky_exit_callback(
|
||||
Event::AboutToWait,
|
||||
&self.window_target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
callback(Event::AboutToWait, &self.window_target);
|
||||
|
||||
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);
|
||||
@@ -655,6 +571,22 @@ impl<T: 'static> EventLoop<T> {
|
||||
))))
|
||||
})
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
self.window_target.p.control_flow()
|
||||
}
|
||||
|
||||
fn exiting(&self) -> bool {
|
||||
self.window_target.p.exiting()
|
||||
}
|
||||
|
||||
fn set_exit_code(&self, code: i32) {
|
||||
self.window_target.p.set_exit_code(code)
|
||||
}
|
||||
|
||||
fn exit_code(&self) -> Option<i32> {
|
||||
self.window_target.p.exit_code()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopWindowTarget<T> {
|
||||
@@ -664,6 +596,12 @@ pub struct EventLoopWindowTarget<T> {
|
||||
/// The main queue used by the event loop.
|
||||
pub queue_handle: QueueHandle<WinitState>,
|
||||
|
||||
/// The application's latest control_flow state
|
||||
pub(crate) control_flow: Cell<ControlFlow>,
|
||||
|
||||
/// The application's exit state.
|
||||
pub(crate) exit: Cell<Option<i32>>,
|
||||
|
||||
// TODO remove that RefCell once we can pass `&mut` in `Window::new`.
|
||||
/// Winit state.
|
||||
pub state: RefCell<WinitState>,
|
||||
@@ -678,10 +616,31 @@ pub struct EventLoopWindowTarget<T> {
|
||||
}
|
||||
|
||||
impl<T> EventLoopWindowTarget<T> {
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
let mut display_handle = WaylandDisplayHandle::empty();
|
||||
#[inline]
|
||||
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
let mut display_handle = rwh_05::WaylandDisplayHandle::empty();
|
||||
display_handle.display = self.connection.display().id().as_ptr() as *mut _;
|
||||
RawDisplayHandle::Wayland(display_handle)
|
||||
rwh_05::RawDisplayHandle::Wayland(display_handle)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
Ok(rwh_06::WaylandDisplayHandle::new({
|
||||
let ptr = self.connection.display().id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _).expect("wl_display should never be null")
|
||||
})
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,29 +3,51 @@ use sctk::reexports::client::Proxy;
|
||||
|
||||
use sctk::output::OutputData;
|
||||
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::platform_impl::platform::{
|
||||
MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode,
|
||||
};
|
||||
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
|
||||
use crate::event_loop::ControlFlow;
|
||||
use crate::platform_impl::platform::VideoMode as PlatformVideoMode;
|
||||
|
||||
use super::event_loop::EventLoopWindowTarget;
|
||||
|
||||
impl<T> EventLoopWindowTarget<T> {
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
|
||||
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
|
||||
self.state
|
||||
.borrow()
|
||||
.output_state
|
||||
.outputs()
|
||||
.map(MonitorHandle::new)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<PlatformMonitorHandle> {
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
// There's no primary monitor on Wayland.
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.control_flow.set(control_flow)
|
||||
}
|
||||
|
||||
pub(crate) fn control_flow(&self) -> ControlFlow {
|
||||
self.control_flow.get()
|
||||
}
|
||||
|
||||
pub(crate) fn exit(&self) {
|
||||
self.exit.set(Some(0))
|
||||
}
|
||||
|
||||
pub(crate) fn exiting(&self) -> bool {
|
||||
self.exit.get().is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn set_exit_code(&self, code: i32) {
|
||||
self.exit.set(Some(code))
|
||||
}
|
||||
|
||||
pub(crate) fn exit_code(&self) -> Option<i32> {
|
||||
self.exit.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -70,7 +92,18 @@ impl MonitorHandle {
|
||||
#[inline]
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
let output_data = self.proxy.data::<OutputData>().unwrap();
|
||||
output_data.with_output_info(|info| info.location).into()
|
||||
output_data.with_output_info(|info| {
|
||||
info.logical_position.map_or_else(
|
||||
|| {
|
||||
LogicalPosition::<i32>::from(info.location)
|
||||
.to_physical(info.scale_factor as f64)
|
||||
},
|
||||
|logical_position| {
|
||||
LogicalPosition::<i32>::from(logical_position)
|
||||
.to_physical(info.scale_factor as f64)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -157,7 +190,7 @@ impl VideoMode {
|
||||
self.refresh_rate_millihertz
|
||||
}
|
||||
|
||||
pub fn monitor(&self) -> PlatformMonitorHandle {
|
||||
PlatformMonitorHandle::Wayland(self.monitor.clone())
|
||||
pub fn monitor(&self) -> MonitorHandle {
|
||||
self.monitor.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,11 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
};
|
||||
|
||||
// Drop the repeat, if there were any.
|
||||
seat_state.keyboard_state.as_mut().unwrap().current_repeat = None;
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.current_repeat = None;
|
||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(token);
|
||||
}
|
||||
|
||||
// The keyboard focus is considered as general focus.
|
||||
state
|
||||
@@ -89,7 +93,11 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
|
||||
// NOTE: we should drop the repeat regardless whethere it was for the present
|
||||
// window of for the window which just went gone.
|
||||
seat_state.keyboard_state.as_mut().unwrap().current_repeat = None;
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.current_repeat = None;
|
||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(token);
|
||||
}
|
||||
|
||||
// NOTE: The check whether the window exists is essential as we might get a
|
||||
// nil surface, regardless of what protocol says.
|
||||
@@ -114,9 +122,9 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
}
|
||||
WlKeyboardEvent::Key {
|
||||
key,
|
||||
state: key_state,
|
||||
state: WEnum::Value(WlKeyState::Pressed),
|
||||
..
|
||||
} if key_state == WEnum::Value(WlKeyState::Pressed) => {
|
||||
} => {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
@@ -184,9 +192,9 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
}
|
||||
WlKeyboardEvent::Key {
|
||||
key,
|
||||
state: key_state,
|
||||
state: WEnum::Value(WlKeyState::Released),
|
||||
..
|
||||
} if key_state == WEnum::Value(WlKeyState::Released) => {
|
||||
} => {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
@@ -204,6 +212,9 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
&& Some(key) == keyboard_state.current_repeat
|
||||
{
|
||||
keyboard_state.current_repeat = None;
|
||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
WlKeyboardEvent::Modifiers {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use ahash::AHashMap;
|
||||
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
||||
@@ -29,7 +29,7 @@ use keyboard::{KeyboardData, KeyboardState};
|
||||
use text_input::TextInputData;
|
||||
use touch::TouchPoint;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct WinitSeatState {
|
||||
/// The pointer bound on the seat.
|
||||
pointer: Option<Arc<ThemedPointer<WinitPointerData>>>,
|
||||
@@ -38,7 +38,7 @@ pub struct WinitSeatState {
|
||||
touch: Option<WlTouch>,
|
||||
|
||||
/// The mapping from touched points to the surfaces they're present.
|
||||
touch_map: FnvHashMap<i32, TouchPoint>,
|
||||
touch_map: AHashMap<i32, TouchPoint>,
|
||||
|
||||
/// The text input bound on the seat.
|
||||
text_input: Option<Arc<ZwpTextInputV3>>,
|
||||
@@ -58,16 +58,7 @@ pub struct WinitSeatState {
|
||||
|
||||
impl WinitSeatState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pointer: None,
|
||||
touch: None,
|
||||
relative_pointer: None,
|
||||
text_input: None,
|
||||
touch_map: Default::default(),
|
||||
keyboard_state: None,
|
||||
modifiers: ModifiersState::empty(),
|
||||
modifiers_pending: false,
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,12 +88,14 @@ impl SeatHandler for WinitState {
|
||||
SeatCapability::Pointer if seat_state.pointer.is_none() => {
|
||||
let surface = self.compositor_state.create_surface(queue_handle);
|
||||
let surface_id = surface.id();
|
||||
let pointer_data = WinitPointerData::new(seat.clone(), surface);
|
||||
let pointer_data = WinitPointerData::new(seat.clone());
|
||||
let themed_pointer = self
|
||||
.seat_state
|
||||
.get_pointer_with_theme_and_data(
|
||||
queue_handle,
|
||||
&seat,
|
||||
self.shm.wl_shm(),
|
||||
surface,
|
||||
ThemeSpec::System,
|
||||
pointer_data,
|
||||
)
|
||||
@@ -167,7 +160,7 @@ impl SeatHandler for WinitState {
|
||||
let pointer_data = pointer.pointer().winit_data();
|
||||
|
||||
// Remove the cursor from the mapping.
|
||||
let surface_id = pointer_data.cursor_surface().id();
|
||||
let surface_id = pointer.surface().id();
|
||||
let _ = self.pointer_surfaces.remove(&surface_id);
|
||||
|
||||
// Remove the inner locks/confines before dropping the pointer.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use sctk::reexports::client::delegate_dispatch;
|
||||
use sctk::reexports::client::protocol::wl_pointer::WlPointer;
|
||||
@@ -10,15 +11,17 @@ use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle, Dispatch};
|
||||
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
|
||||
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1;
|
||||
use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1;
|
||||
use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1;
|
||||
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_pointer_constraints_v1::{Lifetime, ZwpPointerConstraintsV1};
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::csd_frame::FrameClick;
|
||||
|
||||
use sctk::compositor::SurfaceData;
|
||||
use sctk::globals::GlobalData;
|
||||
use sctk::seat::pointer::{PointerData, PointerDataExt};
|
||||
use sctk::seat::pointer::{PointerEvent, PointerEventKind, PointerHandler};
|
||||
use sctk::seat::SeatState;
|
||||
use sctk::shell::xdg::frame::FrameClick;
|
||||
|
||||
use crate::dpi::{LogicalPosition, PhysicalPosition};
|
||||
use crate::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent};
|
||||
@@ -67,35 +70,31 @@ impl PointerHandler for WinitState {
|
||||
PointerEventKind::Enter { .. } | PointerEventKind::Motion { .. }
|
||||
if parent_surface != surface =>
|
||||
{
|
||||
if let Some(icon) =
|
||||
window.frame_point_moved(seat, surface, event.position.0, event.position.1)
|
||||
{
|
||||
if let Some(icon) = window.frame_point_moved(
|
||||
seat,
|
||||
surface,
|
||||
Duration::ZERO,
|
||||
event.position.0,
|
||||
event.position.1,
|
||||
) {
|
||||
if let Some(pointer) = seat_state.pointer.as_ref() {
|
||||
let surface = pointer
|
||||
.pointer()
|
||||
.data::<WinitPointerData>()
|
||||
.unwrap()
|
||||
.cursor_surface();
|
||||
let scale_factor =
|
||||
surface.data::<SurfaceData>().unwrap().scale_factor();
|
||||
|
||||
let _ = pointer.set_cursor(
|
||||
connection,
|
||||
icon,
|
||||
self.shm.wl_shm(),
|
||||
surface,
|
||||
scale_factor,
|
||||
);
|
||||
let _ = pointer.set_cursor(connection, icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
PointerEventKind::Leave { .. } if parent_surface != surface => {
|
||||
window.frame_point_left();
|
||||
}
|
||||
ref kind @ PointerEventKind::Press { button, serial, .. }
|
||||
| ref kind @ PointerEventKind::Release { button, serial, .. }
|
||||
if parent_surface != surface =>
|
||||
{
|
||||
ref kind @ PointerEventKind::Press {
|
||||
button,
|
||||
serial,
|
||||
time,
|
||||
}
|
||||
| ref kind @ PointerEventKind::Release {
|
||||
button,
|
||||
serial,
|
||||
time,
|
||||
} if parent_surface != surface => {
|
||||
let click = match wayland_button_to_winit(button) {
|
||||
MouseButton::Left => FrameClick::Normal,
|
||||
MouseButton::Right => FrameClick::Alternate,
|
||||
@@ -109,6 +108,7 @@ impl PointerHandler for WinitState {
|
||||
pressed,
|
||||
seat,
|
||||
serial,
|
||||
Duration::from_millis(time as u64),
|
||||
window_id,
|
||||
&mut self.window_compositor_updates,
|
||||
);
|
||||
@@ -238,9 +238,6 @@ impl PointerHandler for WinitState {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WinitPointerData {
|
||||
/// The surface associated with this pointer, which is used for icons.
|
||||
cursor_surface: WlSurface,
|
||||
|
||||
/// The inner winit data associated with the pointer.
|
||||
inner: Mutex<WinitPointerDataInner>,
|
||||
|
||||
@@ -249,9 +246,8 @@ pub struct WinitPointerData {
|
||||
}
|
||||
|
||||
impl WinitPointerData {
|
||||
pub fn new(seat: WlSeat, surface: WlSurface) -> Self {
|
||||
pub fn new(seat: WlSeat) -> Self {
|
||||
Self {
|
||||
cursor_surface: surface,
|
||||
inner: Mutex::new(WinitPointerDataInner::default()),
|
||||
sctk_data: PointerData::new(seat),
|
||||
}
|
||||
@@ -313,11 +309,6 @@ impl WinitPointerData {
|
||||
self.sctk_data.seat()
|
||||
}
|
||||
|
||||
/// The WlSurface used to set cursor theme.
|
||||
pub fn cursor_surface(&self) -> &WlSurface {
|
||||
&self.cursor_surface
|
||||
}
|
||||
|
||||
/// Active window.
|
||||
pub fn focused_window(&self) -> Option<WindowId> {
|
||||
self.inner.lock().unwrap().surface
|
||||
@@ -325,7 +316,7 @@ impl WinitPointerData {
|
||||
|
||||
/// Last button serial.
|
||||
pub fn latest_button_serial(&self) -> u32 {
|
||||
self.inner.lock().unwrap().latest_button_serial
|
||||
self.sctk_data.latest_button_serial().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Last enter serial.
|
||||
@@ -341,12 +332,6 @@ impl WinitPointerData {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WinitPointerData {
|
||||
fn drop(&mut self) {
|
||||
self.cursor_surface.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerDataExt for WinitPointerData {
|
||||
fn pointer_data(&self) -> &PointerData {
|
||||
&self.sctk_data
|
||||
@@ -486,7 +471,35 @@ impl Dispatch<ZwpConfinedPointerV1, GlobalData, WinitState> for PointerConstrain
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WpCursorShapeDeviceV1, GlobalData, WinitState> for SeatState {
|
||||
fn event(
|
||||
_: &mut WinitState,
|
||||
_: &WpCursorShapeDeviceV1,
|
||||
_: <WpCursorShapeDeviceV1 as Proxy>::Event,
|
||||
_: &GlobalData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<WinitState>,
|
||||
) {
|
||||
unreachable!("wp_cursor_shape_manager has no events")
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WpCursorShapeManagerV1, GlobalData, WinitState> for SeatState {
|
||||
fn event(
|
||||
_: &mut WinitState,
|
||||
_: &WpCursorShapeManagerV1,
|
||||
_: <WpCursorShapeManagerV1 as Proxy>::Event,
|
||||
_: &GlobalData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<WinitState>,
|
||||
) {
|
||||
unreachable!("wp_cursor_device_manager has no events")
|
||||
}
|
||||
}
|
||||
|
||||
delegate_dispatch!(WinitState: [ WlPointer: WinitPointerData] => SeatState);
|
||||
delegate_dispatch!(WinitState: [ WpCursorShapeManagerV1: GlobalData] => SeatState);
|
||||
delegate_dispatch!(WinitState: [ WpCursorShapeDeviceV1: GlobalData] => SeatState);
|
||||
delegate_dispatch!(WinitState: [ZwpPointerConstraintsV1: GlobalData] => PointerConstraintsState);
|
||||
delegate_dispatch!(WinitState: [ZwpLockedPointerV1: GlobalData] => PointerConstraintsState);
|
||||
delegate_dispatch!(WinitState: [ZwpConfinedPointerV1: GlobalData] => PointerConstraintsState);
|
||||
|
||||
@@ -121,7 +121,7 @@ impl TouchHandler for WinitState {
|
||||
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||
DeviceId,
|
||||
)),
|
||||
phase: TouchPhase::Cancelled,
|
||||
phase: TouchPhase::Moved,
|
||||
location: touch_point.location.to_physical(scale_factor),
|
||||
force: None,
|
||||
id: id as u64,
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::cell::RefCell;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use ahash::AHashMap;
|
||||
|
||||
use sctk::reexports::calloop::LoopHandle;
|
||||
use sctk::reexports::client::backend::ObjectId;
|
||||
@@ -31,6 +31,7 @@ use super::seat::{
|
||||
PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData,
|
||||
WinitPointerDataExt, WinitSeatState,
|
||||
};
|
||||
use super::types::kwin_blur::KWinBlurManager;
|
||||
use super::types::wp_fractional_scaling::FractionalScalingManager;
|
||||
use super::types::wp_viewporter::ViewporterState;
|
||||
use super::types::xdg_activation::XdgActivationState;
|
||||
@@ -61,10 +62,10 @@ pub struct WinitState {
|
||||
pub xdg_shell: XdgShell,
|
||||
|
||||
/// The currently present windows.
|
||||
pub windows: RefCell<FnvHashMap<WindowId, Arc<Mutex<WindowState>>>>,
|
||||
pub windows: RefCell<AHashMap<WindowId, Arc<Mutex<WindowState>>>>,
|
||||
|
||||
/// The requests from the `Window` to EventLoop, such as close operations and redraw requests.
|
||||
pub window_requests: RefCell<FnvHashMap<WindowId, Arc<WindowRequests>>>,
|
||||
pub window_requests: RefCell<AHashMap<WindowId, Arc<WindowRequests>>>,
|
||||
|
||||
/// The events that were generated directly from the window.
|
||||
pub window_events_sink: Arc<Mutex<EventSink>>,
|
||||
@@ -73,10 +74,10 @@ pub struct WinitState {
|
||||
pub window_compositor_updates: Vec<WindowCompositorUpdate>,
|
||||
|
||||
/// Currently handled seats.
|
||||
pub seats: FnvHashMap<ObjectId, WinitSeatState>,
|
||||
pub seats: AHashMap<ObjectId, WinitSeatState>,
|
||||
|
||||
/// Currently present cursor surfaces.
|
||||
pub pointer_surfaces: FnvHashMap<ObjectId, Arc<ThemedPointer<WinitPointerData>>>,
|
||||
pub pointer_surfaces: AHashMap<ObjectId, Arc<ThemedPointer<WinitPointerData>>>,
|
||||
|
||||
/// The state of the text input on the client.
|
||||
pub text_input_state: Option<TextInputState>,
|
||||
@@ -103,6 +104,9 @@ pub struct WinitState {
|
||||
/// Fractional scaling manager.
|
||||
pub fractional_scaling_manager: Option<FractionalScalingManager>,
|
||||
|
||||
/// KWin blur manager.
|
||||
pub kwin_blur_manager: Option<KWinBlurManager>,
|
||||
|
||||
/// Loop handle to re-register event sources, such as keyboard repeat.
|
||||
pub loop_handle: LoopHandle<'static, Self>,
|
||||
|
||||
@@ -132,7 +136,7 @@ impl WinitState {
|
||||
|
||||
let seat_state = SeatState::new(globals, queue_handle);
|
||||
|
||||
let mut seats = FnvHashMap::default();
|
||||
let mut seats = AHashMap::default();
|
||||
for seat in seat_state.seats() {
|
||||
seats.insert(seat.id(), WinitSeatState::new());
|
||||
}
|
||||
@@ -161,6 +165,7 @@ impl WinitState {
|
||||
window_events_sink: Default::default(),
|
||||
viewporter_state,
|
||||
fractional_scaling_manager,
|
||||
kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(),
|
||||
|
||||
seats,
|
||||
text_input_state: TextInputState::new(globals, queue_handle).ok(),
|
||||
@@ -282,7 +287,12 @@ impl WindowHandler for WinitState {
|
||||
.expect("got configure for dead window.")
|
||||
.lock()
|
||||
.unwrap()
|
||||
.configure(configure, &self.shm, &self.subcompositor_state);
|
||||
.configure(
|
||||
configure,
|
||||
&self.shm,
|
||||
&self.subcompositor_state,
|
||||
&mut self.events_sink,
|
||||
);
|
||||
|
||||
self.window_compositor_updates[pos].size = Some(new_size);
|
||||
}
|
||||
@@ -320,6 +330,16 @@ impl OutputHandler for WinitState {
|
||||
}
|
||||
|
||||
impl CompositorHandler for WinitState {
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wayland_client::protocol::wl_surface::WlSurface,
|
||||
_: wayland_client::protocol::wl_output::Transform,
|
||||
) {
|
||||
// TODO(kchibisov) we need to expose it somehow in winit.
|
||||
}
|
||||
|
||||
fn scale_factor_changed(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
|
||||
70
src/platform_impl/linux/wayland/types/kwin_blur.rs
Normal file
70
src/platform_impl/linux/wayland/types/kwin_blur.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
//! Handling of KDE-compatible blur.
|
||||
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::Dispatch;
|
||||
use sctk::reexports::client::{delegate_dispatch, Connection, Proxy, QueueHandle};
|
||||
use wayland_protocols_plasma::blur::client::{
|
||||
org_kde_kwin_blur::OrgKdeKwinBlur, org_kde_kwin_blur_manager::OrgKdeKwinBlurManager,
|
||||
};
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
|
||||
/// KWin blur manager.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KWinBlurManager {
|
||||
manager: OrgKdeKwinBlurManager,
|
||||
}
|
||||
|
||||
impl KWinBlurManager {
|
||||
pub fn new(
|
||||
globals: &GlobalList,
|
||||
queue_handle: &QueueHandle<WinitState>,
|
||||
) -> Result<Self, BindError> {
|
||||
let manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
|
||||
Ok(Self { manager })
|
||||
}
|
||||
|
||||
pub fn blur(
|
||||
&self,
|
||||
surface: &WlSurface,
|
||||
queue_handle: &QueueHandle<WinitState>,
|
||||
) -> OrgKdeKwinBlur {
|
||||
self.manager.create(surface, queue_handle, ())
|
||||
}
|
||||
|
||||
pub fn unset(&self, surface: &WlSurface) {
|
||||
self.manager.unset(surface)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<OrgKdeKwinBlurManager, GlobalData, WinitState> for KWinBlurManager {
|
||||
fn event(
|
||||
_: &mut WinitState,
|
||||
_: &OrgKdeKwinBlurManager,
|
||||
_: <OrgKdeKwinBlurManager as Proxy>::Event,
|
||||
_: &GlobalData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<WinitState>,
|
||||
) {
|
||||
unreachable!("no events defined for org_kde_kwin_blur_manager");
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<OrgKdeKwinBlur, (), WinitState> for KWinBlurManager {
|
||||
fn event(
|
||||
_: &mut WinitState,
|
||||
_: &OrgKdeKwinBlur,
|
||||
_: <OrgKdeKwinBlur as Proxy>::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
_: &QueueHandle<WinitState>,
|
||||
) {
|
||||
unreachable!("no events defined for org_kde_kwin_blur");
|
||||
}
|
||||
}
|
||||
|
||||
delegate_dispatch!(WinitState: [OrgKdeKwinBlurManager: GlobalData] => KWinBlurManager);
|
||||
delegate_dispatch!(WinitState: [OrgKdeKwinBlur: ()] => KWinBlurManager);
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Wayland protocol implementation boilerplate.
|
||||
|
||||
pub mod kwin_blur;
|
||||
pub mod wp_fractional_scaling;
|
||||
pub mod wp_viewporter;
|
||||
pub mod xdg_activation;
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use raw_window_handle::{
|
||||
RawDisplayHandle, RawWindowHandle, WaylandDisplayHandle, WaylandWindowHandle,
|
||||
};
|
||||
|
||||
use sctk::reexports::calloop;
|
||||
use sctk::reexports::client::protocol::wl_display::WlDisplay;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
@@ -24,12 +20,12 @@ use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event::{Ime, WindowEvent};
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::platform_impl::{
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError,
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
|
||||
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
||||
};
|
||||
use crate::window::{
|
||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
};
|
||||
|
||||
use super::event_loop::sink::EventSink;
|
||||
@@ -57,6 +53,7 @@ pub struct Window {
|
||||
compositor: Arc<CompositorState>,
|
||||
|
||||
/// The wayland display used solely for raw window handle.
|
||||
#[allow(dead_code)]
|
||||
display: WlDisplay,
|
||||
|
||||
/// Xdg activation to request user attention.
|
||||
@@ -131,6 +128,8 @@ impl Window {
|
||||
// Set transparency hint.
|
||||
window_state.set_transparent(attributes.transparent);
|
||||
|
||||
window_state.set_blur(attributes.blur);
|
||||
|
||||
// Set the decorations hint.
|
||||
window_state.set_decorate(attributes.decorations);
|
||||
|
||||
@@ -375,6 +374,13 @@ impl Window {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, position: Position) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
self.window_state.lock().unwrap().show_window_menu(position);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
self.window_state
|
||||
@@ -409,6 +415,11 @@ impl Window {
|
||||
self.window_state.lock().unwrap().scale_factor()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_blur(&self, blur: bool) {
|
||||
self.window_state.lock().unwrap().set_blur(blur);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorate: bool) {
|
||||
self.window_state.lock().unwrap().set_decorate(decorate)
|
||||
@@ -419,6 +430,12 @@ impl Window {
|
||||
self.window_state.lock().unwrap().is_decorated()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_level(&self, _level: WindowLevel) {}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_window_icon(&self, _window_icon: Option<PlatformIcon>) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
// You can't unminimize the window on Wayland.
|
||||
@@ -612,6 +629,9 @@ impl Window {
|
||||
self.window_state.lock().unwrap().set_ime_purpose(purpose);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {}
|
||||
|
||||
#[inline]
|
||||
pub fn surface(&self) -> &WlSurface {
|
||||
self.window.wl_surface()
|
||||
@@ -629,23 +649,56 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<PlatformMonitorHandle> {
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
// XXX there's no such concept on Wayland.
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_04")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let mut window_handle = WaylandWindowHandle::empty();
|
||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||
let mut window_handle = rwh_04::WaylandHandle::empty();
|
||||
window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _;
|
||||
RawWindowHandle::Wayland(window_handle)
|
||||
window_handle.display = self.display.id().as_ptr() as *mut _;
|
||||
rwh_04::RawWindowHandle::Wayland(window_handle)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
let mut display_handle = WaylandDisplayHandle::empty();
|
||||
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
||||
let mut window_handle = rwh_05::WaylandWindowHandle::empty();
|
||||
window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _;
|
||||
rwh_05::RawWindowHandle::Wayland(window_handle)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
let mut display_handle = rwh_05::WaylandDisplayHandle::empty();
|
||||
display_handle.display = self.display.id().as_ptr() as *mut _;
|
||||
RawDisplayHandle::Wayland(display_handle)
|
||||
rwh_05::RawDisplayHandle::Wayland(display_handle)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::WaylandWindowHandle::new({
|
||||
let ptr = self.window.wl_surface().id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
|
||||
})
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::WaylandDisplayHandle::new({
|
||||
let ptr = self.display.id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
|
||||
})
|
||||
.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -658,6 +711,8 @@ impl Window {
|
||||
self.window_state.lock().unwrap().theme()
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
#[inline]
|
||||
pub fn title(&self) -> String {
|
||||
self.window_state.lock().unwrap().title().to_owned()
|
||||
|
||||
@@ -3,29 +3,37 @@
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
use log::warn;
|
||||
use log::{info, warn};
|
||||
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_shm::WlShm;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
use sctk::reexports::csd_frame::{
|
||||
DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowState as XdgWindowState,
|
||||
};
|
||||
use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
|
||||
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3;
|
||||
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
||||
use sctk::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge;
|
||||
use sctk::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge as XdgResizeEdge;
|
||||
|
||||
use sctk::compositor::{CompositorState, Region, SurfaceData};
|
||||
use sctk::compositor::{CompositorState, Region};
|
||||
use sctk::seat::pointer::ThemedPointer;
|
||||
use sctk::shell::xdg::frame::{DecorationsFrame, FrameAction, FrameClick};
|
||||
use sctk::shell::xdg::window::{DecorationMode, Window, WindowConfigure};
|
||||
use sctk::shell::xdg::XdgSurface;
|
||||
use sctk::shell::WaylandSurface;
|
||||
use sctk::shm::Shm;
|
||||
use sctk::subcompositor::SubcompositorState;
|
||||
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
||||
|
||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||
use crate::error::{ExternalError, NotSupportedError};
|
||||
use crate::event::WindowEvent;
|
||||
use crate::platform_impl::wayland::event_loop::sink::EventSink;
|
||||
use crate::platform_impl::wayland::make_wid;
|
||||
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
|
||||
use crate::platform_impl::WindowId;
|
||||
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};
|
||||
|
||||
@@ -37,7 +45,7 @@ use crate::platform_impl::wayland::state::{WindowCompositorUpdate, WinitState};
|
||||
#[cfg(feature = "sctk-adwaita")]
|
||||
pub type WinitFrame = sctk_adwaita::AdwaitaFrame<WinitState>;
|
||||
#[cfg(not(feature = "sctk-adwaita"))]
|
||||
pub type WinitFrame = sctk::shell::xdg::frame::fallback_frame::FallbackFrame<WinitState>;
|
||||
pub type WinitFrame = sctk::shell::xdg::fallback_frame::FallbackFrame<WinitState>;
|
||||
|
||||
// Minimum window inner size.
|
||||
const MIN_WINDOW_SIZE: LogicalSize<u32> = LogicalSize::new(2, 1);
|
||||
@@ -130,6 +138,8 @@ pub struct WindowState {
|
||||
|
||||
viewport: Option<WpViewport>,
|
||||
fractional_scale: Option<WpFractionalScaleV1>,
|
||||
blur: Option<OrgKdeKwinBlur>,
|
||||
blur_manager: Option<KWinBlurManager>,
|
||||
|
||||
/// Whether the client side decorations have pending move operations.
|
||||
///
|
||||
@@ -159,6 +169,8 @@ impl WindowState {
|
||||
.map(|fsm| fsm.fractional_scaling(window.wl_surface(), queue_handle));
|
||||
|
||||
Self {
|
||||
blur: None,
|
||||
blur_manager: winit_state.kwin_blur_manager.clone(),
|
||||
compositor,
|
||||
connection,
|
||||
csd_fails: false,
|
||||
@@ -239,6 +251,7 @@ impl WindowState {
|
||||
configure: WindowConfigure,
|
||||
shm: &Shm,
|
||||
subcompositor: &Arc<SubcompositorState>,
|
||||
event_sink: &mut EventSink,
|
||||
) -> LogicalSize<u32> {
|
||||
if configure.decoration_mode == DecorationMode::Client
|
||||
&& self.frame.is_none()
|
||||
@@ -254,6 +267,7 @@ impl WindowState {
|
||||
) {
|
||||
Ok(mut frame) => {
|
||||
frame.set_title(&self.title);
|
||||
frame.set_scaling_factor(self.scale_factor);
|
||||
// Hide the frame if we were asked to not decorate.
|
||||
frame.set_hidden(!self.decorate);
|
||||
self.frame = Some(frame);
|
||||
@@ -270,6 +284,19 @@ impl WindowState {
|
||||
|
||||
let stateless = Self::is_stateless(&configure);
|
||||
|
||||
// Emit `Occluded` event on suspension change.
|
||||
let occluded = configure.state.contains(XdgWindowState::SUSPENDED);
|
||||
if self
|
||||
.last_configure
|
||||
.as_ref()
|
||||
.map(|c| c.state.contains(XdgWindowState::SUSPENDED))
|
||||
.unwrap_or(false)
|
||||
!= occluded
|
||||
{
|
||||
let window_id = make_wid(self.window.wl_surface());
|
||||
event_sink.push_window_event(WindowEvent::Occluded(occluded), window_id);
|
||||
}
|
||||
|
||||
let new_size = if let Some(frame) = self.frame.as_mut() {
|
||||
// Configure the window states.
|
||||
frame.update_state(configure.state);
|
||||
@@ -336,23 +363,40 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Tells whether the window should be closed.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn frame_click(
|
||||
&mut self,
|
||||
click: FrameClick,
|
||||
pressed: bool,
|
||||
seat: &WlSeat,
|
||||
serial: u32,
|
||||
timestamp: Duration,
|
||||
window_id: WindowId,
|
||||
updates: &mut Vec<WindowCompositorUpdate>,
|
||||
) -> Option<bool> {
|
||||
match self.frame.as_mut()?.on_click(click, pressed)? {
|
||||
match self.frame.as_mut()?.on_click(timestamp, click, pressed)? {
|
||||
FrameAction::Minimize => self.window.set_minimized(),
|
||||
FrameAction::Maximize => self.window.set_maximized(),
|
||||
FrameAction::UnMaximize => self.window.unset_maximized(),
|
||||
FrameAction::Close => WinitState::queue_close(updates, window_id),
|
||||
FrameAction::Move => self.has_pending_move = Some(serial),
|
||||
FrameAction::Resize(edge) => self.window.resize(seat, serial, edge),
|
||||
FrameAction::Resize(edge) => {
|
||||
let edge = match edge {
|
||||
ResizeEdge::None => XdgResizeEdge::None,
|
||||
ResizeEdge::Top => XdgResizeEdge::Top,
|
||||
ResizeEdge::Bottom => XdgResizeEdge::Bottom,
|
||||
ResizeEdge::Left => XdgResizeEdge::Left,
|
||||
ResizeEdge::TopLeft => XdgResizeEdge::TopLeft,
|
||||
ResizeEdge::BottomLeft => XdgResizeEdge::BottomLeft,
|
||||
ResizeEdge::Right => XdgResizeEdge::Right,
|
||||
ResizeEdge::TopRight => XdgResizeEdge::TopRight,
|
||||
ResizeEdge::BottomRight => XdgResizeEdge::BottomRight,
|
||||
_ => return None,
|
||||
};
|
||||
self.window.resize(seat, serial, edge);
|
||||
}
|
||||
FrameAction::ShowMenu(x, y) => self.window.show_window_menu(seat, serial, (x, y)),
|
||||
_ => (),
|
||||
};
|
||||
|
||||
Some(false)
|
||||
@@ -369,14 +413,15 @@ impl WindowState {
|
||||
&mut self,
|
||||
seat: &WlSeat,
|
||||
surface: &WlSurface,
|
||||
timestamp: Duration,
|
||||
x: f64,
|
||||
y: f64,
|
||||
) -> Option<&str> {
|
||||
) -> Option<CursorIcon> {
|
||||
// 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() {
|
||||
let cursor = frame.click_point_moved(surface, x, y);
|
||||
let cursor = frame.click_point_moved(timestamp, &surface.id(), 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() {
|
||||
@@ -492,14 +537,12 @@ impl WindowState {
|
||||
/// Refresh the decorations frame if it's present returning whether the client should redraw.
|
||||
pub fn refresh_frame(&mut self) -> bool {
|
||||
if let Some(frame) = self.frame.as_mut() {
|
||||
let dirty = frame.is_dirty();
|
||||
if dirty {
|
||||
frame.draw();
|
||||
if frame.is_dirty() {
|
||||
return frame.draw();
|
||||
}
|
||||
dirty
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Reload the cursor style on the given window.
|
||||
@@ -586,20 +629,8 @@ impl WindowState {
|
||||
return;
|
||||
}
|
||||
|
||||
self.apply_on_poiner(|pointer, data| {
|
||||
let surface = data.cursor_surface();
|
||||
let scale_factor = surface.data::<SurfaceData>().unwrap().scale_factor();
|
||||
|
||||
if pointer
|
||||
.set_cursor(
|
||||
&self.connection,
|
||||
cursor_icon.name(),
|
||||
&self.shm,
|
||||
surface,
|
||||
scale_factor,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
self.apply_on_poiner(|pointer, _| {
|
||||
if pointer.set_cursor(&self.connection, cursor_icon).is_err() {
|
||||
warn!("Failed to set cursor to {:?}", cursor_icon);
|
||||
}
|
||||
})
|
||||
@@ -703,6 +734,15 @@ impl WindowState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn show_window_menu(&self, position: LogicalPosition<u32>) {
|
||||
// TODO(kchibisov) handle touch serials.
|
||||
self.apply_on_poiner(|_, data| {
|
||||
let serial = data.latest_button_serial();
|
||||
let seat = data.seat();
|
||||
self.window.show_window_menu(seat, serial, position.into());
|
||||
});
|
||||
}
|
||||
|
||||
/// Set the position of the cursor.
|
||||
pub fn set_cursor_position(&self, position: LogicalPosition<f64>) -> Result<(), ExternalError> {
|
||||
if self.pointer_constraints.is_none() {
|
||||
@@ -838,6 +878,30 @@ impl WindowState {
|
||||
if self.fractional_scale.is_none() {
|
||||
let _ = self.window.set_buffer_scale(self.scale_factor as _);
|
||||
}
|
||||
|
||||
if let Some(frame) = self.frame.as_mut() {
|
||||
frame.set_scaling_factor(scale_factor);
|
||||
}
|
||||
}
|
||||
|
||||
/// Make window background blurred
|
||||
#[inline]
|
||||
pub fn set_blur(&mut self, blurred: bool) {
|
||||
if blurred && self.blur.is_none() {
|
||||
if let Some(blur_manager) = self.blur_manager.as_ref() {
|
||||
let blur = blur_manager.blur(self.window.wl_surface(), &self.queue_handle);
|
||||
blur.commit();
|
||||
self.blur = Some(blur);
|
||||
} else {
|
||||
info!("Blur manager unavailable, unable to change blur")
|
||||
}
|
||||
} else if !blurred && self.blur.is_some() {
|
||||
self.blur_manager
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.unset(self.window.wl_surface());
|
||||
self.blur.take().unwrap().release();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the window title to a new value.
|
||||
@@ -900,6 +964,20 @@ impl Drop for WindowState {
|
||||
ManuallyDrop::drop(&mut self.window);
|
||||
}
|
||||
|
||||
// Cleanup objects.
|
||||
|
||||
if let Some(blur) = self.blur.take() {
|
||||
blur.release();
|
||||
}
|
||||
|
||||
if let Some(fs) = self.fractional_scale.take() {
|
||||
fs.destroy();
|
||||
}
|
||||
|
||||
if let Some(viewport) = self.viewport.take() {
|
||||
viewport.destroy();
|
||||
}
|
||||
|
||||
surface.destroy();
|
||||
}
|
||||
}
|
||||
@@ -935,17 +1013,17 @@ pub enum FrameCallbackState {
|
||||
Received,
|
||||
}
|
||||
|
||||
impl From<ResizeDirection> for ResizeEdge {
|
||||
impl From<ResizeDirection> for XdgResizeEdge {
|
||||
fn from(value: ResizeDirection) -> Self {
|
||||
match value {
|
||||
ResizeDirection::North => ResizeEdge::Top,
|
||||
ResizeDirection::West => ResizeEdge::Left,
|
||||
ResizeDirection::NorthWest => ResizeEdge::TopLeft,
|
||||
ResizeDirection::NorthEast => ResizeEdge::TopRight,
|
||||
ResizeDirection::East => ResizeEdge::Right,
|
||||
ResizeDirection::SouthWest => ResizeEdge::BottomLeft,
|
||||
ResizeDirection::SouthEast => ResizeEdge::BottomRight,
|
||||
ResizeDirection::South => ResizeEdge::Bottom,
|
||||
ResizeDirection::North => XdgResizeEdge::Top,
|
||||
ResizeDirection::West => XdgResizeEdge::Left,
|
||||
ResizeDirection::NorthWest => XdgResizeEdge::TopLeft,
|
||||
ResizeDirection::NorthEast => XdgResizeEdge::TopRight,
|
||||
ResizeDirection::East => XdgResizeEdge::Right,
|
||||
ResizeDirection::SouthWest => XdgResizeEdge::BottomLeft,
|
||||
ResizeDirection::SouthEast => XdgResizeEdge::BottomRight,
|
||||
ResizeDirection::South => XdgResizeEdge::Bottom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,18 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||
use x11rb::x11_utils::Serialize;
|
||||
use x11rb::{
|
||||
protocol::{
|
||||
xinput,
|
||||
xproto::{self, ConnectionExt as _},
|
||||
},
|
||||
x11_utils::ExtensionInformation,
|
||||
};
|
||||
|
||||
use super::{
|
||||
atoms::*, ffi, get_xtarget, mkdid, mkwid, monitor, util, CookieResultExt, Device, DeviceId,
|
||||
DeviceInfo, Dnd, DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow,
|
||||
WindowId, XExtension,
|
||||
atoms::*, ffi, get_xtarget, mkdid, mkwid, util, CookieResultExt, Device, DeviceId, DeviceInfo,
|
||||
Dnd, DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -35,10 +40,10 @@ pub(super) struct EventProcessor<T: 'static> {
|
||||
pub(super) dnd: Dnd,
|
||||
pub(super) ime_receiver: ImeReceiver,
|
||||
pub(super) ime_event_receiver: ImeEventReceiver,
|
||||
pub(super) randr_event_offset: c_int,
|
||||
pub(super) randr_event_offset: u8,
|
||||
pub(super) devices: RefCell<HashMap<DeviceId, Device>>,
|
||||
pub(super) xi2ext: XExtension,
|
||||
pub(super) xkbext: XExtension,
|
||||
pub(super) xi2ext: ExtensionInformation,
|
||||
pub(super) xkbext: ExtensionInformation,
|
||||
pub(super) target: Rc<RootELW<T>>,
|
||||
pub(super) kb_state: KbdState,
|
||||
// Number of touch events currently in progress
|
||||
@@ -55,12 +60,12 @@ pub(super) struct EventProcessor<T: 'static> {
|
||||
}
|
||||
|
||||
impl<T: 'static> EventProcessor<T> {
|
||||
pub(super) fn init_device(&self, device: c_int) {
|
||||
pub(super) fn init_device(&self, device: xinput::DeviceId) {
|
||||
let wt = get_xtarget(&self.target);
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
if let Some(info) = DeviceInfo::get(&wt.xconn, device) {
|
||||
if let Some(info) = DeviceInfo::get(&wt.xconn, device as _) {
|
||||
for info in info.iter() {
|
||||
devices.insert(DeviceId(info.deviceid), Device::new(info));
|
||||
devices.insert(DeviceId(info.deviceid as _), Device::new(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,12 +122,14 @@ impl<T: 'static> EventProcessor<T> {
|
||||
1
|
||||
}
|
||||
|
||||
let result = (wt.xconn.xlib.XCheckIfEvent)(
|
||||
wt.xconn.display,
|
||||
event_ptr,
|
||||
Some(predicate),
|
||||
std::ptr::null_mut(),
|
||||
);
|
||||
let result = unsafe {
|
||||
(wt.xconn.xlib.XCheckIfEvent)(
|
||||
wt.xconn.display,
|
||||
event_ptr,
|
||||
Some(predicate),
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
result != 0
|
||||
}
|
||||
@@ -420,7 +427,10 @@ impl<T: 'static> EventProcessor<T> {
|
||||
let last_scale_factor = shared_state_lock.last_monitor.scale_factor;
|
||||
let new_scale_factor = {
|
||||
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
|
||||
let monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
|
||||
let monitor = wt
|
||||
.xconn
|
||||
.get_monitor_for_window(Some(window_rect))
|
||||
.expect("Failed to find monitor for window");
|
||||
|
||||
if monitor.is_dummy() {
|
||||
// Avoid updating monitor using a dummy monitor handle
|
||||
@@ -576,7 +586,10 @@ impl<T: 'static> EventProcessor<T> {
|
||||
let window = xev.window as xproto::Window;
|
||||
let window_id = mkwid(window);
|
||||
|
||||
callback(Event::RedrawRequested(window_id));
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +606,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
};
|
||||
|
||||
let window_id = mkwid(window);
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD.into());
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
|
||||
let keycode = xkev.keycode as _;
|
||||
|
||||
@@ -669,7 +682,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
return;
|
||||
};
|
||||
let xev = &guard.cookie;
|
||||
if self.xi2ext.opcode != xev.extension {
|
||||
if self.xi2ext.major_opcode != xev.extension as u8 {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -688,7 +701,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
let window_id = mkwid(xev.event as xproto::Window);
|
||||
let device_id = mkdid(xev.deviceid);
|
||||
let device_id = mkdid(xev.deviceid as xinput::DeviceId);
|
||||
|
||||
// Set the timestamp.
|
||||
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||
@@ -732,7 +745,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
// Suppress emulated scroll wheel clicks, since we handle the real motion events for those.
|
||||
// In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in
|
||||
// turn) as axis motion, so we don't otherwise special-case these button presses.
|
||||
4 | 5 | 6 | 7 => {
|
||||
4..=7 => {
|
||||
if xev.flags & ffi::XIPointerEmulated == 0 {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
@@ -784,7 +797,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
// Set the timestamp.
|
||||
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||
|
||||
let device_id = mkdid(xev.deviceid);
|
||||
let device_id = mkdid(xev.deviceid as xinput::DeviceId);
|
||||
let window = xev.event as xproto::Window;
|
||||
let window_id = mkwid(window);
|
||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||
@@ -817,7 +830,9 @@ impl<T: 'static> EventProcessor<T> {
|
||||
)
|
||||
};
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) {
|
||||
let physical_device = match devices
|
||||
.get_mut(&DeviceId(xev.sourceid as xinput::DeviceId))
|
||||
{
|
||||
Some(device) => device,
|
||||
None => return,
|
||||
};
|
||||
@@ -829,7 +844,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
if let Some(&mut (_, ref mut info)) = physical_device
|
||||
.scroll_axes
|
||||
.iter_mut()
|
||||
.find(|&&mut (axis, _)| axis == i)
|
||||
.find(|&&mut (axis, _)| axis == i as _)
|
||||
{
|
||||
let delta = (x - info.position) / info.increment;
|
||||
info.position = x;
|
||||
@@ -876,9 +891,11 @@ impl<T: 'static> EventProcessor<T> {
|
||||
|
||||
let window = xev.event as xproto::Window;
|
||||
let window_id = mkwid(window);
|
||||
let device_id = mkdid(xev.deviceid);
|
||||
let device_id = mkdid(xev.deviceid as xinput::DeviceId);
|
||||
|
||||
if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) {
|
||||
if let Some(all_info) =
|
||||
DeviceInfo::get(&wt.xconn, super::ALL_DEVICES.into())
|
||||
{
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
for device_info in all_info.iter() {
|
||||
if device_info.deviceid == xev.sourceid
|
||||
@@ -888,7 +905,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
// the virtual device.
|
||||
|| device_info.attachment == xev.sourceid
|
||||
{
|
||||
let device_id = DeviceId(device_info.deviceid);
|
||||
let device_id = DeviceId(device_info.deviceid as _);
|
||||
if let Some(device) = devices.get_mut(&device_id) {
|
||||
device.reset_scroll_position(device_info);
|
||||
}
|
||||
@@ -927,7 +944,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window),
|
||||
event: CursorLeft {
|
||||
device_id: mkdid(xev.deviceid),
|
||||
device_id: mkdid(xev.deviceid as xinput::DeviceId),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -975,14 +992,14 @@ impl<T: 'static> EventProcessor<T> {
|
||||
let pointer_id = self
|
||||
.devices
|
||||
.borrow()
|
||||
.get(&DeviceId(xev.deviceid))
|
||||
.get(&DeviceId(xev.deviceid as xinput::DeviceId))
|
||||
.map(|device| device.attachment)
|
||||
.unwrap_or(2);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id: mkdid(pointer_id),
|
||||
device_id: mkdid(pointer_id as _),
|
||||
position,
|
||||
},
|
||||
});
|
||||
@@ -1073,7 +1090,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::CursorMoved {
|
||||
device_id: mkdid(util::VIRTUAL_CORE_POINTER.into()),
|
||||
device_id: mkdid(util::VIRTUAL_CORE_POINTER),
|
||||
position: location.cast(),
|
||||
},
|
||||
});
|
||||
@@ -1082,7 +1099,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Touch(Touch {
|
||||
device_id: mkdid(xev.deviceid),
|
||||
device_id: mkdid(xev.deviceid as xinput::DeviceId),
|
||||
phase,
|
||||
location,
|
||||
force: None, // TODO
|
||||
@@ -1100,7 +1117,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
|
||||
if xev.flags & ffi::XIPointerEmulated == 0 {
|
||||
callback(Event::DeviceEvent {
|
||||
device_id: mkdid(xev.deviceid),
|
||||
device_id: mkdid(xev.deviceid as xinput::DeviceId),
|
||||
event: DeviceEvent::Button {
|
||||
button: xev.detail as u32,
|
||||
state: match xev.evtype {
|
||||
@@ -1119,7 +1136,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
// Set the timestamp.
|
||||
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||
|
||||
let did = mkdid(xev.deviceid);
|
||||
let did = mkdid(xev.deviceid as xinput::DeviceId);
|
||||
|
||||
let mask = unsafe {
|
||||
slice::from_raw_parts(
|
||||
@@ -1179,7 +1196,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let device_id = mkdid(xev.sourceid);
|
||||
let device_id = mkdid(xev.sourceid as xinput::DeviceId);
|
||||
let keycode = xev.detail as u32;
|
||||
if keycode < KEYCODE_OFFSET as u32 {
|
||||
return;
|
||||
@@ -1205,19 +1222,19 @@ impl<T: 'static> EventProcessor<T> {
|
||||
unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) }
|
||||
{
|
||||
if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) {
|
||||
self.init_device(info.deviceid);
|
||||
self.init_device(info.deviceid as xinput::DeviceId);
|
||||
callback(Event::DeviceEvent {
|
||||
device_id: mkdid(info.deviceid),
|
||||
device_id: mkdid(info.deviceid as xinput::DeviceId),
|
||||
event: DeviceEvent::Added,
|
||||
});
|
||||
} else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved)
|
||||
{
|
||||
callback(Event::DeviceEvent {
|
||||
device_id: mkdid(info.deviceid),
|
||||
device_id: mkdid(info.deviceid as xinput::DeviceId),
|
||||
event: DeviceEvent::Removed,
|
||||
});
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
devices.remove(&DeviceId(info.deviceid));
|
||||
devices.remove(&DeviceId(info.deviceid as xinput::DeviceId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1226,7 +1243,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if event_type == self.xkbext.first_event_id {
|
||||
if event_type == self.xkbext.first_event as _ {
|
||||
let xev = unsafe { &*(xev as *const _ as *const ffi::XkbAnyEvent) };
|
||||
match xev.xkb_type {
|
||||
ffi::XkbNewKeyboardNotify => {
|
||||
@@ -1282,11 +1299,14 @@ impl<T: 'static> EventProcessor<T> {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if event_type == self.randr_event_offset {
|
||||
if event_type == self.randr_event_offset as c_int {
|
||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||
let prev_list = monitor::invalidate_cached_monitor_list();
|
||||
let prev_list = wt.xconn.invalidate_cached_monitor_list();
|
||||
if let Some(prev_list) = prev_list {
|
||||
let new_list = wt.xconn.available_monitors();
|
||||
let new_list = wt
|
||||
.xconn
|
||||
.available_monitors()
|
||||
.expect("Failed to get monitor list");
|
||||
for new_monitor in new_list {
|
||||
// Previous list may be empty, in case of disconnecting and
|
||||
// reconnecting the only one monitor. We still need to emit events in
|
||||
@@ -1299,7 +1319,8 @@ impl<T: 'static> EventProcessor<T> {
|
||||
for (window_id, window) in wt.windows.borrow().iter() {
|
||||
if let Some(window) = window.upgrade() {
|
||||
// Check if the window is on this monitor
|
||||
let monitor = window.current_monitor();
|
||||
let monitor =
|
||||
window.shared_state_lock().last_monitor.clone();
|
||||
if monitor.name == new_monitor.name {
|
||||
let (width, height) = window.inner_size_physical();
|
||||
let (new_width, new_height) = window.adjust_for_dpi(
|
||||
@@ -1416,7 +1437,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
) where
|
||||
F: FnMut(Event<T>),
|
||||
{
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD.into());
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
|
||||
// Update modifiers state and emit key events based on which keys are currently pressed.
|
||||
for keycode in wt
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use x11_dl::xmd::CARD32;
|
||||
pub use x11_dl::{
|
||||
error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*,
|
||||
xrandr::*, xrender::*,
|
||||
};
|
||||
|
||||
// Isn't defined by x11_dl
|
||||
|
||||
@@ -16,7 +16,7 @@ pub(crate) unsafe fn xim_set_callback(
|
||||
) -> Result<(), XError> {
|
||||
// It's advisable to wrap variadic FFI functions in our own functions, as we want to minimize
|
||||
// access that isn't type-checked.
|
||||
(xconn.xlib.XSetIMValues)(xim, field, callback, ptr::null_mut::<()>());
|
||||
unsafe { (xconn.xlib.XSetIMValues)(xim, field, callback, ptr::null_mut::<()>()) };
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
@@ -30,14 +30,16 @@ pub(crate) unsafe fn set_instantiate_callback(
|
||||
xconn: &Arc<XConnection>,
|
||||
client_data: ffi::XPointer,
|
||||
) -> Result<(), XError> {
|
||||
(xconn.xlib.XRegisterIMInstantiateCallback)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
Some(xim_instantiate_callback),
|
||||
client_data,
|
||||
);
|
||||
unsafe {
|
||||
(xconn.xlib.XRegisterIMInstantiateCallback)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
Some(xim_instantiate_callback),
|
||||
client_data,
|
||||
)
|
||||
};
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
@@ -45,14 +47,16 @@ pub(crate) unsafe fn unset_instantiate_callback(
|
||||
xconn: &Arc<XConnection>,
|
||||
client_data: ffi::XPointer,
|
||||
) -> Result<(), XError> {
|
||||
(xconn.xlib.XUnregisterIMInstantiateCallback)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
Some(xim_instantiate_callback),
|
||||
client_data,
|
||||
);
|
||||
unsafe {
|
||||
(xconn.xlib.XUnregisterIMInstantiateCallback)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
Some(xim_instantiate_callback),
|
||||
client_data,
|
||||
)
|
||||
};
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
@@ -61,12 +65,14 @@ pub(crate) unsafe fn set_destroy_callback(
|
||||
im: ffi::XIM,
|
||||
inner: &ImeInner,
|
||||
) -> Result<(), XError> {
|
||||
xim_set_callback(
|
||||
xconn,
|
||||
im,
|
||||
ffi::XNDestroyCallback_0.as_ptr() as *const _,
|
||||
&inner.destroy_callback as *const _ as *mut _,
|
||||
)
|
||||
unsafe {
|
||||
xim_set_callback(
|
||||
xconn,
|
||||
im,
|
||||
ffi::XNDestroyCallback_0.as_ptr() as *const _,
|
||||
&inner.destroy_callback as *const _ as *mut _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -82,14 +88,16 @@ enum ReplaceImError {
|
||||
// includes replacing all existing input contexts and free'ing resources as necessary. This only
|
||||
// modifies existing state if all operations succeed.
|
||||
unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
|
||||
let xconn = &(*inner).xconn;
|
||||
let xconn = unsafe { &(*inner).xconn };
|
||||
|
||||
let (new_im, is_fallback) = {
|
||||
let new_im = (*inner).potential_input_methods.open_im(xconn, None);
|
||||
let new_im = unsafe { (*inner).potential_input_methods.open_im(xconn, None) };
|
||||
let is_fallback = new_im.is_fallback();
|
||||
(
|
||||
new_im.ok().ok_or_else(|| {
|
||||
ReplaceImError::MethodOpenFailed(Box::new((*inner).potential_input_methods.clone()))
|
||||
ReplaceImError::MethodOpenFailed(Box::new(unsafe {
|
||||
(*inner).potential_input_methods.clone()
|
||||
}))
|
||||
})?,
|
||||
is_fallback,
|
||||
)
|
||||
@@ -98,16 +106,16 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
|
||||
// It's important to always set a destroy callback, since there's otherwise potential for us
|
||||
// to try to use or free a resource that's already been destroyed on the server.
|
||||
{
|
||||
let result = set_destroy_callback(xconn, new_im.im, &*inner);
|
||||
let result = unsafe { set_destroy_callback(xconn, new_im.im, &*inner) };
|
||||
if result.is_err() {
|
||||
let _ = close_im(xconn, new_im.im);
|
||||
let _ = unsafe { close_im(xconn, new_im.im) };
|
||||
}
|
||||
result
|
||||
}
|
||||
.map_err(ReplaceImError::SetDestroyCallbackFailed)?;
|
||||
|
||||
let mut new_contexts = HashMap::new();
|
||||
for (window, old_context) in (*inner).contexts.iter() {
|
||||
for (window, old_context) in unsafe { (*inner).contexts.iter() } {
|
||||
let spot = old_context.as_ref().map(|old_context| old_context.ic_spot);
|
||||
|
||||
// Check if the IME was allowed on that context.
|
||||
@@ -125,16 +133,18 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
|
||||
};
|
||||
|
||||
let new_context = {
|
||||
let result = ImeContext::new(
|
||||
xconn,
|
||||
new_im.im,
|
||||
style,
|
||||
*window,
|
||||
spot,
|
||||
(*inner).event_sender.clone(),
|
||||
);
|
||||
let result = unsafe {
|
||||
ImeContext::new(
|
||||
xconn,
|
||||
new_im.im,
|
||||
style,
|
||||
*window,
|
||||
spot,
|
||||
(*inner).event_sender.clone(),
|
||||
)
|
||||
};
|
||||
if result.is_err() {
|
||||
let _ = close_im(xconn, new_im.im);
|
||||
let _ = unsafe { close_im(xconn, new_im.im) };
|
||||
}
|
||||
result.map_err(ReplaceImError::ContextCreationFailed)?
|
||||
};
|
||||
@@ -142,12 +152,14 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
|
||||
}
|
||||
|
||||
// If we've made it this far, everything succeeded.
|
||||
let _ = (*inner).destroy_all_contexts_if_necessary();
|
||||
let _ = (*inner).close_im_if_necessary();
|
||||
(*inner).im = Some(new_im);
|
||||
(*inner).contexts = new_contexts;
|
||||
(*inner).is_destroyed = false;
|
||||
(*inner).is_fallback = is_fallback;
|
||||
unsafe {
|
||||
let _ = (*inner).destroy_all_contexts_if_necessary();
|
||||
let _ = (*inner).close_im_if_necessary();
|
||||
(*inner).im = Some(new_im);
|
||||
(*inner).contexts = new_contexts;
|
||||
(*inner).is_destroyed = false;
|
||||
(*inner).is_fallback = is_fallback;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -159,18 +171,18 @@ pub unsafe extern "C" fn xim_instantiate_callback(
|
||||
) {
|
||||
let inner: *mut ImeInner = client_data as _;
|
||||
if !inner.is_null() {
|
||||
let xconn = &(*inner).xconn;
|
||||
match replace_im(inner) {
|
||||
Ok(()) => {
|
||||
let xconn = unsafe { &(*inner).xconn };
|
||||
match unsafe { replace_im(inner) } {
|
||||
Ok(()) => unsafe {
|
||||
let _ = unset_instantiate_callback(xconn, client_data);
|
||||
(*inner).is_fallback = false;
|
||||
}
|
||||
Err(err) => {
|
||||
},
|
||||
Err(err) => unsafe {
|
||||
if (*inner).is_destroyed {
|
||||
// We have no usable input methods!
|
||||
panic!("Failed to reopen input method: {err:?}");
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,13 +198,13 @@ pub unsafe extern "C" fn xim_destroy_callback(
|
||||
) {
|
||||
let inner: *mut ImeInner = client_data as _;
|
||||
if !inner.is_null() {
|
||||
(*inner).is_destroyed = true;
|
||||
let xconn = &(*inner).xconn;
|
||||
if !(*inner).is_fallback {
|
||||
let _ = set_instantiate_callback(xconn, client_data);
|
||||
unsafe { (*inner).is_destroyed = true };
|
||||
let xconn = unsafe { &(*inner).xconn };
|
||||
if unsafe { !(*inner).is_fallback } {
|
||||
let _ = unsafe { set_instantiate_callback(xconn, client_data) };
|
||||
// Attempt to open fallback input method.
|
||||
match replace_im(inner) {
|
||||
Ok(()) => (*inner).is_fallback = true,
|
||||
match unsafe { replace_im(inner) } {
|
||||
Ok(()) => unsafe { (*inner).is_fallback = true },
|
||||
Err(err) => {
|
||||
// We have no usable input methods!
|
||||
panic!("Failed to open fallback input method: {err:?}");
|
||||
|
||||
@@ -217,15 +217,19 @@ impl ImeContext {
|
||||
}));
|
||||
|
||||
let ic = match style as _ {
|
||||
Style::Preedit(style) => ImeContext::create_preedit_ic(
|
||||
xconn,
|
||||
im,
|
||||
style,
|
||||
window,
|
||||
client_data as ffi::XPointer,
|
||||
),
|
||||
Style::Nothing(style) => ImeContext::create_nothing_ic(xconn, im, style, window),
|
||||
Style::None(style) => ImeContext::create_none_ic(xconn, im, style, window),
|
||||
Style::Preedit(style) => unsafe {
|
||||
ImeContext::create_preedit_ic(
|
||||
xconn,
|
||||
im,
|
||||
style,
|
||||
window,
|
||||
client_data as ffi::XPointer,
|
||||
)
|
||||
},
|
||||
Style::Nothing(style) => unsafe {
|
||||
ImeContext::create_nothing_ic(xconn, im, style, window)
|
||||
},
|
||||
Style::None(style) => unsafe { ImeContext::create_none_ic(xconn, im, style, window) },
|
||||
}
|
||||
.ok_or(ImeContextCreationError::Null)?;
|
||||
|
||||
@@ -237,7 +241,7 @@ impl ImeContext {
|
||||
ic,
|
||||
ic_spot: ffi::XPoint { x: 0, y: 0 },
|
||||
style,
|
||||
_client_data: Box::from_raw(client_data),
|
||||
_client_data: unsafe { Box::from_raw(client_data) },
|
||||
};
|
||||
|
||||
// Set the spot location, if it's present.
|
||||
@@ -254,14 +258,16 @@ impl ImeContext {
|
||||
style: XIMStyle,
|
||||
window: ffi::Window,
|
||||
) -> Option<ffi::XIC> {
|
||||
let ic = (xconn.xlib.XCreateIC)(
|
||||
im,
|
||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||
style,
|
||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||
window,
|
||||
ptr::null_mut::<()>(),
|
||||
);
|
||||
let ic = unsafe {
|
||||
(xconn.xlib.XCreateIC)(
|
||||
im,
|
||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||
style,
|
||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||
window,
|
||||
ptr::null_mut::<()>(),
|
||||
)
|
||||
};
|
||||
|
||||
(!ic.is_null()).then_some(ic)
|
||||
}
|
||||
@@ -274,8 +280,7 @@ impl ImeContext {
|
||||
client_data: ffi::XPointer,
|
||||
) -> Option<ffi::XIC> {
|
||||
let preedit_callbacks = PreeditCallbacks::new(client_data);
|
||||
let preedit_attr = util::memory::XSmartPointer::new(
|
||||
xconn,
|
||||
let preedit_attr = util::memory::XSmartPointer::new(xconn, unsafe {
|
||||
(xconn.xlib.XVaCreateNestedList)(
|
||||
0,
|
||||
ffi::XNPreeditStartCallback_0.as_ptr() as *const _,
|
||||
@@ -287,20 +292,22 @@ impl ImeContext {
|
||||
ffi::XNPreeditDrawCallback_0.as_ptr() as *const _,
|
||||
&(preedit_callbacks.draw_callback) as *const _,
|
||||
ptr::null_mut::<()>(),
|
||||
),
|
||||
)
|
||||
)
|
||||
})
|
||||
.expect("XVaCreateNestedList returned NULL");
|
||||
|
||||
let ic = (xconn.xlib.XCreateIC)(
|
||||
im,
|
||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||
style,
|
||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||
window,
|
||||
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
|
||||
preedit_attr.ptr,
|
||||
ptr::null_mut::<()>(),
|
||||
);
|
||||
let ic = unsafe {
|
||||
(xconn.xlib.XCreateIC)(
|
||||
im,
|
||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||
style,
|
||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||
window,
|
||||
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
|
||||
preedit_attr.ptr,
|
||||
ptr::null_mut::<()>(),
|
||||
)
|
||||
};
|
||||
|
||||
(!ic.is_null()).then_some(ic)
|
||||
}
|
||||
@@ -311,14 +318,16 @@ impl ImeContext {
|
||||
style: XIMStyle,
|
||||
window: ffi::Window,
|
||||
) -> Option<ffi::XIC> {
|
||||
let ic = (xconn.xlib.XCreateIC)(
|
||||
im,
|
||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||
style,
|
||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||
window,
|
||||
ptr::null_mut::<()>(),
|
||||
);
|
||||
let ic = unsafe {
|
||||
(xconn.xlib.XCreateIC)(
|
||||
im,
|
||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||
style,
|
||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||
window,
|
||||
ptr::null_mut::<()>(),
|
||||
)
|
||||
};
|
||||
|
||||
(!ic.is_null()).then_some(ic)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ use super::{
|
||||
use crate::platform_impl::platform::x11::ime::ImeEventSender;
|
||||
|
||||
pub(crate) unsafe fn close_im(xconn: &Arc<XConnection>, im: ffi::XIM) -> Result<(), XError> {
|
||||
(xconn.xlib.XCloseIM)(im);
|
||||
unsafe { (xconn.xlib.XCloseIM)(im) };
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn destroy_ic(xconn: &Arc<XConnection>, ic: ffi::XIC) -> Result<(), XError> {
|
||||
(xconn.xlib.XDestroyIC)(ic);
|
||||
unsafe { (xconn.xlib.XDestroyIC)(ic) };
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ impl ImeInner {
|
||||
|
||||
pub unsafe fn close_im_if_necessary(&self) -> Result<bool, XError> {
|
||||
if !self.is_destroyed && self.im.is_some() {
|
||||
close_im(&self.xconn, self.im.as_ref().unwrap().im).map(|_| true)
|
||||
unsafe { close_im(&self.xconn, self.im.as_ref().unwrap().im) }.map(|_| true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ impl ImeInner {
|
||||
|
||||
pub unsafe fn destroy_ic_if_necessary(&self, ic: ffi::XIC) -> Result<bool, XError> {
|
||||
if !self.is_destroyed {
|
||||
destroy_ic(&self.xconn, ic).map(|_| true)
|
||||
unsafe { destroy_ic(&self.xconn, ic) }.map(|_| true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
@@ -68,7 +68,7 @@ impl ImeInner {
|
||||
|
||||
pub unsafe fn destroy_all_contexts_if_necessary(&self) -> Result<bool, XError> {
|
||||
for context in self.contexts.values().flatten() {
|
||||
self.destroy_ic_if_necessary(context.ic)?;
|
||||
unsafe { self.destroy_ic_if_necessary(context.ic)? };
|
||||
}
|
||||
Ok(!self.is_destroyed)
|
||||
}
|
||||
|
||||
@@ -21,14 +21,16 @@ unsafe fn open_im(xconn: &Arc<XConnection>, locale_modifiers: &CStr) -> Option<f
|
||||
// * The new locale modifiers if we succeeded in setting them.
|
||||
// * NULL if the locale modifiers string is malformed or if the
|
||||
// current locale is not supported by Xlib.
|
||||
(xconn.xlib.XSetLocaleModifiers)(locale_modifiers.as_ptr());
|
||||
unsafe { (xconn.xlib.XSetLocaleModifiers)(locale_modifiers.as_ptr()) };
|
||||
|
||||
let im = (xconn.xlib.XOpenIM)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
let im = unsafe {
|
||||
(xconn.xlib.XOpenIM)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
if im.is_null() {
|
||||
None
|
||||
@@ -178,7 +180,7 @@ unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXi
|
||||
let atoms = xconn.atoms();
|
||||
let servers_atom = atoms[XIM_SERVERS];
|
||||
|
||||
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
||||
let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) };
|
||||
|
||||
let mut atoms: Vec<ffi::Atom> = xconn
|
||||
.get_property::<xproto::Atom>(
|
||||
@@ -192,21 +194,23 @@ unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXi
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len());
|
||||
(xconn.xlib.XGetAtomNames)(
|
||||
xconn.display,
|
||||
atoms.as_mut_ptr(),
|
||||
atoms.len() as _,
|
||||
names.as_mut_ptr() as _,
|
||||
);
|
||||
names.set_len(atoms.len());
|
||||
unsafe {
|
||||
(xconn.xlib.XGetAtomNames)(
|
||||
xconn.display,
|
||||
atoms.as_mut_ptr(),
|
||||
atoms.len() as _,
|
||||
names.as_mut_ptr() as _,
|
||||
)
|
||||
};
|
||||
unsafe { names.set_len(atoms.len()) };
|
||||
|
||||
let mut formatted_names = Vec::with_capacity(names.len());
|
||||
for name in names {
|
||||
let string = CStr::from_ptr(name)
|
||||
let string = unsafe { CStr::from_ptr(name) }
|
||||
.to_owned()
|
||||
.into_string()
|
||||
.map_err(GetXimServersError::InvalidUtf8)?;
|
||||
(xconn.xlib.XFree)(name as _);
|
||||
unsafe { (xconn.xlib.XFree)(name as _) };
|
||||
formatted_names.push(string.replace("@server=", "@im="));
|
||||
}
|
||||
xconn.check_errors().map_err(GetXimServersError::XError)?;
|
||||
|
||||
@@ -28,15 +28,15 @@ use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
ffi::CStr,
|
||||
fmt,
|
||||
mem::{self, MaybeUninit},
|
||||
mem::MaybeUninit,
|
||||
ops::Deref,
|
||||
os::{
|
||||
raw::*,
|
||||
unix::io::{AsRawFd, RawFd},
|
||||
unix::io::{AsRawFd, BorrowedFd},
|
||||
},
|
||||
ptr,
|
||||
rc::Rc,
|
||||
slice,
|
||||
slice, str,
|
||||
sync::mpsc::{Receiver, Sender, TryRecvError},
|
||||
sync::{mpsc, Arc, Weak},
|
||||
time::{Duration, Instant},
|
||||
@@ -45,13 +45,16 @@ use std::{
|
||||
use libc::{self, setlocale, LC_CTYPE};
|
||||
|
||||
use atoms::*;
|
||||
use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle};
|
||||
|
||||
use x11rb::protocol::{
|
||||
xinput,
|
||||
xproto::{self, ConnectionExt},
|
||||
};
|
||||
use x11rb::x11_utils::X11Error as LogicalError;
|
||||
use x11rb::{
|
||||
connection::RequestConnection,
|
||||
protocol::{
|
||||
xinput::{self, ConnectionExt as _},
|
||||
xkb,
|
||||
xproto::{self, ConnectionExt as _},
|
||||
},
|
||||
};
|
||||
use x11rb::{
|
||||
errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError},
|
||||
xcb_ffi::ReplyOrIdError,
|
||||
@@ -62,20 +65,24 @@ use self::{
|
||||
event_processor::EventProcessor,
|
||||
ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender},
|
||||
};
|
||||
use super::{common::xkb_state::KbdState, OsError};
|
||||
use super::{common::xkb_state::KbdState, ControlFlow, OsError};
|
||||
use crate::{
|
||||
error::{EventLoopError, OsError as RootOsError},
|
||||
event::{Event, StartCause},
|
||||
event_loop::{ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::{DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
platform::pump_events::PumpStatus,
|
||||
platform_impl::{
|
||||
platform::{min_timeout, sticky_exit_callback, WindowId},
|
||||
platform::{min_timeout, WindowId},
|
||||
PlatformSpecificWindowBuilderAttributes,
|
||||
},
|
||||
window::WindowAttributes,
|
||||
};
|
||||
|
||||
type X11Source = Generic<RawFd>;
|
||||
// Xinput constants not defined in x11rb
|
||||
const ALL_DEVICES: u16 = 0;
|
||||
const ALL_MASTER_DEVICES: u16 = 1;
|
||||
|
||||
type X11Source = Generic<BorrowedFd<'static>>;
|
||||
|
||||
struct WakeSender<T> {
|
||||
sender: Sender<T>,
|
||||
@@ -140,6 +147,8 @@ pub struct EventLoopWindowTarget<T> {
|
||||
wm_delete_window: xproto::Atom,
|
||||
net_wm_ping: xproto::Atom,
|
||||
ime_sender: ImeSender,
|
||||
control_flow: Cell<ControlFlow>,
|
||||
exit: Cell<Option<i32>>,
|
||||
root: xproto::Window,
|
||||
ime: RefCell<Ime>,
|
||||
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
|
||||
@@ -151,7 +160,6 @@ pub struct EventLoopWindowTarget<T> {
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
loop_running: bool,
|
||||
control_flow: ControlFlow,
|
||||
event_loop: Loop<'static, EventLoopState>,
|
||||
waker: calloop::ping::Ping,
|
||||
event_processor: EventProcessor<T>,
|
||||
@@ -229,71 +237,27 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
|
||||
let randr_event_offset = xconn
|
||||
.select_xrandr_input(root as ffi::Window)
|
||||
.select_xrandr_input(root)
|
||||
.expect("Failed to query XRandR extension");
|
||||
|
||||
let xi2ext = unsafe {
|
||||
let mut ext = XExtension::default();
|
||||
let xi2ext = xconn
|
||||
.xcb_connection()
|
||||
.extension_information(xinput::X11_EXTENSION_NAME)
|
||||
.expect("Failed to query XInput extension")
|
||||
.expect("X server missing XInput extension");
|
||||
let xkbext = xconn
|
||||
.xcb_connection()
|
||||
.extension_information(xkb::X11_EXTENSION_NAME)
|
||||
.expect("Failed to query XKB extension")
|
||||
.expect("X server missing XKB extension");
|
||||
|
||||
let res = (xconn.xlib.XQueryExtension)(
|
||||
xconn.display,
|
||||
b"XInputExtension\0".as_ptr() as *const c_char,
|
||||
&mut ext.opcode,
|
||||
&mut ext.first_event_id,
|
||||
&mut ext.first_error_id,
|
||||
);
|
||||
|
||||
if res == ffi::False {
|
||||
panic!("X server missing XInput extension");
|
||||
}
|
||||
|
||||
ext
|
||||
};
|
||||
|
||||
let xkbext = {
|
||||
let mut ext = XExtension::default();
|
||||
|
||||
let res = unsafe {
|
||||
(xconn.xlib.XkbQueryExtension)(
|
||||
xconn.display,
|
||||
&mut ext.opcode,
|
||||
&mut ext.first_event_id,
|
||||
&mut ext.first_error_id,
|
||||
&mut 1,
|
||||
&mut 0,
|
||||
)
|
||||
};
|
||||
|
||||
if res == ffi::False {
|
||||
panic!("X server missing XKB extension");
|
||||
}
|
||||
|
||||
// Enable detectable auto repeat.
|
||||
let mut supported = 0;
|
||||
unsafe {
|
||||
(xconn.xlib.XkbSetDetectableAutoRepeat)(xconn.display, 1, &mut supported);
|
||||
}
|
||||
if supported == 0 {
|
||||
warn!("Detectable auto repeart is not supported");
|
||||
}
|
||||
|
||||
ext
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut xinput_major_ver = ffi::XI_2_Major;
|
||||
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
||||
if (xconn.xinput2.XIQueryVersion)(
|
||||
xconn.display,
|
||||
&mut xinput_major_ver,
|
||||
&mut xinput_minor_ver,
|
||||
) != ffi::Success as std::os::raw::c_int
|
||||
{
|
||||
panic!(
|
||||
"X server has XInput extension {xinput_major_ver}.{xinput_minor_ver} but does not support XInput2",
|
||||
);
|
||||
}
|
||||
}
|
||||
// Check for XInput2 support.
|
||||
xconn
|
||||
.xcb_connection()
|
||||
.xinput_xi_query_version(2, 3)
|
||||
.expect("Failed to send XInput2 query version request")
|
||||
.reply()
|
||||
.expect("Error while checking for XInput2 query version reply");
|
||||
|
||||
xconn.update_cached_wm_info(root);
|
||||
|
||||
@@ -304,7 +268,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
// Create the X11 event dispatcher.
|
||||
let source = X11Source::new(
|
||||
xconn.xcb_connection().as_raw_fd(),
|
||||
// SAFETY: xcb owns the FD and outlives the source.
|
||||
unsafe { BorrowedFd::borrow_raw(xconn.xcb_connection().as_raw_fd()) },
|
||||
calloop::Interest::READ,
|
||||
calloop::Mode::Level,
|
||||
);
|
||||
@@ -339,6 +304,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
let window_target = EventLoopWindowTarget {
|
||||
ime,
|
||||
root,
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
exit: Cell::new(None),
|
||||
windows: Default::default(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
ime_sender,
|
||||
@@ -387,7 +354,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
.xconn
|
||||
.select_xinput_events(
|
||||
root,
|
||||
ffi::XIAllDevices as _,
|
||||
ALL_DEVICES,
|
||||
x11rb::protocol::xinput::XIEventMask::HIERARCHY,
|
||||
)
|
||||
.expect_then_ignore_error("Failed to register for XInput2 device hotplug events");
|
||||
@@ -396,15 +363,14 @@ impl<T: 'static> EventLoop<T> {
|
||||
.xconn
|
||||
.select_xkb_events(
|
||||
0x100, // Use the "core keyboard device"
|
||||
ffi::XkbNewKeyboardNotifyMask | ffi::XkbStateNotifyMask,
|
||||
xkb::EventType::NEW_KEYBOARD_NOTIFY | xkb::EventType::STATE_NOTIFY,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
event_processor.init_device(ffi::XIAllDevices);
|
||||
event_processor.init_device(ALL_DEVICES);
|
||||
|
||||
EventLoop {
|
||||
loop_running: false,
|
||||
control_flow: ControlFlow::default(),
|
||||
event_loop,
|
||||
waker,
|
||||
event_processor,
|
||||
@@ -432,9 +398,9 @@ impl<T: 'static> EventLoop<T> {
|
||||
&self.target
|
||||
}
|
||||
|
||||
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootELW<T>),
|
||||
{
|
||||
if self.loop_running {
|
||||
return Err(EventLoopError::AlreadyRunning);
|
||||
@@ -455,7 +421,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
};
|
||||
|
||||
// Applications aren't allowed to carry windows between separate
|
||||
// `run_ondemand` calls but if they have only just dropped their
|
||||
// `run_on_demand` calls but if they have only just dropped their
|
||||
// windows we need to make sure those last requests are sent to the
|
||||
// X Server.
|
||||
let wt = get_xtarget(&self.target);
|
||||
@@ -468,35 +434,24 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootELW<T>),
|
||||
{
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
// Reset the internal state for the loop as we start running to
|
||||
// ensure consistent behaviour in case the loop runs and exits more
|
||||
// than once.
|
||||
self.control_flow = ControlFlow::Poll;
|
||||
|
||||
// run the initial loop iteration
|
||||
self.single_iteration(&mut callback, StartCause::Init);
|
||||
}
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit.
|
||||
if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) {
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
}
|
||||
if let ControlFlow::ExitWithCode(code) = self.control_flow {
|
||||
if let Some(code) = self.exit_code() {
|
||||
self.loop_running = false;
|
||||
|
||||
let mut dummy = self.control_flow;
|
||||
sticky_exit_callback(
|
||||
Event::LoopExiting,
|
||||
self.window_target(),
|
||||
&mut dummy,
|
||||
&mut callback,
|
||||
);
|
||||
callback(Event::LoopExiting, self.window_target());
|
||||
|
||||
PumpStatus::Exit(code)
|
||||
} else {
|
||||
@@ -512,7 +467,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootELW<T>),
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
||||
@@ -522,17 +477,12 @@ impl<T: 'static> EventLoop<T> {
|
||||
// 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 {
|
||||
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))
|
||||
}
|
||||
// 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)
|
||||
@@ -546,25 +496,13 @@ impl<T: 'static> EventLoop<T> {
|
||||
{
|
||||
log::error!("Failed to poll for events: {error:?}");
|
||||
let exit_code = error.raw_os_error().unwrap_or(1);
|
||||
self.control_flow = ControlFlow::ExitWithCode(exit_code);
|
||||
return;
|
||||
}
|
||||
|
||||
// False positive / spurious wake ups could lead to us spamming
|
||||
// redundant iterations of the event loop with no new events to
|
||||
// dispatch.
|
||||
//
|
||||
// If there's no readable event source then we just double check if we
|
||||
// have any pending `_receiver` events and if not we return without
|
||||
// running a loop iteration.
|
||||
// If we don't have any pending `_receiver`
|
||||
if !self.has_pending() && !self.state.x11_readiness.readable {
|
||||
self.set_exit_code(exit_code);
|
||||
return;
|
||||
}
|
||||
|
||||
// NB: `StartCause::Init` is handled as a special case and doesn't need
|
||||
// to be considered here
|
||||
let cause = match self.control_flow {
|
||||
let cause = match self.control_flow() {
|
||||
ControlFlow::Poll => StartCause::Poll,
|
||||
ControlFlow::Wait => StartCause::WaitCancelled {
|
||||
start,
|
||||
@@ -583,42 +521,42 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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!(),
|
||||
};
|
||||
|
||||
// False positive / spurious wake ups could lead to us spamming
|
||||
// redundant iterations of the event loop with no new events to
|
||||
// dispatch.
|
||||
//
|
||||
// If there's no readable event source then we just double check if we
|
||||
// have any pending `_receiver` events and if not we return without
|
||||
// running a loop iteration.
|
||||
// If we don't have any pending `_receiver`
|
||||
if !self.has_pending()
|
||||
&& !matches!(
|
||||
&cause,
|
||||
StartCause::ResumeTimeReached { .. } | StartCause::Poll
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.single_iteration(&mut callback, cause);
|
||||
}
|
||||
|
||||
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootELW<T>),
|
||||
{
|
||||
let mut control_flow = self.control_flow;
|
||||
|
||||
sticky_exit_callback(
|
||||
crate::event::Event::NewEvents(cause),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(crate::event::Event::NewEvents(cause), &self.target);
|
||||
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though X11
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
if cause == StartCause::Init {
|
||||
sticky_exit_callback(
|
||||
crate::event::Event::Resumed,
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(crate::event::Event::Resumed, &self.target);
|
||||
}
|
||||
|
||||
// Process all pending events
|
||||
self.drain_events(callback, &mut control_flow);
|
||||
self.drain_events(callback);
|
||||
|
||||
// Empty activation tokens.
|
||||
while let Ok((window_id, serial)) = self.activation_receiver.try_recv() {
|
||||
@@ -629,7 +567,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
|
||||
match token {
|
||||
Some(Ok(token)) => sticky_exit_callback(
|
||||
Some(Ok(token)) => callback(
|
||||
crate::event::Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: crate::event::WindowEvent::ActivationTokenDone {
|
||||
@@ -638,8 +576,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
},
|
||||
},
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
callback,
|
||||
),
|
||||
Some(Err(e)) => {
|
||||
log::error!("Failed to get activation token: {}", e);
|
||||
@@ -651,12 +587,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
// Empty the user event buffer
|
||||
{
|
||||
while let Ok(event) = self.user_receiver.try_recv() {
|
||||
sticky_exit_callback(
|
||||
crate::event::Event::UserEvent(event),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(crate::event::Event::UserEvent(event), &self.target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,31 +601,25 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
for window_id in windows {
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
sticky_exit_callback(
|
||||
Event::RedrawRequested(window_id),
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
},
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
{
|
||||
sticky_exit_callback(
|
||||
crate::event::Event::AboutToWait,
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
callback,
|
||||
);
|
||||
callback(crate::event::Event::AboutToWait, &self.target);
|
||||
}
|
||||
|
||||
self.control_flow = control_flow;
|
||||
}
|
||||
|
||||
fn drain_events<F>(&mut self, callback: &mut F, control_flow: &mut ControlFlow)
|
||||
fn drain_events<F>(&mut self, callback: &mut F)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootELW<T>),
|
||||
{
|
||||
let target = &self.target;
|
||||
let mut xev = MaybeUninit::uninit();
|
||||
@@ -703,21 +628,34 @@ impl<T: 'static> EventLoop<T> {
|
||||
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
|
||||
let mut xev = unsafe { xev.assume_init() };
|
||||
self.event_processor.process_event(&mut xev, |event| {
|
||||
sticky_exit_callback(
|
||||
event,
|
||||
target,
|
||||
control_flow,
|
||||
&mut |event, window_target, control_flow| {
|
||||
if let Event::RedrawRequested(crate::window::WindowId(wid)) = event {
|
||||
wt.redraw_sender.send(wid).unwrap();
|
||||
} else {
|
||||
callback(event, window_target, control_flow);
|
||||
}
|
||||
},
|
||||
);
|
||||
if let Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(wid),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
} = event
|
||||
{
|
||||
wt.redraw_sender.send(wid).unwrap();
|
||||
} else {
|
||||
callback(event, target);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
self.target.p.control_flow()
|
||||
}
|
||||
|
||||
fn exiting(&self) -> bool {
|
||||
self.target.p.exiting()
|
||||
}
|
||||
|
||||
fn set_exit_code(&self, code: i32) {
|
||||
self.target.p.set_exit_code(code)
|
||||
}
|
||||
|
||||
fn exit_code(&self) -> Option<i32> {
|
||||
self.target.p.exit_code()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_xtarget<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> {
|
||||
@@ -735,7 +673,15 @@ impl<T> EventLoopWindowTarget<T> {
|
||||
&self.xconn
|
||||
}
|
||||
|
||||
pub fn set_listen_device_events(&self, allowed: DeviceEvents) {
|
||||
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
|
||||
self.xconn.available_monitors().into_iter().flatten()
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
self.xconn.primary_monitor().ok()
|
||||
}
|
||||
|
||||
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
||||
self.device_events.set(allowed);
|
||||
}
|
||||
|
||||
@@ -754,15 +700,55 @@ impl<T> EventLoopWindowTarget<T> {
|
||||
}
|
||||
|
||||
self.xconn
|
||||
.select_xinput_events(self.root, ffi::XIAllMasterDevices as _, mask)
|
||||
.select_xinput_events(self.root, ALL_MASTER_DEVICES, mask)
|
||||
.expect_then_ignore_error("Failed to update device event filter");
|
||||
}
|
||||
|
||||
pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
|
||||
let mut display_handle = XlibDisplayHandle::empty();
|
||||
#[cfg(feature = "rwh_05")]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
let mut display_handle = rwh_05::XlibDisplayHandle::empty();
|
||||
display_handle.display = self.xconn.display as *mut _;
|
||||
display_handle.screen = self.xconn.default_screen_index() as c_int;
|
||||
RawDisplayHandle::Xlib(display_handle)
|
||||
display_handle.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
let display_handle = rwh_06::XlibDisplayHandle::new(
|
||||
// SAFETY: display will never be null
|
||||
Some(
|
||||
std::ptr::NonNull::new(self.xconn.display as *mut _)
|
||||
.expect("X11 display should never be null"),
|
||||
),
|
||||
self.xconn.default_screen_index() as c_int,
|
||||
);
|
||||
Ok(display_handle.into())
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.control_flow.set(control_flow)
|
||||
}
|
||||
|
||||
pub(crate) fn control_flow(&self) -> ControlFlow {
|
||||
self.control_flow.get()
|
||||
}
|
||||
|
||||
pub(crate) fn exit(&self) {
|
||||
self.exit.set(Some(0))
|
||||
}
|
||||
|
||||
pub(crate) fn exiting(&self) -> bool {
|
||||
self.exit.get().is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn set_exit_code(&self, code: i32) {
|
||||
self.exit.set(Some(code))
|
||||
}
|
||||
|
||||
pub(crate) fn exit_code(&self) -> Option<i32> {
|
||||
self.exit.get()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,7 +801,7 @@ impl<'a> Deref for DeviceInfo<'a> {
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(c_int);
|
||||
pub struct DeviceId(xinput::DeviceId);
|
||||
|
||||
impl DeviceId {
|
||||
#[allow(unused)]
|
||||
@@ -887,6 +873,9 @@ pub enum X11Error {
|
||||
/// Got an invalid activation token.
|
||||
InvalidActivationToken(Vec<u8>),
|
||||
|
||||
/// An extension that we rely on is not available.
|
||||
MissingExtension(&'static str),
|
||||
|
||||
/// Could not find a matching X11 visual for this visualid
|
||||
NoSuchVisual(xproto::Visualid),
|
||||
}
|
||||
@@ -905,6 +894,7 @@ impl fmt::Display for X11Error {
|
||||
"Invalid activation token: {}",
|
||||
std::str::from_utf8(s).unwrap_or("<invalid utf8>")
|
||||
),
|
||||
X11Error::MissingExtension(s) => write!(f, "Missing X11 extension: {}", s),
|
||||
X11Error::NoSuchVisual(visualid) => {
|
||||
write!(
|
||||
f,
|
||||
@@ -1026,17 +1016,10 @@ impl<'a> Drop for GenericEventCookie<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
struct XExtension {
|
||||
opcode: c_int,
|
||||
first_event_id: c_int,
|
||||
first_error_id: c_int,
|
||||
}
|
||||
|
||||
fn mkwid(w: xproto::Window) -> crate::window::WindowId {
|
||||
crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _))
|
||||
}
|
||||
fn mkdid(w: c_int) -> crate::event::DeviceId {
|
||||
fn mkdid(w: xinput::DeviceId) -> crate::event::DeviceId {
|
||||
crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w)))
|
||||
}
|
||||
|
||||
@@ -1069,12 +1052,10 @@ impl Device {
|
||||
|
||||
if Device::physical_device(info) {
|
||||
// Identify scroll axes
|
||||
for class_ptr in Device::classes(info) {
|
||||
let class = unsafe { &**class_ptr };
|
||||
if class._type == ffi::XIScrollClass {
|
||||
let info = unsafe {
|
||||
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class)
|
||||
};
|
||||
for &class_ptr in Device::classes(info) {
|
||||
let ty = unsafe { (*class_ptr)._type };
|
||||
if ty == ffi::XIScrollClass {
|
||||
let info = unsafe { &*(class_ptr as *const ffi::XIScrollClassInfo) };
|
||||
scroll_axes.push((
|
||||
info.number,
|
||||
ScrollAxis {
|
||||
@@ -1102,12 +1083,10 @@ impl Device {
|
||||
|
||||
fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) {
|
||||
if Device::physical_device(info) {
|
||||
for class_ptr in Device::classes(info) {
|
||||
let class = unsafe { &**class_ptr };
|
||||
if class._type == ffi::XIValuatorClass {
|
||||
let info = unsafe {
|
||||
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class)
|
||||
};
|
||||
for &class_ptr in Device::classes(info) {
|
||||
let ty = unsafe { (*class_ptr)._type };
|
||||
if ty == ffi::XIValuatorClass {
|
||||
let info = unsafe { &*(class_ptr as *const ffi::XIValuatorClassInfo) };
|
||||
if let Some(&mut (_, ref mut axis)) = self
|
||||
.scroll_axes
|
||||
.iter_mut()
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
use std::os::raw::*;
|
||||
use std::slice;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::{
|
||||
ffi::{
|
||||
self, RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask,
|
||||
RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRModeInfo, XRRScreenResources,
|
||||
},
|
||||
util, X11Error, XConnection,
|
||||
};
|
||||
use super::{util, X11Error, XConnection};
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
platform_impl::{MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode},
|
||||
platform_impl::VideoMode as PlatformVideoMode,
|
||||
};
|
||||
use x11rb::{
|
||||
connection::RequestConnection,
|
||||
protocol::{
|
||||
randr::{self, ConnectionExt as _},
|
||||
xproto,
|
||||
},
|
||||
};
|
||||
|
||||
// Used for testing. This should always be committed as false.
|
||||
const DISABLE_MONITOR_LIST_CACHING: bool = false;
|
||||
|
||||
static MONITORS: Lazy<Mutex<Option<Vec<MonitorHandle>>>> = Lazy::new(Mutex::default);
|
||||
|
||||
pub fn invalidate_cached_monitor_list() -> Option<Vec<MonitorHandle>> {
|
||||
// We update this lazily.
|
||||
(*MONITORS.lock().unwrap()).take()
|
||||
impl XConnection {
|
||||
pub fn invalidate_cached_monitor_list(&self) -> Option<Vec<MonitorHandle>> {
|
||||
// We update this lazily.
|
||||
self.monitor_handles.lock().unwrap().take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -31,7 +26,7 @@ pub struct VideoMode {
|
||||
pub(crate) size: (u32, u32),
|
||||
pub(crate) bit_depth: u16,
|
||||
pub(crate) refresh_rate_millihertz: u32,
|
||||
pub(crate) native_mode: RRMode,
|
||||
pub(crate) native_mode: randr::Mode,
|
||||
pub(crate) monitor: Option<MonitorHandle>,
|
||||
}
|
||||
|
||||
@@ -52,15 +47,15 @@ impl VideoMode {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn monitor(&self) -> PlatformMonitorHandle {
|
||||
PlatformMonitorHandle::X(self.monitor.clone().unwrap())
|
||||
pub fn monitor(&self) -> MonitorHandle {
|
||||
self.monitor.clone().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MonitorHandle {
|
||||
/// The actual id
|
||||
pub(crate) id: RRCrtc,
|
||||
pub(crate) id: randr::Crtc,
|
||||
/// The name of the monitor
|
||||
pub(crate) name: String,
|
||||
/// The size of the monitor
|
||||
@@ -106,10 +101,10 @@ impl std::hash::Hash for MonitorHandle {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mode_refresh_rate_millihertz(mode: &XRRModeInfo) -> Option<u32> {
|
||||
if mode.dotClock > 0 && mode.hTotal > 0 && mode.vTotal > 0 {
|
||||
pub fn mode_refresh_rate_millihertz(mode: &randr::ModeInfo) -> Option<u32> {
|
||||
if mode.dot_clock > 0 && mode.htotal > 0 && mode.vtotal > 0 {
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
Some((mode.dotClock as u64 * 1000 / (mode.hTotal as u64 * mode.vTotal as u64)) as u32)
|
||||
Some((mode.dot_clock as u64 * 1000 / (mode.htotal as u64 * mode.vtotal as u64)) as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -118,19 +113,18 @@ pub fn mode_refresh_rate_millihertz(mode: &XRRModeInfo) -> Option<u32> {
|
||||
impl MonitorHandle {
|
||||
fn new(
|
||||
xconn: &XConnection,
|
||||
resources: *mut XRRScreenResources,
|
||||
id: RRCrtc,
|
||||
crtc: *mut XRRCrtcInfo,
|
||||
resources: &ScreenResources,
|
||||
id: randr::Crtc,
|
||||
crtc: &randr::GetCrtcInfoReply,
|
||||
primary: bool,
|
||||
) -> Option<Self> {
|
||||
let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? };
|
||||
let dimensions = unsafe { ((*crtc).width, (*crtc).height) };
|
||||
let position = unsafe { ((*crtc).x, (*crtc).y) };
|
||||
let (name, scale_factor, video_modes) = xconn.get_output_info(resources, crtc)?;
|
||||
let dimensions = (crtc.width as u32, crtc.height as u32);
|
||||
let position = (crtc.x as i32, crtc.y as i32);
|
||||
|
||||
// Get the refresh rate of the current video mode.
|
||||
let current_mode = unsafe { (*crtc).mode };
|
||||
let screen_modes =
|
||||
unsafe { slice::from_raw_parts((*resources).modes, (*resources).nmode as usize) };
|
||||
let current_mode = crtc.mode;
|
||||
let screen_modes = resources.modes();
|
||||
let refresh_rate_millihertz = screen_modes
|
||||
.iter()
|
||||
.find(|mode| mode.id == current_mode)
|
||||
@@ -207,19 +201,22 @@ impl MonitorHandle {
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn get_monitor_for_window(&self, window_rect: Option<util::AaRect>) -> MonitorHandle {
|
||||
let monitors = self.available_monitors();
|
||||
pub fn get_monitor_for_window(
|
||||
&self,
|
||||
window_rect: Option<util::AaRect>,
|
||||
) -> Result<MonitorHandle, X11Error> {
|
||||
let monitors = self.available_monitors()?;
|
||||
|
||||
if monitors.is_empty() {
|
||||
// Return a dummy monitor to avoid panicking
|
||||
return MonitorHandle::dummy();
|
||||
return Ok(MonitorHandle::dummy());
|
||||
}
|
||||
|
||||
let default = monitors.get(0).unwrap();
|
||||
|
||||
let window_rect = match window_rect {
|
||||
Some(rect) => rect,
|
||||
None => return default.to_owned(),
|
||||
None => return Ok(default.to_owned()),
|
||||
};
|
||||
|
||||
let mut largest_overlap = 0;
|
||||
@@ -232,110 +229,153 @@ impl XConnection {
|
||||
}
|
||||
}
|
||||
|
||||
matched_monitor.to_owned()
|
||||
Ok(matched_monitor.to_owned())
|
||||
}
|
||||
|
||||
fn query_monitor_list(&self) -> Vec<MonitorHandle> {
|
||||
unsafe {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
||||
fn query_monitor_list(&self) -> Result<Vec<MonitorHandle>, X11Error> {
|
||||
let root = self.default_root();
|
||||
let resources = ScreenResources::from_connection(self.xcb_connection(), root)?;
|
||||
|
||||
let root = self.default_root().root;
|
||||
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
||||
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root as ffi::Window)
|
||||
} else {
|
||||
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
|
||||
// Upon failure, `resources` will be null.
|
||||
(self.xrandr.XRRGetScreenResources)(self.display, root as ffi::Window)
|
||||
};
|
||||
|
||||
if resources.is_null() {
|
||||
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
|
||||
}
|
||||
|
||||
let mut has_primary = false;
|
||||
|
||||
let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root as ffi::Window);
|
||||
let mut available = Vec::with_capacity((*resources).ncrtc as usize);
|
||||
|
||||
for crtc_index in 0..(*resources).ncrtc {
|
||||
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
|
||||
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
|
||||
if is_active {
|
||||
let is_primary = *(*crtc).outputs.offset(0) == primary;
|
||||
has_primary |= is_primary;
|
||||
if let Some(monitor_id) =
|
||||
MonitorHandle::new(self, resources, crtc_id, crtc, is_primary)
|
||||
{
|
||||
available.push(monitor_id)
|
||||
}
|
||||
}
|
||||
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
}
|
||||
|
||||
// If no monitors were detected as being primary, we just pick one ourselves!
|
||||
if !has_primary {
|
||||
if let Some(ref mut fallback) = available.first_mut() {
|
||||
// Setting this here will come in handy if we ever add an `is_primary` method.
|
||||
fallback.primary = true;
|
||||
}
|
||||
}
|
||||
|
||||
(self.xrandr.XRRFreeScreenResources)(resources);
|
||||
available
|
||||
// Pipeline all of the get-crtc requests.
|
||||
let mut crtc_cookies = Vec::with_capacity(resources.crtcs().len());
|
||||
for &crtc in resources.crtcs() {
|
||||
crtc_cookies.push(
|
||||
self.xcb_connection()
|
||||
.randr_get_crtc_info(crtc, x11rb::CURRENT_TIME)?,
|
||||
);
|
||||
}
|
||||
|
||||
// Do this here so we do all of our requests in one shot.
|
||||
let primary = self
|
||||
.xcb_connection()
|
||||
.randr_get_output_primary(root.root)?
|
||||
.reply()?
|
||||
.output;
|
||||
|
||||
let mut crtc_infos = Vec::with_capacity(crtc_cookies.len());
|
||||
for cookie in crtc_cookies {
|
||||
let reply = cookie.reply()?;
|
||||
crtc_infos.push(reply);
|
||||
}
|
||||
|
||||
let mut has_primary = false;
|
||||
let mut available_monitors = Vec::with_capacity(resources.crtcs().len());
|
||||
for (crtc_id, crtc) in resources.crtcs().iter().zip(crtc_infos.iter()) {
|
||||
if crtc.width == 0 || crtc.height == 0 || crtc.outputs.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let is_primary = crtc.outputs[0] == primary;
|
||||
has_primary |= is_primary;
|
||||
let monitor = MonitorHandle::new(self, &resources, *crtc_id, crtc, is_primary);
|
||||
available_monitors.extend(monitor);
|
||||
}
|
||||
|
||||
// If we don't have a primary monitor, just pick one ourselves!
|
||||
if !has_primary {
|
||||
if let Some(ref mut fallback) = available_monitors.first_mut() {
|
||||
// Setting this here will come in handy if we ever add an `is_primary` method.
|
||||
fallback.primary = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(available_monitors)
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
|
||||
let mut monitors_lock = MONITORS.lock().unwrap();
|
||||
pub fn available_monitors(&self) -> Result<Vec<MonitorHandle>, X11Error> {
|
||||
let mut monitors_lock = self.monitor_handles.lock().unwrap();
|
||||
(*monitors_lock)
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.map(Ok)
|
||||
.or_else(|| {
|
||||
let monitors = Some(self.query_monitor_list());
|
||||
if !DISABLE_MONITOR_LIST_CACHING {
|
||||
(*monitors_lock) = monitors.clone();
|
||||
}
|
||||
monitors
|
||||
self.query_monitor_list()
|
||||
.map(|mon_list| {
|
||||
let monitors = Some(mon_list);
|
||||
if !DISABLE_MONITOR_LIST_CACHING {
|
||||
(*monitors_lock) = monitors.clone();
|
||||
}
|
||||
monitors
|
||||
})
|
||||
.transpose()
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> MonitorHandle {
|
||||
self.available_monitors()
|
||||
pub fn primary_monitor(&self) -> Result<MonitorHandle, X11Error> {
|
||||
Ok(self
|
||||
.available_monitors()?
|
||||
.into_iter()
|
||||
.find(|monitor| monitor.primary)
|
||||
.unwrap_or_else(MonitorHandle::dummy)
|
||||
.unwrap_or_else(MonitorHandle::dummy))
|
||||
}
|
||||
|
||||
pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, X11Error> {
|
||||
let has_xrandr = unsafe {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor)
|
||||
};
|
||||
assert!(
|
||||
has_xrandr == True,
|
||||
"[winit] XRandR extension not available."
|
||||
);
|
||||
pub fn select_xrandr_input(&self, root: xproto::Window) -> Result<u8, X11Error> {
|
||||
use randr::NotifyMask;
|
||||
|
||||
let mut event_offset = 0;
|
||||
let mut error_offset = 0;
|
||||
let status = unsafe {
|
||||
(self.xrandr.XRRQueryExtension)(self.display, &mut event_offset, &mut error_offset)
|
||||
};
|
||||
// Get extension info.
|
||||
let info = self
|
||||
.xcb_connection()
|
||||
.extension_information(randr::X11_EXTENSION_NAME)?
|
||||
.ok_or_else(|| X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
|
||||
|
||||
if status != True {
|
||||
self.check_errors()?;
|
||||
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
|
||||
}
|
||||
// Select input data.
|
||||
let event_mask =
|
||||
NotifyMask::CRTC_CHANGE | NotifyMask::OUTPUT_PROPERTY | NotifyMask::SCREEN_CHANGE;
|
||||
self.xcb_connection().randr_select_input(root, event_mask)?;
|
||||
|
||||
let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask;
|
||||
unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
|
||||
|
||||
Ok(event_offset)
|
||||
Ok(info.first_event)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ScreenResources {
|
||||
/// List of attached modes.
|
||||
modes: Vec<randr::ModeInfo>,
|
||||
|
||||
/// List of attached CRTCs.
|
||||
crtcs: Vec<randr::Crtc>,
|
||||
}
|
||||
|
||||
impl ScreenResources {
|
||||
pub(crate) fn modes(&self) -> &[randr::ModeInfo] {
|
||||
&self.modes
|
||||
}
|
||||
|
||||
pub(crate) fn crtcs(&self) -> &[randr::Crtc] {
|
||||
&self.crtcs
|
||||
}
|
||||
|
||||
pub(crate) fn from_connection(
|
||||
conn: &impl x11rb::connection::Connection,
|
||||
root: &x11rb::protocol::xproto::Screen,
|
||||
) -> Result<Self, X11Error> {
|
||||
let version = conn.randr_query_version(0, 0)?.reply()?;
|
||||
|
||||
if (version.major_version == 1 && version.minor_version >= 3) || version.major_version > 1 {
|
||||
let reply = conn
|
||||
.randr_get_screen_resources_current(root.root)?
|
||||
.reply()?;
|
||||
Ok(Self::from_get_screen_resources_current_reply(reply))
|
||||
} else {
|
||||
let reply = conn.randr_get_screen_resources(root.root)?.reply()?;
|
||||
Ok(Self::from_get_screen_resources_reply(reply))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_get_screen_resources_reply(reply: randr::GetScreenResourcesReply) -> Self {
|
||||
Self {
|
||||
modes: reply.modes,
|
||||
crtcs: reply.crtcs,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_get_screen_resources_current_reply(
|
||||
reply: randr::GetScreenResourcesCurrentReply,
|
||||
) -> Self {
|
||||
Self {
|
||||
modes: reply.modes,
|
||||
crtcs: reply.crtcs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![allow(clippy::assertions_on_constants)]
|
||||
|
||||
use super::*;
|
||||
use crate::icon::{Icon, Pixel, PIXEL_SIZE};
|
||||
use crate::icon::{Pixel, RgbaIcon, PIXEL_SIZE};
|
||||
|
||||
impl Pixel {
|
||||
pub fn to_packed_argb(&self) -> Cardinal {
|
||||
@@ -18,16 +18,15 @@ impl Pixel {
|
||||
}
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
impl RgbaIcon {
|
||||
pub(crate) fn to_cardinals(&self) -> Vec<Cardinal> {
|
||||
let rgba_icon = &self.inner;
|
||||
assert_eq!(rgba_icon.rgba.len() % PIXEL_SIZE, 0);
|
||||
let pixel_count = rgba_icon.rgba.len() / PIXEL_SIZE;
|
||||
assert_eq!(pixel_count, (rgba_icon.width * rgba_icon.height) as usize);
|
||||
assert_eq!(self.rgba.len() % PIXEL_SIZE, 0);
|
||||
let pixel_count = self.rgba.len() / PIXEL_SIZE;
|
||||
assert_eq!(pixel_count, (self.width * self.height) as usize);
|
||||
let mut data = Vec::with_capacity(pixel_count);
|
||||
data.push(rgba_icon.width as Cardinal);
|
||||
data.push(rgba_icon.height as Cardinal);
|
||||
let pixels = rgba_icon.rgba.as_ptr() as *const Pixel;
|
||||
data.push(self.width as Cardinal);
|
||||
data.push(self.height as Cardinal);
|
||||
let pixels = self.rgba.as_ptr() as *const Pixel;
|
||||
for pixel_index in 0..pixel_count {
|
||||
let pixel = unsafe { &*pixels.add(pixel_index) };
|
||||
data.push(pixel.to_packed_argb());
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use std::{slice, str};
|
||||
use x11rb::protocol::xinput::{self, ConnectionExt as _};
|
||||
use x11rb::protocol::{
|
||||
xinput::{self, ConnectionExt as _},
|
||||
xkb,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -11,31 +14,6 @@ pub const VIRTUAL_CORE_KEYBOARD: u16 = 3;
|
||||
// To test if `lookup_utf8` works correctly, set this to 1.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
// NOTE: Some of these fields are not used, but may be of use in the future.
|
||||
pub struct PointerState<'a> {
|
||||
xconn: &'a XConnection,
|
||||
pub root: xproto::Window,
|
||||
pub child: xproto::Window,
|
||||
pub root_x: c_double,
|
||||
pub root_y: c_double,
|
||||
pub win_x: c_double,
|
||||
pub win_y: c_double,
|
||||
buttons: ffi::XIButtonState,
|
||||
pub group: ffi::XIGroupState,
|
||||
pub relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl<'a> Drop for PointerState<'a> {
|
||||
fn drop(&mut self) {
|
||||
if !self.buttons.mask.is_null() {
|
||||
unsafe {
|
||||
// This is why you need to read the docs carefully...
|
||||
(self.xconn.xlib.XFree)(self.buttons.mask as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn select_xinput_events(
|
||||
&self,
|
||||
@@ -54,7 +32,12 @@ impl XConnection {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn select_xkb_events(&self, device_id: c_int, mask: c_ulong) -> Result<bool, X11Error> {
|
||||
pub fn select_xkb_events(
|
||||
&self,
|
||||
device_id: xkb::DeviceSpec,
|
||||
mask: xkb::EventType,
|
||||
) -> Result<bool, X11Error> {
|
||||
let mask = u16::from(mask) as _;
|
||||
let status =
|
||||
unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id as _, mask, mask) };
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::{env, slice, str::FromStr};
|
||||
use std::{env, str, str::FromStr};
|
||||
|
||||
use super::{
|
||||
ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources},
|
||||
*,
|
||||
};
|
||||
use super::*;
|
||||
use crate::platform_impl::platform::x11::monitor;
|
||||
use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoMode};
|
||||
|
||||
use x11rb::protocol::randr::{self, ConnectionExt as _};
|
||||
|
||||
/// Represents values of `WINIT_HIDPI_FACTOR`.
|
||||
pub enum EnvVarDPI {
|
||||
Randr,
|
||||
@@ -37,42 +36,32 @@ pub fn calc_dpi_factor(
|
||||
|
||||
impl XConnection {
|
||||
// Retrieve DPI from Xft.dpi property
|
||||
pub unsafe fn get_xft_dpi(&self) -> Option<f64> {
|
||||
(self.xlib.XrmInitialize)();
|
||||
let resource_manager_str = (self.xlib.XResourceManagerString)(self.display);
|
||||
if resource_manager_str.is_null() {
|
||||
return None;
|
||||
}
|
||||
if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
|
||||
let name: &str = "Xft.dpi:\t";
|
||||
for pair in res.split('\n') {
|
||||
if let Some(stripped) = pair.strip_prefix(name) {
|
||||
return f64::from_str(stripped).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
pub fn get_xft_dpi(&self) -> Option<f64> {
|
||||
self.database()
|
||||
.get_string("Xfi.dpi", "")
|
||||
.and_then(|s| f64::from_str(s).ok())
|
||||
}
|
||||
pub unsafe fn get_output_info(
|
||||
pub fn get_output_info(
|
||||
&self,
|
||||
resources: *mut XRRScreenResources,
|
||||
crtc: *mut XRRCrtcInfo,
|
||||
resources: &monitor::ScreenResources,
|
||||
crtc: &randr::GetCrtcInfoReply,
|
||||
) -> Option<(String, f64, Vec<VideoMode>)> {
|
||||
let output_info =
|
||||
(self.xrandr.XRRGetOutputInfo)(self.display, resources, *(*crtc).outputs.offset(0));
|
||||
if output_info.is_null() {
|
||||
// When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display)
|
||||
// it's possible for it to return null.
|
||||
// https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816596
|
||||
let _ = self.check_errors(); // discard `BadRROutput` error
|
||||
return None;
|
||||
}
|
||||
let output_info = match self
|
||||
.xcb_connection()
|
||||
.randr_get_output_info(crtc.outputs[0], x11rb::CURRENT_TIME)
|
||||
.map_err(X11Error::from)
|
||||
.and_then(|r| r.reply().map_err(X11Error::from))
|
||||
{
|
||||
Ok(output_info) => output_info,
|
||||
Err(err) => {
|
||||
warn!("Failed to get output info: {:?}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let bit_depth = self.default_root().root_depth;
|
||||
|
||||
let output_modes =
|
||||
slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize);
|
||||
let resource_modes = slice::from_raw_parts((*resources).modes, (*resources).nmode as usize);
|
||||
let output_modes = &output_info.modes;
|
||||
let resource_modes = resources.modes();
|
||||
|
||||
let modes = resource_modes
|
||||
.iter()
|
||||
@@ -81,7 +70,7 @@ impl XConnection {
|
||||
.filter(|x| output_modes.iter().any(|id| x.id == *id))
|
||||
.map(|mode| {
|
||||
VideoMode {
|
||||
size: (mode.width, mode.height),
|
||||
size: (mode.width.into(), mode.height.into()),
|
||||
refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode)
|
||||
.unwrap_or(0),
|
||||
bit_depth: bit_depth as u16,
|
||||
@@ -93,11 +82,13 @@ impl XConnection {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let name_slice = slice::from_raw_parts(
|
||||
(*output_info).name as *mut u8,
|
||||
(*output_info).nameLen as usize,
|
||||
);
|
||||
let name = String::from_utf8_lossy(name_slice).into();
|
||||
let name = match str::from_utf8(&output_info.name) {
|
||||
Ok(name) => name.to_owned(),
|
||||
Err(err) => {
|
||||
warn!("Failed to get output name: {:?}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
// Override DPI if `WINIT_X11_SCALE_FACTOR` variable is set
|
||||
let deprecated_dpi_override = env::var("WINIT_HIDPI_FACTOR").ok();
|
||||
if deprecated_dpi_override.is_some() {
|
||||
@@ -124,8 +115,8 @@ impl XConnection {
|
||||
|
||||
let scale_factor = match dpi_env {
|
||||
EnvVarDPI::Randr => calc_dpi_factor(
|
||||
((*crtc).width, (*crtc).height),
|
||||
((*output_info).mm_width as _, (*output_info).mm_height as _),
|
||||
(crtc.width.into(), crtc.height.into()),
|
||||
(output_info.mm_width as _, output_info.mm_height as _),
|
||||
),
|
||||
EnvVarDPI::Scale(dpi_override) => {
|
||||
if !validate_scale_factor(dpi_override) {
|
||||
@@ -140,74 +131,47 @@ impl XConnection {
|
||||
dpi / 96.
|
||||
} else {
|
||||
calc_dpi_factor(
|
||||
((*crtc).width, (*crtc).height),
|
||||
((*output_info).mm_width as _, (*output_info).mm_height as _),
|
||||
(crtc.width.into(), crtc.height.into()),
|
||||
(output_info.mm_width as _, output_info.mm_height as _),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(self.xrandr.XRRFreeOutputInfo)(output_info);
|
||||
Some((name, scale_factor, modes))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Option<()> {
|
||||
unsafe {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
||||
pub fn set_crtc_config(
|
||||
&self,
|
||||
crtc_id: randr::Crtc,
|
||||
mode_id: randr::Mode,
|
||||
) -> Result<(), X11Error> {
|
||||
let crtc = self
|
||||
.xcb_connection()
|
||||
.randr_get_crtc_info(crtc_id, x11rb::CURRENT_TIME)?
|
||||
.reply()?;
|
||||
|
||||
let root = self.default_root().root;
|
||||
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
||||
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root as ffi::Window)
|
||||
} else {
|
||||
(self.xrandr.XRRGetScreenResources)(self.display, root as ffi::Window)
|
||||
};
|
||||
|
||||
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||
let status = (self.xrandr.XRRSetCrtcConfig)(
|
||||
self.display,
|
||||
resources,
|
||||
self.xcb_connection()
|
||||
.randr_set_crtc_config(
|
||||
crtc_id,
|
||||
CurrentTime,
|
||||
(*crtc).x,
|
||||
(*crtc).y,
|
||||
crtc.timestamp,
|
||||
x11rb::CURRENT_TIME,
|
||||
crtc.x,
|
||||
crtc.y,
|
||||
mode_id,
|
||||
(*crtc).rotation,
|
||||
(*crtc).outputs.offset(0),
|
||||
1,
|
||||
);
|
||||
|
||||
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
(self.xrandr.XRRFreeScreenResources)(resources);
|
||||
|
||||
if status == Success as i32 {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
crtc.rotation,
|
||||
&crtc.outputs,
|
||||
)?
|
||||
.reply()
|
||||
.map(|_| ())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn get_crtc_mode(&self, crtc_id: RRCrtc) -> RRMode {
|
||||
unsafe {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
||||
|
||||
let root = self.default_root().root;
|
||||
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
||||
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root as ffi::Window)
|
||||
} else {
|
||||
(self.xrandr.XRRGetScreenResources)(self.display, root as ffi::Window)
|
||||
};
|
||||
|
||||
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||
let mode = (*crtc).mode;
|
||||
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
(self.xrandr.XRRFreeScreenResources)(resources);
|
||||
mode
|
||||
}
|
||||
pub fn get_crtc_mode(&self, crtc_id: randr::Crtc) -> Result<randr::Mode, X11Error> {
|
||||
Ok(self
|
||||
.xcb_connection()
|
||||
.randr_get_crtc_info(crtc_id, x11rb::CURRENT_TIME)?
|
||||
.reply()?
|
||||
.mode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,16 @@ use std::{
|
||||
sync::{Arc, Mutex, MutexGuard},
|
||||
};
|
||||
|
||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XlibDisplayHandle, XlibWindowHandle};
|
||||
use x11rb::{
|
||||
connection::Connection,
|
||||
properties::WmHintsState,
|
||||
protocol::xproto::{self, ConnectionExt as _},
|
||||
};
|
||||
use x11rb::{
|
||||
properties::{WmHints, WmSizeHints, WmSizeHintsSpecification},
|
||||
protocol::xinput,
|
||||
properties::{WmHints, WmHintsState, WmSizeHints, WmSizeHintsSpecification},
|
||||
protocol::{
|
||||
randr,
|
||||
shape::SK,
|
||||
xfixes::{ConnectionExt, RegionWrapper},
|
||||
xinput,
|
||||
xproto::{self, ConnectionExt as _, Rectangle},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -24,11 +25,11 @@ use crate::{
|
||||
event_loop::AsyncRequestSerial,
|
||||
platform_impl::{
|
||||
x11::{atoms::*, MonitorHandle as X11MonitorHandle, WakeSender, X11Error},
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError,
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
|
||||
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
|
||||
},
|
||||
window::{
|
||||
CursorGrabMode, CursorIcon, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
},
|
||||
};
|
||||
@@ -55,7 +56,7 @@ pub struct SharedState {
|
||||
// Used to restore position after exiting fullscreen
|
||||
pub restore_position: Option<(i32, i32)>,
|
||||
// Used to restore video mode after exiting fullscreen
|
||||
pub desktop_video_mode: Option<(ffi::RRCrtc, ffi::RRMode)>,
|
||||
pub desktop_video_mode: Option<(randr::Crtc, randr::Mode)>,
|
||||
pub frame_extents: Option<util::FrameExtentsHeuristic>,
|
||||
pub min_inner_size: Option<Size>,
|
||||
pub max_inner_size: Option<Size>,
|
||||
@@ -63,6 +64,7 @@ pub struct SharedState {
|
||||
pub base_size: Option<Size>,
|
||||
pub visibility: Visibility,
|
||||
pub has_focus: bool,
|
||||
pub cursor_hittest: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
@@ -103,6 +105,7 @@ impl SharedState {
|
||||
resize_increments: None,
|
||||
base_size: None,
|
||||
has_focus: false,
|
||||
cursor_hittest: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -113,9 +116,11 @@ unsafe impl Sync for UnownedWindow {}
|
||||
pub(crate) struct UnownedWindow {
|
||||
pub(crate) xconn: Arc<XConnection>, // never changes
|
||||
xwindow: xproto::Window, // never changes
|
||||
visual: u32, // never changes
|
||||
#[allow(dead_code)]
|
||||
visual: u32, // never changes
|
||||
root: xproto::Window, // never changes
|
||||
screen_id: i32, // never changes
|
||||
#[allow(dead_code)]
|
||||
screen_id: i32, // never changes
|
||||
cursor: Mutex<CursorIcon>,
|
||||
cursor_grabbed_mode: Mutex<CursorGrabMode>,
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
@@ -144,14 +149,17 @@ impl UnownedWindow {
|
||||
) -> Result<UnownedWindow, RootOsError> {
|
||||
let xconn = &event_loop.xconn;
|
||||
let atoms = xconn.atoms();
|
||||
#[cfg(feature = "rwh_06")]
|
||||
let root = match window_attrs.parent_window {
|
||||
Some(RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window,
|
||||
Some(RawWindowHandle::Xcb(handle)) => handle.window,
|
||||
Some(rwh_06::RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window,
|
||||
Some(rwh_06::RawWindowHandle::Xcb(handle)) => handle.window.get(),
|
||||
Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"),
|
||||
None => event_loop.root,
|
||||
};
|
||||
#[cfg(not(feature = "rwh_06"))]
|
||||
let root = event_loop.root;
|
||||
|
||||
let mut monitors = xconn.available_monitors();
|
||||
let mut monitors = leap!(xconn.available_monitors());
|
||||
let guessed_monitor = if monitors.is_empty() {
|
||||
X11MonitorHandle::dummy()
|
||||
} else {
|
||||
@@ -243,7 +251,7 @@ impl UnownedWindow {
|
||||
// Find a suitable visual, true color with 32 bits of depth.
|
||||
all_visuals
|
||||
.find_map(|(visual, depth)| {
|
||||
(visual.class == xproto::VisualClass::TRUE_COLOR)
|
||||
(depth == 32 && visual.class == xproto::VisualClass::TRUE_COLOR)
|
||||
.then_some((Some(visual), depth, true))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
@@ -470,7 +478,7 @@ impl UnownedWindow {
|
||||
|
||||
// Set window icons
|
||||
if let Some(icon) = window_attrs.window_icon {
|
||||
leap!(window.set_icon_inner(icon)).ignore_error();
|
||||
leap!(window.set_icon_inner(icon.inner)).ignore_error();
|
||||
}
|
||||
|
||||
// Opt into handling window close
|
||||
@@ -524,7 +532,7 @@ impl UnownedWindow {
|
||||
| xinput::XIEventMask::TOUCH_BEGIN
|
||||
| xinput::XIEventMask::TOUCH_UPDATE
|
||||
| xinput::XIEventMask::TOUCH_END;
|
||||
leap!(xconn.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices as u16, mask))
|
||||
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
|
||||
.ignore_error();
|
||||
|
||||
{
|
||||
@@ -740,8 +748,12 @@ impl UnownedWindow {
|
||||
&Some(Fullscreen::Exclusive(PlatformVideoMode::X(ref video_mode))),
|
||||
) => {
|
||||
let monitor = video_mode.monitor.as_ref().unwrap();
|
||||
shared_state_lock.desktop_video_mode =
|
||||
Some((monitor.id, self.xconn.get_crtc_mode(monitor.id)));
|
||||
shared_state_lock.desktop_video_mode = Some((
|
||||
monitor.id,
|
||||
self.xconn
|
||||
.get_crtc_mode(monitor.id)
|
||||
.expect("Failed to get desktop video mode"),
|
||||
));
|
||||
}
|
||||
// Restore desktop video mode upon exiting exclusive fullscreen
|
||||
(&Some(Fullscreen::Exclusive(_)), &None)
|
||||
@@ -775,7 +787,9 @@ impl UnownedWindow {
|
||||
Fullscreen::Borderless(Some(PlatformMonitorHandle::X(monitor))) => {
|
||||
(None, monitor)
|
||||
}
|
||||
Fullscreen::Borderless(None) => (None, self.current_monitor()),
|
||||
Fullscreen::Borderless(None) => {
|
||||
(None, self.shared_state_lock().last_monitor.clone())
|
||||
}
|
||||
#[cfg(wayland_platform)]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@@ -871,17 +885,22 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_monitor(&self) -> X11MonitorHandle {
|
||||
self.shared_state_lock().last_monitor.clone()
|
||||
pub fn current_monitor(&self) -> Option<X11MonitorHandle> {
|
||||
Some(self.shared_state_lock().last_monitor.clone())
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> Vec<X11MonitorHandle> {
|
||||
self.xconn.available_monitors()
|
||||
self.xconn
|
||||
.available_monitors()
|
||||
.expect("Failed to get available monitors")
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> X11MonitorHandle {
|
||||
self.xconn.primary_monitor()
|
||||
pub fn primary_monitor(&self) -> Option<X11MonitorHandle> {
|
||||
Some(
|
||||
self.xconn
|
||||
.primary_monitor()
|
||||
.expect("Failed to get primary monitor"),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1017,6 +1036,9 @@ impl UnownedWindow {
|
||||
#[inline]
|
||||
pub fn set_transparent(&self, _transparent: bool) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_blur(&self, _blur: bool) {}
|
||||
|
||||
fn set_decorations_inner(&self, decorations: bool) -> Result<VoidCookie<'_>, X11Error> {
|
||||
self.shared_state_lock().is_decorated = decorations;
|
||||
let mut hints = self.xconn.get_motif_hints(self.xwindow);
|
||||
@@ -1070,7 +1092,7 @@ impl UnownedWindow {
|
||||
.expect("Failed to set window-level state");
|
||||
}
|
||||
|
||||
fn set_icon_inner(&self, icon: Icon) -> Result<VoidCookie<'_>, X11Error> {
|
||||
fn set_icon_inner(&self, icon: PlatformIcon) -> Result<VoidCookie<'_>, X11Error> {
|
||||
let atoms = self.xconn.atoms();
|
||||
let icon_atom = atoms[_NET_WM_ICON];
|
||||
let data = icon.to_cardinals();
|
||||
@@ -1097,7 +1119,7 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, icon: Option<Icon>) {
|
||||
pub(crate) fn set_window_icon(&self, icon: Option<PlatformIcon>) {
|
||||
match icon {
|
||||
Some(icon) => self.set_icon_inner(icon),
|
||||
None => self.unset_icon_inner(),
|
||||
@@ -1273,6 +1295,10 @@ impl UnownedWindow {
|
||||
self.xconn
|
||||
.flush_requests()
|
||||
.expect("Failed to call XResizeWindow");
|
||||
// cursor_hittest needs to be reapplied after window resize
|
||||
if self.shared_state_lock().cursor_hittest {
|
||||
let _ = self.set_cursor_hittest(true);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1442,11 +1468,13 @@ impl UnownedWindow {
|
||||
WindowButtons::all()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn xlib_display(&self) -> *mut c_void {
|
||||
self.xconn.display as _
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn xlib_window(&self) -> c_ulong {
|
||||
self.xwindow as ffi::Window
|
||||
@@ -1558,7 +1586,7 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.current_monitor().scale_factor
|
||||
self.shared_state_lock().last_monitor.scale_factor
|
||||
}
|
||||
|
||||
pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> {
|
||||
@@ -1582,8 +1610,25 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
let mut rectangles: Vec<Rectangle> = Vec::new();
|
||||
if hittest {
|
||||
let size = self.inner_size();
|
||||
rectangles.push(Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: size.width as u16,
|
||||
height: size.height as u16,
|
||||
})
|
||||
}
|
||||
let region = RegionWrapper::create_region(self.xconn.xcb_connection(), &rectangles)
|
||||
.map_err(|_e| ExternalError::Ignored)?;
|
||||
self.xconn
|
||||
.xcb_connection()
|
||||
.xfixes_set_window_shape_region(self.xwindow, SK::INPUT, 0, 0, region.region())
|
||||
.map_err(|_e| ExternalError::Ignored)?;
|
||||
self.shared_state_lock().cursor_hittest = hittest;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves the window while it is being dragged.
|
||||
@@ -1591,6 +1636,9 @@ impl UnownedWindow {
|
||||
self.drag_initiate(util::MOVERESIZE_MOVE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
/// Resizes the window while it is being dragged.
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
self.drag_initiate(match direction {
|
||||
@@ -1778,20 +1826,55 @@ impl UnownedWindow {
|
||||
// TODO timer
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_04")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let mut window_handle = XlibWindowHandle::empty();
|
||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||
let mut window_handle = rwh_04::XlibHandle::empty();
|
||||
window_handle.display = self.xlib_display();
|
||||
window_handle.window = self.xlib_window();
|
||||
window_handle.visual_id = self.visual as c_ulong;
|
||||
RawWindowHandle::Xlib(window_handle)
|
||||
rwh_04::RawWindowHandle::Xlib(window_handle)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
let mut display_handle = XlibDisplayHandle::empty();
|
||||
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
||||
let mut window_handle = rwh_05::XlibWindowHandle::empty();
|
||||
window_handle.window = self.xlib_window();
|
||||
window_handle.visual_id = self.visual as c_ulong;
|
||||
window_handle.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
let mut display_handle = rwh_05::XlibDisplayHandle::empty();
|
||||
display_handle.display = self.xlib_display();
|
||||
display_handle.screen = self.screen_id;
|
||||
RawDisplayHandle::Xlib(display_handle)
|
||||
display_handle.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
let mut window_handle = rwh_06::XlibWindowHandle::new(self.xlib_window());
|
||||
window_handle.visual_id = self.visual as c_ulong;
|
||||
Ok(window_handle.into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::XlibDisplayHandle::new(
|
||||
Some(
|
||||
std::ptr::NonNull::new(self.xlib_display())
|
||||
.expect("display pointer should never be null"),
|
||||
),
|
||||
self.screen_id,
|
||||
)
|
||||
.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1799,6 +1882,8 @@ impl UnownedWindow {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
self.shared_state_lock().has_focus
|
||||
|
||||
@@ -10,17 +10,20 @@ use std::{
|
||||
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
use super::{atoms::Atoms, ffi};
|
||||
use x11rb::{connection::Connection, protocol::xproto, xcb_ffi::XCBConnection};
|
||||
use super::{atoms::Atoms, ffi, monitor::MonitorHandle};
|
||||
use x11rb::{connection::Connection, protocol::xproto, resource_manager, xcb_ffi::XCBConnection};
|
||||
|
||||
/// A connection to an X server.
|
||||
pub(crate) struct XConnection {
|
||||
pub xlib: ffi::Xlib,
|
||||
/// Exposes XRandR functions from version < 1.5
|
||||
pub xrandr: ffi::Xrandr_2_2_0,
|
||||
pub xcursor: ffi::Xcursor,
|
||||
|
||||
// TODO(notgull): I'd like to remove this, but apparently Xlib and Xinput2 are tied together
|
||||
// for some reason.
|
||||
pub xinput2: ffi::XInput2,
|
||||
|
||||
pub display: *mut ffi::Display,
|
||||
|
||||
/// The manager for the XCB connection.
|
||||
///
|
||||
/// The `Option` ensures that we can drop it before we close the `Display`.
|
||||
@@ -38,6 +41,12 @@ pub(crate) struct XConnection {
|
||||
/// The last timestamp received by this connection.
|
||||
timestamp: AtomicU32,
|
||||
|
||||
/// List of monitor handles.
|
||||
pub monitor_handles: Mutex<Option<Vec<MonitorHandle>>>,
|
||||
|
||||
/// The resource database.
|
||||
database: resource_manager::Database,
|
||||
|
||||
pub latest_error: Mutex<Option<XError>>,
|
||||
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>,
|
||||
}
|
||||
@@ -53,9 +62,8 @@ impl XConnection {
|
||||
// opening the libraries
|
||||
let xlib = ffi::Xlib::open()?;
|
||||
let xcursor = ffi::Xcursor::open()?;
|
||||
let xrandr = ffi::Xrandr_2_2_0::open()?;
|
||||
let xinput2 = ffi::XInput2::open()?;
|
||||
let xlib_xcb = ffi::Xlib_xcb::open()?;
|
||||
let xinput2 = ffi::XInput2::open()?;
|
||||
|
||||
unsafe { (xlib.XInitThreads)() };
|
||||
unsafe { (xlib.XSetErrorHandler)(error_handler) };
|
||||
@@ -92,9 +100,12 @@ impl XConnection {
|
||||
.reply()
|
||||
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
||||
|
||||
// Load the database.
|
||||
let database = resource_manager::new_from_default(&xcb)
|
||||
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
||||
|
||||
Ok(XConnection {
|
||||
xlib,
|
||||
xrandr,
|
||||
xcursor,
|
||||
xinput2,
|
||||
display,
|
||||
@@ -103,6 +114,8 @@ impl XConnection {
|
||||
default_screen,
|
||||
timestamp: AtomicU32::new(0),
|
||||
latest_error: Mutex::new(None),
|
||||
monitor_handles: Mutex::new(None),
|
||||
database,
|
||||
cursor_cache: Default::default(),
|
||||
})
|
||||
}
|
||||
@@ -144,6 +157,12 @@ impl XConnection {
|
||||
&self.xcb_connection().setup().roots[self.default_screen]
|
||||
}
|
||||
|
||||
/// Get the resource database.
|
||||
#[inline]
|
||||
pub fn database(&self) -> &resource_manager::Database {
|
||||
&self.database
|
||||
}
|
||||
|
||||
/// Get the latest timestamp.
|
||||
#[inline]
|
||||
pub fn timestamp(&self) -> u32 {
|
||||
|
||||
@@ -4,7 +4,7 @@ use icrate::Foundation::NSObject;
|
||||
use objc2::{declare_class, msg_send, mutability, ClassType};
|
||||
|
||||
use super::appkit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
||||
use super::{app_state::AppState, event::EventWrapper, DEVICE_ID};
|
||||
use super::{app_state::AppState, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, ElementState, Event};
|
||||
|
||||
declare_class!(
|
||||
@@ -96,5 +96,5 @@ fn queue_device_event(event: DeviceEvent) {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
};
|
||||
AppState::queue_event(EventWrapper::StaticEvent(event));
|
||||
AppState::queue_event(event);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ declare_class!(
|
||||
fn will_terminate(&self, _sender: Option<&AnyObject>) {
|
||||
trace_scope!("applicationWillTerminate:");
|
||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||
AppState::exit();
|
||||
AppState::internal_exit();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -6,29 +6,24 @@ use std::{
|
||||
rc::{Rc, Weak},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex, MutexGuard,
|
||||
mpsc, Arc, Mutex, MutexGuard,
|
||||
},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp};
|
||||
use icrate::Foundation::{is_main_thread, NSSize};
|
||||
use objc2::rc::autoreleasepool;
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent};
|
||||
use super::{
|
||||
event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never, window::WinitWindow,
|
||||
};
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
|
||||
platform_impl::platform::{
|
||||
event::{EventProxy, EventWrapper},
|
||||
event_loop::PanicInfo,
|
||||
menu,
|
||||
observer::EventLoopWaker,
|
||||
util::Never,
|
||||
window::WinitWindow,
|
||||
},
|
||||
window::WindowId,
|
||||
};
|
||||
|
||||
@@ -45,24 +40,22 @@ impl<Never> Event<Never> {
|
||||
|
||||
pub trait EventHandler: Debug {
|
||||
// Not sure probably it should accept Event<'static, Never>
|
||||
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_nonuser_event(&mut self, event: Event<Never>);
|
||||
fn handle_user_events(&mut self);
|
||||
}
|
||||
|
||||
pub(crate) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>;
|
||||
pub(crate) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>;
|
||||
|
||||
struct EventLoopHandler<T: 'static> {
|
||||
callback: Weak<Callback<T>>,
|
||||
window_target: Rc<RootWindowTarget<T>>,
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
}
|
||||
|
||||
impl<T> EventLoopHandler<T> {
|
||||
fn with_callback<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnOnce(
|
||||
&mut EventLoopHandler<T>,
|
||||
RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>,
|
||||
),
|
||||
F: FnOnce(&mut EventLoopHandler<T>, RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>)>),
|
||||
{
|
||||
// The `NSApp` and our `HANDLER` are global state and so it's possible that
|
||||
// we could get a delegate callback after the application has exit an
|
||||
@@ -89,33 +82,31 @@ impl<T> Debug for EventLoopHandler<T> {
|
||||
}
|
||||
|
||||
impl<T> EventHandler for EventLoopHandler<T> {
|
||||
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
|
||||
fn handle_nonuser_event(&mut self, event: Event<Never>) {
|
||||
self.with_callback(|this, mut callback| {
|
||||
if let ControlFlow::ExitWithCode(code) = *control_flow {
|
||||
// XXX: why isn't event dispatching simply skipped after control_flow = ExitWithCode?
|
||||
let dummy = &mut ControlFlow::ExitWithCode(code);
|
||||
(callback)(event.userify(), &this.window_target, dummy);
|
||||
} else {
|
||||
(callback)(event.userify(), &this.window_target, control_flow);
|
||||
}
|
||||
(callback)(event.userify(), &this.window_target);
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
|
||||
fn handle_user_events(&mut self) {
|
||||
self.with_callback(|this, mut callback| {
|
||||
for event in this.window_target.p.receiver.try_iter() {
|
||||
if let ControlFlow::ExitWithCode(code) = *control_flow {
|
||||
// XXX: why isn't event dispatching simply skipped after control_flow = ExitWithCode?
|
||||
let dummy = &mut ControlFlow::ExitWithCode(code);
|
||||
(callback)(Event::UserEvent(event), &this.window_target, dummy);
|
||||
} else {
|
||||
(callback)(Event::UserEvent(event), &this.window_target, control_flow);
|
||||
}
|
||||
for event in this.receiver.try_iter() {
|
||||
(callback)(Event::UserEvent(event), &this.window_target);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum EventWrapper {
|
||||
StaticEvent(Event<Never>),
|
||||
ScaleFactorChanged {
|
||||
window: Id<WinitWindow>,
|
||||
suggested_size: PhysicalSize<u32>,
|
||||
scale_factor: f64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Handler {
|
||||
stop_app_on_launch: AtomicBool,
|
||||
@@ -126,6 +117,7 @@ struct Handler {
|
||||
running: AtomicBool,
|
||||
in_callback: AtomicBool,
|
||||
control_flow: Mutex<ControlFlow>,
|
||||
exit: AtomicBool,
|
||||
start_time: Mutex<Option<Instant>>,
|
||||
callback: Mutex<Option<Box<dyn EventHandler>>>,
|
||||
pending_events: Mutex<VecDeque<EventWrapper>>,
|
||||
@@ -183,13 +175,6 @@ impl Handler {
|
||||
self.running.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn should_exit(&self) -> bool {
|
||||
matches!(
|
||||
*self.control_flow.lock().unwrap(),
|
||||
ControlFlow::ExitWithCode(_)
|
||||
)
|
||||
}
|
||||
|
||||
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits
|
||||
///
|
||||
/// Since an `EventLoop` may be run more than once we need make sure to reset the
|
||||
@@ -200,7 +185,7 @@ impl Handler {
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
fn exit(&self) {
|
||||
fn internal_exit(&self) {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interiour mutability
|
||||
//
|
||||
@@ -214,13 +199,20 @@ impl Handler {
|
||||
// looks like there have been recuring re-entrancy issues with callback handling that might
|
||||
// make that awkward)
|
||||
self.running.store(false, Ordering::Relaxed);
|
||||
*self.control_flow.lock().unwrap() = ControlFlow::default();
|
||||
self.set_stop_app_on_redraw_requested(false);
|
||||
self.set_stop_app_before_wait(false);
|
||||
self.set_stop_app_after_wait(false);
|
||||
self.set_wait_timeout(None);
|
||||
}
|
||||
|
||||
pub fn exit(&self) {
|
||||
self.exit.store(true, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn exiting(&self) -> bool {
|
||||
self.exit.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn request_stop_app_on_launch(&self) {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interior mutability
|
||||
@@ -281,6 +273,10 @@ impl Handler {
|
||||
self.stop_app_on_redraw.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn set_control_flow(&self, new_control_flow: ControlFlow) {
|
||||
*self.control_flow.lock().unwrap() = new_control_flow
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
*self.control_flow.lock().unwrap()
|
||||
}
|
||||
@@ -313,60 +309,41 @@ impl Handler {
|
||||
self.callback.lock().unwrap().is_some()
|
||||
}
|
||||
|
||||
fn handle_nonuser_event(&self, wrapper: EventWrapper) {
|
||||
fn handle_nonuser_event(&self, event: Event<Never>) {
|
||||
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||
match wrapper {
|
||||
EventWrapper::StaticEvent(event) => {
|
||||
callback.handle_nonuser_event(event, &mut self.control_flow.lock().unwrap())
|
||||
}
|
||||
EventWrapper::EventProxy(proxy) => self.handle_proxy(proxy, callback),
|
||||
}
|
||||
callback.handle_nonuser_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_user_events(&self) {
|
||||
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||
callback.handle_user_events(&mut self.control_flow.lock().unwrap());
|
||||
callback.handle_user_events();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_scale_factor_changed_event(
|
||||
&self,
|
||||
callback: &mut Box<dyn EventHandler + 'static>,
|
||||
window: &WinitWindow,
|
||||
suggested_size: LogicalSize<f64>,
|
||||
suggested_size: PhysicalSize<u32>,
|
||||
scale_factor: f64,
|
||||
) {
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size.to_physical(scale_factor)));
|
||||
let event = Event::WindowEvent {
|
||||
window_id: WindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
},
|
||||
};
|
||||
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
let event = Event::WindowEvent {
|
||||
window_id: WindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
},
|
||||
};
|
||||
|
||||
callback.handle_nonuser_event(event, &mut self.control_flow.lock().unwrap());
|
||||
callback.handle_nonuser_event(event);
|
||||
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
}
|
||||
|
||||
fn handle_proxy(&self, proxy: EventProxy, callback: &mut Box<dyn EventHandler + 'static>) {
|
||||
match proxy {
|
||||
EventProxy::DpiChangedProxy {
|
||||
window,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
} => self.handle_scale_factor_changed_event(
|
||||
callback,
|
||||
&window,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
),
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -381,16 +358,18 @@ impl AppState {
|
||||
/// and can lead to undefined behaviour if the callback is not cleared before the end of
|
||||
/// its real lifetime.
|
||||
///
|
||||
/// All public APIs that take an event callback (`run`, `run_ondemand`,
|
||||
/// All public APIs that take an event callback (`run`, `run_on_demand`,
|
||||
/// `pump_events`) _must_ pair a call to `set_callback` with
|
||||
/// a call to `clear_callback` before returning to avoid undefined behaviour.
|
||||
pub unsafe fn set_callback<T>(
|
||||
callback: Weak<Callback<T>>,
|
||||
window_target: Rc<RootWindowTarget<T>>,
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
) {
|
||||
*HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler {
|
||||
callback,
|
||||
window_target,
|
||||
receiver,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -429,31 +408,36 @@ impl AppState {
|
||||
HANDLER.set_stop_app_on_redraw_requested(stop_on_redraw);
|
||||
}
|
||||
|
||||
pub fn set_control_flow(control_flow: ControlFlow) {
|
||||
HANDLER.set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
pub fn control_flow() -> ControlFlow {
|
||||
HANDLER.control_flow()
|
||||
}
|
||||
|
||||
pub fn exit() -> i32 {
|
||||
pub fn internal_exit() {
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::LoopExiting));
|
||||
HANDLER.handle_nonuser_event(Event::LoopExiting);
|
||||
HANDLER.set_in_callback(false);
|
||||
HANDLER.exit();
|
||||
HANDLER.internal_exit();
|
||||
Self::clear_callback();
|
||||
if let ControlFlow::ExitWithCode(code) = HANDLER.control_flow() {
|
||||
code
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit() {
|
||||
HANDLER.exit()
|
||||
}
|
||||
|
||||
pub fn exiting() -> bool {
|
||||
HANDLER.exiting()
|
||||
}
|
||||
|
||||
pub fn dispatch_init_events() {
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents(
|
||||
StartCause::Init,
|
||||
)));
|
||||
HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init));
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though macOS
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed));
|
||||
HANDLER.handle_nonuser_event(Event::Resumed);
|
||||
HANDLER.set_in_callback(false);
|
||||
}
|
||||
|
||||
@@ -541,10 +525,9 @@ impl AppState {
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlFlow::ExitWithCode(_) => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"),
|
||||
};
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents(cause)));
|
||||
HANDLER.handle_nonuser_event(Event::NewEvents(cause));
|
||||
HANDLER.set_in_callback(false);
|
||||
}
|
||||
|
||||
@@ -564,8 +547,10 @@ impl AppState {
|
||||
// Redraw request might come out of order from the OS.
|
||||
// -> Don't go back into the callback when our callstack originates from there
|
||||
if !HANDLER.in_callback.swap(true, Ordering::AcqRel) {
|
||||
HANDLER
|
||||
.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawRequested(window_id)));
|
||||
HANDLER.handle_nonuser_event(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
HANDLER.set_in_callback(false);
|
||||
|
||||
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested events
|
||||
@@ -576,11 +561,25 @@ impl AppState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_event(wrapper: EventWrapper) {
|
||||
pub fn queue_event(event: Event<Never>) {
|
||||
if !is_main_thread() {
|
||||
panic!("Event queued from different thread: {wrapper:#?}");
|
||||
panic!("Event queued from different thread: {event:#?}");
|
||||
}
|
||||
HANDLER.events().push_back(wrapper);
|
||||
HANDLER.events().push_back(EventWrapper::StaticEvent(event));
|
||||
}
|
||||
|
||||
pub fn queue_static_scale_factor_changed_event(
|
||||
window: Id<WinitWindow>,
|
||||
suggested_size: PhysicalSize<u32>,
|
||||
scale_factor: f64,
|
||||
) {
|
||||
HANDLER
|
||||
.events()
|
||||
.push_back(EventWrapper::ScaleFactorChanged {
|
||||
window,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn stop() {
|
||||
@@ -612,18 +611,35 @@ impl AppState {
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_user_events();
|
||||
for event in HANDLER.take_events() {
|
||||
HANDLER.handle_nonuser_event(event);
|
||||
match event {
|
||||
EventWrapper::StaticEvent(event) => {
|
||||
HANDLER.handle_nonuser_event(event);
|
||||
}
|
||||
EventWrapper::ScaleFactorChanged {
|
||||
window,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
} => {
|
||||
HANDLER.handle_scale_factor_changed_event(
|
||||
&window,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for window_id in HANDLER.should_redraw() {
|
||||
HANDLER
|
||||
.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawRequested(window_id)));
|
||||
HANDLER.handle_nonuser_event(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
}
|
||||
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::AboutToWait));
|
||||
HANDLER.handle_nonuser_event(Event::AboutToWait);
|
||||
HANDLER.set_in_callback(false);
|
||||
|
||||
if HANDLER.should_exit() {
|
||||
if HANDLER.exiting() {
|
||||
Self::stop();
|
||||
}
|
||||
|
||||
@@ -634,7 +650,7 @@ impl AppState {
|
||||
let wait_timeout = HANDLER.wait_timeout(); // configured by pump_events
|
||||
let app_timeout = match HANDLER.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll | ControlFlow::ExitWithCode(_) => Some(Instant::now()),
|
||||
ControlFlow::Poll => Some(Instant::now()),
|
||||
ControlFlow::WaitUntil(instant) => Some(instant),
|
||||
};
|
||||
HANDLER
|
||||
|
||||
@@ -44,9 +44,6 @@ extern_methods!(
|
||||
// _mtm: MainThreadMarker,
|
||||
) -> Option<Id<NSTextInputContext>>;
|
||||
|
||||
#[method(visibleRect)]
|
||||
pub fn visibleRect(&self) -> NSRect;
|
||||
|
||||
#[method(hasMarkedText)]
|
||||
pub fn hasMarkedText(&self) -> bool;
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ extern_methods!(
|
||||
#[method(miniaturize:)]
|
||||
pub(crate) fn miniaturize(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(sender:)]
|
||||
#[method(deminiaturize:)]
|
||||
pub(crate) fn deminiaturize(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(toggleFullScreen:)]
|
||||
|
||||
@@ -5,15 +5,11 @@ use core_foundation::{
|
||||
data::{CFDataGetBytePtr, CFDataRef},
|
||||
};
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
use objc2::rc::Id;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use super::appkit::{NSEvent, NSEventModifierFlags};
|
||||
use super::util::Never;
|
||||
use super::window::WinitWindow;
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyEvent, Modifiers},
|
||||
event::{ElementState, KeyEvent, Modifiers},
|
||||
keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NativeKey, NativeKeyCode,
|
||||
},
|
||||
@@ -21,21 +17,6 @@ use crate::{
|
||||
platform_impl::platform::ffi,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum EventWrapper {
|
||||
StaticEvent(Event<Never>),
|
||||
EventProxy(EventProxy),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum EventProxy {
|
||||
DpiChangedProxy {
|
||||
window: Id<WinitWindow>,
|
||||
suggested_size: LogicalSize<f64>,
|
||||
scale_factor: f64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
pub text_with_all_modifiers: Option<SmolStr>,
|
||||
@@ -115,7 +96,7 @@ fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
|
||||
let string = ns_event
|
||||
.charactersIgnoringModifiers()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(String::new);
|
||||
.unwrap_or_default();
|
||||
if string.is_empty() {
|
||||
// Probably a dead key
|
||||
let first_char = modifierless_chars.chars().next();
|
||||
@@ -145,7 +126,7 @@ pub(crate) fn create_key_event(
|
||||
let characters = ns_event
|
||||
.characters()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(String::new);
|
||||
.unwrap_or_default();
|
||||
if characters.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
||||
@@ -19,14 +19,16 @@ use core_foundation::runloop::{
|
||||
};
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use objc2::runtime::NSObjectProtocol;
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
use raw_window_handle::{AppKitDisplayHandle, RawDisplayHandle};
|
||||
|
||||
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent, NSWindow};
|
||||
use crate::{
|
||||
error::EventLoopError,
|
||||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
|
||||
event_loop::{
|
||||
ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget,
|
||||
},
|
||||
platform::{macos::ActivationPolicy, pump_events::PumpStatus},
|
||||
platform_impl::platform::{
|
||||
app::WinitApplication,
|
||||
@@ -65,9 +67,10 @@ impl PanicInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventLoopWindowTarget<T: 'static> {
|
||||
pub receiver: mpsc::Receiver<T>,
|
||||
mtm: MainThreadMarker,
|
||||
p: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopWindowTarget<T> {
|
||||
@@ -83,8 +86,38 @@ impl<T: 'static> EventLoopWindowTarget<T> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty())
|
||||
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::RawDisplayHandle::AppKit(rwh_05::AppKitDisplayHandle::empty())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::AppKit(
|
||||
rwh_06::AppKitDisplayHandle::new(),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
AppState::set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
pub(crate) fn control_flow(&self) -> ControlFlow {
|
||||
AppState::control_flow()
|
||||
}
|
||||
|
||||
pub(crate) fn exit(&self) {
|
||||
AppState::exit()
|
||||
}
|
||||
|
||||
pub(crate) fn exiting(&self) -> bool {
|
||||
AppState::exiting()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,14 +140,21 @@ impl<T> EventLoopWindowTarget<T> {
|
||||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
/// Store a reference to the application for convenience.
|
||||
///
|
||||
/// We intentially don't store `WinitApplication` since we want to have
|
||||
/// the possiblity of swapping that out at some point.
|
||||
app: Id<NSApplication>,
|
||||
/// The delegate is only weakly referenced by NSApplication, so we keep
|
||||
/// it around here as well.
|
||||
_delegate: Id<ApplicationDelegate>,
|
||||
|
||||
// Event sender and receiver, used for EventLoopProxy.
|
||||
sender: mpsc::Sender<T>,
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
|
||||
window_target: Rc<RootWindowTarget<T>>,
|
||||
panic_info: Rc<PanicInfo>,
|
||||
mtm: MainThreadMarker,
|
||||
|
||||
/// We make sure that the callback closure is dropped during a panic
|
||||
/// by making the event loop own it.
|
||||
@@ -147,15 +187,15 @@ impl<T> EventLoop<T> {
|
||||
attributes: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<Self, EventLoopError> {
|
||||
let mtm = MainThreadMarker::new()
|
||||
.expect("On macOS, `EventLoop` must be created on the main thread!");
|
||||
.expect("on macOS, `EventLoop` must be created on the main thread!");
|
||||
|
||||
// This must be done before `NSApp()` (equivalent to sending
|
||||
// `sharedApplication`) is called anywhere else, or we'll end up
|
||||
// with the wrong `NSApplication` class and the wrong thread could
|
||||
// be marked as main.
|
||||
let app: Id<WinitApplication> =
|
||||
let app: Id<NSApplication> =
|
||||
unsafe { msg_send_id![WinitApplication::class(), sharedApplication] };
|
||||
|
||||
if !app.is_kind_of::<WinitApplication>() {
|
||||
panic!("`winit` requires control over the principal class. You must create the event loop before other parts of your application initialize NSApplication");
|
||||
}
|
||||
|
||||
use NSApplicationActivationPolicy::*;
|
||||
let activation_policy = match attributes.activation_policy {
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
|
||||
@@ -177,13 +217,17 @@ impl<T> EventLoop<T> {
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
Ok(EventLoop {
|
||||
app,
|
||||
_delegate: delegate,
|
||||
sender,
|
||||
receiver: Rc::new(receiver),
|
||||
window_target: Rc::new(RootWindowTarget {
|
||||
p: EventLoopWindowTarget { receiver, mtm },
|
||||
p: EventLoopWindowTarget {
|
||||
mtm,
|
||||
p: PhantomData,
|
||||
},
|
||||
_marker: PhantomData,
|
||||
}),
|
||||
mtm,
|
||||
panic_info,
|
||||
_callback: None,
|
||||
})
|
||||
@@ -195,18 +239,18 @@ impl<T> EventLoop<T> {
|
||||
|
||||
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootWindowTarget<T>),
|
||||
{
|
||||
self.run_ondemand(callback)
|
||||
self.run_on_demand(callback)
|
||||
}
|
||||
|
||||
// NB: we don't base this on `pump_events` because for `MacOs` we can't support
|
||||
// `pump_events` elegantly (we just ask to run the loop for a "short" amount of
|
||||
// time and so a layered implementation would end up using a lot of CPU due to
|
||||
// redundant wake ups.
|
||||
pub fn run_ondemand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
|
||||
pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootWindowTarget<T>),
|
||||
{
|
||||
if AppState::is_running() {
|
||||
return Err(EventLoopError::AlreadyRunning);
|
||||
@@ -223,16 +267,14 @@ impl<T> EventLoop<T> {
|
||||
|
||||
let callback = unsafe {
|
||||
mem::transmute::<
|
||||
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
|
||||
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
|
||||
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
|
||||
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
|
||||
>(Rc::new(RefCell::new(callback)))
|
||||
};
|
||||
|
||||
self._callback = Some(Rc::clone(&callback));
|
||||
|
||||
let exit_code = autoreleasepool(|_| {
|
||||
let app = NSApplication::shared(self.mtm);
|
||||
|
||||
autoreleasepool(|_| {
|
||||
// A bit of juggling with the callback references to make sure
|
||||
// that `self.callback` is the only owner of the callback.
|
||||
let weak_cb: Weak<_> = Rc::downgrade(&callback);
|
||||
@@ -241,7 +283,11 @@ impl<T> EventLoop<T> {
|
||||
// # Safety
|
||||
// We make sure to call `AppState::clear_callback` before returning
|
||||
unsafe {
|
||||
AppState::set_callback(weak_cb, Rc::clone(&self.window_target));
|
||||
AppState::set_callback(
|
||||
weak_cb,
|
||||
Rc::clone(&self.window_target),
|
||||
Rc::clone(&self.receiver),
|
||||
);
|
||||
}
|
||||
|
||||
// catch panics to make sure we can't unwind without clearing the set callback
|
||||
@@ -257,7 +303,7 @@ impl<T> EventLoop<T> {
|
||||
debug_assert!(!AppState::is_running());
|
||||
AppState::start_running(); // Set is_running = true + dispatch `NewEvents(Init)` + `Resumed`
|
||||
}
|
||||
unsafe { app.run() };
|
||||
unsafe { self.app.run() };
|
||||
|
||||
// While the app is running it's possible that we catch a panic
|
||||
// to avoid unwinding across an objective-c ffi boundary, which
|
||||
@@ -268,7 +314,7 @@ impl<T> EventLoop<T> {
|
||||
resume_unwind(panic);
|
||||
}
|
||||
|
||||
AppState::exit()
|
||||
AppState::internal_exit()
|
||||
}));
|
||||
|
||||
// # Safety
|
||||
@@ -278,22 +324,17 @@ impl<T> EventLoop<T> {
|
||||
drop(self._callback.take());
|
||||
AppState::clear_callback();
|
||||
|
||||
match catch_result {
|
||||
Ok(exit_code) => exit_code,
|
||||
Err(payload) => resume_unwind(payload),
|
||||
if let Err(payload) = catch_result {
|
||||
resume_unwind(payload)
|
||||
}
|
||||
});
|
||||
|
||||
if exit_code == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EventLoopError::ExitFailure(exit_code))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
|
||||
F: FnMut(Event<T>, &RootWindowTarget<T>),
|
||||
{
|
||||
// # Safety
|
||||
// We are erasing the lifetime of the application callback here so that we
|
||||
@@ -306,8 +347,8 @@ impl<T> EventLoop<T> {
|
||||
|
||||
let callback = unsafe {
|
||||
mem::transmute::<
|
||||
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
|
||||
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
|
||||
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
|
||||
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
|
||||
>(Rc::new(RefCell::new(callback)))
|
||||
};
|
||||
|
||||
@@ -326,7 +367,11 @@ impl<T> EventLoop<T> {
|
||||
// to ensure that we don't hold on to the callback beyond its (erased)
|
||||
// lifetime
|
||||
unsafe {
|
||||
AppState::set_callback(weak_cb, Rc::clone(&self.window_target));
|
||||
AppState::set_callback(
|
||||
weak_cb,
|
||||
Rc::clone(&self.window_target),
|
||||
Rc::clone(&self.receiver),
|
||||
);
|
||||
}
|
||||
|
||||
// catch panics to make sure we can't unwind without clearing the set callback
|
||||
@@ -383,9 +428,9 @@ impl<T> EventLoop<T> {
|
||||
resume_unwind(panic);
|
||||
}
|
||||
|
||||
if let ControlFlow::ExitWithCode(code) = AppState::control_flow() {
|
||||
AppState::exit();
|
||||
PumpStatus::Exit(code)
|
||||
if AppState::exiting() {
|
||||
AppState::internal_exit();
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
PumpStatus::Continue
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ use crate::{
|
||||
platform::scancode::KeyCodeExtScancode,
|
||||
platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
event::{create_key_event, event_mods, EventWrapper},
|
||||
event::{create_key_event, event_mods},
|
||||
util,
|
||||
window::WinitWindow,
|
||||
DEVICE_ID,
|
||||
@@ -212,7 +212,7 @@ declare_class!(
|
||||
self.removeTrackingRect(tracking_rect);
|
||||
}
|
||||
|
||||
let rect = self.visibleRect();
|
||||
let rect = self.frame();
|
||||
let tracking_rect = self.add_tracking_rect(rect, false);
|
||||
self.state.tracking_rect.set(Some(tracking_rect));
|
||||
}
|
||||
@@ -224,7 +224,7 @@ declare_class!(
|
||||
self.removeTrackingRect(tracking_rect);
|
||||
}
|
||||
|
||||
let rect = self.visibleRect();
|
||||
let rect = self.frame();
|
||||
let tracking_rect = self.add_tracking_rect(rect, false);
|
||||
self.state.tracking_rect.set(Some(tracking_rect));
|
||||
|
||||
@@ -826,7 +826,7 @@ impl WinitView {
|
||||
window_id: self.window_id(),
|
||||
event,
|
||||
};
|
||||
AppState::queue_event(EventWrapper::StaticEvent(event));
|
||||
AppState::queue_event(event);
|
||||
}
|
||||
|
||||
fn queue_device_event(&self, event: DeviceEvent) {
|
||||
@@ -834,7 +834,7 @@ impl WinitView {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
};
|
||||
AppState::queue_event(EventWrapper::StaticEvent(event));
|
||||
AppState::queue_event(event);
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
@@ -850,7 +850,7 @@ impl WinitView {
|
||||
.expect("input context")
|
||||
.selectedKeyboardInputSource()
|
||||
.map(|input_source| input_source.to_string())
|
||||
.unwrap_or_else(String::new)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(super) fn set_cursor_icon(&self, icon: Id<NSCursor>) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user