mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
120 Commits
v0.30.2
...
madsmtm/do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1754bf14f | ||
|
|
69783445f1 | ||
|
|
b674d20edf | ||
|
|
8db3e0e043 | ||
|
|
d37c591378 | ||
|
|
9419e4e1a7 | ||
|
|
241b7a80bb | ||
|
|
e716adcc0a | ||
|
|
aee95114db | ||
|
|
8f4a8efa99 | ||
|
|
1e1f0fd7e9 | ||
|
|
6e008b39e9 | ||
|
|
6c4da19197 | ||
|
|
a61e7bb4f4 | ||
|
|
7fbc2118b6 | ||
|
|
a96491f302 | ||
|
|
d96fd02f33 | ||
|
|
92e9bfe0fc | ||
|
|
3392e9c1de | ||
|
|
038ef5c3ad | ||
|
|
70c54ee0ff | ||
|
|
3bab4ef4fb | ||
|
|
58142680ce | ||
|
|
0ffcfd8a3a | ||
|
|
2e53533cc1 | ||
|
|
1168cd4113 | ||
|
|
9dff801f93 | ||
|
|
f07153b8e0 | ||
|
|
f5304815a1 | ||
|
|
29e1041987 | ||
|
|
15b79b18e1 | ||
|
|
54ff9c3bb5 | ||
|
|
546962c904 | ||
|
|
b7a7f59298 | ||
|
|
586255ac0a | ||
|
|
42ba0a74e0 | ||
|
|
34a37b8747 | ||
|
|
02a0a91a94 | ||
|
|
e3fbfb81d7 | ||
|
|
0909bf3d9d | ||
|
|
3398ebe467 | ||
|
|
21c121f9b3 | ||
|
|
ce0d1dfe1b | ||
|
|
7892e86731 | ||
|
|
7b0104b54c | ||
|
|
facb809f12 | ||
|
|
4e2e764e4a | ||
|
|
a0bc3e5dc8 | ||
|
|
2e97ab3d4f | ||
|
|
ef580b817d | ||
|
|
5ec934b1b0 | ||
|
|
c9c260ca08 | ||
|
|
b6109d4a17 | ||
|
|
eef2848c98 | ||
|
|
73c01fff96 | ||
|
|
88bbdb33da | ||
|
|
652ff7576c | ||
|
|
d741c58ac3 | ||
|
|
944ab60eda | ||
|
|
8e23d1608a | ||
|
|
dc99920612 | ||
|
|
ee3ab33a7c | ||
|
|
5b8f5cb54a | ||
|
|
bf97def398 | ||
|
|
d5fd8682eb | ||
|
|
39a7d5b738 | ||
|
|
2665c12098 | ||
|
|
936da131c2 | ||
|
|
850dd97177 | ||
|
|
1170554dbd | ||
|
|
75ce71f05a | ||
|
|
a0d69c782a | ||
|
|
2e93e48a3b | ||
|
|
82d9bbe559 | ||
|
|
8bdd4d620e | ||
|
|
9d5412ffe1 | ||
|
|
ecb887e5c3 | ||
|
|
7d1287958f | ||
|
|
c0c14aaf00 | ||
|
|
10dc0674bb | ||
|
|
4f59796e8a | ||
|
|
32097d75c7 | ||
|
|
c6c4395c3b | ||
|
|
38e6f9ad84 | ||
|
|
3e6092b8ed | ||
|
|
b4e83a5966 | ||
|
|
db2c97a995 | ||
|
|
1552eb21f7 | ||
|
|
d8ffd4bb26 | ||
|
|
34c15608e0 | ||
|
|
eef6977f45 | ||
|
|
078b46720b | ||
|
|
3b4e064a07 | ||
|
|
39bc139500 | ||
|
|
9522670081 | ||
|
|
9a1ef49dc3 | ||
|
|
3a624e0f52 | ||
|
|
279e3edc54 | ||
|
|
0e74d37ff5 | ||
|
|
2d1382f7d6 | ||
|
|
5d8091fc7f | ||
|
|
d7abe0316e | ||
|
|
5ea20fc905 | ||
|
|
e108fa2fbf | ||
|
|
fff6788c12 | ||
|
|
3e8fa41073 | ||
|
|
2b1c8cea1b | ||
|
|
ab33fb8eda | ||
|
|
b0b64a9a15 | ||
|
|
d5d202d60e | ||
|
|
cb39ab29f4 | ||
|
|
0a3cacd577 | ||
|
|
16fd2baba0 | ||
|
|
7f8771a362 | ||
|
|
337d50779c | ||
|
|
fd477986de | ||
|
|
94664ff687 | ||
|
|
0fc8c37721 | ||
|
|
ec29c81ad2 | ||
|
|
304a585493 |
@@ -2,5 +2,8 @@
|
||||
#
|
||||
# Note that these flags are (intentionally) not included when building from the downloaded crate.
|
||||
[build]
|
||||
rustflags = ["--cfg=unreleased_changelogs"]
|
||||
rustdocflags = ["--cfg=unreleased_changelogs"]
|
||||
rustflags = ["--cfg=unreleased_changelogs"]
|
||||
|
||||
[target.wasm32-unknown-unknown]
|
||||
runner = "wasm-bindgen-test-runner"
|
||||
|
||||
@@ -2,3 +2,7 @@
|
||||
|
||||
# chore(rustfmt): use nightly
|
||||
7b0c7b6cb2c62767ca0c73c857b299883f55a883
|
||||
# Rustfmt: use `group_imports`
|
||||
2665c120981af548433645c6383b3580dd8f8fc4
|
||||
# Use Taplo for TOML formatting
|
||||
3398ebe467c43ccfd91916c5b81ff3c68f598556
|
||||
|
||||
17
.github/CODEOWNERS
vendored
17
.github/CODEOWNERS
vendored
@@ -1,10 +1,11 @@
|
||||
# Android
|
||||
/src/platform/android.rs @msiglreith @MarijnS95
|
||||
/src/platform_impl/android @msiglreith @MarijnS95
|
||||
/src/platform/android.rs @MarijnS95
|
||||
/src/platform_impl/android @MarijnS95
|
||||
|
||||
# iOS
|
||||
# Apple (AppKit + UIKit)
|
||||
/src/platform/ios.rs @madsmtm
|
||||
/src/platform_impl/ios @madsmtm
|
||||
/src/platform/macos.rs @madsmtm
|
||||
/src/platform_impl/apple @madsmtm
|
||||
|
||||
# Unix
|
||||
/src/platform_impl/linux/mod.rs @kchibisov
|
||||
@@ -17,17 +18,13 @@
|
||||
/src/platform/x11.rs @kchibisov @notgull
|
||||
/src/platform_impl/linux/x11 @kchibisov @notgull
|
||||
|
||||
# macOS
|
||||
/src/platform/macos.rs @madsmtm
|
||||
/src/platform_impl/macos @madsmtm
|
||||
|
||||
# Web
|
||||
/src/platform/web.rs @daxpedda
|
||||
/src/platform_impl/web @daxpedda
|
||||
|
||||
# Windows
|
||||
/src/platform/windows.rs @msiglreith
|
||||
/src/platform_impl/windows @msiglreith
|
||||
/src/platform/windows.rs @notgull
|
||||
/src/platform_impl/windows @notgull
|
||||
|
||||
# Orbital (Redox OS)
|
||||
/src/platform/orbital.rs @jackpot51
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_web.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_web.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Web bug
|
||||
description: Create a web-specific bug report
|
||||
description: Create a Web-specific bug report
|
||||
labels:
|
||||
- B - bug
|
||||
- DS - web
|
||||
|
||||
22
.github/dependabot.yaml
vendored
Normal file
22
.github/dependabot.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: src/platform_impl/web/script
|
||||
schedule:
|
||||
interval: daily
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- '*'
|
||||
labels:
|
||||
- "DS - web"
|
||||
131
.github/workflows/ci.yml
vendored
131
.github/workflows/ci.yml
vendored
@@ -10,19 +10,32 @@ jobs:
|
||||
name: Check formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: taiki-e/checkout-action@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rustfmt
|
||||
- name: Check Formatting
|
||||
run: cargo fmt -- --check
|
||||
|
||||
taplo:
|
||||
name: Taplo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: taiki-e/checkout-action@v1
|
||||
- name: Install Taplo
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: taplo-cli
|
||||
- name: Run Taplo
|
||||
run: taplo fmt --check
|
||||
|
||||
typos:
|
||||
name: Check for typos
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: taiki-e/checkout-action@v1
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: typos-cli
|
||||
@@ -42,7 +55,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain: [stable, nightly, '1.70.0']
|
||||
toolchain: [stable, nightly, '1.73']
|
||||
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, }
|
||||
@@ -55,25 +68,35 @@ jobs:
|
||||
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
|
||||
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
- { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, }
|
||||
- { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { name: 'macOS x86_64', target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { name: 'macOS Aarch64', target: aarch64-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, }
|
||||
- { name: 'Web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
|
||||
exclude:
|
||||
# Web on nightly needs extra arguments
|
||||
- toolchain: nightly
|
||||
platform: { name: 'Web', target: wasm32-unknown-unknown, os: ubuntu-latest }
|
||||
# Android is tested on stable-3
|
||||
- toolchain: '1.70.0'
|
||||
- toolchain: '1.73'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
# Redox OS doesn't follow MSRV
|
||||
- toolchain: '1.73'
|
||||
platform: { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest }
|
||||
include:
|
||||
- toolchain: '1.70.0'
|
||||
- toolchain: '1.73'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
- toolchain: 'nightly'
|
||||
platform: { name: 'Web', target: wasm32-unknown-unknown, os: ubuntu-latest, test-options: -Zdoctest-xcompile }
|
||||
- toolchain: 'nightly'
|
||||
platform: {
|
||||
name: 'web Atomic',
|
||||
target: wasm32-unknown-unknown,
|
||||
os: ubuntu-latest,
|
||||
options: '-Zbuild-std=panic_abort,std',
|
||||
rustflags: '-Ctarget-feature=+atomics,+bulk-memory',
|
||||
components: rust-src,
|
||||
name: 'Web Atomic',
|
||||
target: wasm32-unknown-unknown,
|
||||
os: ubuntu-latest,
|
||||
options: '-Zbuild-std=panic_abort,std',
|
||||
test-options: -Zdoctest-xcompile,
|
||||
rustflags: '-Ctarget-feature=+atomics,+bulk-memory',
|
||||
components: rust-src,
|
||||
}
|
||||
|
||||
env:
|
||||
@@ -83,19 +106,21 @@ jobs:
|
||||
|
||||
# Faster compilation and error on warnings
|
||||
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}'
|
||||
RUSTDOCFLAGS: ${{ matrix.platform.rustflags }}
|
||||
|
||||
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
|
||||
TEST_OPTIONS: ${{ matrix.platform.test-options }}
|
||||
CMD: ${{ matrix.platform.cmd }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: taiki-e/checkout-action@v1
|
||||
|
||||
- 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
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
# https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
|
||||
path: |
|
||||
@@ -116,7 +141,7 @@ jobs:
|
||||
- name: Cache cargo-apk
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
id: cargo-apk-cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cargo/bin/cargo-apk
|
||||
# Change this key if we update the required cargo-apk version
|
||||
@@ -131,6 +156,11 @@ jobs:
|
||||
if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
|
||||
run: cargo install cargo-apk --version=^0.9.7 --locked
|
||||
|
||||
- uses: taiki-e/cache-cargo-install-action@v2
|
||||
if: contains(matrix.platform.target, 'wasm32') && matrix.toolchain == 'nightly'
|
||||
with:
|
||||
tool: wasm-bindgen-cli
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
|
||||
@@ -149,52 +179,52 @@ jobs:
|
||||
- name: Test dpi crate
|
||||
if: >
|
||||
contains(matrix.platform.name, 'Linux 64bit') &&
|
||||
matrix.toolchain != '1.70.0'
|
||||
matrix.toolchain != '1.73'
|
||||
run: cargo test -p dpi
|
||||
|
||||
- name: Build tests
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.70.0'
|
||||
matrix.toolchain != '1.73'
|
||||
run: cargo $CMD 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, 'wasm32') || matrix.toolchain == 'nightly') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.70.0'
|
||||
matrix.toolchain != '1.73'
|
||||
run: cargo $CMD test $OPTIONS
|
||||
|
||||
- name: Lint with clippy
|
||||
if: (matrix.toolchain == 'stable') && !contains(matrix.platform.options, '--no-default-features')
|
||||
run: cargo clippy --all-targets $OPTIONS -- -Dwarnings
|
||||
run: cargo clippy --all-targets $OPTIONS $TEST_OPTIONS -- -Dwarnings
|
||||
|
||||
- name: Build tests with serde enabled
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS --features serde
|
||||
matrix.toolchain != '1.73'
|
||||
run: cargo $CMD test --no-run $OPTIONS $TEST_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, 'wasm32') || matrix.toolchain == 'nightly') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test $OPTIONS --features serde
|
||||
matrix.toolchain != '1.73'
|
||||
run: cargo $CMD test $OPTIONS $TEST_OPTIONS --features serde
|
||||
|
||||
- name: Check docs.rs documentation
|
||||
if: matrix.toolchain == 'nightly'
|
||||
run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
|
||||
run: cargo doc --no-deps $OPTIONS --features=serde,mint,android-native-activity
|
||||
env:
|
||||
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs --cfg=unreleased_changelogs'
|
||||
|
||||
# See restore step above
|
||||
- name: Save cache of cargo folder
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
@@ -214,15 +244,50 @@ jobs:
|
||||
- { 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: 'macOS', target: aarch64-apple-darwin }
|
||||
- { name: 'Redox OS', target: x86_64-unknown-redox }
|
||||
- { name: 'web', target: wasm32-unknown-unknown }
|
||||
- { name: 'Windows', target: x86_64-pc-windows-gnu }
|
||||
- { name: 'Web', target: wasm32-unknown-unknown }
|
||||
- { name: 'Windows GNU', target: x86_64-pc-windows-gnu }
|
||||
- { name: 'Windows MSVC', target: x86_64-pc-windows-msvc }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
- uses: taiki-e/checkout-action@v1
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
with:
|
||||
command: check
|
||||
log-level: error
|
||||
arguments: --all-features --target ${{ matrix.platform.target }}
|
||||
|
||||
eslint:
|
||||
name: ESLint
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./src/platform_impl/web/script
|
||||
|
||||
steps:
|
||||
- uses: taiki-e/checkout-action@v1
|
||||
- name: Setup NPM
|
||||
run: npm install
|
||||
- name: Run ESLint
|
||||
run: npx eslint
|
||||
|
||||
swc:
|
||||
name: Minimize JavaScript
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./src/platform_impl/web/script
|
||||
|
||||
steps:
|
||||
- uses: taiki-e/checkout-action@v1
|
||||
- name: Install SWC
|
||||
run: sudo npm i -g @swc/cli
|
||||
- name: Run SWC
|
||||
run: |
|
||||
swc . --ignore node_modules,**/*.d.ts --only **/*.ts -d . --out-file-extension min.js
|
||||
- name: Check for diff
|
||||
run: |
|
||||
[[ -z $(git status -s) ]]
|
||||
|
||||
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@@ -29,10 +29,10 @@ jobs:
|
||||
env:
|
||||
RUSTDOCFLAGS: --crate-version master --cfg=docsrs --cfg=unreleased_changelogs
|
||||
run: |
|
||||
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
|
||||
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=serde,mint,android-native-activity
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Fix permissions
|
||||
run: |
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,8 +3,8 @@ target/
|
||||
rls/
|
||||
.vscode/
|
||||
*~
|
||||
*.wasm
|
||||
*.ts
|
||||
*.js
|
||||
#*#
|
||||
.DS_Store
|
||||
# NPM package used to run ESLint.
|
||||
/src/platform_impl/web/script/node_modules
|
||||
/src/platform_impl/web/script/package-lock.json
|
||||
|
||||
296
Cargo.toml
296
Cargo.toml
@@ -1,25 +1,30 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.30.2"
|
||||
authors = [
|
||||
"The winit contributors",
|
||||
"Pierre Krieger <pierre.krieger1708@gmail.com>",
|
||||
]
|
||||
description = "Cross-platform window creation library."
|
||||
keywords = ["windowing"]
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/winit"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
categories = ["gui"]
|
||||
rust-version.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
description = "Cross-platform window creation library."
|
||||
documentation = "https://docs.rs/winit"
|
||||
edition.workspace = true
|
||||
exclude = ["/.cargo"]
|
||||
include = [
|
||||
"/build.rs",
|
||||
"/docs",
|
||||
"/examples",
|
||||
"/FEATURES.md",
|
||||
"/LICENSE",
|
||||
"/src",
|
||||
"!/src/platform_impl/web/script",
|
||||
"/src/platform_impl/web/script/**/*.min.js",
|
||||
"/tests",
|
||||
]
|
||||
keywords = ["windowing"]
|
||||
license.workspace = true
|
||||
name = "winit"
|
||||
readme = "README.md"
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
version = "0.30.5"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [
|
||||
"rwh_04",
|
||||
"rwh_05",
|
||||
"rwh_06",
|
||||
"serde",
|
||||
"mint",
|
||||
@@ -27,28 +32,33 @@ features = [
|
||||
"android-native-activity",
|
||||
]
|
||||
# These are all tested in CI
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
targets = [
|
||||
# Windows
|
||||
"i686-pc-windows-msvc",
|
||||
"x86_64-pc-windows-msvc",
|
||||
# macOS
|
||||
"aarch64-apple-darwin",
|
||||
"x86_64-apple-darwin",
|
||||
# Unix (X11 & Wayland)
|
||||
"i686-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
# iOS
|
||||
"x86_64-apple-ios",
|
||||
"aarch64-apple-ios",
|
||||
# Android
|
||||
"aarch64-linux-android",
|
||||
# Web
|
||||
"wasm32-unknown-unknown",
|
||||
]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
# Features are documented in either `lib.rs` or under `winit::platform`.
|
||||
[features]
|
||||
android-game-activity = ["android-activity/game-activity"]
|
||||
android-native-activity = ["android-activity/native-activity"]
|
||||
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
|
||||
mint = ["dpi/mint"]
|
||||
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
||||
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde", "bitflags/serde"]
|
||||
wayland = [
|
||||
"wayland-client",
|
||||
"wayland-backend",
|
||||
@@ -58,17 +68,11 @@ wayland = [
|
||||
"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"]
|
||||
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
|
||||
android-native-activity = ["android-activity/native-activity"]
|
||||
android-game-activity = ["android-activity/game-activity"]
|
||||
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde"]
|
||||
mint = ["dpi/mint"]
|
||||
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
|
||||
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
|
||||
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
||||
wayland-dlopen = ["wayland-backend/dlopen"]
|
||||
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases = "0.2.1"
|
||||
@@ -77,22 +81,15 @@ cfg_aliases = "0.2.1"
|
||||
bitflags = "2"
|
||||
cursor-icon = "1.1.0"
|
||||
dpi = { version = "0.1.1", path = "dpi" }
|
||||
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
|
||||
rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = [
|
||||
"std",
|
||||
], optional = true }
|
||||
rwh_06 = { package = "raw-window-handle", version = "0.6", features = [
|
||||
"std",
|
||||
], optional = true }
|
||||
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
smol_str = "0.2.0"
|
||||
tracing = { version = "0.1.40", default_features = false }
|
||||
tracing = { version = "0.1.40", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
image = { version = "0.25.0", default-features = false, features = ["png"] }
|
||||
tracing = { version = "0.1.40", default_features = false, features = ["log"] }
|
||||
tracing = { version = "0.1.40", default-features = false, features = ["log"] }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
winit = { path = ".", features = ["rwh_05"] }
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
|
||||
softbuffer = { version = "0.4.0", default-features = false, features = [
|
||||
@@ -102,42 +99,21 @@ softbuffer = { version = "0.4.0", default-features = false, features = [
|
||||
"wayland-dlopen",
|
||||
] }
|
||||
|
||||
# Android
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android-activity = "0.6.0"
|
||||
ndk = { version = "0.9.0", default-features = false }
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
# AppKit or UIKit
|
||||
[target.'cfg(target_vendor = "apple")'.dependencies]
|
||||
block2 = "0.5.1"
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = "0.5.2"
|
||||
|
||||
# AppKit
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-graphics = "0.23.1"
|
||||
block2 = "0.5.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
|
||||
version = "0.2.2"
|
||||
features = [
|
||||
"block2",
|
||||
"dispatch",
|
||||
"NSArray",
|
||||
"NSAttributedString",
|
||||
"NSData",
|
||||
"NSDictionary",
|
||||
"NSDistributedNotificationCenter",
|
||||
"NSEnumerator",
|
||||
"NSNotification",
|
||||
"NSObjCRuntime",
|
||||
"NSPathUtilities",
|
||||
"NSProcessInfo",
|
||||
"NSRunLoop",
|
||||
"NSString",
|
||||
"NSThread",
|
||||
"NSValue",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.objc2-app-kit]
|
||||
version = "0.2.2"
|
||||
features = [
|
||||
objc2-app-kit = { version = "0.2.2", features = [
|
||||
"NSAppearance",
|
||||
"NSApplication",
|
||||
"NSBitmapImageRep",
|
||||
@@ -164,30 +140,51 @@ features = [
|
||||
"NSWindow",
|
||||
"NSWindowScripting",
|
||||
"NSWindowTabGroup",
|
||||
]
|
||||
] }
|
||||
objc2-foundation = { version = "0.2.2", features = [
|
||||
"block2",
|
||||
"dispatch",
|
||||
"NSArray",
|
||||
"NSAttributedString",
|
||||
"NSData",
|
||||
"NSDictionary",
|
||||
"NSDistributedNotificationCenter",
|
||||
"NSEnumerator",
|
||||
"NSKeyValueObserving",
|
||||
"NSNotification",
|
||||
"NSObjCRuntime",
|
||||
"NSOperation",
|
||||
"NSPathUtilities",
|
||||
"NSProcessInfo",
|
||||
"NSRunLoop",
|
||||
"NSString",
|
||||
"NSThread",
|
||||
"NSValue",
|
||||
] }
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies.objc2-foundation]
|
||||
version = "0.2.2"
|
||||
features = [
|
||||
# UIKit
|
||||
[target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dependencies]
|
||||
objc2-foundation = { version = "0.2.2", features = [
|
||||
"block2",
|
||||
"dispatch",
|
||||
"NSArray",
|
||||
"NSEnumerator",
|
||||
"NSGeometry",
|
||||
"NSObjCRuntime",
|
||||
"NSOperation",
|
||||
"NSString",
|
||||
"NSProcessInfo",
|
||||
"NSThread",
|
||||
"NSSet",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies.objc2-ui-kit]
|
||||
version = "0.2.2"
|
||||
features = [
|
||||
] }
|
||||
objc2-ui-kit = { version = "0.2.2", features = [
|
||||
"UIApplication",
|
||||
"UIDevice",
|
||||
"UIEvent",
|
||||
"UIGeometry",
|
||||
"UIGestureRecognizer",
|
||||
"UITextInput",
|
||||
"UITextInputTraits",
|
||||
"UIOrientation",
|
||||
"UIPanGestureRecognizer",
|
||||
"UIPinchGestureRecognizer",
|
||||
@@ -201,14 +198,12 @@ features = [
|
||||
"UIView",
|
||||
"UIViewController",
|
||||
"UIWindow",
|
||||
]
|
||||
] }
|
||||
|
||||
# Windows
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
unicode-segmentation = "1.7.1"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
|
||||
version = "0.52.0"
|
||||
features = [
|
||||
windows-sys = { version = "0.52.0", features = [
|
||||
"Win32_Devices_HumanInterfaceDevice",
|
||||
"Win32_Foundation",
|
||||
"Win32_Globalization",
|
||||
@@ -233,12 +228,13 @@ features = [
|
||||
"Win32_UI_Shell",
|
||||
"Win32_UI_TextServices",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
] }
|
||||
|
||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
|
||||
# Linux
|
||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
|
||||
ahash = { version = "0.8.7", features = ["no-rng"], optional = true }
|
||||
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
||||
calloop = "0.12.3"
|
||||
calloop = "0.13.0"
|
||||
libc = "0.2.64"
|
||||
memmap2 = { version = "0.9.0", optional = true }
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
@@ -248,86 +244,92 @@ rustix = { version = "0.38.4", default-features = false, features = [
|
||||
"thread",
|
||||
"process",
|
||||
] }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = [
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.19.2", default-features = false, features = [
|
||||
"calloop",
|
||||
], optional = true }
|
||||
sctk-adwaita = { version = "0.9.0", default_features = false, optional = true }
|
||||
wayland-backend = { version = "0.3.0", default_features = false, features = [
|
||||
sctk-adwaita = { version = "0.10.1", default-features = false, optional = true }
|
||||
wayland-backend = { version = "0.3.5", default-features = false, features = [
|
||||
"client_system",
|
||||
], optional = true }
|
||||
wayland-client = { version = "0.31.1", optional = true }
|
||||
wayland-protocols = { version = "0.31.0", features = [
|
||||
"staging",
|
||||
], optional = true }
|
||||
wayland-protocols-plasma = { version = "0.2.0", features = [
|
||||
"client",
|
||||
], optional = true }
|
||||
wayland-client = { version = "0.31.4", optional = true }
|
||||
wayland-protocols = { version = "0.32.2", features = ["staging"], optional = true }
|
||||
wayland-protocols-plasma = { version = "0.3.2", features = ["client"], optional = true }
|
||||
x11-dl = { version = "2.19.1", optional = true }
|
||||
x11rb = { version = "0.13.0", default-features = false, features = [
|
||||
"allow-unsafe-code",
|
||||
"cursor",
|
||||
"dl-libxcb",
|
||||
"randr",
|
||||
"resource_manager",
|
||||
"sync",
|
||||
"xinput",
|
||||
"xkb",
|
||||
], optional = true }
|
||||
xkbcommon-dl = "0.4.2"
|
||||
|
||||
# Orbital
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
orbclient = { version = "0.3.47", default-features = false }
|
||||
redox_syscall = "0.4.1"
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies.web_sys]
|
||||
package = "web-sys"
|
||||
version = "0.3.64"
|
||||
features = [
|
||||
'AbortController',
|
||||
'AbortSignal',
|
||||
'Blob',
|
||||
'console',
|
||||
'CssStyleDeclaration',
|
||||
'Document',
|
||||
'DomException',
|
||||
'DomRect',
|
||||
'DomRectReadOnly',
|
||||
'Element',
|
||||
'Event',
|
||||
'EventTarget',
|
||||
'FocusEvent',
|
||||
'HtmlCanvasElement',
|
||||
'HtmlElement',
|
||||
'HtmlImageElement',
|
||||
'ImageBitmap',
|
||||
'ImageBitmapOptions',
|
||||
'ImageBitmapRenderingContext',
|
||||
'ImageData',
|
||||
'IntersectionObserver',
|
||||
'IntersectionObserverEntry',
|
||||
'KeyboardEvent',
|
||||
'MediaQueryList',
|
||||
'MessageChannel',
|
||||
'MessagePort',
|
||||
'Node',
|
||||
'PageTransitionEvent',
|
||||
'PointerEvent',
|
||||
'PremultiplyAlpha',
|
||||
'ResizeObserver',
|
||||
'ResizeObserverBoxOptions',
|
||||
'ResizeObserverEntry',
|
||||
'ResizeObserverOptions',
|
||||
'ResizeObserverSize',
|
||||
'VisibilityState',
|
||||
'Window',
|
||||
'WheelEvent',
|
||||
'Url',
|
||||
]
|
||||
|
||||
# Web
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
js-sys = "0.3.64"
|
||||
js-sys = "0.3.70"
|
||||
pin-project = "1"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-futures = "0.4.43"
|
||||
web-time = "1"
|
||||
web_sys = { package = "web-sys", version = "0.3.70", features = [
|
||||
"AbortController",
|
||||
"AbortSignal",
|
||||
"Blob",
|
||||
"BlobPropertyBag",
|
||||
"console",
|
||||
"CssStyleDeclaration",
|
||||
"Document",
|
||||
"DomException",
|
||||
"DomRect",
|
||||
"DomRectReadOnly",
|
||||
"Element",
|
||||
"Event",
|
||||
"EventTarget",
|
||||
"FocusEvent",
|
||||
"HtmlCanvasElement",
|
||||
"HtmlElement",
|
||||
"HtmlImageElement",
|
||||
"ImageBitmap",
|
||||
"ImageBitmapOptions",
|
||||
"ImageBitmapRenderingContext",
|
||||
"ImageData",
|
||||
"IntersectionObserver",
|
||||
"IntersectionObserverEntry",
|
||||
"KeyboardEvent",
|
||||
"MediaQueryList",
|
||||
"MessageChannel",
|
||||
"MessagePort",
|
||||
"Navigator",
|
||||
"Node",
|
||||
"OrientationLockType",
|
||||
"OrientationType",
|
||||
"PageTransitionEvent",
|
||||
"Permissions",
|
||||
"PermissionState",
|
||||
"PermissionStatus",
|
||||
"PointerEvent",
|
||||
"PremultiplyAlpha",
|
||||
"ResizeObserver",
|
||||
"ResizeObserverBoxOptions",
|
||||
"ResizeObserverEntry",
|
||||
"ResizeObserverOptions",
|
||||
"ResizeObserverSize",
|
||||
"Screen",
|
||||
"ScreenOrientation",
|
||||
"Url",
|
||||
"VisibilityState",
|
||||
"WheelEvent",
|
||||
"Window",
|
||||
"Worker",
|
||||
] }
|
||||
|
||||
[target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies]
|
||||
atomic-waker = "1"
|
||||
@@ -336,21 +338,27 @@ concurrent-queue = { version = "2", default-features = false }
|
||||
[target.'cfg(target_family = "wasm")'.dev-dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
tracing-web = "0.1"
|
||||
wasm-bindgen-test = "0.3"
|
||||
|
||||
[[example]]
|
||||
doc-scrape-examples = true
|
||||
name = "window"
|
||||
required-features = ["rwh_06"]
|
||||
|
||||
[[example]]
|
||||
name = "child_window"
|
||||
required-features = ["rwh_06"]
|
||||
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["dpi"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.70.0"
|
||||
repository = "https://github.com/rust-windowing/winit"
|
||||
license = "Apache-2.0"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/rust-windowing/winit"
|
||||
rust-version = "1.73"
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1", features = ["serde_derive"] }
|
||||
mint = "0.5.6"
|
||||
serde = { version = "1", features = ["serde_derive"] }
|
||||
|
||||
@@ -154,7 +154,6 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* Home indicator visibility
|
||||
* Status bar visibility and style
|
||||
* Deferring system gestures
|
||||
* Getting the device idiom
|
||||
* Getting the preferred video mode
|
||||
|
||||
### Web
|
||||
|
||||
11
README.md
11
README.md
@@ -8,7 +8,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.30.2"
|
||||
winit = "0.30.5"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -19,7 +19,7 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
|
||||
|
||||
## Contact Us
|
||||
|
||||
Join us in our [](https://matrix.to/#/#rust-windowing:matrix.org) room. If you don't get an answer there, try [](https://web.libera.chat/#winit).
|
||||
Join us in our [](https://matrix.to/#/#rust-windowing:matrix.org) room.
|
||||
|
||||
The maintainers have a meeting every friday at UTC 15. The meeting notes can be found [here](https://hackmd.io/@winit-meetings).
|
||||
|
||||
@@ -35,7 +35,7 @@ another library.
|
||||
|
||||
## MSRV Policy
|
||||
|
||||
This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to
|
||||
This crate's Minimum Supported Rust Version (MSRV) is **1.73**. 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
|
||||
@@ -50,12 +50,15 @@ Where `sid` is the current version of `rustc` provided by [Debian Sid], and
|
||||
|
||||
[Debian Sid]: https://packages.debian.org/sid/rustc
|
||||
|
||||
The exception is for the Android platform, where a higher Rust version
|
||||
An exception is made 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.
|
||||
|
||||
Redox OS is also not covered by this MSRV policy, as it requires a Rust nightly
|
||||
toolchain to compile.
|
||||
|
||||
All crates in the [`rust-windowing`] organizations have the
|
||||
same MSRV policy.
|
||||
|
||||
|
||||
5
build.rs
5
build.rs
@@ -10,10 +10,9 @@ fn main() {
|
||||
android_platform: { target_os = "android" },
|
||||
web_platform: { all(target_family = "wasm", target_os = "unknown") },
|
||||
macos_platform: { target_os = "macos" },
|
||||
ios_platform: { target_os = "ios" },
|
||||
ios_platform: { all(target_vendor = "apple", not(target_os = "macos")) },
|
||||
windows_platform: { target_os = "windows" },
|
||||
apple: { any(target_os = "ios", target_os = "macos") },
|
||||
free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) },
|
||||
free_unix: { all(unix, not(target_vendor = "apple"), not(android_platform), not(target_os = "emscripten")) },
|
||||
redox: { target_os = "redox" },
|
||||
|
||||
// Native displays.
|
||||
|
||||
23
clippy.toml
23
clippy.toml
@@ -1,15 +1,16 @@
|
||||
disallowed-methods = [
|
||||
{ path = "web_sys::window", reason = "is not available in every context" },
|
||||
{ path = "web_sys::HtmlCanvasElement::width", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::HtmlElement::style", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "objc2_app_kit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
|
||||
{ path = "objc2_app_kit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
|
||||
{ path = "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 = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::width", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlElement::style", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::Window::navigator", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::window", reason = "is not available in every context" },
|
||||
]
|
||||
|
||||
87
deny.toml
87
deny.toml
@@ -1,15 +1,21 @@
|
||||
# https://embarkstudios.github.io/cargo-deny/
|
||||
# https://embarkstudios.github.io/cargo-deny
|
||||
# cargo install cargo-deny
|
||||
# cargo update && cargo deny --all-features --log-level error --target aarch64-apple-ios check
|
||||
# cargo update && cargo deny --target aarch64-apple-ios check
|
||||
# Note: running just `cargo deny check` without a `--target` will result in
|
||||
# false positives due to https://github.com/EmbarkStudios/cargo-deny/issues/324
|
||||
[graph]
|
||||
all-features = true
|
||||
exclude-dev = true
|
||||
targets = [
|
||||
{ triple = "aarch64-apple-darwin" },
|
||||
{ triple = "aarch64-apple-ios" },
|
||||
{ triple = "aarch64-linux-android" },
|
||||
{ triple = "i686-pc-windows-gnu" },
|
||||
{ triple = "i686-pc-windows-msvc" },
|
||||
{ triple = "i686-unknown-linux-gnu" },
|
||||
{ triple = "wasm32-unknown-unknown" },
|
||||
{ triple = "wasm32-unknown-unknown", features = [
|
||||
"atomics",
|
||||
] },
|
||||
{ triple = "x86_64-apple-darwin" },
|
||||
{ triple = "x86_64-apple-ios" },
|
||||
{ triple = "x86_64-pc-windows-gnu" },
|
||||
@@ -18,46 +24,49 @@ targets = [
|
||||
{ triple = "x86_64-unknown-redox" },
|
||||
]
|
||||
|
||||
|
||||
[advisories]
|
||||
vulnerability = "deny"
|
||||
unmaintained = "warn"
|
||||
yanked = "deny"
|
||||
ignore = []
|
||||
|
||||
[licenses]
|
||||
allow = [
|
||||
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
|
||||
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
|
||||
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
|
||||
"ISC", # https://tldrlegal.com/license/-isc-license
|
||||
"MIT", # https://tldrlegal.com/license/mit-license
|
||||
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
|
||||
]
|
||||
confidence-threshold = 1.0
|
||||
private = { ignore = true }
|
||||
|
||||
[bans]
|
||||
multiple-versions = "deny"
|
||||
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
|
||||
deny = []
|
||||
skip = [
|
||||
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this
|
||||
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
|
||||
{ name = "libloading" }, # x11rb uses a different version until the next update
|
||||
]
|
||||
skip-tree = []
|
||||
skip = [{ crate = "bitflags@1", reason = "the ecosystem is in the process of migrating" }]
|
||||
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
|
||||
|
||||
[bans.build]
|
||||
include-archives = true
|
||||
interpreted = "deny"
|
||||
|
||||
[licenses]
|
||||
private = { ignore = true }
|
||||
unlicensed = "deny"
|
||||
allow-osi-fsf-free = "neither"
|
||||
confidence-threshold = 0.92 # We want really high confidence when inferring licenses from text
|
||||
copyleft = "deny"
|
||||
[[bans.build.bypass]]
|
||||
allow = [
|
||||
"Apache-2.0 WITH LLVM-exception", # https://spdx.org/licenses/LLVM-exception.html
|
||||
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
|
||||
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
|
||||
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
|
||||
"BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained
|
||||
"CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/
|
||||
"ISC", # https://tldrlegal.com/license/-isc-license
|
||||
"LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 - no official SPDX, see https://github.com/emilk/egui/issues/2321
|
||||
"MIT-0", # https://choosealicense.com/licenses/mit-0/
|
||||
"MIT", # https://tldrlegal.com/license/mit-license
|
||||
"MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11. Used by webpki-roots on Linux.
|
||||
"OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html
|
||||
"OpenSSL", # https://www.openssl.org/source/license.html - used on Linux
|
||||
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
|
||||
"Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib)
|
||||
{ path = "generate-bindings.sh", checksum = "268ec23248218d779e33853cdc60e2985e70214ff004716cd734270de1f6b561" },
|
||||
]
|
||||
crate = "android-activity"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["freetype2/*"]
|
||||
crate = "freetype-sys"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["lib/*.a"]
|
||||
crate = "windows_i686_gnu"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["lib/*.lib"]
|
||||
crate = "windows_i686_msvc"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["lib/*.a"]
|
||||
crate = "windows_x86_64_gnu"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["lib/*.lib"]
|
||||
crate = "windows_x86_64_msvc"
|
||||
|
||||
@@ -1,39 +1,40 @@
|
||||
[package]
|
||||
name = "dpi"
|
||||
version = "0.1.1"
|
||||
description = "Types for handling UI scaling"
|
||||
keywords = ["DPI", "HiDPI", "scale-factor"]
|
||||
categories = ["gui"]
|
||||
rust-version.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
description = "Types for handling UI scaling"
|
||||
edition.workspace = true
|
||||
keywords = ["DPI", "HiDPI", "scale-factor"]
|
||||
license.workspace = true
|
||||
name = "dpi"
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
version = "0.1.1"
|
||||
|
||||
[features]
|
||||
serde = ["dep:serde"]
|
||||
mint = ["dep:mint"]
|
||||
serde = ["dep:serde"]
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true, optional = true }
|
||||
mint = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["serde", "mint"]
|
||||
features = ["mint", "serde"]
|
||||
# These are all tested in CI
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
targets = [
|
||||
# Windows
|
||||
"i686-pc-windows-msvc",
|
||||
"x86_64-pc-windows-msvc",
|
||||
# macOS
|
||||
"aarch64-apple-darwin",
|
||||
"x86_64-apple-darwin",
|
||||
# Unix (X11 & Wayland)
|
||||
"i686-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
# iOS
|
||||
"x86_64-apple-ios",
|
||||
"aarch64-apple-ios",
|
||||
# Android
|
||||
"aarch64-linux-android",
|
||||
# Web
|
||||
"wasm32-unknown-unknown",
|
||||
]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
@@ -761,9 +761,10 @@ impl<P: Pixel> From<LogicalPosition<P>> for Position {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! test_pixel_int_impl {
|
||||
($($name:ident => $ty:ty),*) => {$(
|
||||
#[test]
|
||||
|
||||
@@ -1,52 +1,47 @@
|
||||
#[cfg(all(feature = "rwh_06", any(x11_platform, macos_platform, windows_platform)))]
|
||||
#[cfg(any(x11_platform, macos_platform, windows_platform))]
|
||||
#[allow(deprecated)]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize, Position};
|
||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::raw_window_handle::HasRawWindowHandle;
|
||||
use winit::window::Window;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let mut window_attributes = Window::default_attributes()
|
||||
.with_title("child window")
|
||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_visible(true);
|
||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||
window_attributes = unsafe { window_attributes.with_parent_window(Some(parent)) };
|
||||
|
||||
event_loop.create_window(window_attributes).unwrap()
|
||||
#[derive(Default)]
|
||||
struct Application {
|
||||
parent_window_id: Option<WindowId>,
|
||||
windows: HashMap<WindowId, Box<dyn Window>>,
|
||||
}
|
||||
|
||||
let mut windows = HashMap::new();
|
||||
impl ApplicationHandler for Application {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let attributes = WindowAttributes::default()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||
let window = event_loop.create_window(attributes).unwrap();
|
||||
|
||||
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
|
||||
let mut parent_window_id = None;
|
||||
println!("Parent window id: {:?})", window.id());
|
||||
self.parent_window_id = Some(window.id());
|
||||
|
||||
event_loop.run(move |event: Event<()>, event_loop| {
|
||||
match event {
|
||||
Event::Resumed => {
|
||||
let attributes = Window::default_attributes()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||
let window = event_loop.create_window(attributes).unwrap();
|
||||
self.windows.insert(window.id(), window);
|
||||
}
|
||||
|
||||
parent_window_id = Some(window.id());
|
||||
|
||||
println!("Parent window id: {parent_window_id:?})");
|
||||
windows.insert(window.id(), window);
|
||||
},
|
||||
Event::WindowEvent { window_id, event } => match event {
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
windows.clear();
|
||||
self.windows.clear();
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::CursorEntered { device_id: _ } => {
|
||||
@@ -61,25 +56,43 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event: KeyEvent { state: ElementState::Pressed, .. },
|
||||
..
|
||||
} => {
|
||||
let parent_window = windows.get(&parent_window_id.unwrap()).unwrap();
|
||||
let child_window = spawn_child_window(parent_window, event_loop);
|
||||
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap();
|
||||
let child_window = spawn_child_window(parent_window.as_ref(), event_loop);
|
||||
let child_id = child_window.id();
|
||||
println!("Child window created with id: {child_id:?}");
|
||||
windows.insert(child_id, child_window);
|
||||
self.windows.insert(child_id, child_window);
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
if let Some(window) = self.windows.get(&window_id) {
|
||||
fill::fill_window(window.as_ref());
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn spawn_child_window(
|
||||
parent: &dyn Window,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
) -> Box<dyn Window> {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let mut window_attributes = WindowAttributes::default()
|
||||
.with_title("child window")
|
||||
.with_surface_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_visible(true);
|
||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||
window_attributes = unsafe { window_attributes.with_parent_window(Some(parent)) };
|
||||
|
||||
event_loop.create_window(window_attributes).unwrap()
|
||||
}
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
event_loop.run_app(Application::default())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rwh_06", not(any(x11_platform, macos_platform, windows_platform))))]
|
||||
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))]
|
||||
fn main() {
|
||||
panic!(
|
||||
"This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature \
|
||||
|
||||
@@ -7,12 +7,11 @@ use std::time;
|
||||
use ::tracing::{info, warn};
|
||||
#[cfg(web_platform)]
|
||||
use web_time as time;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::{Window, WindowId};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
@@ -44,8 +43,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let mut app = ControlFlowDemo::default();
|
||||
event_loop.run_app(&mut app)
|
||||
event_loop.run_app(ControlFlowDemo::default())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -54,11 +52,11 @@ struct ControlFlowDemo {
|
||||
request_redraw: bool,
|
||||
wait_cancelled: bool,
|
||||
close_requested: bool,
|
||||
window: Option<Window>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for ControlFlowDemo {
|
||||
fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) {
|
||||
fn new_events(&mut self, _event_loop: &dyn ActiveEventLoop, cause: StartCause) {
|
||||
info!("new_events: {cause:?}");
|
||||
|
||||
self.wait_cancelled = match cause {
|
||||
@@ -67,8 +65,8 @@ impl ApplicationHandler for ControlFlowDemo {
|
||||
}
|
||||
}
|
||||
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let window_attributes = Window::default_attributes().with_title(
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default().with_title(
|
||||
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
|
||||
);
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
@@ -76,7 +74,7 @@ impl ApplicationHandler for ControlFlowDemo {
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
_event_loop: &ActiveEventLoop,
|
||||
_event_loop: &dyn ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
@@ -116,13 +114,13 @@ impl ApplicationHandler for ControlFlowDemo {
|
||||
WindowEvent::RedrawRequested => {
|
||||
let window = self.window.as_ref().unwrap();
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window);
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
if self.request_redraw && !self.wait_cancelled && !self.close_requested {
|
||||
self.window.as_ref().unwrap().request_redraw();
|
||||
}
|
||||
|
||||
@@ -11,25 +11,25 @@ fn main() -> std::process::ExitCode {
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus};
|
||||
use winit::window::{Window, WindowId};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Default)]
|
||||
struct PumpDemo {
|
||||
window: Option<Window>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for PumpDemo {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let window_attributes = Window::default_attributes().with_title("A fantastic window!");
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default().with_title("A fantastic window!");
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
@@ -43,7 +43,7 @@ fn main() -> std::process::ExitCode {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(window);
|
||||
fill::fill_window(window.as_ref());
|
||||
window.request_redraw();
|
||||
},
|
||||
_ => (),
|
||||
|
||||
@@ -9,7 +9,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
|
||||
use winit::window::{Window, WindowId};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
@@ -18,20 +18,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
struct App {
|
||||
idx: usize,
|
||||
window_id: Option<WindowId>,
|
||||
window: Option<Window>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let window_attributes = Window::default_attributes()
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default()
|
||||
.with_title("Fantastic window number one!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0));
|
||||
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
|
||||
let window = event_loop.create_window(window_attributes).unwrap();
|
||||
self.window_id = Some(window.id());
|
||||
self.window = Some(window);
|
||||
@@ -39,7 +39,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
@@ -65,11 +65,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
CloseRequested",
|
||||
self.idx
|
||||
);
|
||||
fill::cleanup_window(window);
|
||||
fill::cleanup_window(window.as_ref());
|
||||
self.window = None;
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(window);
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
pub use platform::cleanup_window;
|
||||
pub use platform::fill_window;
|
||||
|
||||
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
|
||||
#[cfg(all(feature = "rwh_06", not(any(target_os = "android", target_os = "ios"))))]
|
||||
mod platform {
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
@@ -34,18 +34,20 @@ mod platform {
|
||||
/// The graphics context used to draw to a window.
|
||||
struct GraphicsContext {
|
||||
/// The global softbuffer context.
|
||||
context: RefCell<Context<&'static Window>>,
|
||||
context: RefCell<Context<&'static dyn Window>>,
|
||||
|
||||
/// The hash map of window IDs to surfaces.
|
||||
surfaces: HashMap<WindowId, Surface<&'static Window, &'static Window>>,
|
||||
surfaces: HashMap<WindowId, Surface<&'static dyn Window, &'static dyn Window>>,
|
||||
}
|
||||
|
||||
impl GraphicsContext {
|
||||
fn new(w: &Window) -> Self {
|
||||
fn new(w: &dyn Window) -> Self {
|
||||
Self {
|
||||
context: RefCell::new(
|
||||
Context::new(unsafe { mem::transmute::<&'_ Window, &'static Window>(w) })
|
||||
.expect("Failed to create a softbuffer context"),
|
||||
Context::new(unsafe {
|
||||
mem::transmute::<&'_ dyn Window, &'static dyn Window>(w)
|
||||
})
|
||||
.expect("Failed to create a softbuffer context"),
|
||||
),
|
||||
surfaces: HashMap::new(),
|
||||
}
|
||||
@@ -53,24 +55,24 @@ mod platform {
|
||||
|
||||
fn create_surface(
|
||||
&mut self,
|
||||
window: &Window,
|
||||
) -> &mut Surface<&'static Window, &'static Window> {
|
||||
window: &dyn Window,
|
||||
) -> &mut Surface<&'static dyn Window, &'static dyn Window> {
|
||||
self.surfaces.entry(window.id()).or_insert_with(|| {
|
||||
Surface::new(&self.context.borrow(), unsafe {
|
||||
mem::transmute::<&'_ Window, &'static Window>(window)
|
||||
mem::transmute::<&'_ dyn Window, &'static dyn Window>(window)
|
||||
})
|
||||
.expect("Failed to create a softbuffer surface")
|
||||
})
|
||||
}
|
||||
|
||||
fn destroy_surface(&mut self, window: &Window) {
|
||||
fn destroy_surface(&mut self, window: &dyn Window) {
|
||||
self.surfaces.remove(&window.id());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_window(window: &Window) {
|
||||
pub fn fill_window(window: &dyn Window) {
|
||||
GC.with(|gc| {
|
||||
let size = window.inner_size();
|
||||
let size = window.surface_size();
|
||||
let (Some(width), Some(height)) =
|
||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
else {
|
||||
@@ -94,7 +96,7 @@ mod platform {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(window: &Window) {
|
||||
pub fn cleanup_window(window: &dyn Window) {
|
||||
GC.with(|gc| {
|
||||
let mut gc = gc.borrow_mut();
|
||||
if let Some(context) = gc.as_mut() {
|
||||
@@ -104,14 +106,14 @@ mod platform {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
|
||||
#[cfg(not(all(feature = "rwh_06", not(any(target_os = "android", target_os = "ios")))))]
|
||||
mod platform {
|
||||
pub fn fill_window(_window: &winit::window::Window) {
|
||||
pub fn fill_window(_window: &dyn winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(_window: &winit::window::Window) {
|
||||
pub fn cleanup_window(_window: &dyn winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::error::Error;
|
||||
use std::fmt::Debug;
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem};
|
||||
|
||||
@@ -14,23 +15,24 @@ use cursor_icon::CursorIcon;
|
||||
use rwh_06::{DisplayHandle, HasDisplayHandle};
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
use softbuffer::{Context, Surface};
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
|
||||
use winit::error::RequestError;
|
||||
use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, ModifiersState};
|
||||
use winit::window::{
|
||||
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection,
|
||||
Theme, Window, WindowId,
|
||||
};
|
||||
|
||||
#[cfg(macos_platform)]
|
||||
use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS};
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use winit::platform::startup_notify::{
|
||||
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
|
||||
};
|
||||
#[cfg(web_platform)]
|
||||
use winit::platform::web::{ActiveEventLoopExtWeb, CustomCursorExtWeb, WindowAttributesExtWeb};
|
||||
use winit::window::{
|
||||
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection,
|
||||
Theme, Window, WindowAttributes, WindowId,
|
||||
};
|
||||
|
||||
#[path = "util/tracing.rs"]
|
||||
mod tracing;
|
||||
@@ -44,36 +46,37 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
tracing::init();
|
||||
|
||||
let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
|
||||
let _event_loop_proxy = event_loop.create_proxy();
|
||||
let event_loop = EventLoop::new()?;
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
||||
// Wire the user event from another thread.
|
||||
#[cfg(not(web_platform))]
|
||||
std::thread::spawn(move || {
|
||||
// Wake up the `event_loop` once every second and dispatch a custom event
|
||||
// from a different thread.
|
||||
info!("Starting to send user event every second");
|
||||
loop {
|
||||
let _ = _event_loop_proxy.send_event(UserEvent::WakeUp);
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
}
|
||||
});
|
||||
{
|
||||
let event_loop_proxy = event_loop.create_proxy();
|
||||
let sender = sender.clone();
|
||||
std::thread::spawn(move || {
|
||||
// Wake up the `event_loop` once every second and dispatch a custom event
|
||||
// from a different thread.
|
||||
info!("Starting to send user event every second");
|
||||
loop {
|
||||
let _ = sender.send(Action::Message);
|
||||
event_loop_proxy.wake_up();
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut state = Application::new(&event_loop);
|
||||
|
||||
event_loop.run_app(&mut state).map_err(Into::into)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum UserEvent {
|
||||
WakeUp,
|
||||
let app = Application::new(&event_loop, receiver, sender);
|
||||
Ok(event_loop.run_app(app)?)
|
||||
}
|
||||
|
||||
/// Application state and event handling.
|
||||
struct Application {
|
||||
/// Trigger actions through proxy wake up.
|
||||
receiver: Receiver<Action>,
|
||||
sender: Sender<Action>,
|
||||
/// Custom cursors assets.
|
||||
custom_cursors: Vec<CustomCursor>,
|
||||
custom_cursors: Result<Vec<CustomCursor>, RequestError>,
|
||||
/// Application icon.
|
||||
icon: Icon,
|
||||
windows: HashMap<WindowId, WindowState>,
|
||||
@@ -85,7 +88,7 @@ struct Application {
|
||||
}
|
||||
|
||||
impl Application {
|
||||
fn new<T>(event_loop: &EventLoop<T>) -> Self {
|
||||
fn new(event_loop: &EventLoop, receiver: Receiver<Action>, sender: Sender<Action>) -> Self {
|
||||
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
let context = Some(
|
||||
@@ -105,13 +108,17 @@ impl Application {
|
||||
let icon = load_icon(include_bytes!("data/icon.png"));
|
||||
|
||||
info!("Loading cursor assets");
|
||||
let custom_cursors = vec![
|
||||
let custom_cursors = [
|
||||
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross.png"))),
|
||||
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross2.png"))),
|
||||
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/gradient.png"))),
|
||||
];
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
receiver,
|
||||
sender,
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
context,
|
||||
custom_cursors,
|
||||
@@ -122,13 +129,13 @@ impl Application {
|
||||
|
||||
fn create_window(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
_tab_id: Option<String>,
|
||||
) -> Result<WindowId, Box<dyn Error>> {
|
||||
// TODO read-out activation token.
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut window_attributes = Window::default_attributes()
|
||||
let mut window_attributes = WindowAttributes::default()
|
||||
.with_title("Winit window")
|
||||
.with_transparent(true)
|
||||
.with_window_icon(Some(self.icon.clone()));
|
||||
@@ -147,7 +154,6 @@ impl Application {
|
||||
|
||||
#[cfg(web_platform)]
|
||||
{
|
||||
use winit::platform::web::WindowAttributesExtWebSys;
|
||||
window_attributes = window_attributes.with_append(true);
|
||||
}
|
||||
|
||||
@@ -169,7 +175,23 @@ impl Application {
|
||||
Ok(window_id)
|
||||
}
|
||||
|
||||
fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
|
||||
fn handle_action_from_proxy(&mut self, _event_loop: &dyn ActiveEventLoop, action: Action) {
|
||||
match action {
|
||||
#[cfg(web_platform)]
|
||||
Action::DumpMonitors => self.dump_monitors(_event_loop),
|
||||
Action::Message => {
|
||||
info!("User wake up");
|
||||
},
|
||||
_ => unreachable!("Tried to execute invalid action without `WindowId`"),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_action_with_window(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
action: Action,
|
||||
) {
|
||||
// let cursor_position = self.cursor_position;
|
||||
let window = self.windows.get_mut(&window_id).unwrap();
|
||||
info!("Executing action: {action:?}");
|
||||
@@ -198,12 +220,27 @@ impl Application {
|
||||
Action::ToggleImeInput => window.toggle_ime(),
|
||||
Action::Minimize => window.minimize(),
|
||||
Action::NextCursor => window.next_cursor(),
|
||||
Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors),
|
||||
Action::NextCustomCursor => {
|
||||
if let Err(err) = self.custom_cursors.as_ref().map(|c| window.next_custom_cursor(c))
|
||||
{
|
||||
error!("Error creating custom cursor: {err}");
|
||||
}
|
||||
},
|
||||
#[cfg(web_platform)]
|
||||
Action::UrlCustomCursor => window.url_custom_cursor(event_loop),
|
||||
Action::UrlCustomCursor => {
|
||||
if let Err(err) = window.url_custom_cursor(event_loop) {
|
||||
error!("Error creating custom cursor from URL: {err}");
|
||||
}
|
||||
},
|
||||
#[cfg(web_platform)]
|
||||
Action::AnimationCustomCursor => {
|
||||
window.animation_custom_cursor(event_loop, &self.custom_cursors)
|
||||
if let Err(err) = self
|
||||
.custom_cursors
|
||||
.as_ref()
|
||||
.map(|c| window.animation_custom_cursor(event_loop, c))
|
||||
{
|
||||
error!("Error creating animated custom cursor: {err}");
|
||||
}
|
||||
},
|
||||
Action::CycleCursorGrab => window.cycle_cursor_grab(),
|
||||
Action::DragWindow => window.drag_window(),
|
||||
@@ -212,6 +249,12 @@ impl Application {
|
||||
Action::PrintHelp => self.print_help(),
|
||||
#[cfg(macos_platform)]
|
||||
Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
|
||||
Action::SetTheme(theme) => {
|
||||
window.window.set_theme(theme);
|
||||
// Get the resulting current theme to draw with
|
||||
let actual_theme = theme.or_else(|| window.window.theme()).unwrap_or(Theme::Dark);
|
||||
window.set_draw_theme(actual_theme);
|
||||
},
|
||||
#[cfg(macos_platform)]
|
||||
Action::CreateNewTab => {
|
||||
let tab_id = window.window.tabbing_identifier();
|
||||
@@ -220,10 +263,30 @@ impl Application {
|
||||
}
|
||||
},
|
||||
Action::RequestResize => window.swap_dimensions(),
|
||||
#[cfg(web_platform)]
|
||||
Action::DumpMonitors => {
|
||||
let future = event_loop.request_detailed_monitor_permission();
|
||||
let proxy = event_loop.create_proxy();
|
||||
let sender = self.sender.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
if let Err(error) = future.await {
|
||||
error!("{error}")
|
||||
}
|
||||
|
||||
let _ = sender.send(Action::DumpMonitors);
|
||||
proxy.wake_up();
|
||||
});
|
||||
},
|
||||
#[cfg(not(web_platform))]
|
||||
Action::DumpMonitors => self.dump_monitors(event_loop),
|
||||
Action::Message => {
|
||||
self.sender.send(Action::Message).unwrap();
|
||||
event_loop.create_proxy().wake_up();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_monitors(&self, event_loop: &ActiveEventLoop) {
|
||||
fn dump_monitors(&self, event_loop: &dyn ActiveEventLoop) {
|
||||
info!("Monitors information");
|
||||
let primary_monitor = event_loop.primary_monitor();
|
||||
for monitor in event_loop.available_monitors() {
|
||||
@@ -239,27 +302,32 @@ impl Application {
|
||||
info!("{intro}: [no name]");
|
||||
}
|
||||
|
||||
let PhysicalSize { width, height } = monitor.size();
|
||||
info!(
|
||||
" Current mode: {width}x{height}{}",
|
||||
if let Some(m_hz) = monitor.refresh_rate_millihertz() {
|
||||
format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
);
|
||||
if let Some(current_mode) = monitor.current_video_mode() {
|
||||
let PhysicalSize { width, height } = current_mode.size();
|
||||
let bits =
|
||||
current_mode.bit_depth().map(|bits| format!("x{bits}")).unwrap_or_default();
|
||||
let m_hz = current_mode
|
||||
.refresh_rate_millihertz()
|
||||
.map(|m_hz| format!(" @ {}.{} Hz", m_hz.get() / 1000, m_hz.get() % 1000))
|
||||
.unwrap_or_default();
|
||||
info!(" {width}x{height}{bits}{m_hz}");
|
||||
}
|
||||
|
||||
let PhysicalPosition { x, y } = monitor.position();
|
||||
info!(" Position: {x},{y}");
|
||||
if let Some(PhysicalPosition { x, y }) = monitor.position() {
|
||||
info!(" Position: {x},{y}");
|
||||
}
|
||||
|
||||
info!(" Scale factor: {}", monitor.scale_factor());
|
||||
|
||||
info!(" Available modes (width x height x bit-depth):");
|
||||
for mode in monitor.video_modes() {
|
||||
let PhysicalSize { width, height } = mode.size();
|
||||
let bits = mode.bit_depth();
|
||||
let m_hz = mode.refresh_rate_millihertz();
|
||||
info!(" {width}x{height}x{bits} @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
|
||||
let bits = mode.bit_depth().map(|bits| format!("x{bits}")).unwrap_or_default();
|
||||
let m_hz = mode
|
||||
.refresh_rate_millihertz()
|
||||
.map(|m_hz| format!(" @ {}.{} Hz", m_hz.get() / 1000, m_hz.get() % 1000))
|
||||
.unwrap_or_default();
|
||||
info!(" {width}x{height}{bits}{m_hz}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,14 +370,16 @@ impl Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler<UserEvent> for Application {
|
||||
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
|
||||
info!("User event: {event:?}");
|
||||
impl ApplicationHandler for Application {
|
||||
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
while let Ok(action) = self.receiver.try_recv() {
|
||||
self.handle_action_from_proxy(event_loop, action)
|
||||
}
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
@@ -319,7 +389,7 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||
};
|
||||
|
||||
match event {
|
||||
WindowEvent::Resized(size) => {
|
||||
WindowEvent::SurfaceResized(size) => {
|
||||
window.resize(size);
|
||||
},
|
||||
WindowEvent::Focused(focused) => {
|
||||
@@ -334,7 +404,7 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||
},
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
info!("Theme changed to {theme:?}");
|
||||
window.set_theme(theme);
|
||||
window.set_draw_theme(theme);
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Err(err) = window.draw() {
|
||||
@@ -372,7 +442,7 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||
};
|
||||
|
||||
if let Some(action) = action {
|
||||
self.handle_action(event_loop, window_id, action);
|
||||
self.handle_action_with_window(event_loop, window_id, action);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -381,7 +451,7 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||
if let Some(action) =
|
||||
state.is_pressed().then(|| Self::process_mouse_binding(button, &mods)).flatten()
|
||||
{
|
||||
self.handle_action(event_loop, window_id, action);
|
||||
self.handle_action_with_window(event_loop, window_id, action);
|
||||
}
|
||||
},
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
@@ -441,7 +511,6 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||
| WindowEvent::HoveredFileCancelled
|
||||
| WindowEvent::KeyboardInput { .. }
|
||||
| WindowEvent::CursorEntered { .. }
|
||||
| WindowEvent::AxisMotion { .. }
|
||||
| WindowEvent::DroppedFile(_)
|
||||
| WindowEvent::HoveredFile(_)
|
||||
| WindowEvent::Destroyed
|
||||
@@ -452,15 +521,15 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||
|
||||
fn device_event(
|
||||
&mut self,
|
||||
_event_loop: &ActiveEventLoop,
|
||||
_event_loop: &dyn ActiveEventLoop,
|
||||
device_id: DeviceId,
|
||||
event: DeviceEvent,
|
||||
) {
|
||||
info!("Device {device_id:?} event: {event:?}");
|
||||
}
|
||||
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
info!("Resumed the event loop");
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
info!("Ready to create surfaces");
|
||||
self.dump_monitors(event_loop);
|
||||
|
||||
// Create initial window.
|
||||
@@ -469,7 +538,7 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||
self.print_help();
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
if self.windows.is_empty() {
|
||||
info!("No windows left, exiting...");
|
||||
event_loop.exit();
|
||||
@@ -477,7 +546,7 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||
}
|
||||
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
fn exiting(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||
// We must drop the context here.
|
||||
self.context = None;
|
||||
}
|
||||
@@ -491,9 +560,9 @@ struct WindowState {
|
||||
///
|
||||
/// NOTE: This surface must be dropped before the `Window`.
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
surface: Surface<DisplayHandle<'static>, Arc<Window>>,
|
||||
surface: Surface<DisplayHandle<'static>, Arc<dyn Window>>,
|
||||
/// The actual winit Window.
|
||||
window: Arc<Window>,
|
||||
window: Arc<dyn Window>,
|
||||
/// The window theme we're drawing with.
|
||||
theme: Theme,
|
||||
/// Cursor position over the window.
|
||||
@@ -521,8 +590,8 @@ struct WindowState {
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
|
||||
let window = Arc::new(window);
|
||||
fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> {
|
||||
let window: Arc<dyn Window> = Arc::from(window);
|
||||
|
||||
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
|
||||
// it doesn't outlive it.
|
||||
@@ -532,17 +601,17 @@ impl WindowState {
|
||||
let theme = window.theme().unwrap_or(Theme::Dark);
|
||||
info!("Theme: {theme:?}");
|
||||
let named_idx = 0;
|
||||
window.set_cursor(CURSORS[named_idx]);
|
||||
window.set_cursor(CURSORS[named_idx].into());
|
||||
|
||||
// Allow IME out of the box.
|
||||
let ime = true;
|
||||
window.set_ime_allowed(ime);
|
||||
|
||||
let size = window.inner_size();
|
||||
let size = window.surface_size();
|
||||
let mut state = Self {
|
||||
#[cfg(macos_platform)]
|
||||
option_as_alt: window.option_as_alt(),
|
||||
custom_idx: app.custom_cursors.len() - 1,
|
||||
custom_idx: app.custom_cursors.as_ref().map(Vec::len).unwrap_or(1) - 1,
|
||||
cursor_grab: CursorGrabMode::None,
|
||||
named_idx,
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
@@ -567,7 +636,7 @@ impl WindowState {
|
||||
self.ime = !self.ime;
|
||||
self.window.set_ime_allowed(self.ime);
|
||||
if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
|
||||
self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
|
||||
self.window.set_ime_cursor_area(position.into(), PhysicalSize::new(20, 20).into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,7 +647,7 @@ impl WindowState {
|
||||
pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
|
||||
self.cursor_position = Some(position);
|
||||
if self.ime {
|
||||
self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
|
||||
self.window.set_ime_cursor_area(position.into(), PhysicalSize::new(20, 20).into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,12 +681,12 @@ impl WindowState {
|
||||
|
||||
/// Toggle resize increments on a window.
|
||||
fn toggle_resize_increments(&mut self) {
|
||||
let new_increments = match self.window.resize_increments() {
|
||||
let new_increments = match self.window.surface_resize_increments() {
|
||||
Some(_) => None,
|
||||
None => Some(LogicalSize::new(25.0, 25.0)),
|
||||
None => Some(LogicalSize::new(25.0, 25.0).into()),
|
||||
};
|
||||
info!("Had increments: {}", new_increments.is_none());
|
||||
self.window.set_resize_increments(new_increments);
|
||||
self.window.set_surface_resize_increments(new_increments);
|
||||
}
|
||||
|
||||
/// Toggle fullscreen.
|
||||
@@ -656,22 +725,22 @@ impl WindowState {
|
||||
self.window.set_option_as_alt(self.option_as_alt);
|
||||
}
|
||||
|
||||
/// Swap the window dimensions with `request_inner_size`.
|
||||
/// Swap the window dimensions with `request_surface_size`.
|
||||
fn swap_dimensions(&mut self) {
|
||||
let old_inner_size = self.window.inner_size();
|
||||
let mut inner_size = old_inner_size;
|
||||
let old_surface_size = self.window.surface_size();
|
||||
let mut surface_size = old_surface_size;
|
||||
|
||||
mem::swap(&mut inner_size.width, &mut inner_size.height);
|
||||
info!("Requesting resize from {old_inner_size:?} to {inner_size:?}");
|
||||
mem::swap(&mut surface_size.width, &mut surface_size.height);
|
||||
info!("Requesting resize from {old_surface_size:?} to {surface_size:?}");
|
||||
|
||||
if let Some(new_inner_size) = self.window.request_inner_size(inner_size) {
|
||||
if old_inner_size == new_inner_size {
|
||||
if let Some(new_surface_size) = self.window.request_surface_size(surface_size.into()) {
|
||||
if old_surface_size == new_surface_size {
|
||||
info!("Inner size change got ignored");
|
||||
} else {
|
||||
self.resize(new_inner_size);
|
||||
self.resize(new_surface_size);
|
||||
}
|
||||
} else {
|
||||
info!("Request inner size is asynchronous");
|
||||
info!("Requesting surface size is asynchronous");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,36 +760,42 @@ impl WindowState {
|
||||
|
||||
/// Custom cursor from an URL.
|
||||
#[cfg(web_platform)]
|
||||
fn url_custom_cursor(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let cursor = event_loop.create_custom_cursor(url_custom_cursor());
|
||||
fn url_custom_cursor(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let cursor = event_loop.create_custom_cursor(url_custom_cursor())?;
|
||||
|
||||
self.window.set_cursor(cursor);
|
||||
self.window.set_cursor(cursor.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Custom cursor from a URL.
|
||||
#[cfg(web_platform)]
|
||||
fn animation_custom_cursor(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
custom_cursors: &[CustomCursor],
|
||||
) {
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
use std::time::Duration;
|
||||
use winit::platform::web::CustomCursorExtWebSys;
|
||||
|
||||
let cursors = vec![
|
||||
custom_cursors[0].clone(),
|
||||
custom_cursors[1].clone(),
|
||||
event_loop.create_custom_cursor(url_custom_cursor()),
|
||||
event_loop.create_custom_cursor(url_custom_cursor())?,
|
||||
];
|
||||
let cursor = CustomCursor::from_animation(Duration::from_secs(3), cursors).unwrap();
|
||||
let cursor = event_loop.create_custom_cursor(cursor);
|
||||
let cursor = event_loop.create_custom_cursor(cursor)?;
|
||||
|
||||
self.window.set_cursor(cursor);
|
||||
self.window.set_cursor(cursor.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resize the window to the new size.
|
||||
/// Resize the surface to the new size.
|
||||
fn resize(&mut self, size: PhysicalSize<u32>) {
|
||||
info!("Resized to {size:?}");
|
||||
info!("Surface resized to {size:?}");
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
{
|
||||
let (width, height) = match (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
@@ -733,8 +808,8 @@ impl WindowState {
|
||||
self.window.request_redraw();
|
||||
}
|
||||
|
||||
/// Change the theme.
|
||||
fn set_theme(&mut self, theme: Theme) {
|
||||
/// Change the theme that things are drawn in.
|
||||
fn set_draw_theme(&mut self, theme: Theme) {
|
||||
self.theme = theme;
|
||||
self.window.request_redraw();
|
||||
}
|
||||
@@ -742,7 +817,7 @@ impl WindowState {
|
||||
/// Show window menu.
|
||||
fn show_menu(&self) {
|
||||
if let Some(position) = self.cursor_position {
|
||||
self.window.show_window_menu(position);
|
||||
self.window.show_window_menu(position.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,7 +840,7 @@ impl WindowState {
|
||||
},
|
||||
};
|
||||
|
||||
let win_size = self.window.inner_size();
|
||||
let win_size = self.window.surface_size();
|
||||
let border_size = BORDER_SIZE * self.window.scale_factor();
|
||||
|
||||
let x_direction = if position.x < border_size {
|
||||
@@ -884,9 +959,12 @@ enum Action {
|
||||
ShowWindowMenu,
|
||||
#[cfg(macos_platform)]
|
||||
CycleOptionAsAlt,
|
||||
SetTheme(Option<Theme>),
|
||||
#[cfg(macos_platform)]
|
||||
CreateNewTab,
|
||||
RequestResize,
|
||||
DumpMonitors,
|
||||
Message,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
@@ -915,9 +993,20 @@ impl Action {
|
||||
Action::ShowWindowMenu => "Show window menu",
|
||||
#[cfg(macos_platform)]
|
||||
Action::CycleOptionAsAlt => "Cycle option as alt mode",
|
||||
Action::SetTheme(None) => "Change to the system theme",
|
||||
Action::SetTheme(Some(Theme::Light)) => "Change to a light theme",
|
||||
Action::SetTheme(Some(Theme::Dark)) => "Change to a dark theme",
|
||||
#[cfg(macos_platform)]
|
||||
Action::CreateNewTab => "Create new tab",
|
||||
Action::RequestResize => "Request a resize",
|
||||
#[cfg(not(web_platform))]
|
||||
Action::DumpMonitors => "Dump monitor information",
|
||||
#[cfg(web_platform)]
|
||||
Action::DumpMonitors => {
|
||||
"Request permission to query detailed monitor information and dump monitor \
|
||||
information"
|
||||
},
|
||||
Action::Message => "Prints a message through a user wake up",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -940,8 +1029,6 @@ fn decode_cursor(bytes: &[u8]) -> CustomCursorSource {
|
||||
fn url_custom_cursor() -> CustomCursorSource {
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use winit::platform::web::CustomCursorExtWebSys;
|
||||
|
||||
static URL_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
CustomCursor::from_url(
|
||||
@@ -1039,6 +1126,7 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
|
||||
Binding::new("R", ModifiersState::CONTROL, Action::ToggleResizable),
|
||||
Binding::new("R", ModifiersState::ALT, Action::RequestResize),
|
||||
// M.
|
||||
Binding::new("M", ModifiersState::CONTROL.union(ModifiersState::ALT), Action::DumpMonitors),
|
||||
Binding::new("M", ModifiersState::CONTROL, Action::ToggleMaximize),
|
||||
Binding::new("M", ModifiersState::ALT, Action::Minimize),
|
||||
// N.
|
||||
@@ -1059,10 +1147,15 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
|
||||
Action::AnimationCustomCursor,
|
||||
),
|
||||
Binding::new("Z", ModifiersState::CONTROL, Action::ToggleCursorVisibility),
|
||||
// K.
|
||||
Binding::new("K", ModifiersState::empty(), Action::SetTheme(None)),
|
||||
Binding::new("K", ModifiersState::SUPER, Action::SetTheme(Some(Theme::Light))),
|
||||
Binding::new("K", ModifiersState::CONTROL, Action::SetTheme(Some(Theme::Dark))),
|
||||
#[cfg(macos_platform)]
|
||||
Binding::new("T", ModifiersState::SUPER, Action::CreateNewTab),
|
||||
#[cfg(macos_platform)]
|
||||
Binding::new("O", ModifiersState::CONTROL, Action::CycleOptionAsAlt),
|
||||
Binding::new("S", ModifiersState::CONTROL, Action::Message),
|
||||
];
|
||||
|
||||
const MOUSE_BINDINGS: &[Binding<MouseButton>] = &[
|
||||
|
||||
@@ -7,21 +7,21 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::platform::x11::WindowAttributesExtX11;
|
||||
use winit::window::{Window, WindowId};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
pub struct XEmbedDemo {
|
||||
parent_window_id: u32,
|
||||
window: Option<Window>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for XEmbedDemo {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let window_attributes = Window::default_attributes()
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default()
|
||||
.with_title("An embedded window!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.with_embed_parent_window(self.parent_window_id);
|
||||
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
@@ -29,7 +29,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
@@ -38,13 +38,13 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window);
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||
self.window.as_ref().unwrap().request_redraw();
|
||||
}
|
||||
}
|
||||
@@ -58,8 +58,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
tracing_subscriber::fmt::init();
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
let mut app = XEmbedDemo { parent_window_id, window: None };
|
||||
event_loop.run_app(&mut app).map_err(Into::into)
|
||||
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?)
|
||||
}
|
||||
|
||||
#[cfg(not(x11_platform))]
|
||||
|
||||
27
rustfmt.toml
27
rustfmt.toml
@@ -1,19 +1,20 @@
|
||||
format_code_in_doc_comments = true
|
||||
match_block_trailing_comma = true
|
||||
comment_width = 100
|
||||
condense_wildcard_suffixes = true
|
||||
use_field_init_shorthand = true
|
||||
error_on_unformatted = true
|
||||
format_code_in_doc_comments = true
|
||||
format_macro_bodies = true
|
||||
format_macro_matchers = true
|
||||
format_strings = true
|
||||
group_imports = "StdExternalCrate"
|
||||
hex_literal_case = "Lower"
|
||||
imports_granularity = "Module"
|
||||
match_block_trailing_comma = true
|
||||
newline_style = "Unix"
|
||||
normalize_comments = true
|
||||
normalize_doc_attributes = true
|
||||
overflow_delimited_expr = true
|
||||
imports_granularity = "Module"
|
||||
use_small_heuristics = "Max"
|
||||
format_macro_matchers = true
|
||||
error_on_unformatted = true
|
||||
format_macro_bodies = true
|
||||
hex_literal_case = "Lower"
|
||||
normalize_comments = true
|
||||
# Some macros break with this.
|
||||
reorder_impl_items = false
|
||||
newline_style = "Unix"
|
||||
format_strings = true
|
||||
use_field_init_shorthand = true
|
||||
use_small_heuristics = "Max"
|
||||
wrap_comments = true
|
||||
comment_width = 100
|
||||
|
||||
@@ -5,94 +5,189 @@ use crate::event_loop::ActiveEventLoop;
|
||||
use crate::window::WindowId;
|
||||
|
||||
/// The handler of the application events.
|
||||
pub trait ApplicationHandler<T: 'static = ()> {
|
||||
pub trait ApplicationHandler {
|
||||
/// Emitted when new events arrive from the OS to be processed.
|
||||
///
|
||||
/// This is a useful place to put code that should be done before you start processing
|
||||
/// events, such as updating frame timing information for benchmarking or checking the
|
||||
/// [`StartCause`] to see if a timer set by
|
||||
/// [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil] has elapsed.
|
||||
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
|
||||
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
|
||||
let _ = (event_loop, cause);
|
||||
}
|
||||
|
||||
/// Emitted when the application has been resumed.
|
||||
///
|
||||
/// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a
|
||||
/// formal suspend/resume lifecycle. For systems without a formal suspend/resume lifecycle
|
||||
/// the `Resumed` event is always emitted after the
|
||||
/// [`NewEvents(StartCause::Init)`][StartCause::Init] event.
|
||||
/// See [`suspended()`][Self::suspended].
|
||||
///
|
||||
/// # Portability
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// It's recommended that applications should only initialize their graphics context and create
|
||||
/// a window after they have received their first `Resumed` event. Some systems
|
||||
/// (specifically Android) won't allow applications to create a render surface until they are
|
||||
/// resumed.
|
||||
/// ### iOS
|
||||
///
|
||||
/// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally
|
||||
/// driven by multiple platform-specific events, and that there may be subtle differences across
|
||||
/// platforms with how these internal events are delivered, it's recommended that applications
|
||||
/// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed`
|
||||
/// events.
|
||||
/// On iOS, the [`resumed()`] method is called in response to an [`applicationDidBecomeActive`]
|
||||
/// callback which means the application is about to transition from the inactive to active
|
||||
/// state (according to the [iOS application lifecycle]).
|
||||
///
|
||||
/// Also see [`Suspended`] notes.
|
||||
/// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive
|
||||
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
|
||||
///
|
||||
/// ## Android
|
||||
/// ### Web
|
||||
///
|
||||
/// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is
|
||||
/// expected to closely correlate with the [`onResume`] lifecycle event but there may
|
||||
/// technically be a discrepancy.
|
||||
/// On Web, the [`resumed()`] method is called in response to a [`pageshow`] event if the
|
||||
/// page is being restored from the [`bfcache`] (back/forward cache) - an in-memory cache
|
||||
/// that stores a complete snapshot of a page (including the JavaScript heap) as the user is
|
||||
/// navigating away.
|
||||
///
|
||||
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
|
||||
/// [`bfcache`]: https://web.dev/bfcache/
|
||||
///
|
||||
/// ### Others
|
||||
///
|
||||
/// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported.
|
||||
///
|
||||
/// [`resumed()`]: Self::resumed
|
||||
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
|
||||
/// Emitted from the point onwards the application should create render surfaces.
|
||||
///
|
||||
/// See [`destroy_surfaces()`].
|
||||
///
|
||||
/// ## Portability
|
||||
///
|
||||
/// It's recommended that applications should only initialize their render surfaces after the
|
||||
/// [`can_create_surfaces()`] method is called. Some systems (specifically Android) won't allow
|
||||
/// applications to create a render surface until that point.
|
||||
///
|
||||
/// For consistency, all platforms call this method even if they don't themselves have a formal
|
||||
/// surface destroy/create lifecycle. For systems without a surface destroy/create lifecycle the
|
||||
/// [`can_create_surfaces()`] event is always emitted after the [`StartCause::Init`] event.
|
||||
///
|
||||
/// Applications should be able to gracefully handle back-to-back [`can_create_surfaces()`] and
|
||||
/// [`destroy_surfaces()`] calls.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// ### Android
|
||||
///
|
||||
/// On Android, the [`can_create_surfaces()`] method is called when a new [`SurfaceView`] has
|
||||
/// been created. This is expected to closely correlate with the [`onResume`] lifecycle
|
||||
/// event but there may technically be a discrepancy.
|
||||
///
|
||||
/// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
|
||||
///
|
||||
/// Applications that need to run on Android must wait until they have been `Resumed`
|
||||
/// before they will be able to create a render surface (such as an `EGLSurface`,
|
||||
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a
|
||||
/// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their
|
||||
/// render surfaces are invalid and should be dropped.
|
||||
///
|
||||
/// Also see [`Suspended`] notes.
|
||||
/// Applications that need to run on Android must wait until they have been "resumed" before
|
||||
/// they will be able to create a render surface (such as an `EGLSurface`, [`VkSurfaceKHR`]
|
||||
/// or [`wgpu::Surface`]) which depend on having a [`SurfaceView`]. Applications must also
|
||||
/// assume that if they are [suspended], then their render surfaces are invalid and should
|
||||
/// be dropped.
|
||||
///
|
||||
/// [suspended]: Self::destroy_surfaces
|
||||
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
|
||||
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
|
||||
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
|
||||
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
|
||||
///
|
||||
/// ## iOS
|
||||
///
|
||||
/// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`]
|
||||
/// callback which means the application is "active" (according to the
|
||||
/// [iOS application lifecycle]).
|
||||
///
|
||||
/// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive
|
||||
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
|
||||
///
|
||||
/// ## Web
|
||||
///
|
||||
/// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event
|
||||
/// with the property [`persisted`] being true, which means that the page is being
|
||||
/// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
|
||||
/// stores a complete snapshot of a page (including the JavaScript heap) as the
|
||||
/// user is navigating away.
|
||||
///
|
||||
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
|
||||
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
|
||||
/// [`bfcache`]: https://web.dev/bfcache/
|
||||
/// [`Suspended`]: Self::suspended
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop);
|
||||
/// [`can_create_surfaces()`]: Self::can_create_surfaces
|
||||
/// [`destroy_surfaces()`]: Self::destroy_surfaces
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop);
|
||||
|
||||
/// Emitted when an event is sent from [`EventLoopProxy::send_event`].
|
||||
/// Called after a wake up is requested using [`EventLoopProxy::wake_up()`].
|
||||
///
|
||||
/// [`EventLoopProxy::send_event`]: crate::event_loop::EventLoopProxy::send_event
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
|
||||
let _ = (event_loop, event);
|
||||
/// Multiple calls to the aforementioned method will be merged, and will only wake the event
|
||||
/// loop once; however, due to the nature of multi-threading some wake ups may appear
|
||||
/// spuriously. For these reasons, you should not rely on the number of times that this was
|
||||
/// called.
|
||||
///
|
||||
/// The order in which this is emitted in relation to other events is not guaranteed. The time
|
||||
/// at which this will be emitted is not guaranteed, only that it will happen "soon". That is,
|
||||
/// there may be several executions of the event loop, including multiple redraws to windows,
|
||||
/// between [`EventLoopProxy::wake_up()`] being called and the event being delivered.
|
||||
///
|
||||
/// [`EventLoopProxy::wake_up()`]: crate::event_loop::EventLoopProxy::wake_up
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Use a [`std::sync::mpsc`] channel to handle events from a different thread.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::sync::mpsc;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// use winit::application::ApplicationHandler;
|
||||
/// use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
///
|
||||
/// struct MyApp {
|
||||
/// receiver: mpsc::Receiver<u64>,
|
||||
/// }
|
||||
///
|
||||
/// impl ApplicationHandler for MyApp {
|
||||
/// # fn window_event(
|
||||
/// # &mut self,
|
||||
/// # _event_loop: &dyn ActiveEventLoop,
|
||||
/// # _window_id: winit::window::WindowId,
|
||||
/// # _event: winit::event::WindowEvent,
|
||||
/// # ) {
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn can_create_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {}
|
||||
/// #
|
||||
/// fn proxy_wake_up(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||
/// // Iterate current events, since wake-ups may have been merged.
|
||||
/// //
|
||||
/// // Note: We take care not to use `recv` or `iter` here, as those are blocking,
|
||||
/// // and that would be bad for performance and might lead to a deadlock.
|
||||
/// for i in self.receiver.try_iter() {
|
||||
/// println!("received: {i}");
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Rest of `ApplicationHandler`
|
||||
/// }
|
||||
///
|
||||
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let event_loop = EventLoop::new()?;
|
||||
///
|
||||
/// let (sender, receiver) = mpsc::channel();
|
||||
///
|
||||
/// let mut app = MyApp { receiver };
|
||||
///
|
||||
/// // Send an event in a loop
|
||||
/// let proxy = event_loop.create_proxy();
|
||||
/// let background_thread = thread::spawn(move || {
|
||||
/// let mut i = 0;
|
||||
/// loop {
|
||||
/// println!("sending: {i}");
|
||||
/// if sender.send(i).is_err() {
|
||||
/// // Stop sending once `MyApp` is dropped
|
||||
/// break;
|
||||
/// }
|
||||
/// // Trigger the wake-up _after_ we placed the event in the channel.
|
||||
/// // Otherwise, `proxy_wake_up` might be triggered prematurely.
|
||||
/// proxy.wake_up();
|
||||
/// i += 1;
|
||||
/// thread::sleep(Duration::from_secs(1));
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// event_loop.run_app(&mut app)?;
|
||||
///
|
||||
/// drop(app);
|
||||
/// background_thread.join().unwrap();
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
|
||||
/// Emitted when the OS sends an event to a winit window.
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
);
|
||||
@@ -100,7 +195,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
|
||||
/// Emitted when the OS sends an event to a device.
|
||||
fn device_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
device_id: DeviceId,
|
||||
event: DeviceEvent,
|
||||
) {
|
||||
@@ -118,31 +213,53 @@ pub trait ApplicationHandler<T: 'static = ()> {
|
||||
///
|
||||
/// This is not an ideal event to drive application rendering from and instead applications
|
||||
/// should render in response to [`WindowEvent::RedrawRequested`] events.
|
||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
|
||||
/// Emitted when the application has been suspended.
|
||||
///
|
||||
/// # Portability
|
||||
/// See [`resumed()`][Self::resumed].
|
||||
///
|
||||
/// Not all platforms support the notion of suspending applications, and there may be no
|
||||
/// technical way to guarantee being able to emit a `Suspended` event if the OS has
|
||||
/// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason,
|
||||
/// Winit does not currently try to emit pseudo `Suspended` events before the application
|
||||
/// quits on platforms without an application lifecycle.
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally
|
||||
/// driven by multiple platform-specific events, and that there may be subtle differences across
|
||||
/// platforms with how these internal events are delivered, it's recommended that applications
|
||||
/// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`]
|
||||
/// events.
|
||||
/// ### iOS
|
||||
///
|
||||
/// Also see [`Resumed`] notes.
|
||||
/// On iOS, the [`suspended()`] method is called in response to an
|
||||
/// [`applicationWillResignActive`] callback which means that the application is about to
|
||||
/// transition from the active to inactive state (according to the [iOS application lifecycle]).
|
||||
///
|
||||
/// ## Android
|
||||
/// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive
|
||||
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
|
||||
///
|
||||
/// On Android, the `Suspended` event is only sent when the application's associated
|
||||
/// ### Web
|
||||
///
|
||||
/// On Web, the [`suspended()`] method is called in response to a [`pagehide`] event if the
|
||||
/// page is being restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
|
||||
/// stores a complete snapshot of a page (including the JavaScript heap) as the user is
|
||||
/// navigating away.
|
||||
///
|
||||
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
|
||||
/// [`bfcache`]: https://web.dev/bfcache/
|
||||
///
|
||||
/// ### Others
|
||||
///
|
||||
/// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported.
|
||||
///
|
||||
/// [`suspended()`]: Self::suspended
|
||||
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
|
||||
/// Emitted when the application must destroy its render surfaces.
|
||||
///
|
||||
/// See [`can_create_surfaces()`] for more details.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// ### Android
|
||||
///
|
||||
/// On Android, the [`destroy_surfaces()`] method is called when the application's associated
|
||||
/// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`]
|
||||
/// lifecycle event but there may technically be a discrepancy.
|
||||
///
|
||||
@@ -152,38 +269,24 @@ pub trait ApplicationHandler<T: 'static = ()> {
|
||||
/// destroyed, which indirectly invalidates any existing render surfaces that may have been
|
||||
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
|
||||
///
|
||||
/// After being `Suspended` on Android applications must drop all render surfaces before
|
||||
/// After being [suspended] on Android applications must drop all render surfaces before
|
||||
/// the event callback completes, which may be re-created when the application is next
|
||||
/// [`Resumed`].
|
||||
/// [resumed].
|
||||
///
|
||||
/// [suspended]: Self::destroy_surfaces
|
||||
/// [resumed]: Self::can_create_surfaces
|
||||
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
|
||||
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
|
||||
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
|
||||
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
|
||||
///
|
||||
/// ## iOS
|
||||
/// ### Others
|
||||
///
|
||||
/// On iOS, the `Suspended` event is currently emitted in response to an
|
||||
/// [`applicationWillResignActive`] callback which means that the application is
|
||||
/// about to transition from the active to inactive state (according to the
|
||||
/// [iOS application lifecycle]).
|
||||
/// - **iOS / macOS / Orbital / Wayland / Web / Windows / X11:** Unsupported.
|
||||
///
|
||||
/// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive
|
||||
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
|
||||
///
|
||||
/// ## Web
|
||||
///
|
||||
/// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event
|
||||
/// with the property [`persisted`] being true, which means that the page is being
|
||||
/// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a
|
||||
/// complete snapshot of a page (including the JavaScript heap) as the user is
|
||||
/// navigating away.
|
||||
///
|
||||
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
|
||||
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
|
||||
/// [`bfcache`]: https://web.dev/bfcache/
|
||||
/// [`Resumed`]: Self::resumed
|
||||
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
|
||||
/// [`can_create_surfaces()`]: Self::can_create_surfaces
|
||||
/// [`destroy_surfaces()`]: Self::destroy_surfaces
|
||||
fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
|
||||
@@ -191,7 +294,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
|
||||
///
|
||||
/// This is irreversible - if this method is called, it is guaranteed that the event loop
|
||||
/// will exit right after.
|
||||
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
|
||||
@@ -219,31 +322,37 @@ pub trait ApplicationHandler<T: 'static = ()> {
|
||||
/// ### Others
|
||||
///
|
||||
/// - **macOS / Orbital / Wayland / Web / Windows:** Unsupported.
|
||||
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for &mut A {
|
||||
#[deny(clippy::missing_trait_methods)]
|
||||
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
||||
#[inline]
|
||||
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
|
||||
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
|
||||
(**self).new_events(event_loop, cause);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).resumed(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
|
||||
(**self).user_event(event_loop, event);
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).can_create_surfaces(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).proxy_wake_up(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
@@ -253,7 +362,7 @@ impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for &m
|
||||
#[inline]
|
||||
fn device_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
device_id: DeviceId,
|
||||
event: DeviceEvent,
|
||||
) {
|
||||
@@ -261,46 +370,57 @@ impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for &m
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).about_to_wait(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).suspended(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).destroy_surfaces(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).exiting(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).memory_warning(event_loop);
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for Box<A> {
|
||||
#[deny(clippy::missing_trait_methods)]
|
||||
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
|
||||
#[inline]
|
||||
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
|
||||
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
|
||||
(**self).new_events(event_loop, cause);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).resumed(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
|
||||
(**self).user_event(event_loop, event);
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).can_create_surfaces(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).proxy_wake_up(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
@@ -310,7 +430,7 @@ impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for Bo
|
||||
#[inline]
|
||||
fn device_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
device_id: DeviceId,
|
||||
event: DeviceEvent,
|
||||
) {
|
||||
@@ -318,22 +438,27 @@ impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for Bo
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).about_to_wait(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).suspended(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).destroy_surfaces(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).exiting(event_loop);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
|
||||
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
(**self).memory_warning(event_loop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,3 +39,118 @@ The migration guide could reference other migration examples in the current
|
||||
changelog entry.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Add `ActiveEventLoop::create_proxy()`.
|
||||
- On Web, add `ActiveEventLoopExtWeb::is_cursor_lock_raw()` to determine if
|
||||
`DeviceEvent::MouseMotion` is returning raw data, not OS accelerated, when using
|
||||
`CursorGrabMode::Locked`.
|
||||
- On Web, implement `MonitorHandle` and `VideoModeHandle`.
|
||||
|
||||
Without prompting the user for permission, only the current monitor is returned. But when
|
||||
prompting and being granted permission through
|
||||
`ActiveEventLoop::request_detailed_monitor_permission()`, access to all monitors and their
|
||||
details is available. Handles created with "detailed monitor permissions" can be used in
|
||||
`Window::set_fullscreen()` as well.
|
||||
|
||||
Keep in mind that handles do not auto-upgrade after permissions are granted and have to be
|
||||
re-created to make full use of this feature.
|
||||
- Add `Touch::finger_id` with a new type `FingerId`.
|
||||
- On Web and Windows, add `FingerIdExt*::is_primary()`, exposing a way to determine
|
||||
the primary finger in a multi-touch interaction.
|
||||
- Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd`
|
||||
and `Serialize` on many types.
|
||||
- Add `MonitorHandle::current_video_mode()`.
|
||||
- Add basic iOS IME support. The soft keyboard can now be shown using `Window::set_ime_allowed`.
|
||||
- On macOS, add `WindowExtMacOS::set_borderless_game` and `WindowAttributesExtMacOS::with_borderless_game`
|
||||
to fully disable the menu bar and dock in Borderless Fullscreen as commonly done in games.
|
||||
|
||||
### Changed
|
||||
|
||||
- Change `ActiveEventLoop` to be a trait.
|
||||
- Change `Window` to be a trait.
|
||||
- `ActiveEventLoop::create_window` now returns `Box<dyn Window>`.
|
||||
- `ApplicationHandler` now uses `dyn ActiveEventLoop`.
|
||||
- On Web, let events wake up event loop immediately when using `ControlFlow::Poll`.
|
||||
- Bump MSRV from `1.70` to `1.73`.
|
||||
- Changed `ApplicationHandler::user_event` to `user_wake_up`, removing the
|
||||
generic user event.
|
||||
|
||||
Winit will now only indicate that wake up happened, you will have to pair
|
||||
this with an external mechanism like `std::sync::mpsc::channel` if you want
|
||||
to send specific data to be processed on the main thread.
|
||||
- Changed `EventLoopProxy::send_event` to `EventLoopProxy::wake_up`, it now
|
||||
only wakes up the loop.
|
||||
- On X11, implement smooth resizing through the sync extension API.
|
||||
- `ApplicationHandler::create|destroy_surfaces()` was split off from
|
||||
`ApplicationHandler::resumed/suspended()`.
|
||||
|
||||
`ApplicationHandler::can_create_surfaces()` should, for portability reasons
|
||||
to Android, be the only place to create render surfaces.
|
||||
|
||||
`ApplicationHandler::resumed/suspended()` are now only emitted by iOS and Web
|
||||
and now signify actually resuming/suspending the application.
|
||||
- Rename `platform::web::*ExtWebSys` to `*ExtWeb`.
|
||||
- Change signature of `EventLoop::run_app`, `EventLoopExtPumpEvents::pump_app_events` and
|
||||
`EventLoopExtRunOnDemand::run_app_on_demand` to accept a `impl ApplicationHandler` directly,
|
||||
instead of requiring a `&mut` reference to it.
|
||||
- On Web, `Window::canvas()` now returns a reference.
|
||||
- On Web, `CursorGrabMode::Locked` now lets `DeviceEvent::MouseMotion` return raw data, not OS
|
||||
accelerated, if the browser supports it.
|
||||
- `(Active)EventLoop::create_custom_cursor()` now returns a `Result<CustomCursor, ExternalError>`.
|
||||
- Changed how `ModifiersState` is serialized by Serde.
|
||||
- `VideoModeHandle::refresh_rate_millihertz()` and `bit_depth()` now return a `Option<NonZero*>`.
|
||||
- `MonitorHandle::position()` now returns an `Option`.
|
||||
- On iOS and macOS, remove custom application delegates. You are now allowed to override the
|
||||
application delegate yourself.
|
||||
- On iOS, no longer act as-if the application successfully open all URLs. Override
|
||||
`application:didFinishLaunchingWithOptions:` and provide the desired behaviour yourself.
|
||||
- On X11, remove our dependency on libXcursor. (#3749)
|
||||
- Renamed the following APIs to make it clearer that the sizes apply to the underlying surface:
|
||||
- `WindowEvent::Resized` to `SurfaceResized`.
|
||||
- `InnerSizeWriter` to `SurfaceSizeWriter`.
|
||||
- `WindowAttributes.inner_size` to `surface_size`.
|
||||
- `WindowAttributes.min_inner_size` to `min_surface_size`.
|
||||
- `WindowAttributes.max_inner_size` to `max_surface_size`.
|
||||
- `WindowAttributes.resize_increments` to `surface_resize_increments`.
|
||||
- `WindowAttributes::with_inner_size` to `with_surface_size`.
|
||||
- `WindowAttributes::with_min_inner_size` to `with_min_surface_size`.
|
||||
- `WindowAttributes::with_max_inner_size` to `with_max_surface_size`.
|
||||
- `WindowAttributes::with_resize_increments` to `with_surface_resize_increments`.
|
||||
- `Window::inner_size` to `surface_size`.
|
||||
- `Window::request_inner_size` to `request_surface_size`.
|
||||
- `Window::set_min_inner_size` to `set_min_surface_size`.
|
||||
- `Window::set_max_inner_size` to `set_max_surface_size`.
|
||||
|
||||
To migrate, you can probably just replace all instances of `inner_size` with `surface_size` in your codebase.
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove `Event`.
|
||||
- Remove already deprecated APIs:
|
||||
- `EventLoop::create_window()`
|
||||
- `EventLoop::run`.
|
||||
- `EventLoopBuilder::new()`
|
||||
- `EventLoopExtPumpEvents::pump_events`.
|
||||
- `EventLoopExtRunOnDemand::run_on_demand`.
|
||||
- `VideoMode`
|
||||
- `WindowAttributes::new()`
|
||||
- `Window::set_cursor_icon()`
|
||||
- On iOS, remove `platform::ios::EventLoopExtIOS` and related `platform::ios::Idiom` type.
|
||||
|
||||
This feature was incomplete, and the equivalent functionality can be trivially achieved outside
|
||||
of `winit` using `objc2-ui-kit` and calling `UIDevice::currentDevice().userInterfaceIdiom()`.
|
||||
- On Web, remove unused `platform::web::CustomCursorError::Animation`.
|
||||
- Remove the `rwh_04` and `rwh_05` cargo feature and the corresponding `raw-window-handle` v0.4 and
|
||||
v0.5 support. v0.6 remains in place and is enabled by default.
|
||||
- Remove `DeviceEvent::Added` and `DeviceEvent::Removed`.
|
||||
- Remove `DeviceEvent::Motion` and `WindowEvent::AxisMotion`.
|
||||
- Remove `Touch::id` in favor of `Touch::finger_id`.
|
||||
- Remove `MonitorHandle::size()` and `refresh_rate_millihertz()` in favor of
|
||||
`MonitorHandle::current_video_mode()`.
|
||||
- On Android, remove all `MonitorHandle` support instead of emitting false data.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On Orbital, `MonitorHandle::name()` now returns `None` instead of a dummy name.
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
|
||||
- On X11, non-resizable windows now have maximize explicitly disabled.
|
||||
- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile`
|
||||
and `WindowEvent::HoveredFile`.
|
||||
and `WindowEvent::HoveredFile`.
|
||||
- On Mac, implement `DeviceEvent::Button`.
|
||||
- Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`.
|
||||
- On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor.
|
||||
|
||||
@@ -1,3 +1,53 @@
|
||||
## 0.30.5
|
||||
|
||||
### Added
|
||||
|
||||
- Add `ActiveEventLoop::system_theme()`, returning the current system theme.
|
||||
- On Web, implement `Error` for `platform::web::CustomCursorError`.
|
||||
- On Android, add `{Active,}EventLoopExtAndroid::android_app()` to access the app used to create the loop.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On MacOS, fix building with `feature = "rwh_04"`.
|
||||
- On Web, pen events are now routed through to `WindowEvent::Cursor*`.
|
||||
- On macOS, fix panic when releasing not available monitor.
|
||||
- On MacOS, return the system theme in `Window::theme()` if no theme override is set.
|
||||
|
||||
## 0.30.4
|
||||
|
||||
### Changed
|
||||
|
||||
- `DeviceId::dummy()` and `WindowId::dummy()` are no longer marked `unsafe`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On Wayland, avoid crashing when compositor is misbehaving.
|
||||
- On Web, fix `WindowEvent::Resized` not using `requestAnimationFrame` when sending
|
||||
`WindowEvent::RedrawRequested` and also potentially causing `WindowEvent::RedrawRequested`
|
||||
to not be de-duplicated.
|
||||
- Account for different browser engine implementations of pointer movement coordinate space.
|
||||
|
||||
## 0.30.3
|
||||
|
||||
### Added
|
||||
|
||||
- On Web, add `EventLoopExtWebSys::(set_)poll_strategy()` to allow setting
|
||||
control flow strategies before starting the event loop.
|
||||
- On Web, add `WaitUntilStrategy`, which allows to set different strategies for
|
||||
`ControlFlow::WaitUntil`. By default the Prioritized Task Scheduling API is
|
||||
used, with a fallback to `setTimeout()` with a trick to circumvent throttling
|
||||
to 4ms. But an option to use a Web worker to schedule the timer is available
|
||||
as well, which commonly prevents any throttling when the window is not focused.
|
||||
|
||||
### Changed
|
||||
|
||||
- On macOS, set the window theme on the `NSWindow` instead of application-wide.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On X11, build on arm platforms.
|
||||
- On macOS, fixed `WindowBuilder::with_theme` not having any effect on the window.
|
||||
|
||||
## 0.30.2
|
||||
|
||||
### Fixed
|
||||
@@ -14,6 +64,7 @@
|
||||
|
||||
- Reexport `raw-window-handle` versions 0.4 and 0.5 as `raw_window_handle_04` and `raw_window_handle_05`.
|
||||
- Implement `ApplicationHandler` for `&mut` references and heap allocations to something that implements `ApplicationHandler`.
|
||||
- Add traits `EventLoopExtWayland` and `EventLoopExtX11`, providing methods `is_wayland` and `is_x11` on `EventLoop`.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -90,7 +141,7 @@
|
||||
`Fn`. The semantics are mostly the same, given that the capture list of the
|
||||
closure is your new `State`. Consider the following code:
|
||||
|
||||
```rust,no_run
|
||||
```rust,no_run,ignore
|
||||
use winit::event::Event;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::Window;
|
||||
@@ -126,7 +177,7 @@
|
||||
we move particular `match event` arms into methods on `ApplicationHandler`,
|
||||
for example:
|
||||
|
||||
```rust,no_run
|
||||
```rust,no_run,ignore
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
|
||||
use winit::event_loop::{EventLoop, ActiveEventLoop};
|
||||
@@ -189,7 +240,7 @@
|
||||
|
||||
Using the migration example from above, you can change your code as follows:
|
||||
|
||||
```rust,no_run
|
||||
```rust,no_run,ignore
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
|
||||
use winit::event_loop::{EventLoop, ActiveEventLoop};
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
- Added event `WindowEvent::HiDPIFactorChanged`.
|
||||
- Added method `MonitorId::get_hidpi_factor`.
|
||||
- Deprecated `get_inner_size_pixels` and `get_inner_size_points` methods of `Window` in favor of
|
||||
`get_inner_size`.
|
||||
`get_inner_size`.
|
||||
- **Breaking:** `EventsLoop` is `!Send` and `!Sync` because of platform-dependant constraints,
|
||||
but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`.
|
||||
- `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`.
|
||||
- Rewrite of the wayland backend to use wayland-client-0.11
|
||||
- Support for dead keys on wayland for keyboard utf8 input
|
||||
- Monitor enumeration on Windows is now implemented using `EnumDisplayMonitors` instead of
|
||||
`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`.
|
||||
`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`.
|
||||
- On Windows added `MonitorIdExt::hmonitor` method
|
||||
- Impl `Clone` for `EventsLoopProxy`
|
||||
- `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found
|
||||
- Support for touch event on wayland
|
||||
- `WindowEvent`s `MouseMoved`, `MouseEntered`, and `MouseLeft` have been renamed to
|
||||
`CursorMoved`, `CursorEntered`, and `CursorLeft`.
|
||||
`CursorMoved`, `CursorEntered`, and `CursorLeft`.
|
||||
- New `DeviceEvent`s added, `MouseMotion` and `MouseWheel`.
|
||||
- Send `CursorMoved` event after `CursorEntered` and `Focused` events.
|
||||
- Add support for `ModifiersState`, `MouseMove`, `MouseInput`, `MouseMotion` for emscripten backend.
|
||||
|
||||
@@ -50,7 +50,7 @@ impl From<CustomCursor> for Cursor {
|
||||
/// ```no_run
|
||||
/// # use winit::event_loop::ActiveEventLoop;
|
||||
/// # use winit::window::Window;
|
||||
/// # fn scope(event_loop: &ActiveEventLoop, window: &Window) {
|
||||
/// # fn scope(event_loop: &dyn ActiveEventLoop, window: &dyn Window) {
|
||||
/// use winit::window::CustomCursor;
|
||||
///
|
||||
/// let w = 10;
|
||||
@@ -62,13 +62,13 @@ impl From<CustomCursor> for Cursor {
|
||||
///
|
||||
/// #[cfg(target_family = "wasm")]
|
||||
/// let source = {
|
||||
/// use winit::platform::web::CustomCursorExtWebSys;
|
||||
/// use winit::platform::web::CustomCursorExtWeb;
|
||||
/// CustomCursor::from_url(String::from("http://localhost:3000/cursor.png"), 0, 0)
|
||||
/// };
|
||||
///
|
||||
/// let custom_cursor = event_loop.create_custom_cursor(source);
|
||||
///
|
||||
/// window.set_cursor(custom_cursor.clone());
|
||||
/// if let Ok(custom_cursor) = event_loop.create_custom_cursor(source) {
|
||||
/// window.set_cursor(custom_cursor.clone().into());
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
@@ -107,13 +107,16 @@ impl CustomCursor {
|
||||
/// Source for [`CustomCursor`].
|
||||
///
|
||||
/// See [`CustomCursor`] for more details.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||
pub struct CustomCursorSource {
|
||||
// Some platforms don't support custom cursors.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) inner: PlatformCustomCursorSource,
|
||||
}
|
||||
|
||||
/// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum BadImage {
|
||||
/// Produced when the image dimensions are larger than [`MAX_CURSOR_SIZE`]. This doesn't
|
||||
/// guarantee that the cursor will work, but should avoid many platform and device specific
|
||||
@@ -164,7 +167,7 @@ impl Error for BadImage {}
|
||||
/// Platforms export this directly as `PlatformCustomCursorSource` if they need to only work with
|
||||
/// images.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||
pub(crate) struct OnlyCursorImageSource(pub(crate) CursorImage);
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -199,7 +202,7 @@ impl PartialEq for OnlyCursorImage {
|
||||
|
||||
impl Eq for OnlyCursorImage {}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct CursorImage {
|
||||
pub(crate) rgba: Vec<u8>,
|
||||
|
||||
223
src/error.rs
223
src/error.rs
@@ -1,44 +1,40 @@
|
||||
use std::{error, fmt};
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
use crate::platform_impl;
|
||||
|
||||
// TODO: Rename
|
||||
/// An error that may be generated when requesting Winit state
|
||||
#[derive(Debug)]
|
||||
pub enum ExternalError {
|
||||
/// The operation is not supported by the backend.
|
||||
NotSupported(NotSupportedError),
|
||||
/// The operation was ignored.
|
||||
Ignored,
|
||||
/// The OS cannot perform the operation.
|
||||
Os(OsError),
|
||||
}
|
||||
|
||||
/// The error type for when the requested operation is not supported by the backend.
|
||||
#[derive(Clone)]
|
||||
pub struct NotSupportedError {
|
||||
_marker: (),
|
||||
}
|
||||
|
||||
/// The error type for when the OS cannot perform the requested operation.
|
||||
#[derive(Debug)]
|
||||
pub struct OsError {
|
||||
line: u32,
|
||||
file: &'static str,
|
||||
error: platform_impl::OsError,
|
||||
}
|
||||
|
||||
/// A general error that may occur while running the Winit event loop
|
||||
/// A general error that may occur while running or creating
|
||||
/// the event loop.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum EventLoopError {
|
||||
/// The operation is not supported by the backend.
|
||||
NotSupported(NotSupportedError),
|
||||
/// The OS cannot perform the operation.
|
||||
Os(OsError),
|
||||
/// The event loop can't be re-created.
|
||||
RecreationAttempt,
|
||||
/// Application has exit with an error status.
|
||||
ExitFailure(i32),
|
||||
/// Got unspecified OS-specific error during the request.
|
||||
Os(OsError),
|
||||
/// Creating the event loop with the requested configuration is not supported.
|
||||
NotSupported(NotSupportedError),
|
||||
}
|
||||
|
||||
impl fmt::Display for EventLoopError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
|
||||
Self::Os(err) => err.fmt(f),
|
||||
Self::ExitFailure(status) => write!(f, "Exit Failure: {status}"),
|
||||
Self::NotSupported(err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for EventLoopError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
if let Self::Os(err) = self {
|
||||
err.source()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OsError> for EventLoopError {
|
||||
@@ -47,18 +43,102 @@ impl From<OsError> for EventLoopError {
|
||||
}
|
||||
}
|
||||
|
||||
impl NotSupportedError {
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new() -> NotSupportedError {
|
||||
NotSupportedError { _marker: () }
|
||||
impl From<NotSupportedError> for EventLoopError {
|
||||
fn from(value: NotSupportedError) -> Self {
|
||||
Self::NotSupported(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// A general error that may occur during a request to the windowing system.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum RequestError {
|
||||
/// The request is not supported.
|
||||
NotSupported(NotSupportedError),
|
||||
/// The request was ignored by the operating system.
|
||||
Ignored,
|
||||
/// Got unspecified OS specific error during the request.
|
||||
Os(OsError),
|
||||
}
|
||||
|
||||
impl Display for RequestError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::NotSupported(err) => err.fmt(f),
|
||||
Self::Ignored => write!(f, "The request was ignored"),
|
||||
Self::Os(err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Error for RequestError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
if let Self::Os(err) = self {
|
||||
err.source()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NotSupportedError> for RequestError {
|
||||
fn from(value: NotSupportedError) -> Self {
|
||||
Self::NotSupported(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OsError> for RequestError {
|
||||
fn from(value: OsError) -> Self {
|
||||
Self::Os(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// The requested operation is not supported.
|
||||
#[derive(Debug)]
|
||||
pub struct NotSupportedError {
|
||||
/// The reason why a certain operation is not supported.
|
||||
reason: &'static str,
|
||||
}
|
||||
|
||||
impl NotSupportedError {
|
||||
pub(crate) fn new(reason: &'static str) -> Self {
|
||||
Self { reason }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NotSupportedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Operation is not supported: {}", self.reason)
|
||||
}
|
||||
}
|
||||
impl Error for NotSupportedError {}
|
||||
|
||||
/// Unclassified error from the OS.
|
||||
#[derive(Debug)]
|
||||
pub struct OsError {
|
||||
line: u32,
|
||||
file: &'static str,
|
||||
error: Box<dyn Error + Send + Sync + 'static>,
|
||||
}
|
||||
|
||||
impl OsError {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError {
|
||||
OsError { line, file, error }
|
||||
pub(crate) fn new(
|
||||
line: u32,
|
||||
file: &'static str,
|
||||
error: impl Into<Box<dyn Error + Send + Sync + 'static>>,
|
||||
) -> Self {
|
||||
Self { line, file, error: error.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for OsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad(&format!("os error at {}:{}: {}", self.file, self.line, self.error))
|
||||
}
|
||||
}
|
||||
impl Error for OsError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(self.error.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,64 +148,3 @@ macro_rules! os_error {
|
||||
crate::error::OsError::new(line!(), file!(), $error)
|
||||
}};
|
||||
}
|
||||
|
||||
impl fmt::Display for OsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
f.pad(&format!("os error at {}:{}: {}", self.file, self.line, self.error))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExternalError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
ExternalError::NotSupported(e) => e.fmt(f),
|
||||
ExternalError::Ignored => write!(f, "Operation was ignored"),
|
||||
ExternalError::Os(e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NotSupportedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
f.debug_struct("NotSupportedError").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NotSupportedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
f.pad("the requested operation is not supported by Winit")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EventLoopError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
|
||||
EventLoopError::NotSupported(e) => e.fmt(f),
|
||||
EventLoopError::Os(e) => e.fmt(f),
|
||||
EventLoopError::ExitFailure(status) => write!(f, "Exit Failure: {status}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for OsError {}
|
||||
impl error::Error for ExternalError {}
|
||||
impl error::Error for NotSupportedError {}
|
||||
impl error::Error for EventLoopError {}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::redundant_clone)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Eat attributes for testing
|
||||
#[test]
|
||||
fn ensure_fmt_does_not_panic() {
|
||||
let _ = format!("{:?}, {}", NotSupportedError::new(), NotSupportedError::new().clone());
|
||||
let _ = format!(
|
||||
"{:?}, {}",
|
||||
ExternalError::NotSupported(NotSupportedError::new()),
|
||||
ExternalError::NotSupported(NotSupportedError::new())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
226
src/event.rs
226
src/event.rs
@@ -1,4 +1,4 @@
|
||||
//! The [`Event`] enum and assorted supporting types.
|
||||
//! The event enums and assorted supporting types.
|
||||
//!
|
||||
//! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get
|
||||
//! processed and used to modify the program state. For more details, see the root-level
|
||||
@@ -46,7 +46,7 @@ use smol_str::SmolStr;
|
||||
use web_time::Instant;
|
||||
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::error::ExternalError;
|
||||
use crate::error::RequestError;
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState};
|
||||
use crate::platform_impl;
|
||||
@@ -54,11 +54,15 @@ use crate::platform_impl;
|
||||
use crate::window::Window;
|
||||
use crate::window::{ActivationToken, Theme, WindowId};
|
||||
|
||||
// TODO: Remove once the backends can call `ApplicationHandler` methods directly. For now backends
|
||||
// like Windows and Web require `Event` to wire user events, otherwise each backend will have to
|
||||
// wrap `Event` in some other structure.
|
||||
/// Describes a generic event.
|
||||
///
|
||||
/// See the module-level docs for more information on the event loop manages each event.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Event<T: 'static> {
|
||||
pub(crate) enum Event {
|
||||
/// See [`ApplicationHandler::new_events`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events
|
||||
@@ -67,23 +71,25 @@ pub enum Event<T: 'static> {
|
||||
/// See [`ApplicationHandler::window_event`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
WindowEvent { window_id: WindowId, event: WindowEvent },
|
||||
|
||||
/// See [`ApplicationHandler::device_event`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
DeviceEvent { device_id: DeviceId, event: DeviceEvent },
|
||||
|
||||
/// See [`ApplicationHandler::user_event`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::user_event`]: crate::application::ApplicationHandler::user_event
|
||||
UserEvent(T),
|
||||
|
||||
/// See [`ApplicationHandler::suspended`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended
|
||||
Suspended,
|
||||
|
||||
/// See [`ApplicationHandler::can_create_surfaces`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::can_create_surfaces`]: crate::application::ApplicationHandler::can_create_surfaces
|
||||
CreateSurfaces,
|
||||
|
||||
/// See [`ApplicationHandler::resumed`] for details.
|
||||
///
|
||||
/// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
|
||||
@@ -103,28 +109,13 @@ pub enum Event<T: 'static> {
|
||||
///
|
||||
/// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning
|
||||
MemoryWarning,
|
||||
}
|
||||
|
||||
impl<T> Event<T> {
|
||||
#[allow(clippy::result_large_err)]
|
||||
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
|
||||
use self::Event::*;
|
||||
match self {
|
||||
UserEvent(_) => Err(self),
|
||||
WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }),
|
||||
DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }),
|
||||
NewEvents(cause) => Ok(NewEvents(cause)),
|
||||
AboutToWait => Ok(AboutToWait),
|
||||
LoopExiting => Ok(LoopExiting),
|
||||
Suspended => Ok(Suspended),
|
||||
Resumed => Ok(Resumed),
|
||||
MemoryWarning => Ok(MemoryWarning),
|
||||
}
|
||||
}
|
||||
/// User requested a wake up.
|
||||
UserWakeUp,
|
||||
}
|
||||
|
||||
/// Describes the reason the event loop is resuming.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum StartCause {
|
||||
/// Sent if the time specified by [`ControlFlow::WaitUntil`] has been reached. Contains the
|
||||
/// moment the timeout was requested and the requested resume time. The actual resume time is
|
||||
@@ -157,8 +148,13 @@ pub enum WindowEvent {
|
||||
/// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token
|
||||
ActivationTokenDone { serial: AsyncRequestSerial, token: ActivationToken },
|
||||
|
||||
/// The size of the window has changed. Contains the client area's new dimensions.
|
||||
Resized(PhysicalSize<u32>),
|
||||
/// The size of the window's surface has changed.
|
||||
///
|
||||
/// Contains the new dimensions of the surface (can also be retrieved with
|
||||
/// [`Window::surface_size`]).
|
||||
///
|
||||
/// [`Window::surface_size`]: crate::window::Window::surface_size
|
||||
SurfaceResized(PhysicalSize<u32>),
|
||||
|
||||
/// The position of the window has changed. Contains the window's new position.
|
||||
///
|
||||
@@ -349,9 +345,6 @@ pub enum WindowEvent {
|
||||
/// touchpad is being pressed) and stage (integer representing the click level).
|
||||
TouchpadPressure { device_id: DeviceId, pressure: f32, stage: i64 },
|
||||
|
||||
/// Motion on some analog axis. May report data redundant to other, more specific events.
|
||||
AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 },
|
||||
|
||||
/// Touch event has been received
|
||||
///
|
||||
/// ## Platform-specific
|
||||
@@ -372,16 +365,16 @@ pub enum WindowEvent {
|
||||
/// * Changing the display's scale factor (e.g. in Control Panel on Windows).
|
||||
/// * Moving the window to a display with a different scale factor.
|
||||
///
|
||||
/// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the
|
||||
/// To update the window size, use the provided [`SurfaceSizeWriter`] handle. By default, the
|
||||
/// window is resized to the value suggested by the OS, but it can be changed to any value.
|
||||
///
|
||||
/// For more information about DPI in general, see the [`dpi`] crate.
|
||||
ScaleFactorChanged {
|
||||
scale_factor: f64,
|
||||
/// Handle to update inner size during scale changes.
|
||||
/// Handle to update surface size during scale changes.
|
||||
///
|
||||
/// See [`InnerSizeWriter`] docs for more details.
|
||||
inner_size_writer: InnerSizeWriter,
|
||||
/// See [`SurfaceSizeWriter`] docs for more details.
|
||||
surface_size_writer: SurfaceSizeWriter,
|
||||
},
|
||||
|
||||
/// The system window theme has changed.
|
||||
@@ -389,6 +382,8 @@ pub enum WindowEvent {
|
||||
/// Applications might wish to react to this to change the theme of the content of the window
|
||||
/// when the system changes the window theme.
|
||||
///
|
||||
/// This only reports a change if the window theme was not overridden by [`Window::set_theme`].
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported.
|
||||
@@ -444,19 +439,42 @@ pub enum WindowEvent {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(pub(crate) platform_impl::DeviceId);
|
||||
|
||||
impl Default for DeviceId {
|
||||
fn default() -> Self {
|
||||
Self::dummy()
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceId {
|
||||
/// Returns a dummy id, useful for unit testing.
|
||||
///
|
||||
/// # Safety
|
||||
/// # Notes
|
||||
///
|
||||
/// The only guarantee made about the return value of this function is that
|
||||
/// it will always be equal to itself and to future values returned by this function.
|
||||
/// No other guarantees are made. This may be equal to a real `DeviceId`.
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId(platform_impl::DeviceId::dummy())
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier of a finger in a touch event.
|
||||
///
|
||||
/// Whenever a touch event is received it contains a `FingerId` which uniquely identifies the finger
|
||||
/// used for the current interaction.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId(pub(crate) platform_impl::FingerId);
|
||||
|
||||
impl FingerId {
|
||||
/// Returns a dummy id, useful for unit testing.
|
||||
///
|
||||
/// **Passing this into a winit function will result in undefined behavior.**
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
#[allow(unused_unsafe)]
|
||||
DeviceId(unsafe { platform_impl::DeviceId::dummy() })
|
||||
/// # Notes
|
||||
///
|
||||
/// The only guarantee made about the return value of this function is that
|
||||
/// it will always be equal to itself and to future values returned by this function.
|
||||
/// No other guarantees are made. This may be equal to a real `FingerId`.
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId(platform_impl::FingerId::dummy())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,15 +486,28 @@ impl DeviceId {
|
||||
/// (corresponding to GUI cursors and keyboard focus) the device IDs may not match.
|
||||
///
|
||||
/// Note that these events are delivered regardless of input focus.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum DeviceEvent {
|
||||
Added,
|
||||
Removed,
|
||||
|
||||
/// Change in physical position of a pointing device.
|
||||
///
|
||||
/// This represents raw, unfiltered physical motion. Not to be confused with
|
||||
/// [`WindowEvent::CursorMoved`].
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **Web:** Only returns raw data, not OS accelerated, if [`CursorGrabMode::Locked`] is used
|
||||
/// and browser support is available, see
|
||||
#[cfg_attr(
|
||||
any(web_platform, docsrs),
|
||||
doc = "[`ActiveEventLoopExtWeb::is_cursor_lock_raw()`][crate::platform::web::ActiveEventLoopExtWeb::is_cursor_lock_raw()]."
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(any(web_platform, docsrs)),
|
||||
doc = "`ActiveEventLoopExtWeb::is_cursor_lock_raw()`."
|
||||
)]
|
||||
///
|
||||
#[rustfmt::skip]
|
||||
/// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked
|
||||
MouseMotion {
|
||||
/// (x, y) change in position in unspecified units.
|
||||
///
|
||||
@@ -489,14 +520,6 @@ pub enum DeviceEvent {
|
||||
delta: MouseScrollDelta,
|
||||
},
|
||||
|
||||
/// Motion on some analog axis. This event will be reported for all arbitrary input devices
|
||||
/// that winit supports on this platform, including mouse devices. If the device is a mouse
|
||||
/// device then this will be reported alongside the MouseMotion event.
|
||||
Motion {
|
||||
axis: AxisId,
|
||||
value: f64,
|
||||
},
|
||||
|
||||
Button {
|
||||
button: ButtonId,
|
||||
state: ElementState,
|
||||
@@ -512,7 +535,7 @@ pub enum DeviceEvent {
|
||||
/// repeat or the initial keypress. An application may emulate this by, for
|
||||
/// example keeping a Map/Set of pressed keys and determining whether a keypress
|
||||
/// corresponds to an already pressed key.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct RawKeyEvent {
|
||||
pub physical_key: keyboard::PhysicalKey,
|
||||
@@ -533,11 +556,11 @@ pub struct KeyEvent {
|
||||
/// ## Caveats
|
||||
///
|
||||
/// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that
|
||||
/// implements DVORAK in hardware (or firmware)
|
||||
/// implements DVORAK in hardware (or firmware)
|
||||
/// - Your application will likely have to handle keyboards which are missing keys that your
|
||||
/// own keyboard has.
|
||||
/// own keyboard has.
|
||||
/// - Certain `KeyCode`s will move between a couple of different positions depending on what
|
||||
/// layout the keyboard was manufactured to support.
|
||||
/// layout the keyboard was manufactured to support.
|
||||
///
|
||||
/// **Because of these caveats, it is important that you provide users with a way to configure
|
||||
/// most (if not all) keybinds in your application.**
|
||||
@@ -559,8 +582,7 @@ pub struct KeyEvent {
|
||||
///
|
||||
/// This has two use cases:
|
||||
/// - Allows querying whether the current input is a Dead key.
|
||||
/// - Allows handling key-bindings on platforms which don't
|
||||
/// support [`key_without_modifiers`].
|
||||
/// - Allows handling key-bindings on platforms which don't support [`key_without_modifiers`].
|
||||
///
|
||||
/// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard
|
||||
/// shortcuts, **it is important that you provide users with a way to configure your
|
||||
@@ -568,8 +590,8 @@ pub struct KeyEvent {
|
||||
/// incompatible keyboard layout.**
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Web:** Dead keys might be reported as the real key instead
|
||||
/// of `Dead` depending on the browser/OS.
|
||||
/// - **Web:** Dead keys might be reported as the real key instead of `Dead` depending on the
|
||||
/// browser/OS.
|
||||
///
|
||||
/// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
|
||||
pub logical_key: keyboard::Key,
|
||||
@@ -626,7 +648,7 @@ pub struct KeyEvent {
|
||||
/// In games, you often want to ignore repated key events - this can be
|
||||
/// done by ignoring events where this property is set.
|
||||
///
|
||||
/// ```
|
||||
/// ```no_run
|
||||
/// use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
/// use winit::keyboard::{KeyCode, PhysicalKey};
|
||||
/// # let window_event = WindowEvent::RedrawRequested; // To make the example compile
|
||||
@@ -658,7 +680,8 @@ pub struct KeyEvent {
|
||||
}
|
||||
|
||||
/// Describes keyboard modifiers event.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Modifiers {
|
||||
pub(crate) state: ModifiersState,
|
||||
|
||||
@@ -851,15 +874,16 @@ pub struct Touch {
|
||||
///
|
||||
/// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**.
|
||||
/// - **Android**: This will never be [None]. If the device doesn't support pressure
|
||||
/// sensitivity, force will either be 0.0 or 1.0. Also see the
|
||||
/// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE).
|
||||
/// sensitivity, force will either be 0.0 or 1.0. Also see the
|
||||
/// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE).
|
||||
pub force: Option<Force>,
|
||||
/// Unique identifier of a finger.
|
||||
pub id: u64,
|
||||
pub finger_id: FingerId,
|
||||
}
|
||||
|
||||
/// Describes the force of a touch event
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Force {
|
||||
/// On iOS, the force is calibrated so that the same number corresponds to
|
||||
/// roughly the same amount of pressure on the screen regardless of the
|
||||
@@ -976,50 +1000,53 @@ pub enum MouseScrollDelta {
|
||||
PixelDelta(PhysicalPosition<f64>),
|
||||
}
|
||||
|
||||
/// Handle to synchronously change the size of the window from the
|
||||
/// [`WindowEvent`].
|
||||
/// Handle to synchronously change the size of the window from the [`WindowEvent`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InnerSizeWriter {
|
||||
pub(crate) new_inner_size: Weak<Mutex<PhysicalSize<u32>>>,
|
||||
pub struct SurfaceSizeWriter {
|
||||
pub(crate) new_surface_size: Weak<Mutex<PhysicalSize<u32>>>,
|
||||
}
|
||||
|
||||
impl InnerSizeWriter {
|
||||
impl SurfaceSizeWriter {
|
||||
#[cfg(not(orbital_platform))]
|
||||
pub(crate) fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
|
||||
Self { new_inner_size }
|
||||
pub(crate) fn new(new_surface_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
|
||||
Self { new_surface_size }
|
||||
}
|
||||
|
||||
/// Try to request inner size which will be set synchronously on the window.
|
||||
pub fn request_inner_size(
|
||||
/// Try to request surface size which will be set synchronously on the window.
|
||||
pub fn request_surface_size(
|
||||
&mut self,
|
||||
new_inner_size: PhysicalSize<u32>,
|
||||
) -> Result<(), ExternalError> {
|
||||
if let Some(inner) = self.new_inner_size.upgrade() {
|
||||
*inner.lock().unwrap() = new_inner_size;
|
||||
new_surface_size: PhysicalSize<u32>,
|
||||
) -> Result<(), RequestError> {
|
||||
if let Some(inner) = self.new_surface_size.upgrade() {
|
||||
*inner.lock().unwrap() = new_surface_size;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ExternalError::Ignored)
|
||||
Err(RequestError::Ignored)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InnerSizeWriter {
|
||||
impl PartialEq for SurfaceSizeWriter {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.new_inner_size.as_ptr() == other.new_inner_size.as_ptr()
|
||||
self.new_surface_size.as_ptr() == other.new_surface_size.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SurfaceSizeWriter {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
|
||||
use crate::dpi::PhysicalPosition;
|
||||
use crate::event;
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
|
||||
macro_rules! foreach_event {
|
||||
($closure:expr) => {{
|
||||
#[allow(unused_mut)]
|
||||
let mut x = $closure;
|
||||
let did = unsafe { event::DeviceId::dummy() };
|
||||
let did = event::DeviceId::dummy();
|
||||
let fid = event::FingerId::dummy();
|
||||
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
@@ -1029,8 +1056,7 @@ mod tests {
|
||||
use crate::window::WindowId;
|
||||
|
||||
// Mainline events.
|
||||
let wid = unsafe { WindowId::dummy() };
|
||||
x(UserEvent(()));
|
||||
let wid = WindowId::dummy();
|
||||
x(NewEvents(event::StartCause::Init));
|
||||
x(AboutToWait);
|
||||
x(LoopExiting);
|
||||
@@ -1044,7 +1070,7 @@ mod tests {
|
||||
with_window_event(Destroyed);
|
||||
with_window_event(Focused(true));
|
||||
with_window_event(Moved((0, 0).into()));
|
||||
with_window_event(Resized((0, 0).into()));
|
||||
with_window_event(SurfaceResized((0, 0).into()));
|
||||
with_window_event(DroppedFile("x.txt".into()));
|
||||
with_window_event(HoveredFile("x.txt".into()));
|
||||
with_window_event(HoveredFileCancelled);
|
||||
@@ -1080,12 +1106,11 @@ mod tests {
|
||||
phase: event::TouchPhase::Started,
|
||||
});
|
||||
with_window_event(TouchpadPressure { device_id: did, pressure: 0.0, stage: 0 });
|
||||
with_window_event(AxisMotion { device_id: did, axis: 0, value: 0.0 });
|
||||
with_window_event(Touch(event::Touch {
|
||||
device_id: did,
|
||||
phase: event::TouchPhase::Started,
|
||||
location: (0.0, 0.0).into(),
|
||||
id: 0,
|
||||
finger_id: fid,
|
||||
force: Some(event::Force::Normalized(0.0)),
|
||||
}));
|
||||
with_window_event(ThemeChanged(crate::window::Theme::Light));
|
||||
@@ -1099,13 +1124,10 @@ mod tests {
|
||||
let with_device_event =
|
||||
|dev_ev| x(event::Event::DeviceEvent { device_id: did, event: dev_ev });
|
||||
|
||||
with_device_event(Added);
|
||||
with_device_event(Removed);
|
||||
with_device_event(MouseMotion { delta: (0.0, 0.0).into() });
|
||||
with_device_event(MouseWheel {
|
||||
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
|
||||
});
|
||||
with_device_event(Motion { axis: 0, value: 0.0 });
|
||||
with_device_event(Button { button: 0, state: event::ElementState::Pressed });
|
||||
}
|
||||
}};
|
||||
@@ -1114,25 +1136,12 @@ mod tests {
|
||||
#[allow(clippy::redundant_clone)]
|
||||
#[test]
|
||||
fn test_event_clone() {
|
||||
foreach_event!(|event: event::Event<()>| {
|
||||
foreach_event!(|event: event::Event| {
|
||||
let event2 = event.clone();
|
||||
assert_eq!(event, event2);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_nonuser_event() {
|
||||
foreach_event!(|event: event::Event<()>| {
|
||||
let is_user = matches!(event, event::Event::UserEvent(()));
|
||||
let event2 = event.map_nonuser_event::<()>();
|
||||
if is_user {
|
||||
assert_eq!(event2, Err(event::Event::UserEvent(())));
|
||||
} else {
|
||||
assert!(event2.is_ok());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_force_normalize() {
|
||||
let force = event::Force::Normalized(0.0);
|
||||
@@ -1153,12 +1162,13 @@ mod tests {
|
||||
#[allow(clippy::clone_on_copy)]
|
||||
#[test]
|
||||
fn ensure_attrs_do_not_panic() {
|
||||
foreach_event!(|event: event::Event<()>| {
|
||||
foreach_event!(|event: event::Event| {
|
||||
let _ = format!("{:?}", event);
|
||||
});
|
||||
let _ = event::StartCause::Init.clone();
|
||||
|
||||
let did = unsafe { crate::event::DeviceId::dummy() }.clone();
|
||||
let did = crate::event::DeviceId::dummy().clone();
|
||||
let fid = crate::event::FingerId::dummy().clone();
|
||||
HashSet::new().insert(did);
|
||||
let mut set = [did, did, did];
|
||||
set.sort_unstable();
|
||||
@@ -1174,7 +1184,7 @@ mod tests {
|
||||
device_id: did,
|
||||
phase: event::TouchPhase::Started,
|
||||
location: (0.0, 0.0).into(),
|
||||
id: 0,
|
||||
finger_id: fid,
|
||||
force: Some(event::Force::Normalized(0.0)),
|
||||
}
|
||||
.clone();
|
||||
|
||||
@@ -3,27 +3,28 @@
|
||||
//!
|
||||
//! If you want to send custom events to the event loop, use
|
||||
//! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its
|
||||
//! [`send_event`][EventLoopProxy::send_event] method.
|
||||
//! [`wake_up`][EventLoopProxy::wake_up] method. Then during handling the wake up
|
||||
//! you can poll your event sources.
|
||||
//!
|
||||
//! See the root-level documentation for information on how to create and use an event loop to
|
||||
//! handle events.
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::{error, fmt};
|
||||
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(web_platform)]
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, OsError};
|
||||
use crate::event::Event;
|
||||
use crate::error::{EventLoopError, RequestError};
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::platform_impl;
|
||||
use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
|
||||
use crate::utils::AsAny;
|
||||
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes};
|
||||
|
||||
/// Provides a way to retrieve events from the system and from the windows that were registered to
|
||||
/// the events loop.
|
||||
@@ -40,17 +41,8 @@ use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
|
||||
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub struct EventLoop<T: 'static> {
|
||||
pub(crate) event_loop: platform_impl::EventLoop<T>,
|
||||
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
|
||||
}
|
||||
|
||||
/// Target that associates windows with an [`EventLoop`].
|
||||
///
|
||||
/// This type exists to allow you to create new windows while Winit executes
|
||||
/// your callback.
|
||||
pub struct ActiveEventLoop {
|
||||
pub(crate) p: platform_impl::ActiveEventLoop,
|
||||
pub struct EventLoop {
|
||||
pub(crate) event_loop: platform_impl::EventLoop,
|
||||
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
|
||||
}
|
||||
|
||||
@@ -59,25 +51,15 @@ pub struct ActiveEventLoop {
|
||||
/// This is used to make specifying options that affect the whole application
|
||||
/// easier. But note that constructing multiple event loops is not supported.
|
||||
///
|
||||
/// This can be created using [`EventLoop::new`] or [`EventLoop::with_user_event`].
|
||||
#[derive(Default)]
|
||||
pub struct EventLoopBuilder<T: 'static> {
|
||||
/// This can be created using [`EventLoop::builder`].
|
||||
#[derive(Default, PartialEq, Eq, Hash)]
|
||||
pub struct EventLoopBuilder {
|
||||
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
|
||||
_p: PhantomData<T>,
|
||||
}
|
||||
|
||||
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
impl EventLoopBuilder<()> {
|
||||
/// Start building a new event loop.
|
||||
#[inline]
|
||||
#[deprecated = "use `EventLoop::builder` instead"]
|
||||
pub fn new() -> Self {
|
||||
EventLoop::builder()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilder<T> {
|
||||
impl EventLoopBuilder {
|
||||
/// Builds a new event loop.
|
||||
///
|
||||
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
|
||||
@@ -112,7 +94,7 @@ impl<T> EventLoopBuilder<T> {
|
||||
doc = "[`.with_android_app(app)`]: #only-available-on-android"
|
||||
)]
|
||||
#[inline]
|
||||
pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> {
|
||||
pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
|
||||
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
|
||||
|
||||
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
|
||||
@@ -133,26 +115,27 @@ impl<T> EventLoopBuilder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for EventLoop<T> {
|
||||
impl fmt::Debug for EventLoopBuilder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad("EventLoop { .. }")
|
||||
f.debug_struct("EventLoopBuilder").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ActiveEventLoop {
|
||||
impl fmt::Debug for EventLoop {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad("ActiveEventLoop { .. }")
|
||||
f.debug_struct("EventLoop").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// Set through [`ActiveEventLoop::set_control_flow()`].
|
||||
///
|
||||
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
|
||||
/// Indicates the desired behavior of the event loop after [`about_to_wait`] is called.
|
||||
///
|
||||
/// Defaults to [`Wait`].
|
||||
///
|
||||
/// [`Wait`]: Self::Wait
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
/// [`about_to_wait`]: crate::application::ApplicationHandler::about_to_wait
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||
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.
|
||||
@@ -165,11 +148,12 @@ pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, suspend the thread until either another event
|
||||
/// arrives or the given time is reached.
|
||||
///
|
||||
/// Useful for implementing efficient timers. Applications which want to render at the
|
||||
/// display's native refresh rate should instead use [`Poll`] and the VSync functionality
|
||||
/// of a graphics API to reduce odds of missed frames.
|
||||
/// Useful for implementing efficient timers, but should not be relied on for real-time needs
|
||||
/// like rendering (VSync) or audio. Applications which want to render at the display's native
|
||||
/// refresh rate should instead issue a [`Window::request_redraw`] at the end of
|
||||
/// [`WindowEvent::RedrawRequested`].
|
||||
///
|
||||
/// [`Poll`]: Self::Poll
|
||||
/// [`WindowEvent::RedrawRequested`]: crate::event::WindowEvent::RedrawRequested
|
||||
WaitUntil(Instant),
|
||||
}
|
||||
|
||||
@@ -189,12 +173,12 @@ impl ControlFlow {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop<()> {
|
||||
impl EventLoop {
|
||||
/// Create the event loop.
|
||||
///
|
||||
/// This is an alias of `EventLoop::builder().build()`.
|
||||
#[inline]
|
||||
pub fn new() -> Result<EventLoop<()>, EventLoopError> {
|
||||
pub fn new() -> Result<EventLoop, EventLoopError> {
|
||||
Self::builder().build()
|
||||
}
|
||||
|
||||
@@ -204,33 +188,12 @@ impl EventLoop<()> {
|
||||
///
|
||||
/// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that.
|
||||
#[inline]
|
||||
pub fn builder() -> EventLoopBuilder<()> {
|
||||
Self::with_user_event()
|
||||
pub fn builder() -> EventLoopBuilder {
|
||||
EventLoopBuilder { platform_specific: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
/// Start building a new event loop, with the given type as the user event
|
||||
/// type.
|
||||
pub fn with_user_event() -> EventLoopBuilder<T> {
|
||||
EventLoopBuilder { platform_specific: Default::default(), _p: PhantomData }
|
||||
}
|
||||
|
||||
/// See [`run_app`].
|
||||
///
|
||||
/// [`run_app`]: Self::run_app
|
||||
#[inline]
|
||||
#[deprecated = "use `EventLoop::run_app` instead"]
|
||||
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
|
||||
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &ActiveEventLoop),
|
||||
{
|
||||
let _span = tracing::debug_span!("winit::EventLoop::run").entered();
|
||||
|
||||
self.event_loop.run(event_handler)
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
/// Run the application with the event loop on the calling thread.
|
||||
///
|
||||
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
|
||||
@@ -245,37 +208,38 @@ impl<T> EventLoop<T> {
|
||||
///
|
||||
/// Web applications are recommended to use
|
||||
#[cfg_attr(
|
||||
web_platform,
|
||||
doc = "[`EventLoopExtWebSys::spawn_app()`][crate::platform::web::EventLoopExtWebSys::spawn_app()]"
|
||||
any(web_platform, docsrs),
|
||||
doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
|
||||
)]
|
||||
#[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")]
|
||||
/// [^1] instead of [`run_app()`] to avoid the need
|
||||
/// for the Javascript exception trick, and to make it clearer that the event loop runs
|
||||
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the
|
||||
/// current thread of execution like it does on other platforms.
|
||||
#[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")]
|
||||
/// [^1] instead of [`run_app()`] to avoid the need for the Javascript exception trick, and to
|
||||
/// make it clearer that the event loop runs asynchronously (via the browser's own,
|
||||
/// internal, event loop) and doesn't block the current thread of execution like it does
|
||||
/// on other platforms.
|
||||
///
|
||||
/// This function won't be available with `target_feature = "exception-handling"`.
|
||||
///
|
||||
/// [^1]: `spawn_app()` is only available on the Web platform.
|
||||
///
|
||||
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
|
||||
/// [`run_app()`]: Self::run_app()
|
||||
/// [^1]: `EventLoopExtWebSys::spawn_app()` is only available on Web.
|
||||
#[inline]
|
||||
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
|
||||
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.event_loop.run(|event, event_loop| dispatch_event_for_app(app, event_loop, event))
|
||||
pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
|
||||
self.event_loop.run_app(app)
|
||||
}
|
||||
|
||||
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
|
||||
/// to the main event loop, possibly from another thread.
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy { event_loop_proxy: self.event_loop.create_proxy() }
|
||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
||||
self.event_loop.window_target().create_proxy()
|
||||
}
|
||||
|
||||
/// Gets a persistent reference to the underlying platform display.
|
||||
///
|
||||
/// See the [`OwnedDisplayHandle`] type for more information.
|
||||
pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
|
||||
OwnedDisplayHandle { platform: self.event_loop.window_target().p.owned_display_handle() }
|
||||
self.event_loop.window_target().owned_display_handle()
|
||||
}
|
||||
|
||||
/// Change if or when [`DeviceEvent`]s are captured.
|
||||
@@ -289,56 +253,36 @@ impl<T> EventLoop<T> {
|
||||
allowed = ?allowed
|
||||
)
|
||||
.entered();
|
||||
|
||||
self.event_loop.window_target().p.listen_device_events(allowed);
|
||||
self.event_loop.window_target().listen_device_events(allowed)
|
||||
}
|
||||
|
||||
/// Sets the [`ControlFlow`].
|
||||
pub fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.event_loop.window_target().p.set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
/// Create a window.
|
||||
///
|
||||
/// Creating window without event loop running often leads to improper window creation;
|
||||
/// use [`ActiveEventLoop::create_window`] instead.
|
||||
#[deprecated = "use `ActiveEventLoop::create_window` instead"]
|
||||
#[inline]
|
||||
pub fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError> {
|
||||
let _span = tracing::debug_span!(
|
||||
"winit::EventLoop::create_window",
|
||||
window_attributes = ?window_attributes
|
||||
)
|
||||
.entered();
|
||||
|
||||
let window =
|
||||
platform_impl::Window::new(&self.event_loop.window_target().p, window_attributes)?;
|
||||
Ok(Window { window })
|
||||
self.event_loop.window_target().set_control_flow(control_flow);
|
||||
}
|
||||
|
||||
/// Create custom cursor.
|
||||
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
|
||||
self.event_loop.window_target().p.create_custom_cursor(custom_cursor)
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **iOS / Android / Orbital:** Unsupported.
|
||||
pub fn create_custom_cursor(
|
||||
&self,
|
||||
custom_cursor: CustomCursorSource,
|
||||
) -> Result<CustomCursor, RequestError> {
|
||||
self.event_loop.window_target().create_custom_cursor(custom_cursor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl<T> rwh_06::HasDisplayHandle for EventLoop<T> {
|
||||
impl rwh_06::HasDisplayHandle for EventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
|
||||
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::HasRawDisplayHandle::raw_display_handle(self.event_loop.window_target())
|
||||
rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
impl AsFd for EventLoop {
|
||||
/// Get the underlying [EventLoop]'s `fd` which you can register
|
||||
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
|
||||
/// loop must be polled with the [`pump_app_events`] API.
|
||||
@@ -352,7 +296,7 @@ impl<T> AsFd for EventLoop<T> {
|
||||
}
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
impl AsRawFd for EventLoop {
|
||||
/// Get the underlying [EventLoop]'s raw `fd` which you can register
|
||||
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
|
||||
/// loop must be polled with the [`pump_app_events`] API.
|
||||
@@ -365,42 +309,45 @@ impl<T> AsRawFd for EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
pub trait ActiveEventLoop: AsAny {
|
||||
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
|
||||
/// to the main event loop, possibly from another thread.
|
||||
fn create_proxy(&self) -> EventLoopProxy;
|
||||
|
||||
/// Create the window.
|
||||
///
|
||||
/// Possible causes of error include denied permission, incompatible system, and lack of memory.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** The window is created but not inserted into the web page automatically. Please
|
||||
/// see the web platform module for more information.
|
||||
#[inline]
|
||||
pub fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError> {
|
||||
let _span = tracing::debug_span!(
|
||||
"winit::ActiveEventLoop::create_window",
|
||||
window_attributes = ?window_attributes
|
||||
)
|
||||
.entered();
|
||||
|
||||
let window = platform_impl::Window::new(&self.p, window_attributes)?;
|
||||
Ok(Window { window })
|
||||
}
|
||||
/// - **Web:** The window is created but not inserted into the Web page automatically. Please
|
||||
/// see the Web platform module for more information.
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: WindowAttributes,
|
||||
) -> Result<Box<dyn Window>, RequestError>;
|
||||
|
||||
/// Create custom cursor.
|
||||
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
|
||||
let _span = tracing::debug_span!("winit::ActiveEventLoop::create_custom_cursor",).entered();
|
||||
|
||||
self.p.create_custom_cursor(custom_cursor)
|
||||
}
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **iOS / Android / Orbital:** Unsupported.
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
custom_cursor: CustomCursorSource,
|
||||
) -> Result<CustomCursor, RequestError>;
|
||||
|
||||
/// Returns the list of all the monitors available on the system.
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
|
||||
let _span = tracing::debug_span!("winit::ActiveEventLoop::available_monitors",).entered();
|
||||
|
||||
#[allow(clippy::useless_conversion)] // false positive on some platforms
|
||||
self.p.available_monitors().into_iter().map(|inner| MonitorHandle { inner })
|
||||
}
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **Web:** Only returns the current monitor without
|
||||
#[cfg_attr(
|
||||
any(web_platform, docsrs),
|
||||
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
|
||||
)]
|
||||
#[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = MonitorHandle>>;
|
||||
|
||||
/// Returns the primary monitor of the system.
|
||||
///
|
||||
@@ -408,13 +355,14 @@ impl ActiveEventLoop {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **Wayland / Web:** Always returns `None`.
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
let _span = tracing::debug_span!("winit::ActiveEventLoop::primary_monitor",).entered();
|
||||
|
||||
self.p.primary_monitor().map(|inner| MonitorHandle { inner })
|
||||
}
|
||||
/// - **Wayland:** Always returns `None`.
|
||||
/// - **Web:** Always returns `None` without
|
||||
#[cfg_attr(
|
||||
any(web_platform, docsrs),
|
||||
doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
|
||||
)]
|
||||
#[cfg_attr(not(any(web_platform, docsrs)), doc = " detailed monitor permissions.")]
|
||||
fn primary_monitor(&self) -> Option<MonitorHandle>;
|
||||
|
||||
/// Change if or when [`DeviceEvent`]s are captured.
|
||||
///
|
||||
@@ -427,64 +375,47 @@ impl ActiveEventLoop {
|
||||
/// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
|
||||
///
|
||||
/// [`DeviceEvent`]: crate::event::DeviceEvent
|
||||
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
||||
let _span = tracing::debug_span!(
|
||||
"winit::ActiveEventLoop::listen_device_events",
|
||||
allowed = ?allowed
|
||||
)
|
||||
.entered();
|
||||
fn listen_device_events(&self, allowed: DeviceEvents);
|
||||
|
||||
self.p.listen_device_events(allowed);
|
||||
}
|
||||
/// Returns the current system theme.
|
||||
///
|
||||
/// Returns `None` if it cannot be determined on the current platform.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
|
||||
fn system_theme(&self) -> Option<Theme>;
|
||||
|
||||
/// Sets the [`ControlFlow`].
|
||||
pub fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.p.set_control_flow(control_flow)
|
||||
}
|
||||
fn set_control_flow(&self, control_flow: ControlFlow);
|
||||
|
||||
/// Gets the current [`ControlFlow`].
|
||||
pub fn control_flow(&self) -> ControlFlow {
|
||||
self.p.control_flow()
|
||||
}
|
||||
fn control_flow(&self) -> ControlFlow;
|
||||
|
||||
/// This exits the event loop.
|
||||
///
|
||||
/// See [`LoopExiting`][Event::LoopExiting].
|
||||
pub fn exit(&self) {
|
||||
let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered();
|
||||
|
||||
self.p.exit()
|
||||
}
|
||||
/// See [`exiting`][crate::application::ApplicationHandler::exiting].
|
||||
fn exit(&self);
|
||||
|
||||
/// Returns if the [`EventLoop`] is about to stop.
|
||||
///
|
||||
/// See [`exit()`][Self::exit].
|
||||
pub fn exiting(&self) -> bool {
|
||||
self.p.exiting()
|
||||
}
|
||||
fn exiting(&self) -> bool;
|
||||
|
||||
/// Gets a persistent reference to the underlying platform display.
|
||||
///
|
||||
/// See the [`OwnedDisplayHandle`] type for more information.
|
||||
pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
|
||||
OwnedDisplayHandle { platform: self.p.owned_display_handle() }
|
||||
}
|
||||
fn owned_display_handle(&self) -> OwnedDisplayHandle;
|
||||
|
||||
/// Get the raw-window-handle handle.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
|
||||
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 rwh_05::HasRawDisplayHandle for ActiveEventLoop {
|
||||
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
self.p.raw_display_handle_rwh_05()
|
||||
self.rwh_06_handle().display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,10 +431,10 @@ unsafe impl rwh_05::HasRawDisplayHandle for ActiveEventLoop {
|
||||
///
|
||||
/// - A zero-sized type that is likely optimized out.
|
||||
/// - A reference-counted pointer to the underlying type.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct OwnedDisplayHandle {
|
||||
#[cfg_attr(not(any(feature = "rwh_05", feature = "rwh_06")), allow(dead_code))]
|
||||
platform: platform_impl::OwnedDisplayHandle,
|
||||
#[cfg_attr(not(feature = "rwh_06"), allow(dead_code))]
|
||||
pub(crate) platform: platform_impl::OwnedDisplayHandle,
|
||||
}
|
||||
|
||||
impl fmt::Debug for OwnedDisplayHandle {
|
||||
@@ -526,63 +457,42 @@ impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
unsafe impl rwh_05::HasRawDisplayHandle for OwnedDisplayHandle {
|
||||
#[inline]
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
self.platform.raw_display_handle_rwh_05()
|
||||
}
|
||||
/// Control the [`EventLoop`], possibly from a different thread, without referencing it directly.
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
pub(crate) event_loop_proxy: platform_impl::EventLoopProxy,
|
||||
}
|
||||
|
||||
/// Used to send custom events to [`EventLoop`].
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
event_loop_proxy: platform_impl::EventLoopProxy<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { event_loop_proxy: self.event_loop_proxy.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
/// Send an event to the [`EventLoop`] from which this proxy was created. This emits a
|
||||
/// `UserEvent(event)` event in the event loop, where `event` is the value passed to this
|
||||
/// function.
|
||||
impl EventLoopProxy {
|
||||
/// Wake up the [`EventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being
|
||||
/// called.
|
||||
///
|
||||
/// Returns an `Err` if the associated [`EventLoop`] no longer exists.
|
||||
/// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the
|
||||
/// documentation on that for details.
|
||||
///
|
||||
/// [`UserEvent(event)`]: Event::UserEvent
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
let _span = tracing::debug_span!("winit::EventLoopProxy::send_event",).entered();
|
||||
|
||||
self.event_loop_proxy.send_event(event)
|
||||
/// If the event loop is no longer running, this is a no-op.
|
||||
///
|
||||
/// [`proxy_wake_up`]: ApplicationHandler::proxy_wake_up
|
||||
///
|
||||
/// # Platform-specific
|
||||
///
|
||||
/// - **Windows**: The wake-up may be ignored under high contention, see [#3687].
|
||||
///
|
||||
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
|
||||
pub fn wake_up(&self) {
|
||||
self.event_loop_proxy.wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
|
||||
impl fmt::Debug for EventLoopProxy {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad("EventLoopProxy { .. }")
|
||||
f.debug_struct("ActiveEventLoop").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] that
|
||||
/// no longer exists.
|
||||
///
|
||||
/// Contains the original event given to [`EventLoopProxy::send_event`].
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventLoopClosed<T>(pub T);
|
||||
|
||||
impl<T> fmt::Display for EventLoopClosed<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("Tried to wake up a closed `EventLoop`")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {}
|
||||
|
||||
/// Control when device events are captured.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum DeviceEvents {
|
||||
/// Report device events regardless of window focus.
|
||||
Always,
|
||||
@@ -602,7 +512,7 @@ pub enum DeviceEvents {
|
||||
/// containing [`AsyncRequestSerial`] and some closure associated with it.
|
||||
/// Then once event is arriving the working list is being traversed and a job
|
||||
/// executed and removed from the list.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct AsyncRequestSerial {
|
||||
serial: usize,
|
||||
}
|
||||
@@ -618,23 +528,3 @@ impl AsyncRequestSerial {
|
||||
Self { serial }
|
||||
}
|
||||
}
|
||||
|
||||
/// Shim for various run APIs.
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_event_for_app<T: 'static, A: ApplicationHandler<T>>(
|
||||
app: &mut A,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event: Event<T>,
|
||||
) {
|
||||
match event {
|
||||
Event::NewEvents(cause) => app.new_events(event_loop, cause),
|
||||
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
|
||||
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
|
||||
Event::UserEvent(event) => app.user_event(event_loop, event),
|
||||
Event::Suspended => app.suspended(event_loop),
|
||||
Event::Resumed => app.resumed(event_loop),
|
||||
Event::AboutToWait => app.about_to_wait(event_loop),
|
||||
Event::LoopExiting => app.exiting(event_loop),
|
||||
Event::MemoryWarning => app.memory_warning(event_loop),
|
||||
}
|
||||
}
|
||||
|
||||
11
src/icon.rs
11
src/icon.rs
@@ -1,7 +1,8 @@
|
||||
use crate::platform_impl::PlatformIcon;
|
||||
use std::error::Error;
|
||||
use std::{fmt, io, mem};
|
||||
|
||||
use crate::platform_impl::PlatformIcon;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Pixel {
|
||||
@@ -49,15 +50,15 @@ impl fmt::Display for BadIcon {
|
||||
|
||||
impl Error for BadIcon {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct RgbaIcon {
|
||||
pub(crate) rgba: Vec<u8>,
|
||||
pub(crate) width: u32,
|
||||
pub(crate) height: u32,
|
||||
}
|
||||
|
||||
/// For platforms which don't have window icons (e.g. web)
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// For platforms which don't have window icons (e.g. Web)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NoIcon;
|
||||
|
||||
#[allow(dead_code)] // These are not used on every platform
|
||||
@@ -93,7 +94,7 @@ mod constructors {
|
||||
}
|
||||
|
||||
/// An icon used for the window titlebar, taskbar, etc.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
pub struct Icon {
|
||||
pub(crate) inner: PlatformIcon,
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ pub use smol_str::SmolStr;
|
||||
/// haven't mapped for you yet, this lets you use use [`KeyCode`] to:
|
||||
///
|
||||
/// - Correctly match key press and release events.
|
||||
/// - On non-web platforms, support assigning keybinds to virtually any key through a UI.
|
||||
/// - On non-Web platforms, support assigning keybinds to virtually any key through a UI.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum NativeKeyCode {
|
||||
@@ -1568,10 +1568,15 @@ impl NamedKey {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(web_platform)]
|
||||
/// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
/// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)]
|
||||
/// # fn main() {
|
||||
/// use winit::keyboard::NamedKey;
|
||||
///
|
||||
/// assert_eq!(NamedKey::Enter.to_text(), Some("\r"));
|
||||
/// assert_eq!(NamedKey::F20.to_text(), None);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn to_text(&self) -> Option<&str> {
|
||||
match self {
|
||||
@@ -1591,11 +1596,16 @@ impl Key {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(web_platform)]
|
||||
/// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
/// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)]
|
||||
/// # fn main() {
|
||||
/// use winit::keyboard::{Key, NamedKey};
|
||||
///
|
||||
/// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
|
||||
/// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
|
||||
/// assert_eq!(Key::Named(NamedKey::F20).to_text(), None);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn to_text(&self) -> Option<&str> {
|
||||
match self {
|
||||
@@ -1618,7 +1628,7 @@ impl Key {
|
||||
///
|
||||
/// [`location`]: ../event/struct.KeyEvent.html#structfield.location
|
||||
/// [`KeyEvent`]: crate::event::KeyEvent
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum KeyLocation {
|
||||
/// The key is in its "normal" location on the keyboard.
|
||||
@@ -1690,6 +1700,7 @@ bitflags! {
|
||||
///
|
||||
/// Each flag represents a modifier and is set if this modifier is active.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ModifiersState: u32 {
|
||||
/// The "shift" key.
|
||||
const SHIFT = 0b100;
|
||||
@@ -1725,7 +1736,8 @@ impl ModifiersState {
|
||||
}
|
||||
|
||||
/// The state of the particular modifiers key.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ModifiersKeyState {
|
||||
/// The particular key is pressed.
|
||||
Pressed,
|
||||
@@ -1744,6 +1756,7 @@ pub enum ModifiersKeyState {
|
||||
// on macOS due to their AltGr/Option situation.
|
||||
bitflags! {
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub(crate) struct ModifiersKeys: u8 {
|
||||
const LSHIFT = 0b0000_0001;
|
||||
const RSHIFT = 0b0000_0010;
|
||||
@@ -1755,50 +1768,3 @@ bitflags! {
|
||||
const RSUPER = 0b1000_0000;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod modifiers_serde {
|
||||
use super::ModifiersState;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
#[serde(rename = "ModifiersState")]
|
||||
pub struct ModifiersStateSerialize {
|
||||
pub shift_key: bool,
|
||||
pub control_key: bool,
|
||||
pub alt_key: bool,
|
||||
pub super_key: bool,
|
||||
}
|
||||
|
||||
impl Serialize for ModifiersState {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s = ModifiersStateSerialize {
|
||||
shift_key: self.shift_key(),
|
||||
control_key: self.control_key(),
|
||||
alt_key: self.alt_key(),
|
||||
super_key: self.super_key(),
|
||||
};
|
||||
s.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ModifiersState {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let ModifiersStateSerialize { shift_key, control_key, alt_key, super_key } =
|
||||
ModifiersStateSerialize::deserialize(deserializer)?;
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::SHIFT, shift_key);
|
||||
m.set(ModifiersState::CONTROL, control_key);
|
||||
m.set(ModifiersState::ALT, alt_key);
|
||||
m.set(ModifiersState::SUPER, super_key);
|
||||
Ok(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
30
src/lib.rs
30
src/lib.rs
@@ -19,15 +19,14 @@
|
||||
//! window or a key getting pressed while the window is focused. Devices can generate
|
||||
//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window.
|
||||
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
|
||||
//! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if
|
||||
//! desired.
|
||||
//! [`DeviceEvent`].
|
||||
//!
|
||||
//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will
|
||||
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
|
||||
//! will run until [`exit()`] is used, at which point [`Event::LoopExiting`].
|
||||
//! will run until [`exit()`] is used, at which point [`exiting()`] is called.
|
||||
//!
|
||||
//! 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
|
||||
//! model, since that can't be implemented properly on some platforms (e.g Web, iOS) and works
|
||||
//! poorly on most other platforms. However, this model can be re-implemented to an extent with
|
||||
#![cfg_attr(
|
||||
any(windows_platform, macos_platform, android_platform, x11_platform, wayland_platform),
|
||||
@@ -45,19 +44,19 @@
|
||||
//! use winit::application::ApplicationHandler;
|
||||
//! use winit::event::WindowEvent;
|
||||
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
//! use winit::window::{Window, WindowId};
|
||||
//! use winit::window::{Window, WindowId, WindowAttributes};
|
||||
//!
|
||||
//! #[derive(Default)]
|
||||
//! struct App {
|
||||
//! window: Option<Window>,
|
||||
//! window: Option<Box<dyn Window>>,
|
||||
//! }
|
||||
//!
|
||||
//! impl ApplicationHandler for App {
|
||||
//! fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
//! self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
|
||||
//! fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
//! self.window = Some(event_loop.create_window(WindowAttributes::default()).unwrap());
|
||||
//! }
|
||||
//!
|
||||
//! fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
|
||||
//! fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, id: WindowId, event: WindowEvent) {
|
||||
//! match event {
|
||||
//! WindowEvent::CloseRequested => {
|
||||
//! println!("The close button was pressed; stopping");
|
||||
@@ -141,8 +140,6 @@
|
||||
//!
|
||||
//! * `x11` (enabled by default): On Unix platforms, enables the X11 backend.
|
||||
//! * `wayland` (enabled by default): On Unix platforms, enables the Wayland backend.
|
||||
//! * `rwh_04`: Implement `raw-window-handle v0.4` traits.
|
||||
//! * `rwh_05`: Implement `raw-window-handle v0.5` traits.
|
||||
//! * `rwh_06`: Implement `raw-window-handle v0.6` traits.
|
||||
//! * `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde).
|
||||
//! * `mint`: Enables mint (math interoperability standard types) conversions.
|
||||
@@ -163,7 +160,7 @@
|
||||
//! [`WindowEvent`]: event::WindowEvent
|
||||
//! [`DeviceEvent`]: event::DeviceEvent
|
||||
//! [`Event::UserEvent`]: event::Event::UserEvent
|
||||
//! [`Event::LoopExiting`]: event::Event::LoopExiting
|
||||
//! [`exiting()`]: crate::application::ApplicationHandler::exiting
|
||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
|
||||
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland.
|
||||
@@ -178,16 +175,11 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
#[cfg(feature = "rwh_04")]
|
||||
pub use rwh_04 as raw_window_handle_04;
|
||||
#[cfg(feature = "rwh_05")]
|
||||
pub use rwh_05 as raw_window_handle_05;
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub use rwh_06 as raw_window_handle;
|
||||
|
||||
// Re-export DPI types so that users don't have to put it in Cargo.toml.
|
||||
#[doc(inline)]
|
||||
pub use dpi;
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub use rwh_06 as raw_window_handle;
|
||||
|
||||
pub mod application;
|
||||
#[cfg(any(doc, doctest, test))]
|
||||
|
||||
106
src/monitor.rs
106
src/monitor.rs
@@ -5,13 +5,11 @@
|
||||
//! methods, which return an iterator of [`MonitorHandle`]:
|
||||
//! - [`ActiveEventLoop::available_monitors`][crate::event_loop::ActiveEventLoop::available_monitors].
|
||||
//! - [`Window::available_monitors`][crate::window::Window::available_monitors].
|
||||
use std::num::{NonZeroU16, NonZeroU32};
|
||||
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::platform_impl;
|
||||
|
||||
/// Deprecated! Use `VideoModeHandle` instead.
|
||||
#[deprecated = "Renamed to `VideoModeHandle`"]
|
||||
pub type VideoMode = VideoModeHandle;
|
||||
|
||||
/// Describes a fullscreen video mode of a monitor.
|
||||
///
|
||||
/// Can be acquired with [`MonitorHandle::video_modes`].
|
||||
@@ -48,7 +46,10 @@ impl Ord for VideoModeHandle {
|
||||
}
|
||||
|
||||
impl VideoModeHandle {
|
||||
/// Returns the resolution of this video mode.
|
||||
/// Returns the resolution of this video mode. This **must not** be used to create your
|
||||
/// rendering surface. Use [`Window::surface_size()`] instead.
|
||||
///
|
||||
/// [`Window::surface_size()`]: crate::window::Window::surface_size
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.video_mode.size()
|
||||
@@ -57,19 +58,14 @@ impl VideoModeHandle {
|
||||
/// Returns the bit depth of this video mode, as in how many bits you have
|
||||
/// available per color. This is generally 24 bits or 32 bits on modern
|
||||
/// systems, depending on whether the alpha channel is counted or not.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Wayland / Orbital:** Always returns 32.
|
||||
/// - **iOS:** Always returns 32.
|
||||
#[inline]
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
pub fn bit_depth(&self) -> Option<NonZeroU16> {
|
||||
self.video_mode.bit_depth()
|
||||
}
|
||||
|
||||
/// Returns the refresh rate of this video mode in mHz.
|
||||
#[inline]
|
||||
pub fn refresh_rate_millihertz(&self) -> u32 {
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
|
||||
self.video_mode.refresh_rate_millihertz()
|
||||
}
|
||||
|
||||
@@ -85,11 +81,11 @@ impl std::fmt::Display for VideoModeHandle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}x{} @ {} mHz ({} bpp)",
|
||||
"{}x{} {}{}",
|
||||
self.size().width,
|
||||
self.size().height,
|
||||
self.refresh_rate_millihertz(),
|
||||
self.bit_depth()
|
||||
self.refresh_rate_millihertz().map(|rate| format!("@ {rate} mHz ")).unwrap_or_default(),
|
||||
self.bit_depth().map(|bit_depth| format!("({bit_depth} bpp)")).unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -98,46 +94,69 @@ impl std::fmt::Display for VideoModeHandle {
|
||||
///
|
||||
/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **Web:** A [`MonitorHandle`] created without
|
||||
#[cfg_attr(
|
||||
any(web_platform, docsrs),
|
||||
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
|
||||
)]
|
||||
#[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
|
||||
/// will always represent the current monitor the browser window is in instead of a specific
|
||||
/// monitor. See
|
||||
#[cfg_attr(
|
||||
any(web_platform, docsrs),
|
||||
doc = "[`MonitorHandleExtWeb::is_detailed()`][crate::platform::web::MonitorHandleExtWeb::is_detailed]"
|
||||
)]
|
||||
#[cfg_attr(not(any(web_platform, docsrs)), doc = "`MonitorHandleExtWeb::is_detailed()`")]
|
||||
/// to check.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct MonitorHandle {
|
||||
pub(crate) inner: platform_impl::MonitorHandle,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for MonitorHandle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
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`] without
|
||||
#[cfg_attr(
|
||||
any(web_platform, docsrs),
|
||||
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
|
||||
)]
|
||||
#[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
|
||||
#[inline]
|
||||
pub fn name(&self) -> Option<String> {
|
||||
self.inner.name()
|
||||
}
|
||||
|
||||
/// Returns the monitor's resolution.
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.inner.size()
|
||||
}
|
||||
|
||||
/// Returns the top-left corner position of the monitor relative to the larger full
|
||||
/// screen area.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **Web:** Always returns [`None`] without
|
||||
#[cfg_attr(
|
||||
any(web_platform, docsrs),
|
||||
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
|
||||
)]
|
||||
#[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
|
||||
#[inline]
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
|
||||
self.inner.position()
|
||||
}
|
||||
|
||||
/// The monitor refresh rate used by the system.
|
||||
///
|
||||
/// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor
|
||||
/// the window is on is removed.
|
||||
///
|
||||
/// When using exclusive fullscreen, the refresh rate of the [`VideoModeHandle`] that was
|
||||
/// used to enter fullscreen should be used instead.
|
||||
#[inline]
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
self.inner.refresh_rate_millihertz()
|
||||
}
|
||||
|
||||
/// Returns the scale factor of the underlying monitor. To map logical pixels to physical
|
||||
/// pixels and vice versa, use [`Window::scale_factor`].
|
||||
///
|
||||
@@ -148,18 +167,27 @@ impl MonitorHandle {
|
||||
/// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
|
||||
/// - **Wayland:** May differ from [`Window::scale_factor`].
|
||||
/// - **Android:** Always returns 1.0.
|
||||
/// - **Web:** Always returns `0.0` without
|
||||
#[cfg_attr(
|
||||
any(web_platform, docsrs),
|
||||
doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
|
||||
)]
|
||||
#[cfg_attr(not(any(web_platform, docsrs)), doc = " detailed monitor permissions.")]
|
||||
///
|
||||
#[rustfmt::skip]
|
||||
/// [`Window::scale_factor`]: crate::window::Window::scale_factor
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.inner.scale_factor()
|
||||
}
|
||||
|
||||
/// Returns the currently active video mode of this monitor.
|
||||
#[inline]
|
||||
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
|
||||
self.inner.current_video_mode().map(|video_mode| VideoModeHandle { video_mode })
|
||||
}
|
||||
|
||||
/// Returns all fullscreen video modes supported by this monitor.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Always returns an empty iterator
|
||||
#[inline]
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
|
||||
self.inner.video_modes().map(|video_mode| VideoModeHandle { video_mode })
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building
|
||||
//! with `cargo apk`, then the minimal changes would be:
|
||||
//! 1. Remove `ndk-glue` from your `Cargo.toml`
|
||||
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.2",
|
||||
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.5",
|
||||
//! 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
|
||||
@@ -70,18 +70,27 @@
|
||||
//! 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your
|
||||
//! event loop (as shown above).
|
||||
|
||||
use self::activity::{AndroidApp, ConfigurationRef, Rect};
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
|
||||
use crate::window::{Window, WindowAttributes};
|
||||
|
||||
use self::activity::{AndroidApp, ConfigurationRef, Rect};
|
||||
|
||||
/// Additional methods on [`EventLoop`] that are specific to Android.
|
||||
pub trait EventLoopExtAndroid {}
|
||||
pub trait EventLoopExtAndroid {
|
||||
/// Get the [`AndroidApp`] which was used to create this event loop.
|
||||
fn android_app(&self) -> &AndroidApp;
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtAndroid for EventLoop<T> {}
|
||||
impl EventLoopExtAndroid for EventLoop {
|
||||
fn android_app(&self) -> &AndroidApp {
|
||||
&self.event_loop.android_app
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`ActiveEventLoop`] that are specific to Android.
|
||||
pub trait ActiveEventLoopExtAndroid {}
|
||||
pub trait ActiveEventLoopExtAndroid {
|
||||
/// Get the [`AndroidApp`] which was used to create this event loop.
|
||||
fn android_app(&self) -> &AndroidApp;
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to Android.
|
||||
pub trait WindowExtAndroid {
|
||||
@@ -90,17 +99,25 @@ pub trait WindowExtAndroid {
|
||||
fn config(&self) -> ConfigurationRef;
|
||||
}
|
||||
|
||||
impl WindowExtAndroid for Window {
|
||||
impl WindowExtAndroid for dyn Window + '_ {
|
||||
fn content_rect(&self) -> Rect {
|
||||
self.window.content_rect()
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.content_rect()
|
||||
}
|
||||
|
||||
fn config(&self) -> ConfigurationRef {
|
||||
self.window.config()
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.config()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtAndroid for ActiveEventLoop {}
|
||||
impl ActiveEventLoopExtAndroid for dyn ActiveEventLoop + '_ {
|
||||
fn android_app(&self) -> &AndroidApp {
|
||||
let event_loop =
|
||||
self.as_any().downcast_ref::<crate::platform_impl::ActiveEventLoop>().unwrap();
|
||||
&event_loop.app
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`WindowAttributes`] that are specific to Android.
|
||||
pub trait WindowAttributesExtAndroid {}
|
||||
@@ -108,9 +125,9 @@ pub trait WindowAttributesExtAndroid {}
|
||||
impl WindowAttributesExtAndroid for WindowAttributes {}
|
||||
|
||||
pub trait EventLoopBuilderExtAndroid {
|
||||
/// Associates the `AndroidApp` that was passed to `android_main()` with the event loop
|
||||
/// Associates the [`AndroidApp`] that was passed to `android_main()` with the event loop
|
||||
///
|
||||
/// This must be called on Android since the `AndroidApp` is not global state.
|
||||
/// This must be called on Android since the [`AndroidApp`] is not global state.
|
||||
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self;
|
||||
|
||||
/// Calling this will mark the volume keys to be manually handled by the application
|
||||
@@ -119,7 +136,7 @@ pub trait EventLoopBuilderExtAndroid {
|
||||
fn handle_volume_keys(&mut self) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
|
||||
impl EventLoopBuilderExtAndroid for EventLoopBuilder {
|
||||
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self {
|
||||
self.platform_specific.android_app = Some(app);
|
||||
self
|
||||
@@ -147,7 +164,7 @@ impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
|
||||
/// depending on the `android_activity` crate, and instead consume the API that
|
||||
/// is re-exported by Winit.
|
||||
///
|
||||
/// For compatibility applications should then import the `AndroidApp` type for
|
||||
/// For compatibility applications should then import the [`AndroidApp`] type for
|
||||
/// their `android_main(app: AndroidApp)` function like:
|
||||
/// ```rust
|
||||
/// #[cfg(target_os = "android")]
|
||||
|
||||
@@ -1,51 +1,79 @@
|
||||
//! # iOS / UIKit
|
||||
//!
|
||||
//! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on
|
||||
//! iOS 9.3.
|
||||
//! Winit has [the same iOS version requirements as `rustc`][rustc-ios-version], although it's
|
||||
//! frequently only tested on newer iOS versions.
|
||||
//!
|
||||
//! iOS's main `UIApplicationMain` does some init work that's required by all
|
||||
//! UI-related code (see issue [#1705]). It is best to create your windows
|
||||
//! inside `Event::Resumed`.
|
||||
//! [rustc-ios-version]: https://doc.rust-lang.org/rustc/platform-support/apple-ios.html#os-version
|
||||
//!
|
||||
//! [#1705]: https://github.com/rust-windowing/winit/issues/1705
|
||||
//! ## Running on Mac Catalyst
|
||||
//!
|
||||
//! ## Building app
|
||||
//! Mac Catalyst allows running applications using UIKit on macOS, which can be very useful for
|
||||
//! testing. See [`rustc`'s documentation on Mac Catalyst][rustc-mac-catalyst] for details on how to
|
||||
//! use these targets. To use these with Winit, you'll need to bundle your application before
|
||||
//! running it, otherwise UIKit will exit with an error.
|
||||
//!
|
||||
//! To build ios app you will need rustc built for this targets:
|
||||
//! To run e.g. the `window` example in the Winit repository, you can use [`cargo-bundle`] as
|
||||
//! follows:
|
||||
//!
|
||||
//! - armv7-apple-ios
|
||||
//! - armv7s-apple-ios
|
||||
//! - i386-apple-ios
|
||||
//! - aarch64-apple-ios
|
||||
//! - x86_64-apple-ios
|
||||
//!
|
||||
//! Then
|
||||
//!
|
||||
//! ```
|
||||
//! cargo build --target=...
|
||||
//! ```
|
||||
//! The simplest way to integrate your app into xcode environment is to build it
|
||||
//! as a static library. Wrap your main function and export it.
|
||||
//!
|
||||
//! ```rust, ignore
|
||||
//! #[no_mangle]
|
||||
//! pub extern fn start_winit_app() {
|
||||
//! start_inner()
|
||||
//! }
|
||||
//!
|
||||
//! fn start_inner() {
|
||||
//! ...
|
||||
//! }
|
||||
//! ```console
|
||||
//! $ cargo +nightly bundle --format=ios --target=aarch64-apple-ios-macabi --example=window
|
||||
//! $ ./target/aarch64-apple-ios-macabi/debug/examples/bundle/ios/winit.app/window
|
||||
//! ```
|
||||
//!
|
||||
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
|
||||
//! [rustc-mac-catalyst]: https://doc.rust-lang.org/rustc/platform-support/apple-ios-macabi.html
|
||||
//! [`cargo-bundle`]: https://github.com/burtonageo/cargo-bundle
|
||||
//!
|
||||
//! ```ignore
|
||||
//! void start_winit_app();
|
||||
//! ## Introduction to building an app
|
||||
//!
|
||||
//! Building and running your application in the iOS simulator, or on a real device, is a bit more
|
||||
//! complicated than Mac Catalyst - fundamentally, you must use Xcode, since the binary needs to be
|
||||
//! bundled, signed, notarized and uploaded to the device (there is [an open source work-in-progress
|
||||
//! on re-implementing parts of this][apple-platform-rs], but the user-story around it is not yet
|
||||
//! clear).
|
||||
//!
|
||||
//! This means that you're left with effectively two options: Use a tool that manages the Xcode
|
||||
//! configuration for you, or use Xcode directly. [`cargo-dinghy`] and [`cargo-mobile2`] are notable
|
||||
//! projects in the ecosystem that attempt the former, and [`cargo-xcode`] is an excellent project
|
||||
//! that attempts the latter. We will also attempt to describe here how you would go about using
|
||||
//! Xcode directly:
|
||||
//!
|
||||
//! First off, you'll need the correct Rust targets, see [`rustc`'s documentation on iOS][rustc-ios]
|
||||
//! for details. Nowadays, the correct targets are usually `aarch64-apple-ios-sim` for the
|
||||
//! simulator, and `aarch64-apple-ios` for the actual device.
|
||||
//!
|
||||
//! Next, create a new Xcode project using the "App" template. The exact configuration does not
|
||||
//! really matter, as we're going to delete most of it, since it's tailored for Objective-C and/or
|
||||
//! Swift, and Rust/Winit is neither. Specifically, we need to delete:
|
||||
//! - Everything relating to storyboards (unless you want to use e.g. a launch screen). This
|
||||
//! includes the relevant keys in `Info.plist`.
|
||||
//! - All the generated C header, Objective-C and/or Swift files.
|
||||
//!
|
||||
//! Now that we have a fairly clean slate that we can build upon, you can add a "run script" build
|
||||
//! phase to your Xcode target, which will get invoked instead of the "compile sources" and "link
|
||||
//! binary" steps. The basic script should look something like:
|
||||
//!
|
||||
//! ```sh
|
||||
//! # Build desired targets based on `ARCHS` environment variable
|
||||
//! cargo build --target=aarch64-apple-ios --target=armv7s-apple-ios
|
||||
//! # Merge these with `lipo`, and place the result in "$TARGET_BUILD_DIR/$EXECUTABLE_PATH", which
|
||||
//! # is understood by Xcode
|
||||
//! lipo "$TARGET_BUILD_DIR/$EXECUTABLE_PATH" target/aarch64-apple-ios/debug/my_app target/armv7s-apple-ios/debug/my_app
|
||||
//! ```
|
||||
//!
|
||||
//! Use start_winit_app inside your xcode's main function.
|
||||
//! Note that this is very much the overall idea; the script needs to be much more involved to
|
||||
//! properly deal with different target architectures, invoking `lipo` when needed, incremental
|
||||
//! rebuild change detection, and so on. `cargo-xcode` has a script [here][cargo-xcode-script] that
|
||||
//! handles most of this complexity, you might be able to build upon that.
|
||||
//!
|
||||
//! Apologies that we're not able to provide you with more than this; work is in-progress on
|
||||
//! improving the situation, but it's slow-going.
|
||||
//!
|
||||
//! [apple-platform-rs]: https://github.com/indygreg/apple-platform-rs
|
||||
//! [`cargo-dinghy`]: https://github.com/sonos/dinghy
|
||||
//! [`cargo-mobile2`]: https://github.com/tauri-apps/cargo-mobile2
|
||||
//! [`cargo-xcode`]: https://crates.io/crates/cargo-xcode
|
||||
//! [rustc-ios]: https://doc.rust-lang.org/rustc/platform-support/apple-ios.html
|
||||
//! [cargo-xcode-script]: https://gitlab.com/kornelski/cargo-xcode/-/blob/main/src/xcodebuild.sh
|
||||
//!
|
||||
//! ## App lifecycle and events
|
||||
//!
|
||||
@@ -63,25 +91,25 @@
|
||||
//! opengl will result in segfault.
|
||||
//!
|
||||
//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
|
||||
//!
|
||||
//! ## Custom `UIApplicationDelegate`
|
||||
//!
|
||||
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
|
||||
//! though, you might want to access some of the more niche stuff that [the application
|
||||
//! delegate][app-delegate] provides. This functionality is not exposed directly in Winit, since it
|
||||
//! would increase the API surface by quite a lot. Instead, Winit guarantees that it will not
|
||||
//! register an application delegate, so you can set up a custom one in a nib file instead.
|
||||
//!
|
||||
//! [app-delegate]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate?language=objc
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::event_loop::EventLoop;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::monitor::{MonitorHandle, VideoModeHandle};
|
||||
use crate::window::{Window, WindowAttributes};
|
||||
|
||||
/// Additional methods on [`EventLoop`] that are specific to iOS.
|
||||
pub trait EventLoopExtIOS {
|
||||
/// Returns the [`Idiom`] (phone/tablet/tv/etc) for the current device.
|
||||
fn idiom(&self) -> Idiom;
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopExtIOS for EventLoop<T> {
|
||||
fn idiom(&self) -> Idiom {
|
||||
self.event_loop.idiom()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to iOS.
|
||||
pub trait WindowExtIOS {
|
||||
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
|
||||
@@ -181,42 +209,49 @@ pub trait WindowExtIOS {
|
||||
fn recognize_rotation_gesture(&self, should_recognize: bool);
|
||||
}
|
||||
|
||||
impl WindowExtIOS for Window {
|
||||
impl WindowExtIOS for dyn Window + '_ {
|
||||
#[inline]
|
||||
fn set_scale_factor(&self, scale_factor: f64) {
|
||||
self.window.maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_scale_factor(scale_factor));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
|
||||
self.window.maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_valid_orientations(valid_orientations));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
|
||||
self.window.maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
|
||||
self.window.maybe_queue_on_main(move |w| {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| {
|
||||
w.set_preferred_screen_edges_deferring_system_gestures(edges)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
||||
self.window.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_prefers_status_bar_hidden(hidden));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
|
||||
self.window.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn recognize_pinch_gesture(&self, should_recognize: bool) {
|
||||
self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -226,7 +261,8 @@ impl WindowExtIOS for Window {
|
||||
minimum_number_of_touches: u8,
|
||||
maximum_number_of_touches: u8,
|
||||
) {
|
||||
self.window.maybe_queue_on_main(move |w| {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| {
|
||||
w.recognize_pan_gesture(
|
||||
should_recognize,
|
||||
minimum_number_of_touches,
|
||||
@@ -237,12 +273,14 @@ impl WindowExtIOS for Window {
|
||||
|
||||
#[inline]
|
||||
fn recognize_doubletap_gesture(&self, should_recognize: bool) {
|
||||
self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn recognize_rotation_gesture(&self, should_recognize: bool) {
|
||||
self.window.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,6 +406,7 @@ impl MonitorHandleExtIOS for MonitorHandle {
|
||||
|
||||
/// Valid orientations for a particular [`Window`].
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ValidOrientations {
|
||||
/// Excludes `PortraitUpsideDown` on iphone
|
||||
#[default]
|
||||
@@ -379,29 +418,12 @@ pub enum ValidOrientations {
|
||||
Portrait,
|
||||
}
|
||||
|
||||
/// The device [idiom].
|
||||
///
|
||||
/// [idiom]: https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom?language=objc
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Idiom {
|
||||
Unspecified,
|
||||
|
||||
/// iPhone and iPod touch.
|
||||
Phone,
|
||||
|
||||
/// iPad.
|
||||
Pad,
|
||||
|
||||
/// tvOS and Apple TV.
|
||||
TV,
|
||||
CarPlay,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// The [edges] of a screen.
|
||||
///
|
||||
/// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ScreenEdge: u8 {
|
||||
const NONE = 0;
|
||||
const TOP = 1 << 0;
|
||||
@@ -413,7 +435,8 @@ bitflags::bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum StatusBarStyle {
|
||||
#[default]
|
||||
Default,
|
||||
|
||||
@@ -1,18 +1,74 @@
|
||||
//! # macOS / AppKit
|
||||
//!
|
||||
//! Winit has an OS requirement of macOS 10.11 or higher (same as Rust
|
||||
//! itself), and is regularly tested on macOS 10.14.
|
||||
//! Winit has [the same macOS version requirements as `rustc`][rustc-macos-version], and is tested
|
||||
//! once in a while on as low as macOS 10.14.
|
||||
//!
|
||||
//! A lot of functionality expects the application to be ready before you
|
||||
//! start doing anything; this includes creating windows, fetching monitors,
|
||||
//! drawing, and so on, see issues [#2238], [#2051] and [#2087].
|
||||
//! [rustc-macos-version]: https://doc.rust-lang.org/rustc/platform-support/apple-darwin.html#os-version
|
||||
//!
|
||||
//! If you encounter problems, you should try doing your initialization inside
|
||||
//! `Event::Resumed`.
|
||||
//! ## Custom `NSApplicationDelegate`
|
||||
//!
|
||||
//! [#2238]: https://github.com/rust-windowing/winit/issues/2238
|
||||
//! [#2051]: https://github.com/rust-windowing/winit/issues/2051
|
||||
//! [#2087]: https://github.com/rust-windowing/winit/issues/2087
|
||||
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
|
||||
//! though, you might want to do more niche stuff, such as [handle when the user re-activates the
|
||||
//! application][reopen]. Such functionality is not exposed directly in Winit, since it would
|
||||
//! increase the API surface by quite a lot.
|
||||
//!
|
||||
//! [reopen]: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc
|
||||
//!
|
||||
//! Instead, Winit guarantees that it will not register an application delegate, so the solution is
|
||||
//! to register your own application delegate, as outlined in the following example (see
|
||||
//! `objc2-app-kit` for more detailed information).
|
||||
#![cfg_attr(target_os = "macos", doc = "```")]
|
||||
#![cfg_attr(not(target_os = "macos"), doc = "```ignore")]
|
||||
//! use objc2::rc::Retained;
|
||||
//! use objc2::runtime::ProtocolObject;
|
||||
//! use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||
//! use objc2_app_kit::{NSApplication, NSApplicationDelegate};
|
||||
//! use objc2_foundation::{NSArray, NSURL, MainThreadMarker, NSObject, NSObjectProtocol};
|
||||
//! use winit::event_loop::EventLoop;
|
||||
//!
|
||||
//! declare_class!(
|
||||
//! struct AppDelegate;
|
||||
//!
|
||||
//! unsafe impl ClassType for AppDelegate {
|
||||
//! type Super = NSObject;
|
||||
//! type Mutability = mutability::MainThreadOnly;
|
||||
//! const NAME: &'static str = "MyAppDelegate";
|
||||
//! }
|
||||
//!
|
||||
//! impl DeclaredClass for AppDelegate {}
|
||||
//!
|
||||
//! unsafe impl NSObjectProtocol for AppDelegate {}
|
||||
//!
|
||||
//! unsafe impl NSApplicationDelegate for AppDelegate {
|
||||
//! #[method(application:openURLs:)]
|
||||
//! fn application_openURLs(&self, application: &NSApplication, urls: &NSArray<NSURL>) {
|
||||
//! // Note: To specifically get `application:openURLs:` to work, you _might_
|
||||
//! // have to bundle your application. This is not done in this example.
|
||||
//! println!("open urls: {application:?}, {urls:?}");
|
||||
//! }
|
||||
//! }
|
||||
//! );
|
||||
//!
|
||||
//! impl AppDelegate {
|
||||
//! fn new(mtm: MainThreadMarker) -> Retained<Self> {
|
||||
//! unsafe { msg_send_id![super(mtm.alloc().set_ivars(())), init] }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let event_loop = EventLoop::new()?;
|
||||
//!
|
||||
//! let mtm = MainThreadMarker::new().unwrap();
|
||||
//! let delegate = AppDelegate::new(mtm);
|
||||
//! // Important: Call `sharedApplication` after `EventLoop::new`,
|
||||
//! // doing it before is not yet supported.
|
||||
//! let app = NSApplication::sharedApplication(mtm);
|
||||
//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
//!
|
||||
//! // event_loop.run_app(&mut my_app);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
@@ -94,82 +150,115 @@ pub trait WindowExtMacOS {
|
||||
|
||||
/// Getter for the [`WindowExtMacOS::set_option_as_alt`].
|
||||
fn option_as_alt(&self) -> OptionAsAlt;
|
||||
|
||||
/// Disable the Menu Bar and Dock in Borderless Fullscreen mode. Useful for games.
|
||||
fn set_borderless_game(&self, borderless_game: bool);
|
||||
|
||||
/// Getter for the [`WindowExtMacOS::set_borderless_game`].
|
||||
fn is_borderless_game(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WindowExtMacOS for Window {
|
||||
impl WindowExtMacOS for dyn Window + '_ {
|
||||
#[inline]
|
||||
fn simple_fullscreen(&self) -> bool {
|
||||
self.window.maybe_wait_on_main(|w| w.simple_fullscreen())
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.simple_fullscreen())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
|
||||
self.window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_shadow(&self) -> bool {
|
||||
self.window.maybe_wait_on_main(|w| w.has_shadow())
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.has_shadow())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_has_shadow(&self, has_shadow: bool) {
|
||||
self.window.maybe_queue_on_main(move |w| w.set_has_shadow(has_shadow))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_has_shadow(has_shadow));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_tabbing_identifier(&self, identifier: &str) {
|
||||
self.window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn tabbing_identifier(&self) -> String {
|
||||
self.window.maybe_wait_on_main(|w| w.tabbing_identifier())
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.tabbing_identifier())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn select_next_tab(&self) {
|
||||
self.window.maybe_queue_on_main(|w| w.select_next_tab())
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.select_next_tab());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn select_previous_tab(&self) {
|
||||
self.window.maybe_queue_on_main(|w| w.select_previous_tab())
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.select_previous_tab());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn select_tab_at_index(&self, index: usize) {
|
||||
self.window.maybe_queue_on_main(move |w| w.select_tab_at_index(index))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.select_tab_at_index(index));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn num_tabs(&self) -> usize {
|
||||
self.window.maybe_wait_on_main(|w| w.num_tabs())
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.num_tabs())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_document_edited(&self) -> bool {
|
||||
self.window.maybe_wait_on_main(|w| w.is_document_edited())
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.is_document_edited())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_document_edited(&self, edited: bool) {
|
||||
self.window.maybe_queue_on_main(move |w| w.set_document_edited(edited))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_document_edited(edited));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
|
||||
self.window.maybe_queue_on_main(move |w| w.set_option_as_alt(option_as_alt))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(move |w| w.set_option_as_alt(option_as_alt));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn option_as_alt(&self) -> OptionAsAlt {
|
||||
self.window.maybe_wait_on_main(|w| w.option_as_alt())
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.option_as_alt())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_borderless_game(&self, borderless_game: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_borderless_game(&self) -> bool {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.is_borderless_game())
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to `NSApplicationActivationPolicy`.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ActivationPolicy {
|
||||
/// Corresponds to `NSApplicationActivationPolicyRegular`.
|
||||
#[default]
|
||||
@@ -216,6 +305,8 @@ pub trait WindowAttributesExtMacOS {
|
||||
///
|
||||
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
|
||||
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
|
||||
/// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
|
||||
fn with_borderless_game(self, borderless_game: bool) -> Self;
|
||||
}
|
||||
|
||||
impl WindowAttributesExtMacOS for WindowAttributes {
|
||||
@@ -284,6 +375,12 @@ impl WindowAttributesExtMacOS for WindowAttributes {
|
||||
self.platform_specific.option_as_alt = option_as_alt;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_borderless_game(mut self, borderless_game: bool) -> Self {
|
||||
self.platform_specific.borderless_game = borderless_game;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EventLoopBuilderExtMacOS {
|
||||
@@ -296,11 +393,11 @@ pub trait EventLoopBuilderExtMacOS {
|
||||
/// Set the activation policy to "accessory".
|
||||
///
|
||||
/// ```
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// use winit::event_loop::EventLoop;
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS};
|
||||
///
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// let mut builder = EventLoop::builder();
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// builder.with_activation_policy(ActivationPolicy::Accessory);
|
||||
/// # if false { // We can't test this part
|
||||
@@ -318,11 +415,11 @@ pub trait EventLoopBuilderExtMacOS {
|
||||
/// Disable creating a default menubar.
|
||||
///
|
||||
/// ```
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// use winit::event_loop::EventLoop;
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// use winit::platform::macos::EventLoopBuilderExtMacOS;
|
||||
///
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// let mut builder = EventLoop::builder();
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// builder.with_default_menu(false);
|
||||
/// # if false { // We can't test this part
|
||||
@@ -338,7 +435,7 @@ pub trait EventLoopBuilderExtMacOS {
|
||||
fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtMacOS for EventLoopBuilder<T> {
|
||||
impl EventLoopBuilderExtMacOS for EventLoopBuilder {
|
||||
#[inline]
|
||||
fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self {
|
||||
self.platform_specific.activation_policy = activation_policy;
|
||||
@@ -395,28 +492,44 @@ pub trait ActiveEventLoopExtMacOS {
|
||||
fn allows_automatic_window_tabbing(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtMacOS for ActiveEventLoop {
|
||||
impl ActiveEventLoopExtMacOS for dyn ActiveEventLoop + '_ {
|
||||
fn hide_application(&self) {
|
||||
self.p.hide_application()
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non macOS event loop on macOS");
|
||||
event_loop.hide_application()
|
||||
}
|
||||
|
||||
fn hide_other_applications(&self) {
|
||||
self.p.hide_other_applications()
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non macOS event loop on macOS");
|
||||
event_loop.hide_other_applications()
|
||||
}
|
||||
|
||||
fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
|
||||
self.p.set_allows_automatic_window_tabbing(enabled);
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non macOS event loop on macOS");
|
||||
event_loop.set_allows_automatic_window_tabbing(enabled);
|
||||
}
|
||||
|
||||
fn allows_automatic_window_tabbing(&self) -> bool {
|
||||
self.p.allows_automatic_window_tabbing()
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non macOS event loop on macOS");
|
||||
event_loop.allows_automatic_window_tabbing()
|
||||
}
|
||||
}
|
||||
|
||||
/// Option as alt behavior.
|
||||
///
|
||||
/// The default is `None`.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum OptionAsAlt {
|
||||
/// The left `Option` key is treated as `Alt`.
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
|
||||
use crate::event_loop::EventLoop;
|
||||
|
||||
/// Additional methods on [`EventLoop`] for pumping events within an external event loop
|
||||
pub trait EventLoopExtPumpEvents {
|
||||
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// Pump the `EventLoop` to check for and dispatch pending events.
|
||||
///
|
||||
/// This API is designed to enable applications to integrate Winit into an
|
||||
@@ -52,18 +48,18 @@ pub trait EventLoopExtPumpEvents {
|
||||
/// - `RedrawRequested` events, used to schedule rendering.
|
||||
///
|
||||
/// macOS for example uses a `drawRect` callback to drive rendering
|
||||
/// within applications and expects rendering to be finished before
|
||||
/// the `drawRect` callback returns.
|
||||
/// within applications and expects rendering to be finished before
|
||||
/// the `drawRect` callback returns.
|
||||
///
|
||||
/// For portability it's strongly recommended that applications should
|
||||
/// keep their rendering inside the closure provided to Winit.
|
||||
/// keep their rendering inside the closure provided to Winit.
|
||||
/// - Any lifecycle events, such as `Suspended` / `Resumed`.
|
||||
///
|
||||
/// The handling of these events needs to be synchronized with the
|
||||
/// operating system and it would never be appropriate to buffer a
|
||||
/// notification that your application has been suspended or resumed and
|
||||
/// then handled that later since there would always be a chance that
|
||||
/// other lifecycle events occur while the event is buffered.
|
||||
/// operating system and it would never be appropriate to buffer a
|
||||
/// notification that your application has been suspended or resumed and
|
||||
/// then handled that later since there would always be a chance that
|
||||
/// other lifecycle events occur while the event is buffered.
|
||||
///
|
||||
/// ## Supported Platforms
|
||||
///
|
||||
@@ -74,13 +70,13 @@ pub trait EventLoopExtPumpEvents {
|
||||
///
|
||||
/// ## Unsupported Platforms
|
||||
///
|
||||
/// - **Web:** This API is fundamentally incompatible with the event-based way in which
|
||||
/// Web browsers work because it's not possible to have a long-running external
|
||||
/// loop that would block the browser and there is nothing that can be
|
||||
/// polled to ask for new new events. Events are delivered via callbacks based
|
||||
/// on an event loop that is internal to the browser itself.
|
||||
/// - **Web:** This API is fundamentally incompatible with the event-based way in which Web
|
||||
/// browsers work because it's not possible to have a long-running external loop that would
|
||||
/// block the browser and there is nothing that can be polled to ask for new new events.
|
||||
/// Events are delivered via callbacks based on an event loop that is internal to the browser
|
||||
/// itself.
|
||||
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so
|
||||
/// there's no way to support the same approach to polling as on MacOS.
|
||||
/// there's no way to support the same approach to polling as on MacOS.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
@@ -103,38 +99,25 @@ pub trait EventLoopExtPumpEvents {
|
||||
/// If you render outside of Winit you are likely to see window resizing artifacts
|
||||
/// since MacOS expects applications to render synchronously during any `drawRect`
|
||||
/// callback.
|
||||
fn pump_app_events<A: ApplicationHandler<Self::UserEvent>>(
|
||||
fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
#[allow(deprecated)]
|
||||
self.pump_events(timeout, |event, event_loop| {
|
||||
event_loop::dispatch_event_for_app(app, event_loop, event)
|
||||
})
|
||||
}
|
||||
|
||||
/// See [`pump_app_events`].
|
||||
///
|
||||
/// [`pump_app_events`]: Self::pump_app_events
|
||||
#[deprecated = "use EventLoopExtPumpEvents::pump_app_events"]
|
||||
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||
app: A,
|
||||
) -> PumpStatus;
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtPumpEvents for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
||||
{
|
||||
self.event_loop.pump_events(timeout, event_handler)
|
||||
impl EventLoopExtPumpEvents for EventLoop {
|
||||
fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: A,
|
||||
) -> PumpStatus {
|
||||
self.event_loop.pump_app_events(timeout, app)
|
||||
}
|
||||
}
|
||||
|
||||
/// The return status for `pump_events`
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum PumpStatus {
|
||||
/// Continue running external loop.
|
||||
Continue,
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
|
||||
|
||||
use crate::event_loop::EventLoop;
|
||||
#[cfg(doc)]
|
||||
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
|
||||
use crate::{
|
||||
event_loop::ActiveEventLoop, platform::pump_events::EventLoopExtPumpEvents, window::Window,
|
||||
};
|
||||
|
||||
/// Additional methods on [`EventLoop`] to return control flow to the caller.
|
||||
pub trait EventLoopExtRunOnDemand {
|
||||
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// See [`run_app_on_demand`].
|
||||
///
|
||||
/// [`run_app_on_demand`]: Self::run_app_on_demand
|
||||
#[deprecated = "use EventLoopExtRunOnDemand::run_app_on_demand"]
|
||||
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||
|
||||
/// Run the application with the event loop on the calling thread.
|
||||
///
|
||||
/// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`)
|
||||
@@ -42,7 +31,13 @@ pub trait EventLoopExtRunOnDemand {
|
||||
/// # Caveats
|
||||
/// - This extension isn't available on all platforms, since it's not always possible to return
|
||||
/// to the caller (specifically this is impossible on iOS and Web - though with the Web
|
||||
/// backend it is possible to use `EventLoopExtWebSys::spawn()`[^1] more than once instead).
|
||||
/// backend it is possible to use
|
||||
#[cfg_attr(
|
||||
any(web_platform, docsrs),
|
||||
doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
|
||||
)]
|
||||
#[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")]
|
||||
/// [^1] more than once instead).
|
||||
/// - No [`Window`] state can be carried between separate runs of the event loop.
|
||||
///
|
||||
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you
|
||||
@@ -60,38 +55,17 @@ pub trait EventLoopExtRunOnDemand {
|
||||
/// block the browser and there is nothing that can be polled to ask for new events. Events
|
||||
/// are delivered via callbacks based on an event loop that is internal to the browser itself.
|
||||
/// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS.
|
||||
#[cfg_attr(not(web_platform), doc = "[^1]: `spawn()` is only available on `wasm` platforms.")]
|
||||
#[rustfmt::skip]
|
||||
///
|
||||
/// [^1]: `spawn_app()` is only available on the Web platforms.
|
||||
///
|
||||
/// [`exit()`]: ActiveEventLoop::exit()
|
||||
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
|
||||
fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
#[allow(deprecated)]
|
||||
self.run_on_demand(|event, event_loop| {
|
||||
event_loop::dispatch_event_for_app(app, event_loop, event)
|
||||
})
|
||||
}
|
||||
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>;
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
||||
{
|
||||
self.event_loop.window_target().clear_exit();
|
||||
self.event_loop.run_on_demand(event_handler)
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
/// Clear exit status.
|
||||
pub(crate) fn clear_exit(&self) {
|
||||
self.p.clear_exit()
|
||||
impl EventLoopExtRunOnDemand for EventLoop {
|
||||
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError> {
|
||||
self.event_loop.run_app_on_demand(app)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
|
||||
use std::env;
|
||||
|
||||
use crate::error::NotSupportedError;
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial};
|
||||
#[cfg(wayland_platform)]
|
||||
use crate::platform::wayland::ActiveEventLoopExtWayland;
|
||||
use crate::window::{ActivationToken, Window, WindowAttributes};
|
||||
|
||||
/// The variable which is used mostly on X11.
|
||||
@@ -44,7 +46,7 @@ pub trait WindowExtStartupNotify {
|
||||
/// Request a new activation token.
|
||||
///
|
||||
/// The token will be delivered inside
|
||||
fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError>;
|
||||
fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError>;
|
||||
}
|
||||
|
||||
pub trait WindowAttributesExtStartupNotify {
|
||||
@@ -55,22 +57,37 @@ pub trait WindowAttributesExtStartupNotify {
|
||||
fn with_activation_token(self, token: ActivationToken) -> Self;
|
||||
}
|
||||
|
||||
impl EventLoopExtStartupNotify for ActiveEventLoop {
|
||||
impl EventLoopExtStartupNotify for dyn ActiveEventLoop + '_ {
|
||||
fn read_token_from_env(&self) -> Option<ActivationToken> {
|
||||
match self.p {
|
||||
#[cfg(wayland_platform)]
|
||||
crate::platform_impl::ActiveEventLoop::Wayland(_) => env::var(WAYLAND_VAR),
|
||||
#[cfg(x11_platform)]
|
||||
crate::platform_impl::ActiveEventLoop::X(_) => env::var(X11_VAR),
|
||||
#[cfg(x11_platform)]
|
||||
let _is_wayland = false;
|
||||
#[cfg(wayland_platform)]
|
||||
let _is_wayland = self.is_wayland();
|
||||
|
||||
if _is_wayland {
|
||||
env::var(WAYLAND_VAR).ok().map(ActivationToken::_new)
|
||||
} else {
|
||||
env::var(X11_VAR).ok().map(ActivationToken::_new)
|
||||
}
|
||||
.ok()
|
||||
.map(ActivationToken::_new)
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowExtStartupNotify for Window {
|
||||
fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||
self.window.request_activation_token()
|
||||
impl WindowExtStartupNotify for dyn Window + '_ {
|
||||
fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
|
||||
#[cfg(wayland_platform)]
|
||||
if let Some(window) = self.as_any().downcast_ref::<crate::platform_impl::wayland::Window>()
|
||||
{
|
||||
return window.request_activation_token();
|
||||
}
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
if let Some(window) =
|
||||
self.as_any().downcast_ref::<crate::platform_impl::x11::window::Window>()
|
||||
{
|
||||
return window.request_activation_token();
|
||||
}
|
||||
|
||||
Err(NotSupportedError::new("startup notify is not supported").into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
//! * `wayland-csd-adwaita-notitle`.
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::window::{Window, WindowAttributes};
|
||||
|
||||
pub use crate::window::Theme;
|
||||
use crate::window::{Window as CoreWindow, WindowAttributes};
|
||||
|
||||
/// Additional methods on [`ActiveEventLoop`] that are specific to Wayland.
|
||||
pub trait ActiveEventLoopExtWayland {
|
||||
@@ -25,10 +24,10 @@ pub trait ActiveEventLoopExtWayland {
|
||||
fn is_wayland(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtWayland for ActiveEventLoop {
|
||||
impl ActiveEventLoopExtWayland for dyn ActiveEventLoop + '_ {
|
||||
#[inline]
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.p.is_wayland()
|
||||
self.as_any().downcast_ref::<crate::platform_impl::wayland::ActiveEventLoop>().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +37,7 @@ pub trait EventLoopExtWayland {
|
||||
fn is_wayland(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopExtWayland for EventLoop<T> {
|
||||
impl EventLoopExtWayland for EventLoop {
|
||||
#[inline]
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.event_loop.is_wayland()
|
||||
@@ -57,7 +56,7 @@ pub trait EventLoopBuilderExtWayland {
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> {
|
||||
impl EventLoopBuilderExtWayland for EventLoopBuilder {
|
||||
#[inline]
|
||||
fn with_wayland(&mut self) -> &mut Self {
|
||||
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland);
|
||||
@@ -72,9 +71,11 @@ impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> {
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to Wayland.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub trait WindowExtWayland {}
|
||||
|
||||
impl WindowExtWayland for Window {}
|
||||
impl WindowExtWayland for dyn CoreWindow + '_ {}
|
||||
|
||||
/// Additional methods on [`WindowAttributes`] that are specific to Wayland.
|
||||
pub trait WindowAttributesExtWayland {
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
//! # Web
|
||||
//!
|
||||
//! The officially supported browsers are Chrome, Firefox and Safari 13.1+,
|
||||
//! though forks of these should work fine.
|
||||
//! Winit supports running in Browsers by compiling to WebAssembly with
|
||||
//! [`wasm-bindgen`][wasm_bindgen]. For information on using Rust on WebAssembly, check out the
|
||||
//! [Rust and WebAssembly book].
|
||||
//!
|
||||
//! Winit supports compiling to the `wasm32-unknown-unknown` target with
|
||||
//! `web-sys`.
|
||||
//! The officially supported browsers are Chrome, Firefox and Safari 13.1+, though forks of these
|
||||
//! should work fine.
|
||||
//!
|
||||
//! On the web platform, a Winit window is backed by a `<canvas>` element. You
|
||||
//! can either [provide Winit with a `<canvas>` element][with_canvas], or
|
||||
//! [let Winit create a `<canvas>` element which you can then retrieve][get]
|
||||
//! and insert it into the DOM yourself.
|
||||
//! On the Web platform, a Winit [`Window`] is backed by a [`HTMLCanvasElement`][canvas]. Winit will
|
||||
//! create that canvas for you or you can [provide your own][with_canvas]. Then you can either let
|
||||
//! Winit [insert it into the DOM for you][insert], or [retrieve the canvas][get] and insert it
|
||||
//! yourself.
|
||||
//!
|
||||
//! Currently, there is no example code using Winit on Web, see [#3473]. For
|
||||
//! information on using Rust on WebAssembly, check out the [Rust and
|
||||
//! WebAssembly book].
|
||||
//!
|
||||
//! [with_canvas]: WindowAttributesExtWebSys::with_canvas
|
||||
//! [get]: WindowExtWebSys::canvas
|
||||
//! [#3473]: https://github.com/rust-windowing/winit/issues/3473
|
||||
//! [Rust and WebAssembly book]: https://rustwasm.github.io/book/
|
||||
//! [canvas]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement
|
||||
//! [with_canvas]: WindowAttributesExtWeb::with_canvas
|
||||
//! [get]: WindowExtWeb::canvas
|
||||
//! [insert]: WindowAttributesExtWeb::with_append
|
||||
#![cfg_attr(not(web_platform), doc = "[wasm_bindgen]: https://docs.rs/wasm-bindgen")]
|
||||
//! [Rust and WebAssembly book]: https://rustwasm.github.io/book
|
||||
//!
|
||||
//! ## CSS properties
|
||||
//!
|
||||
@@ -28,14 +27,14 @@
|
||||
//! - [`padding`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding)
|
||||
//!
|
||||
//! The following APIs can't take them into account and will therefore provide inaccurate results:
|
||||
//! - [`WindowEvent::Resized`] and [`Window::(set_)inner_size()`]
|
||||
//! - [`WindowEvent::SurfaceResized`] and [`Window::(set_)surface_size()`]
|
||||
//! - [`WindowEvent::Occluded`]
|
||||
//! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`], and
|
||||
//! [`WindowEvent::Touch`].
|
||||
//! - [`Window::set_outer_position()`]
|
||||
//!
|
||||
//! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
|
||||
//! [`Window::(set_)inner_size()`]: crate::window::Window::inner_size
|
||||
//! [`WindowEvent::SurfaceResized`]: crate::event::WindowEvent::SurfaceResized
|
||||
//! [`Window::(set_)surface_size()`]: crate::window::Window::surface_size
|
||||
//! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded
|
||||
//! [`WindowEvent::CursorMoved`]: crate::event::WindowEvent::CursorMoved
|
||||
//! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered
|
||||
@@ -43,6 +42,7 @@
|
||||
//! [`WindowEvent::Touch`]: crate::event::WindowEvent::Touch
|
||||
//! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position
|
||||
|
||||
use std::cell::Ref;
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::future::Future;
|
||||
@@ -50,30 +50,39 @@ use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(web_platform)]
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::CustomCursorSource;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
|
||||
#[cfg(web_platform)]
|
||||
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
|
||||
use crate::error::NotSupportedError;
|
||||
use crate::event::FingerId;
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::platform_impl::PlatformCustomCursorSource;
|
||||
#[cfg(web_platform)]
|
||||
use crate::platform_impl::{
|
||||
CustomCursorFuture as PlatformCustomCursorFuture,
|
||||
HasMonitorPermissionFuture as PlatformHasMonitorPermissionFuture,
|
||||
MonitorPermissionFuture as PlatformMonitorPermissionFuture,
|
||||
OrientationLockFuture as PlatformOrientationLockFuture,
|
||||
};
|
||||
use crate::window::{CustomCursor, Window, WindowAttributes};
|
||||
|
||||
#[cfg(not(web_platform))]
|
||||
#[doc(hidden)]
|
||||
pub struct HtmlCanvasElement;
|
||||
|
||||
pub trait WindowExtWebSys {
|
||||
pub trait WindowExtWeb {
|
||||
/// Only returns the canvas if called from inside the window context (the
|
||||
/// main thread).
|
||||
fn canvas(&self) -> Option<HtmlCanvasElement>;
|
||||
fn canvas(&self) -> Option<Ref<'_, HtmlCanvasElement>>;
|
||||
|
||||
/// Returns [`true`] if calling `event.preventDefault()` is enabled.
|
||||
///
|
||||
/// See [`Window::set_prevent_default()`] for more details.
|
||||
/// See [`WindowExtWeb::set_prevent_default()`] for more details.
|
||||
fn prevent_default(&self) -> bool;
|
||||
|
||||
/// Sets whether `event.preventDefault()` should be called on events on the
|
||||
@@ -85,28 +94,52 @@ pub trait WindowExtWebSys {
|
||||
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
|
||||
/// context menu with Shift+Rightclick.
|
||||
fn set_prevent_default(&self, prevent_default: bool);
|
||||
|
||||
/// Returns whether using [`CursorGrabMode::Locked`] returns raw, un-accelerated mouse input.
|
||||
///
|
||||
/// This is the same as [`ActiveEventLoopExtWeb::is_cursor_lock_raw()`], and is provided for
|
||||
/// convenience.
|
||||
///
|
||||
/// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked
|
||||
fn is_cursor_lock_raw(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WindowExtWebSys for Window {
|
||||
impl WindowExtWeb for dyn Window + '_ {
|
||||
#[inline]
|
||||
fn canvas(&self) -> Option<HtmlCanvasElement> {
|
||||
self.window.canvas()
|
||||
fn canvas(&self) -> Option<Ref<'_, HtmlCanvasElement>> {
|
||||
self.as_any()
|
||||
.downcast_ref::<crate::platform_impl::Window>()
|
||||
.expect("non Web window on Web")
|
||||
.canvas()
|
||||
}
|
||||
|
||||
fn prevent_default(&self) -> bool {
|
||||
self.window.prevent_default()
|
||||
self.as_any()
|
||||
.downcast_ref::<crate::platform_impl::Window>()
|
||||
.expect("non Web window on Web")
|
||||
.prevent_default()
|
||||
}
|
||||
|
||||
fn set_prevent_default(&self, prevent_default: bool) {
|
||||
self.window.set_prevent_default(prevent_default)
|
||||
self.as_any()
|
||||
.downcast_ref::<crate::platform_impl::Window>()
|
||||
.expect("non Web window on Web")
|
||||
.set_prevent_default(prevent_default)
|
||||
}
|
||||
|
||||
fn is_cursor_lock_raw(&self) -> bool {
|
||||
self.as_any()
|
||||
.downcast_ref::<crate::platform_impl::Window>()
|
||||
.expect("non Web window on Web")
|
||||
.is_cursor_lock_raw()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WindowAttributesExtWebSys {
|
||||
pub trait WindowAttributesExtWeb {
|
||||
/// Pass an [`HtmlCanvasElement`] to be used for this [`Window`]. If [`None`],
|
||||
/// [`WindowAttributes::default()`] will create one.
|
||||
///
|
||||
/// In any case, the canvas won't be automatically inserted into the web page.
|
||||
/// In any case, the canvas won't be automatically inserted into the Web page.
|
||||
///
|
||||
/// [`None`] by default.
|
||||
#[cfg_attr(not(web_platform), doc = "", doc = "[`HtmlCanvasElement`]: #only-available-on-wasm")]
|
||||
@@ -115,7 +148,7 @@ pub trait WindowAttributesExtWebSys {
|
||||
/// Sets whether `event.preventDefault()` should be called on events on the
|
||||
/// canvas that have side effects.
|
||||
///
|
||||
/// See [`Window::set_prevent_default()`] for more details.
|
||||
/// See [`WindowExtWeb::set_prevent_default()`] for more details.
|
||||
///
|
||||
/// Enabled by default.
|
||||
fn with_prevent_default(self, prevent_default: bool) -> Self;
|
||||
@@ -126,13 +159,13 @@ pub trait WindowAttributesExtWebSys {
|
||||
/// Enabled by default.
|
||||
fn with_focusable(self, focusable: bool) -> Self;
|
||||
|
||||
/// On window creation, append the canvas element to the web page if it isn't already.
|
||||
/// On window creation, append the canvas element to the Web page if it isn't already.
|
||||
///
|
||||
/// Disabled by default.
|
||||
fn with_append(self, append: bool) -> Self;
|
||||
}
|
||||
|
||||
impl WindowAttributesExtWebSys for WindowAttributes {
|
||||
impl WindowAttributesExtWeb for WindowAttributes {
|
||||
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
|
||||
self.platform_specific.set_canvas(canvas);
|
||||
self
|
||||
@@ -154,11 +187,8 @@ impl WindowAttributesExtWebSys for WindowAttributes {
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to the web.
|
||||
pub trait EventLoopExtWebSys {
|
||||
/// A type provided by the user that can be passed through `Event::UserEvent`.
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to the Web.
|
||||
pub trait EventLoopExtWeb {
|
||||
/// Initializes the winit event loop.
|
||||
///
|
||||
/// Unlike
|
||||
@@ -180,36 +210,9 @@ pub trait EventLoopExtWebSys {
|
||||
not(all(web_platform, target_feature = "exception-handling")),
|
||||
doc = "[`run_app()`]: EventLoop::run_app()"
|
||||
)]
|
||||
/// [^1]: `run_app()` is _not_ available on WASM when the target supports `exception-handling`.
|
||||
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, app: A);
|
||||
/// [^1]: `run_app()` is _not_ available on Wasm when the target supports `exception-handling`.
|
||||
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A);
|
||||
|
||||
/// See [`spawn_app`].
|
||||
///
|
||||
/// [`spawn_app`]: Self::spawn_app
|
||||
#[deprecated = "use EventLoopExtWebSys::spawn_app"]
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, mut app: A) {
|
||||
self.event_loop.spawn(move |event, event_loop| {
|
||||
event_loop::dispatch_event_for_app(&mut app, event_loop, event)
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
||||
{
|
||||
self.event_loop.spawn(event_handler)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ActiveEventLoopExtWebSys {
|
||||
/// Sets the strategy for [`ControlFlow::Poll`].
|
||||
///
|
||||
/// See [`PollStrategy`].
|
||||
@@ -224,30 +227,224 @@ pub trait ActiveEventLoopExtWebSys {
|
||||
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
||||
fn poll_strategy(&self) -> PollStrategy;
|
||||
|
||||
/// Sets the strategy for [`ControlFlow::WaitUntil`].
|
||||
///
|
||||
/// See [`WaitUntilStrategy`].
|
||||
///
|
||||
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);
|
||||
|
||||
/// Gets the strategy for [`ControlFlow::WaitUntil`].
|
||||
///
|
||||
/// See [`WaitUntilStrategy`].
|
||||
///
|
||||
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||
fn wait_until_strategy(&self) -> WaitUntilStrategy;
|
||||
|
||||
/// Returns if the users device has multiple screens. Useful to check before prompting the user
|
||||
/// with [`EventLoopExtWeb::request_detailed_monitor_permission()`].
|
||||
///
|
||||
/// Browsers might always return [`false`] to reduce fingerprinting.
|
||||
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError>;
|
||||
|
||||
/// Prompts the user for permission to query detailed information about available monitors. The
|
||||
/// returned [`MonitorPermissionFuture`] can be dropped without aborting the request.
|
||||
///
|
||||
/// Check [`EventLoopExtWeb::has_multiple_screens()`] before unnecessarily prompting the user
|
||||
/// for such permissions.
|
||||
///
|
||||
/// [`MonitorHandle`]s don't automatically make use of this after permission is granted. New
|
||||
/// [`MonitorHandle`]s have to be created instead.
|
||||
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture;
|
||||
|
||||
/// Returns whether the user has given permission to access detailed monitor information.
|
||||
///
|
||||
/// [`MonitorHandle`]s don't automatically make use of detailed monitor information after
|
||||
/// permission is granted. New [`MonitorHandle`]s have to be created instead.
|
||||
fn has_detailed_monitor_permission(&self) -> HasMonitorPermissionFuture;
|
||||
}
|
||||
|
||||
impl EventLoopExtWeb for EventLoop {
|
||||
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A) {
|
||||
self.event_loop.spawn_app(app);
|
||||
}
|
||||
|
||||
fn set_poll_strategy(&self, strategy: PollStrategy) {
|
||||
self.event_loop.set_poll_strategy(strategy);
|
||||
}
|
||||
|
||||
fn poll_strategy(&self) -> PollStrategy {
|
||||
self.event_loop.poll_strategy()
|
||||
}
|
||||
|
||||
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
|
||||
self.event_loop.set_wait_until_strategy(strategy);
|
||||
}
|
||||
|
||||
fn wait_until_strategy(&self) -> WaitUntilStrategy {
|
||||
self.event_loop.wait_until_strategy()
|
||||
}
|
||||
|
||||
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError> {
|
||||
self.event_loop.has_multiple_screens()
|
||||
}
|
||||
|
||||
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
|
||||
MonitorPermissionFuture(self.event_loop.request_detailed_monitor_permission())
|
||||
}
|
||||
|
||||
fn has_detailed_monitor_permission(&self) -> HasMonitorPermissionFuture {
|
||||
HasMonitorPermissionFuture(self.event_loop.has_detailed_monitor_permission())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ActiveEventLoopExtWeb {
|
||||
/// Sets the strategy for [`ControlFlow::Poll`].
|
||||
///
|
||||
/// See [`PollStrategy`].
|
||||
///
|
||||
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
||||
fn set_poll_strategy(&self, strategy: PollStrategy);
|
||||
|
||||
/// Gets the strategy for [`ControlFlow::Poll`].
|
||||
///
|
||||
/// See [`PollStrategy`].
|
||||
///
|
||||
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
||||
fn poll_strategy(&self) -> PollStrategy;
|
||||
|
||||
/// Sets the strategy for [`ControlFlow::WaitUntil`].
|
||||
///
|
||||
/// See [`WaitUntilStrategy`].
|
||||
///
|
||||
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);
|
||||
|
||||
/// Gets the strategy for [`ControlFlow::WaitUntil`].
|
||||
///
|
||||
/// See [`WaitUntilStrategy`].
|
||||
///
|
||||
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||
fn wait_until_strategy(&self) -> WaitUntilStrategy;
|
||||
|
||||
/// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the
|
||||
/// cursor has completely finished loading.
|
||||
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture;
|
||||
|
||||
/// Returns whether using [`CursorGrabMode::Locked`] returns raw, un-accelerated mouse input.
|
||||
///
|
||||
/// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked
|
||||
fn is_cursor_lock_raw(&self) -> bool;
|
||||
|
||||
/// Returns if the users device has multiple screens. Useful to check before prompting the user
|
||||
/// with [`EventLoopExtWeb::request_detailed_monitor_permission()`].
|
||||
///
|
||||
/// Browsers might always return [`false`] to reduce fingerprinting.
|
||||
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError>;
|
||||
|
||||
/// Prompts the user for permission to query detailed information about available monitors. The
|
||||
/// returned [`MonitorPermissionFuture`] can be dropped without aborting the request.
|
||||
///
|
||||
/// Check [`EventLoopExtWeb::has_multiple_screens()`] before unnecessarily prompting the user
|
||||
/// for such permissions.
|
||||
///
|
||||
/// [`MonitorHandle`]s don't automatically make use of this after permission is granted. New
|
||||
/// [`MonitorHandle`]s have to be created instead.
|
||||
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture;
|
||||
|
||||
/// Returns whether the user has given permission to access detailed monitor information.
|
||||
///
|
||||
/// [`MonitorHandle`]s don't automatically make use of detailed monitor information after
|
||||
/// permission is granted. New [`MonitorHandle`]s have to be created instead.
|
||||
fn has_detailed_monitor_permission(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtWebSys for ActiveEventLoop {
|
||||
impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ {
|
||||
#[inline]
|
||||
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture {
|
||||
self.p.create_custom_cursor_async(source)
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non Web event loop on Web");
|
||||
event_loop.create_custom_cursor_async(source)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_poll_strategy(&self, strategy: PollStrategy) {
|
||||
self.p.set_poll_strategy(strategy);
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non Web event loop on Web");
|
||||
event_loop.set_poll_strategy(strategy);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_strategy(&self) -> PollStrategy {
|
||||
self.p.poll_strategy()
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non Web event loop on Web");
|
||||
event_loop.poll_strategy()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non Web event loop on Web");
|
||||
event_loop.set_wait_until_strategy(strategy);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wait_until_strategy(&self) -> WaitUntilStrategy {
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non Web event loop on Web");
|
||||
event_loop.wait_until_strategy()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_cursor_lock_raw(&self) -> bool {
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non Web event loop on Web");
|
||||
event_loop.is_cursor_lock_raw()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError> {
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non Web event loop on Web");
|
||||
event_loop.has_multiple_screens()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non Web event loop on Web");
|
||||
MonitorPermissionFuture(event_loop.request_detailed_monitor_permission())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_detailed_monitor_permission(&self) -> bool {
|
||||
let event_loop = self
|
||||
.as_any()
|
||||
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
|
||||
.expect("non Web event loop on Web");
|
||||
event_loop.has_detailed_monitor_permission()
|
||||
}
|
||||
}
|
||||
|
||||
/// Strategy used for [`ControlFlow::Poll`][crate::event_loop::ControlFlow::Poll].
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum PollStrategy {
|
||||
/// Uses [`Window.requestIdleCallback()`] to queue the next event loop. If not available
|
||||
/// this will fallback to [`setTimeout()`].
|
||||
@@ -272,7 +469,31 @@ pub enum PollStrategy {
|
||||
Scheduler,
|
||||
}
|
||||
|
||||
pub trait CustomCursorExtWebSys {
|
||||
/// Strategy used for [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil].
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum WaitUntilStrategy {
|
||||
/// Uses the [Prioritized Task Scheduling API] to queue the next event loop. If not available
|
||||
/// this will fallback to [`setTimeout()`].
|
||||
///
|
||||
/// This strategy is commonly not affected by browser throttling unless the window is not
|
||||
/// focused.
|
||||
///
|
||||
/// This is the default strategy.
|
||||
///
|
||||
/// [Prioritized Task Scheduling API]: https://developer.mozilla.org/en-US/docs/Web/API/Prioritized_Task_Scheduling_API
|
||||
/// [`setTimeout()`]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
|
||||
#[default]
|
||||
Scheduler,
|
||||
/// Equal to [`Scheduler`][Self::Scheduler] but wakes up the event loop from a [worker].
|
||||
///
|
||||
/// This strategy is commonly not affected by browser throttling regardless of window focus.
|
||||
///
|
||||
/// [worker]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
|
||||
Worker,
|
||||
}
|
||||
|
||||
pub trait CustomCursorExtWeb {
|
||||
/// Returns if this cursor is an animation.
|
||||
fn is_animation(&self) -> bool;
|
||||
|
||||
@@ -291,7 +512,7 @@ pub trait CustomCursorExtWebSys {
|
||||
) -> Result<CustomCursorSource, BadAnimation>;
|
||||
}
|
||||
|
||||
impl CustomCursorExtWebSys for CustomCursor {
|
||||
impl CustomCursorExtWeb for CustomCursor {
|
||||
fn is_animation(&self) -> bool {
|
||||
self.inner.animation
|
||||
}
|
||||
@@ -319,7 +540,8 @@ impl CustomCursorExtWebSys for CustomCursor {
|
||||
}
|
||||
|
||||
/// An error produced when using [`CustomCursor::from_animation`] with invalid arguments.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum BadAnimation {
|
||||
/// Produced when no cursors were supplied.
|
||||
Empty,
|
||||
@@ -331,7 +553,7 @@ impl fmt::Display for BadAnimation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Empty => write!(f, "No cursors supplied"),
|
||||
Self::Animation => write!(f, "A supplied cursor is an animtion"),
|
||||
Self::Animation => write!(f, "A supplied cursor is an animation"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,11 +574,11 @@ impl Future for CustomCursorFuture {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum CustomCursorError {
|
||||
Blob,
|
||||
Decode(String),
|
||||
Animation,
|
||||
}
|
||||
|
||||
impl Display for CustomCursorError {
|
||||
@@ -364,9 +586,211 @@ impl Display for CustomCursorError {
|
||||
match self {
|
||||
Self::Blob => write!(f, "failed to create `Blob`"),
|
||||
Self::Decode(error) => write!(f, "failed to decode image: {error}"),
|
||||
Self::Animation => {
|
||||
write!(f, "found `CustomCursor` that is an animation when building an animation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for CustomCursorError {}
|
||||
|
||||
#[cfg(not(web_platform))]
|
||||
struct PlatformMonitorPermissionFuture;
|
||||
|
||||
/// Can be dropped without aborting the request for detailed monitor permissions.
|
||||
#[derive(Debug)]
|
||||
pub struct MonitorPermissionFuture(pub(crate) PlatformMonitorPermissionFuture);
|
||||
|
||||
impl Future for MonitorPermissionFuture {
|
||||
type Output = Result<(), MonitorPermissionError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
Pin::new(&mut self.0).poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum MonitorPermissionError {
|
||||
/// User has explicitly denied permission to query detailed monitor information.
|
||||
Denied,
|
||||
/// User has not decided to give permission to query detailed monitor information.
|
||||
Prompt,
|
||||
/// Browser does not support detailed monitor information.
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
impl Display for MonitorPermissionError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
MonitorPermissionError::Denied => write!(
|
||||
f,
|
||||
"User has explicitly denied permission to query detailed monitor information"
|
||||
),
|
||||
MonitorPermissionError::Prompt => write!(
|
||||
f,
|
||||
"User has not decided to give permission to query detailed monitor information"
|
||||
),
|
||||
MonitorPermissionError::Unsupported => {
|
||||
write!(f, "Browser does not support detailed monitor information")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for MonitorPermissionError {}
|
||||
|
||||
#[cfg(not(web_platform))]
|
||||
struct PlatformHasMonitorPermissionFuture;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HasMonitorPermissionFuture(PlatformHasMonitorPermissionFuture);
|
||||
|
||||
impl Future for HasMonitorPermissionFuture {
|
||||
type Output = bool;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
Pin::new(&mut self.0).poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`MonitorHandle`] that are specific to the Web.
|
||||
pub trait MonitorHandleExtWeb {
|
||||
/// Returns whether the screen is internal to the device or external.
|
||||
///
|
||||
/// External devices are generally manufactured separately from the device they are attached to
|
||||
/// and can be connected and disconnected as needed, whereas internal screens are part of
|
||||
/// the device and not intended to be disconnected.
|
||||
fn is_internal(&self) -> Option<bool>;
|
||||
|
||||
/// Returns screen orientation data for this monitor.
|
||||
fn orientation(&self) -> OrientationData;
|
||||
|
||||
/// Lock the screen orientation. The returned [`OrientationLockFuture`] can be dropped without
|
||||
/// aborting the request.
|
||||
///
|
||||
/// Will fail if another locking call is in progress.
|
||||
fn request_lock(&self, orientation: OrientationLock) -> OrientationLockFuture;
|
||||
|
||||
/// Unlock the screen orientation.
|
||||
///
|
||||
/// Will fail if a locking call is in progress.
|
||||
fn unlock(&self) -> Result<(), OrientationLockError>;
|
||||
|
||||
/// Returns whether this [`MonitorHandle`] was created using detailed monitor permissions. If
|
||||
/// [`false`] will always represent the current monitor the browser window is in instead of a
|
||||
/// specific monitor.
|
||||
///
|
||||
/// See [`ActiveEventLoopExtWeb::request_detailed_monitor_permission()`].
|
||||
fn is_detailed(&self) -> bool;
|
||||
}
|
||||
|
||||
impl MonitorHandleExtWeb for MonitorHandle {
|
||||
fn is_internal(&self) -> Option<bool> {
|
||||
self.inner.is_internal()
|
||||
}
|
||||
|
||||
fn orientation(&self) -> OrientationData {
|
||||
self.inner.orientation()
|
||||
}
|
||||
|
||||
fn request_lock(&self, orientation_lock: OrientationLock) -> OrientationLockFuture {
|
||||
OrientationLockFuture(self.inner.request_lock(orientation_lock))
|
||||
}
|
||||
|
||||
fn unlock(&self) -> Result<(), OrientationLockError> {
|
||||
self.inner.unlock()
|
||||
}
|
||||
|
||||
fn is_detailed(&self) -> bool {
|
||||
self.inner.is_detailed()
|
||||
}
|
||||
}
|
||||
|
||||
/// Screen orientation data.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct OrientationData {
|
||||
/// The orientation.
|
||||
pub orientation: Orientation,
|
||||
/// [`true`] if the [`orientation`](Self::orientation) is flipped upside down.
|
||||
pub flipped: bool,
|
||||
/// The most natural orientation for the screen. Computer monitors are commonly naturally
|
||||
/// landscape mode, while mobile phones are commonly naturally portrait mode.
|
||||
pub natural: Orientation,
|
||||
}
|
||||
|
||||
/// Screen orientation.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum Orientation {
|
||||
/// The screen's aspect ratio has a width greater than the height.
|
||||
Landscape,
|
||||
/// The screen's aspect ratio has a height greater than the width.
|
||||
Portrait,
|
||||
}
|
||||
|
||||
/// Screen orientation lock options. Reoresents which orientations a user can use.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum OrientationLock {
|
||||
/// User is free to use any orientation.
|
||||
Any,
|
||||
/// User is locked to the most upright natural orientation for the screen. Computer monitors
|
||||
/// are commonly naturally landscape mode, while mobile phones are commonly
|
||||
/// naturally portrait mode.
|
||||
Natural,
|
||||
/// User is locked to landscape mode.
|
||||
Landscape {
|
||||
/// - [`None`]: User is locked to both upright or upside down landscape mode.
|
||||
/// - [`true`]: User is locked to upright landscape mode.
|
||||
/// - [`false`]: User is locked to upside down landscape mode.
|
||||
flipped: Option<bool>,
|
||||
},
|
||||
/// User is locked to portrait mode.
|
||||
Portrait {
|
||||
/// - [`None`]: User is locked to both upright or upside down portrait mode.
|
||||
/// - [`true`]: User is locked to upright portrait mode.
|
||||
/// - [`false`]: User is locked to upside down portrait mode.
|
||||
flipped: Option<bool>,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(not(web_platform))]
|
||||
struct PlatformOrientationLockFuture;
|
||||
|
||||
/// Can be dropped without aborting the request to lock the screen.
|
||||
#[derive(Debug)]
|
||||
pub struct OrientationLockFuture(PlatformOrientationLockFuture);
|
||||
|
||||
impl Future for OrientationLockFuture {
|
||||
type Output = Result<(), OrientationLockError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
Pin::new(&mut self.0).poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum OrientationLockError {
|
||||
Unsupported,
|
||||
Busy,
|
||||
}
|
||||
|
||||
impl Display for OrientationLockError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Unsupported => write!(f, "Locking the screen orientation is not supported"),
|
||||
Self::Busy => write!(f, "Another locking call is in progress"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for OrientationLockError {}
|
||||
|
||||
/// Additional methods on [`FingerId`] that are specific to Web.
|
||||
pub trait FingerIdExtWeb {
|
||||
/// Indicates if the finger represents the first contact in a multi-touch interaction.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_primary(self) -> bool;
|
||||
}
|
||||
|
||||
impl FingerIdExtWeb for FingerId {
|
||||
fn is_primary(self) -> bool {
|
||||
self.0.is_primary()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
//! tested regularly.
|
||||
use std::borrow::Borrow;
|
||||
use std::ffi::c_void;
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::event::DeviceId;
|
||||
use crate::event::{DeviceId, FingerId};
|
||||
use crate::event_loop::EventLoopBuilder;
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::window::{BadIcon, Icon, Window, WindowAttributes};
|
||||
@@ -25,6 +29,7 @@ pub type HMONITOR = isize;
|
||||
///
|
||||
/// [`DWM_SYSTEMBACKDROP_TYPE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum BackdropType {
|
||||
/// Corresponds to `DWMSBT_AUTO`.
|
||||
///
|
||||
@@ -54,6 +59,7 @@ pub enum BackdropType {
|
||||
/// Describes a color used by Windows
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Color(u32);
|
||||
|
||||
impl Color {
|
||||
@@ -82,6 +88,7 @@ impl Default for Color {
|
||||
/// [`DWM_WINDOW_CORNER_PREFERENCE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum CornerPreference {
|
||||
/// Corresponds to `DWMWCP_DEFAULT`.
|
||||
///
|
||||
@@ -108,51 +115,26 @@ pub enum CornerPreference {
|
||||
/// A wrapper around a [`Window`] that ignores thread-specific window handle limitations.
|
||||
///
|
||||
/// See [`WindowBorrowExtWindows::any_thread`] for more information.
|
||||
#[derive(Debug)]
|
||||
pub struct AnyThread<W>(W);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnyThread<W: Window>(W);
|
||||
|
||||
impl<W: Borrow<Window>> AnyThread<W> {
|
||||
impl<W: Window> AnyThread<W> {
|
||||
/// Get a reference to the inner window.
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> &Window {
|
||||
self.0.borrow()
|
||||
}
|
||||
|
||||
/// Get a reference to the inner object.
|
||||
#[inline]
|
||||
pub fn inner(&self) -> &W {
|
||||
pub fn get_ref(&self) -> &dyn Window {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Unwrap and get the inner window.
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> W {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Borrow<Window>> AsRef<Window> for AnyThread<W> {
|
||||
fn as_ref(&self) -> &Window {
|
||||
self.get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Borrow<Window>> Borrow<Window> for AnyThread<W> {
|
||||
fn borrow(&self) -> &Window {
|
||||
self.get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Borrow<Window>> std::ops::Deref for AnyThread<W> {
|
||||
type Target = Window;
|
||||
|
||||
impl<W: Window> Deref for AnyThread<W> {
|
||||
type Target = W;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.get_ref()
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl<W: Borrow<Window>> rwh_06::HasWindowHandle for AnyThread<W> {
|
||||
impl<W: Window> rwh_06::HasWindowHandle for AnyThread<W> {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
// SAFETY: The top level user has asserted this is only used safely.
|
||||
unsafe { self.get_ref().window_handle_any_thread() }
|
||||
@@ -183,11 +165,11 @@ pub trait EventLoopBuilderExtWindows {
|
||||
/// Disable process-wide DPI awareness.
|
||||
///
|
||||
/// ```
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// use winit::event_loop::EventLoop;
|
||||
/// #[cfg(target_os = "windows")]
|
||||
/// use winit::platform::windows::EventLoopBuilderExtWindows;
|
||||
///
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// let mut builder = EventLoop::builder();
|
||||
/// #[cfg(target_os = "windows")]
|
||||
/// builder.with_dpi_aware(false);
|
||||
/// # if false { // We can't test this part
|
||||
@@ -203,11 +185,11 @@ pub trait EventLoopBuilderExtWindows {
|
||||
///
|
||||
/// ```
|
||||
/// # use windows_sys::Win32::UI::WindowsAndMessaging::{ACCEL, CreateAcceleratorTableW, TranslateAcceleratorW, DispatchMessageW, TranslateMessage, MSG};
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// use winit::event_loop::EventLoop;
|
||||
/// #[cfg(target_os = "windows")]
|
||||
/// use winit::platform::windows::EventLoopBuilderExtWindows;
|
||||
///
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// let mut builder = EventLoop::builder();
|
||||
/// #[cfg(target_os = "windows")]
|
||||
/// builder.with_msg_hook(|msg|{
|
||||
/// let msg = msg as *const MSG;
|
||||
@@ -227,7 +209,7 @@ pub trait EventLoopBuilderExtWindows {
|
||||
F: FnMut(*const c_void) -> bool + 'static;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtWindows for EventLoopBuilder<T> {
|
||||
impl EventLoopBuilderExtWindows for EventLoopBuilder {
|
||||
#[inline]
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
|
||||
self.platform_specific.any_thread = any_thread;
|
||||
@@ -335,8 +317,9 @@ pub trait WindowExtWindows {
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use winit::window::Window;
|
||||
/// # fn scope(window: Window) {
|
||||
/// # fn scope(window: Box<dyn Window>) {
|
||||
/// use std::thread;
|
||||
///
|
||||
/// use winit::platform::windows::WindowExtWindows;
|
||||
/// use winit::raw_window_handle::HasWindowHandle;
|
||||
///
|
||||
@@ -358,35 +341,41 @@ pub trait WindowExtWindows {
|
||||
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>;
|
||||
}
|
||||
|
||||
impl WindowExtWindows for Window {
|
||||
impl WindowExtWindows for dyn Window + '_ {
|
||||
#[inline]
|
||||
fn set_enable(&self, enabled: bool) {
|
||||
self.window.set_enable(enabled)
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_enable(enabled)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
||||
self.window.set_taskbar_icon(taskbar_icon)
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_taskbar_icon(taskbar_icon)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_skip_taskbar(&self, skip: bool) {
|
||||
self.window.set_skip_taskbar(skip)
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_skip_taskbar(skip)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_undecorated_shadow(&self, shadow: bool) {
|
||||
self.window.set_undecorated_shadow(shadow)
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_undecorated_shadow(shadow)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_system_backdrop(&self, backdrop_type: BackdropType) {
|
||||
self.window.set_system_backdrop(backdrop_type)
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_system_backdrop(backdrop_type)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_border_color(&self, color: Option<Color>) {
|
||||
self.window.set_border_color(color.unwrap_or(Color::NONE))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_border_color(color.unwrap_or(Color::NONE))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -394,25 +383,29 @@ impl WindowExtWindows for Window {
|
||||
// The windows docs don't mention NONE as a valid options but it works in practice and is
|
||||
// useful to circumvent the Windows option "Show accent color on title bars and
|
||||
// window borders"
|
||||
self.window.set_title_background_color(color.unwrap_or(Color::NONE))
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_title_background_color(color.unwrap_or(Color::NONE))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_title_text_color(&self, color: Color) {
|
||||
self.window.set_title_text_color(color)
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_title_text_color(color)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_corner_preference(&self, preference: CornerPreference) {
|
||||
self.window.set_corner_preference(preference)
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.set_corner_preference(preference)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
unsafe fn window_handle_any_thread(
|
||||
&self,
|
||||
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
unsafe {
|
||||
let handle = self.window.rwh_06_no_thread_check()?;
|
||||
let handle = window.rwh_06_no_thread_check()?;
|
||||
|
||||
// SAFETY: The handle is valid in this context.
|
||||
Ok(rwh_06::WindowHandle::borrow_raw(handle))
|
||||
@@ -423,7 +416,7 @@ impl WindowExtWindows for Window {
|
||||
/// Additional methods for anything that dereference to [`Window`].
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub trait WindowBorrowExtWindows: Borrow<Window> + Sized {
|
||||
pub trait WindowBorrowExtWindows: Borrow<dyn Window> + Sized {
|
||||
/// Create an object that allows accessing the inner window handle in a thread-unsafe way.
|
||||
///
|
||||
/// It is possible to call [`window_handle_any_thread`] to get around Windows's thread
|
||||
@@ -439,15 +432,26 @@ pub trait WindowBorrowExtWindows: Borrow<Window> + Sized {
|
||||
/// It is the responsibility of the user to only pass the window handle into thread-safe
|
||||
/// Win32 APIs.
|
||||
///
|
||||
/// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread
|
||||
/// [`Window`]: crate::window::Window
|
||||
/// [`HasWindowHandle`]: rwh_06::HasWindowHandle
|
||||
unsafe fn any_thread(self) -> AnyThread<Self> {
|
||||
#[cfg_attr(
|
||||
feature = "rwh_06",
|
||||
doc = "[`HasWindowHandle`]: rwh_06::HasWindowHandle",
|
||||
doc = "[`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(feature = "rwh_06"),
|
||||
doc = "[`HasWindowHandle`]: #only-available-with-rwh_06",
|
||||
doc = "[`window_handle_any_thread`]: #only-available-with-rwh_06"
|
||||
)]
|
||||
unsafe fn any_thread(self) -> AnyThread<Self>
|
||||
where
|
||||
Self: Window,
|
||||
{
|
||||
AnyThread(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Borrow<Window> + Sized> WindowBorrowExtWindows for W {}
|
||||
impl<W: Borrow<dyn Window> + Sized> WindowBorrowExtWindows for W {}
|
||||
|
||||
/// Additional methods on `WindowAttributes` that are specific to Windows.
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
@@ -659,6 +663,20 @@ impl DeviceIdExtWindows for DeviceId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `FingerId` that are specific to Windows.
|
||||
pub trait FingerIdExtWindows {
|
||||
/// Indicates if the finger represents the first contact in a multi-touch interaction.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_primary(self) -> bool;
|
||||
}
|
||||
|
||||
impl FingerIdExtWindows for FingerId {
|
||||
#[inline]
|
||||
fn is_primary(self) -> bool {
|
||||
self.0.is_primary()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Icon` that are specific to Windows.
|
||||
pub trait IconExtWindows: Sized {
|
||||
/// Create an icon from a file path.
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::dpi::Size;
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::window::{Window, WindowAttributes};
|
||||
|
||||
use crate::dpi::Size;
|
||||
use crate::window::{Window as CoreWindow, WindowAttributes};
|
||||
|
||||
/// X window type. Maps directly to
|
||||
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
|
||||
@@ -92,10 +91,10 @@ pub trait ActiveEventLoopExtX11 {
|
||||
fn is_x11(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ActiveEventLoopExtX11 for ActiveEventLoop {
|
||||
impl ActiveEventLoopExtX11 for dyn ActiveEventLoop + '_ {
|
||||
#[inline]
|
||||
fn is_x11(&self) -> bool {
|
||||
!self.p.is_wayland()
|
||||
self.as_any().downcast_ref::<crate::platform_impl::x11::ActiveEventLoop>().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +104,7 @@ pub trait EventLoopExtX11 {
|
||||
fn is_x11(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopExtX11 for EventLoop<T> {
|
||||
impl EventLoopExtX11 for EventLoop {
|
||||
#[inline]
|
||||
fn is_x11(&self) -> bool {
|
||||
!self.event_loop.is_wayland()
|
||||
@@ -124,7 +123,7 @@ pub trait EventLoopBuilderExtX11 {
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> {
|
||||
impl EventLoopBuilderExtX11 for EventLoopBuilder {
|
||||
#[inline]
|
||||
fn with_x11(&mut self) -> &mut Self {
|
||||
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X);
|
||||
@@ -139,9 +138,11 @@ impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> {
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to X11.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub trait WindowExtX11 {}
|
||||
|
||||
impl WindowExtX11 for Window {}
|
||||
impl WindowExtX11 for dyn CoreWindow {}
|
||||
|
||||
/// Additional methods on [`WindowAttributes`] that are specific to X11.
|
||||
pub trait WindowAttributesExtX11 {
|
||||
@@ -170,13 +171,13 @@ pub trait WindowAttributesExtX11 {
|
||||
///
|
||||
/// ```
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::window::Window;
|
||||
/// # use winit::window::{Window, WindowAttributes};
|
||||
/// # use winit::platform::x11::WindowAttributesExtX11;
|
||||
/// // Specify the size in logical dimensions like this:
|
||||
/// Window::default_attributes().with_base_size(LogicalSize::new(400.0, 200.0));
|
||||
/// WindowAttributes::default().with_base_size(LogicalSize::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the size in physical dimensions like this:
|
||||
/// Window::default_attributes().with_base_size(PhysicalSize::new(400, 200));
|
||||
/// WindowAttributes::default().with_base_size(PhysicalSize::new(400, 200));
|
||||
/// ```
|
||||
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
|
||||
|
||||
@@ -185,12 +186,12 @@ pub trait WindowAttributesExtX11 {
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winit::window::Window;
|
||||
/// use winit::window::{Window, WindowAttributes};
|
||||
/// use winit::event_loop::ActiveEventLoop;
|
||||
/// use winit::platform::x11::{XWindow, WindowAttributesExtX11};
|
||||
/// # fn create_window(event_loop: &ActiveEventLoop) -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// # fn create_window(event_loop: &dyn ActiveEventLoop) -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?;
|
||||
/// let window_attributes = Window::default_attributes().with_embed_parent_window(parent_window_id);
|
||||
/// let window_attributes = WindowAttributes::default().with_embed_parent_window(parent_window_id);
|
||||
/// let window = event_loop.create_window(window_attributes)?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,13 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::app_state::AppState;
|
||||
use super::DEVICE_ID;
|
||||
use crate::event::{DeviceEvent, ElementState};
|
||||
|
||||
declare_class!(
|
||||
@@ -37,15 +40,15 @@ declare_class!(
|
||||
key_window.sendEvent(event);
|
||||
}
|
||||
} else {
|
||||
let delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
|
||||
maybe_dispatch_device_event(&delegate, event);
|
||||
let app_state = AppState::get(MainThreadMarker::from(self));
|
||||
maybe_dispatch_device_event(&app_state, event);
|
||||
unsafe { msg_send![super(self), sendEvent: event] }
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
fn maybe_dispatch_device_event(delegate: &ApplicationDelegate, event: &NSEvent) {
|
||||
fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||
let event_type = unsafe { event.r#type() };
|
||||
#[allow(non_upper_case_globals)]
|
||||
match event_type {
|
||||
@@ -56,30 +59,30 @@ fn maybe_dispatch_device_event(delegate: &ApplicationDelegate, event: &NSEvent)
|
||||
let delta_x = unsafe { event.deltaX() } as f64;
|
||||
let delta_y = unsafe { event.deltaY() } as f64;
|
||||
|
||||
if delta_x != 0.0 {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Motion { axis: 0, value: delta_x });
|
||||
}
|
||||
|
||||
if delta_y != 0.0 {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Motion { axis: 1, value: delta_y })
|
||||
}
|
||||
|
||||
if delta_x != 0.0 || delta_y != 0.0 {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::MouseMotion {
|
||||
delta: (delta_x, delta_y),
|
||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseMotion {
|
||||
delta: (delta_x, delta_y),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Button {
|
||||
button: unsafe { event.buttonNumber() } as u32,
|
||||
state: ElementState::Pressed,
|
||||
let button = unsafe { event.buttonNumber() } as u32;
|
||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
|
||||
button,
|
||||
state: ElementState::Pressed,
|
||||
});
|
||||
});
|
||||
},
|
||||
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Button {
|
||||
button: unsafe { event.buttonNumber() } as u32,
|
||||
state: ElementState::Released,
|
||||
let button = unsafe { event.buttonNumber() } as u32;
|
||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
|
||||
button,
|
||||
state: ElementState::Released,
|
||||
});
|
||||
});
|
||||
},
|
||||
_ => (),
|
||||
@@ -1,27 +1,30 @@
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::{Cell, OnceCell, RefCell};
|
||||
use std::mem;
|
||||
use std::rc::Weak;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use objc2::rc::Retained;
|
||||
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotification};
|
||||
|
||||
use super::event_handler::EventHandler;
|
||||
use super::super::event_handler::EventHandler;
|
||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
||||
use super::observer::{EventLoopWaker, RunLoop};
|
||||
use super::{menu, WindowId, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, Event, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
||||
use super::{menu, WindowId};
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::event::{StartCause, WindowEvent};
|
||||
use crate::event_loop::ControlFlow;
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct AppState {
|
||||
mtm: MainThreadMarker,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
run_loop: RunLoop,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
event_handler: EventHandler,
|
||||
stop_on_launch: Cell<bool>,
|
||||
stop_before_wait: Cell<bool>,
|
||||
@@ -42,44 +45,34 @@ pub(super) struct AppState {
|
||||
// as such should be careful to not add fields that, in turn, strongly reference those.
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ApplicationDelegate;
|
||||
// TODO(madsmtm): Use `MainThreadBound` once that is possible in `static`s.
|
||||
struct StaticMainThreadBound<T>(T);
|
||||
|
||||
unsafe impl ClassType for ApplicationDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitApplicationDelegate";
|
||||
impl<T> StaticMainThreadBound<T> {
|
||||
const fn get(&self, _mtm: MainThreadMarker) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DeclaredClass for ApplicationDelegate {
|
||||
type Ivars = AppState;
|
||||
}
|
||||
unsafe impl<T> Send for StaticMainThreadBound<T> {}
|
||||
unsafe impl<T> Sync for StaticMainThreadBound<T> {}
|
||||
|
||||
unsafe impl NSObjectProtocol for ApplicationDelegate {}
|
||||
// SAFETY: Creating `StaticMainThreadBound` in a `const` context, where there is no concept of the
|
||||
// main thread.
|
||||
static GLOBAL: StaticMainThreadBound<OnceCell<Rc<AppState>>> =
|
||||
StaticMainThreadBound(OnceCell::new());
|
||||
|
||||
unsafe impl NSApplicationDelegate for ApplicationDelegate {
|
||||
#[method(applicationDidFinishLaunching:)]
|
||||
fn app_did_finish_launching(&self, notification: &NSNotification) {
|
||||
self.did_finish_launching(notification)
|
||||
}
|
||||
|
||||
#[method(applicationWillTerminate:)]
|
||||
fn app_will_terminate(&self, notification: &NSNotification) {
|
||||
self.will_terminate(notification)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl ApplicationDelegate {
|
||||
pub(super) fn new(
|
||||
impl AppState {
|
||||
pub(super) fn setup_global(
|
||||
mtm: MainThreadMarker,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) -> Retained<Self> {
|
||||
let this = mtm.alloc().set_ivars(AppState {
|
||||
) -> Rc<Self> {
|
||||
let this = Rc::new(AppState {
|
||||
mtm,
|
||||
activation_policy,
|
||||
proxy_wake_up: Arc::new(AtomicBool::new(false)),
|
||||
default_menu,
|
||||
activate_ignoring_other_apps,
|
||||
run_loop: RunLoop::main(mtm),
|
||||
@@ -97,33 +90,41 @@ impl ApplicationDelegate {
|
||||
wait_timeout: Cell::new(None),
|
||||
pending_redraw: RefCell::new(vec![]),
|
||||
});
|
||||
unsafe { msg_send_id![super(this), init] }
|
||||
|
||||
GLOBAL.get(mtm).set(this.clone()).expect("application state can only be set once");
|
||||
this
|
||||
}
|
||||
|
||||
// NOTE: This will, globally, only be run once, no matter how many
|
||||
// `EventLoop`s the user creates.
|
||||
fn did_finish_launching(&self, _notification: &NSNotification) {
|
||||
trace_scope!("applicationDidFinishLaunching:");
|
||||
self.ivars().is_launched.set(true);
|
||||
pub fn get(mtm: MainThreadMarker) -> Rc<Self> {
|
||||
GLOBAL
|
||||
.get(mtm)
|
||||
.get()
|
||||
.expect("tried to get application state before it was registered")
|
||||
.clone()
|
||||
}
|
||||
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
// NOTE: This notification will, globally, only be emitted once,
|
||||
// no matter how many `EventLoop`s the user creates.
|
||||
pub fn did_finish_launching(self: &Rc<Self>, _notification: &NSNotification) {
|
||||
trace_scope!("NSApplicationDidFinishLaunchingNotification");
|
||||
self.is_launched.set(true);
|
||||
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
app.setActivationPolicy(self.ivars().activation_policy);
|
||||
app.setActivationPolicy(self.activation_policy);
|
||||
|
||||
window_activation_hack(&app);
|
||||
#[allow(deprecated)]
|
||||
app.activateIgnoringOtherApps(self.ivars().activate_ignoring_other_apps);
|
||||
app.activateIgnoringOtherApps(self.activate_ignoring_other_apps);
|
||||
|
||||
if self.ivars().default_menu {
|
||||
if self.default_menu {
|
||||
// The menubar initialization should be before the `NewEvents` event, to allow
|
||||
// overriding of the default menu even if it's created
|
||||
menu::initialize(&app);
|
||||
}
|
||||
|
||||
self.ivars().waker.borrow_mut().start();
|
||||
self.waker.borrow_mut().start();
|
||||
|
||||
self.set_is_running(true);
|
||||
self.dispatch_init_events();
|
||||
@@ -133,74 +134,68 @@ impl ApplicationDelegate {
|
||||
//
|
||||
// In this case we still want to consider Winit's `EventLoop` to be "running",
|
||||
// so we call `start_running()` above.
|
||||
if self.ivars().stop_on_launch.get() {
|
||||
if self.stop_on_launch.get() {
|
||||
// NOTE: the original idea had been to only stop the underlying `RunLoop`
|
||||
// for the app but that didn't work as expected (`-[NSApplication run]`
|
||||
// effectively ignored the attempt to stop the RunLoop and re-started it).
|
||||
//
|
||||
// So we return from `pump_events` by stopping the application.
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
}
|
||||
|
||||
fn will_terminate(&self, _notification: &NSNotification) {
|
||||
trace_scope!("applicationWillTerminate:");
|
||||
pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) {
|
||||
trace_scope!("NSApplicationWillTerminateNotification");
|
||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||
self.internal_exit();
|
||||
}
|
||||
|
||||
pub fn get(mtm: MainThreadMarker) -> Retained<Self> {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
let delegate =
|
||||
unsafe { app.delegate() }.expect("a delegate was not configured on the application");
|
||||
if delegate.is_kind_of::<Self>() {
|
||||
// SAFETY: Just checked that the delegate is an instance of `ApplicationDelegate`
|
||||
unsafe { Retained::cast(delegate) }
|
||||
} else {
|
||||
panic!("tried to get a delegate that was not the one Winit has registered")
|
||||
}
|
||||
}
|
||||
|
||||
/// Place the event handler in the application delegate for the duration
|
||||
/// Place the event handler in the application state for the duration
|
||||
/// of the given closure.
|
||||
pub fn set_event_handler<R>(
|
||||
&self,
|
||||
handler: impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop),
|
||||
handler: &mut dyn ApplicationHandler,
|
||||
closure: impl FnOnce() -> R,
|
||||
) -> R {
|
||||
self.ivars().event_handler.set(handler, closure)
|
||||
self.event_handler.set(handler, closure)
|
||||
}
|
||||
|
||||
pub fn proxy_wake_up(&self) -> Arc<AtomicBool> {
|
||||
self.proxy_wake_up.clone()
|
||||
}
|
||||
|
||||
/// If `pump_events` is called to progress the event loop then we
|
||||
/// bootstrap the event loop via `-[NSApplication run]` but will use
|
||||
/// `CFRunLoopRunInMode` for subsequent calls to `pump_events`.
|
||||
pub fn set_stop_on_launch(&self) {
|
||||
self.ivars().stop_on_launch.set(true);
|
||||
self.stop_on_launch.set(true);
|
||||
}
|
||||
|
||||
pub fn set_stop_before_wait(&self, value: bool) {
|
||||
self.ivars().stop_before_wait.set(value)
|
||||
self.stop_before_wait.set(value)
|
||||
}
|
||||
|
||||
pub fn set_stop_after_wait(&self, value: bool) {
|
||||
self.ivars().stop_after_wait.set(value)
|
||||
self.stop_after_wait.set(value)
|
||||
}
|
||||
|
||||
pub fn set_stop_on_redraw(&self, value: bool) {
|
||||
self.ivars().stop_on_redraw.set(value)
|
||||
self.stop_on_redraw.set(value)
|
||||
}
|
||||
|
||||
pub fn set_wait_timeout(&self, value: Option<Instant>) {
|
||||
self.ivars().wait_timeout.set(value)
|
||||
self.wait_timeout.set(value)
|
||||
}
|
||||
|
||||
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits.
|
||||
///
|
||||
/// NOTE: that if the `NSApplication` has been launched then that state is preserved,
|
||||
/// and we won't need to re-launch the app if subsequent EventLoops are run.
|
||||
pub fn internal_exit(&self) {
|
||||
self.handle_event(Event::LoopExiting);
|
||||
pub fn internal_exit(self: &Rc<Self>) {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.exiting(event_loop);
|
||||
});
|
||||
|
||||
self.set_is_running(false);
|
||||
self.set_stop_on_redraw(false);
|
||||
@@ -210,125 +205,119 @@ impl ApplicationDelegate {
|
||||
}
|
||||
|
||||
pub fn is_launched(&self) -> bool {
|
||||
self.ivars().is_launched.get()
|
||||
self.is_launched.get()
|
||||
}
|
||||
|
||||
pub fn set_is_running(&self, value: bool) {
|
||||
self.ivars().is_running.set(value)
|
||||
self.is_running.set(value)
|
||||
}
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.ivars().is_running.get()
|
||||
self.is_running.get()
|
||||
}
|
||||
|
||||
pub fn exit(&self) {
|
||||
self.ivars().exit.set(true)
|
||||
self.exit.set(true)
|
||||
}
|
||||
|
||||
pub fn clear_exit(&self) {
|
||||
self.ivars().exit.set(false)
|
||||
self.exit.set(false)
|
||||
}
|
||||
|
||||
pub fn exiting(&self) -> bool {
|
||||
self.ivars().exit.get()
|
||||
self.exit.get()
|
||||
}
|
||||
|
||||
pub fn set_control_flow(&self, value: ControlFlow) {
|
||||
self.ivars().control_flow.set(value)
|
||||
self.control_flow.set(value)
|
||||
}
|
||||
|
||||
pub fn control_flow(&self) -> ControlFlow {
|
||||
self.ivars().control_flow.get()
|
||||
self.control_flow.get()
|
||||
}
|
||||
|
||||
pub fn maybe_queue_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||
self.maybe_queue_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||
}
|
||||
|
||||
pub fn handle_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||
self.handle_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||
}
|
||||
|
||||
pub fn maybe_queue_device_event(&self, event: DeviceEvent) {
|
||||
self.maybe_queue_event(Event::DeviceEvent { device_id: DEVICE_ID, event });
|
||||
}
|
||||
|
||||
pub fn handle_redraw(&self, window_id: WindowId) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
pub fn handle_redraw(self: &Rc<Self>, window_id: WindowId) {
|
||||
// Redraw request might come out of order from the OS.
|
||||
// -> Don't go back into the event handler when our callstack originates from there
|
||||
if !self.ivars().event_handler.in_use() {
|
||||
self.handle_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
if !self.event_handler.in_use() {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
|
||||
});
|
||||
|
||||
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
|
||||
// events as a way to ensure that `pump_events` can't block an external loop
|
||||
// indefinitely
|
||||
if self.ivars().stop_on_redraw.get() {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
if self.stop_on_redraw.get() {
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_redraw(&self, window_id: WindowId) {
|
||||
let mut pending_redraw = self.ivars().pending_redraw.borrow_mut();
|
||||
let mut pending_redraw = self.pending_redraw.borrow_mut();
|
||||
if !pending_redraw.contains(&window_id) {
|
||||
pending_redraw.push(window_id);
|
||||
}
|
||||
self.ivars().run_loop.wakeup();
|
||||
self.run_loop.wakeup();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn maybe_queue_event(&self, event: Event<HandlePendingUserEvents>) {
|
||||
pub fn maybe_queue_with_handler(
|
||||
self: &Rc<Self>,
|
||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop) + 'static,
|
||||
) {
|
||||
// Most programmer actions in AppKit (e.g. change window fullscreen, set focused, etc.)
|
||||
// result in an event being queued, and applied at a later point.
|
||||
//
|
||||
// However, it is not documented which actions do this, and which ones are done immediately,
|
||||
// so to make sure that we don't encounter re-entrancy issues, we first check if we're
|
||||
// currently handling another event, and if we are, we queue the event instead.
|
||||
if !self.ivars().event_handler.in_use() {
|
||||
self.handle_event(event);
|
||||
if !self.event_handler.in_use() {
|
||||
self.with_handler(callback);
|
||||
} else {
|
||||
tracing::debug!(?event, "had to queue event since another is currently being handled");
|
||||
let this = self.retain();
|
||||
self.ivars().run_loop.queue_closure(move || this.handle_event(event));
|
||||
tracing::debug!("had to queue event since another is currently being handled");
|
||||
let this = Rc::clone(self);
|
||||
self.run_loop.queue_closure(move || {
|
||||
this.with_handler(callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn handle_event(&self, event: Event<HandlePendingUserEvents>) {
|
||||
self.ivars().event_handler.handle_event(event, &ActiveEventLoop::new_root(self.retain()))
|
||||
fn with_handler(
|
||||
self: &Rc<Self>,
|
||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop),
|
||||
) {
|
||||
let event_loop = ActiveEventLoop { app_state: Rc::clone(self), mtm: self.mtm };
|
||||
self.event_handler.handle(|app| callback(app, &event_loop));
|
||||
}
|
||||
|
||||
/// dispatch `NewEvents(Init)` + `Resumed`
|
||||
pub fn dispatch_init_events(&self) {
|
||||
self.handle_event(Event::NewEvents(StartCause::Init));
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though macOS
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
self.handle_event(Event::Resumed);
|
||||
pub fn dispatch_init_events(self: &Rc<Self>) {
|
||||
self.with_handler(|app, event_loop| app.new_events(event_loop, StartCause::Init));
|
||||
// NB: For consistency all platforms must call `can_create_surfaces` even though macOS
|
||||
// applications don't themselves have a formal surface destroy/create lifecycle.
|
||||
self.with_handler(|app, event_loop| app.can_create_surfaces(event_loop));
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver after finishing waiting for new events
|
||||
pub fn wakeup(&self, panic_info: Weak<PanicInfo>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
pub fn wakeup(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
|
||||
let panic_info = panic_info
|
||||
.upgrade()
|
||||
.expect("The panic info must exist here. This failure indicates a developer error.");
|
||||
|
||||
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
|
||||
if panic_info.is_panicking() || !self.ivars().event_handler.ready() || !self.is_running() {
|
||||
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.ivars().stop_after_wait.get() {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
if self.stop_after_wait.get() {
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
|
||||
let start = self.ivars().start_time.get().unwrap();
|
||||
let start = self.start_time.get().unwrap();
|
||||
let cause = match self.control_flow() {
|
||||
ControlFlow::Poll => StartCause::Poll,
|
||||
ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
|
||||
@@ -341,12 +330,11 @@ impl ApplicationDelegate {
|
||||
},
|
||||
};
|
||||
|
||||
self.handle_event(Event::NewEvents(cause));
|
||||
self.with_handler(|app, event_loop| app.new_events(event_loop, cause));
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver before waiting for new events
|
||||
pub fn cleared(&self, panic_info: Weak<PanicInfo>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
pub fn cleared(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
|
||||
let panic_info = panic_info
|
||||
.upgrade()
|
||||
.expect("The panic info must exist here. This failure indicates a developer error.");
|
||||
@@ -354,71 +342,47 @@ impl ApplicationDelegate {
|
||||
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
|
||||
// XXX: how does it make sense that `event_handler.ready()` can ever return `false` here if
|
||||
// we're about to return to the `CFRunLoop` to poll for new events?
|
||||
if panic_info.is_panicking() || !self.ivars().event_handler.ready() || !self.is_running() {
|
||||
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
||||
if self.proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
|
||||
self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
|
||||
}
|
||||
|
||||
let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut());
|
||||
let redraw = mem::take(&mut *self.pending_redraw.borrow_mut());
|
||||
for window_id in redraw {
|
||||
self.handle_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
|
||||
});
|
||||
}
|
||||
|
||||
self.handle_event(Event::AboutToWait);
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.about_to_wait(event_loop);
|
||||
});
|
||||
|
||||
if self.exiting() {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
|
||||
if self.ivars().stop_before_wait.get() {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
if self.stop_before_wait.get() {
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
self.ivars().start_time.set(Some(Instant::now()));
|
||||
let wait_timeout = self.ivars().wait_timeout.get(); // configured by pump_events
|
||||
self.start_time.set(Some(Instant::now()));
|
||||
let wait_timeout = self.wait_timeout.get(); // configured by pump_events
|
||||
let app_timeout = match self.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Instant::now()),
|
||||
ControlFlow::WaitUntil(instant) => Some(instant),
|
||||
};
|
||||
self.ivars().waker.borrow_mut().start_at(min_timeout(wait_timeout, app_timeout));
|
||||
self.waker.borrow_mut().start_at(min_timeout(wait_timeout, app_timeout));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct HandlePendingUserEvents;
|
||||
|
||||
/// Returns the minimum `Option<Instant>`, taking into account that `None`
|
||||
/// equates to an infinite timeout, not a zero timeout (so can't just use
|
||||
/// `Option::min`)
|
||||
fn min_timeout(a: Option<Instant>, b: Option<Instant>) -> Option<Instant> {
|
||||
a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
|
||||
}
|
||||
|
||||
/// A hack to make activation of multiple windows work when creating them before
|
||||
/// `applicationDidFinishLaunching:` / `Event::Event::NewEvents(StartCause::Init)`.
|
||||
///
|
||||
/// Alternative to this would be the user calling `window.set_visible(true)` in
|
||||
/// `StartCause::Init`.
|
||||
///
|
||||
/// If this becomes too bothersome to maintain, it can probably be removed
|
||||
/// without too much damage.
|
||||
fn window_activation_hack(app: &NSApplication) {
|
||||
// TODO: Proper ordering of the windows
|
||||
app.windows().into_iter().for_each(|window| {
|
||||
// Call `makeKeyAndOrderFront` if it was called on the window in `WinitWindow::new`
|
||||
// This way we preserve the user's desired initial visibility status
|
||||
// TODO: Also filter on the type/"level" of the window, and maybe other things?
|
||||
if window.isVisible() {
|
||||
tracing::trace!("Activating visible window");
|
||||
window.makeKeyAndOrderFront(None);
|
||||
} else {
|
||||
tracing::trace!("Skipping activating invisible window");
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use objc2_foundation::{
|
||||
};
|
||||
|
||||
use crate::cursor::{CursorImage, OnlyCursorImageSource};
|
||||
use crate::error::RequestError;
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
@@ -23,12 +24,12 @@ unsafe impl Send for CustomCursor {}
|
||||
unsafe impl Sync for CustomCursor {}
|
||||
|
||||
impl CustomCursor {
|
||||
pub(crate) fn new(cursor: OnlyCursorImageSource) -> CustomCursor {
|
||||
Self(cursor_from_image(&cursor.0))
|
||||
pub(crate) fn new(cursor: OnlyCursorImageSource) -> Result<CustomCursor, RequestError> {
|
||||
cursor_from_image(&cursor.0).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Retained<NSCursor> {
|
||||
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result<Retained<NSCursor>, RequestError> {
|
||||
let width = cursor.width;
|
||||
let height = cursor.height;
|
||||
|
||||
@@ -45,8 +46,8 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Retained<NSCursor> {
|
||||
NSDeviceRGBColorSpace,
|
||||
width as isize * 4,
|
||||
32,
|
||||
).unwrap()
|
||||
};
|
||||
)
|
||||
}.ok_or_else(|| os_error!("parent view should be installed in a window"))?;
|
||||
let bitmap_data = unsafe { slice::from_raw_parts_mut(bitmap.bitmapData(), cursor.rgba.len()) };
|
||||
bitmap_data.copy_from_slice(&cursor.rgba);
|
||||
|
||||
@@ -57,7 +58,7 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Retained<NSCursor> {
|
||||
|
||||
let hotspot = NSPoint::new(cursor.hotspot_x as f64, cursor.hotspot_y as f64);
|
||||
|
||||
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
|
||||
Ok(NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot))
|
||||
}
|
||||
|
||||
pub(crate) fn default_cursor() -> Retained<NSCursor> {
|
||||
@@ -7,12 +7,12 @@ use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSEventType};
|
||||
use objc2_foundation::{run_on_main, NSPoint};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use super::ffi;
|
||||
use crate::event::{ElementState, KeyEvent, Modifiers};
|
||||
use crate::keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
|
||||
PhysicalKey,
|
||||
};
|
||||
use crate::platform_impl::platform::ffi;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::any::Any;
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe};
|
||||
use std::ptr;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::mpsc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use core_foundation::base::{CFIndex, CFRelease};
|
||||
@@ -15,25 +14,31 @@ use core_foundation::runloop::{
|
||||
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use objc2::rc::{autoreleasepool, Retained};
|
||||
use objc2::runtime::ProtocolObject;
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSWindow};
|
||||
use objc2_foundation::{MainThreadMarker, NSObjectProtocol};
|
||||
|
||||
use super::app::WinitApplication;
|
||||
use super::app_state::{ApplicationDelegate, HandlePendingUserEvents};
|
||||
use super::event::dummy_event;
|
||||
use super::monitor::{self, MonitorHandle};
|
||||
use super::observer::setup_control_flow_observers;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||
use objc2::{msg_send_id, sel, ClassType};
|
||||
use objc2_app_kit::{
|
||||
NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
|
||||
NSApplicationWillTerminateNotification, NSWindow,
|
||||
};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject, NSObjectProtocol};
|
||||
|
||||
use super::super::notification_center::create_observer;
|
||||
use super::app::WinitApplication;
|
||||
use super::app_state::AppState;
|
||||
use super::cursor::CustomCursor;
|
||||
use super::event::dummy_event;
|
||||
use super::monitor;
|
||||
use super::observer::setup_control_flow_observers;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, RequestError};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform::macos::ActivationPolicy;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::platform::cursor::CustomCursor;
|
||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource};
|
||||
use crate::platform_impl::Window;
|
||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PanicInfo {
|
||||
@@ -67,77 +72,11 @@ impl PanicInfo {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ActiveEventLoop {
|
||||
delegate: Retained<ApplicationDelegate>,
|
||||
pub(super) app_state: Rc<AppState>,
|
||||
pub(super) mtm: MainThreadMarker,
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
pub(super) fn new_root(delegate: Retained<ApplicationDelegate>) -> RootWindowTarget {
|
||||
let mtm = MainThreadMarker::from(&*delegate);
|
||||
let p = Self { delegate, mtm };
|
||||
RootWindowTarget { p, _marker: PhantomData }
|
||||
}
|
||||
|
||||
pub(super) fn app_delegate(&self) -> &ApplicationDelegate {
|
||||
&self.delegate
|
||||
}
|
||||
|
||||
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
|
||||
RootCustomCursor { inner: CustomCursor::new(source.inner) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
monitor::available_monitors()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
let monitor = monitor::primary_monitor();
|
||||
Some(monitor)
|
||||
}
|
||||
|
||||
#[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::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) {
|
||||
self.delegate.set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
pub(crate) fn control_flow(&self) -> ControlFlow {
|
||||
self.delegate.control_flow()
|
||||
}
|
||||
|
||||
pub(crate) fn exit(&self) {
|
||||
self.delegate.exit()
|
||||
}
|
||||
|
||||
pub(crate) fn clear_exit(&self) {
|
||||
self.delegate.clear_exit()
|
||||
}
|
||||
|
||||
pub(crate) fn exiting(&self) -> bool {
|
||||
self.delegate.exiting()
|
||||
}
|
||||
|
||||
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
|
||||
OwnedDisplayHandle
|
||||
}
|
||||
|
||||
pub(crate) fn hide_application(&self) {
|
||||
NSApplication::sharedApplication(self.mtm).hide(None)
|
||||
}
|
||||
@@ -155,38 +94,98 @@ impl ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
fn map_user_event<T: 'static>(
|
||||
mut handler: impl FnMut(Event<T>, &RootWindowTarget),
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget) {
|
||||
move |event, window_target| match event.map_nonuser_event() {
|
||||
Ok(event) => (handler)(event, window_target),
|
||||
Err(_) => {
|
||||
for event in receiver.try_iter() {
|
||||
(handler)(Event::UserEvent(event), window_target);
|
||||
}
|
||||
},
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> RootEventLoopProxy {
|
||||
let event_loop_proxy = EventLoopProxy::new(self.app_state.proxy_wake_up());
|
||||
RootEventLoopProxy { event_loop_proxy }
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: crate::window::WindowAttributes,
|
||||
) -> Result<Box<dyn crate::window::Window>, RequestError> {
|
||||
Ok(Box::new(Window::new(self, window_attributes)?))
|
||||
}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
source: CustomCursorSource,
|
||||
) -> Result<RootCustomCursor, RequestError> {
|
||||
Ok(RootCustomCursor { inner: CustomCursor::new(source.inner)? })
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
|
||||
Box::new(monitor::available_monitors().into_iter().map(|inner| RootMonitorHandle { inner }))
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
|
||||
let monitor = monitor::primary_monitor();
|
||||
Some(RootMonitorHandle { inner: monitor })
|
||||
}
|
||||
|
||||
fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||
|
||||
fn system_theme(&self) -> Option<Theme> {
|
||||
let app = NSApplication::sharedApplication(self.mtm);
|
||||
|
||||
if app.respondsToSelector(sel!(effectiveAppearance)) {
|
||||
Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
|
||||
} else {
|
||||
Some(Theme::Light)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.app_state.set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
self.app_state.control_flow()
|
||||
}
|
||||
|
||||
fn exit(&self) {
|
||||
self.app_state.exit()
|
||||
}
|
||||
|
||||
fn exiting(&self) -> bool {
|
||||
self.app_state.exiting()
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop {
|
||||
/// Store a reference to the application for convenience.
|
||||
///
|
||||
/// We intentionally don't store `WinitApplication` since we want to have
|
||||
/// the possibility of swapping that out at some point.
|
||||
app: Retained<NSApplication>,
|
||||
/// The application delegate that we've registered.
|
||||
///
|
||||
/// The delegate is only weakly referenced by NSApplication, so we must
|
||||
/// keep it around here as well.
|
||||
delegate: Retained<ApplicationDelegate>,
|
||||
app_state: Rc<AppState>,
|
||||
|
||||
// Event sender and receiver, used for EventLoopProxy.
|
||||
sender: mpsc::Sender<T>,
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
|
||||
window_target: RootWindowTarget,
|
||||
window_target: ActiveEventLoop,
|
||||
panic_info: Rc<PanicInfo>,
|
||||
|
||||
// Since macOS 10.11, we no longer need to remove the observers before they are deallocated;
|
||||
// the system instead cleans it up next time it would have posted a notification to it.
|
||||
//
|
||||
// Though we do still need to keep the observers around to prevent them from being deallocated.
|
||||
_did_finish_launching_observer: Retained<NSObject>,
|
||||
_will_terminate_observer: Retained<NSObject>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -206,7 +205,7 @@ impl Default for PlatformSpecificEventLoopAttributes {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
impl EventLoop {
|
||||
pub(crate) fn new(
|
||||
attributes: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<Self, EventLoopError> {
|
||||
@@ -228,67 +227,82 @@ impl<T> EventLoop<T> {
|
||||
ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory,
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited,
|
||||
};
|
||||
let delegate = ApplicationDelegate::new(
|
||||
|
||||
let app_state = AppState::setup_global(
|
||||
mtm,
|
||||
activation_policy,
|
||||
attributes.default_menu,
|
||||
attributes.activate_ignoring_other_apps,
|
||||
);
|
||||
|
||||
autoreleasepool(|_| {
|
||||
app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
});
|
||||
let center = unsafe { NSNotificationCenter::defaultCenter() };
|
||||
|
||||
let weak_app_state = Rc::downgrade(&app_state);
|
||||
let _did_finish_launching_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationDidFinishLaunching:`
|
||||
unsafe { NSApplicationDidFinishLaunchingNotification },
|
||||
move |notification| {
|
||||
if let Some(app_state) = weak_app_state.upgrade() {
|
||||
app_state.did_finish_launching(notification);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let weak_app_state = Rc::downgrade(&app_state);
|
||||
let _will_terminate_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationWillTerminate:`
|
||||
unsafe { NSApplicationWillTerminateNotification },
|
||||
move |notification| {
|
||||
if let Some(app_state) = weak_app_state.upgrade() {
|
||||
app_state.will_terminate(notification);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let panic_info: Rc<PanicInfo> = Default::default();
|
||||
setup_control_flow_observers(mtm, Rc::downgrade(&panic_info));
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
Ok(EventLoop {
|
||||
app,
|
||||
delegate: delegate.clone(),
|
||||
sender,
|
||||
receiver: Rc::new(receiver),
|
||||
window_target: RootWindowTarget {
|
||||
p: ActiveEventLoop { delegate, mtm },
|
||||
_marker: PhantomData,
|
||||
},
|
||||
app_state: app_state.clone(),
|
||||
window_target: ActiveEventLoop { app_state, mtm },
|
||||
panic_info,
|
||||
_did_finish_launching_observer,
|
||||
_will_terminate_observer,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &RootWindowTarget {
|
||||
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
|
||||
&self.window_target
|
||||
}
|
||||
|
||||
pub fn run<F>(mut self, handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget),
|
||||
{
|
||||
self.run_on_demand(handler)
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
// 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_on_demand<F>(&mut self, handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget),
|
||||
{
|
||||
let handler = map_user_event(handler, self.receiver.clone());
|
||||
|
||||
self.delegate.set_event_handler(handler, || {
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut app: A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
self.app_state.clear_exit();
|
||||
self.app_state.set_event_handler(&mut app, || {
|
||||
autoreleasepool(|_| {
|
||||
// clear / normalize pump_events state
|
||||
self.delegate.set_wait_timeout(None);
|
||||
self.delegate.set_stop_before_wait(false);
|
||||
self.delegate.set_stop_after_wait(false);
|
||||
self.delegate.set_stop_on_redraw(false);
|
||||
self.app_state.set_wait_timeout(None);
|
||||
self.app_state.set_stop_before_wait(false);
|
||||
self.app_state.set_stop_after_wait(false);
|
||||
self.app_state.set_stop_on_redraw(false);
|
||||
|
||||
if self.delegate.is_launched() {
|
||||
debug_assert!(!self.delegate.is_running());
|
||||
self.delegate.set_is_running(true);
|
||||
self.delegate.dispatch_init_events();
|
||||
if self.app_state.is_launched() {
|
||||
debug_assert!(!self.app_state.is_running());
|
||||
self.app_state.set_is_running(true);
|
||||
self.app_state.dispatch_init_events();
|
||||
}
|
||||
|
||||
// SAFETY: We do not run the application re-entrantly
|
||||
@@ -303,60 +317,59 @@ impl<T> EventLoop<T> {
|
||||
resume_unwind(panic);
|
||||
}
|
||||
|
||||
self.delegate.internal_exit()
|
||||
self.app_state.internal_exit()
|
||||
})
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget),
|
||||
{
|
||||
let handler = map_user_event(handler, self.receiver.clone());
|
||||
|
||||
self.delegate.set_event_handler(handler, || {
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
mut app: A,
|
||||
) -> PumpStatus {
|
||||
self.app_state.set_event_handler(&mut app, || {
|
||||
autoreleasepool(|_| {
|
||||
// As a special case, if the application hasn't been launched yet then we at least
|
||||
// run the loop until it has fully launched.
|
||||
if !self.delegate.is_launched() {
|
||||
debug_assert!(!self.delegate.is_running());
|
||||
if !self.app_state.is_launched() {
|
||||
debug_assert!(!self.app_state.is_running());
|
||||
|
||||
self.delegate.set_stop_on_launch();
|
||||
self.app_state.set_stop_on_launch();
|
||||
// SAFETY: We do not run the application re-entrantly
|
||||
unsafe { self.app.run() };
|
||||
|
||||
// Note: we dispatch `NewEvents(Init)` + `Resumed` events after the application
|
||||
// has launched
|
||||
} else if !self.delegate.is_running() {
|
||||
} else if !self.app_state.is_running() {
|
||||
// Even though the application may have been launched, it's possible we aren't
|
||||
// running if the `EventLoop` was run before and has since
|
||||
// exited. This indicates that we just starting to re-run
|
||||
// the same `EventLoop` again.
|
||||
self.delegate.set_is_running(true);
|
||||
self.delegate.dispatch_init_events();
|
||||
self.app_state.set_is_running(true);
|
||||
self.app_state.dispatch_init_events();
|
||||
} else {
|
||||
// Only run for as long as the given `Duration` allows so we don't block the
|
||||
// external loop.
|
||||
match timeout {
|
||||
Some(Duration::ZERO) => {
|
||||
self.delegate.set_wait_timeout(None);
|
||||
self.delegate.set_stop_before_wait(true);
|
||||
self.app_state.set_wait_timeout(None);
|
||||
self.app_state.set_stop_before_wait(true);
|
||||
},
|
||||
Some(duration) => {
|
||||
self.delegate.set_stop_before_wait(false);
|
||||
self.app_state.set_stop_before_wait(false);
|
||||
let timeout = Instant::now() + duration;
|
||||
self.delegate.set_wait_timeout(Some(timeout));
|
||||
self.delegate.set_stop_after_wait(true);
|
||||
self.app_state.set_wait_timeout(Some(timeout));
|
||||
self.app_state.set_stop_after_wait(true);
|
||||
},
|
||||
None => {
|
||||
self.delegate.set_wait_timeout(None);
|
||||
self.delegate.set_stop_before_wait(false);
|
||||
self.delegate.set_stop_after_wait(true);
|
||||
self.app_state.set_wait_timeout(None);
|
||||
self.app_state.set_stop_before_wait(false);
|
||||
self.app_state.set_stop_after_wait(true);
|
||||
},
|
||||
}
|
||||
self.delegate.set_stop_on_redraw(true);
|
||||
self.app_state.set_stop_on_redraw(true);
|
||||
// SAFETY: We do not run the application re-entrantly
|
||||
unsafe { self.app.run() };
|
||||
}
|
||||
@@ -370,8 +383,8 @@ impl<T> EventLoop<T> {
|
||||
resume_unwind(panic);
|
||||
}
|
||||
|
||||
if self.delegate.exiting() {
|
||||
self.delegate.internal_exit();
|
||||
if self.app_state.exiting() {
|
||||
self.app_state.internal_exit();
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
PumpStatus::Continue
|
||||
@@ -379,22 +392,12 @@ impl<T> EventLoop<T> {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.sender.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::AppKitDisplayHandle::empty().into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
@@ -439,15 +442,15 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy<T> {
|
||||
sender: mpsc::Sender<T>,
|
||||
pub struct EventLoopProxy {
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
source: CFRunLoopSourceRef,
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for EventLoopProxy<T> {}
|
||||
unsafe impl<T: Send> Sync for EventLoopProxy<T> {}
|
||||
unsafe impl Send for EventLoopProxy {}
|
||||
unsafe impl Sync for EventLoopProxy {}
|
||||
|
||||
impl<T> Drop for EventLoopProxy<T> {
|
||||
impl Drop for EventLoopProxy {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
CFRelease(self.source as _);
|
||||
@@ -455,14 +458,14 @@ impl<T> Drop for EventLoopProxy<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for EventLoopProxy<T> {
|
||||
impl Clone for EventLoopProxy {
|
||||
fn clone(&self) -> Self {
|
||||
EventLoopProxy::new(self.sender.clone())
|
||||
EventLoopProxy::new(self.proxy_wake_up.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoopProxy<T> {
|
||||
fn new(sender: mpsc::Sender<T>) -> Self {
|
||||
impl EventLoopProxy {
|
||||
fn new(proxy_wake_up: Arc<AtomicBool>) -> Self {
|
||||
unsafe {
|
||||
// just wake up the eventloop
|
||||
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
|
||||
@@ -486,18 +489,17 @@ impl<T> EventLoopProxy<T> {
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||
CFRunLoopWakeUp(rl);
|
||||
|
||||
EventLoopProxy { sender, source }
|
||||
EventLoopProxy { proxy_wake_up, source }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.sender.send(event).map_err(|mpsc::SendError(x)| EventLoopClosed(x))?;
|
||||
pub fn wake_up(&self) {
|
||||
self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
CFRunLoopSourceSignal(self.source);
|
||||
let rl = CFRunLoopGetMain();
|
||||
CFRunLoopWakeUp(rl);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ mod app;
|
||||
mod app_state;
|
||||
mod cursor;
|
||||
mod event;
|
||||
mod event_handler;
|
||||
mod event_loop;
|
||||
mod ffi;
|
||||
mod menu;
|
||||
@@ -15,21 +14,17 @@ mod view;
|
||||
mod window;
|
||||
mod window_delegate;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
|
||||
pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey, KeyEventExtra};
|
||||
pub(crate) use self::event_loop::{
|
||||
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
|
||||
PlatformSpecificEventLoopAttributes,
|
||||
};
|
||||
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
|
||||
pub(crate) use self::window::WindowId;
|
||||
pub(crate) use self::window::{Window, WindowId};
|
||||
pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
|
||||
use crate::event::DeviceId as RootDeviceId;
|
||||
|
||||
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
|
||||
pub(crate) use self::window::Window;
|
||||
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
|
||||
use crate::event::DeviceId as RootDeviceId;
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
|
||||
@@ -37,7 +32,7 @@ pub(crate) use crate::platform_impl::Fullscreen;
|
||||
pub struct DeviceId;
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId
|
||||
}
|
||||
}
|
||||
@@ -45,17 +40,11 @@ impl DeviceId {
|
||||
// Constant device ID; to be removed when if backend is updated to report real device IDs.
|
||||
pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OsError {
|
||||
CGError(core_graphics::base::CGError),
|
||||
CreationError(&'static str),
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId;
|
||||
|
||||
impl fmt::Display for OsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
OsError::CGError(e) => f.pad(&format!("CGError {e}")),
|
||||
OsError::CreationError(e) => f.pad(e),
|
||||
}
|
||||
impl FingerId {
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::num::{NonZeroU16, NonZeroU32};
|
||||
|
||||
use core_foundation::array::{CFArrayGetCount, CFArrayGetValueAtIndex};
|
||||
use core_foundation::base::{CFRelease, TCFType};
|
||||
@@ -20,8 +21,8 @@ use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
|
||||
#[derive(Clone)]
|
||||
pub struct VideoModeHandle {
|
||||
size: PhysicalSize<u32>,
|
||||
bit_depth: u16,
|
||||
refresh_rate_millihertz: u32,
|
||||
bit_depth: Option<NonZeroU16>,
|
||||
refresh_rate_millihertz: Option<NonZeroU32>,
|
||||
pub(crate) monitor: MonitorHandle,
|
||||
pub(crate) native_mode: NativeDisplayMode,
|
||||
}
|
||||
@@ -80,15 +81,47 @@ impl Clone for NativeDisplayMode {
|
||||
}
|
||||
|
||||
impl VideoModeHandle {
|
||||
fn new(
|
||||
monitor: MonitorHandle,
|
||||
mode: NativeDisplayMode,
|
||||
refresh_rate_millihertz: Option<NonZeroU32>,
|
||||
) -> Self {
|
||||
unsafe {
|
||||
let pixel_encoding =
|
||||
CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode.0))
|
||||
.to_string();
|
||||
let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) {
|
||||
32
|
||||
} else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) {
|
||||
16
|
||||
} else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO30BitDirectPixels) {
|
||||
30
|
||||
} else {
|
||||
unimplemented!()
|
||||
};
|
||||
|
||||
VideoModeHandle {
|
||||
size: PhysicalSize::new(
|
||||
ffi::CGDisplayModeGetPixelWidth(mode.0) as u32,
|
||||
ffi::CGDisplayModeGetPixelHeight(mode.0) as u32,
|
||||
),
|
||||
refresh_rate_millihertz,
|
||||
bit_depth: NonZeroU16::new(bit_depth),
|
||||
monitor: monitor.clone(),
|
||||
native_mode: mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
pub fn bit_depth(&self) -> Option<NonZeroU16> {
|
||||
self.bit_depth
|
||||
}
|
||||
|
||||
pub fn refresh_rate_millihertz(&self) -> u32 {
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
|
||||
self.refresh_rate_millihertz
|
||||
}
|
||||
|
||||
@@ -158,10 +191,8 @@ impl fmt::Debug for MonitorHandle {
|
||||
f.debug_struct("MonitorHandle")
|
||||
.field("name", &self.name())
|
||||
.field("native_identifier", &self.native_identifier())
|
||||
.field("size", &self.size())
|
||||
.field("position", &self.position())
|
||||
.field("scale_factor", &self.scale_factor())
|
||||
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
@@ -184,22 +215,14 @@ impl MonitorHandle {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
let MonitorHandle(display_id) = *self;
|
||||
let display = CGDisplay::new(display_id);
|
||||
let height = display.pixels_high();
|
||||
let width = display.pixels_wide();
|
||||
PhysicalSize::from_logical::<_, f64>((width as f64, height as f64), self.scale_factor())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
|
||||
// This is already in screen coordinates. If we were using `NSScreen`,
|
||||
// then a conversion would've been needed:
|
||||
// flip_window_screen_coordinates(self.ns_screen(mtm)?.frame())
|
||||
let bounds = unsafe { CGDisplayBounds(self.native_identifier()) };
|
||||
let position = LogicalPosition::new(bounds.origin.x, bounds.origin.y);
|
||||
position.to_physical(self.scale_factor())
|
||||
Some(position.to_physical(self.scale_factor()))
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
@@ -211,34 +234,20 @@ impl MonitorHandle {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
unsafe {
|
||||
let current_display_mode = NativeDisplayMode(CGDisplayCopyDisplayMode(self.0) as _);
|
||||
let refresh_rate = ffi::CGDisplayModeGetRefreshRate(current_display_mode.0);
|
||||
if refresh_rate > 0.0 {
|
||||
return Some((refresh_rate * 1000.0).round() as u32);
|
||||
}
|
||||
fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
|
||||
let current_display_mode =
|
||||
NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _);
|
||||
refresh_rate_millihertz(self.0, ¤t_display_mode)
|
||||
}
|
||||
|
||||
let mut display_link = std::ptr::null_mut();
|
||||
if ffi::CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link)
|
||||
!= ffi::kCVReturnSuccess
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let time = ffi::CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link);
|
||||
ffi::CVDisplayLinkRelease(display_link);
|
||||
|
||||
// This value is indefinite if an invalid display link was specified
|
||||
if time.flags & ffi::kCVTimeIsIndefinite != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
(time.time_scale as i64).checked_div(time.time_value).map(|v| (v * 1000) as u32)
|
||||
}
|
||||
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
|
||||
let mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _);
|
||||
let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode);
|
||||
Some(VideoModeHandle::new(self.clone(), mode, refresh_rate_millihertz))
|
||||
}
|
||||
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
|
||||
let refresh_rate_millihertz = self.refresh_rate_millihertz().unwrap_or(0);
|
||||
let refresh_rate_millihertz = self.refresh_rate_millihertz();
|
||||
let monitor = self.clone();
|
||||
|
||||
unsafe {
|
||||
@@ -263,34 +272,16 @@ impl MonitorHandle {
|
||||
// CGDisplayModeGetRefreshRate returns 0.0 for any display that
|
||||
// isn't a CRT
|
||||
let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 {
|
||||
(cg_refresh_rate_hertz * 1000) as u32
|
||||
NonZeroU32::new((cg_refresh_rate_hertz * 1000) as u32)
|
||||
} else {
|
||||
refresh_rate_millihertz
|
||||
};
|
||||
|
||||
let pixel_encoding =
|
||||
CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode))
|
||||
.to_string();
|
||||
let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) {
|
||||
32
|
||||
} else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) {
|
||||
16
|
||||
} else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO30BitDirectPixels) {
|
||||
30
|
||||
} else {
|
||||
unimplemented!()
|
||||
};
|
||||
|
||||
VideoModeHandle {
|
||||
size: PhysicalSize::new(
|
||||
ffi::CGDisplayModeGetPixelWidth(mode) as u32,
|
||||
ffi::CGDisplayModeGetPixelHeight(mode) as u32,
|
||||
),
|
||||
VideoModeHandle::new(
|
||||
monitor.clone(),
|
||||
NativeDisplayMode(mode),
|
||||
refresh_rate_millihertz,
|
||||
bit_depth,
|
||||
monitor: monitor.clone(),
|
||||
native_mode: NativeDisplayMode(mode),
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -349,3 +340,29 @@ pub(crate) fn flip_window_screen_coordinates(frame: NSRect) -> NSPoint {
|
||||
let y = main_screen_height - frame.size.height - frame.origin.y;
|
||||
NSPoint::new(frame.origin.x, y)
|
||||
}
|
||||
|
||||
fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> Option<NonZeroU32> {
|
||||
unsafe {
|
||||
let refresh_rate = ffi::CGDisplayModeGetRefreshRate(mode.0);
|
||||
if refresh_rate > 0.0 {
|
||||
return NonZeroU32::new((refresh_rate * 1000.0).round() as u32);
|
||||
}
|
||||
|
||||
let mut display_link = std::ptr::null_mut();
|
||||
if ffi::CVDisplayLinkCreateWithCGDisplay(id, &mut display_link) != ffi::kCVReturnSuccess {
|
||||
return None;
|
||||
}
|
||||
let time = ffi::CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link);
|
||||
ffi::CVDisplayLinkRelease(display_link);
|
||||
|
||||
// This value is indefinite if an invalid display link was specified
|
||||
if time.flags & ffi::kCVTimeIsIndefinite != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
(time.time_scale as i64)
|
||||
.checked_div(time.time_value)
|
||||
.map(|v| (v * 1000) as u32)
|
||||
.and_then(NonZeroU32::new)
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ use core_foundation::runloop::{
|
||||
use objc2_foundation::MainThreadMarker;
|
||||
use tracing::error;
|
||||
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::app_state::AppState;
|
||||
use super::event_loop::{stop_app_on_panic, PanicInfo};
|
||||
use super::ffi;
|
||||
|
||||
@@ -59,7 +59,7 @@ extern "C" fn control_flow_begin_handler(
|
||||
match activity {
|
||||
kCFRunLoopAfterWaiting => {
|
||||
// trace!("Triggered `CFRunLoopAfterWaiting`");
|
||||
ApplicationDelegate::get(MainThreadMarker::new().unwrap()).wakeup(panic_info);
|
||||
AppState::get(MainThreadMarker::new().unwrap()).wakeup(panic_info);
|
||||
// trace!("Completed `CFRunLoopAfterWaiting`");
|
||||
},
|
||||
_ => unreachable!(),
|
||||
@@ -81,7 +81,7 @@ extern "C" fn control_flow_end_handler(
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => {
|
||||
// trace!("Triggered `CFRunLoopBeforeWaiting`");
|
||||
ApplicationDelegate::get(MainThreadMarker::new().unwrap()).cleared(panic_info);
|
||||
AppState::get(MainThreadMarker::new().unwrap()).cleared(panic_info);
|
||||
// trace!("Completed `CFRunLoopBeforeWaiting`");
|
||||
},
|
||||
kCFRunLoopExit => (), // unimplemented!(), // not expected to ever happen
|
||||
@@ -2,7 +2,8 @@ use tracing::trace;
|
||||
|
||||
macro_rules! trace_scope {
|
||||
($s:literal) => {
|
||||
let _crate = $crate::platform_impl::platform::util::TraceGuard::new(module_path!(), $s);
|
||||
let _crate =
|
||||
$crate::platform_impl::platform::appkit::util::TraceGuard::new(module_path!(), $s);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use objc2::rc::{Retained, WeakId};
|
||||
use objc2::runtime::{AnyObject, Sel};
|
||||
@@ -16,7 +17,7 @@ use objc2_foundation::{
|
||||
NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
||||
};
|
||||
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::app_state::AppState;
|
||||
use super::cursor::{default_cursor, invisible_cursor};
|
||||
use super::event::{
|
||||
code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed,
|
||||
@@ -31,6 +32,7 @@ use crate::event::{
|
||||
};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey};
|
||||
use crate::platform::macos::OptionAsAlt;
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CursorState {
|
||||
@@ -111,7 +113,7 @@ fn get_left_modifier_code(key: &Key) -> KeyCode {
|
||||
#[derive(Debug)]
|
||||
pub struct ViewState {
|
||||
/// Strong reference to the global application state.
|
||||
app_delegate: Retained<ApplicationDelegate>,
|
||||
app_state: Rc<AppState>,
|
||||
|
||||
cursor_state: RefCell<CursorState>,
|
||||
ime_position: Cell<NSPoint>,
|
||||
@@ -196,7 +198,7 @@ declare_class!(
|
||||
// 2. Even when a window resize does occur on a new tabbed window, it contains the wrong size (includes tab height).
|
||||
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
||||
let size = logical_size.to_physical::<u32>(self.scale_factor());
|
||||
self.queue_event(WindowEvent::Resized(size));
|
||||
self.queue_event(WindowEvent::SurfaceResized(size));
|
||||
}
|
||||
|
||||
#[method(drawRect:)]
|
||||
@@ -205,7 +207,7 @@ declare_class!(
|
||||
|
||||
// It's a workaround for https://github.com/rust-windowing/winit/issues/2640, don't replace with `self.window_id()`.
|
||||
if let Some(window) = self.ivars()._ns_window.load() {
|
||||
self.ivars().app_delegate.handle_redraw(window.id());
|
||||
self.ivars().app_state.handle_redraw(window.id());
|
||||
}
|
||||
|
||||
// This is a direct subclass of NSView, no need to call superclass' drawRect:
|
||||
@@ -686,7 +688,9 @@ declare_class!(
|
||||
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
self.ivars().app_delegate.maybe_queue_device_event(DeviceEvent::MouseWheel { delta });
|
||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop|
|
||||
app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseWheel { delta })
|
||||
);
|
||||
self.queue_event(WindowEvent::MouseWheel {
|
||||
device_id: DEVICE_ID,
|
||||
delta,
|
||||
@@ -779,14 +783,14 @@ declare_class!(
|
||||
|
||||
impl WinitView {
|
||||
pub(super) fn new(
|
||||
app_delegate: &ApplicationDelegate,
|
||||
app_state: &Rc<AppState>,
|
||||
window: &WinitWindow,
|
||||
accepts_first_mouse: bool,
|
||||
option_as_alt: OptionAsAlt,
|
||||
) -> Retained<Self> {
|
||||
let mtm = MainThreadMarker::from(window);
|
||||
let this = mtm.alloc().set_ivars(ViewState {
|
||||
app_delegate: app_delegate.retain(),
|
||||
app_state: Rc::clone(app_state),
|
||||
cursor_state: Default::default(),
|
||||
ime_position: Default::default(),
|
||||
ime_size: Default::default(),
|
||||
@@ -830,7 +834,10 @@ impl WinitView {
|
||||
}
|
||||
|
||||
fn queue_event(&self, event: WindowEvent) {
|
||||
self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event);
|
||||
let window_id = RootWindowId(self.window().id());
|
||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.window_event(event_loop, window_id, event);
|
||||
});
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
391
src/platform_impl/apple/appkit/window.rs
Normal file
391
src/platform_impl/apple/appkit/window.rs
Normal file
@@ -0,0 +1,391 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use dpi::{Position, Size};
|
||||
use objc2::rc::{autoreleasepool, Retained};
|
||||
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSResponder, NSWindow};
|
||||
use objc2_foundation::{MainThreadBound, MainThreadMarker, NSObject};
|
||||
|
||||
use super::event_loop::ActiveEventLoop;
|
||||
use super::window_delegate::WindowDelegate;
|
||||
use crate::error::RequestError;
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
use crate::window::{
|
||||
Cursor, Fullscreen, Icon, ImePurpose, Theme, UserAttentionType, Window as CoreWindow,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
};
|
||||
|
||||
pub(crate) struct Window {
|
||||
window: MainThreadBound<Retained<WinitWindow>>,
|
||||
/// The window only keeps a weak reference to this, so we must keep it around here.
|
||||
delegate: MainThreadBound<Retained<WindowDelegate>>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new(
|
||||
window_target: &ActiveEventLoop,
|
||||
attributes: WindowAttributes,
|
||||
) -> Result<Self, RequestError> {
|
||||
let mtm = window_target.mtm;
|
||||
let delegate =
|
||||
autoreleasepool(|_| WindowDelegate::new(&window_target.app_state, attributes, mtm))?;
|
||||
Ok(Window {
|
||||
window: MainThreadBound::new(delegate.window().retain(), mtm),
|
||||
delegate: MainThreadBound::new(delegate, mtm),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(
|
||||
&self,
|
||||
f: impl FnOnce(&WindowDelegate) -> R + Send,
|
||||
) -> R {
|
||||
self.delegate.get_on_main(|delegate| f(delegate))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub(crate) fn raw_window_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
if let Some(mtm) = MainThreadMarker::new() {
|
||||
Ok(self.delegate.get(mtm).raw_window_handle_rwh_06())
|
||||
} else {
|
||||
Err(rwh_06::HandleError::Unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub(crate) fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
// Restore the video mode.
|
||||
if matches!(self.fullscreen(), Some(Fullscreen::Exclusive(_))) {
|
||||
self.set_fullscreen(None);
|
||||
}
|
||||
|
||||
self.window.get_on_main(|window| autoreleasepool(|_| window.close()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> crate::window::WindowId {
|
||||
self.maybe_wait_on_main(|delegate| crate::window::WindowId(delegate.id()))
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
self.maybe_wait_on_main(|delegate| delegate.scale_factor())
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_redraw());
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.pre_present_notify());
|
||||
}
|
||||
|
||||
fn reset_dead_keys(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_outer_position(position));
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> dpi::PhysicalSize<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_size())
|
||||
}
|
||||
|
||||
fn request_surface_size(&self, size: Size) -> Option<dpi::PhysicalSize<u32>> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_surface_size(size))
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> dpi::PhysicalSize<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.outer_size())
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size))
|
||||
}
|
||||
|
||||
fn set_max_surface_size(&self, max_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_max_surface_size(max_size));
|
||||
}
|
||||
|
||||
fn surface_resize_increments(&self) -> Option<dpi::PhysicalSize<u32>> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_resize_increments())
|
||||
}
|
||||
|
||||
fn set_surface_resize_increments(&self, increments: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_surface_resize_increments(increments));
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_title(title));
|
||||
}
|
||||
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_transparent(transparent));
|
||||
}
|
||||
|
||||
fn set_blur(&self, blur: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_blur(blur));
|
||||
}
|
||||
|
||||
fn set_visible(&self, visible: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_visible(visible));
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_visible())
|
||||
}
|
||||
|
||||
fn set_resizable(&self, resizable: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_resizable(resizable))
|
||||
}
|
||||
|
||||
fn is_resizable(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_resizable())
|
||||
}
|
||||
|
||||
fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_enabled_buttons(buttons))
|
||||
}
|
||||
|
||||
fn enabled_buttons(&self) -> WindowButtons {
|
||||
self.maybe_wait_on_main(|delegate| delegate.enabled_buttons())
|
||||
}
|
||||
|
||||
fn set_minimized(&self, minimized: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_minimized(minimized));
|
||||
}
|
||||
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_minimized())
|
||||
}
|
||||
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_maximized(maximized));
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_maximized())
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen.map(Into::into)))
|
||||
}
|
||||
|
||||
fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.fullscreen().map(Into::into))
|
||||
}
|
||||
|
||||
fn set_decorations(&self, decorations: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_decorations(decorations));
|
||||
}
|
||||
|
||||
fn is_decorated(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_decorated())
|
||||
}
|
||||
|
||||
fn set_window_level(&self, level: WindowLevel) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_window_level(level));
|
||||
}
|
||||
|
||||
fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_window_icon(window_icon));
|
||||
}
|
||||
|
||||
fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_cursor_area(position, size));
|
||||
}
|
||||
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_allowed(allowed));
|
||||
}
|
||||
|
||||
fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_purpose(purpose));
|
||||
}
|
||||
|
||||
fn focus_window(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.focus_window());
|
||||
}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.has_focus())
|
||||
}
|
||||
|
||||
fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_user_attention(request_type));
|
||||
}
|
||||
|
||||
fn set_theme(&self, theme: Option<Theme>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_theme(theme));
|
||||
}
|
||||
|
||||
fn theme(&self) -> Option<Theme> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.theme())
|
||||
}
|
||||
|
||||
fn set_content_protected(&self, protected: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_content_protected(protected));
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
self.maybe_wait_on_main(|delegate| delegate.title())
|
||||
}
|
||||
|
||||
fn set_cursor(&self, cursor: Cursor) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor(cursor));
|
||||
}
|
||||
|
||||
fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_position(position))
|
||||
}
|
||||
|
||||
fn set_cursor_grab(&self, mode: crate::window::CursorGrabMode) -> Result<(), RequestError> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_grab(mode))
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_visible(visible))
|
||||
}
|
||||
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.drag_window());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn drag_resize_window(
|
||||
&self,
|
||||
direction: crate::window::ResizeDirection,
|
||||
) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.drag_resize_window(direction))?)
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, position: Position) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.show_window_menu(position))
|
||||
}
|
||||
|
||||
fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_hittest(hittest));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
delegate.current_monitor().map(|inner| CoreMonitorHandle { inner })
|
||||
})
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
Box::new(
|
||||
delegate.available_monitors().into_iter().map(|inner| CoreMonitorHandle { inner }),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
delegate.primary_monitor().map(|inner| CoreMonitorHandle { inner })
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(pub usize);
|
||||
|
||||
impl WindowId {
|
||||
pub const fn dummy() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WindowId> for u64 {
|
||||
fn from(window_id: WindowId) -> Self {
|
||||
window_id.0 as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for WindowId {
|
||||
fn from(raw_id: u64) -> Self {
|
||||
Self(raw_id as usize)
|
||||
}
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
pub struct WinitWindow;
|
||||
|
||||
unsafe impl ClassType for WinitWindow {
|
||||
#[inherits(NSResponder, NSObject)]
|
||||
type Super = NSWindow;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitWindow";
|
||||
}
|
||||
|
||||
impl DeclaredClass for WinitWindow {}
|
||||
|
||||
unsafe impl WinitWindow {
|
||||
#[method(canBecomeMainWindow)]
|
||||
fn can_become_main_window(&self) -> bool {
|
||||
trace_scope!("canBecomeMainWindow");
|
||||
true
|
||||
}
|
||||
|
||||
#[method(canBecomeKeyWindow)]
|
||||
fn can_become_key_window(&self) -> bool {
|
||||
trace_scope!("canBecomeKeyWindow");
|
||||
true
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl WinitWindow {
|
||||
pub(super) fn id(&self) -> WindowId {
|
||||
WindowId(self as *const Self as usize)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::c_void;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use core_graphics::display::{CGDisplay, CGPoint};
|
||||
@@ -9,36 +12,38 @@ use objc2::rc::{autoreleasepool, Retained};
|
||||
use objc2::runtime::{AnyObject, ProtocolObject};
|
||||
use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{
|
||||
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSApplication,
|
||||
NSApplicationPresentationOptions, NSBackingStoreType, NSColor, NSDraggingDestination,
|
||||
NSFilenamesPboardType, NSPasteboard, NSRequestUserAttentionType, NSScreen, NSView,
|
||||
NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel,
|
||||
NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask,
|
||||
NSWindowTabbingMode, NSWindowTitleVisibility,
|
||||
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization,
|
||||
NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType,
|
||||
NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard,
|
||||
NSRequestUserAttentionType, NSScreen, NSView, NSWindowButton, NSWindowDelegate,
|
||||
NSWindowFullScreenButton, NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode,
|
||||
NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility,
|
||||
};
|
||||
use objc2_foundation::{
|
||||
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDistributedNotificationCenter,
|
||||
NSObject, NSObjectNSDelayedPerforming, NSObjectNSThreadPerformAdditions, NSObjectProtocol,
|
||||
NSPoint, NSRect, NSSize, NSString,
|
||||
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSKeyValueChangeKey,
|
||||
NSKeyValueChangeNewKey, NSKeyValueChangeOldKey, NSKeyValueObservingOptions, NSObject,
|
||||
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
|
||||
NSRect, NSSize, NSString,
|
||||
};
|
||||
use tracing::{trace, warn};
|
||||
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::app_state::AppState;
|
||||
use super::cursor::cursor_from_icon;
|
||||
use super::monitor::{self, flip_window_screen_coordinates, get_display_id};
|
||||
use super::observer::RunLoop;
|
||||
use super::view::WinitView;
|
||||
use super::window::WinitWindow;
|
||||
use super::{ffi, Fullscreen, MonitorHandle, OsError, WindowId};
|
||||
use super::{ffi, Fullscreen, MonitorHandle, WindowId};
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event::{InnerSizeWriter, WindowEvent};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{SurfaceSizeWriter, WindowEvent};
|
||||
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||
use crate::window::{
|
||||
Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformSpecificWindowAttributes {
|
||||
pub movable_by_window_background: bool,
|
||||
pub titlebar_transparent: bool,
|
||||
@@ -51,6 +56,7 @@ pub struct PlatformSpecificWindowAttributes {
|
||||
pub accepts_first_mouse: bool,
|
||||
pub tabbing_identifier: Option<String>,
|
||||
pub option_as_alt: OptionAsAlt,
|
||||
pub borderless_game: bool,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificWindowAttributes {
|
||||
@@ -68,6 +74,7 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
accepts_first_mouse: true,
|
||||
tabbing_identifier: None,
|
||||
option_as_alt: Default::default(),
|
||||
borderless_game: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,12 +82,10 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct State {
|
||||
/// Strong reference to the global application state.
|
||||
app_delegate: Retained<ApplicationDelegate>,
|
||||
app_state: Rc<AppState>,
|
||||
|
||||
window: Retained<WinitWindow>,
|
||||
|
||||
current_theme: Cell<Option<Theme>>,
|
||||
|
||||
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
||||
//
|
||||
// This is expressed in native screen coordinates.
|
||||
@@ -90,7 +95,7 @@ pub(crate) struct State {
|
||||
previous_scale_factor: Cell<f64>,
|
||||
|
||||
/// The current resize increments for the window content.
|
||||
resize_increments: Cell<NSSize>,
|
||||
surface_resize_increments: Cell<NSSize>,
|
||||
/// Whether the window is showing decorations.
|
||||
decorations: Cell<bool>,
|
||||
resizable: Cell<bool>,
|
||||
@@ -118,6 +123,7 @@ pub(crate) struct State {
|
||||
standard_frame: Cell<Option<NSRect>>,
|
||||
is_simple_fullscreen: Cell<bool>,
|
||||
saved_style: Cell<Option<NSWindowStyleMask>>,
|
||||
is_borderless_game: Cell<bool>,
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
@@ -158,7 +164,7 @@ declare_class!(
|
||||
#[method(windowDidResize:)]
|
||||
fn window_did_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidResize:");
|
||||
// NOTE: WindowEvent::Resized is reported in frameDidChange.
|
||||
// NOTE: WindowEvent::SurfaceResized is reported in frameDidChange.
|
||||
self.emit_move_event();
|
||||
}
|
||||
|
||||
@@ -166,7 +172,7 @@ declare_class!(
|
||||
fn window_will_start_live_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillStartLiveResize:");
|
||||
|
||||
let increments = self.ivars().resize_increments.get();
|
||||
let increments = self.ivars().surface_resize_increments.get();
|
||||
self.set_resize_increments_inner(increments);
|
||||
}
|
||||
|
||||
@@ -419,34 +425,68 @@ declare_class!(
|
||||
}
|
||||
}
|
||||
|
||||
// Key-Value Observing
|
||||
unsafe impl WindowDelegate {
|
||||
// Observe theme change
|
||||
#[method(effectiveAppearanceDidChange:)]
|
||||
fn effective_appearance_did_change(&self, sender: Option<&AnyObject>) {
|
||||
trace_scope!("effectiveAppearanceDidChange:");
|
||||
unsafe {
|
||||
self.performSelectorOnMainThread_withObject_waitUntilDone(
|
||||
sel!(effectiveAppearanceDidChangedOnMainThread:),
|
||||
sender,
|
||||
false,
|
||||
)
|
||||
};
|
||||
}
|
||||
#[method(observeValueForKeyPath:ofObject:change:context:)]
|
||||
fn observe_value(
|
||||
&self,
|
||||
key_path: Option<&NSString>,
|
||||
_object: Option<&AnyObject>,
|
||||
change: Option<&NSDictionary<NSKeyValueChangeKey, AnyObject>>,
|
||||
_context: *mut c_void,
|
||||
) {
|
||||
trace_scope!("observeValueForKeyPath:ofObject:change:context:");
|
||||
// NOTE: We don't _really_ need to check the key path, as there should only be one, but
|
||||
// in the future we might want to observe other key paths.
|
||||
if key_path == Some(ns_string!("effectiveAppearance")) {
|
||||
let change = change.expect("requested a change dictionary in `addObserver`, but none was provided");
|
||||
let old = change.get(unsafe { NSKeyValueChangeOldKey }).expect("requested change dictionary did not contain `NSKeyValueChangeOldKey`");
|
||||
let new = change.get(unsafe { NSKeyValueChangeNewKey }).expect("requested change dictionary did not contain `NSKeyValueChangeNewKey`");
|
||||
|
||||
#[method(effectiveAppearanceDidChangedOnMainThread:)]
|
||||
fn effective_appearance_did_changed_on_main_thread(&self, _: Option<&AnyObject>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let theme = get_ns_theme(mtm);
|
||||
let old_theme = self.ivars().current_theme.replace(Some(theme));
|
||||
if old_theme != Some(theme) {
|
||||
self.queue_event(WindowEvent::ThemeChanged(theme));
|
||||
// SAFETY: The value of `effectiveAppearance` is `NSAppearance`
|
||||
let old: *const AnyObject = old;
|
||||
let old: *const NSAppearance = old.cast();
|
||||
let old: &NSAppearance = unsafe { &*old };
|
||||
let new: *const AnyObject = new;
|
||||
let new: *const NSAppearance = new.cast();
|
||||
let new: &NSAppearance = unsafe { &*new };
|
||||
|
||||
trace!(old = %unsafe { old.name() }, new = %unsafe { new.name() }, "effectiveAppearance changed");
|
||||
|
||||
// Ignore the change if the window's theme is customized by the user (since in that
|
||||
// case the `effectiveAppearance` is only emitted upon said customization, and then
|
||||
// it's triggered directly by a user action, and we don't want to emit the event).
|
||||
if unsafe { self.window().appearance() }.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let old = appearance_to_theme(old);
|
||||
let new = appearance_to_theme(new);
|
||||
// Check that the theme changed in Winit's terms (the theme might have changed on
|
||||
// other parameters, such as level of contrast, but the event should not be emitted
|
||||
// in those cases).
|
||||
if old == new {
|
||||
return;
|
||||
}
|
||||
|
||||
self.queue_event(WindowEvent::ThemeChanged(new));
|
||||
} else {
|
||||
panic!("unknown observed keypath {key_path:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl Drop for WindowDelegate {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.window().removeObserver_forKeyPath(self, ns_string!("effectiveAppearance"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn new_window(
|
||||
app_delegate: &ApplicationDelegate,
|
||||
app_state: &Rc<AppState>,
|
||||
attrs: &WindowAttributes,
|
||||
mtm: MainThreadMarker,
|
||||
) -> Option<Retained<WinitWindow>> {
|
||||
@@ -465,7 +505,7 @@ fn new_window(
|
||||
let scale_factor = NSScreen::mainScreen(mtm)
|
||||
.map(|screen| screen.backingScaleFactor() as f64)
|
||||
.unwrap_or(1.0);
|
||||
let size = match attrs.inner_size {
|
||||
let size = match attrs.surface_size {
|
||||
Some(size) => {
|
||||
let size = size.to_logical(scale_factor);
|
||||
NSSize::new(size.width, size.height)
|
||||
@@ -521,6 +561,11 @@ fn new_window(
|
||||
masks |= NSWindowStyleMask::FullSizeContentView;
|
||||
}
|
||||
|
||||
// NOTE: This should only be created after the application has started launching,
|
||||
// (`applicationWillFinishLaunching:` at the earliest), otherwise you'll run into very
|
||||
// confusing issues with the window not being properly activated.
|
||||
//
|
||||
// Winit ensures this by not allowing access to `ActiveEventLoop` before handling events.
|
||||
let window: Option<Retained<WinitWindow>> = unsafe {
|
||||
msg_send_id![
|
||||
super(mtm.alloc().set_ivars(())),
|
||||
@@ -586,7 +631,7 @@ fn new_window(
|
||||
}
|
||||
|
||||
let view = WinitView::new(
|
||||
app_delegate,
|
||||
app_state,
|
||||
&window,
|
||||
attrs.platform_specific.accepts_first_mouse,
|
||||
attrs.platform_specific.option_as_alt,
|
||||
@@ -629,12 +674,12 @@ fn new_window(
|
||||
|
||||
impl WindowDelegate {
|
||||
pub(super) fn new(
|
||||
app_delegate: &ApplicationDelegate,
|
||||
app_state: &Rc<AppState>,
|
||||
attrs: WindowAttributes,
|
||||
mtm: MainThreadMarker,
|
||||
) -> Result<Retained<Self>, RootOsError> {
|
||||
let window = new_window(app_delegate, &attrs, mtm)
|
||||
.ok_or_else(|| os_error!(OsError::CreationError("couldn't create `NSWindow`")))?;
|
||||
) -> Result<Retained<Self>, RequestError> {
|
||||
let window = new_window(app_state, &attrs, mtm)
|
||||
.ok_or_else(|| os_error!("couldn't create `NSWindow`"))?;
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
match attrs.parent_window.map(|handle| handle.0) {
|
||||
@@ -643,9 +688,9 @@ impl WindowDelegate {
|
||||
// Unwrap is fine, since the pointer comes from `NonNull`.
|
||||
let parent_view: Retained<NSView> =
|
||||
unsafe { Retained::retain(handle.ns_view.as_ptr().cast()) }.unwrap();
|
||||
let parent = parent_view.window().ok_or_else(|| {
|
||||
os_error!(OsError::CreationError("parent view should be installed in a window"))
|
||||
})?;
|
||||
let parent = parent_view
|
||||
.window()
|
||||
.ok_or_else(|| os_error!("parent view should be installed in a window"))?;
|
||||
|
||||
// SAFETY: We know that there are no parent -> child -> parent cycles since the only
|
||||
// place in `winit` where we allow making a window a child window is
|
||||
@@ -658,28 +703,28 @@ impl WindowDelegate {
|
||||
None => (),
|
||||
}
|
||||
|
||||
let resize_increments =
|
||||
match attrs.resize_increments.map(|i| i.to_logical(window.backingScaleFactor() as _)) {
|
||||
Some(LogicalSize { width, height }) if width >= 1. && height >= 1. => {
|
||||
NSSize::new(width, height)
|
||||
},
|
||||
_ => NSSize::new(1., 1.),
|
||||
};
|
||||
let surface_resize_increments = match attrs
|
||||
.surface_resize_increments
|
||||
.map(|i| i.to_logical(window.backingScaleFactor() as _))
|
||||
{
|
||||
Some(LogicalSize { width, height }) if width >= 1. && height >= 1. => {
|
||||
NSSize::new(width, height)
|
||||
},
|
||||
_ => NSSize::new(1., 1.),
|
||||
};
|
||||
|
||||
let scale_factor = window.backingScaleFactor() as _;
|
||||
|
||||
let current_theme = match attrs.preferred_theme {
|
||||
Some(theme) => Some(theme),
|
||||
None => Some(get_ns_theme(mtm)),
|
||||
};
|
||||
if let Some(appearance) = theme_to_appearance(attrs.preferred_theme) {
|
||||
unsafe { window.setAppearance(Some(&appearance)) };
|
||||
}
|
||||
|
||||
let delegate = mtm.alloc().set_ivars(State {
|
||||
app_delegate: app_delegate.retain(),
|
||||
app_state: Rc::clone(app_state),
|
||||
window: window.retain(),
|
||||
current_theme: Cell::new(current_theme),
|
||||
previous_position: Cell::new(None),
|
||||
previous_scale_factor: Cell::new(scale_factor),
|
||||
resize_increments: Cell::new(resize_increments),
|
||||
surface_resize_increments: Cell::new(surface_resize_increments),
|
||||
decorations: Cell::new(attrs.decorations),
|
||||
resizable: Cell::new(attrs.resizable),
|
||||
maximized: Cell::new(attrs.maximized),
|
||||
@@ -691,6 +736,7 @@ impl WindowDelegate {
|
||||
standard_frame: Cell::new(None),
|
||||
is_simple_fullscreen: Cell::new(false),
|
||||
saved_style: Cell::new(None),
|
||||
is_borderless_game: Cell::new(attrs.platform_specific.borderless_game),
|
||||
});
|
||||
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
|
||||
|
||||
@@ -702,14 +748,16 @@ impl WindowDelegate {
|
||||
}
|
||||
window.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
|
||||
// Enable theme change event
|
||||
let notification_center = unsafe { NSDistributedNotificationCenter::defaultCenter() };
|
||||
// Listen for theme change event.
|
||||
//
|
||||
// SAFETY: The observer is un-registered in the `Drop` of the delegate.
|
||||
unsafe {
|
||||
notification_center.addObserver_selector_name_object(
|
||||
window.addObserver_forKeyPath_options_context(
|
||||
&delegate,
|
||||
sel!(effectiveAppearanceDidChange:),
|
||||
Some(ns_string!("AppleInterfaceThemeChangedNotification")),
|
||||
None,
|
||||
ns_string!("effectiveAppearance"),
|
||||
NSKeyValueObservingOptions::NSKeyValueObservingOptionNew
|
||||
| NSKeyValueObservingOptions::NSKeyValueObservingOptionOld,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
@@ -717,11 +765,11 @@ impl WindowDelegate {
|
||||
delegate.set_blur(attrs.blur);
|
||||
}
|
||||
|
||||
if let Some(dim) = attrs.min_inner_size {
|
||||
delegate.set_min_inner_size(Some(dim));
|
||||
if let Some(dim) = attrs.min_surface_size {
|
||||
delegate.set_min_surface_size(Some(dim));
|
||||
}
|
||||
if let Some(dim) = attrs.max_inner_size {
|
||||
delegate.set_max_inner_size(Some(dim));
|
||||
if let Some(dim) = attrs.max_surface_size {
|
||||
delegate.set_max_surface_size(Some(dim));
|
||||
}
|
||||
|
||||
delegate.set_window_level(attrs.window_level);
|
||||
@@ -771,31 +819,33 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
||||
self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event);
|
||||
let window_id = RootWindowId(self.window().id());
|
||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.window_event(event_loop, window_id, event);
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_scale_factor_changed(&self, scale_factor: CGFloat) {
|
||||
let app_delegate = &self.ivars().app_delegate;
|
||||
let window = self.window();
|
||||
|
||||
let content_size = window.contentRectForFrameRect(window.frame()).size;
|
||||
let content_size = LogicalSize::new(content_size.width, content_size.height);
|
||||
|
||||
let suggested_size = content_size.to_physical(scale_factor);
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
app_delegate.handle_window_event(window.id(), WindowEvent::ScaleFactorChanged {
|
||||
let new_surface_size = Arc::new(Mutex::new(suggested_size));
|
||||
self.queue_event(WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
|
||||
});
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
let physical_size = *new_surface_size.lock().unwrap();
|
||||
drop(new_surface_size);
|
||||
|
||||
if physical_size != suggested_size {
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
}
|
||||
app_delegate.handle_window_event(window.id(), WindowEvent::Resized(physical_size));
|
||||
self.queue_event(WindowEvent::SurfaceResized(physical_size));
|
||||
}
|
||||
|
||||
fn emit_move_event(&self) {
|
||||
@@ -869,21 +919,21 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
self.ivars().app_delegate.queue_redraw(self.window().id());
|
||||
self.ivars().app_state.queue_redraw(self.window().id());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
pub fn outer_position(&self) -> PhysicalPosition<i32> {
|
||||
let position = flip_window_screen_coordinates(self.window().frame());
|
||||
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
|
||||
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
|
||||
}
|
||||
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
pub fn inner_position(&self) -> PhysicalPosition<i32> {
|
||||
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
|
||||
let position = flip_window_screen_coordinates(content_rect);
|
||||
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
|
||||
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
|
||||
}
|
||||
|
||||
pub fn set_outer_position(&self, position: Position) {
|
||||
@@ -896,7 +946,7 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
|
||||
let logical = LogicalSize::new(content_rect.size.width, content_rect.size.height);
|
||||
logical.to_physical(self.scale_factor())
|
||||
@@ -910,14 +960,14 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
pub fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let size = size.to_logical(scale_factor);
|
||||
self.window().setContentSize(NSSize::new(size.width, size.height));
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
||||
pub fn set_min_surface_size(&self, dimensions: Option<Size>) {
|
||||
let dimensions =
|
||||
dimensions.unwrap_or(Size::Logical(LogicalSize { width: 0.0, height: 0.0 }));
|
||||
let min_size = dimensions.to_logical::<CGFloat>(self.scale_factor());
|
||||
@@ -936,7 +986,7 @@ impl WindowDelegate {
|
||||
self.window().setContentSize(current_size);
|
||||
}
|
||||
|
||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||
pub fn set_max_surface_size(&self, dimensions: Option<Size>) {
|
||||
let dimensions = dimensions.unwrap_or(Size::Logical(LogicalSize {
|
||||
width: f32::MAX as f64,
|
||||
height: f32::MAX as f64,
|
||||
@@ -958,8 +1008,8 @@ impl WindowDelegate {
|
||||
self.window().setContentSize(current_size);
|
||||
}
|
||||
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
let increments = self.ivars().resize_increments.get();
|
||||
pub fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
let increments = self.ivars().surface_resize_increments.get();
|
||||
let (w, h) = (increments.width, increments.height);
|
||||
if w > 1.0 || h > 1.0 {
|
||||
Some(LogicalSize::new(w, h).to_physical(self.scale_factor()))
|
||||
@@ -968,9 +1018,9 @@ impl WindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_resize_increments(&self, increments: Option<Size>) {
|
||||
pub fn set_surface_resize_increments(&self, increments: Option<Size>) {
|
||||
// XXX the resize increments are only used during live resizes.
|
||||
self.ivars().resize_increments.set(
|
||||
self.ivars().surface_resize_increments.set(
|
||||
increments
|
||||
.map(|increments| {
|
||||
let logical = increments.to_logical::<f64>(self.scale_factor());
|
||||
@@ -1075,18 +1125,18 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
let associate_mouse_cursor = match mode {
|
||||
CursorGrabMode::Locked => false,
|
||||
CursorGrabMode::None => true,
|
||||
CursorGrabMode::Confined => {
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
return Err(NotSupportedError::new("confined cursor is not supported").into())
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
|
||||
CGDisplay::associate_mouse_and_mouse_cursor_position(associate_mouse_cursor)
|
||||
.map_err(|status| ExternalError::Os(os_error!(OsError::CGError(status))))
|
||||
.map_err(|status| os_error!(format!("CGError {status}")).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1104,8 +1154,8 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), ExternalError> {
|
||||
let physical_window_position = self.inner_position().unwrap();
|
||||
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), RequestError> {
|
||||
let physical_window_position = self.inner_position();
|
||||
let scale_factor = self.scale_factor();
|
||||
let window_position = physical_window_position.to_logical::<CGFloat>(scale_factor);
|
||||
let logical_cursor_position = cursor_position.to_logical::<CGFloat>(scale_factor);
|
||||
@@ -1114,33 +1164,31 @@ impl WindowDelegate {
|
||||
y: logical_cursor_position.y + window_position.y,
|
||||
};
|
||||
CGDisplay::warp_mouse_cursor_position(point)
|
||||
.map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?;
|
||||
.map_err(|status| os_error!(format!("CGError {status}")))?;
|
||||
CGDisplay::associate_mouse_and_mouse_cursor_position(true)
|
||||
.map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?;
|
||||
.map_err(|status| os_error!(format!("CGError {status}")))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
pub fn drag_window(&self) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let event = NSApplication::sharedApplication(mtm).currentEvent().unwrap();
|
||||
self.window().performWindowDragWithEvent(&event);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("drag_resize_window is not supported"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) {
|
||||
self.window().setIgnoresMouseEvents(!hittest);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn is_zoomed(&self) -> bool {
|
||||
@@ -1373,7 +1421,7 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
match (old_fullscreen, fullscreen) {
|
||||
(None, Some(_)) => {
|
||||
(None, Some(fullscreen)) => {
|
||||
// `toggleFullScreen` doesn't work if the `StyleMask` is none, so we
|
||||
// set a normal style temporarily. The previous state will be
|
||||
// restored in `WindowDelegate::window_did_exit_fullscreen`.
|
||||
@@ -1383,6 +1431,17 @@ impl WindowDelegate {
|
||||
self.set_style_mask(required);
|
||||
self.ivars().saved_style.set(Some(curr_mask));
|
||||
}
|
||||
|
||||
// In borderless games, we want to disable the dock and menu bar
|
||||
// by setting the presentation options. We do this here rather than in
|
||||
// `window:willUseFullScreenPresentationOptions` because for some reason
|
||||
// the menu bar remains interactable despite being hidden.
|
||||
if self.is_borderless_game() && matches!(fullscreen, Fullscreen::Borderless(_)) {
|
||||
let presentation_options = NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||
app.setPresentationOptions(presentation_options);
|
||||
}
|
||||
|
||||
toggle_fullscreen(self.window());
|
||||
},
|
||||
(Some(Fullscreen::Borderless(_)), None) => {
|
||||
@@ -1390,13 +1449,7 @@ impl WindowDelegate {
|
||||
toggle_fullscreen(self.window());
|
||||
},
|
||||
(Some(Fullscreen::Exclusive(ref video_mode)), None) => {
|
||||
unsafe {
|
||||
ffi::CGRestorePermanentDisplayConfiguration();
|
||||
assert_eq!(
|
||||
ffi::CGDisplayRelease(video_mode.monitor().native_identifier()),
|
||||
ffi::kCGErrorSuccess
|
||||
);
|
||||
};
|
||||
restore_and_release_display(&video_mode.monitor());
|
||||
toggle_fullscreen(self.window());
|
||||
},
|
||||
(Some(Fullscreen::Borderless(_)), Some(Fullscreen::Exclusive(_))) => {
|
||||
@@ -1427,13 +1480,7 @@ impl WindowDelegate {
|
||||
);
|
||||
app.setPresentationOptions(presentation_options);
|
||||
|
||||
unsafe {
|
||||
ffi::CGRestorePermanentDisplayConfiguration();
|
||||
assert_eq!(
|
||||
ffi::CGDisplayRelease(video_mode.monitor().native_identifier()),
|
||||
ffi::kCGErrorSuccess
|
||||
);
|
||||
};
|
||||
restore_and_release_display(&video_mode.monitor());
|
||||
|
||||
// Restore the normal window level following the Borderless fullscreen
|
||||
// `CGShieldingWindowLevel() + 1` hack.
|
||||
@@ -1572,30 +1619,6 @@ impl WindowDelegate {
|
||||
Some(monitor)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_04")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||
let mut window_handle = rwh_04::AppKitHandle::empty();
|
||||
window_handle.ns_window = self.window() as *const WinitWindow as *mut _;
|
||||
window_handle.ns_view = Retained::as_ptr(&self.contentView().unwrap()) as *mut _;
|
||||
rwh_04::RawWindowHandle::AppKit(window_handle)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
||||
let mut window_handle = rwh_05::AppKitWindowHandle::empty();
|
||||
window_handle.ns_window = self.window() as *const WinitWindow as *mut _;
|
||||
window_handle.ns_view = Retained::as_ptr(&self.view()) as *mut _;
|
||||
rwh_05::RawWindowHandle::AppKit(window_handle)
|
||||
}
|
||||
|
||||
#[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_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
|
||||
@@ -1615,20 +1638,28 @@ impl WindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
self.ivars().current_theme.get()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
self.window().isKeyWindow()
|
||||
}
|
||||
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
unsafe { self.window().appearance() }
|
||||
.map(|appearance| appearance_to_theme(&appearance))
|
||||
.or_else(|| {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
|
||||
if app.respondsToSelector(sel!(effectiveAppearance)) {
|
||||
Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
|
||||
} else {
|
||||
Some(Theme::Light)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_theme(&self, theme: Option<Theme>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
set_ns_theme(theme, mtm);
|
||||
self.ivars().current_theme.set(theme.or_else(|| Some(get_ns_theme(mtm))));
|
||||
unsafe { self.window().setAppearance(theme_to_appearance(theme).as_deref()) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1649,6 +1680,21 @@ impl WindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
fn restore_and_release_display(monitor: &MonitorHandle) {
|
||||
let available_monitors = monitor::available_monitors();
|
||||
if available_monitors.contains(monitor) {
|
||||
unsafe {
|
||||
ffi::CGRestorePermanentDisplayConfiguration();
|
||||
assert_eq!(ffi::CGDisplayRelease(monitor.native_identifier()), ffi::kCGErrorSuccess);
|
||||
};
|
||||
} else {
|
||||
warn!(
|
||||
monitor = monitor.name(),
|
||||
"Tried to restore exclusive fullscreen on a monitor that is no longer available"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowExtMacOS for WindowDelegate {
|
||||
#[inline]
|
||||
fn simple_fullscreen(&self) -> bool {
|
||||
@@ -1782,39 +1828,52 @@ impl WindowExtMacOS for WindowDelegate {
|
||||
fn option_as_alt(&self) -> OptionAsAlt {
|
||||
self.view().option_as_alt()
|
||||
}
|
||||
|
||||
fn set_borderless_game(&self, borderless_game: bool) {
|
||||
self.ivars().is_borderless_game.set(borderless_game);
|
||||
}
|
||||
|
||||
fn is_borderless_game(&self) -> bool {
|
||||
self.ivars().is_borderless_game.get()
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_STANDARD_FRAME: NSRect =
|
||||
NSRect::new(NSPoint::new(50.0, 50.0), NSSize::new(800.0, 600.0));
|
||||
|
||||
pub(super) fn get_ns_theme(mtm: MainThreadMarker) -> Theme {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
if !app.respondsToSelector(sel!(effectiveAppearance)) {
|
||||
return Theme::Light;
|
||||
}
|
||||
let appearance = app.effectiveAppearance();
|
||||
let name = appearance
|
||||
.bestMatchFromAppearancesWithNames(&NSArray::from_id_slice(&[
|
||||
NSString::from_str("NSAppearanceNameAqua"),
|
||||
NSString::from_str("NSAppearanceNameDarkAqua"),
|
||||
]))
|
||||
.unwrap();
|
||||
match &*name.to_string() {
|
||||
"NSAppearanceNameDarkAqua" => Theme::Dark,
|
||||
_ => Theme::Light,
|
||||
fn dark_appearance_name() -> &'static NSString {
|
||||
// Don't use the static `NSAppearanceNameDarkAqua` to allow linking on macOS < 10.14
|
||||
ns_string!("NSAppearanceNameDarkAqua")
|
||||
}
|
||||
|
||||
pub fn appearance_to_theme(appearance: &NSAppearance) -> Theme {
|
||||
let best_match = appearance.bestMatchFromAppearancesWithNames(&NSArray::from_id_slice(&[
|
||||
unsafe { NSAppearanceNameAqua.copy() },
|
||||
dark_appearance_name().copy(),
|
||||
]));
|
||||
if let Some(best_match) = best_match {
|
||||
if *best_match == *dark_appearance_name() {
|
||||
Theme::Dark
|
||||
} else {
|
||||
Theme::Light
|
||||
}
|
||||
} else {
|
||||
warn!(?appearance, "failed to determine the theme of the appearance");
|
||||
// Default to light in this case
|
||||
Theme::Light
|
||||
}
|
||||
}
|
||||
|
||||
fn set_ns_theme(theme: Option<Theme>, mtm: MainThreadMarker) {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
if app.respondsToSelector(sel!(effectiveAppearance)) {
|
||||
let appearance = theme.map(|t| {
|
||||
let name = match t {
|
||||
Theme::Dark => NSString::from_str("NSAppearanceNameDarkAqua"),
|
||||
Theme::Light => NSString::from_str("NSAppearanceNameAqua"),
|
||||
};
|
||||
NSAppearance::appearanceNamed(&name).unwrap()
|
||||
});
|
||||
app.setAppearance(appearance.as_ref().map(|a| a.as_ref()));
|
||||
fn theme_to_appearance(theme: Option<Theme>) -> Option<Retained<NSAppearance>> {
|
||||
let appearance = match theme? {
|
||||
Theme::Light => unsafe { NSAppearance::appearanceNamed(NSAppearanceNameAqua) },
|
||||
Theme::Dark => NSAppearance::appearanceNamed(dark_appearance_name()),
|
||||
};
|
||||
if let Some(appearance) = appearance {
|
||||
Some(appearance)
|
||||
} else {
|
||||
warn!(?theme, "could not find appearance for theme");
|
||||
// Assume system appearance in this case
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,32 @@
|
||||
use std::cell::RefCell;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use super::app_state::HandlePendingUserEvents;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
||||
use crate::application::ApplicationHandler;
|
||||
|
||||
struct EventHandlerData {
|
||||
#[allow(clippy::type_complexity)]
|
||||
handler: Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'static>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventHandlerData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EventHandlerData").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A helper type for storing a reference to `ApplicationHandler`, allowing interior mutable access
|
||||
/// to it within the execution of a closure.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct EventHandler {
|
||||
/// This can be in the following states:
|
||||
/// - Not registered by the event loop (None).
|
||||
/// - Present (Some(handler)).
|
||||
/// - Currently executing the handler / in use (RefCell borrowed).
|
||||
inner: RefCell<Option<EventHandlerData>>,
|
||||
inner: RefCell<Option<&'static mut dyn ApplicationHandler>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventHandler {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let state = match self.inner.try_borrow().as_deref() {
|
||||
Ok(Some(_)) => "<available>",
|
||||
Ok(None) => "<not set>",
|
||||
Err(_) => "<in use>",
|
||||
};
|
||||
f.debug_struct("EventHandler").field("state", &state).finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventHandler {
|
||||
pub(crate) const fn new() -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { inner: RefCell::new(None) }
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ impl EventHandler {
|
||||
/// from within the closure.
|
||||
pub(crate) fn set<'handler, R>(
|
||||
&self,
|
||||
handler: impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'handler,
|
||||
app: &'handler mut dyn ApplicationHandler,
|
||||
closure: impl FnOnce() -> R,
|
||||
) -> R {
|
||||
// SAFETY: We extend the lifetime of the handler here so that we can
|
||||
@@ -48,9 +48,9 @@ impl EventHandler {
|
||||
// extended beyond `'handler`.
|
||||
let handler = unsafe {
|
||||
mem::transmute::<
|
||||
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'handler>,
|
||||
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'static>,
|
||||
>(Box::new(handler))
|
||||
&'handler mut dyn ApplicationHandler,
|
||||
&'static mut dyn ApplicationHandler,
|
||||
>(app)
|
||||
};
|
||||
|
||||
match self.inner.try_borrow_mut().as_deref_mut() {
|
||||
@@ -58,7 +58,7 @@ impl EventHandler {
|
||||
unreachable!("tried to set handler while another was already set");
|
||||
},
|
||||
Ok(data @ None) => {
|
||||
*data = Some(EventHandlerData { handler });
|
||||
*data = Some(handler);
|
||||
},
|
||||
Err(_) => {
|
||||
unreachable!("tried to set handler that is currently in use");
|
||||
@@ -101,31 +101,29 @@ impl EventHandler {
|
||||
// soundness.
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(crate) fn in_use(&self) -> bool {
|
||||
self.inner.try_borrow().is_err()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(crate) fn ready(&self) -> bool {
|
||||
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
|
||||
}
|
||||
|
||||
pub(crate) fn handle_event(
|
||||
&self,
|
||||
event: Event<HandlePendingUserEvents>,
|
||||
event_loop: &RootActiveEventLoop,
|
||||
) {
|
||||
pub(crate) fn handle(&self, callback: impl FnOnce(&mut dyn ApplicationHandler)) {
|
||||
match self.inner.try_borrow_mut().as_deref_mut() {
|
||||
Ok(Some(EventHandlerData { handler })) => {
|
||||
Ok(Some(user_app)) => {
|
||||
// It is important that we keep the reference borrowed here,
|
||||
// so that `in_use` can properly detect that the handler is
|
||||
// still in use.
|
||||
//
|
||||
// If the handler unwinds, the `RefMut` will ensure that the
|
||||
// handler is no longer borrowed.
|
||||
(handler)(event, event_loop);
|
||||
callback(*user_app);
|
||||
},
|
||||
Ok(None) => {
|
||||
// `NSApplication`, our app delegate and this handler are all
|
||||
// `NSApplication`, our app state and this handler are all
|
||||
// global state and so it's not impossible that we could get
|
||||
// an event after the application has exited the `EventLoop`.
|
||||
tracing::error!("tried to run event handler, but no handler was set");
|
||||
13
src/platform_impl/apple/mod.rs
Normal file
13
src/platform_impl/apple/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
//! Apple/Darwin-specific implementations
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod appkit;
|
||||
mod event_handler;
|
||||
mod notification_center;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
mod uikit;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use self::appkit::*;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub use self::uikit::*;
|
||||
27
src/platform_impl/apple/notification_center.rs
Normal file
27
src/platform_impl/apple/notification_center.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use block2::RcBlock;
|
||||
use objc2::rc::Retained;
|
||||
use objc2_foundation::{NSNotification, NSNotificationCenter, NSNotificationName, NSObject};
|
||||
|
||||
/// Observe the given notification.
|
||||
///
|
||||
/// This is used in Winit as an alternative to declaring an application delegate, as we want to
|
||||
/// give the user full control over those.
|
||||
pub fn create_observer(
|
||||
center: &NSNotificationCenter,
|
||||
name: &NSNotificationName,
|
||||
handler: impl Fn(&NSNotification) + 'static,
|
||||
) -> Retained<NSObject> {
|
||||
let block = RcBlock::new(move |notification: NonNull<NSNotification>| {
|
||||
handler(unsafe { notification.as_ref() });
|
||||
});
|
||||
unsafe {
|
||||
center.addObserverForName_object_queue_usingBlock(
|
||||
Some(name),
|
||||
None, // No sender filter
|
||||
None, // No queue, run on posting thread (i.e. main thread)
|
||||
&block,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
#![deny(unused_results)]
|
||||
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::cell::{OnceCell, RefCell, RefMut};
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
use std::time::Instant;
|
||||
use std::{fmt, mem, ptr};
|
||||
use std::{mem, ptr};
|
||||
|
||||
use core_foundation::base::CFRelease;
|
||||
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
||||
@@ -14,18 +15,20 @@ use core_foundation::runloop::{
|
||||
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
||||
};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{msg_send, sel};
|
||||
use objc2::sel;
|
||||
use objc2_foundation::{
|
||||
CGRect, CGSize, MainThreadMarker, NSInteger, NSObjectProtocol, NSOperatingSystemVersion,
|
||||
NSProcessInfo,
|
||||
};
|
||||
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
|
||||
|
||||
use super::super::event_handler::EventHandler;
|
||||
use super::window::WinitUIWindow;
|
||||
use super::ActiveEventLoop;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
||||
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::event_loop::ControlFlow;
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
macro_rules! bug {
|
||||
@@ -40,33 +43,50 @@ macro_rules! bug_assert {
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct HandlePendingUserEvents;
|
||||
/// Get the global event handler for the application.
|
||||
///
|
||||
/// This is stored separately from AppState, since AppState needs to be accessible while the handler
|
||||
/// is executing.
|
||||
fn get_handler(mtm: MainThreadMarker) -> &'static EventHandler {
|
||||
// TODO(madsmtm): Use `MainThreadBound` once that is possible in `static`s.
|
||||
struct StaticMainThreadBound<T>(T);
|
||||
|
||||
pub(crate) struct EventLoopHandler {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) handler: Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop)>,
|
||||
pub(crate) event_loop: RootActiveEventLoop,
|
||||
impl<T> StaticMainThreadBound<T> {
|
||||
const fn get(&self, _mtm: MainThreadMarker) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for StaticMainThreadBound<T> {}
|
||||
unsafe impl<T> Sync for StaticMainThreadBound<T> {}
|
||||
|
||||
// SAFETY: Creating `StaticMainThreadBound` in a `const` context, where there is no concept
|
||||
// of the main thread.
|
||||
static GLOBAL: StaticMainThreadBound<OnceCell<EventHandler>> =
|
||||
StaticMainThreadBound(OnceCell::new());
|
||||
|
||||
GLOBAL.get(mtm).get_or_init(EventHandler::new)
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventLoopHandler {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EventLoopHandler")
|
||||
.field("handler", &"...")
|
||||
.field("event_loop", &self.event_loop)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopHandler {
|
||||
fn handle_event(&mut self, event: Event<HandlePendingUserEvents>) {
|
||||
(self.handler)(event, &self.event_loop)
|
||||
}
|
||||
fn handle_event(mtm: MainThreadMarker, event: Event) {
|
||||
let event_loop = &ActiveEventLoop { mtm };
|
||||
get_handler(mtm).handle(|app| match event {
|
||||
Event::NewEvents(cause) => app.new_events(event_loop, cause),
|
||||
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
|
||||
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
|
||||
Event::UserWakeUp => app.proxy_wake_up(event_loop),
|
||||
Event::Suspended => app.suspended(event_loop),
|
||||
Event::Resumed => app.resumed(event_loop),
|
||||
Event::CreateSurfaces => app.can_create_surfaces(event_loop),
|
||||
Event::AboutToWait => app.about_to_wait(event_loop),
|
||||
Event::LoopExiting => app.exiting(event_loop),
|
||||
Event::MemoryWarning => app.memory_warning(event_loop),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum EventWrapper {
|
||||
StaticEvent(Event<HandlePendingUserEvents>),
|
||||
StaticEvent(Event),
|
||||
ScaleFactorChanged(ScaleFactorChanged),
|
||||
}
|
||||
|
||||
@@ -78,17 +98,11 @@ pub struct ScaleFactorChanged {
|
||||
}
|
||||
|
||||
enum UserCallbackTransitionResult<'a> {
|
||||
Success {
|
||||
handler: EventLoopHandler,
|
||||
active_control_flow: ControlFlow,
|
||||
processing_redraws: bool,
|
||||
},
|
||||
ReentrancyPrevented {
|
||||
queued_events: &'a mut Vec<EventWrapper>,
|
||||
},
|
||||
Success { active_control_flow: ControlFlow, processing_redraws: bool },
|
||||
ReentrancyPrevented { queued_events: &'a mut Vec<EventWrapper> },
|
||||
}
|
||||
|
||||
impl Event<HandlePendingUserEvents> {
|
||||
impl Event {
|
||||
fn is_redraw(&self) -> bool {
|
||||
matches!(self, Event::WindowEvent { event: WindowEvent::RedrawRequested, .. })
|
||||
}
|
||||
@@ -98,19 +112,11 @@ impl Event<HandlePendingUserEvents> {
|
||||
#[derive(Debug)]
|
||||
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
|
||||
enum AppStateImpl {
|
||||
NotLaunched {
|
||||
queued_windows: Vec<Retained<WinitUIWindow>>,
|
||||
Initial {
|
||||
queued_events: Vec<EventWrapper>,
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
},
|
||||
Launching {
|
||||
queued_windows: Vec<Retained<WinitUIWindow>>,
|
||||
queued_events: Vec<EventWrapper>,
|
||||
queued_handler: EventLoopHandler,
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
},
|
||||
ProcessingEvents {
|
||||
handler: EventLoopHandler,
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
active_control_flow: ControlFlow,
|
||||
},
|
||||
@@ -120,16 +126,12 @@ enum AppStateImpl {
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
},
|
||||
ProcessingRedraws {
|
||||
handler: EventLoopHandler,
|
||||
active_control_flow: ControlFlow,
|
||||
},
|
||||
Waiting {
|
||||
waiting_handler: EventLoopHandler,
|
||||
start: Instant,
|
||||
},
|
||||
PollFinished {
|
||||
waiting_handler: EventLoopHandler,
|
||||
},
|
||||
PollFinished,
|
||||
Terminated,
|
||||
}
|
||||
|
||||
@@ -138,6 +140,7 @@ pub(crate) struct AppState {
|
||||
app_state: Option<AppStateImpl>,
|
||||
control_flow: ControlFlow,
|
||||
waker: EventLoopWaker,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -154,13 +157,13 @@ impl AppState {
|
||||
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(),
|
||||
app_state: Some(AppStateImpl::Initial {
|
||||
queued_events: Vec::new(),
|
||||
queued_gpu_redraws: HashSet::new(),
|
||||
}),
|
||||
control_flow: ControlFlow::default(),
|
||||
waker,
|
||||
proxy_wake_up: Arc::new(AtomicBool::new(false)),
|
||||
});
|
||||
}
|
||||
init_guard(&mut guard);
|
||||
@@ -206,46 +209,25 @@ impl AppState {
|
||||
}
|
||||
|
||||
fn has_launched(&self) -> bool {
|
||||
!matches!(self.state(), AppStateImpl::NotLaunched { .. } | AppStateImpl::Launching { .. })
|
||||
!matches!(self.state(), AppStateImpl::Initial { .. })
|
||||
}
|
||||
|
||||
fn has_terminated(&self) -> bool {
|
||||
matches!(self.state(), AppStateImpl::Terminated)
|
||||
}
|
||||
|
||||
fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) {
|
||||
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::NotLaunched { queued_windows, queued_events, queued_gpu_redraws } => {
|
||||
(queued_windows, queued_events, queued_gpu_redraws)
|
||||
fn did_finish_launching_transition(&mut self) -> Vec<EventWrapper> {
|
||||
let (events, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::Initial { queued_events, queued_gpu_redraws } => {
|
||||
(queued_events, queued_gpu_redraws)
|
||||
},
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
self.set_state(AppStateImpl::Launching {
|
||||
queued_windows,
|
||||
queued_events,
|
||||
queued_handler,
|
||||
queued_gpu_redraws,
|
||||
});
|
||||
}
|
||||
|
||||
fn did_finish_launching_transition(
|
||||
&mut self,
|
||||
) -> (Vec<Retained<WinitUIWindow>>, Vec<EventWrapper>) {
|
||||
let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::Launching {
|
||||
queued_windows,
|
||||
queued_events,
|
||||
queued_handler,
|
||||
queued_gpu_redraws,
|
||||
} => (queued_windows, queued_events, queued_handler, queued_gpu_redraws),
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
self.set_state(AppStateImpl::ProcessingEvents {
|
||||
handler,
|
||||
active_control_flow: self.control_flow,
|
||||
queued_gpu_redraws,
|
||||
});
|
||||
(windows, events)
|
||||
events
|
||||
}
|
||||
|
||||
fn wakeup_transition(&mut self) -> Option<EventWrapper> {
|
||||
@@ -255,22 +237,18 @@ impl AppState {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (handler, event) = match (self.control_flow, self.take_state()) {
|
||||
(ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => {
|
||||
(waiting_handler, EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)))
|
||||
let event = match (self.control_flow, self.take_state()) {
|
||||
(ControlFlow::Poll, AppStateImpl::PollFinished) => {
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll))
|
||||
},
|
||||
(ControlFlow::Wait, AppStateImpl::Waiting { waiting_handler, start }) => (
|
||||
waiting_handler,
|
||||
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: None,
|
||||
})),
|
||||
),
|
||||
(
|
||||
ControlFlow::WaitUntil(requested_resume),
|
||||
AppStateImpl::Waiting { waiting_handler, start },
|
||||
) => {
|
||||
let event = if Instant::now() >= requested_resume {
|
||||
}))
|
||||
},
|
||||
(ControlFlow::WaitUntil(requested_resume), AppStateImpl::Waiting { start }) => {
|
||||
if Instant::now() >= requested_resume {
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached {
|
||||
start,
|
||||
requested_resume,
|
||||
@@ -280,14 +258,12 @@ impl AppState {
|
||||
start,
|
||||
requested_resume: Some(requested_resume),
|
||||
}))
|
||||
};
|
||||
(waiting_handler, event)
|
||||
}
|
||||
},
|
||||
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
|
||||
};
|
||||
|
||||
self.set_state(AppStateImpl::ProcessingEvents {
|
||||
handler,
|
||||
queued_gpu_redraws: Default::default(),
|
||||
active_control_flow: self.control_flow,
|
||||
});
|
||||
@@ -298,8 +274,7 @@ impl AppState {
|
||||
// If we're not able to process an event due to recursion or `Init` not having been sent out
|
||||
// yet, then queue the events up.
|
||||
match self.state_mut() {
|
||||
&mut AppStateImpl::Launching { ref mut queued_events, .. }
|
||||
| &mut AppStateImpl::NotLaunched { ref mut queued_events, .. }
|
||||
&mut AppStateImpl::Initial { ref mut queued_events, .. }
|
||||
| &mut AppStateImpl::InUserCallback { ref mut queued_events, .. } => {
|
||||
// A lifetime cast: early returns are not currently handled well with NLL, but
|
||||
// polonius handles them well. This transmute is a safe workaround.
|
||||
@@ -323,17 +298,14 @@ impl AppState {
|
||||
},
|
||||
}
|
||||
|
||||
let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) = match self
|
||||
.take_state()
|
||||
let (queued_gpu_redraws, active_control_flow, processing_redraws) = match self.take_state()
|
||||
{
|
||||
AppStateImpl::Launching { .. }
|
||||
| AppStateImpl::NotLaunched { .. }
|
||||
| AppStateImpl::InUserCallback { .. } => unreachable!(),
|
||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => {
|
||||
(handler, queued_gpu_redraws, active_control_flow, false)
|
||||
AppStateImpl::Initial { .. } | AppStateImpl::InUserCallback { .. } => unreachable!(),
|
||||
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
|
||||
(queued_gpu_redraws, active_control_flow, false)
|
||||
},
|
||||
AppStateImpl::ProcessingRedraws { handler, active_control_flow } => {
|
||||
(handler, Default::default(), active_control_flow, true)
|
||||
AppStateImpl::ProcessingRedraws { active_control_flow } => {
|
||||
(Default::default(), active_control_flow, true)
|
||||
},
|
||||
AppStateImpl::PollFinished { .. }
|
||||
| AppStateImpl::Waiting { .. }
|
||||
@@ -343,17 +315,17 @@ impl AppState {
|
||||
queued_events: Vec::new(),
|
||||
queued_gpu_redraws,
|
||||
});
|
||||
UserCallbackTransitionResult::Success { handler, active_control_flow, processing_redraws }
|
||||
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws }
|
||||
}
|
||||
|
||||
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
|
||||
let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => {
|
||||
(handler, queued_gpu_redraws, active_control_flow)
|
||||
let (queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
||||
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
|
||||
(queued_gpu_redraws, active_control_flow)
|
||||
},
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
self.set_state(AppStateImpl::ProcessingRedraws { handler, active_control_flow });
|
||||
self.set_state(AppStateImpl::ProcessingRedraws { active_control_flow });
|
||||
queued_gpu_redraws
|
||||
}
|
||||
|
||||
@@ -361,10 +333,8 @@ impl AppState {
|
||||
if !self.has_launched() || self.has_terminated() {
|
||||
return;
|
||||
}
|
||||
let (waiting_handler, old) = match self.take_state() {
|
||||
AppStateImpl::ProcessingRedraws { handler, active_control_flow } => {
|
||||
(handler, active_control_flow)
|
||||
},
|
||||
let old = match self.take_state() {
|
||||
AppStateImpl::ProcessingRedraws { active_control_flow } => active_control_flow,
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
|
||||
@@ -372,39 +342,43 @@ impl AppState {
|
||||
match (old, new) {
|
||||
(ControlFlow::Wait, ControlFlow::Wait) => {
|
||||
let start = Instant::now();
|
||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
||||
self.set_state(AppStateImpl::Waiting { start });
|
||||
},
|
||||
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
|
||||
if old_instant == new_instant =>
|
||||
{
|
||||
let start = Instant::now();
|
||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
||||
self.set_state(AppStateImpl::Waiting { start });
|
||||
},
|
||||
(_, ControlFlow::Wait) => {
|
||||
let start = Instant::now();
|
||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
||||
self.set_state(AppStateImpl::Waiting { start });
|
||||
self.waker.stop()
|
||||
},
|
||||
(_, ControlFlow::WaitUntil(new_instant)) => {
|
||||
let start = Instant::now();
|
||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
||||
self.set_state(AppStateImpl::Waiting { start });
|
||||
self.waker.start_at(new_instant)
|
||||
},
|
||||
// Unlike on macOS, handle Poll to Poll transition here to call the waker
|
||||
(_, ControlFlow::Poll) => {
|
||||
self.set_state(AppStateImpl::PollFinished { waiting_handler });
|
||||
self.set_state(AppStateImpl::PollFinished);
|
||||
self.waker.start()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn terminated_transition(&mut self) -> EventLoopHandler {
|
||||
fn terminated_transition(&mut self) {
|
||||
match self.replace_state(AppStateImpl::Terminated) {
|
||||
AppStateImpl::ProcessingEvents { handler, .. } => handler,
|
||||
AppStateImpl::ProcessingEvents { .. } => {},
|
||||
s => bug!("`LoopExiting` happened while not processing events {:?}", s),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn proxy_wake_up(&self) -> Arc<AtomicBool> {
|
||||
self.proxy_wake_up.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) {
|
||||
self.control_flow = control_flow;
|
||||
}
|
||||
@@ -414,31 +388,10 @@ impl AppState {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWindow>) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } => {
|
||||
return queued_windows.push(window.clone())
|
||||
},
|
||||
&mut AppStateImpl::ProcessingEvents { .. }
|
||||
| &mut AppStateImpl::InUserCallback { .. }
|
||||
| &mut AppStateImpl::ProcessingRedraws { .. } => {},
|
||||
s @ &mut AppStateImpl::Launching { .. }
|
||||
| s @ &mut AppStateImpl::Waiting { .. }
|
||||
| s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s),
|
||||
&mut AppStateImpl::Terminated => {
|
||||
panic!("Attempt to create a `Window` after the app has terminated")
|
||||
},
|
||||
}
|
||||
drop(this);
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::NotLaunched { ref mut queued_gpu_redraws, .. }
|
||||
| &mut AppStateImpl::Launching { ref mut queued_gpu_redraws, .. }
|
||||
&mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. }
|
||||
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. }
|
||||
| &mut AppStateImpl::InUserCallback { ref mut queued_gpu_redraws, .. } => {
|
||||
let _ = queued_gpu_redraws.insert(window);
|
||||
@@ -452,55 +405,27 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<W
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn will_launch(mtm: MainThreadMarker, queued_handler: EventLoopHandler) {
|
||||
AppState::get_mut(mtm).will_launch_transition(queued_handler)
|
||||
pub(crate) fn launch(mtm: MainThreadMarker, app: &mut dyn ApplicationHandler, run: impl FnOnce()) {
|
||||
get_handler(mtm).set(app, run)
|
||||
}
|
||||
|
||||
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let windows = match this.state_mut() {
|
||||
AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows),
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
|
||||
this.waker.start();
|
||||
|
||||
// have to drop RefMut because the window setup code below can trigger new events
|
||||
drop(this);
|
||||
|
||||
for window in windows {
|
||||
// Do a little screen dance here to account for windows being created before
|
||||
// `UIApplicationMain` is called. This fixes visual issues such as being
|
||||
// offcenter and sized incorrectly. Additionally, to fix orientation issues, we
|
||||
// gotta reset the `rootViewController`.
|
||||
//
|
||||
// relevant iOS log:
|
||||
// ```
|
||||
// [ApplicationLifecycle] Windows were created before application initialization
|
||||
// completed. This may result in incorrect visual appearance.
|
||||
// ```
|
||||
let screen = window.screen();
|
||||
let _: () = unsafe { msg_send![&window, setScreen: ptr::null::<AnyObject>()] };
|
||||
window.setScreen(&screen);
|
||||
let events = AppState::get_mut(mtm).did_finish_launching_transition();
|
||||
|
||||
let controller = window.rootViewController();
|
||||
window.setRootViewController(None);
|
||||
window.setRootViewController(controller.as_deref());
|
||||
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition();
|
||||
|
||||
let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init)))
|
||||
.chain(events);
|
||||
let events = [
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init)),
|
||||
EventWrapper::StaticEvent(Event::CreateSurfaces),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(events);
|
||||
handle_nonuser_events(mtm, events);
|
||||
|
||||
// the above window dance hack, could possibly trigger new windows to be created.
|
||||
// we can just set those windows up normally, as they were created after didFinishLaunching
|
||||
for window in windows {
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
}
|
||||
|
||||
// AppState::did_finish_launching handles the special transition `Init`
|
||||
@@ -528,18 +453,15 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
return;
|
||||
}
|
||||
|
||||
let (mut handler, active_control_flow, processing_redraws) =
|
||||
match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
||||
queued_events.extend(events);
|
||||
return;
|
||||
},
|
||||
UserCallbackTransitionResult::Success {
|
||||
handler,
|
||||
active_control_flow,
|
||||
processing_redraws,
|
||||
} => (handler, active_control_flow, processing_redraws),
|
||||
};
|
||||
let (active_control_flow, processing_redraws) = match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
||||
queued_events.extend(events);
|
||||
return;
|
||||
},
|
||||
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws } => {
|
||||
(active_control_flow, processing_redraws)
|
||||
},
|
||||
};
|
||||
drop(this);
|
||||
|
||||
for wrapper in events {
|
||||
@@ -553,9 +475,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
event
|
||||
);
|
||||
}
|
||||
handler.handle_event(event)
|
||||
handle_event(mtm, event)
|
||||
},
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,9 +501,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
queued_gpu_redraws.is_empty(),
|
||||
"redraw queued while processing redraws"
|
||||
);
|
||||
AppStateImpl::ProcessingRedraws { handler, active_control_flow }
|
||||
AppStateImpl::ProcessingRedraws { active_control_flow }
|
||||
} else {
|
||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow }
|
||||
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow }
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -599,9 +521,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
event
|
||||
);
|
||||
}
|
||||
handler.handle_event(event)
|
||||
handle_event(mtm, event)
|
||||
},
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -609,23 +531,23 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
|
||||
fn handle_user_events(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let (mut handler, active_control_flow, processing_redraws) =
|
||||
match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
|
||||
bug!("unexpected attempted to process an event")
|
||||
},
|
||||
UserCallbackTransitionResult::Success {
|
||||
handler,
|
||||
active_control_flow,
|
||||
processing_redraws,
|
||||
} => (handler, active_control_flow, processing_redraws),
|
||||
};
|
||||
let (active_control_flow, processing_redraws) = match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
|
||||
bug!("unexpected attempted to process an event")
|
||||
},
|
||||
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws } => {
|
||||
(active_control_flow, processing_redraws)
|
||||
},
|
||||
};
|
||||
if processing_redraws {
|
||||
bug!("user events attempted to be sent out while `ProcessingRedraws`");
|
||||
}
|
||||
let proxy_wake_up = this.proxy_wake_up.clone();
|
||||
drop(this);
|
||||
|
||||
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
||||
if proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||
handle_event(mtm, Event::UserWakeUp);
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
@@ -642,23 +564,22 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
this.app_state = Some(AppStateImpl::ProcessingEvents {
|
||||
handler,
|
||||
queued_gpu_redraws,
|
||||
active_control_flow,
|
||||
});
|
||||
this.app_state =
|
||||
Some(AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow });
|
||||
break;
|
||||
}
|
||||
drop(this);
|
||||
|
||||
for wrapper in queued_events {
|
||||
match wrapper {
|
||||
EventWrapper::StaticEvent(event) => handler.handle_event(event),
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
|
||||
EventWrapper::StaticEvent(event) => handle_event(mtm, event),
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||
}
|
||||
}
|
||||
|
||||
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
||||
if proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||
handle_event(mtm, Event::UserWakeUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,26 +661,26 @@ pub(crate) fn terminated(application: &UIApplication) {
|
||||
handle_nonuser_events(mtm, events);
|
||||
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let mut handler = this.terminated_transition();
|
||||
this.terminated_transition();
|
||||
drop(this);
|
||||
|
||||
handler.handle_event(Event::LoopExiting)
|
||||
handle_event(mtm, Event::LoopExiting)
|
||||
}
|
||||
|
||||
fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged) {
|
||||
fn handle_hidpi_proxy(mtm: MainThreadMarker, event: ScaleFactorChanged) {
|
||||
let ScaleFactorChanged { suggested_size, scale_factor, window } = event;
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
let new_surface_size = Arc::new(Mutex::new(suggested_size));
|
||||
let event = Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
|
||||
},
|
||||
};
|
||||
handler.handle_event(event);
|
||||
handle_event(mtm, event);
|
||||
let (view, screen_frame) = get_view_and_screen_frame(&window);
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
let physical_size = *new_surface_size.lock().unwrap();
|
||||
drop(new_surface_size);
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = CGSize::new(logical_size.width, logical_size.height);
|
||||
let new_frame: CGRect = CGRect::new(screen_frame.origin, size);
|
||||
446
src/platform_impl/apple/uikit/event_loop.rs
Normal file
446
src/platform_impl/apple/uikit/event_loop.rs
Normal file
@@ -0,0 +1,446 @@
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use core_foundation::base::{CFIndex, CFRelease};
|
||||
use core_foundation::runloop::{
|
||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopDefaultMode,
|
||||
kCFRunLoopExit, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
|
||||
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
||||
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject};
|
||||
use objc2_ui_kit::{
|
||||
UIApplication, UIApplicationDidBecomeActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification, UIApplicationDidFinishLaunchingNotification,
|
||||
UIApplicationDidReceiveMemoryWarningNotification, UIApplicationMain,
|
||||
UIApplicationWillEnterForegroundNotification, UIApplicationWillResignActiveNotification,
|
||||
UIApplicationWillTerminateNotification, UIScreen,
|
||||
};
|
||||
|
||||
use super::super::notification_center::create_observer;
|
||||
use super::app_state::{send_occluded_event_for_all_windows, AppState, EventWrapper};
|
||||
use super::{app_state, monitor, MonitorHandle};
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform_impl::Window;
|
||||
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window as CoreWindow};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ActiveEventLoop {
|
||||
pub(super) mtm: MainThreadMarker,
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
|
||||
let event_loop_proxy = EventLoopProxy::new(AppState::get_mut(self.mtm).proxy_wake_up());
|
||||
RootEventLoopProxy { event_loop_proxy }
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: crate::window::WindowAttributes,
|
||||
) -> Result<Box<dyn CoreWindow>, RequestError> {
|
||||
Ok(Box::new(Window::new(self, window_attributes)?))
|
||||
}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
_source: CustomCursorSource,
|
||||
) -> Result<CustomCursor, RequestError> {
|
||||
Err(NotSupportedError::new("create_custom_cursor is not supported").into())
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
|
||||
Box::new(monitor::uiscreens(self.mtm).into_iter().map(|inner| RootMonitorHandle { inner }))
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
|
||||
#[allow(deprecated)]
|
||||
let monitor = MonitorHandle::new(UIScreen::mainScreen(self.mtm));
|
||||
Some(RootMonitorHandle { inner: monitor })
|
||||
}
|
||||
|
||||
fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||
|
||||
fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
AppState::get_mut(self.mtm).set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
fn system_theme(&self) -> Option<Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
AppState::get_mut(self.mtm).control_flow()
|
||||
}
|
||||
|
||||
fn exit(&self) {
|
||||
// https://developer.apple.com/library/archive/qa/qa1561/_index.html
|
||||
// it is not possible to quit an iOS app gracefully and programmatically
|
||||
tracing::warn!("`ControlFlow::Exit` ignored on iOS");
|
||||
}
|
||||
|
||||
fn exiting(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::UiKit(rwh_06::UiKitDisplayHandle::new());
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::UiKitDisplayHandle::new().into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop {
|
||||
mtm: MainThreadMarker,
|
||||
window_target: ActiveEventLoop,
|
||||
|
||||
// Since iOS 9.0, we no longer need to remove the observers before they are deallocated; the
|
||||
// system instead cleans it up next time it would have posted a notification to it.
|
||||
//
|
||||
// Though we do still need to keep the observers around to prevent them from being deallocated.
|
||||
_did_finish_launching_observer: Retained<NSObject>,
|
||||
_did_become_active_observer: Retained<NSObject>,
|
||||
_will_resign_active_observer: Retained<NSObject>,
|
||||
_will_enter_foreground_observer: Retained<NSObject>,
|
||||
_did_enter_background_observer: Retained<NSObject>,
|
||||
_will_terminate_observer: Retained<NSObject>,
|
||||
_did_receive_memory_warning_observer: Retained<NSObject>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
impl EventLoop {
|
||||
pub(crate) fn new(
|
||||
_: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<EventLoop, EventLoopError> {
|
||||
let mtm = MainThreadMarker::new()
|
||||
.expect("On iOS, `EventLoop` must be created on the main thread");
|
||||
|
||||
static mut SINGLETON_INIT: bool = false;
|
||||
unsafe {
|
||||
assert!(
|
||||
!SINGLETON_INIT,
|
||||
"Only one `EventLoop` is supported on iOS. `EventLoopProxy` might be helpful"
|
||||
);
|
||||
SINGLETON_INIT = true;
|
||||
}
|
||||
|
||||
// this line sets up the main run loop before `UIApplicationMain`
|
||||
setup_control_flow_observers();
|
||||
|
||||
let center = unsafe { NSNotificationCenter::defaultCenter() };
|
||||
|
||||
let _did_finish_launching_observer = create_observer(
|
||||
¢er,
|
||||
// `application:didFinishLaunchingWithOptions:`
|
||||
unsafe { UIApplicationDidFinishLaunchingNotification },
|
||||
move |_| {
|
||||
app_state::did_finish_launching(mtm);
|
||||
},
|
||||
);
|
||||
let _did_become_active_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationDidBecomeActive:`
|
||||
unsafe { UIApplicationDidBecomeActiveNotification },
|
||||
move |_| {
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed));
|
||||
},
|
||||
);
|
||||
let _will_resign_active_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationWillResignActive:`
|
||||
unsafe { UIApplicationWillResignActiveNotification },
|
||||
move |_| {
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended));
|
||||
},
|
||||
);
|
||||
let _will_enter_foreground_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationWillEnterForeground:`
|
||||
unsafe { UIApplicationWillEnterForegroundNotification },
|
||||
move |notification| {
|
||||
let app = unsafe { notification.object() }.expect(
|
||||
"UIApplicationWillEnterForegroundNotification to have application object",
|
||||
);
|
||||
// SAFETY: The `object` in `UIApplicationWillEnterForegroundNotification` is
|
||||
// documented to be `UIApplication`.
|
||||
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
|
||||
send_occluded_event_for_all_windows(&app, false);
|
||||
},
|
||||
);
|
||||
let _did_enter_background_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationDidEnterBackground:`
|
||||
unsafe { UIApplicationDidEnterBackgroundNotification },
|
||||
move |notification| {
|
||||
let app = unsafe { notification.object() }.expect(
|
||||
"UIApplicationDidEnterBackgroundNotification to have application object",
|
||||
);
|
||||
// SAFETY: The `object` in `UIApplicationDidEnterBackgroundNotification` is
|
||||
// documented to be `UIApplication`.
|
||||
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
|
||||
send_occluded_event_for_all_windows(&app, true);
|
||||
},
|
||||
);
|
||||
let _will_terminate_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationWillTerminate:`
|
||||
unsafe { UIApplicationWillTerminateNotification },
|
||||
move |notification| {
|
||||
let app = unsafe { notification.object() }
|
||||
.expect("UIApplicationWillTerminateNotification to have application object");
|
||||
// SAFETY: The `object` in `UIApplicationWillTerminateNotification` is
|
||||
// (somewhat) documented to be `UIApplication`.
|
||||
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
|
||||
app_state::terminated(&app);
|
||||
},
|
||||
);
|
||||
let _did_receive_memory_warning_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationDidReceiveMemoryWarning:`
|
||||
unsafe { UIApplicationDidReceiveMemoryWarningNotification },
|
||||
move |_| {
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::MemoryWarning),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Ok(EventLoop {
|
||||
mtm,
|
||||
window_target: ActiveEventLoop { mtm },
|
||||
_did_finish_launching_observer,
|
||||
_did_become_active_observer,
|
||||
_will_resign_active_observer,
|
||||
_will_enter_foreground_observer,
|
||||
_did_enter_background_observer,
|
||||
_will_terminate_observer,
|
||||
_did_receive_memory_warning_observer,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(self, mut app: A) -> ! {
|
||||
let application: Option<Retained<UIApplication>> =
|
||||
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
|
||||
assert!(
|
||||
application.is_none(),
|
||||
"\
|
||||
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\nNote: \
|
||||
`EventLoop::run_app` calls `UIApplicationMain` on iOS",
|
||||
);
|
||||
|
||||
extern "C" {
|
||||
// These functions are in crt_externs.h.
|
||||
fn _NSGetArgc() -> *mut c_int;
|
||||
fn _NSGetArgv() -> *mut *mut *mut c_char;
|
||||
}
|
||||
|
||||
app_state::launch(self.mtm, &mut app, || unsafe {
|
||||
UIApplicationMain(
|
||||
*_NSGetArgc(),
|
||||
NonNull::new(*_NSGetArgv()).unwrap(),
|
||||
// We intentionally override neither the application nor the delegate, to allow
|
||||
// the user to do so themselves!
|
||||
None,
|
||||
None,
|
||||
);
|
||||
});
|
||||
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
|
||||
&self.window_target
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy {
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
source: CFRunLoopSourceRef,
|
||||
}
|
||||
|
||||
unsafe impl Send for EventLoopProxy {}
|
||||
unsafe impl Sync for EventLoopProxy {}
|
||||
|
||||
impl Clone for EventLoopProxy {
|
||||
fn clone(&self) -> EventLoopProxy {
|
||||
EventLoopProxy::new(self.proxy_wake_up.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventLoopProxy {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
CFRunLoopSourceInvalidate(self.source);
|
||||
CFRelease(self.source as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
fn new(proxy_wake_up: Arc<AtomicBool>) -> EventLoopProxy {
|
||||
unsafe {
|
||||
// just wake up the eventloop
|
||||
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
|
||||
|
||||
// adding a Source to the main CFRunLoop lets us wake it up and
|
||||
// process user events through the normal OS EventLoop mechanisms.
|
||||
let rl = CFRunLoopGetMain();
|
||||
let mut context = CFRunLoopSourceContext {
|
||||
version: 0,
|
||||
info: ptr::null_mut(),
|
||||
retain: None,
|
||||
release: None,
|
||||
copyDescription: None,
|
||||
equal: None,
|
||||
hash: None,
|
||||
schedule: None,
|
||||
cancel: None,
|
||||
perform: event_loop_proxy_handler,
|
||||
};
|
||||
let source = CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::MAX - 1, &mut context);
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||
CFRunLoopWakeUp(rl);
|
||||
|
||||
EventLoopProxy { proxy_wake_up, source }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wake_up(&self) {
|
||||
self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
CFRunLoopSourceSignal(self.source);
|
||||
let rl = CFRunLoopGetMain();
|
||||
CFRunLoopWakeUp(rl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_control_flow_observers() {
|
||||
unsafe {
|
||||
// begin is queued with the highest priority to ensure it is processed before other
|
||||
// observers
|
||||
extern "C" fn control_flow_begin_handler(
|
||||
_: CFRunLoopObserverRef,
|
||||
activity: CFRunLoopActivity,
|
||||
_: *mut c_void,
|
||||
) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(mtm),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
||||
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end
|
||||
// priority to be 0, in order to send AboutToWait before RedrawRequested. This value was
|
||||
// chosen conservatively to guard against apple using different priorities for their redraw
|
||||
// observers in different OS's or on different devices. If it so happens that it's too
|
||||
// conservative, the main symptom would be non-redraw events coming in after `AboutToWait`.
|
||||
//
|
||||
// The value of `0x1e8480` was determined by inspecting stack traces and the associated
|
||||
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
|
||||
//
|
||||
// Also tested to be `0x1e8480` on iPhone 8, iOS 13 beta 4.
|
||||
extern "C" fn control_flow_main_end_handler(
|
||||
_: CFRunLoopObserverRef,
|
||||
activity: CFRunLoopActivity,
|
||||
_: *mut c_void,
|
||||
) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm),
|
||||
kCFRunLoopExit => {}, // may happen when running on macOS
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// end is queued with the lowest priority to ensure it is processed after other observers
|
||||
extern "C" fn control_flow_end_handler(
|
||||
_: CFRunLoopObserverRef,
|
||||
activity: CFRunLoopActivity,
|
||||
_: *mut c_void,
|
||||
) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm),
|
||||
kCFRunLoopExit => {}, // may happen when running on macOS
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let main_loop = CFRunLoopGetMain();
|
||||
|
||||
let begin_observer = CFRunLoopObserverCreate(
|
||||
ptr::null_mut(),
|
||||
kCFRunLoopAfterWaiting,
|
||||
1, // repeat = true
|
||||
CFIndex::MIN,
|
||||
control_flow_begin_handler,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
CFRunLoopAddObserver(main_loop, begin_observer, kCFRunLoopDefaultMode);
|
||||
|
||||
let main_end_observer = CFRunLoopObserverCreate(
|
||||
ptr::null_mut(),
|
||||
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
||||
1, // repeat = true
|
||||
0, // see comment on `control_flow_main_end_handler`
|
||||
control_flow_main_end_handler,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
CFRunLoopAddObserver(main_loop, main_end_observer, kCFRunLoopDefaultMode);
|
||||
|
||||
let end_observer = CFRunLoopObserverCreate(
|
||||
ptr::null_mut(),
|
||||
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
||||
1, // repeat = true
|
||||
CFIndex::MAX,
|
||||
control_flow_end_handler,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
#![allow(clippy::let_unit_value)]
|
||||
|
||||
mod app_delegate;
|
||||
mod app_state;
|
||||
mod event_loop;
|
||||
mod monitor;
|
||||
@@ -10,8 +9,6 @@ mod window;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::event::DeviceId as RootDeviceId;
|
||||
|
||||
pub(crate) use self::event_loop::{
|
||||
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
|
||||
PlatformSpecificEventLoopAttributes,
|
||||
@@ -21,6 +18,7 @@ pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window, WindowId
|
||||
pub(crate) use crate::cursor::{
|
||||
NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource,
|
||||
};
|
||||
use crate::event::DeviceId as RootDeviceId;
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
|
||||
@@ -32,13 +30,22 @@ pub(crate) use crate::platform_impl::Fullscreen;
|
||||
pub struct DeviceId;
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId(usize);
|
||||
|
||||
impl FingerId {
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use std::collections::{BTreeSet, VecDeque};
|
||||
use std::num::{NonZeroU16, NonZeroU32};
|
||||
use std::{fmt, hash, ptr};
|
||||
|
||||
use objc2::mutability::IsRetainable;
|
||||
@@ -9,9 +10,9 @@ use objc2::Message;
|
||||
use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker, NSInteger};
|
||||
use objc2_ui_kit::{UIScreen, UIScreenMode};
|
||||
|
||||
use super::app_state;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::monitor::VideoModeHandle as RootVideoModeHandle;
|
||||
use crate::platform_impl::platform::app_state;
|
||||
|
||||
// Workaround for `MainThreadBound` implementing almost no traits
|
||||
#[derive(Debug)]
|
||||
@@ -44,8 +45,7 @@ impl<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {}
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub struct VideoModeHandle {
|
||||
pub(crate) size: (u32, u32),
|
||||
pub(crate) bit_depth: u16,
|
||||
pub(crate) refresh_rate_millihertz: u32,
|
||||
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
|
||||
screen_mode: MainThreadBoundDelegateImpls<UIScreenMode>,
|
||||
pub(crate) monitor: MonitorHandle,
|
||||
}
|
||||
@@ -60,7 +60,6 @@ impl VideoModeHandle {
|
||||
let size = screen_mode.size();
|
||||
VideoModeHandle {
|
||||
size: (size.width as u32, size.height as u32),
|
||||
bit_depth: 32,
|
||||
refresh_rate_millihertz,
|
||||
screen_mode: MainThreadBoundDelegateImpls(MainThreadBound::new(screen_mode, mtm)),
|
||||
monitor: MonitorHandle::new(uiscreen),
|
||||
@@ -71,11 +70,11 @@ impl VideoModeHandle {
|
||||
self.size.into()
|
||||
}
|
||||
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
self.bit_depth
|
||||
pub fn bit_depth(&self) -> Option<NonZeroU16> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn refresh_rate_millihertz(&self) -> u32 {
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
|
||||
self.refresh_rate_millihertz
|
||||
}
|
||||
|
||||
@@ -131,10 +130,8 @@ impl fmt::Debug for MonitorHandle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MonitorHandle")
|
||||
.field("name", &self.name())
|
||||
.field("size", &self.size())
|
||||
.field("position", &self.position())
|
||||
.field("scale_factor", &self.scale_factor())
|
||||
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
@@ -164,22 +161,23 @@ impl MonitorHandle {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
|
||||
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.ui_screen.get_on_main(|ui_screen| ui_screen.nativeBounds());
|
||||
(bounds.origin.x as f64, bounds.origin.y as f64).into()
|
||||
Some((bounds.origin.x as f64, bounds.origin.y as f64).into())
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64
|
||||
}
|
||||
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
Some(self.ui_screen.get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen)))
|
||||
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
|
||||
Some(run_on_main(|mtm| {
|
||||
VideoModeHandle::new(
|
||||
self.ui_screen(mtm).clone(),
|
||||
self.ui_screen(mtm).currentMode().unwrap(),
|
||||
mtm,
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
|
||||
@@ -214,7 +212,7 @@ impl MonitorHandle {
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 {
|
||||
fn refresh_rate_millihertz(uiscreen: &UIScreen) -> Option<NonZeroU32> {
|
||||
let refresh_rate_millihertz: NSInteger = {
|
||||
let os_capabilities = app_state::os_capabilities();
|
||||
if os_capabilities.maximum_frames_per_second {
|
||||
@@ -235,7 +233,7 @@ fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 {
|
||||
}
|
||||
};
|
||||
|
||||
refresh_rate_millihertz as u32 * 1000
|
||||
NonZeroU32::new(refresh_rate_millihertz as u32 * 1000)
|
||||
}
|
||||
|
||||
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
||||
@@ -4,19 +4,23 @@ use std::cell::{Cell, RefCell};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::{NSObjectProtocol, ProtocolObject};
|
||||
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet};
|
||||
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet, NSString};
|
||||
use objc2_ui_kit::{
|
||||
UICoordinateSpace, UIEvent, UIForceTouchCapability, UIGestureRecognizer,
|
||||
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIPanGestureRecognizer,
|
||||
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer,
|
||||
UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer,
|
||||
UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||
UITextInputTraits, UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||
};
|
||||
|
||||
use super::app_state::{self, EventWrapper};
|
||||
use super::window::WinitUIWindow;
|
||||
use super::{FingerId, DEVICE_ID};
|
||||
use crate::dpi::PhysicalPosition;
|
||||
use crate::event::{Event, Force, Touch, TouchPhase, WindowEvent};
|
||||
use crate::platform_impl::platform::DEVICE_ID;
|
||||
use crate::event::{
|
||||
ElementState, Event, FingerId as RootFingerId, Force, KeyEvent, Touch, TouchPhase, WindowEvent,
|
||||
};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
|
||||
use crate::platform_impl::KeyEventExtra;
|
||||
use crate::window::{WindowAttributes, WindowId as RootWindowId};
|
||||
|
||||
pub struct WinitViewState {
|
||||
@@ -89,7 +93,7 @@ declare_class!(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::Resized(size),
|
||||
event: WindowEvent::SurfaceResized(size),
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -140,7 +144,7 @@ declare_class!(
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
||||
event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
@@ -314,6 +318,11 @@ declare_class!(
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, gesture_event);
|
||||
}
|
||||
|
||||
#[method(canBecomeFirstResponder)]
|
||||
fn can_become_first_responder(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NSObjectProtocol for WinitView {}
|
||||
@@ -324,6 +333,26 @@ declare_class!(
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl UITextInputTraits for WinitView {
|
||||
}
|
||||
|
||||
unsafe impl UIKeyInput for WinitView {
|
||||
#[method(hasText)]
|
||||
fn has_text(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[method(insertText:)]
|
||||
fn insert_text(&self, text: &NSString) {
|
||||
self.handle_insert_text(text)
|
||||
}
|
||||
|
||||
#[method(deleteBackward)]
|
||||
fn delete_backward(&self) {
|
||||
self.handle_delete_backward()
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl WinitView {
|
||||
@@ -480,7 +509,7 @@ impl WinitView {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let touch_id = touch as *const UITouch as u64;
|
||||
let touch_id = touch as *const UITouch as usize;
|
||||
let phase = touch.phase();
|
||||
let phase = match phase {
|
||||
UITouchPhase::Began => TouchPhase::Started,
|
||||
@@ -502,7 +531,7 @@ impl WinitView {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
device_id: DEVICE_ID,
|
||||
id: touch_id,
|
||||
finger_id: RootFingerId(FingerId(touch_id)),
|
||||
location: physical_location,
|
||||
force,
|
||||
phase,
|
||||
@@ -512,4 +541,69 @@ impl WinitView {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(mtm, touch_events);
|
||||
}
|
||||
|
||||
fn handle_insert_text(&self, text: &NSString) {
|
||||
let window = self.window().unwrap();
|
||||
let window_id = RootWindowId(window.id());
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
// send individual events for each character
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
text.to_string().chars().flat_map(|c| {
|
||||
let text = smol_str::SmolStr::from_iter([c]);
|
||||
// Emit both press and release events
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
event: KeyEvent {
|
||||
text: if state == ElementState::Pressed {
|
||||
Some(text.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
state,
|
||||
location: KeyLocation::Standard,
|
||||
repeat: false,
|
||||
logical_key: Key::Character(text.clone()),
|
||||
physical_key: PhysicalKey::Unidentified(
|
||||
NativeKeyCode::Unidentified,
|
||||
),
|
||||
platform_specific: KeyEventExtra {},
|
||||
},
|
||||
is_synthetic: false,
|
||||
device_id: DEVICE_ID,
|
||||
},
|
||||
})
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn handle_delete_backward(&self) {
|
||||
let window = self.window().unwrap();
|
||||
let window_id = RootWindowId(window.id());
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event: KeyEvent {
|
||||
state,
|
||||
logical_key: Key::Named(NamedKey::Backspace),
|
||||
physical_key: PhysicalKey::Code(KeyCode::Backspace),
|
||||
platform_specific: KeyEventExtra {},
|
||||
repeat: false,
|
||||
location: KeyLocation::Standard,
|
||||
text: None,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,17 @@ use tracing::{debug, warn};
|
||||
use super::app_state::EventWrapper;
|
||||
use super::view::WinitView;
|
||||
use super::view_controller::WinitViewController;
|
||||
use super::{app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle};
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{Event, WindowEvent};
|
||||
use crate::icon::Icon;
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations};
|
||||
use crate::platform_impl::platform::{
|
||||
app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle,
|
||||
};
|
||||
use crate::window::{
|
||||
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
|
||||
WindowButtons, WindowId as RootWindowId, WindowLevel,
|
||||
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
|
||||
WindowAttributes, WindowButtons, WindowId as CoreWindowId, WindowLevel,
|
||||
};
|
||||
|
||||
declare_class!(
|
||||
@@ -51,7 +50,7 @@ declare_class!(
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(self.id()),
|
||||
window_id: CoreWindowId(self.id()),
|
||||
event: WindowEvent::Focused(true),
|
||||
}),
|
||||
);
|
||||
@@ -64,7 +63,7 @@ declare_class!(
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(self.id()),
|
||||
window_id: CoreWindowId(self.id()),
|
||||
event: WindowEvent::Focused(false),
|
||||
}),
|
||||
);
|
||||
@@ -80,6 +79,11 @@ impl WinitUIWindow {
|
||||
frame: CGRect,
|
||||
view_controller: &UIViewController,
|
||||
) -> Retained<Self> {
|
||||
// NOTE: This should only be created after the application has started launching,
|
||||
// (`application:willFinishLaunchingWithOptions:` at the earliest), otherwise you'll run
|
||||
// into very confusing issues with the window not being properly activated.
|
||||
//
|
||||
// Winit ensures this by not allowing access to `ActiveEventLoop` before handling events.
|
||||
let this: Retained<Self> = unsafe { msg_send_id![mtm.alloc(), initWithFrame: frame] };
|
||||
|
||||
this.setRootViewController(Some(view_controller));
|
||||
@@ -155,20 +159,20 @@ impl Inner {
|
||||
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
pub fn inner_position(&self) -> PhysicalPosition<i32> {
|
||||
let safe_area = self.safe_area_screen_space();
|
||||
let position =
|
||||
LogicalPosition { x: safe_area.origin.x as f64, y: safe_area.origin.y as f64 };
|
||||
let scale_factor = self.scale_factor();
|
||||
Ok(position.to_physical(scale_factor))
|
||||
position.to_physical(scale_factor)
|
||||
}
|
||||
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
pub fn outer_position(&self) -> PhysicalPosition<i32> {
|
||||
let screen_frame = self.screen_frame();
|
||||
let position =
|
||||
LogicalPosition { x: screen_frame.origin.x as f64, y: screen_frame.origin.y as f64 };
|
||||
let scale_factor = self.scale_factor();
|
||||
Ok(position.to_physical(scale_factor))
|
||||
position.to_physical(scale_factor)
|
||||
}
|
||||
|
||||
pub fn set_outer_position(&self, physical_position: Position) {
|
||||
@@ -183,7 +187,7 @@ impl Inner {
|
||||
self.window.setBounds(bounds);
|
||||
}
|
||||
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let safe_area = self.safe_area_screen_space();
|
||||
let size = LogicalSize {
|
||||
@@ -203,25 +207,25 @@ impl Inner {
|
||||
size.to_physical(scale_factor)
|
||||
}
|
||||
|
||||
pub fn request_inner_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
|
||||
Some(self.inner_size())
|
||||
pub fn request_surface_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
|
||||
Some(self.surface_size())
|
||||
}
|
||||
|
||||
pub fn set_min_inner_size(&self, _dimensions: Option<Size>) {
|
||||
warn!("`Window::set_min_inner_size` is ignored on iOS")
|
||||
pub fn set_min_surface_size(&self, _dimensions: Option<Size>) {
|
||||
warn!("`Window::set_min_surface_size` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_max_inner_size(&self, _dimensions: Option<Size>) {
|
||||
warn!("`Window::set_max_inner_size` is ignored on iOS")
|
||||
pub fn set_max_surface_size(&self, _dimensions: Option<Size>) {
|
||||
warn!("`Window::set_max_surface_size` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
pub fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resize_increments(&self, _increments: Option<Size>) {
|
||||
warn!("`Window::set_resize_increments` is ignored on iOS")
|
||||
pub fn set_surface_resize_increments(&self, _increments: Option<Size>) {
|
||||
warn!("`Window::set_surface_resize_increments` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
@@ -252,31 +256,31 @@ impl Inner {
|
||||
debug!("`Window::set_cursor` ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
pub fn set_cursor_position(&self, _position: Position) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("set_cursor_position is not supported"))
|
||||
}
|
||||
|
||||
pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("set_cursor_grab is not supported"))
|
||||
}
|
||||
|
||||
pub fn set_cursor_visible(&self, _visible: bool) {
|
||||
debug!("`Window::set_cursor_visible` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
pub fn drag_window(&self) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("drag_window is not supported"))
|
||||
}
|
||||
|
||||
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("drag_resize_window is not supported"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), NotSupportedError> {
|
||||
Err(NotSupportedError::new("set_cursor_hittest is not supported"))
|
||||
}
|
||||
|
||||
pub fn set_minimized(&self, _minimized: bool) {
|
||||
@@ -367,12 +371,24 @@ impl Inner {
|
||||
warn!("`Window::set_ime_cursor_area` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_ime_allowed(&self, _allowed: bool) {
|
||||
warn!("`Window::set_ime_allowed` is ignored on iOS")
|
||||
/// Show / hide the keyboard. To show the keyboard, we call `becomeFirstResponder`,
|
||||
/// requesting focus for the [WinitView]. Since [WinitView] implements
|
||||
/// [objc2_ui_kit::UIKeyInput], the keyboard will be shown.
|
||||
/// <https://developer.apple.com/documentation/uikit/uiresponder/1621113-becomefirstresponder>
|
||||
pub fn set_ime_allowed(&self, allowed: bool) {
|
||||
if allowed {
|
||||
unsafe {
|
||||
self.view.becomeFirstResponder();
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
self.view.resignFirstResponder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {
|
||||
warn!("`Window::set_ime_allowed` is ignored on iOS")
|
||||
warn!("`Window::set_ime_purpose` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn focus_window(&self) {
|
||||
@@ -405,29 +421,6 @@ impl Inner {
|
||||
self.window.id()
|
||||
}
|
||||
|
||||
#[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 = Retained::as_ptr(&self.window) as _;
|
||||
window_handle.ui_view = Retained::as_ptr(&self.view) as _;
|
||||
window_handle.ui_view_controller = Retained::as_ptr(&self.view_controller) as _;
|
||||
rwh_04::RawWindowHandle::UiKit(window_handle)
|
||||
}
|
||||
|
||||
#[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 = Retained::as_ptr(&self.window) as _;
|
||||
window_handle.ui_view = Retained::as_ptr(&self.view) as _;
|
||||
window_handle.ui_view_controller = Retained::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) -> rwh_06::RawWindowHandle {
|
||||
let mut window_handle = rwh_06::UiKitWindowHandle::new({
|
||||
@@ -473,14 +466,14 @@ impl Window {
|
||||
pub(crate) fn new(
|
||||
event_loop: &ActiveEventLoop,
|
||||
window_attributes: WindowAttributes,
|
||||
) -> Result<Window, RootOsError> {
|
||||
) -> Result<Window, RequestError> {
|
||||
let mtm = event_loop.mtm;
|
||||
|
||||
if window_attributes.min_inner_size.is_some() {
|
||||
warn!("`WindowAttributes::min_inner_size` is ignored on iOS");
|
||||
if window_attributes.min_surface_size.is_some() {
|
||||
warn!("`WindowAttributes::min_surface_size` is ignored on iOS");
|
||||
}
|
||||
if window_attributes.max_inner_size.is_some() {
|
||||
warn!("`WindowAttributes::max_inner_size` is ignored on iOS");
|
||||
if window_attributes.max_surface_size.is_some() {
|
||||
warn!("`WindowAttributes::max_surface_size` is ignored on iOS");
|
||||
}
|
||||
|
||||
// TODO: transparency, visible
|
||||
@@ -496,7 +489,7 @@ impl Window {
|
||||
|
||||
let screen_bounds = screen.bounds();
|
||||
|
||||
let frame = match window_attributes.inner_size {
|
||||
let frame = match window_attributes.surface_size {
|
||||
Some(dim) => {
|
||||
let scale_factor = screen.scale();
|
||||
let size = dim.to_logical::<f64>(scale_factor as f64);
|
||||
@@ -515,10 +508,9 @@ impl Window {
|
||||
|
||||
let view_controller = WinitViewController::new(mtm, &window_attributes, &view);
|
||||
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
|
||||
window.makeKeyAndVisible();
|
||||
|
||||
app_state::set_key_window(mtm, &window);
|
||||
|
||||
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
|
||||
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `SurfaceResized`
|
||||
// event on window creation if the DPI factor != 1.0
|
||||
let scale_factor = view.contentScaleFactor();
|
||||
let scale_factor = scale_factor as f64;
|
||||
@@ -531,7 +523,7 @@ impl Window {
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
};
|
||||
let window_id = RootWindowId(window.id());
|
||||
let window_id = CoreWindowId(window.id());
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
std::iter::once(EventWrapper::ScaleFactorChanged(app_state::ScaleFactorChanged {
|
||||
@@ -542,7 +534,7 @@ impl Window {
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
||||
event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
@@ -552,11 +544,6 @@ impl Window {
|
||||
Ok(Window { inner: MainThreadBound::new(inner, mtm) })
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Inner) + Send + 'static) {
|
||||
// For now, don't actually do queuing, since it may be less predictable
|
||||
self.maybe_wait_on_main(f)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Inner) -> R + Send) -> R {
|
||||
self.inner.get_on_main(|inner| f(inner))
|
||||
}
|
||||
@@ -582,6 +569,265 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> crate::window::WindowId {
|
||||
self.maybe_wait_on_main(|delegate| crate::window::WindowId(delegate.id()))
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
self.maybe_wait_on_main(|delegate| delegate.scale_factor())
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_redraw());
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.pre_present_notify());
|
||||
}
|
||||
|
||||
fn reset_dead_keys(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_outer_position(position));
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_size())
|
||||
}
|
||||
|
||||
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_surface_size(size))
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.outer_size())
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size))
|
||||
}
|
||||
|
||||
fn set_max_surface_size(&self, max_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_max_surface_size(max_size));
|
||||
}
|
||||
|
||||
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_resize_increments())
|
||||
}
|
||||
|
||||
fn set_surface_resize_increments(&self, increments: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_surface_resize_increments(increments));
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_title(title));
|
||||
}
|
||||
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_transparent(transparent));
|
||||
}
|
||||
|
||||
fn set_blur(&self, blur: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_blur(blur));
|
||||
}
|
||||
|
||||
fn set_visible(&self, visible: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_visible(visible));
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_visible())
|
||||
}
|
||||
|
||||
fn set_resizable(&self, resizable: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_resizable(resizable))
|
||||
}
|
||||
|
||||
fn is_resizable(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_resizable())
|
||||
}
|
||||
|
||||
fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_enabled_buttons(buttons))
|
||||
}
|
||||
|
||||
fn enabled_buttons(&self) -> WindowButtons {
|
||||
self.maybe_wait_on_main(|delegate| delegate.enabled_buttons())
|
||||
}
|
||||
|
||||
fn set_minimized(&self, minimized: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_minimized(minimized));
|
||||
}
|
||||
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_minimized())
|
||||
}
|
||||
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_maximized(maximized));
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_maximized())
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, fullscreen: Option<crate::window::Fullscreen>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen.map(Into::into)))
|
||||
}
|
||||
|
||||
fn fullscreen(&self) -> Option<crate::window::Fullscreen> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.fullscreen().map(Into::into))
|
||||
}
|
||||
|
||||
fn set_decorations(&self, decorations: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_decorations(decorations));
|
||||
}
|
||||
|
||||
fn is_decorated(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.is_decorated())
|
||||
}
|
||||
|
||||
fn set_window_level(&self, level: WindowLevel) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_window_level(level));
|
||||
}
|
||||
|
||||
fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_window_icon(window_icon));
|
||||
}
|
||||
|
||||
fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_cursor_area(position, size));
|
||||
}
|
||||
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_allowed(allowed));
|
||||
}
|
||||
|
||||
fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_ime_purpose(purpose));
|
||||
}
|
||||
|
||||
fn focus_window(&self) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.focus_window());
|
||||
}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
self.maybe_wait_on_main(|delegate| delegate.has_focus())
|
||||
}
|
||||
|
||||
fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.request_user_attention(request_type));
|
||||
}
|
||||
|
||||
fn set_theme(&self, theme: Option<Theme>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_theme(theme));
|
||||
}
|
||||
|
||||
fn theme(&self) -> Option<Theme> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.theme())
|
||||
}
|
||||
|
||||
fn set_content_protected(&self, protected: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_content_protected(protected));
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
self.maybe_wait_on_main(|delegate| delegate.title())
|
||||
}
|
||||
|
||||
fn set_cursor(&self, cursor: Cursor) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor(cursor));
|
||||
}
|
||||
|
||||
fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.set_cursor_position(position))?)
|
||||
}
|
||||
|
||||
fn set_cursor_grab(&self, mode: crate::window::CursorGrabMode) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.set_cursor_grab(mode))?)
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_cursor_visible(visible))
|
||||
}
|
||||
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.drag_window())?)
|
||||
}
|
||||
|
||||
fn drag_resize_window(
|
||||
&self,
|
||||
direction: crate::window::ResizeDirection,
|
||||
) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.drag_resize_window(direction))?)
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, position: Position) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.show_window_menu(position))
|
||||
}
|
||||
|
||||
fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.set_cursor_hittest(hittest))?)
|
||||
}
|
||||
|
||||
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
delegate.current_monitor().map(|inner| CoreMonitorHandle { inner })
|
||||
})
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
Box::new(
|
||||
delegate.available_monitors().into_iter().map(|inner| CoreMonitorHandle { inner }),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
self.maybe_wait_on_main(|delegate| {
|
||||
delegate.primary_monitor().map(|inner| CoreMonitorHandle { inner })
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// WindowExtIOS
|
||||
impl Inner {
|
||||
pub fn set_scale_factor(&self, scale_factor: f64) {
|
||||
@@ -701,7 +947,7 @@ pub struct WindowId {
|
||||
}
|
||||
|
||||
impl WindowId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
WindowId { window: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
@@ -727,7 +973,7 @@ impl From<&AnyObject> for WindowId {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct PlatformSpecificWindowAttributes {
|
||||
pub scale_factor: Option<f64>,
|
||||
pub valid_orientations: ValidOrientations,
|
||||
@@ -1,60 +0,0 @@
|
||||
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||
use objc2_ui_kit::UIApplication;
|
||||
|
||||
use super::app_state::{self, send_occluded_event_for_all_windows, EventWrapper};
|
||||
use crate::event::Event;
|
||||
|
||||
declare_class!(
|
||||
pub struct AppDelegate;
|
||||
|
||||
unsafe impl ClassType for AppDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
const NAME: &'static str = "WinitApplicationDelegate";
|
||||
}
|
||||
|
||||
impl DeclaredClass for AppDelegate {}
|
||||
|
||||
// UIApplicationDelegate protocol
|
||||
unsafe impl AppDelegate {
|
||||
#[method(application:didFinishLaunchingWithOptions:)]
|
||||
fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
|
||||
app_state::did_finish_launching(MainThreadMarker::new().unwrap());
|
||||
true
|
||||
}
|
||||
|
||||
#[method(applicationDidBecomeActive:)]
|
||||
fn did_become_active(&self, _application: &UIApplication) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed))
|
||||
}
|
||||
|
||||
#[method(applicationWillResignActive:)]
|
||||
fn will_resign_active(&self, _application: &UIApplication) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended))
|
||||
}
|
||||
|
||||
#[method(applicationWillEnterForeground:)]
|
||||
fn will_enter_foreground(&self, application: &UIApplication) {
|
||||
send_occluded_event_for_all_windows(application, false);
|
||||
}
|
||||
|
||||
#[method(applicationDidEnterBackground:)]
|
||||
fn did_enter_background(&self, application: &UIApplication) {
|
||||
send_occluded_event_for_all_windows(application, true);
|
||||
}
|
||||
|
||||
#[method(applicationWillTerminate:)]
|
||||
fn will_terminate(&self, application: &UIApplication) {
|
||||
app_state::terminated(application);
|
||||
}
|
||||
|
||||
#[method(applicationDidReceiveMemoryWarning:)]
|
||||
fn did_receive_memory_warning(&self, _application: &UIApplication) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning))
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,386 +0,0 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
|
||||
use core_foundation::base::{CFIndex, CFRelease};
|
||||
use core_foundation::runloop::{
|
||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopDefaultMode,
|
||||
kCFRunLoopExit, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
|
||||
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
||||
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
use objc2_foundation::{MainThreadMarker, NSString};
|
||||
use objc2_ui_kit::{UIApplication, UIApplicationMain, UIDevice, UIScreen, UIUserInterfaceIdiom};
|
||||
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||
};
|
||||
use crate::platform::ios::Idiom;
|
||||
use crate::platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents};
|
||||
use crate::window::{CustomCursor, CustomCursorSource};
|
||||
|
||||
use super::app_delegate::AppDelegate;
|
||||
use super::app_state::AppState;
|
||||
use super::{app_state, monitor, MonitorHandle};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ActiveEventLoop {
|
||||
pub(super) mtm: MainThreadMarker,
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor {
|
||||
let _ = source.inner;
|
||||
CustomCursor { inner: super::PlatformCustomCursor }
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
monitor::uiscreens(self.mtm)
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
#[allow(deprecated)]
|
||||
Some(MonitorHandle::new(UIScreen::mainScreen(self.mtm)))
|
||||
}
|
||||
|
||||
#[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 programmatically
|
||||
tracing::warn!("`ControlFlow::Exit` ignored on iOS");
|
||||
}
|
||||
|
||||
pub(crate) fn exiting(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
|
||||
OwnedDisplayHandle
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::UiKitDisplayHandle::empty().into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::UiKitDisplayHandle::new().into())
|
||||
}
|
||||
}
|
||||
|
||||
fn map_user_event<T: 'static>(
|
||||
mut handler: impl FnMut(Event<T>, &RootActiveEventLoop),
|
||||
receiver: mpsc::Receiver<T>,
|
||||
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) {
|
||||
move |event, window_target| match event.map_nonuser_event() {
|
||||
Ok(event) => (handler)(event, window_target),
|
||||
Err(_) => {
|
||||
for event in receiver.try_iter() {
|
||||
(handler)(Event::UserEvent(event), window_target);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
mtm: MainThreadMarker,
|
||||
sender: Sender<T>,
|
||||
receiver: Receiver<T>,
|
||||
window_target: RootActiveEventLoop,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub(crate) fn new(
|
||||
_: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<EventLoop<T>, EventLoopError> {
|
||||
let mtm = MainThreadMarker::new()
|
||||
.expect("On iOS, `EventLoop` must be created on the main thread");
|
||||
|
||||
static mut SINGLETON_INIT: bool = false;
|
||||
unsafe {
|
||||
assert!(
|
||||
!SINGLETON_INIT,
|
||||
"Only one `EventLoop` is supported on iOS. `EventLoopProxy` might be helpful"
|
||||
);
|
||||
SINGLETON_INIT = true;
|
||||
}
|
||||
|
||||
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: RootActiveEventLoop { p: ActiveEventLoop { mtm }, _marker: PhantomData },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run<F>(self, handler: F) -> !
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
let application: Option<Retained<UIApplication>> =
|
||||
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
|
||||
assert!(
|
||||
application.is_none(),
|
||||
"\
|
||||
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\nNote: \
|
||||
`EventLoop::run_app` calls `UIApplicationMain` on iOS",
|
||||
);
|
||||
|
||||
let handler = map_user_event(handler, self.receiver);
|
||||
|
||||
let handler = unsafe {
|
||||
std::mem::transmute::<
|
||||
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop)>,
|
||||
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop)>,
|
||||
>(Box::new(handler))
|
||||
};
|
||||
|
||||
let handler = EventLoopHandler { handler, event_loop: self.window_target };
|
||||
|
||||
app_state::will_launch(self.mtm, handler);
|
||||
|
||||
// Ensure application delegate is initialized
|
||||
let _ = AppDelegate::class();
|
||||
|
||||
extern "C" {
|
||||
// These functions are in crt_externs.h.
|
||||
fn _NSGetArgc() -> *mut c_int;
|
||||
fn _NSGetArgv() -> *mut *mut *mut c_char;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
UIApplicationMain(
|
||||
*_NSGetArgc(),
|
||||
NonNull::new(*_NSGetArgv()).unwrap(),
|
||||
None,
|
||||
Some(&NSString::from_str(AppDelegate::NAME)),
|
||||
)
|
||||
};
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.sender.clone())
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &RootActiveEventLoop {
|
||||
&self.window_target
|
||||
}
|
||||
}
|
||||
|
||||
// EventLoopExtIOS
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn idiom(&self) -> Idiom {
|
||||
match UIDevice::currentDevice(self.mtm).userInterfaceIdiom() {
|
||||
UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified,
|
||||
UIUserInterfaceIdiom::Phone => Idiom::Phone,
|
||||
UIUserInterfaceIdiom::Pad => Idiom::Pad,
|
||||
UIUserInterfaceIdiom::TV => Idiom::TV,
|
||||
UIUserInterfaceIdiom::CarPlay => Idiom::CarPlay,
|
||||
_ => Idiom::Unspecified,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy<T> {
|
||||
sender: Sender<T>,
|
||||
source: CFRunLoopSourceRef,
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for EventLoopProxy<T> {}
|
||||
unsafe impl<T: Send> Sync for EventLoopProxy<T> {}
|
||||
|
||||
impl<T> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.sender.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for EventLoopProxy<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
CFRunLoopSourceInvalidate(self.source);
|
||||
CFRelease(self.source as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoopProxy<T> {
|
||||
fn new(sender: Sender<T>) -> EventLoopProxy<T> {
|
||||
unsafe {
|
||||
// just wake up the eventloop
|
||||
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
|
||||
|
||||
// adding a Source to the main CFRunLoop lets us wake it up and
|
||||
// process user events through the normal OS EventLoop mechanisms.
|
||||
let rl = CFRunLoopGetMain();
|
||||
let mut context = CFRunLoopSourceContext {
|
||||
version: 0,
|
||||
info: ptr::null_mut(),
|
||||
retain: None,
|
||||
release: None,
|
||||
copyDescription: None,
|
||||
equal: None,
|
||||
hash: None,
|
||||
schedule: None,
|
||||
cancel: None,
|
||||
perform: event_loop_proxy_handler,
|
||||
};
|
||||
let source = CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::MAX - 1, &mut context);
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||
CFRunLoopWakeUp(rl);
|
||||
|
||||
EventLoopProxy { sender, source }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.sender.send(event).map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?;
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
CFRunLoopSourceSignal(self.source);
|
||||
let rl = CFRunLoopGetMain();
|
||||
CFRunLoopWakeUp(rl);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_control_flow_observers() {
|
||||
unsafe {
|
||||
// begin is queued with the highest priority to ensure it is processed before other
|
||||
// observers
|
||||
extern "C" fn control_flow_begin_handler(
|
||||
_: CFRunLoopObserverRef,
|
||||
activity: CFRunLoopActivity,
|
||||
_: *mut c_void,
|
||||
) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(mtm),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
||||
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end
|
||||
// priority to be 0, in order to send AboutToWait before RedrawRequested. This value was
|
||||
// chosen conservatively to guard against apple using different priorities for their redraw
|
||||
// observers in different OS's or on different devices. If it so happens that it's too
|
||||
// conservative, the main symptom would be non-redraw events coming in after `AboutToWait`.
|
||||
//
|
||||
// The value of `0x1e8480` was determined by inspecting stack traces and the associated
|
||||
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
|
||||
//
|
||||
// Also tested to be `0x1e8480` on iPhone 8, iOS 13 beta 4.
|
||||
extern "C" fn control_flow_main_end_handler(
|
||||
_: CFRunLoopObserverRef,
|
||||
activity: CFRunLoopActivity,
|
||||
_: *mut c_void,
|
||||
) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm),
|
||||
kCFRunLoopExit => {}, // may happen when running on macOS
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// end is queued with the lowest priority to ensure it is processed after other observers
|
||||
extern "C" fn control_flow_end_handler(
|
||||
_: CFRunLoopObserverRef,
|
||||
activity: CFRunLoopActivity,
|
||||
_: *mut c_void,
|
||||
) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm),
|
||||
kCFRunLoopExit => {}, // may happen when running on macOS
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let main_loop = CFRunLoopGetMain();
|
||||
|
||||
let begin_observer = CFRunLoopObserverCreate(
|
||||
ptr::null_mut(),
|
||||
kCFRunLoopAfterWaiting,
|
||||
1, // repeat = true
|
||||
CFIndex::MIN,
|
||||
control_flow_begin_handler,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
CFRunLoopAddObserver(main_loop, begin_observer, kCFRunLoopDefaultMode);
|
||||
|
||||
let main_end_observer = CFRunLoopObserverCreate(
|
||||
ptr::null_mut(),
|
||||
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
||||
1, // repeat = true
|
||||
0, // see comment on `control_flow_main_end_handler`
|
||||
control_flow_main_end_handler,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
CFRunLoopAddObserver(main_loop, main_end_observer, kCFRunLoopDefaultMode);
|
||||
|
||||
let end_observer = CFRunLoopObserverCreate(
|
||||
ptr::null_mut(),
|
||||
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
||||
1, // repeat = true
|
||||
CFIndex::MAX,
|
||||
control_flow_end_handler,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode);
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,14 @@ use std::ops::Deref;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use super::{XkbContext, XKBCH};
|
||||
use smol_str::SmolStr;
|
||||
use xkbcommon_dl::{
|
||||
xkb_compose_compile_flags, xkb_compose_feed_result, xkb_compose_state, xkb_compose_state_flags,
|
||||
xkb_compose_status, xkb_compose_table, xkb_keysym_t,
|
||||
};
|
||||
|
||||
use super::{XkbContext, XKBCH};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XkbComposeTable {
|
||||
table: NonNull<xkb_compose_table>,
|
||||
|
||||
@@ -6,14 +6,13 @@ use std::ptr::{self, NonNull};
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
use x11_dl::xlib_xcb::xcb_connection_t;
|
||||
#[cfg(wayland_platform)]
|
||||
use {memmap2::MmapOptions, std::os::unix::io::OwnedFd};
|
||||
|
||||
use xkb::XKB_MOD_INVALID;
|
||||
use xkbcommon_dl::{
|
||||
self as xkb, xkb_keycode_t, xkb_keymap, xkb_keymap_compile_flags, xkb_keysym_t,
|
||||
xkb_layout_index_t, xkb_mod_index_t,
|
||||
};
|
||||
#[cfg(wayland_platform)]
|
||||
use {memmap2::MmapOptions, std::os::unix::io::OwnedFd};
|
||||
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey};
|
||||
#[cfg(x11_platform)]
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_char;
|
||||
#[cfg(wayland_platform)]
|
||||
use std::os::unix::io::OwnedFd;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::utils::Lazy;
|
||||
use smol_str::SmolStr;
|
||||
#[cfg(wayland_platform)]
|
||||
use std::os::unix::io::OwnedFd;
|
||||
use tracing::warn;
|
||||
use xkbcommon_dl::{
|
||||
self as xkb, xkb_compose_status, xkb_context, xkb_context_flags, xkbcommon_compose_handle,
|
||||
@@ -18,16 +17,16 @@ use {x11_dl::xlib_xcb::xcb_connection_t, xkbcommon_dl::x11::xkbcommon_x11_handle
|
||||
use crate::event::{ElementState, KeyEvent};
|
||||
use crate::keyboard::{Key, KeyLocation};
|
||||
use crate::platform_impl::KeyEventExtra;
|
||||
use crate::utils::Lazy;
|
||||
|
||||
mod compose;
|
||||
mod keymap;
|
||||
mod state;
|
||||
|
||||
use compose::{ComposeStatus, XkbComposeState, XkbComposeTable};
|
||||
use keymap::XkbKeymap;
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
pub use keymap::raw_keycode_to_physicalkey;
|
||||
use keymap::XkbKeymap;
|
||||
pub use keymap::{physicalkey_to_scancode, scancode_to_physicalkey};
|
||||
pub use state::XkbState;
|
||||
|
||||
|
||||
@@ -3,39 +3,33 @@
|
||||
#[cfg(all(not(x11_platform), not(wayland_platform)))]
|
||||
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::env;
|
||||
use std::num::{NonZeroU16, NonZeroU32};
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{env, fmt};
|
||||
#[cfg(x11_platform)]
|
||||
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex};
|
||||
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc, sync::Mutex};
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
use crate::utils::Lazy;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
|
||||
#[cfg(x11_platform)]
|
||||
use self::x11::{X11Error, XConnection, XError, XNotSupported};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootELW, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||
};
|
||||
use crate::icon::Icon;
|
||||
use self::x11::{XConnection, XError, XNotSupported};
|
||||
use crate::application::ApplicationHandler;
|
||||
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::dpi::Size;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::error::{EventLoopError, NotSupportedError};
|
||||
use crate::event_loop::ActiveEventLoop;
|
||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||
use crate::keyboard::Key;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
|
||||
use crate::window::{
|
||||
ActivationToken, Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose,
|
||||
ResizeDirection, Theme, UserAttentionType, WindowAttributes, WindowButtons, WindowLevel,
|
||||
};
|
||||
|
||||
pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
|
||||
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
|
||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::utils::Lazy;
|
||||
use crate::window::ActivationToken;
|
||||
|
||||
pub(crate) mod common;
|
||||
#[cfg(wayland_platform)]
|
||||
@@ -69,7 +63,7 @@ impl ApplicationName {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformSpecificWindowAttributes {
|
||||
pub name: Option<ApplicationName>,
|
||||
pub activation_token: Option<ActivationToken>,
|
||||
@@ -77,7 +71,7 @@ pub struct PlatformSpecificWindowAttributes {
|
||||
pub x11: X11WindowAttributes,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg(x11_platform)]
|
||||
pub struct X11WindowAttributes {
|
||||
pub visual_id: Option<x11rb::protocol::xproto::Visualid>,
|
||||
@@ -113,34 +107,6 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported>>> =
|
||||
Lazy::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)));
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OsError {
|
||||
Misc(&'static str),
|
||||
#[cfg(x11_platform)]
|
||||
XError(Arc<X11Error>),
|
||||
#[cfg(wayland_platform)]
|
||||
WaylandError(Arc<wayland::WaylandError>),
|
||||
}
|
||||
|
||||
impl fmt::Display for OsError {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
OsError::Misc(e) => _f.pad(e),
|
||||
#[cfg(x11_platform)]
|
||||
OsError::XError(ref e) => fmt::Display::fmt(e, _f),
|
||||
#[cfg(wayland_platform)]
|
||||
OsError::WaylandError(ref e) => fmt::Display::fmt(e, _f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum Window {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::Window),
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(wayland::Window),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(u64);
|
||||
|
||||
@@ -157,7 +123,7 @@ impl From<u64> for WindowId {
|
||||
}
|
||||
|
||||
impl WindowId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
@@ -171,15 +137,32 @@ pub enum DeviceId {
|
||||
}
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
#[cfg(wayland_platform)]
|
||||
return DeviceId::Wayland(unsafe { wayland::DeviceId::dummy() });
|
||||
return DeviceId::Wayland(wayland::DeviceId::dummy());
|
||||
#[cfg(all(not(wayland_platform), x11_platform))]
|
||||
return DeviceId::X(unsafe { x11::DeviceId::dummy() });
|
||||
return DeviceId::X(x11::DeviceId::dummy());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum FingerId {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::FingerId),
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(wayland::FingerId),
|
||||
}
|
||||
|
||||
impl FingerId {
|
||||
pub const fn dummy() -> Self {
|
||||
#[cfg(wayland_platform)]
|
||||
return FingerId::Wayland(wayland::FingerId::dummy());
|
||||
#[cfg(all(not(wayland_platform), x11_platform))]
|
||||
return FingerId::X(x11::FingerId::dummy());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum MonitorHandle {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::MonitorHandle),
|
||||
@@ -227,25 +210,20 @@ impl MonitorHandle {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
x11_or_wayland!(match self; MonitorHandle(m) => m.size())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
|
||||
x11_or_wayland!(match self; MonitorHandle(m) => m.position())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
x11_or_wayland!(match self; MonitorHandle(m) => m.refresh_rate_millihertz())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
x11_or_wayland!(match self; MonitorHandle(m) => m.scale_factor() as _)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
|
||||
x11_or_wayland!(match self; MonitorHandle(m) => m.current_video_mode())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoModeHandle>> {
|
||||
x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes()))
|
||||
@@ -267,12 +245,12 @@ impl VideoModeHandle {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
pub fn bit_depth(&self) -> Option<NonZeroU16> {
|
||||
x11_or_wayland!(match self; VideoModeHandle(m) => m.bit_depth())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn refresh_rate_millihertz(&self) -> u32 {
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
|
||||
x11_or_wayland!(match self; VideoModeHandle(m) => m.refresh_rate_millihertz())
|
||||
}
|
||||
|
||||
@@ -282,351 +260,6 @@ impl VideoModeHandle {
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
window_target: &ActiveEventLoop,
|
||||
attribs: WindowAttributes,
|
||||
) -> Result<Self, RootOsError> {
|
||||
match *window_target {
|
||||
#[cfg(wayland_platform)]
|
||||
ActiveEventLoop::Wayland(ref window_target) => {
|
||||
wayland::Window::new(window_target, attribs).map(Window::Wayland)
|
||||
},
|
||||
#[cfg(x11_platform)]
|
||||
ActiveEventLoop::X(ref window_target) => {
|
||||
x11::Window::new(window_target, attribs).map(Window::X)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) {
|
||||
f(self)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Self) -> R + Send) -> R {
|
||||
f(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
x11_or_wayland!(match self; Window(w) => w.id())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, title: &str) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_title(title));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_transparent(&self, transparent: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_transparent(transparent));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_blur(&self, blur: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_blur(blur));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_visible(visible))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_visible(&self) -> Option<bool> {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_visible())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.outer_position())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.inner_position())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_outer_position(&self, position: Position) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_outer_position(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
x11_or_wayland!(match self; Window(w) => w.inner_size())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
x11_or_wayland!(match self; Window(w) => w.outer_size())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_inner_size(size))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_activation_token())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_min_inner_size(dimensions))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_max_inner_size(dimensions))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
x11_or_wayland!(match self; Window(w) => w.resize_increments())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resize_increments(&self, increments: Option<Size>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_resize_increments(increments))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_resizable(resizable))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_resizable(&self) -> bool {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_resizable())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_enabled_buttons(buttons))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||
x11_or_wayland!(match self; Window(w) => w.enabled_buttons())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: Cursor) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_cursor(cursor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_cursor_grab(mode))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_cursor_visible(visible))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.drag_window())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.drag_resize_window(direction))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, position: Position) {
|
||||
x11_or_wayland!(match self; Window(w) => w.show_window_menu(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_cursor_hittest(hittest))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
x11_or_wayland!(match self; Window(w) => w.scale_factor())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_cursor_position(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_maximized(maximized))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_maximized())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_minimized(minimized))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_minimized(&self) -> Option<bool> {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_minimized())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
x11_or_wayland!(match self; Window(w) => w.fullscreen())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_fullscreen(monitor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_decorations(decorations))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_decorated(&self) -> bool {
|
||||
x11_or_wayland!(match self; Window(w) => w.is_decorated())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_level(&self, level: WindowLevel) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_window_level(level))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_window_icon(window_icon.map(|icon| icon.inner)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_ime_cursor_area(position, size))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reset_dead_keys(&self) {
|
||||
common::xkb::reset_dead_keys()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_allowed(&self, allowed: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_ime_allowed(allowed))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_ime_purpose(purpose))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
x11_or_wayland!(match self; Window(w) => w.focus_window())
|
||||
}
|
||||
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_user_attention(request_type))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_redraw())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pre_present_notify(&self) {
|
||||
x11_or_wayland!(match self; Window(w) => w.pre_present_notify())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(x11_or_wayland!(match self; Window(w) => w.current_monitor()?; as MonitorHandle))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Window::X(ref window) => {
|
||||
window.available_monitors().into_iter().map(MonitorHandle::X).collect()
|
||||
},
|
||||
#[cfg(wayland_platform)]
|
||||
Window::Wayland(ref window) => {
|
||||
window.available_monitors().into_iter().map(MonitorHandle::Wayland).collect()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(x11_or_wayland!(match self; Window(w) => w.primary_monitor()?; as MonitorHandle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_04")]
|
||||
#[inline]
|
||||
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_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]
|
||||
pub fn set_theme(&self, theme: Option<Theme>) {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_theme(theme))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
x11_or_wayland!(match self; Window(window) => window.theme())
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, protected: bool) {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_content_protected(protected))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
x11_or_wayland!(match self; Window(window) => window.has_focus())
|
||||
}
|
||||
|
||||
pub fn title(&self) -> String {
|
||||
x11_or_wayland!(match self; Window(window) => window.title())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
pub text_with_all_modifiers: Option<SmolStr>,
|
||||
@@ -650,7 +283,7 @@ unsafe extern "C" fn x_error_callback(
|
||||
display: *mut x11::ffi::Display,
|
||||
event: *mut x11::ffi::XErrorEvent,
|
||||
) -> c_int {
|
||||
let xconn_lock = X11_BACKEND.lock().unwrap();
|
||||
let xconn_lock = X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner());
|
||||
if let Ok(ref xconn) = *xconn_lock {
|
||||
// Call all the hooks.
|
||||
let mut error_handled = false;
|
||||
@@ -692,27 +325,22 @@ unsafe extern "C" fn x_error_callback(
|
||||
0
|
||||
}
|
||||
|
||||
pub enum EventLoop<T: 'static> {
|
||||
pub enum EventLoop {
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(Box<wayland::EventLoop<T>>),
|
||||
Wayland(Box<wayland::EventLoop>),
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::EventLoop<T>),
|
||||
X(x11::EventLoop),
|
||||
}
|
||||
|
||||
pub enum EventLoopProxy<T: 'static> {
|
||||
#[derive(Clone)]
|
||||
pub enum EventLoopProxy {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::EventLoopProxy<T>),
|
||||
X(x11::EventLoopProxy),
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(wayland::EventLoopProxy<T>),
|
||||
Wayland(wayland::EventLoopProxy),
|
||||
}
|
||||
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.clone(); as EventLoopProxy)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
impl EventLoop {
|
||||
pub(crate) fn new(
|
||||
attributes: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<Self, EventLoopError> {
|
||||
@@ -757,7 +385,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
} else {
|
||||
"neither WAYLAND_DISPLAY nor WAYLAND_SOCKET nor DISPLAY is set."
|
||||
};
|
||||
return Err(EventLoopError::Os(os_error!(OsError::Misc(msg))));
|
||||
return Err(NotSupportedError::new(msg).into());
|
||||
},
|
||||
};
|
||||
|
||||
@@ -771,15 +399,15 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
|
||||
#[cfg(wayland_platform)]
|
||||
fn new_wayland_any_thread() -> Result<EventLoop<T>, EventLoopError> {
|
||||
fn new_wayland_any_thread() -> Result<EventLoop, EventLoopError> {
|
||||
wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp)))
|
||||
}
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
fn new_x11_any_thread() -> Result<EventLoop<T>, EventLoopError> {
|
||||
let xconn = match X11_BACKEND.lock().unwrap().as_ref() {
|
||||
fn new_x11_any_thread() -> Result<EventLoop, EventLoopError> {
|
||||
let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
|
||||
Ok(xconn) => xconn.clone(),
|
||||
Err(_) => return Err(EventLoopError::NotSupported(NotSupportedError::new())),
|
||||
Err(err) => return Err(os_error!(err.clone()).into()),
|
||||
};
|
||||
|
||||
Ok(EventLoop::X(x11::EventLoop::new(xconn)))
|
||||
@@ -795,153 +423,45 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
|
||||
pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app))
|
||||
}
|
||||
|
||||
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW),
|
||||
{
|
||||
self.run_on_demand(callback)
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
app: A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app_on_demand(app))
|
||||
}
|
||||
|
||||
pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW),
|
||||
{
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(callback))
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: A,
|
||||
) -> PumpStatus {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_app_events(timeout, app))
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW),
|
||||
{
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &crate::event_loop::ActiveEventLoop {
|
||||
pub fn window_target(&self) -> &dyn ActiveEventLoop {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
impl AsFd for EventLoop {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_fd())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
impl AsRawFd for EventLoop {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.send_event(event))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ActiveEventLoop {
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(wayland::ActiveEventLoop),
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::ActiveEventLoop),
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
#[inline]
|
||||
pub fn is_wayland(&self) -> bool {
|
||||
match *self {
|
||||
#[cfg(wayland_platform)]
|
||||
ActiveEventLoop::Wayland(_) => true,
|
||||
#[cfg(x11_platform)]
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_custom_cursor(&self, cursor: CustomCursorSource) -> CustomCursor {
|
||||
x11_or_wayland!(match self; ActiveEventLoop(evlp) => evlp.create_custom_cursor(cursor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
match *self {
|
||||
#[cfg(wayland_platform)]
|
||||
ActiveEventLoop::Wayland(ref evlp) => {
|
||||
evlp.available_monitors().map(MonitorHandle::Wayland).collect()
|
||||
},
|
||||
#[cfg(x11_platform)]
|
||||
ActiveEventLoop::X(ref evlp) => {
|
||||
evlp.available_monitors().map(MonitorHandle::X).collect()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(
|
||||
x11_or_wayland!(match self; ActiveEventLoop(evlp) => evlp.primary_monitor()?; as MonitorHandle),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.listen_device_events(allowed))
|
||||
}
|
||||
|
||||
#[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 clear_exit(&self) {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.clear_exit())
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Self::X(conn) => OwnedDisplayHandle::X(conn.x_connection().clone()),
|
||||
#[cfg(wayland_platform)]
|
||||
Self::Wayland(conn) => OwnedDisplayHandle::Wayland(conn.connection.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn set_exit_code(&self, code: i32) {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn exit_code(&self) -> Option<i32> {
|
||||
x11_or_wayland!(match self; Self(evlp) => evlp.exit_code())
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.wake_up())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,29 +475,6 @@ pub(crate) enum OwnedDisplayHandle {
|
||||
}
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Self::X(xconn) => {
|
||||
let mut xlib_handle = rwh_05::XlibDisplayHandle::empty();
|
||||
xlib_handle.display = xconn.display.cast();
|
||||
xlib_handle.screen = xconn.default_screen_index() as _;
|
||||
xlib_handle.into()
|
||||
},
|
||||
|
||||
#[cfg(wayland_platform)]
|
||||
Self::Wayland(conn) => {
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
let mut wayland_handle = rwh_05::WaylandDisplayHandle::empty();
|
||||
wayland_handle.display = conn.display().id().as_ptr() as *mut _;
|
||||
wayland_handle.into()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
@@ -1006,6 +503,21 @@ impl OwnedDisplayHandle {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for OwnedDisplayHandle {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
#[cfg(x11_platform)]
|
||||
(Self::X(this), Self::X(other)) => Arc::as_ptr(this).eq(&Arc::as_ptr(other)),
|
||||
#[cfg(wayland_platform)]
|
||||
(Self::Wayland(this), Self::Wayland(other)) => this.eq(other),
|
||||
#[cfg(all(x11_platform, wayland_platform))]
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for OwnedDisplayHandle {}
|
||||
|
||||
/// Returns the minimum `Option<Duration>`, taking into account that `None`
|
||||
/// equates to an infinite timeout, not a zero timeout (so can't just use
|
||||
/// `Option::min`)
|
||||
|
||||
@@ -2,29 +2,25 @@
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::io::Result as IOResult;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use sctk::reexports::calloop::Error as CalloopError;
|
||||
use sctk::reexports::calloop_wayland_source::WaylandSource;
|
||||
use sctk::reexports::client::{globals, Connection, QueueHandle};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::OnlyCursorImage;
|
||||
use crate::dpi::LogicalSize;
|
||||
use crate::error::{EventLoopError, OsError as RootOsError};
|
||||
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||
use crate::error::{EventLoopError, OsError, RequestError};
|
||||
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::platform::min_timeout;
|
||||
use crate::platform_impl::{
|
||||
ActiveEventLoop as PlatformActiveEventLoop, OsError, PlatformCustomCursor,
|
||||
};
|
||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource};
|
||||
use crate::platform_impl::PlatformCustomCursor;
|
||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme};
|
||||
|
||||
mod proxy;
|
||||
pub mod sink;
|
||||
@@ -34,12 +30,12 @@ use sink::EventSink;
|
||||
|
||||
use super::state::{WindowCompositorUpdate, WinitState};
|
||||
use super::window::state::FrameCallbackState;
|
||||
use super::{logical_to_physical_rounded, DeviceId, WaylandError, WindowId};
|
||||
use super::{logical_to_physical_rounded, DeviceId, WindowId};
|
||||
|
||||
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
|
||||
|
||||
/// The Wayland event loop.
|
||||
pub struct EventLoop<T: 'static> {
|
||||
pub struct EventLoop {
|
||||
/// Has `run` or `run_on_demand` been called or a call to `pump_events` that starts the loop
|
||||
loop_running: bool,
|
||||
|
||||
@@ -47,14 +43,6 @@ pub struct EventLoop<T: 'static> {
|
||||
compositor_updates: Vec<WindowCompositorUpdate>,
|
||||
window_ids: Vec<WindowId>,
|
||||
|
||||
/// Sender of user events.
|
||||
user_events_sender: calloop::channel::Sender<T>,
|
||||
|
||||
// XXX can't remove RefCell out of here, unless we can plumb generics into the `Window`, which
|
||||
// we don't really want, since it'll break public API by a lot.
|
||||
/// Pending events from the user.
|
||||
pending_user_events: Rc<RefCell<Vec<T>>>,
|
||||
|
||||
/// The Wayland dispatcher to has raw access to the queue when needed, such as
|
||||
/// when creating a new window.
|
||||
wayland_dispatcher: WaylandDispatcher,
|
||||
@@ -63,36 +51,29 @@ pub struct EventLoop<T: 'static> {
|
||||
connection: Connection,
|
||||
|
||||
/// Event loop window target.
|
||||
window_target: RootActiveEventLoop,
|
||||
active_event_loop: ActiveEventLoop,
|
||||
|
||||
// XXX drop after everything else, just to be safe.
|
||||
/// Calloop's event loop.
|
||||
event_loop: calloop::EventLoop<'static, WinitState>,
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn new() -> Result<EventLoop<T>, EventLoopError> {
|
||||
macro_rules! map_err {
|
||||
($e:expr, $err:expr) => {
|
||||
$e.map_err(|error| os_error!($err(error).into()))
|
||||
};
|
||||
}
|
||||
|
||||
let connection = map_err!(Connection::connect_to_env(), WaylandError::Connection)?;
|
||||
impl EventLoop {
|
||||
pub fn new() -> Result<EventLoop, EventLoopError> {
|
||||
let connection = Connection::connect_to_env().map_err(|err| os_error!(err))?;
|
||||
|
||||
let (globals, mut event_queue) =
|
||||
map_err!(globals::registry_queue_init(&connection), WaylandError::Global)?;
|
||||
globals::registry_queue_init(&connection).map_err(|err| os_error!(err))?;
|
||||
let queue_handle = event_queue.handle();
|
||||
|
||||
let event_loop =
|
||||
map_err!(calloop::EventLoop::<WinitState>::try_new(), WaylandError::Calloop)?;
|
||||
calloop::EventLoop::<WinitState>::try_new().map_err(|err| os_error!(err))?;
|
||||
|
||||
let mut winit_state = WinitState::new(&globals, &queue_handle, event_loop.handle())
|
||||
.map_err(|error| os_error!(error))?;
|
||||
let mut winit_state = WinitState::new(&globals, &queue_handle, event_loop.handle())?;
|
||||
|
||||
// NOTE: do a roundtrip after binding the globals to prevent potential
|
||||
// races with the server.
|
||||
map_err!(event_queue.roundtrip(&mut winit_state), WaylandError::Dispatch)?;
|
||||
event_queue.roundtrip(&mut winit_state).map_err(|err| os_error!(err))?;
|
||||
|
||||
// Register Wayland source.
|
||||
let wayland_source = WaylandSource::new(connection.clone(), event_queue);
|
||||
@@ -108,46 +89,38 @@ impl<T: 'static> EventLoop<T> {
|
||||
result
|
||||
});
|
||||
|
||||
map_err!(
|
||||
event_loop.handle().register_dispatcher(wayland_dispatcher.clone()),
|
||||
WaylandError::Calloop
|
||||
)?;
|
||||
event_loop
|
||||
.handle()
|
||||
.register_dispatcher(wayland_dispatcher.clone())
|
||||
.map_err(|err| os_error!(err))?;
|
||||
|
||||
// Setup the user proxy.
|
||||
let pending_user_events = Rc::new(RefCell::new(Vec::new()));
|
||||
let pending_user_events_clone = pending_user_events.clone();
|
||||
let (user_events_sender, user_events_channel) = calloop::channel::channel();
|
||||
let result = event_loop
|
||||
let (ping, ping_source) = calloop::ping::make_ping().unwrap();
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(user_events_channel, move |event, _, winit_state: &mut WinitState| {
|
||||
if let calloop::channel::Event::Msg(msg) = event {
|
||||
winit_state.dispatched_events = true;
|
||||
pending_user_events_clone.borrow_mut().push(msg);
|
||||
}
|
||||
.insert_source(ping_source, move |_, _, winit_state: &mut WinitState| {
|
||||
winit_state.dispatched_events = true;
|
||||
winit_state.proxy_wake_up = true;
|
||||
})
|
||||
.map_err(|error| error.error);
|
||||
map_err!(result, WaylandError::Calloop)?;
|
||||
.map_err(|err| os_error!(err))?;
|
||||
|
||||
// An event's loop awakener to wake up for window events from winit's windows.
|
||||
let (event_loop_awakener, event_loop_awakener_source) = map_err!(
|
||||
calloop::ping::make_ping()
|
||||
.map_err(|error| CalloopError::OtherError(Box::new(error).into())),
|
||||
WaylandError::Calloop
|
||||
)?;
|
||||
let (event_loop_awakener, event_loop_awakener_source) =
|
||||
calloop::ping::make_ping().map_err(|err| os_error!(err))?;
|
||||
|
||||
let result = event_loop
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(event_loop_awakener_source, move |_, _, winit_state: &mut WinitState| {
|
||||
// Mark that we have something to dispatch.
|
||||
winit_state.dispatched_events = true;
|
||||
})
|
||||
.map_err(|error| error.error);
|
||||
map_err!(result, WaylandError::Calloop)?;
|
||||
.map_err(|err| os_error!(err))?;
|
||||
|
||||
let window_target = ActiveEventLoop {
|
||||
let active_event_loop = ActiveEventLoop {
|
||||
connection: connection.clone(),
|
||||
wayland_dispatcher: wayland_dispatcher.clone(),
|
||||
event_loop_awakener,
|
||||
event_loop_proxy: EventLoopProxy::new(ping),
|
||||
queue_handle,
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
exit: Cell::new(None),
|
||||
@@ -161,24 +134,24 @@ impl<T: 'static> EventLoop<T> {
|
||||
window_ids: Vec::new(),
|
||||
connection,
|
||||
wayland_dispatcher,
|
||||
user_events_sender,
|
||||
pending_user_events,
|
||||
event_loop,
|
||||
window_target: RootActiveEventLoop {
|
||||
p: PlatformActiveEventLoop::Wayland(window_target),
|
||||
_marker: PhantomData,
|
||||
},
|
||||
active_event_loop,
|
||||
};
|
||||
|
||||
Ok(event_loop)
|
||||
}
|
||||
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut app: A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
self.active_event_loop.clear_exit();
|
||||
let exit = loop {
|
||||
match self.pump_events(None, &mut event_handler) {
|
||||
match self.pump_app_events(None, &mut app) {
|
||||
PumpStatus::Exit(0) => {
|
||||
break Ok(());
|
||||
},
|
||||
@@ -200,26 +173,27 @@ impl<T: 'static> EventLoop<T> {
|
||||
exit
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
mut app: A,
|
||||
) -> PumpStatus {
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
// Run the initial loop iteration.
|
||||
self.single_iteration(&mut callback, StartCause::Init);
|
||||
self.single_iteration(&mut app, StartCause::Init);
|
||||
}
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit.
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
self.poll_events_with_timeout(timeout, &mut app);
|
||||
}
|
||||
if let Some(code) = self.exit_code() {
|
||||
self.loop_running = false;
|
||||
|
||||
callback(Event::LoopExiting, self.window_target());
|
||||
app.exiting(&self.active_event_loop);
|
||||
|
||||
PumpStatus::Exit(code)
|
||||
} else {
|
||||
@@ -227,10 +201,11 @@ 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>, &RootActiveEventLoop),
|
||||
{
|
||||
fn poll_events_with_timeout<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) {
|
||||
let cause = loop {
|
||||
let start = Instant::now();
|
||||
|
||||
@@ -292,13 +267,10 @@ impl<T: 'static> EventLoop<T> {
|
||||
break cause;
|
||||
};
|
||||
|
||||
self.single_iteration(&mut callback, cause);
|
||||
self.single_iteration(app, cause);
|
||||
}
|
||||
|
||||
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
fn single_iteration<A: ApplicationHandler>(&mut self, app: &mut A, cause: StartCause) {
|
||||
// NOTE currently just indented to simplify the diff
|
||||
|
||||
// We retain these grow-only scratch buffers as part of the EventLoop
|
||||
@@ -309,18 +281,17 @@ 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);
|
||||
|
||||
callback(Event::NewEvents(cause), &self.window_target);
|
||||
app.new_events(&self.active_event_loop, cause);
|
||||
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though Wayland
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
// NB: For consistency all platforms must call `can_create_surfaces` even though Wayland
|
||||
// applications don't themselves have a formal surface destroy/create lifecycle.
|
||||
if cause == StartCause::Init {
|
||||
callback(Event::Resumed, &self.window_target);
|
||||
app.can_create_surfaces(&self.active_event_loop);
|
||||
}
|
||||
|
||||
// Handle pending user events. We don't need back buffer, since we can't dispatch
|
||||
// user events indirectly via callback to the user.
|
||||
for user_event in self.pending_user_events.borrow_mut().drain(..) {
|
||||
callback(Event::UserEvent(user_event), &self.window_target);
|
||||
// Indicate user wake up.
|
||||
if self.with_state(|state| mem::take(&mut state.proxy_wake_up)) {
|
||||
app.proxy_wake_up(&self.active_event_loop);
|
||||
}
|
||||
|
||||
// Drain the pending compositor updates.
|
||||
@@ -333,29 +304,24 @@ impl<T: 'static> EventLoop<T> {
|
||||
let windows = state.windows.get_mut();
|
||||
let window = windows.get(&window_id).unwrap().lock().unwrap();
|
||||
let scale_factor = window.scale_factor();
|
||||
let size = logical_to_physical_rounded(window.inner_size(), scale_factor);
|
||||
let size = logical_to_physical_rounded(window.surface_size(), scale_factor);
|
||||
(size, scale_factor)
|
||||
});
|
||||
|
||||
// Stash the old window size.
|
||||
let old_physical_size = physical_size;
|
||||
|
||||
let new_inner_size = Arc::new(Mutex::new(physical_size));
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&new_inner_size,
|
||||
)),
|
||||
},
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
let new_surface_size = Arc::new(Mutex::new(physical_size));
|
||||
let root_window_id = crate::window::WindowId(window_id);
|
||||
let event = WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
|
||||
};
|
||||
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
app.window_event(&self.active_event_loop, root_window_id, event);
|
||||
|
||||
let physical_size = *new_surface_size.lock().unwrap();
|
||||
drop(new_surface_size);
|
||||
|
||||
// Resize the window when user altered the size.
|
||||
if old_physical_size != physical_size {
|
||||
@@ -365,7 +331,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
let new_logical_size: LogicalSize<f64> =
|
||||
physical_size.to_logical(scale_factor);
|
||||
window.request_inner_size(new_logical_size.into());
|
||||
window.request_surface_size(new_logical_size.into());
|
||||
});
|
||||
|
||||
// Make it queue resize.
|
||||
@@ -381,7 +347,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
let window = windows.get(&window_id).unwrap().lock().unwrap();
|
||||
|
||||
let scale_factor = window.scale_factor();
|
||||
let size = logical_to_physical_rounded(window.inner_size(), scale_factor);
|
||||
let size = logical_to_physical_rounded(window.surface_size(), scale_factor);
|
||||
|
||||
// Mark the window as needed a redraw.
|
||||
state
|
||||
@@ -395,23 +361,14 @@ impl<T: 'static> EventLoop<T> {
|
||||
size
|
||||
});
|
||||
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::Resized(physical_size),
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
let event = WindowEvent::SurfaceResized(physical_size);
|
||||
app.window_event(&self.active_event_loop, window_id, event);
|
||||
}
|
||||
|
||||
if compositor_update.close_window {
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::CloseRequested,
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
app.window_event(&self.active_event_loop, window_id, WindowEvent::CloseRequested);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,8 +377,15 @@ impl<T: 'static> EventLoop<T> {
|
||||
buffer_sink.append(&mut state.window_events_sink.lock().unwrap());
|
||||
});
|
||||
for event in buffer_sink.drain() {
|
||||
let event = event.map_nonuser_event().unwrap();
|
||||
callback(event, &self.window_target);
|
||||
match event {
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(&self.active_event_loop, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(&self.active_event_loop, device_id, event)
|
||||
},
|
||||
_ => unreachable!("event which is neither device nor window event."),
|
||||
}
|
||||
}
|
||||
|
||||
// Handle non-synthetic events.
|
||||
@@ -429,8 +393,15 @@ impl<T: 'static> EventLoop<T> {
|
||||
buffer_sink.append(&mut state.events_sink);
|
||||
});
|
||||
for event in buffer_sink.drain() {
|
||||
let event = event.map_nonuser_event().unwrap();
|
||||
callback(event, &self.window_target);
|
||||
match event {
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(&self.active_event_loop, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(&self.active_event_loop, device_id, event)
|
||||
},
|
||||
_ => unreachable!("event which is neither device nor window event."),
|
||||
}
|
||||
}
|
||||
|
||||
// Collect the window ids
|
||||
@@ -466,10 +437,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
|
||||
if let Some(event) = event {
|
||||
callback(
|
||||
Event::WindowEvent { window_id: crate::window::WindowId(*window_id), event },
|
||||
&self.window_target,
|
||||
);
|
||||
let window_id = crate::window::WindowId(*window_id);
|
||||
app.window_event(&self.active_event_loop, window_id, event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,7 +448,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
callback(Event::AboutToWait, &self.window_target);
|
||||
app.about_to_wait(&self.active_event_loop);
|
||||
|
||||
// Update the window frames and schedule redraws.
|
||||
let mut wake_up = false;
|
||||
@@ -508,13 +477,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
// If the user draws from the `AboutToWait` this is likely not required, however
|
||||
// we can't do much about it.
|
||||
if wake_up {
|
||||
match &self.window_target.p {
|
||||
PlatformActiveEventLoop::Wayland(window_target) => {
|
||||
window_target.event_loop_awakener.ping();
|
||||
},
|
||||
#[cfg(x11_platform)]
|
||||
PlatformActiveEventLoop::X(_) => unreachable!(),
|
||||
}
|
||||
self.active_event_loop.event_loop_awakener.ping();
|
||||
}
|
||||
|
||||
std::mem::swap(&mut self.compositor_updates, &mut compositor_updates);
|
||||
@@ -523,31 +486,17 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.user_events_sender.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn window_target(&self) -> &RootActiveEventLoop {
|
||||
&self.window_target
|
||||
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
|
||||
&self.active_event_loop
|
||||
}
|
||||
|
||||
fn with_state<'a, U: 'a, F: FnOnce(&'a mut WinitState) -> U>(&'a mut self, callback: F) -> U {
|
||||
let state = match &mut self.window_target.p {
|
||||
PlatformActiveEventLoop::Wayland(window_target) => window_target.state.get_mut(),
|
||||
#[cfg(x11_platform)]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let state = self.active_event_loop.state.get_mut();
|
||||
callback(state)
|
||||
}
|
||||
|
||||
fn loop_dispatch<D: Into<Option<std::time::Duration>>>(&mut self, timeout: D) -> IOResult<()> {
|
||||
let state = match &mut self.window_target.p {
|
||||
PlatformActiveEventLoop::Wayland(window_target) => window_target.state.get_mut(),
|
||||
#[cfg(feature = "x11")]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let state = &mut self.active_event_loop.state.get_mut();
|
||||
|
||||
self.event_loop.dispatch(timeout, state).map_err(|error| {
|
||||
tracing::error!("Error dispatching event loop: {}", error);
|
||||
@@ -555,50 +504,47 @@ impl<T: 'static> EventLoop<T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn roundtrip(&mut self) -> Result<usize, RootOsError> {
|
||||
let state = match &mut self.window_target.p {
|
||||
PlatformActiveEventLoop::Wayland(window_target) => window_target.state.get_mut(),
|
||||
#[cfg(feature = "x11")]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
fn roundtrip(&mut self) -> Result<usize, OsError> {
|
||||
let state = &mut self.active_event_loop.state.get_mut();
|
||||
|
||||
let mut wayland_source = self.wayland_dispatcher.as_source_mut();
|
||||
let event_queue = wayland_source.queue();
|
||||
event_queue.roundtrip(state).map_err(|error| {
|
||||
os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
|
||||
})
|
||||
event_queue.roundtrip(state).map_err(|err| os_error!(err))
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
self.window_target.p.control_flow()
|
||||
self.active_event_loop.control_flow()
|
||||
}
|
||||
|
||||
fn exiting(&self) -> bool {
|
||||
self.window_target.p.exiting()
|
||||
self.active_event_loop.exiting()
|
||||
}
|
||||
|
||||
fn set_exit_code(&self, code: i32) {
|
||||
self.window_target.p.set_exit_code(code)
|
||||
self.active_event_loop.set_exit_code(code)
|
||||
}
|
||||
|
||||
fn exit_code(&self) -> Option<i32> {
|
||||
self.window_target.p.exit_code()
|
||||
self.active_event_loop.exit_code()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
impl AsFd for EventLoop {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
self.event_loop.as_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
impl AsRawFd for EventLoop {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.event_loop.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ActiveEventLoop {
|
||||
/// Event loop proxy
|
||||
event_loop_proxy: EventLoopProxy,
|
||||
|
||||
/// The event loop wakeup source.
|
||||
pub event_loop_awakener: calloop::ping::Ping,
|
||||
|
||||
@@ -622,65 +568,109 @@ pub struct ActiveEventLoop {
|
||||
pub connection: Connection,
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
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 clear_exit(&self) {
|
||||
self.exit.set(None)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||
|
||||
pub(crate) fn create_custom_cursor(&self, cursor: CustomCursorSource) -> RootCustomCursor {
|
||||
RootCustomCursor {
|
||||
inner: PlatformCustomCursor::Wayland(OnlyCursorImage(Arc::from(cursor.inner.0))),
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
|
||||
crate::event_loop::EventLoopProxy {
|
||||
event_loop_proxy: crate::platform_impl::EventLoopProxy::Wayland(
|
||||
self.event_loop_proxy.clone(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||
use sctk::reexports::client::Proxy;
|
||||
fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.control_flow.set(control_flow)
|
||||
}
|
||||
|
||||
let mut display_handle = rwh_05::WaylandDisplayHandle::empty();
|
||||
display_handle.display = self.connection.display().id().as_ptr() as *mut _;
|
||||
rwh_05::RawDisplayHandle::Wayland(display_handle)
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
self.control_flow.get()
|
||||
}
|
||||
|
||||
fn exit(&self) {
|
||||
self.exit.set(Some(0))
|
||||
}
|
||||
|
||||
fn exiting(&self) -> bool {
|
||||
self.exit.get().is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||
|
||||
fn create_custom_cursor(
|
||||
&self,
|
||||
cursor: CustomCursorSource,
|
||||
) -> Result<RootCustomCursor, RequestError> {
|
||||
Ok(RootCustomCursor {
|
||||
inner: PlatformCustomCursor::Wayland(OnlyCursorImage(Arc::from(cursor.inner.0))),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn system_theme(&self) -> Option<Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: crate::window::WindowAttributes,
|
||||
) -> Result<Box<dyn crate::window::Window>, RequestError> {
|
||||
let window = crate::platform_impl::wayland::Window::new(self, window_attributes)?;
|
||||
Ok(Box::new(window))
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
|
||||
Box::new(
|
||||
self.state
|
||||
.borrow()
|
||||
.output_state
|
||||
.outputs()
|
||||
.map(crate::platform_impl::wayland::output::MonitorHandle::new)
|
||||
.map(crate::platform_impl::MonitorHandle::Wayland)
|
||||
.map(|inner| crate::monitor::MonitorHandle { inner }),
|
||||
)
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
|
||||
// There's no primary monitor on Wayland.
|
||||
None
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> crate::event_loop::OwnedDisplayHandle {
|
||||
crate::event_loop::OwnedDisplayHandle {
|
||||
platform: crate::platform_impl::OwnedDisplayHandle::Wayland(self.connection.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
#[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())
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
fn clear_exit(&self) {
|
||||
self.exit.set(None)
|
||||
}
|
||||
|
||||
fn set_exit_code(&self, code: i32) {
|
||||
self.exit.set(Some(code))
|
||||
}
|
||||
|
||||
fn exit_code(&self) -> Option<i32> {
|
||||
self.exit.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
let raw = 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")
|
||||
});
|
||||
|
||||
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw.into()) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
//! An event loop proxy.
|
||||
|
||||
use std::sync::mpsc::SendError;
|
||||
|
||||
use sctk::reexports::calloop::channel::Sender;
|
||||
|
||||
use crate::event_loop::EventLoopClosed;
|
||||
use sctk::reexports::calloop::ping::Ping;
|
||||
|
||||
/// A handle that can be sent across the threads and used to wake up the `EventLoop`.
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
user_events_sender: Sender<T>,
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
ping: Ping,
|
||||
}
|
||||
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
EventLoopProxy { user_events_sender: self.user_events_sender.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn new(user_events_sender: Sender<T>) -> Self {
|
||||
Self { user_events_sender }
|
||||
impl EventLoopProxy {
|
||||
pub fn new(ping: Ping) -> Self {
|
||||
Self { ping }
|
||||
}
|
||||
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.user_events_sender.send(event).map_err(|SendError(error)| EventLoopClosed(error))
|
||||
pub fn wake_up(&self) {
|
||||
self.ping.ping();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
|
||||
use std::vec::Drain;
|
||||
|
||||
use super::{DeviceId, WindowId};
|
||||
use crate::event::{DeviceEvent, DeviceId as RootDeviceId, Event, WindowEvent};
|
||||
use crate::platform_impl::platform::DeviceId as PlatformDeviceId;
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
use super::{DeviceId, WindowId};
|
||||
|
||||
/// An event loop's sink to deliver events from the Wayland event callbacks
|
||||
/// to the winit's user.
|
||||
#[derive(Default)]
|
||||
pub struct EventSink {
|
||||
pub window_events: Vec<Event<()>>,
|
||||
pub(crate) window_events: Vec<Event>,
|
||||
}
|
||||
|
||||
impl EventSink {
|
||||
@@ -47,7 +46,7 @@ impl EventSink {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drain(&mut self) -> Drain<'_, Event<()>> {
|
||||
pub(crate) fn drain(&mut self) -> Drain<'_, Event> {
|
||||
self.window_events.drain(..)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
//! Winit's Wayland backend.
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::sync::Arc;
|
||||
|
||||
use sctk::reexports::client::globals::{BindError, GlobalError};
|
||||
pub use event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
|
||||
pub use output::{MonitorHandle, VideoModeHandle};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{self, ConnectError, DispatchError, Proxy};
|
||||
use sctk::reexports::client::Proxy;
|
||||
pub use window::Window;
|
||||
|
||||
pub(super) use crate::cursor::OnlyCursorImage as CustomCursor;
|
||||
use crate::dpi::{LogicalSize, PhysicalSize};
|
||||
pub use crate::platform_impl::platform::{OsError, WindowId};
|
||||
pub use event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
|
||||
pub use output::{MonitorHandle, VideoModeHandle};
|
||||
pub use window::Window;
|
||||
pub use crate::platform_impl::platform::WindowId;
|
||||
|
||||
mod event_loop;
|
||||
mod output;
|
||||
@@ -21,56 +17,25 @@ mod state;
|
||||
mod types;
|
||||
mod window;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WaylandError {
|
||||
/// Error connecting to the socket.
|
||||
Connection(ConnectError),
|
||||
|
||||
/// Error binding the global.
|
||||
Global(GlobalError),
|
||||
|
||||
// Bind error.
|
||||
Bind(BindError),
|
||||
|
||||
/// Error during the dispatching the event queue.
|
||||
Dispatch(DispatchError),
|
||||
|
||||
/// Calloop error.
|
||||
Calloop(calloop::Error),
|
||||
|
||||
/// Wayland
|
||||
Wire(client::backend::WaylandError),
|
||||
}
|
||||
|
||||
impl Display for WaylandError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
WaylandError::Connection(error) => error.fmt(f),
|
||||
WaylandError::Global(error) => error.fmt(f),
|
||||
WaylandError::Bind(error) => error.fmt(f),
|
||||
WaylandError::Dispatch(error) => error.fmt(f),
|
||||
WaylandError::Calloop(error) => error.fmt(f),
|
||||
WaylandError::Wire(error) => error.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WaylandError> for OsError {
|
||||
fn from(value: WaylandError) -> Self {
|
||||
Self::WaylandError(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Dummy device id, since Wayland doesn't have device events.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId(i32);
|
||||
|
||||
impl FingerId {
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the WindowId out of the surface.
|
||||
#[inline]
|
||||
fn make_wid(surface: &WlSurface) -> WindowId {
|
||||
|
||||
@@ -1,26 +1,12 @@
|
||||
use std::num::{NonZeroU16, NonZeroU32};
|
||||
|
||||
use sctk::output::{Mode, OutputData};
|
||||
use sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
use sctk::output::OutputData;
|
||||
|
||||
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
|
||||
use crate::platform_impl::platform::VideoModeHandle as PlatformVideoModeHandle;
|
||||
|
||||
use super::event_loop::ActiveEventLoop;
|
||||
|
||||
impl ActiveEventLoop {
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
|
||||
self.state.borrow().output_state.outputs().map(MonitorHandle::new)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
// There's no primary monitor on Wayland.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MonitorHandle {
|
||||
pub(crate) proxy: WlOutput,
|
||||
@@ -45,23 +31,9 @@ impl MonitorHandle {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
|
||||
let output_data = self.proxy.data::<OutputData>().unwrap();
|
||||
let dimensions = output_data.with_output_info(|info| {
|
||||
info.modes.iter().find_map(|mode| mode.current.then_some(mode.dimensions))
|
||||
});
|
||||
|
||||
match dimensions {
|
||||
Some((width, height)) => (width as u32, height as u32),
|
||||
_ => (0, 0),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
let output_data = self.proxy.data::<OutputData>().unwrap();
|
||||
output_data.with_output_info(|info| {
|
||||
Some(output_data.with_output_info(|info| {
|
||||
info.logical_position.map_or_else(
|
||||
|| {
|
||||
LogicalPosition::<i32>::from(info.location)
|
||||
@@ -72,15 +44,7 @@ impl MonitorHandle {
|
||||
.to_physical(info.scale_factor as f64)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
let output_data = self.proxy.data::<OutputData>().unwrap();
|
||||
output_data.with_output_info(|info| {
|
||||
info.modes.iter().find_map(|mode| mode.current.then_some(mode.refresh_rate as u32))
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -89,6 +53,18 @@ impl MonitorHandle {
|
||||
output_data.scale_factor()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_video_mode(&self) -> Option<PlatformVideoModeHandle> {
|
||||
let output_data = self.proxy.data::<OutputData>().unwrap();
|
||||
output_data.with_output_info(|info| {
|
||||
let mode = info.modes.iter().find(|mode| mode.current).cloned();
|
||||
|
||||
mode.map(|mode| {
|
||||
PlatformVideoModeHandle::Wayland(VideoModeHandle::new(self.clone(), mode))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = PlatformVideoModeHandle> {
|
||||
let output_data = self.proxy.data::<OutputData>().unwrap();
|
||||
@@ -97,12 +73,7 @@ impl MonitorHandle {
|
||||
let monitor = self.clone();
|
||||
|
||||
modes.into_iter().map(move |mode| {
|
||||
PlatformVideoModeHandle::Wayland(VideoModeHandle {
|
||||
size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(),
|
||||
refresh_rate_millihertz: mode.refresh_rate as u32,
|
||||
bit_depth: 32,
|
||||
monitor: monitor.clone(),
|
||||
})
|
||||
PlatformVideoModeHandle::Wayland(VideoModeHandle::new(monitor.clone(), mode))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -136,24 +107,31 @@ impl std::hash::Hash for MonitorHandle {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VideoModeHandle {
|
||||
pub(crate) size: PhysicalSize<u32>,
|
||||
pub(crate) bit_depth: u16,
|
||||
pub(crate) refresh_rate_millihertz: u32,
|
||||
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
|
||||
pub(crate) monitor: MonitorHandle,
|
||||
}
|
||||
|
||||
impl VideoModeHandle {
|
||||
fn new(monitor: MonitorHandle, mode: Mode) -> Self {
|
||||
VideoModeHandle {
|
||||
size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(),
|
||||
refresh_rate_millihertz: NonZeroU32::new(mode.refresh_rate as u32),
|
||||
monitor: monitor.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.size
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
self.bit_depth
|
||||
pub fn bit_depth(&self) -> Option<NonZeroU16> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn refresh_rate_millihertz(&self) -> u32 {
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
|
||||
self.refresh_rate_millihertz
|
||||
}
|
||||
|
||||
|
||||
@@ -5,20 +5,17 @@ use std::time::Duration;
|
||||
|
||||
use calloop::timer::{TimeoutAction, Timer};
|
||||
use calloop::{LoopHandle, RegistrationToken};
|
||||
use tracing::warn;
|
||||
|
||||
use sctk::reexports::client::protocol::wl_keyboard::{
|
||||
Event as WlKeyboardEvent, KeyState as WlKeyState, KeymapFormat as WlKeymapFormat, WlKeyboard,
|
||||
};
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, WEnum};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::event::{ElementState, WindowEvent};
|
||||
use crate::keyboard::ModifiersState;
|
||||
|
||||
use crate::platform_impl::common::xkb::Context;
|
||||
use crate::platform_impl::wayland::event_loop::sink::EventSink;
|
||||
use crate::platform_impl::wayland::seat::WinitSeatState;
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
use crate::platform_impl::wayland::{self, DeviceId, WindowId};
|
||||
|
||||
@@ -33,7 +30,17 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
) {
|
||||
let seat_state = match state.seats.get_mut(&data.seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => return,
|
||||
None => {
|
||||
warn!("Received keyboard event {event:?} without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
let keyboard_state = match seat_state.keyboard_state.as_mut() {
|
||||
Some(keyboard_state) => keyboard_state,
|
||||
None => {
|
||||
warn!("Received keyboard event {event:?} without keyboard");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
match event {
|
||||
@@ -43,7 +50,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
warn!("non-xkb compatible keymap")
|
||||
},
|
||||
WlKeymapFormat::XkbV1 => {
|
||||
let context = &mut seat_state.keyboard_state.as_mut().unwrap().xkb_context;
|
||||
let context = &mut keyboard_state.xkb_context;
|
||||
context.set_keymap_from_fd(fd, size as usize);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
@@ -67,7 +74,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
};
|
||||
|
||||
// Drop the repeat, if there were any.
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.current_repeat = None;
|
||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(token);
|
||||
@@ -93,7 +99,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
|
||||
// NOTE: we should drop the repeat regardless whethere it was for the present
|
||||
// window of for the window which just went gone.
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.current_repeat = None;
|
||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(token);
|
||||
@@ -128,7 +133,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
keyboard_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
key,
|
||||
@@ -136,7 +141,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
false,
|
||||
);
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
let delay = match keyboard_state.repeat_info {
|
||||
RepeatInfo::Repeat { delay, .. } => delay,
|
||||
RepeatInfo::Disable => return,
|
||||
@@ -163,18 +167,25 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
state.dispatched_events = true;
|
||||
|
||||
let data = wl_keyboard.data::<KeyboardData>().unwrap();
|
||||
let seat_state = state.seats.get_mut(&data.seat.id()).unwrap();
|
||||
let seat_state = match state.seats.get_mut(&data.seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
|
||||
// NOTE: The removed on event source is batched, but key change to
|
||||
// `None` is instant.
|
||||
let repeat_keycode =
|
||||
match seat_state.keyboard_state.as_ref().unwrap().current_repeat {
|
||||
Some(repeat_keycode) => repeat_keycode,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
let keyboard_state = match seat_state.keyboard_state.as_mut() {
|
||||
Some(keyboard_state) => keyboard_state,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
|
||||
// NOTE: The removed on event source is batched, but key change to `None`
|
||||
// is instant.
|
||||
let repeat_keycode = match keyboard_state.current_repeat {
|
||||
Some(repeat_keycode) => repeat_keycode,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
keyboard_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
repeat_keycode,
|
||||
@@ -183,7 +194,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
);
|
||||
|
||||
// NOTE: the gap could change dynamically while repeat is going.
|
||||
match seat_state.keyboard_state.as_ref().unwrap().repeat_info {
|
||||
match keyboard_state.repeat_info {
|
||||
RepeatInfo::Repeat { gap, .. } => TimeoutAction::ToDuration(gap),
|
||||
RepeatInfo::Disable => TimeoutAction::Drop,
|
||||
}
|
||||
@@ -194,7 +205,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
keyboard_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
key,
|
||||
@@ -202,7 +213,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
false,
|
||||
);
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
if keyboard_state.repeat_info != RepeatInfo::Disable
|
||||
&& keyboard_state.xkb_context.keymap_mut().unwrap().key_repeats(key)
|
||||
&& Some(key) == keyboard_state.current_repeat
|
||||
@@ -216,7 +226,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
WlKeyboardEvent::Modifiers {
|
||||
mods_depressed, mods_latched, mods_locked, group, ..
|
||||
} => {
|
||||
let xkb_context = &mut seat_state.keyboard_state.as_mut().unwrap().xkb_context;
|
||||
let xkb_context = &mut keyboard_state.xkb_context;
|
||||
let xkb_state = match xkb_context.state_mut() {
|
||||
Some(state) => state,
|
||||
None => return,
|
||||
@@ -240,7 +250,6 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
);
|
||||
},
|
||||
WlKeyboardEvent::RepeatInfo { rate, delay } => {
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.repeat_info = if rate == 0 {
|
||||
// Stop the repeat once we get a disable event.
|
||||
keyboard_state.current_repeat = None;
|
||||
@@ -348,7 +357,7 @@ impl KeyboardData {
|
||||
}
|
||||
|
||||
fn key_input(
|
||||
seat_state: &mut WinitSeatState,
|
||||
keyboard_state: &mut KeyboardState,
|
||||
event_sink: &mut EventSink,
|
||||
data: &KeyboardData,
|
||||
keycode: u32,
|
||||
@@ -360,8 +369,6 @@ fn key_input(
|
||||
None => return,
|
||||
};
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
|
||||
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
|
||||
if let Some(mut key_context) = keyboard_state.xkb_context.key_context() {
|
||||
let event = key_context.process_key_event(keycode, state, repeat);
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ahash::AHashMap;
|
||||
|
||||
use sctk::reexports::client::backend::ObjectId;
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
use sctk::reexports::protocols::wp::relative_pointer::zv1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1;
|
||||
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3;
|
||||
|
||||
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
||||
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::event::WindowEvent;
|
||||
use crate::keyboard::ModifiersState;
|
||||
@@ -23,12 +22,11 @@ mod pointer;
|
||||
mod text_input;
|
||||
mod touch;
|
||||
|
||||
use keyboard::{KeyboardData, KeyboardState};
|
||||
pub use pointer::relative_pointer::RelativePointerState;
|
||||
pub use pointer::{PointerConstraintsState, WinitPointerData, WinitPointerDataExt};
|
||||
pub use text_input::{TextInputState, ZwpTextInputV3Ext};
|
||||
|
||||
use keyboard::{KeyboardData, KeyboardState};
|
||||
use text_input::TextInputData;
|
||||
pub use text_input::{TextInputState, ZwpTextInputV3Ext};
|
||||
use touch::TouchPoint;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -76,7 +74,13 @@ impl SeatHandler for WinitState {
|
||||
seat: WlSeat,
|
||||
capability: SeatCapability,
|
||||
) {
|
||||
let seat_state = self.seats.get_mut(&seat.id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_seat::new_capability for unknown seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
match capability {
|
||||
SeatCapability::Touch if seat_state.touch.is_none() => {
|
||||
@@ -139,7 +143,13 @@ impl SeatHandler for WinitState {
|
||||
seat: WlSeat,
|
||||
capability: SeatCapability,
|
||||
) {
|
||||
let seat_state = self.seats.get_mut(&seat.id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_seat::remove_capability for unknown seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(text_input) = seat_state.text_input.take() {
|
||||
text_input.destroy();
|
||||
|
||||
@@ -4,6 +4,8 @@ use std::ops::Deref;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use tracing::warn;
|
||||
|
||||
use sctk::reexports::client::delegate_dispatch;
|
||||
use sctk::reexports::client::protocol::wl_pointer::WlPointer;
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
@@ -41,7 +43,21 @@ impl PointerHandler for WinitState {
|
||||
events: &[PointerEvent],
|
||||
) {
|
||||
let seat = pointer.winit_data().seat();
|
||||
let seat_state = self.seats.get(&seat.id()).unwrap();
|
||||
let seat_state = match self.seats.get(&seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received pointer event without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let themed_pointer = match seat_state.pointer.as_ref() {
|
||||
Some(pointer) => pointer,
|
||||
None => {
|
||||
warn!("Received pointer event without pointer");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
|
||||
|
||||
@@ -78,9 +94,7 @@ impl PointerHandler for WinitState {
|
||||
event.position.0,
|
||||
event.position.1,
|
||||
) {
|
||||
if let Some(pointer) = seat_state.pointer.as_ref() {
|
||||
let _ = pointer.set_cursor(connection, icon);
|
||||
}
|
||||
let _ = themed_pointer.set_cursor(connection, icon);
|
||||
}
|
||||
},
|
||||
PointerEventKind::Leave { .. } if parent_surface != surface => {
|
||||
@@ -113,9 +127,7 @@ impl PointerHandler for WinitState {
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::CursorEntered { device_id }, window_id);
|
||||
|
||||
if let Some(pointer) = seat_state.pointer.as_ref().map(Arc::downgrade) {
|
||||
window.pointer_entered(pointer);
|
||||
}
|
||||
window.pointer_entered(Arc::downgrade(themed_pointer));
|
||||
|
||||
// Set the currently focused surface.
|
||||
pointer.winit_data().inner.lock().unwrap().surface = Some(window_id);
|
||||
@@ -126,9 +138,7 @@ impl PointerHandler for WinitState {
|
||||
);
|
||||
},
|
||||
PointerEventKind::Leave { .. } => {
|
||||
if let Some(pointer) = seat_state.pointer.as_ref().map(Arc::downgrade) {
|
||||
window.pointer_left(pointer);
|
||||
}
|
||||
window.pointer_left(Arc::downgrade(themed_pointer));
|
||||
|
||||
// Remove the active surface.
|
||||
pointer.winit_data().inner.lock().unwrap().surface = None;
|
||||
@@ -185,13 +195,13 @@ impl PointerHandler for WinitState {
|
||||
// Mice events have both pixel and discrete delta's at the same time. So prefer
|
||||
// the descrite values if they are present.
|
||||
let delta = if has_discrete_scroll {
|
||||
// XXX Wayland sign convention is the inverse of winit.
|
||||
// NOTE: Wayland sign convention is the inverse of winit.
|
||||
MouseScrollDelta::LineDelta(
|
||||
(-horizontal.discrete) as f32,
|
||||
(-vertical.discrete) as f32,
|
||||
)
|
||||
} else {
|
||||
// XXX Wayland sign convention is the inverse of winit.
|
||||
// NOTE: Wayland sign convention is the inverse of winit.
|
||||
MouseScrollDelta::PixelDelta(
|
||||
LogicalPosition::new(-horizontal.absolute, -vertical.absolute)
|
||||
.to_physical(scale_factor),
|
||||
|
||||
@@ -66,12 +66,6 @@ impl Dispatch<ZwpRelativePointerV1, GlobalData, WinitState> for RelativePointerS
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
state
|
||||
.events_sink
|
||||
.push_device_event(DeviceEvent::Motion { axis: 0, value: dx_unaccel }, super::DeviceId);
|
||||
state
|
||||
.events_sink
|
||||
.push_device_event(DeviceEvent::Motion { axis: 1, value: dy_unaccel }, super::DeviceId);
|
||||
state.events_sink.push_device_event(
|
||||
DeviceEvent::MouseMotion { delta: (dx_unaccel, dy_unaccel) },
|
||||
super::DeviceId,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{delegate_dispatch, Dispatch};
|
||||
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
|
||||
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
|
||||
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::{
|
||||
ContentHint, ContentPurpose, Event as TextInputEvent, ZwpTextInputV3,
|
||||
|
||||
@@ -4,14 +4,13 @@ use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
|
||||
use sctk::seat::touch::{TouchData, TouchHandler};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::dpi::LogicalPosition;
|
||||
use crate::event::{Touch, TouchPhase, WindowEvent};
|
||||
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
use crate::platform_impl::wayland::{self, DeviceId};
|
||||
use crate::platform_impl::wayland::{self, DeviceId, FingerId};
|
||||
|
||||
impl TouchHandler for WinitState {
|
||||
fn down(
|
||||
@@ -31,11 +30,16 @@ impl TouchHandler for WinitState {
|
||||
None => return,
|
||||
};
|
||||
|
||||
let location = LogicalPosition::<f64>::from(position);
|
||||
|
||||
let seat_state = self.seats.get_mut(&touch.seat().id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_touch::down without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Update the state of the point.
|
||||
let location = LogicalPosition::<f64>::from(position);
|
||||
seat_state.touch_map.insert(id, TouchPoint { surface, location });
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
@@ -46,7 +50,9 @@ impl TouchHandler for WinitState {
|
||||
phase: TouchPhase::Started,
|
||||
location: location.to_physical(scale_factor),
|
||||
force: None,
|
||||
id: id as u64,
|
||||
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
|
||||
FingerId(id),
|
||||
)),
|
||||
}),
|
||||
window_id,
|
||||
);
|
||||
@@ -61,7 +67,13 @@ impl TouchHandler for WinitState {
|
||||
_: u32,
|
||||
id: i32,
|
||||
) {
|
||||
let seat_state = self.seats.get_mut(&touch.seat().id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_touch::up without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Remove the touch point.
|
||||
let touch_point = match seat_state.touch_map.remove(&id) {
|
||||
@@ -83,7 +95,9 @@ impl TouchHandler for WinitState {
|
||||
phase: TouchPhase::Ended,
|
||||
location: touch_point.location.to_physical(scale_factor),
|
||||
force: None,
|
||||
id: id as u64,
|
||||
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
|
||||
FingerId(id),
|
||||
)),
|
||||
}),
|
||||
window_id,
|
||||
);
|
||||
@@ -98,7 +112,13 @@ impl TouchHandler for WinitState {
|
||||
id: i32,
|
||||
position: (f64, f64),
|
||||
) {
|
||||
let seat_state = self.seats.get_mut(&touch.seat().id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_touch::motion without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Remove the touch point.
|
||||
let touch_point = match seat_state.touch_map.get_mut(&id) {
|
||||
@@ -122,14 +142,22 @@ impl TouchHandler for WinitState {
|
||||
phase: TouchPhase::Moved,
|
||||
location: touch_point.location.to_physical(scale_factor),
|
||||
force: None,
|
||||
id: id as u64,
|
||||
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
|
||||
FingerId(id),
|
||||
)),
|
||||
}),
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
|
||||
fn cancel(&mut self, _: &Connection, _: &QueueHandle<Self>, touch: &WlTouch) {
|
||||
let seat_state = self.seats.get_mut(&touch.seat().id()).unwrap();
|
||||
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => {
|
||||
warn!("Received wl_touch::cancel without seat");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
for (id, touch_point) in seat_state.touch_map.drain() {
|
||||
let window_id = wayland::make_wid(&touch_point.surface);
|
||||
@@ -148,7 +176,9 @@ impl TouchHandler for WinitState {
|
||||
phase: TouchPhase::Cancelled,
|
||||
location,
|
||||
force: None,
|
||||
id: id as u64,
|
||||
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
|
||||
FingerId(id),
|
||||
)),
|
||||
}),
|
||||
window_id,
|
||||
);
|
||||
|
||||
@@ -3,16 +3,14 @@ use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use ahash::AHashMap;
|
||||
|
||||
use sctk::compositor::{CompositorHandler, CompositorState};
|
||||
use sctk::output::{OutputHandler, OutputState};
|
||||
use sctk::reexports::calloop::LoopHandle;
|
||||
use sctk::reexports::client::backend::ObjectId;
|
||||
use sctk::reexports::client::globals::GlobalList;
|
||||
use sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
|
||||
use sctk::compositor::{CompositorHandler, CompositorState};
|
||||
use sctk::output::{OutputHandler, OutputState};
|
||||
use sctk::registry::{ProvidesRegistryState, RegistryState};
|
||||
use sctk::seat::pointer::ThemedPointer;
|
||||
use sctk::seat::SeatState;
|
||||
@@ -23,6 +21,7 @@ use sctk::shm::slot::SlotPool;
|
||||
use sctk::shm::{Shm, ShmHandler};
|
||||
use sctk::subcompositor::SubcompositorState;
|
||||
|
||||
use crate::error::OsError;
|
||||
use crate::platform_impl::wayland::event_loop::sink::EventSink;
|
||||
use crate::platform_impl::wayland::output::MonitorHandle;
|
||||
use crate::platform_impl::wayland::seat::{
|
||||
@@ -34,8 +33,7 @@ use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScali
|
||||
use crate::platform_impl::wayland::types::wp_viewporter::ViewporterState;
|
||||
use crate::platform_impl::wayland::types::xdg_activation::XdgActivationState;
|
||||
use crate::platform_impl::wayland::window::{WindowRequests, WindowState};
|
||||
use crate::platform_impl::wayland::{WaylandError, WindowId};
|
||||
use crate::platform_impl::OsError;
|
||||
use crate::platform_impl::wayland::WindowId;
|
||||
|
||||
/// Winit's Wayland state.
|
||||
pub struct WinitState {
|
||||
@@ -115,6 +113,9 @@ pub struct WinitState {
|
||||
/// Whether we have dispatched events to the user thus we want to
|
||||
/// send `AboutToWait` and normally wakeup the user.
|
||||
pub dispatched_events: bool,
|
||||
|
||||
/// Whether the user initiated a wake up.
|
||||
pub proxy_wake_up: bool,
|
||||
}
|
||||
|
||||
impl WinitState {
|
||||
@@ -125,7 +126,7 @@ impl WinitState {
|
||||
) -> Result<Self, OsError> {
|
||||
let registry_state = RegistryState::new(globals);
|
||||
let compositor_state =
|
||||
CompositorState::bind(globals, queue_handle).map_err(WaylandError::Bind)?;
|
||||
CompositorState::bind(globals, queue_handle).map_err(|err| os_error!(err))?;
|
||||
let subcompositor_state = match SubcompositorState::bind(
|
||||
compositor_state.wl_compositor().clone(),
|
||||
globals,
|
||||
@@ -155,7 +156,7 @@ impl WinitState {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let shm = Shm::bind(globals, queue_handle).map_err(WaylandError::Bind)?;
|
||||
let shm = Shm::bind(globals, queue_handle).map_err(|err| os_error!(err))?;
|
||||
let custom_cursor_pool = Arc::new(Mutex::new(SlotPool::new(2, &shm).unwrap()));
|
||||
|
||||
Ok(Self {
|
||||
@@ -167,7 +168,7 @@ impl WinitState {
|
||||
shm,
|
||||
custom_cursor_pool,
|
||||
|
||||
xdg_shell: XdgShell::bind(globals, queue_handle).map_err(WaylandError::Bind)?,
|
||||
xdg_shell: XdgShell::bind(globals, queue_handle).map_err(|err| os_error!(err))?,
|
||||
xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(),
|
||||
|
||||
windows: Default::default(),
|
||||
@@ -192,6 +193,7 @@ impl WinitState {
|
||||
loop_handle,
|
||||
// Make it true by default.
|
||||
dispatched_events: true,
|
||||
proxy_wake_up: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -339,12 +341,30 @@ impl CompositorHandler for WinitState {
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wayland_client::protocol::wl_surface::WlSurface,
|
||||
_: &WlSurface,
|
||||
_: wayland_client::protocol::wl_output::Transform,
|
||||
) {
|
||||
// TODO(kchibisov) we need to expose it somehow in winit.
|
||||
}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlSurface,
|
||||
_: &WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn surface_leave(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlSurface,
|
||||
_: &WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn scale_factor_changed(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use cursor_icon::CursorIcon;
|
||||
|
||||
use sctk::reexports::client::protocol::wl_shm::Format;
|
||||
use sctk::shm::slot::{Buffer, SlotPool};
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
//! Handling of KDE-compatible blur.
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
|
||||
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
||||
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur_manager::OrgKdeKwinBlurManager;
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
|
||||
/// KWin blur manager.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Handling of the fractional scaling.
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
|
||||
@@ -8,8 +9,6 @@ use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_
|
||||
Event as FractionalScalingEvent, WpFractionalScaleV1,
|
||||
};
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
|
||||
/// The scaling factor denominator.
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
//! Handling of the wp-viewporter.
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
|
||||
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
||||
use sctk::reexports::protocols::wp::viewporter::client::wp_viewporter::WpViewporter;
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
|
||||
/// Viewporter.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Weak;
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
|
||||
@@ -11,8 +12,6 @@ use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_toke
|
||||
};
|
||||
use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1;
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
use crate::platform_impl::WindowId;
|
||||
|
||||
@@ -3,34 +3,31 @@
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use sctk::compositor::{CompositorState, Region, SurfaceData};
|
||||
use sctk::reexports::client::protocol::wl_display::WlDisplay;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{Proxy, QueueHandle};
|
||||
|
||||
use sctk::compositor::{CompositorState, Region, SurfaceData};
|
||||
use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1;
|
||||
use sctk::shell::xdg::window::{Window as SctkWindow, WindowDecorations};
|
||||
use sctk::shell::WaylandSurface;
|
||||
|
||||
use tracing::warn;
|
||||
|
||||
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
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, PlatformIcon,
|
||||
};
|
||||
use crate::window::{
|
||||
Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
};
|
||||
|
||||
use super::event_loop::sink::EventSink;
|
||||
use super::output::MonitorHandle;
|
||||
use super::state::WinitState;
|
||||
use super::types::xdg_activation::XdgActivationTokenData;
|
||||
use super::{ActiveEventLoop, WaylandError, WindowId};
|
||||
use super::{ActiveEventLoop, WindowId};
|
||||
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{Ime, WindowEvent};
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
use crate::platform_impl::{Fullscreen, MonitorHandle as PlatformMonitorHandle};
|
||||
use crate::window::{
|
||||
Cursor, CursorGrabMode, Fullscreen as CoreFullscreen, ImePurpose, ResizeDirection, Theme,
|
||||
UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons,
|
||||
WindowId as CoreWindowId, WindowLevel,
|
||||
};
|
||||
|
||||
pub(crate) mod state;
|
||||
|
||||
@@ -80,7 +77,7 @@ impl Window {
|
||||
pub(crate) fn new(
|
||||
event_loop_window_target: &ActiveEventLoop,
|
||||
attributes: WindowAttributes,
|
||||
) -> Result<Self, RootOsError> {
|
||||
) -> Result<Self, RequestError> {
|
||||
let queue_handle = event_loop_window_target.queue_handle.clone();
|
||||
let mut state = event_loop_window_target.state.borrow_mut();
|
||||
|
||||
@@ -92,7 +89,7 @@ impl Window {
|
||||
state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone());
|
||||
let display = event_loop_window_target.connection.display();
|
||||
|
||||
let size: Size = attributes.inner_size.unwrap_or(LogicalSize::new(800., 600.).into());
|
||||
let size: Size = attributes.surface_size.unwrap_or(LogicalSize::new(800., 600.).into());
|
||||
|
||||
// We prefer server side decorations, however to not have decorations we ask for client
|
||||
// side decorations instead.
|
||||
@@ -132,10 +129,10 @@ impl Window {
|
||||
|
||||
// Set the min and max sizes. We must set the hints upon creating a window, so
|
||||
// we use the default `1.` scaling...
|
||||
let min_size = attributes.min_inner_size.map(|size| size.to_logical(1.));
|
||||
let max_size = attributes.max_inner_size.map(|size| size.to_logical(1.));
|
||||
window_state.set_min_inner_size(min_size);
|
||||
window_state.set_max_inner_size(max_size);
|
||||
let min_size = attributes.min_surface_size.map(|size| size.to_logical(1.));
|
||||
let max_size = attributes.max_surface_size.map(|size| size.to_logical(1.));
|
||||
window_state.set_min_surface_size(min_size);
|
||||
window_state.set_max_surface_size(max_size);
|
||||
|
||||
// Non-resizable implies that the min and max sizes are set to the same value.
|
||||
window_state.set_resizable(attributes.resizable);
|
||||
@@ -193,15 +190,11 @@ impl Window {
|
||||
let event_queue = wayland_source.queue();
|
||||
|
||||
// Do a roundtrip.
|
||||
event_queue.roundtrip(&mut state).map_err(|error| {
|
||||
os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
|
||||
})?;
|
||||
event_queue.roundtrip(&mut state).map_err(|err| os_error!(err))?;
|
||||
|
||||
// XXX Wait for the initial configure to arrive.
|
||||
while !window_state.lock().unwrap().is_configured() {
|
||||
event_queue.blocking_dispatch(&mut state).map_err(|error| {
|
||||
os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
|
||||
})?;
|
||||
event_queue.blocking_dispatch(&mut state).map_err(|err| os_error!(err))?;
|
||||
}
|
||||
|
||||
// Wake-up event loop, so it'll send initial redraw requested.
|
||||
@@ -226,51 +219,65 @@ impl Window {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
self.window_id
|
||||
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
|
||||
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||
Some(xdg_activation) => xdg_activation,
|
||||
None => return Err(NotSupportedError::new("xdg_activation_v1 is not available").into()),
|
||||
};
|
||||
|
||||
let serial = AsyncRequestSerial::get();
|
||||
|
||||
let data = XdgActivationTokenData::Obtain((self.window_id, serial));
|
||||
let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
|
||||
xdg_activation_token.set_surface(self.surface());
|
||||
xdg_activation_token.commit();
|
||||
|
||||
Ok(serial)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, title: impl ToString) {
|
||||
let new_title = title.to_string();
|
||||
self.window_state.lock().unwrap().set_title(new_title);
|
||||
pub fn surface(&self) -> &WlSurface {
|
||||
self.window.wl_surface()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
self.window_requests.closed.store(true, Ordering::Relaxed);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::WaylandWindowHandle::new({
|
||||
let ptr = self.window.wl_surface().id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
|
||||
});
|
||||
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw.into())) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::WaylandDisplayHandle::new({
|
||||
let ptr = self.display.id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
|
||||
});
|
||||
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw.into())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> CoreWindowId {
|
||||
CoreWindowId(self.window_id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_visible(&self, _visible: bool) {
|
||||
// Not possible on Wayland.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_visible(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
Err(NotSupportedError::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
Err(NotSupportedError::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_outer_position(&self, _: Position) {
|
||||
// Not possible on Wayland.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
let scale_factor = window_state.scale_factor();
|
||||
super::logical_to_physical_rounded(window_state.inner_size(), scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
fn request_redraw(&self) {
|
||||
// NOTE: try to not wake up the loop when the event was already scheduled and not yet
|
||||
// processed by the loop, because if at this point the value was `true` it could only
|
||||
// mean that the loop still haven't dispatched the value to the client and will do
|
||||
@@ -286,135 +293,116 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pre_present_notify(&self) {
|
||||
fn title(&self) -> String {
|
||||
self.window_state.lock().unwrap().title().to_owned()
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {
|
||||
self.window_state.lock().unwrap().request_frame_callback();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
fn reset_dead_keys(&self) {
|
||||
crate::platform_impl::common::xkb::reset_dead_keys()
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Err(NotSupportedError::new("window position information is not available on Wayland")
|
||||
.into())
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Err(NotSupportedError::new("window position information is not available on Wayland")
|
||||
.into())
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, _position: Position) {
|
||||
// Not possible.
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
let scale_factor = window_state.scale_factor();
|
||||
super::logical_to_physical_rounded(window_state.surface_size(), scale_factor)
|
||||
}
|
||||
|
||||
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
let new_size = window_state.request_surface_size(size);
|
||||
self.request_redraw();
|
||||
Some(new_size)
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
let scale_factor = window_state.scale_factor();
|
||||
super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
let new_size = window_state.request_inner_size(size);
|
||||
self.request_redraw();
|
||||
Some(new_size)
|
||||
}
|
||||
|
||||
/// Set the minimum inner size for the window.
|
||||
#[inline]
|
||||
pub fn set_min_inner_size(&self, min_size: Option<Size>) {
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let min_size = min_size.map(|size| size.to_logical(scale_factor));
|
||||
self.window_state.lock().unwrap().set_min_inner_size(min_size);
|
||||
self.window_state.lock().unwrap().set_min_surface_size(min_size);
|
||||
// NOTE: Requires commit to be applied.
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
/// Set the maximum inner size for the window.
|
||||
/// Set the maximum surface size for the window.
|
||||
#[inline]
|
||||
pub fn set_max_inner_size(&self, max_size: Option<Size>) {
|
||||
fn set_max_surface_size(&self, max_size: Option<Size>) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let max_size = max_size.map(|size| size.to_logical(scale_factor));
|
||||
self.window_state.lock().unwrap().set_max_inner_size(max_size);
|
||||
self.window_state.lock().unwrap().set_max_surface_size(max_size);
|
||||
// NOTE: Requires commit to be applied.
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resize_increments(&self, _increments: Option<Size>) {
|
||||
warn!("`set_resize_increments` is not implemented for Wayland");
|
||||
fn set_surface_resize_increments(&self, _increments: Option<Size>) {
|
||||
warn!("`set_surface_resize_increments` is not implemented for Wayland");
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
let new_title = title.to_string();
|
||||
self.window_state.lock().unwrap().set_title(new_title);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_transparent(&self, transparent: bool) {
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
self.window_state.lock().unwrap().set_transparent(transparent);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
self.window_state.lock().unwrap().has_focus()
|
||||
fn set_visible(&self, _visible: bool) {
|
||||
// Not possible on Wayland.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_minimized(&self) -> Option<bool> {
|
||||
// XXX clients don't know whether they are minimized or not.
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
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.lock().unwrap().drag_resize_window(direction)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
fn set_resizable(&self, resizable: bool) {
|
||||
if self.window_state.lock().unwrap().set_resizable(resizable) {
|
||||
// NOTE: Requires commit to be applied.
|
||||
self.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_resizable(&self) -> bool {
|
||||
fn is_resizable(&self) -> bool {
|
||||
self.window_state.lock().unwrap().resizable()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {
|
||||
fn set_enabled_buttons(&self, _buttons: WindowButtons) {
|
||||
// TODO(kchibisov) v5 of the xdg_shell allows that.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||
fn enabled_buttons(&self) -> WindowButtons {
|
||||
// TODO(kchibisov) v5 of the xdg_shell allows that.
|
||||
WindowButtons::all()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.window_state.lock().unwrap().scale_factor()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_blur(&self, blur: bool) {
|
||||
self.window_state.lock().unwrap().set_blur(blur);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorate: bool) {
|
||||
self.window_state.lock().unwrap().set_decorate(decorate)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_decorated(&self) -> bool {
|
||||
self.window_state.lock().unwrap().is_decorated()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_level(&self, _level: WindowLevel) {}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_window_icon(&self, _window_icon: Option<PlatformIcon>) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
fn set_minimized(&self, minimized: bool) {
|
||||
// You can't unminimize the window on Wayland.
|
||||
if !minimized {
|
||||
warn!("Unminimizing is ignored on Wayland.");
|
||||
@@ -424,8 +412,20 @@ impl Window {
|
||||
self.window.set_minimized();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
// XXX clients don't know whether they are minimized or not.
|
||||
None
|
||||
}
|
||||
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
if maximized {
|
||||
self.window.set_maximized()
|
||||
} else {
|
||||
self.window.unset_maximized()
|
||||
}
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.window_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
@@ -435,43 +435,14 @@ impl Window {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
if maximized {
|
||||
self.window.set_maximized()
|
||||
} else {
|
||||
self.window.unset_maximized()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
let is_fullscreen = self
|
||||
.window_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.last_configure
|
||||
.as_ref()
|
||||
.map(|last_configure| last_configure.is_fullscreen())
|
||||
.unwrap_or_default();
|
||||
|
||||
if is_fullscreen {
|
||||
let current_monitor = self.current_monitor().map(PlatformMonitorHandle::Wayland);
|
||||
Some(Fullscreen::Borderless(current_monitor))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
fn set_fullscreen(&self, fullscreen: Option<CoreFullscreen>) {
|
||||
match fullscreen {
|
||||
Some(Fullscreen::Exclusive(_)) => {
|
||||
Some(CoreFullscreen::Exclusive(_)) => {
|
||||
warn!("`Fullscreen::Exclusive` is ignored on Wayland");
|
||||
},
|
||||
#[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
|
||||
Some(Fullscreen::Borderless(monitor)) => {
|
||||
let output = monitor.and_then(|monitor| match monitor {
|
||||
Some(CoreFullscreen::Borderless(monitor)) => {
|
||||
let output = monitor.and_then(|monitor| match monitor.inner {
|
||||
PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
|
||||
#[cfg(x11_platform)]
|
||||
PlatformMonitorHandle::X(_) => None,
|
||||
@@ -483,22 +454,82 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: Cursor) {
|
||||
let window_state = &mut self.window_state.lock().unwrap();
|
||||
fn fullscreen(&self) -> Option<CoreFullscreen> {
|
||||
let is_fullscreen = self
|
||||
.window_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.last_configure
|
||||
.as_ref()
|
||||
.map(|last_configure| last_configure.is_fullscreen())
|
||||
.unwrap_or_default();
|
||||
|
||||
match cursor {
|
||||
Cursor::Icon(icon) => window_state.set_cursor(icon),
|
||||
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
|
||||
if is_fullscreen {
|
||||
let current_monitor = self.current_monitor();
|
||||
Some(CoreFullscreen::Borderless(current_monitor))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
self.window_state.lock().unwrap().set_cursor_visible(visible);
|
||||
fn scale_factor(&self) -> f64 {
|
||||
self.window_state.lock().unwrap().scale_factor()
|
||||
}
|
||||
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
#[inline]
|
||||
fn set_blur(&self, blur: bool) {
|
||||
self.window_state.lock().unwrap().set_blur(blur);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_decorations(&self, decorate: bool) {
|
||||
self.window_state.lock().unwrap().set_decorate(decorate)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_decorated(&self) -> bool {
|
||||
self.window_state.lock().unwrap().is_decorated()
|
||||
}
|
||||
|
||||
fn set_window_level(&self, _level: WindowLevel) {}
|
||||
|
||||
fn set_window_icon(&self, _window_icon: Option<crate::window::Icon>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
if window_state.ime_allowed() {
|
||||
let scale_factor = window_state.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
let size = size.to_logical(scale_factor);
|
||||
window_state.set_ime_cursor_area(position, size);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
|
||||
let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
|
||||
self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.window_state.lock().unwrap().set_ime_purpose(purpose);
|
||||
}
|
||||
|
||||
fn focus_window(&self) {}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
self.window_state.lock().unwrap().has_focus()
|
||||
}
|
||||
|
||||
fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||
Some(xdg_activation) => xdg_activation,
|
||||
None => {
|
||||
@@ -524,29 +555,26 @@ impl Window {
|
||||
xdg_activation_token.commit();
|
||||
}
|
||||
|
||||
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
|
||||
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||
Some(xdg_activation) => xdg_activation,
|
||||
None => return Err(NotSupportedError::new()),
|
||||
};
|
||||
|
||||
let serial = AsyncRequestSerial::get();
|
||||
|
||||
let data = XdgActivationTokenData::Obtain((self.window_id, serial));
|
||||
let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
|
||||
xdg_activation_token.set_surface(self.surface());
|
||||
xdg_activation_token.commit();
|
||||
|
||||
Ok(serial)
|
||||
fn set_theme(&self, theme: Option<Theme>) {
|
||||
self.window_state.lock().unwrap().set_theme(theme)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
self.window_state.lock().unwrap().set_cursor_grab(mode)
|
||||
fn theme(&self) -> Option<Theme> {
|
||||
self.window_state.lock().unwrap().theme()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
|
||||
fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
fn set_cursor(&self, cursor: Cursor) {
|
||||
let window_state = &mut self.window_state.lock().unwrap();
|
||||
|
||||
match cursor {
|
||||
Cursor::Icon(icon) => window_state.set_cursor(icon),
|
||||
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
self.window_state
|
||||
@@ -557,149 +585,78 @@ impl Window {
|
||||
.map(|_| self.request_redraw())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
self.window_state.lock().unwrap().set_cursor_grab(mode)
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
self.window_state.lock().unwrap().set_cursor_visible(visible);
|
||||
}
|
||||
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
self.window_state.lock().unwrap().drag_window()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
|
||||
self.window_state.lock().unwrap().drag_resize_window(direction)
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, position: Position) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
self.window_state.lock().unwrap().show_window_menu(position);
|
||||
}
|
||||
|
||||
fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
|
||||
let surface = self.window.wl_surface();
|
||||
|
||||
if hittest {
|
||||
surface.set_input_region(None);
|
||||
Ok(())
|
||||
} else {
|
||||
let region = Region::new(&*self.compositor).map_err(|_| {
|
||||
ExternalError::Os(os_error!(OsError::Misc("failed to set input region.")))
|
||||
})?;
|
||||
let region = Region::new(&*self.compositor).map_err(|err| os_error!(err))?;
|
||||
region.add(0, 0, 0, 0);
|
||||
surface.set_input_region(Some(region.wl_region()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
if window_state.ime_allowed() {
|
||||
let scale_factor = window_state.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
let size = size.to_logical(scale_factor);
|
||||
window_state.set_ime_cursor_area(position, size);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_allowed(&self, allowed: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
|
||||
let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
|
||||
self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.window_state.lock().unwrap().set_ime_purpose(purpose);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {}
|
||||
|
||||
#[inline]
|
||||
pub fn surface(&self) -> &WlSurface {
|
||||
self.window.wl_surface()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
let data = self.window.wl_surface().data::<SurfaceData>()?;
|
||||
data.outputs().next().map(MonitorHandle::new)
|
||||
data.outputs()
|
||||
.next()
|
||||
.map(MonitorHandle::new)
|
||||
.map(crate::platform_impl::MonitorHandle::Wayland)
|
||||
.map(|inner| CoreMonitorHandle { inner })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
|
||||
self.monitors.lock().unwrap().clone()
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
|
||||
Box::new(
|
||||
self.monitors
|
||||
.lock()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(crate::platform_impl::MonitorHandle::Wayland)
|
||||
.map(|inner| CoreMonitorHandle { inner }),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
// XXX there's no such concept on Wayland.
|
||||
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
// NOTE: There's no such concept on Wayland.
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_04")]
|
||||
#[inline]
|
||||
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 _;
|
||||
window_handle.display = self.display.id().as_ptr() as *mut _;
|
||||
rwh_04::RawWindowHandle::Wayland(window_handle)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
#[inline]
|
||||
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 _;
|
||||
rwh_05::RawDisplayHandle::Wayland(display_handle)
|
||||
}
|
||||
|
||||
/// Get the raw-window-handle v0.6 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())
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the raw-window-handle v0.6 window handle.
|
||||
#[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]
|
||||
pub fn set_theme(&self, theme: Option<Theme>) {
|
||||
self.window_state.lock().unwrap().set_theme(theme)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
self.window_state.lock().unwrap().theme()
|
||||
}
|
||||
|
||||
pub fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
#[inline]
|
||||
pub fn title(&self) -> String {
|
||||
self.window_state.lock().unwrap().title().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
self.window_requests.closed.store(true, Ordering::Relaxed);
|
||||
self.event_loop_awakener.ping();
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ use std::sync::{Arc, Mutex, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
use ahash::HashSet;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use sctk::compositor::{CompositorState, Region, SurfaceData, SurfaceDataExt};
|
||||
use sctk::reexports::client::backend::ObjectId;
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_shm::WlShm;
|
||||
@@ -19,8 +18,6 @@ use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_
|
||||
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 as XdgResizeEdge;
|
||||
|
||||
use sctk::compositor::{CompositorState, Region, SurfaceData, SurfaceDataExt};
|
||||
use sctk::seat::pointer::{PointerDataExt, ThemedPointer};
|
||||
use sctk::shell::xdg::window::{DecorationMode, Window, WindowConfigure};
|
||||
use sctk::shell::xdg::XdgSurface;
|
||||
@@ -28,28 +25,28 @@ use sctk::shell::WaylandSurface;
|
||||
use sctk::shm::slot::SlotPool;
|
||||
use sctk::shm::Shm;
|
||||
use sctk::subcompositor::SubcompositorState;
|
||||
use tracing::{info, warn};
|
||||
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
||||
|
||||
use crate::cursor::CustomCursor as RootCustomCursor;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
|
||||
use crate::error::{ExternalError, NotSupportedError};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::platform_impl::wayland::logical_to_physical_rounded;
|
||||
use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor};
|
||||
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
|
||||
use crate::platform_impl::{PlatformCustomCursor, WindowId};
|
||||
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};
|
||||
|
||||
use crate::platform_impl::wayland::seat::{
|
||||
PointerConstraintsState, WinitPointerData, WinitPointerDataExt, ZwpTextInputV3Ext,
|
||||
};
|
||||
use crate::platform_impl::wayland::state::{WindowCompositorUpdate, WinitState};
|
||||
use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor};
|
||||
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
|
||||
use crate::platform_impl::{PlatformCustomCursor, WindowId};
|
||||
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};
|
||||
|
||||
#[cfg(feature = "sctk-adwaita")]
|
||||
pub type WinitFrame = sctk_adwaita::AdwaitaFrame<WinitState>;
|
||||
#[cfg(not(feature = "sctk-adwaita"))]
|
||||
pub type WinitFrame = sctk::shell::xdg::fallback_frame::FallbackFrame<WinitState>;
|
||||
|
||||
// Minimum window inner size.
|
||||
// Minimum window surface size.
|
||||
const MIN_WINDOW_SIZE: LogicalSize<u32> = LogicalSize::new(2, 1);
|
||||
|
||||
/// The state of the window which is being updated from the [`WinitState`].
|
||||
@@ -115,7 +112,7 @@ pub struct WindowState {
|
||||
/// The text inputs observed on the window.
|
||||
text_inputs: Vec<ZwpTextInputV3>,
|
||||
|
||||
/// The inner size of the window, as in without client side decorations.
|
||||
/// The surface size of the window, as in without client side decorations.
|
||||
size: LogicalSize<u32>,
|
||||
|
||||
/// Whether the CSD fail to create, so we don't try to create them on each iteration.
|
||||
@@ -125,8 +122,8 @@ pub struct WindowState {
|
||||
decorate: bool,
|
||||
|
||||
/// Min size.
|
||||
min_inner_size: LogicalSize<u32>,
|
||||
max_inner_size: Option<LogicalSize<u32>>,
|
||||
min_surface_size: LogicalSize<u32>,
|
||||
max_surface_size: Option<LogicalSize<u32>>,
|
||||
|
||||
/// The size of the window when no states were applied to it. The primary use for it
|
||||
/// is to fallback to original window size, before it was maximized, if the compositor
|
||||
@@ -200,8 +197,8 @@ impl WindowState {
|
||||
ime_allowed: false,
|
||||
ime_purpose: ImePurpose::Normal,
|
||||
last_configure: None,
|
||||
max_inner_size: None,
|
||||
min_inner_size: MIN_WINDOW_SIZE,
|
||||
max_surface_size: None,
|
||||
min_surface_size: MIN_WINDOW_SIZE,
|
||||
pointer_constraints,
|
||||
pointers: Default::default(),
|
||||
queue_handle: queue_handle.clone(),
|
||||
@@ -331,7 +328,7 @@ impl WindowState {
|
||||
|
||||
// Apply configure bounds only when compositor let the user decide what size to pick.
|
||||
if constrain {
|
||||
let bounds = self.inner_size_bounds(&configure);
|
||||
let bounds = self.surface_size_bounds(&configure);
|
||||
new_size.width =
|
||||
bounds.0.map(|bound_w| new_size.width.min(bound_w.get())).unwrap_or(new_size.width);
|
||||
new_size.height = bounds
|
||||
@@ -356,7 +353,7 @@ impl WindowState {
|
||||
// NOTE: Set the configure before doing a resize, since we query it during it.
|
||||
self.last_configure = Some(configure);
|
||||
|
||||
if state_change_requires_resize || new_size != self.inner_size() {
|
||||
if state_change_requires_resize || new_size != self.surface_size() {
|
||||
self.resize(new_size);
|
||||
true
|
||||
} else {
|
||||
@@ -364,8 +361,8 @@ impl WindowState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the bounds for the inner size of the surface.
|
||||
fn inner_size_bounds(
|
||||
/// Compute the bounds for the surface size of the surface.
|
||||
fn surface_size_bounds(
|
||||
&self,
|
||||
configure: &WindowConfigure,
|
||||
) -> (Option<NonZeroU32>, Option<NonZeroU32>) {
|
||||
@@ -391,7 +388,7 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Start interacting drag resize.
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
|
||||
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
|
||||
let xdg_toplevel = self.window.xdg_toplevel();
|
||||
|
||||
// TODO(kchibisov) handle touch serials.
|
||||
@@ -405,7 +402,7 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Start the window drag.
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
pub fn drag_window(&self) -> Result<(), RequestError> {
|
||||
let xdg_toplevel = self.window.xdg_toplevel();
|
||||
// TODO(kchibisov) handle touch serials.
|
||||
self.apply_on_pointer(|_, data| {
|
||||
@@ -510,8 +507,8 @@ impl WindowState {
|
||||
// Restore min/max sizes of the window.
|
||||
self.reload_min_max_hints();
|
||||
} else {
|
||||
self.set_min_inner_size(Some(self.size));
|
||||
self.set_max_inner_size(Some(self.size));
|
||||
self.set_min_surface_size(Some(self.size));
|
||||
self.set_max_surface_size(Some(self.size));
|
||||
}
|
||||
|
||||
// Reload the state on the frame as well.
|
||||
@@ -536,7 +533,7 @@ impl WindowState {
|
||||
|
||||
/// Get the size of the window.
|
||||
#[inline]
|
||||
pub fn inner_size(&self) -> LogicalSize<u32> {
|
||||
pub fn surface_size(&self) -> LogicalSize<u32> {
|
||||
self.size
|
||||
}
|
||||
|
||||
@@ -631,21 +628,21 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Try to resize the window when the user can do so.
|
||||
pub fn request_inner_size(&mut self, inner_size: Size) -> PhysicalSize<u32> {
|
||||
pub fn request_surface_size(&mut self, surface_size: Size) -> PhysicalSize<u32> {
|
||||
if self.last_configure.as_ref().map(Self::is_stateless).unwrap_or(true) {
|
||||
self.resize(inner_size.to_logical(self.scale_factor()))
|
||||
self.resize(surface_size.to_logical(self.scale_factor()))
|
||||
}
|
||||
|
||||
logical_to_physical_rounded(self.inner_size(), self.scale_factor())
|
||||
logical_to_physical_rounded(self.surface_size(), self.scale_factor())
|
||||
}
|
||||
|
||||
/// Resize the window to the new inner size.
|
||||
fn resize(&mut self, inner_size: LogicalSize<u32>) {
|
||||
self.size = inner_size;
|
||||
/// Resize the window to the new surface size.
|
||||
fn resize(&mut self, surface_size: LogicalSize<u32>) {
|
||||
self.size = surface_size;
|
||||
|
||||
// Update the stateless size.
|
||||
if Some(true) == self.last_configure.as_ref().map(Self::is_stateless) {
|
||||
self.stateless_size = inner_size;
|
||||
self.stateless_size = surface_size;
|
||||
}
|
||||
|
||||
// Update the inner frame.
|
||||
@@ -676,7 +673,7 @@ impl WindowState {
|
||||
|
||||
// Update the target viewport, this is used if and only if fractional scaling is in use.
|
||||
if let Some(viewport) = self.viewport.as_ref() {
|
||||
// Set inner size without the borders.
|
||||
// Set surface size without the borders.
|
||||
viewport.set_destination(self.size.width as _, self.size.height as _);
|
||||
}
|
||||
}
|
||||
@@ -756,7 +753,7 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Set maximum inner window size.
|
||||
pub fn set_min_inner_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
pub fn set_min_surface_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
// Ensure that the window has the right minimum size.
|
||||
let mut size = size.unwrap_or(MIN_WINDOW_SIZE);
|
||||
size.width = size.width.max(MIN_WINDOW_SIZE.width);
|
||||
@@ -769,12 +766,12 @@ impl WindowState {
|
||||
.map(|frame| frame.add_borders(size.width, size.height).into())
|
||||
.unwrap_or(size);
|
||||
|
||||
self.min_inner_size = size;
|
||||
self.min_surface_size = size;
|
||||
self.window.set_min_size(Some(size.into()));
|
||||
}
|
||||
|
||||
/// Set maximum inner window size.
|
||||
pub fn set_max_inner_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
pub fn set_max_surface_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
let size = size.map(|size| {
|
||||
self.frame
|
||||
.as_ref()
|
||||
@@ -782,7 +779,7 @@ impl WindowState {
|
||||
.unwrap_or(size)
|
||||
});
|
||||
|
||||
self.max_inner_size = size;
|
||||
self.max_surface_size = size;
|
||||
self.window.set_max_size(size.map(Into::into));
|
||||
}
|
||||
|
||||
@@ -802,7 +799,7 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Set the cursor grabbing state on the top-level.
|
||||
pub fn set_cursor_grab(&mut self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
pub fn set_cursor_grab(&mut self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
if self.cursor_grab_mode.user_grab_mode == mode {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -815,16 +812,20 @@ impl WindowState {
|
||||
|
||||
/// Reload the hints for minimum and maximum sizes.
|
||||
pub fn reload_min_max_hints(&mut self) {
|
||||
self.set_min_inner_size(Some(self.min_inner_size));
|
||||
self.set_max_inner_size(self.max_inner_size);
|
||||
self.set_min_surface_size(Some(self.min_surface_size));
|
||||
self.set_max_surface_size(self.max_surface_size);
|
||||
}
|
||||
|
||||
/// Set the grabbing state on the surface.
|
||||
fn set_cursor_grab_inner(&mut self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
fn set_cursor_grab_inner(&mut self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
||||
let pointer_constraints = match self.pointer_constraints.as_ref() {
|
||||
Some(pointer_constraints) => pointer_constraints,
|
||||
None if mode == CursorGrabMode::None => return Ok(()),
|
||||
None => return Err(ExternalError::NotSupported(NotSupportedError::new())),
|
||||
None => {
|
||||
return Err(
|
||||
NotSupportedError::new("zwp_pointer_constraints is not available").into()
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
// Replace the current mode.
|
||||
@@ -868,16 +869,17 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Set the position of the cursor.
|
||||
pub fn set_cursor_position(&self, position: LogicalPosition<f64>) -> Result<(), ExternalError> {
|
||||
pub fn set_cursor_position(&self, position: LogicalPosition<f64>) -> Result<(), RequestError> {
|
||||
if self.pointer_constraints.is_none() {
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
||||
return Err(NotSupportedError::new("zwp_pointer_constraints is not available").into());
|
||||
}
|
||||
|
||||
// Position can be set only for locked cursor.
|
||||
if self.cursor_grab_mode.current_grab_mode != CursorGrabMode::Locked {
|
||||
return Err(ExternalError::Os(os_error!(crate::platform_impl::OsError::Misc(
|
||||
"cursor position can be set only for locked cursor."
|
||||
))));
|
||||
return Err(NotSupportedError::new(
|
||||
"cursor position could only be changed for locked pointer",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
self.apply_on_pointer(|_, data| {
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
//! X11 has a "startup notification" specification similar to Wayland's, see this URL:
|
||||
//! <https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt>
|
||||
|
||||
use super::atoms::*;
|
||||
use super::{VoidCookie, X11Error, XConnection};
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::fmt::Write;
|
||||
|
||||
use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||
|
||||
use super::atoms::*;
|
||||
use super::{VoidCookie, X11Error, XConnection};
|
||||
|
||||
impl XConnection {
|
||||
/// "Request" a new activation token from the server.
|
||||
pub(crate) fn request_activation_token(&self, window_title: &str) -> Result<String, X11Error> {
|
||||
|
||||
@@ -48,6 +48,8 @@ atom_manager! {
|
||||
_NET_WM_NAME,
|
||||
_NET_WM_PID,
|
||||
_NET_WM_PING,
|
||||
_NET_WM_SYNC_REQUEST,
|
||||
_NET_WM_SYNC_REQUEST_COUNTER,
|
||||
_NET_WM_STATE,
|
||||
_NET_WM_STATE_ABOVE,
|
||||
_NET_WM_STATE_BELOW,
|
||||
@@ -112,6 +114,7 @@ impl Index<AtomName> for Atoms {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use AtomName::*;
|
||||
// Make sure `None` is still defined.
|
||||
pub(crate) use core::option::Option::None;
|
||||
|
||||
pub(crate) use AtomName::*;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user