mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 07:03:15 -04:00
Compare commits
324 Commits
v0.29.5
...
notgull/sp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d28c867b61 | ||
|
|
cfd35d2708 | ||
|
|
1b9c576f53 | ||
|
|
52532d2579 | ||
|
|
4b28f10470 | ||
|
|
daf1304879 | ||
|
|
c6e57430bb | ||
|
|
284f4c0558 | ||
|
|
859a6f109c | ||
|
|
a232f7bc77 | ||
|
|
48831f7910 | ||
|
|
114e6999d3 | ||
|
|
2f197315d9 | ||
|
|
557464489e | ||
|
|
623b3e5070 | ||
|
|
a97255e0fc | ||
|
|
3a817647d5 | ||
|
|
56035e1f13 | ||
|
|
08fc4099e8 | ||
|
|
4d4d6e5052 | ||
|
|
cf5f4de19e | ||
|
|
21df84b7f4 | ||
|
|
dd13ccda4c | ||
|
|
df8805c0d2 | ||
|
|
db1ca45a17 | ||
|
|
ff731197dc | ||
|
|
f526a47152 | ||
|
|
f204467838 | ||
|
|
6641dfa412 | ||
|
|
c1168b4f58 | ||
|
|
f8b7c4b78f | ||
|
|
3830b492c4 | ||
|
|
8862ce0163 | ||
|
|
569c44a632 | ||
|
|
ef2ec904ce | ||
|
|
98d3391f2d | ||
|
|
d0a1917603 | ||
|
|
b36d8d1e52 | ||
|
|
a5b08fc48c | ||
|
|
0482d9cfce | ||
|
|
10a785019c | ||
|
|
0cc19716f3 | ||
|
|
572d7ee77c | ||
|
|
b0c59c8416 | ||
|
|
d7c7ba1d6c | ||
|
|
aec608f93c | ||
|
|
d1717b6a01 | ||
|
|
6b29253797 | ||
|
|
30775f4982 | ||
|
|
41070d7c67 | ||
|
|
6df972d108 | ||
|
|
73910d20cc | ||
|
|
16d860736b | ||
|
|
2ee44246ae | ||
|
|
14b418a3a7 | ||
|
|
c86b0daf7f | ||
|
|
40b61d2d92 | ||
|
|
22311802b5 | ||
|
|
169cd39f93 | ||
|
|
bdeb2574dc | ||
|
|
4fe38d8067 | ||
|
|
816798bfd1 | ||
|
|
f99c810bec | ||
|
|
d39528aa69 | ||
|
|
787b2d7362 | ||
|
|
37b6243289 | ||
|
|
8ea1da7879 | ||
|
|
b1209bc253 | ||
|
|
021fd23c34 | ||
|
|
dd127463c5 | ||
|
|
ac247cd081 | ||
|
|
ea1bfd254d | ||
|
|
178f5fda05 | ||
|
|
42dbc4748e | ||
|
|
8b3de7cedf | ||
|
|
8b0ffb7e7d | ||
|
|
c55a2c779b | ||
|
|
c5a422eed6 | ||
|
|
1893b0ec42 | ||
|
|
5e106b4dbb | ||
|
|
5a1d3e4656 | ||
|
|
8f6de4ef4b | ||
|
|
ad1843aea6 | ||
|
|
ca1674519a | ||
|
|
f78edc7ef1 | ||
|
|
4f295e0c94 | ||
|
|
658f49b014 | ||
|
|
34e42ff94d | ||
|
|
ba654bb61e | ||
|
|
f5c691467b | ||
|
|
a87cfb62c3 | ||
|
|
25d6a1d46d | ||
|
|
e0fea25b06 | ||
|
|
843d7904d6 | ||
|
|
28a811bbba | ||
|
|
61a873d79a | ||
|
|
be4a660011 | ||
|
|
34dd2cdba9 | ||
|
|
775c8ece70 | ||
|
|
c12c7b82e8 | ||
|
|
8cc5cb9d9b | ||
|
|
9a28bb4b49 | ||
|
|
4f6fd44c6c | ||
|
|
5a43ea8cd6 | ||
|
|
e9a25a4c91 | ||
|
|
674657efb6 | ||
|
|
7d5bee767c | ||
|
|
745cfaab2c | ||
|
|
a8f49dc8ef | ||
|
|
e5310ade08 | ||
|
|
37946e0a3a | ||
|
|
86b737f5e7 | ||
|
|
e37585e5bc | ||
|
|
4aeeb24745 | ||
|
|
8cd3aaa8a2 | ||
|
|
2c15de7cf9 | ||
|
|
0a7ea61834 | ||
|
|
4ee11018c2 | ||
|
|
4f669ebbd2 | ||
|
|
7761b2b16c | ||
|
|
ae41e3265f | ||
|
|
8702a09333 | ||
|
|
8b5c84f404 | ||
|
|
a676d0018b | ||
|
|
04ca85a909 | ||
|
|
cc33212479 | ||
|
|
f2c5127f27 | ||
|
|
af93167237 | ||
|
|
7f6b16a6af | ||
|
|
bf5806a9b2 | ||
|
|
2a9c593e01 | ||
|
|
becdd0dbd2 | ||
|
|
3eea505440 | ||
|
|
b863283c38 | ||
|
|
73718c9f2f | ||
|
|
f735f028a1 | ||
|
|
da947992ac | ||
|
|
e9784127df | ||
|
|
0be2bb0a8c | ||
|
|
075996b1fa | ||
|
|
a7241b3db3 | ||
|
|
17296e9878 | ||
|
|
b3c87caa7c | ||
|
|
81a1d9c396 | ||
|
|
5612626944 | ||
|
|
d3ca685b77 | ||
|
|
7bed5eecfd | ||
|
|
14140607d1 | ||
|
|
eab982c402 | ||
|
|
21701a33de | ||
|
|
c89e6df758 | ||
|
|
e9210555c1 | ||
|
|
0994b5ceb8 | ||
|
|
bcce5134e1 | ||
|
|
d333dd8664 | ||
|
|
52af1b4a77 | ||
|
|
3c9f9da19e | ||
|
|
075dfcea19 | ||
|
|
92b7dcccc1 | ||
|
|
5a3be586f4 | ||
|
|
12dbbf8012 | ||
|
|
53ca5af48f | ||
|
|
c235bd154a | ||
|
|
f4e71a1d9c | ||
|
|
62ed51a138 | ||
|
|
b2a2ec91ae | ||
|
|
d37d1a03b2 | ||
|
|
772b21ce09 | ||
|
|
2edcd09704 | ||
|
|
d35c3bea42 | ||
|
|
89a184ed84 | ||
|
|
36d4907da8 | ||
|
|
98b3508aca | ||
|
|
c0db53a516 | ||
|
|
52b7205b75 | ||
|
|
41dbbc27a0 | ||
|
|
c346fb7e61 | ||
|
|
6a041f84ba | ||
|
|
acfeff5327 | ||
|
|
b9e1e96eaa | ||
|
|
880238a24f | ||
|
|
f5b4d6938f | ||
|
|
c65e2247a1 | ||
|
|
801fddbfcf | ||
|
|
3ad64fb811 | ||
|
|
9bf4493a21 | ||
|
|
48f6582eb4 | ||
|
|
ef34692148 | ||
|
|
c48116a8fd | ||
|
|
b938fe9df5 | ||
|
|
b7e3649e8b | ||
|
|
844269d017 | ||
|
|
e41fac825c | ||
|
|
bbeacc46d5 | ||
|
|
61581ebb4f | ||
|
|
93f1000a05 | ||
|
|
1ea41a2ee2 | ||
|
|
42c9b7e40e | ||
|
|
0960635895 | ||
|
|
789a497980 | ||
|
|
fac6110cb6 | ||
|
|
0363be4776 | ||
|
|
f5dd1c008c | ||
|
|
48a1e84906 | ||
|
|
ee0db52ac4 | ||
|
|
c7cf0cfd83 | ||
|
|
8393d98940 | ||
|
|
af247eac0f | ||
|
|
b2b4564a5f | ||
|
|
cb58c49a90 | ||
|
|
ffb46dd61f | ||
|
|
8c8fb39fcd | ||
|
|
2422ea39d0 | ||
|
|
878d832d24 | ||
|
|
e2e01e1fc6 | ||
|
|
c8b685ddbc | ||
|
|
f10ae52385 | ||
|
|
e731041c15 | ||
|
|
9df7fc47a1 | ||
|
|
992aeb0ca0 | ||
|
|
0caba93b51 | ||
|
|
c00c1e9eb7 | ||
|
|
83950acd5a | ||
|
|
b99403b1b9 | ||
|
|
4f0ce7201d | ||
|
|
e648169861 | ||
|
|
8fdd81ecef | ||
|
|
7a2a2341c2 | ||
|
|
d68d9eab38 | ||
|
|
a06ea45c0f | ||
|
|
67b041e231 | ||
|
|
6dfc78fb50 | ||
|
|
477619c0a7 | ||
|
|
5f1a4b65ad | ||
|
|
bb9b629bc3 | ||
|
|
0c8cf94a70 | ||
|
|
1dfca5a395 | ||
|
|
7541220a41 | ||
|
|
7e11912d22 | ||
|
|
86baa1c99a | ||
|
|
d9f04780cc | ||
|
|
67d3fd28f7 | ||
|
|
a3cba838ea | ||
|
|
48abf52aac | ||
|
|
68ef9f707e | ||
|
|
9979441c82 | ||
|
|
309e6aa85a | ||
|
|
8b8556798e | ||
|
|
2d96480a89 | ||
|
|
6caff77abb | ||
|
|
af6c343d0e | ||
|
|
119462795a | ||
|
|
f801c4a00b | ||
|
|
f9758528f6 | ||
|
|
778d70c001 | ||
|
|
dc973883c9 | ||
|
|
2233edb9a0 | ||
|
|
bd2f1e8312 | ||
|
|
e9ebf1e5f4 | ||
|
|
5f7955cb2b | ||
|
|
793c535b01 | ||
|
|
ed26dd58fd | ||
|
|
584aab4cd0 | ||
|
|
8100a6a584 | ||
|
|
cad3277550 | ||
|
|
3c3a863cc9 | ||
|
|
8a7e18aaf0 | ||
|
|
57fad2ce15 | ||
|
|
7a58fe58ce | ||
|
|
38f28d5836 | ||
|
|
189a0080a6 | ||
|
|
b5aa96bea4 | ||
|
|
19e3906369 | ||
|
|
9ac3259a79 | ||
|
|
2b2dd6b65d | ||
|
|
75173118b0 | ||
|
|
e33d2bee6c | ||
|
|
ae7497e18f | ||
|
|
935146d299 | ||
|
|
755c533b08 | ||
|
|
ae9b02e097 | ||
|
|
e6c7cc297d | ||
|
|
b74cee8df1 | ||
|
|
e5eb253698 | ||
|
|
ec11b4877f | ||
|
|
9e46dffcc5 | ||
|
|
7501039d57 | ||
|
|
289ce32d77 | ||
|
|
0d366ffbda | ||
|
|
a6f414d732 | ||
|
|
c47d0846fa | ||
|
|
461efaf99f | ||
|
|
420840278b | ||
|
|
f5e73b0af4 | ||
|
|
f40b5f0dad | ||
|
|
c91402efb9 | ||
|
|
43acf7f42f | ||
|
|
c62e64060b | ||
|
|
f7a84a5b50 | ||
|
|
89aa7cc06e | ||
|
|
b166e1ff13 | ||
|
|
97434d8d80 | ||
|
|
06fb089633 | ||
|
|
4d6dbea74c | ||
|
|
c5941d105f | ||
|
|
b63164645b | ||
|
|
5b5ebc25d8 | ||
|
|
d7ec899d69 | ||
|
|
5379d60e4d | ||
|
|
44e2f95331 | ||
|
|
3b2d1a7643 | ||
|
|
50b17a3907 | ||
|
|
af26f01b95 | ||
|
|
c4d70d75c1 | ||
|
|
db8de03142 | ||
|
|
ff0ce9d065 | ||
|
|
42e492cde8 | ||
|
|
5e0e1e96bc | ||
|
|
bd890e69aa | ||
|
|
bca57ed0b4 | ||
|
|
4652d48105 | ||
|
|
96c0b267e2 | ||
|
|
81fd39485f | ||
|
|
6178acede8 |
16
.github/CODEOWNERS
vendored
16
.github/CODEOWNERS
vendored
@@ -1,12 +1,6 @@
|
||||
# Core maintainers:
|
||||
# - @msiglreith
|
||||
# - @kchibisov
|
||||
# - @madsmtm
|
||||
# - @maroider
|
||||
|
||||
# Android
|
||||
/src/platform/android.rs @msiglreith
|
||||
/src/platform_impl/android @msiglreith
|
||||
/src/platform/android.rs @msiglreith @MarijnS95
|
||||
/src/platform_impl/android @msiglreith @MarijnS95
|
||||
|
||||
# iOS
|
||||
/src/platform/ios.rs @madsmtm
|
||||
@@ -20,14 +14,14 @@
|
||||
/src/platform_impl/linux/wayland @kchibisov
|
||||
|
||||
# X11
|
||||
/src/platform/x11.rs @kchibisov
|
||||
/src/platform_impl/linux/x11 @kchibisov
|
||||
/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 (no maintainer)
|
||||
# Web
|
||||
/src/platform/web.rs @daxpedda
|
||||
/src/platform_impl/web @daxpedda
|
||||
|
||||
|
||||
44
.github/workflows/ci.yml
vendored
44
.github/workflows/ci.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain: [stable, nightly, '1.65.0']
|
||||
toolchain: [stable, nightly, '1.70.0']
|
||||
platform:
|
||||
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
|
||||
- { name: 'Windows 64bit MSVC', target: x86_64-pc-windows-msvc, os: windows-latest, }
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- { name: 'Linux 64bit', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
|
||||
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
|
||||
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--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: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, }
|
||||
@@ -43,11 +43,20 @@ jobs:
|
||||
- { name: 'web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
|
||||
exclude:
|
||||
# Android is tested on stable-3
|
||||
- toolchain: '1.65.0'
|
||||
- toolchain: '1.70.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
include:
|
||||
- toolchain: '1.69.0'
|
||||
- toolchain: '1.70.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
- toolchain: 'nightly'
|
||||
platform: {
|
||||
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,
|
||||
}
|
||||
|
||||
env:
|
||||
# Set more verbose terminal output
|
||||
@@ -55,8 +64,7 @@ jobs:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
# Faster compilation and error on warnings
|
||||
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings'
|
||||
RUSTDOCFLAGS: '--deny=warnings'
|
||||
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}'
|
||||
|
||||
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
|
||||
CMD: ${{ matrix.platform.cmd }}
|
||||
@@ -109,19 +117,21 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
|
||||
targets: ${{ matrix.platform.target }}
|
||||
components: clippy
|
||||
components: clippy, ${{ matrix.platform.components }}
|
||||
|
||||
- name: Check documentation
|
||||
run: cargo doc --no-deps $OPTIONS --document-private-items
|
||||
env:
|
||||
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }}'
|
||||
|
||||
- name: Build crate
|
||||
run: cargo $CMD build $OPTIONS
|
||||
run: cargo $CMD build -p winit $OPTIONS
|
||||
|
||||
- name: Build tests
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test -p winit --no-run $OPTIONS
|
||||
|
||||
- name: Run tests
|
||||
if: >
|
||||
@@ -129,7 +139,7 @@ jobs:
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test $OPTIONS
|
||||
|
||||
- name: Lint with clippy
|
||||
@@ -139,8 +149,8 @@ jobs:
|
||||
- name: Build tests with serde enabled
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS --features serde
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS -p winit --features serde
|
||||
|
||||
- name: Run tests with serde enabled
|
||||
if: >
|
||||
@@ -148,9 +158,15 @@ jobs:
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD 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
|
||||
env:
|
||||
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs'
|
||||
|
||||
# See restore step above
|
||||
- name: Save cache of cargo folder
|
||||
uses: actions/cache/save@v3
|
||||
|
||||
50
.github/workflows/docs.yml
vendored
Normal file
50
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
name: Documentation
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}winit
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly
|
||||
|
||||
- name: Run Rustdoc
|
||||
env:
|
||||
RUSTDOCFLAGS: --crate-version master --cfg=docsrs
|
||||
run: |
|
||||
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
|
||||
- name: Fix permissions
|
||||
run: |
|
||||
chmod -c -R +rX "target/doc" | while read line; do
|
||||
echo "::warning title=Invalid file permissions automatically fixed::$line"
|
||||
done
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: target/doc
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
80
CHANGELOG.md
80
CHANGELOG.md
@@ -11,6 +11,85 @@ Unreleased` header.
|
||||
|
||||
# Unreleased
|
||||
|
||||
- On X11, fix swapped instance and general class names.
|
||||
- **Breaking:** Removed unnecessary generic parameter `T` from `EventLoopWindowTarget`.
|
||||
- On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example.
|
||||
- **Breaking:** Remove `Window::set_cursor_icon`
|
||||
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a `CursorIcon` or `CustomCursor`
|
||||
- Add `CustomCursor`
|
||||
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
|
||||
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
|
||||
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated cursors from other `CustomCursor`s.
|
||||
- On macOS, add services menu.
|
||||
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
|
||||
- On Web, fix setting cursor icon overriding cursor visibility.
|
||||
- **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
|
||||
- **Breaking:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available.
|
||||
- **Breaking:** Bump MSRV from `1.65` to `1.70`.
|
||||
- On Web, add the ability to toggle calling `Event.preventDefault()` on `Window`.
|
||||
- **Breaking:** Remove `WindowAttributes::fullscreen()` and expose as field directly.
|
||||
- **Breaking:** Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold static data.
|
||||
- **Breaking:** No longer export `platform::x11::XNotSupported`.
|
||||
- **Breaking:** Renamed `platform::x11::XWindowType` to `platform::x11::WindowType`.
|
||||
- Add the `OwnedDisplayHandle` type for allowing safe display handle usage outside of trivial cases.
|
||||
- **Breaking:** Rename `TouchpadMagnify` to `PinchGesture`, `SmartMagnify` to `DoubleTapGesture` and `TouchpadRotate` to `RotationGesture` to represent the action rather than the intent.
|
||||
- on iOS, add detection support for `PinchGesture`, `DoubleTapGesture` and `RotationGesture`.
|
||||
- on Windows: add `with_system_backdrop`, `with_border_color`, `with_title_background_color`, `with_title_text_color` and `with_corner_preference`
|
||||
- On Windows, Remove `WS_CAPTION`, `WS_BORDER` and `WS_EX_WINDOWEDGE` styles for child windows without decorations.
|
||||
- On Windows, fixed a race condition when sending an event through the loop proxy.
|
||||
- **Breaking:** Removed `EventLoopError::AlreadyRunning`, which can't happen as it is already prevented by the type system.
|
||||
- On Wayland, disable `Occluded` event handling.
|
||||
- Added `EventLoop::builder`, which is intended to replace the (now deprecated) `EventLoopBuilder::new`.
|
||||
- **Breaking:** Changed the signature of `EventLoop::with_user_event` to return a builder.
|
||||
- **Breaking:** Removed `EventLoopBuilder::with_user_event`, the functionality is now available in `EventLoop::with_user_event`.
|
||||
- Add `Window::builder`, which is intended to replace the (now deprecated) `WindowBuilder::new`.
|
||||
- On X11, reload dpi on `_XSETTINGS_SETTINGS` update.
|
||||
- On X11, fix deadlock when adjusting DPI and resizing at the same time.
|
||||
- On Wayland, fix `Focused(false)` being send when other seats still have window focused.
|
||||
- On Wayland, fix `Window::set_{min,max}_inner_size` not always applied.
|
||||
- On Windows, fix inconsistent resizing behavior with multi-monitor setups when repositioning outside the event loop.
|
||||
- On Wayland, fix `WAYLAND_SOCKET` not used when detecting platform.
|
||||
- **Breaking:** Move some types to the `winit-core` crate. Most types are
|
||||
re-exported verbatim; however:
|
||||
- `Event`, `WindowEvent` and `KeyEvent` now take a generic that `winit`
|
||||
implements.
|
||||
- `ActivationToken` and `InnerSizeWriter` expose new methods to make them
|
||||
useful.
|
||||
- `InnerSizeWriter::request_inner_size` returns an `InnerSizeIgnored` error
|
||||
instead of an `ExternalError`.
|
||||
|
||||
# 0.29.10
|
||||
|
||||
- On Web, account for canvas being focused already before event loop starts.
|
||||
- On Web, increase cursor position accuracy.
|
||||
|
||||
# 0.29.9
|
||||
|
||||
- On X11, fix `NotSupported` error not propagated when creating event loop.
|
||||
- On Wayland, fix resize not issued when scale changes
|
||||
- On X11 and Wayland, fix arrow up on keypad reported as `ArrowLeft`.
|
||||
- On macOS, report correct logical key when Ctrl or Cmd is pressed.
|
||||
|
||||
# 0.29.8
|
||||
|
||||
- On X11, fix IME input lagging behind.
|
||||
- On X11, fix `ModifiersChanged` not sent from xdotool-like input
|
||||
- On X11, fix keymap not updated from xmodmap.
|
||||
- On X11, reduce the amount of time spent fetching screen resources.
|
||||
- On Wayland, fix `Window::request_inner_size` being overwritten by resize.
|
||||
- On Wayland, fix `Window::inner_size` not using the correct rounding.
|
||||
|
||||
# 0.29.7
|
||||
|
||||
- On X11, fix `Xft.dpi` reload during runtime.
|
||||
- On X11, fix window minimize.
|
||||
|
||||
# 0.29.6
|
||||
|
||||
- On Web, fix context menu not being disabled by `with_prevent_default(true)`.
|
||||
- On Wayland, fix `WindowEvent::Destroyed` not being delivered after destroying window.
|
||||
- Fix `EventLoopExtRunOnDemand::run_on_demand` not working for consequent invocation
|
||||
|
||||
# 0.29.5
|
||||
|
||||
- On macOS, remove spurious error logging when handling `Fn`.
|
||||
@@ -34,6 +113,7 @@ Unreleased` header.
|
||||
- On macOS, send a `Resized` event after each `ScaleFactorChanged` event.
|
||||
- On Wayland, fix `wl_surface` being destroyed before associated objects.
|
||||
- On macOS, fix assertion when pressing `Fn` key.
|
||||
- On Windows, add `WindowBuilderExtWindows::with_clip_children` to control `WS_CLIPCHILDREN` style.
|
||||
|
||||
# 0.29.3
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ your description of the issue as detailed as possible:
|
||||
|
||||
When making a code contribution to winit, before opening your pull request, please make sure that:
|
||||
|
||||
- your patch builds with Winit's minimal supported rust version - Rust 1.65.
|
||||
- your patch builds with Winit's minimal supported rust version - Rust 1.70.
|
||||
- you tested your modifications on all the platforms impacted, or if not possible, detail which platforms
|
||||
were not tested, and what should be tested, so that a maintainer or another contributor can test them
|
||||
- you updated any relevant documentation in winit
|
||||
|
||||
234
Cargo.toml
234
Cargo.toml
@@ -1,226 +1,16 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.29.5"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2021"
|
||||
keywords = ["windowing"]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/rust-windowing/winit"
|
||||
documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
rust-version = "1.65.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [
|
||||
"rwh_04",
|
||||
"rwh_05",
|
||||
"rwh_06",
|
||||
"serde",
|
||||
# Enabled to get docs to compile
|
||||
"android-native-activity",
|
||||
]
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
# These are all tested in CI
|
||||
targets = [
|
||||
# Windows
|
||||
"i686-pc-windows-msvc",
|
||||
"x86_64-pc-windows-msvc",
|
||||
# macOS
|
||||
"x86_64-apple-darwin",
|
||||
# Unix (X11 & Wayland)
|
||||
"i686-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
# iOS
|
||||
"x86_64-apple-ios",
|
||||
# Android
|
||||
"aarch64-linux-android",
|
||||
# WebAssembly
|
||||
"wasm32-unknown-unknown",
|
||||
]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
|
||||
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"]
|
||||
wayland-dlopen = ["wayland-backend/dlopen"]
|
||||
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
|
||||
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
|
||||
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"]
|
||||
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
|
||||
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
|
||||
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases = "0.1.1"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2"
|
||||
cursor-icon = "1.1.0"
|
||||
log = "0.4"
|
||||
mint = { version = "0.5.6", optional = true }
|
||||
once_cell = "1.12"
|
||||
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
|
||||
rwh_05 = { package = "raw-window-handle", version = "0.5", features = ["std"], optional = true }
|
||||
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
|
||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||
smol_str = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
image = { version = "0.24.0", default-features = false, features = ["png"] }
|
||||
simple_logger = { version = "4.2.0", default_features = false }
|
||||
winit = { path = ".", features = ["rwh_05"] }
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
|
||||
softbuffer = "0.3.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android-activity = "0.5.0"
|
||||
ndk = { version = "0.8.0", default-features = false }
|
||||
ndk-sys = "0.5.0"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = "0.4.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-graphics = "0.23.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.icrate]
|
||||
version = "0.0.4"
|
||||
features = [
|
||||
"dispatch",
|
||||
"Foundation",
|
||||
"Foundation_NSArray",
|
||||
"Foundation_NSAttributedString",
|
||||
"Foundation_NSMutableAttributedString",
|
||||
"Foundation_NSData",
|
||||
"Foundation_NSDictionary",
|
||||
"Foundation_NSString",
|
||||
"Foundation_NSProcessInfo",
|
||||
"Foundation_NSThread",
|
||||
"Foundation_NSNumber",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies.icrate]
|
||||
version = "0.0.4"
|
||||
features = [
|
||||
"dispatch",
|
||||
"Foundation",
|
||||
"Foundation_NSArray",
|
||||
"Foundation_NSString",
|
||||
"Foundation_NSProcessInfo",
|
||||
"Foundation_NSThread",
|
||||
"Foundation_NSSet",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
unicode-segmentation = "1.7.1"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
|
||||
version = "0.48"
|
||||
features = [
|
||||
"Win32_Devices_HumanInterfaceDevice",
|
||||
"Win32_Foundation",
|
||||
"Win32_Globalization",
|
||||
"Win32_Graphics_Dwm",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_Media",
|
||||
"Win32_System_Com_StructuredStorage",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Ole",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_WindowsProgramming",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_UI_Controls",
|
||||
"Win32_UI_HiDpi",
|
||||
"Win32_UI_Input_Ime",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
"Win32_UI_Input_Pointer",
|
||||
"Win32_UI_Input_Touch",
|
||||
"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]
|
||||
ahash = { version = "0.8.3", features = ["no-rng"], optional = true }
|
||||
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
||||
calloop = "0.12.3"
|
||||
libc = "0.2.64"
|
||||
memmap2 = { version = "0.9.0", optional = true }
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
|
||||
sctk-adwaita = { version = "0.8.0", default_features = false, optional = true }
|
||||
wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
|
||||
wayland-client = { version = "0.31.1", optional = true }
|
||||
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
|
||||
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
|
||||
xkbcommon-dl = "0.4.0"
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
orbclient = { version = "0.3.42", default-features = false }
|
||||
redox_syscall = "0.3"
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies.web_sys]
|
||||
package = "web-sys"
|
||||
version = "0.3.64"
|
||||
features = [
|
||||
'AbortController',
|
||||
'AbortSignal',
|
||||
'console',
|
||||
'CssStyleDeclaration',
|
||||
'Document',
|
||||
'DomRect',
|
||||
'DomRectReadOnly',
|
||||
'Element',
|
||||
'Event',
|
||||
'EventTarget',
|
||||
'FocusEvent',
|
||||
'HtmlCanvasElement',
|
||||
'HtmlElement',
|
||||
'IntersectionObserver',
|
||||
'IntersectionObserverEntry',
|
||||
'KeyboardEvent',
|
||||
'MediaQueryList',
|
||||
'MessageChannel',
|
||||
'MessagePort',
|
||||
'Node',
|
||||
'PageTransitionEvent',
|
||||
'PointerEvent',
|
||||
'ResizeObserver',
|
||||
'ResizeObserverBoxOptions',
|
||||
'ResizeObserverEntry',
|
||||
'ResizeObserverOptions',
|
||||
'ResizeObserverSize',
|
||||
'VisibilityState',
|
||||
'Window',
|
||||
'WheelEvent'
|
||||
]
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
atomic-waker = "1"
|
||||
js-sys = "0.3.64"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-time = "0.2"
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dev-dependencies]
|
||||
console_log = "1"
|
||||
web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"run-wasm",
|
||||
"winit",
|
||||
"winit-core"
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
bitflags = "2"
|
||||
cfg_aliases = "0.2.0"
|
||||
cursor-icon = "1.1.0"
|
||||
serde = { version = "1", features = ["serde_derive"] }
|
||||
smol_str = "0.2.0"
|
||||
web-time = "1"
|
||||
winit-core = { path = "./winit-core", default-features = false, features = ["std"] }
|
||||
|
||||
@@ -106,6 +106,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
- **Cursor locking**: Locking the cursor inside the window so it cannot move.
|
||||
- **Cursor confining**: Confining the cursor to the window bounds so it cannot leave them.
|
||||
- **Cursor icon**: Changing the cursor icon or hiding the cursor.
|
||||
- **Cursor image**: Changing the cursor to your own image.
|
||||
- **Cursor hittest**: Handle or ignore mouse events for a window.
|
||||
- **Touch events**: Single-touch events.
|
||||
- **Touch pressure**: Touch events contain information about the amount of force being applied.
|
||||
@@ -125,6 +126,11 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* Setting a menu bar
|
||||
* `WS_EX_NOREDIRECTIONBITMAP` support
|
||||
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme
|
||||
* Changing a system-drawn backdrop
|
||||
* Setting the window border color
|
||||
* Setting the title bar background color
|
||||
* Setting the title color
|
||||
* Setting the corner rounding preference
|
||||
|
||||
### macOS
|
||||
* Window activation policy
|
||||
@@ -206,6 +212,7 @@ Legend:
|
||||
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |❌ |
|
||||
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|
||||
|Cursor image |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|
||||
|Cursor hittest |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A** |
|
||||
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |
|
||||
|
||||
19
README.md
19
README.md
@@ -2,11 +2,13 @@
|
||||
|
||||
[](https://crates.io/crates/winit)
|
||||
[](https://docs.rs/winit)
|
||||
[](https://rust-windowing.github.io/winit/winit/index.html)
|
||||
[](https://github.com/rust-windowing/winit/actions)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.29.5"
|
||||
winit = "0.29.10"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -17,10 +19,9 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
|
||||
|
||||
## Contact Us
|
||||
|
||||
Join us in any of these:
|
||||
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).
|
||||
|
||||
[](https://matrix.to/#/#rust-windowing:matrix.org)
|
||||
[](https://web.libera.chat/#winit)
|
||||
The maintainers have a meeting every friday at UTC 14. The meeting notes can be found [here](https://hackmd.io/@winit-meetings).
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -42,7 +43,7 @@ Winit provides the following features, which can be enabled in your `Cargo.toml`
|
||||
|
||||
## MSRV Policy
|
||||
|
||||
This crate's Minimum Supported Rust Version (MSRV) is **1.65**. Changes to
|
||||
This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to
|
||||
the MSRV will be accompanied by a minor version bump.
|
||||
|
||||
As a **tentative** policy, the upper bound of the MSRV is given by the following
|
||||
@@ -74,7 +75,7 @@ same MSRV policy.
|
||||
|
||||
Note that windows don't appear on Wayland until you draw/present to them.
|
||||
|
||||
#### WebAssembly
|
||||
#### Web
|
||||
|
||||
To run the web example: `cargo run-wasm --example web`
|
||||
|
||||
@@ -85,7 +86,7 @@ either [provide Winit with a `<canvas>` element][web with_canvas], or [let Winit
|
||||
create a `<canvas>` element which you can then retrieve][web canvas getter] and
|
||||
insert it into the DOM yourself.
|
||||
|
||||
For the example code using Winit with WebAssembly, check out the [web example]. For
|
||||
For the example code using Winit on Web, check out the [web example]. For
|
||||
information on using Rust on WebAssembly, check out the [Rust and WebAssembly
|
||||
book].
|
||||
|
||||
@@ -150,13 +151,13 @@ class. Your application _must_ specify the base class it needs via a feature fla
|
||||
[agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
|
||||
[Gradle]: https://developer.android.com/studio/build
|
||||
|
||||
For more details, refer to these `android-activity` [example applications](https://github.com/rib/android-activity/tree/main/examples).
|
||||
For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples).
|
||||
|
||||
##### Converting from `ndk-glue` to `android-activity`
|
||||
|
||||
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.29.5", features = [ "android-native-activity" ] }`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.10", features = [ "android-native-activity" ] }`
|
||||
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
|
||||
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ disallowed-methods = [
|
||||
{ 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 = "icrate::AppKit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
|
||||
{ path = "icrate::AppKit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
|
||||
]
|
||||
|
||||
@@ -34,9 +34,10 @@ 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
|
||||
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
|
||||
]
|
||||
skip-tree = []
|
||||
skip-tree = [
|
||||
{ name = "run-wasm", version = "0.1.0" }
|
||||
]
|
||||
|
||||
|
||||
[licenses]
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
//! Fill the window buffer with a solid color.
|
||||
//!
|
||||
//! Launching a window without drawing to it has unpredictable results varying from platform to
|
||||
//! platform. In order to have well-defined examples, this module provides an easy way to
|
||||
//! fill the window buffer with a solid color.
|
||||
//!
|
||||
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
|
||||
//! also be used to fill the window buffer, but they are more complicated to use.
|
||||
|
||||
use winit::window::Window;
|
||||
|
||||
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
|
||||
pub(super) fn fill_window(window: &Window) {
|
||||
use softbuffer::{Context, Surface};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU32;
|
||||
use winit::window::WindowId;
|
||||
|
||||
/// The graphics context used to draw to a window.
|
||||
struct GraphicsContext {
|
||||
/// The global softbuffer context.
|
||||
context: Context,
|
||||
|
||||
/// The hash map of window IDs to surfaces.
|
||||
surfaces: HashMap<WindowId, Surface>,
|
||||
}
|
||||
|
||||
impl GraphicsContext {
|
||||
fn new(w: &Window) -> Self {
|
||||
Self {
|
||||
context: unsafe { Context::new(w) }.expect("Failed to create a softbuffer context"),
|
||||
surfaces: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn surface(&mut self, w: &Window) -> &mut Surface {
|
||||
self.surfaces.entry(w.id()).or_insert_with(|| {
|
||||
unsafe { Surface::new(&self.context, w) }
|
||||
.expect("Failed to create a softbuffer surface")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
// NOTE: You should never do things like that, create context and drop it before
|
||||
// you drop the event loop. We do this for brevity to not blow up examples. We use
|
||||
// ManuallyDrop to prevent destructors from running.
|
||||
//
|
||||
// A static, thread-local map of graphics contexts to open windows.
|
||||
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
|
||||
}
|
||||
|
||||
GC.with(|gc| {
|
||||
let size = window.inner_size();
|
||||
let (Some(width), Some(height)) =
|
||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Either get the last context used or create a new one.
|
||||
let mut gc = gc.borrow_mut();
|
||||
let surface = gc
|
||||
.get_or_insert_with(|| GraphicsContext::new(window))
|
||||
.surface(window);
|
||||
|
||||
// Fill a buffer with a solid color.
|
||||
const DARK_GRAY: u32 = 0xFF181818;
|
||||
|
||||
surface
|
||||
.resize(width, height)
|
||||
.expect("Failed to resize the softbuffer surface");
|
||||
|
||||
let mut buffer = surface
|
||||
.buffer_mut()
|
||||
.expect("Failed to get the softbuffer buffer");
|
||||
buffer.fill(DARK_GRAY);
|
||||
buffer
|
||||
.present()
|
||||
.expect("Failed to present the softbuffer buffer");
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
|
||||
pub(super) fn fill_window(_window: &Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
@@ -2,8 +2,7 @@
|
||||
name = "run-wasm"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
cargo-run-wasm = "0.2.0"
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
//! Contains traits with platform-specific methods in them.
|
||||
//!
|
||||
//! Contains the follow OS-specific modules:
|
||||
//!
|
||||
//! - `android`
|
||||
//! - `ios`
|
||||
//! - `macos`
|
||||
//! - `unix`
|
||||
//! - `windows`
|
||||
//! - `web`
|
||||
//!
|
||||
//! And the following platform-specific modules:
|
||||
//!
|
||||
//! - `run_on_demand` (available on `windows`, `unix`, `macos`, `android`)
|
||||
//! - `pump_events` (available on `windows`, `unix`, `macos`, `android`)
|
||||
//!
|
||||
//! However only the module corresponding to the platform you're compiling to will be available.
|
||||
|
||||
#[cfg(android_platform)]
|
||||
pub mod android;
|
||||
#[cfg(ios_platform)]
|
||||
pub mod ios;
|
||||
#[cfg(macos_platform)]
|
||||
pub mod macos;
|
||||
#[cfg(orbital_platform)]
|
||||
pub mod orbital;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
pub mod startup_notify;
|
||||
#[cfg(wayland_platform)]
|
||||
pub mod wayland;
|
||||
#[cfg(wasm_platform)]
|
||||
pub mod web;
|
||||
#[cfg(windows_platform)]
|
||||
pub mod windows;
|
||||
#[cfg(x11_platform)]
|
||||
pub mod x11;
|
||||
|
||||
#[cfg(any(
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
android_platform,
|
||||
x11_platform,
|
||||
wayland_platform
|
||||
))]
|
||||
pub mod run_on_demand;
|
||||
|
||||
#[cfg(any(
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
android_platform,
|
||||
x11_platform,
|
||||
wayland_platform
|
||||
))]
|
||||
pub mod pump_events;
|
||||
|
||||
pub mod modifier_supplement;
|
||||
pub mod scancode;
|
||||
@@ -1,89 +0,0 @@
|
||||
use std::ffi::CString;
|
||||
use std::iter;
|
||||
|
||||
use x11rb::connection::Connection;
|
||||
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl XConnection {
|
||||
pub fn set_cursor_icon(&self, window: xproto::Window, cursor: Option<CursorIcon>) {
|
||||
let cursor = *self
|
||||
.cursor_cache
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(cursor)
|
||||
.or_insert_with(|| self.get_cursor(cursor));
|
||||
|
||||
self.update_cursor(window, cursor)
|
||||
.expect("Failed to set cursor");
|
||||
}
|
||||
|
||||
fn create_empty_cursor(&self) -> ffi::Cursor {
|
||||
let data = 0;
|
||||
let pixmap = unsafe {
|
||||
let screen = (self.xlib.XDefaultScreen)(self.display);
|
||||
let window = (self.xlib.XRootWindow)(self.display, screen);
|
||||
(self.xlib.XCreateBitmapFromData)(self.display, window, &data, 1, 1)
|
||||
};
|
||||
|
||||
if pixmap == 0 {
|
||||
panic!("failed to allocate pixmap for cursor");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// We don't care about this color, since it only fills bytes
|
||||
// in the pixmap which are not 0 in the mask.
|
||||
let mut dummy_color = MaybeUninit::uninit();
|
||||
let cursor = (self.xlib.XCreatePixmapCursor)(
|
||||
self.display,
|
||||
pixmap,
|
||||
pixmap,
|
||||
dummy_color.as_mut_ptr(),
|
||||
dummy_color.as_mut_ptr(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
(self.xlib.XFreePixmap)(self.display, pixmap);
|
||||
|
||||
cursor
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cursor(&self, cursor: Option<CursorIcon>) -> ffi::Cursor {
|
||||
let cursor = match cursor {
|
||||
Some(cursor) => cursor,
|
||||
None => return self.create_empty_cursor(),
|
||||
};
|
||||
|
||||
let mut xcursor = 0;
|
||||
for &name in iter::once(&cursor.name()).chain(cursor.alt_names().iter()) {
|
||||
let name = CString::new(name).unwrap();
|
||||
xcursor = unsafe {
|
||||
(self.xcursor.XcursorLibraryLoadCursor)(
|
||||
self.display,
|
||||
name.as_ptr() as *const c_char,
|
||||
)
|
||||
};
|
||||
|
||||
if xcursor != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
xcursor
|
||||
}
|
||||
|
||||
fn update_cursor(&self, window: xproto::Window, cursor: ffi::Cursor) -> Result<(), X11Error> {
|
||||
self.xcb_connection()
|
||||
.change_window_attributes(
|
||||
window,
|
||||
&xproto::ChangeWindowAttributesAux::new().cursor(cursor as xproto::Cursor),
|
||||
)?
|
||||
.ignore_error();
|
||||
|
||||
self.xcb_connection().flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::{declare_class, msg_send, mutability, ClassType};
|
||||
|
||||
use super::appkit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
||||
use super::{app_state::AppState, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, ElementState, Event};
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(super) struct WinitApplication;
|
||||
|
||||
unsafe impl ClassType for WinitApplication {
|
||||
#[inherits(NSResponder, NSObject)]
|
||||
type Super = NSApplication;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
const NAME: &'static str = "WinitApplication";
|
||||
}
|
||||
|
||||
unsafe impl WinitApplication {
|
||||
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
|
||||
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
|
||||
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
|
||||
#[method(sendEvent:)]
|
||||
fn send_event(&self, event: &NSEvent) {
|
||||
// For posterity, there are some undocumented event types
|
||||
// (https://github.com/servo/cocoa-rs/issues/155)
|
||||
// but that doesn't really matter here.
|
||||
let event_type = event.type_();
|
||||
let modifier_flags = event.modifierFlags();
|
||||
if event_type == NSEventType::NSKeyUp
|
||||
&& modifier_flags.contains(NSEventModifierFlags::NSCommandKeyMask)
|
||||
{
|
||||
if let Some(key_window) = self.keyWindow() {
|
||||
unsafe { key_window.sendEvent(event) };
|
||||
}
|
||||
} else {
|
||||
maybe_dispatch_device_event(event);
|
||||
unsafe { msg_send![super(self), sendEvent: event] }
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
fn maybe_dispatch_device_event(event: &NSEvent) {
|
||||
let event_type = event.type_();
|
||||
match event_type {
|
||||
NSEventType::NSMouseMoved
|
||||
| NSEventType::NSLeftMouseDragged
|
||||
| NSEventType::NSOtherMouseDragged
|
||||
| NSEventType::NSRightMouseDragged => {
|
||||
let delta_x = event.deltaX() as f64;
|
||||
let delta_y = event.deltaY() as f64;
|
||||
|
||||
if delta_x != 0.0 {
|
||||
queue_device_event(DeviceEvent::Motion {
|
||||
axis: 0,
|
||||
value: delta_x,
|
||||
});
|
||||
}
|
||||
|
||||
if delta_y != 0.0 {
|
||||
queue_device_event(DeviceEvent::Motion {
|
||||
axis: 1,
|
||||
value: delta_y,
|
||||
})
|
||||
}
|
||||
|
||||
if delta_x != 0.0 || delta_y != 0.0 {
|
||||
queue_device_event(DeviceEvent::MouseMotion {
|
||||
delta: (delta_x, delta_y),
|
||||
});
|
||||
}
|
||||
}
|
||||
NSEventType::NSLeftMouseDown
|
||||
| NSEventType::NSRightMouseDown
|
||||
| NSEventType::NSOtherMouseDown => {
|
||||
queue_device_event(DeviceEvent::Button {
|
||||
button: event.buttonNumber() as u32,
|
||||
state: ElementState::Pressed,
|
||||
});
|
||||
}
|
||||
NSEventType::NSLeftMouseUp | NSEventType::NSRightMouseUp | NSEventType::NSOtherMouseUp => {
|
||||
queue_device_event(DeviceEvent::Button {
|
||||
button: event.buttonNumber() as u32,
|
||||
state: ElementState::Released,
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_device_event(event: DeviceEvent) {
|
||||
let event = Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
};
|
||||
AppState::queue_event(event);
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::declare::{IvarBool, IvarEncode};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType};
|
||||
|
||||
use super::app_state::AppState;
|
||||
use super::appkit::NSApplicationActivationPolicy;
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ApplicationDelegate {
|
||||
activation_policy: IvarEncode<NSApplicationActivationPolicy, "_activation_policy">,
|
||||
default_menu: IvarBool<"_default_menu">,
|
||||
activate_ignoring_other_apps: IvarBool<"_activate_ignoring_other_apps">,
|
||||
}
|
||||
|
||||
mod ivars;
|
||||
|
||||
unsafe impl ClassType for ApplicationDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
const NAME: &'static str = "WinitApplicationDelegate";
|
||||
}
|
||||
|
||||
unsafe impl ApplicationDelegate {
|
||||
#[method(initWithActivationPolicy:defaultMenu:activateIgnoringOtherApps:)]
|
||||
unsafe fn init(
|
||||
this: *mut Self,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) -> Option<NonNull<Self>> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![super(this), init] };
|
||||
this.map(|this| {
|
||||
*this.activation_policy = activation_policy;
|
||||
*this.default_menu = default_menu;
|
||||
*this.activate_ignoring_other_apps = activate_ignoring_other_apps;
|
||||
NonNull::from(this)
|
||||
})
|
||||
}
|
||||
|
||||
#[method(applicationDidFinishLaunching:)]
|
||||
fn did_finish_launching(&self, _sender: Option<&AnyObject>) {
|
||||
trace_scope!("applicationDidFinishLaunching:");
|
||||
AppState::launched(
|
||||
*self.activation_policy,
|
||||
*self.default_menu,
|
||||
*self.activate_ignoring_other_apps,
|
||||
);
|
||||
}
|
||||
|
||||
#[method(applicationWillTerminate:)]
|
||||
fn will_terminate(&self, _sender: Option<&AnyObject>) {
|
||||
trace_scope!("applicationWillTerminate:");
|
||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||
AppState::internal_exit();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl ApplicationDelegate {
|
||||
pub(super) fn new(
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) -> Id<Self> {
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
Self::alloc(),
|
||||
initWithActivationPolicy: activation_policy,
|
||||
defaultMenu: default_menu,
|
||||
activateIgnoringOtherApps: activate_ignoring_other_apps,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,698 +0,0 @@
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
collections::VecDeque,
|
||||
fmt::{self, Debug},
|
||||
mem,
|
||||
rc::{Rc, Weak},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc, Arc, Mutex, MutexGuard,
|
||||
},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp};
|
||||
use icrate::Foundation::{is_main_thread, NSSize};
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent};
|
||||
use super::{
|
||||
event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never, window::WinitWindow,
|
||||
};
|
||||
use crate::{
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
|
||||
window::WindowId,
|
||||
};
|
||||
|
||||
static HANDLER: Lazy<Handler> = Lazy::new(Default::default);
|
||||
|
||||
impl<Never> Event<Never> {
|
||||
fn userify<T: 'static>(self) -> Event<T> {
|
||||
self.map_nonuser_event()
|
||||
// `Never` can't be constructed, so the `UserEvent` variant can't
|
||||
// be present here.
|
||||
.unwrap_or_else(|_| unreachable!())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EventHandler: Debug {
|
||||
// Not sure probably it should accept Event<'static, Never>
|
||||
fn handle_nonuser_event(&mut self, event: Event<Never>);
|
||||
fn handle_user_events(&mut self);
|
||||
}
|
||||
|
||||
pub(crate) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>;
|
||||
|
||||
struct EventLoopHandler<T: 'static> {
|
||||
callback: Weak<Callback<T>>,
|
||||
window_target: Rc<RootWindowTarget<T>>,
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
}
|
||||
|
||||
impl<T> EventLoopHandler<T> {
|
||||
fn with_callback<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnOnce(&mut EventLoopHandler<T>, RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>)>),
|
||||
{
|
||||
// The `NSApp` and our `HANDLER` are global state and so it's possible that
|
||||
// we could get a delegate callback after the application has exit an
|
||||
// `EventLoop`. If the loop has been exit then our weak `self.callback`
|
||||
// will fail to upgrade.
|
||||
//
|
||||
// We don't want to panic or output any verbose logging if we fail to
|
||||
// upgrade the weak reference since it might be valid that the application
|
||||
// re-starts the `NSApp` after exiting a Winit `EventLoop`
|
||||
if let Some(callback) = self.callback.upgrade() {
|
||||
let callback = callback.borrow_mut();
|
||||
(f)(self, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for EventLoopHandler<T> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter
|
||||
.debug_struct("EventLoopHandler")
|
||||
.field("window_target", &self.window_target)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventHandler for EventLoopHandler<T> {
|
||||
fn handle_nonuser_event(&mut self, event: Event<Never>) {
|
||||
self.with_callback(|this, mut callback| {
|
||||
(callback)(event.userify(), &this.window_target);
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_user_events(&mut self) {
|
||||
self.with_callback(|this, mut callback| {
|
||||
for event in this.receiver.try_iter() {
|
||||
(callback)(Event::UserEvent(event), &this.window_target);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum EventWrapper {
|
||||
StaticEvent(Event<Never>),
|
||||
ScaleFactorChanged {
|
||||
window: Id<WinitWindow>,
|
||||
suggested_size: PhysicalSize<u32>,
|
||||
scale_factor: f64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Handler {
|
||||
stop_app_on_launch: AtomicBool,
|
||||
stop_app_before_wait: AtomicBool,
|
||||
stop_app_after_wait: AtomicBool,
|
||||
stop_app_on_redraw: AtomicBool,
|
||||
launched: AtomicBool,
|
||||
running: AtomicBool,
|
||||
in_callback: AtomicBool,
|
||||
control_flow: Mutex<ControlFlow>,
|
||||
exit: AtomicBool,
|
||||
start_time: Mutex<Option<Instant>>,
|
||||
callback: Mutex<Option<Box<dyn EventHandler>>>,
|
||||
pending_events: Mutex<VecDeque<EventWrapper>>,
|
||||
pending_redraw: Mutex<Vec<WindowId>>,
|
||||
wait_timeout: Mutex<Option<Instant>>,
|
||||
waker: Mutex<EventLoopWaker>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Handler {}
|
||||
unsafe impl Sync for Handler {}
|
||||
|
||||
impl Handler {
|
||||
fn events(&self) -> MutexGuard<'_, VecDeque<EventWrapper>> {
|
||||
self.pending_events.lock().unwrap()
|
||||
}
|
||||
|
||||
fn redraw(&self) -> MutexGuard<'_, Vec<WindowId>> {
|
||||
self.pending_redraw.lock().unwrap()
|
||||
}
|
||||
|
||||
fn waker(&self) -> MutexGuard<'_, EventLoopWaker> {
|
||||
self.waker.lock().unwrap()
|
||||
}
|
||||
|
||||
/// `true` after `ApplicationDelegate::applicationDidFinishLaunching` called
|
||||
///
|
||||
/// NB: This is global / `NSApp` state and since the app will only be launched
|
||||
/// once but an `EventLoop` may be run more than once then only the first
|
||||
/// `EventLoop` will observe the `NSApp` before it is launched.
|
||||
fn is_launched(&self) -> bool {
|
||||
self.launched.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
/// Set via `ApplicationDelegate::applicationDidFinishLaunching`
|
||||
fn set_launched(&self) {
|
||||
self.launched.store(true, Ordering::Release);
|
||||
}
|
||||
|
||||
/// `true` if an `EventLoop` is currently running
|
||||
///
|
||||
/// NB: This is global / `NSApp` state and may persist beyond the lifetime of
|
||||
/// a running `EventLoop`.
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
fn is_running(&self) -> bool {
|
||||
self.running.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Set when an `EventLoop` starts running, after the `NSApp` is launched
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
fn set_running(&self) {
|
||||
self.running.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits
|
||||
///
|
||||
/// Since an `EventLoop` may be run more than once we need make sure to reset the
|
||||
/// `control_flow` state back to `Poll` each time the loop exits.
|
||||
///
|
||||
/// Note: that if the `NSApp` has been launched then that state is preserved, and we won't
|
||||
/// need to re-launch the app if subsequent EventLoops are run.
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
fn internal_exit(&self) {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interiour mutability
|
||||
//
|
||||
// XXX: As an aside; having each individual bit of state for `Handler` be atomic or wrapped in a
|
||||
// `Mutex` for the sake of interior mutability seems a bit odd, and also a potential foot
|
||||
// gun in case the state is unwittingly accessed across threads because the fine-grained locking
|
||||
// wouldn't ensure that there's interior consistency.
|
||||
//
|
||||
// Maybe the whole thing should just be put in a static `Mutex<>` to make it clear
|
||||
// the we can mutate more than one peice of state while maintaining consistency. (though it
|
||||
// looks like there have been recuring re-entrancy issues with callback handling that might
|
||||
// make that awkward)
|
||||
self.running.store(false, Ordering::Relaxed);
|
||||
self.set_stop_app_on_redraw_requested(false);
|
||||
self.set_stop_app_before_wait(false);
|
||||
self.set_stop_app_after_wait(false);
|
||||
self.set_wait_timeout(None);
|
||||
}
|
||||
|
||||
pub fn exit(&self) {
|
||||
self.exit.store(true, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn exiting(&self) -> bool {
|
||||
self.exit.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn request_stop_app_on_launch(&self) {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interior mutability
|
||||
self.stop_app_on_launch.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn should_stop_app_on_launch(&self) -> bool {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interior mutability
|
||||
self.stop_app_on_launch.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_stop_app_before_wait(&self, stop_before_wait: bool) {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interior mutability
|
||||
self.stop_app_before_wait
|
||||
.store(stop_before_wait, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn should_stop_app_before_wait(&self) -> bool {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interior mutability
|
||||
self.stop_app_before_wait.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_stop_app_after_wait(&self, stop_after_wait: bool) {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interior mutability
|
||||
self.stop_app_after_wait
|
||||
.store(stop_after_wait, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn set_wait_timeout(&self, new_timeout: Option<Instant>) {
|
||||
let mut timeout = self.wait_timeout.lock().unwrap();
|
||||
*timeout = new_timeout;
|
||||
}
|
||||
|
||||
pub fn wait_timeout(&self) -> Option<Instant> {
|
||||
*self.wait_timeout.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn should_stop_app_after_wait(&self) -> bool {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interior mutability
|
||||
self.stop_app_after_wait.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_stop_app_on_redraw_requested(&self, stop_on_redraw: bool) {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interior mutability
|
||||
self.stop_app_on_redraw
|
||||
.store(stop_on_redraw, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn should_stop_app_on_redraw_requested(&self) -> bool {
|
||||
// Relaxed ordering because we don't actually have multiple threads involved, we just want
|
||||
// interior mutability
|
||||
self.stop_app_on_redraw.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn set_control_flow(&self, new_control_flow: ControlFlow) {
|
||||
*self.control_flow.lock().unwrap() = new_control_flow
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
*self.control_flow.lock().unwrap()
|
||||
}
|
||||
|
||||
fn get_start_time(&self) -> Option<Instant> {
|
||||
*self.start_time.lock().unwrap()
|
||||
}
|
||||
|
||||
fn update_start_time(&self) {
|
||||
*self.start_time.lock().unwrap() = Some(Instant::now());
|
||||
}
|
||||
|
||||
fn take_events(&self) -> VecDeque<EventWrapper> {
|
||||
mem::take(&mut *self.events())
|
||||
}
|
||||
|
||||
fn should_redraw(&self) -> Vec<WindowId> {
|
||||
mem::take(&mut *self.redraw())
|
||||
}
|
||||
|
||||
fn get_in_callback(&self) -> bool {
|
||||
self.in_callback.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
fn set_in_callback(&self, in_callback: bool) {
|
||||
self.in_callback.store(in_callback, Ordering::Release);
|
||||
}
|
||||
|
||||
fn have_callback(&self) -> bool {
|
||||
self.callback.lock().unwrap().is_some()
|
||||
}
|
||||
|
||||
fn handle_nonuser_event(&self, event: Event<Never>) {
|
||||
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||
callback.handle_nonuser_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_user_events(&self) {
|
||||
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||
callback.handle_user_events();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_scale_factor_changed_event(
|
||||
&self,
|
||||
window: &WinitWindow,
|
||||
suggested_size: PhysicalSize<u32>,
|
||||
scale_factor: f64,
|
||||
) {
|
||||
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
let scale_factor_changed_event = Event::WindowEvent {
|
||||
window_id: WindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
},
|
||||
};
|
||||
|
||||
callback.handle_nonuser_event(scale_factor_changed_event);
|
||||
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
|
||||
let resized_event = Event::WindowEvent {
|
||||
window_id: WindowId(window.id()),
|
||||
event: WindowEvent::Resized(physical_size),
|
||||
};
|
||||
callback.handle_nonuser_event(resized_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum AppState {}
|
||||
|
||||
impl AppState {
|
||||
/// Associate the application's event callback with the (global static) Handler state
|
||||
///
|
||||
/// # Safety
|
||||
/// This is ignoring the lifetime of the application callback (which may not be 'static)
|
||||
/// and can lead to undefined behaviour if the callback is not cleared before the end of
|
||||
/// its real lifetime.
|
||||
///
|
||||
/// All public APIs that take an event callback (`run`, `run_on_demand`,
|
||||
/// `pump_events`) _must_ pair a call to `set_callback` with
|
||||
/// a call to `clear_callback` before returning to avoid undefined behaviour.
|
||||
pub unsafe fn set_callback<T>(
|
||||
callback: Weak<Callback<T>>,
|
||||
window_target: Rc<RootWindowTarget<T>>,
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
) {
|
||||
*HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler {
|
||||
callback,
|
||||
window_target,
|
||||
receiver,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn clear_callback() {
|
||||
HANDLER.callback.lock().unwrap().take();
|
||||
}
|
||||
|
||||
pub fn is_launched() -> bool {
|
||||
HANDLER.is_launched()
|
||||
}
|
||||
|
||||
pub fn is_running() -> bool {
|
||||
HANDLER.is_running()
|
||||
}
|
||||
|
||||
// If `pump_events` is called to progress the event loop then we bootstrap the event
|
||||
// loop via `[NSApp run]` but will use `CFRunLoopRunInMode` for subsequent calls to
|
||||
// `pump_events`
|
||||
pub fn request_stop_on_launch() {
|
||||
HANDLER.request_stop_app_on_launch();
|
||||
}
|
||||
|
||||
pub fn set_stop_app_before_wait(stop_before_wait: bool) {
|
||||
HANDLER.set_stop_app_before_wait(stop_before_wait);
|
||||
}
|
||||
|
||||
pub fn set_stop_app_after_wait(stop_after_wait: bool) {
|
||||
HANDLER.set_stop_app_after_wait(stop_after_wait);
|
||||
}
|
||||
|
||||
pub fn set_wait_timeout(timeout: Option<Instant>) {
|
||||
HANDLER.set_wait_timeout(timeout);
|
||||
}
|
||||
|
||||
pub fn set_stop_app_on_redraw_requested(stop_on_redraw: bool) {
|
||||
HANDLER.set_stop_app_on_redraw_requested(stop_on_redraw);
|
||||
}
|
||||
|
||||
pub fn set_control_flow(control_flow: ControlFlow) {
|
||||
HANDLER.set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
pub fn control_flow() -> ControlFlow {
|
||||
HANDLER.control_flow()
|
||||
}
|
||||
|
||||
pub fn internal_exit() {
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_nonuser_event(Event::LoopExiting);
|
||||
HANDLER.set_in_callback(false);
|
||||
HANDLER.internal_exit();
|
||||
Self::clear_callback();
|
||||
}
|
||||
|
||||
pub fn exit() {
|
||||
HANDLER.exit()
|
||||
}
|
||||
|
||||
pub fn exiting() -> bool {
|
||||
HANDLER.exiting()
|
||||
}
|
||||
|
||||
pub fn dispatch_init_events() {
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init));
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though macOS
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
HANDLER.handle_nonuser_event(Event::Resumed);
|
||||
HANDLER.set_in_callback(false);
|
||||
}
|
||||
|
||||
pub fn start_running() {
|
||||
debug_assert!(HANDLER.is_launched());
|
||||
|
||||
HANDLER.set_running();
|
||||
Self::dispatch_init_events()
|
||||
}
|
||||
|
||||
pub fn launched(
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
create_default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) {
|
||||
let app = NSApp();
|
||||
// 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(activation_policy);
|
||||
|
||||
window_activation_hack(&app);
|
||||
app.activateIgnoringOtherApps(activate_ignoring_other_apps);
|
||||
|
||||
HANDLER.set_launched();
|
||||
HANDLER.waker().start();
|
||||
if create_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();
|
||||
}
|
||||
|
||||
Self::start_running();
|
||||
|
||||
// If the `NSApp` is being launched via `EventLoop::pump_events()` then we'll
|
||||
// want to stop the app once it is launched (and return to the external loop)
|
||||
//
|
||||
// In this case we still want to consider Winit's `EventLoop` to be "running",
|
||||
// so we call `start_running()` above.
|
||||
if HANDLER.should_stop_app_on_launch() {
|
||||
// Note: the original idea had been to only stop the underlying `RunLoop`
|
||||
// for the app but that didn't work as expected (`[NSApp run]` effectively
|
||||
// ignored the attempt to stop the RunLoop and re-started it.). So we
|
||||
// return from `pump_events` by stopping the `NSApp`
|
||||
Self::stop();
|
||||
}
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver after finishing waiting for new events
|
||||
pub fn wakeup(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 callback due to https://github.com/rust-windowing/winit/issues/1779
|
||||
if panic_info.is_panicking()
|
||||
|| HANDLER.get_in_callback()
|
||||
|| !HANDLER.have_callback()
|
||||
|| !HANDLER.is_running()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if HANDLER.should_stop_app_after_wait() {
|
||||
Self::stop();
|
||||
}
|
||||
|
||||
let start = HANDLER.get_start_time().unwrap();
|
||||
let cause = match HANDLER.control_flow() {
|
||||
ControlFlow::Poll => StartCause::Poll,
|
||||
ControlFlow::Wait => StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: None,
|
||||
},
|
||||
ControlFlow::WaitUntil(requested_resume) => {
|
||||
if Instant::now() >= requested_resume {
|
||||
StartCause::ResumeTimeReached {
|
||||
start,
|
||||
requested_resume,
|
||||
}
|
||||
} else {
|
||||
StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: Some(requested_resume),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_nonuser_event(Event::NewEvents(cause));
|
||||
HANDLER.set_in_callback(false);
|
||||
}
|
||||
|
||||
// This is called from multiple threads at present
|
||||
pub fn queue_redraw(window_id: WindowId) {
|
||||
let mut pending_redraw = HANDLER.redraw();
|
||||
if !pending_redraw.contains(&window_id) {
|
||||
pending_redraw.push(window_id);
|
||||
}
|
||||
unsafe {
|
||||
let rl = CFRunLoopGetMain();
|
||||
CFRunLoopWakeUp(rl);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_redraw(window_id: WindowId) {
|
||||
// Redraw request might come out of order from the OS.
|
||||
// -> Don't go back into the callback when our callstack originates from there
|
||||
if !HANDLER.in_callback.swap(true, Ordering::AcqRel) {
|
||||
HANDLER.handle_nonuser_event(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
HANDLER.set_in_callback(false);
|
||||
|
||||
// `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 HANDLER.should_stop_app_on_redraw_requested() {
|
||||
AppState::stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_event(event: Event<Never>) {
|
||||
if !is_main_thread() {
|
||||
panic!("Event queued from different thread: {event:#?}");
|
||||
}
|
||||
HANDLER.events().push_back(EventWrapper::StaticEvent(event));
|
||||
}
|
||||
|
||||
pub fn queue_static_scale_factor_changed_event(
|
||||
window: Id<WinitWindow>,
|
||||
suggested_size: PhysicalSize<u32>,
|
||||
scale_factor: f64,
|
||||
) {
|
||||
HANDLER
|
||||
.events()
|
||||
.push_back(EventWrapper::ScaleFactorChanged {
|
||||
window,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn stop() {
|
||||
let app = NSApp();
|
||||
autoreleasepool(|_| {
|
||||
app.stop(None);
|
||||
// To stop event loop immediately, we need to post some event here.
|
||||
app.postEvent_atStart(&NSEvent::dummy(), true);
|
||||
});
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver before waiting for new events
|
||||
pub fn cleared(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 callback due to https://github.com/rust-windowing/winit/issues/1779
|
||||
// XXX: how does it make sense that `get_in_callback()` can ever return `true` here if we're
|
||||
// about to return to the `CFRunLoop` to poll for new events?
|
||||
if panic_info.is_panicking()
|
||||
|| HANDLER.get_in_callback()
|
||||
|| !HANDLER.have_callback()
|
||||
|| !HANDLER.is_running()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_user_events();
|
||||
for event in HANDLER.take_events() {
|
||||
match event {
|
||||
EventWrapper::StaticEvent(event) => {
|
||||
HANDLER.handle_nonuser_event(event);
|
||||
}
|
||||
EventWrapper::ScaleFactorChanged {
|
||||
window,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
} => {
|
||||
HANDLER.handle_scale_factor_changed_event(
|
||||
&window,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for window_id in HANDLER.should_redraw() {
|
||||
HANDLER.handle_nonuser_event(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
}
|
||||
|
||||
HANDLER.handle_nonuser_event(Event::AboutToWait);
|
||||
HANDLER.set_in_callback(false);
|
||||
|
||||
if HANDLER.exiting() {
|
||||
Self::stop();
|
||||
}
|
||||
|
||||
if HANDLER.should_stop_app_before_wait() {
|
||||
Self::stop();
|
||||
}
|
||||
HANDLER.update_start_time();
|
||||
let wait_timeout = HANDLER.wait_timeout(); // configured by pump_events
|
||||
let app_timeout = match HANDLER.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Instant::now()),
|
||||
ControlFlow::WaitUntil(instant) => Some(instant),
|
||||
};
|
||||
HANDLER
|
||||
.waker()
|
||||
.start_at(min_timeout(wait_timeout, app_timeout));
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 visiblity status
|
||||
// TODO: Also filter on the type/"level" of the window, and maybe other things?
|
||||
if window.isVisible() {
|
||||
trace!("Activating visible window");
|
||||
window.makeKeyAndOrderFront(None);
|
||||
} else {
|
||||
trace!("Skipping activating invisible window");
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
use icrate::Foundation::{NSArray, NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSAppearance;
|
||||
|
||||
unsafe impl ClassType for NSAppearance {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
type NSAppearanceName = NSString;
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSAppearance {
|
||||
#[method_id(appearanceNamed:)]
|
||||
pub fn appearanceNamed(name: &NSAppearanceName) -> Id<Self>;
|
||||
|
||||
#[method_id(bestMatchFromAppearancesWithNames:)]
|
||||
pub fn bestMatchFromAppearancesWithNames(
|
||||
&self,
|
||||
appearances: &NSArray<NSAppearanceName>,
|
||||
) -> Id<NSAppearanceName>;
|
||||
}
|
||||
);
|
||||
@@ -1,140 +0,0 @@
|
||||
use icrate::Foundation::{MainThreadMarker, NSArray, NSInteger, NSObject, NSUInteger};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||
use objc2::{Encode, Encoding};
|
||||
|
||||
use super::{NSAppearance, NSEvent, NSMenu, NSResponder, NSWindow};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSApplication;
|
||||
|
||||
unsafe impl ClassType for NSApplication {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
pub(crate) fn NSApp() -> Id<NSApplication> {
|
||||
// TODO: Only allow access from main thread
|
||||
NSApplication::shared(unsafe { MainThreadMarker::new_unchecked() })
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSApplication {
|
||||
/// This can only be called on the main thread since it may initialize
|
||||
/// the application and since it's parameters may be changed by the main
|
||||
/// thread at any time (hence it is only safe to access on the main thread).
|
||||
pub fn shared(_mtm: MainThreadMarker) -> Id<Self> {
|
||||
let app: Option<_> = unsafe { msg_send_id![Self::class(), sharedApplication] };
|
||||
// SAFETY: `sharedApplication` always initializes the app if it isn't already
|
||||
unsafe { app.unwrap_unchecked() }
|
||||
}
|
||||
|
||||
#[method_id(currentEvent)]
|
||||
pub fn currentEvent(&self) -> Option<Id<NSEvent>>;
|
||||
|
||||
#[method(postEvent:atStart:)]
|
||||
pub fn postEvent_atStart(&self, event: &NSEvent, front_of_queue: bool);
|
||||
|
||||
#[method(presentationOptions)]
|
||||
pub fn presentationOptions(&self) -> NSApplicationPresentationOptions;
|
||||
|
||||
#[method_id(windows)]
|
||||
pub fn windows(&self) -> Id<NSArray<NSWindow>>;
|
||||
|
||||
#[method_id(keyWindow)]
|
||||
pub fn keyWindow(&self) -> Option<Id<NSWindow>>;
|
||||
|
||||
// TODO: NSApplicationDelegate
|
||||
#[method(setDelegate:)]
|
||||
pub fn setDelegate(&self, delegate: &AnyObject);
|
||||
|
||||
#[method(setPresentationOptions:)]
|
||||
pub fn setPresentationOptions(&self, options: NSApplicationPresentationOptions);
|
||||
|
||||
#[method(hide:)]
|
||||
pub fn hide(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(orderFrontCharacterPalette:)]
|
||||
#[allow(dead_code)]
|
||||
pub fn orderFrontCharacterPalette(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(hideOtherApplications:)]
|
||||
pub fn hideOtherApplications(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(stop:)]
|
||||
pub fn stop(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(activateIgnoringOtherApps:)]
|
||||
pub fn activateIgnoringOtherApps(&self, ignore: bool);
|
||||
|
||||
#[method(requestUserAttention:)]
|
||||
pub fn requestUserAttention(&self, type_: NSRequestUserAttentionType) -> NSInteger;
|
||||
|
||||
#[method(setActivationPolicy:)]
|
||||
pub fn setActivationPolicy(&self, policy: NSApplicationActivationPolicy) -> bool;
|
||||
|
||||
#[method(setMainMenu:)]
|
||||
pub fn setMainMenu(&self, menu: &NSMenu);
|
||||
|
||||
#[method_id(effectiveAppearance)]
|
||||
pub fn effectiveAppearance(&self) -> Id<NSAppearance>;
|
||||
|
||||
#[method(setAppearance:)]
|
||||
pub fn setAppearance(&self, appearance: Option<&NSAppearance>);
|
||||
|
||||
#[method(run)]
|
||||
pub unsafe fn run(&self);
|
||||
}
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSApplicationActivationPolicy {
|
||||
NSApplicationActivationPolicyRegular = 0,
|
||||
NSApplicationActivationPolicyAccessory = 1,
|
||||
NSApplicationActivationPolicyProhibited = 2,
|
||||
NSApplicationActivationPolicyERROR = -1,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSApplicationActivationPolicy {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NSApplicationPresentationOptions: NSUInteger {
|
||||
const NSApplicationPresentationDefault = 0;
|
||||
const NSApplicationPresentationAutoHideDock = 1 << 0;
|
||||
const NSApplicationPresentationHideDock = 1 << 1;
|
||||
const NSApplicationPresentationAutoHideMenuBar = 1 << 2;
|
||||
const NSApplicationPresentationHideMenuBar = 1 << 3;
|
||||
const NSApplicationPresentationDisableAppleMenu = 1 << 4;
|
||||
const NSApplicationPresentationDisableProcessSwitching = 1 << 5;
|
||||
const NSApplicationPresentationDisableForceQuit = 1 << 6;
|
||||
const NSApplicationPresentationDisableSessionTermination = 1 << 7;
|
||||
const NSApplicationPresentationDisableHideApplication = 1 << 8;
|
||||
const NSApplicationPresentationDisableMenuBarTransparency = 1 << 9;
|
||||
const NSApplicationPresentationFullScreen = 1 << 10;
|
||||
const NSApplicationPresentationAutoHideToolbar = 1 << 11;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSApplicationPresentationOptions {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSRequestUserAttentionType {
|
||||
NSCriticalRequest = 0,
|
||||
NSInformationalRequest = 10,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSRequestUserAttentionType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::{extern_class, mutability, ClassType};
|
||||
|
||||
use super::{NSControl, NSResponder, NSView};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSButton;
|
||||
|
||||
unsafe impl ClassType for NSButton {
|
||||
#[inherits(NSView, NSResponder, NSObject)]
|
||||
type Super = NSControl;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
@@ -1,28 +0,0 @@
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// An object that stores color data and sometimes opacity (alpha value).
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nscolor?language=objc>
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSColor;
|
||||
|
||||
unsafe impl ClassType for NSColor {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Documentation clearly states:
|
||||
// > Color objects are immutable and thread-safe
|
||||
unsafe impl Send for NSColor {}
|
||||
unsafe impl Sync for NSColor {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSColor {
|
||||
#[method_id(clearColor)]
|
||||
pub fn clear() -> Id<Self>;
|
||||
}
|
||||
);
|
||||
@@ -1,25 +0,0 @@
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::{NSResponder, NSView};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSControl;
|
||||
|
||||
unsafe impl ClassType for NSControl {
|
||||
#[inherits(NSResponder, NSObject)]
|
||||
type Super = NSView;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSControl {
|
||||
#[method(setEnabled:)]
|
||||
pub fn setEnabled(&self, enabled: bool);
|
||||
|
||||
#[method(isEnabled)]
|
||||
pub fn isEnabled(&self) -> bool;
|
||||
}
|
||||
);
|
||||
@@ -1,241 +0,0 @@
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use icrate::ns_string;
|
||||
use icrate::Foundation::{
|
||||
NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSString,
|
||||
};
|
||||
use objc2::rc::{DefaultId, Id};
|
||||
use objc2::runtime::Sel;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, sel, ClassType};
|
||||
|
||||
use super::NSImage;
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/appkit/nscursor?language=objc>
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSCursor;
|
||||
|
||||
unsafe impl ClassType for NSCursor {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: NSCursor is immutable, stated here:
|
||||
// https://developer.apple.com/documentation/appkit/nscursor/1527062-image?language=objc
|
||||
unsafe impl Send for NSCursor {}
|
||||
unsafe impl Sync for NSCursor {}
|
||||
|
||||
macro_rules! def_cursor {
|
||||
{$(
|
||||
$(#[$($m:meta)*])*
|
||||
pub fn $name:ident();
|
||||
)*} => {$(
|
||||
$(#[$($m)*])*
|
||||
pub fn $name() -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::class(), $name] }
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
macro_rules! def_undocumented_cursor {
|
||||
{$(
|
||||
$(#[$($m:meta)*])*
|
||||
pub fn $name:ident();
|
||||
)*} => {$(
|
||||
$(#[$($m)*])*
|
||||
pub fn $name() -> Id<Self> {
|
||||
unsafe { Self::from_selector(sel!($name)).unwrap_or_else(|| Default::default()) }
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
/// Documented cursors
|
||||
unsafe impl NSCursor {
|
||||
def_cursor!(
|
||||
pub fn arrowCursor();
|
||||
pub fn pointingHandCursor();
|
||||
pub fn openHandCursor();
|
||||
pub fn closedHandCursor();
|
||||
pub fn IBeamCursor();
|
||||
pub fn IBeamCursorForVerticalLayout();
|
||||
pub fn dragCopyCursor();
|
||||
pub fn dragLinkCursor();
|
||||
pub fn operationNotAllowedCursor();
|
||||
pub fn contextualMenuCursor();
|
||||
pub fn crosshairCursor();
|
||||
pub fn resizeRightCursor();
|
||||
pub fn resizeUpCursor();
|
||||
pub fn resizeLeftCursor();
|
||||
pub fn resizeDownCursor();
|
||||
pub fn resizeLeftRightCursor();
|
||||
pub fn resizeUpDownCursor();
|
||||
);
|
||||
|
||||
// Creating cursors should be thread-safe, though using them for anything probably isn't.
|
||||
pub fn new(image: &NSImage, hotSpot: NSPoint) -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::alloc(), initWithImage: image, hotSpot: hotSpot] }
|
||||
}
|
||||
|
||||
pub fn invisible() -> Id<Self> {
|
||||
// 16x16 GIF data for invisible cursor
|
||||
// You can reproduce this via ImageMagick.
|
||||
// $ convert -size 16x16 xc:none cursor.gif
|
||||
static CURSOR_BYTES: &[u8] = &[
|
||||
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x84, 0x8F, 0xA9,
|
||||
0xCB, 0xED, 0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B,
|
||||
];
|
||||
|
||||
static CURSOR: Lazy<Id<NSCursor>> = Lazy::new(|| {
|
||||
// TODO: Consider using `dataWithBytesNoCopy:`
|
||||
let data = NSData::with_bytes(CURSOR_BYTES);
|
||||
let image = NSImage::new_with_data(&data);
|
||||
NSCursor::new(&image, NSPoint::new(0.0, 0.0))
|
||||
});
|
||||
|
||||
CURSOR.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Undocumented cursors
|
||||
unsafe impl NSCursor {
|
||||
#[method(respondsToSelector:)]
|
||||
fn class_responds_to(sel: Sel) -> bool;
|
||||
|
||||
#[method_id(performSelector:)]
|
||||
unsafe fn from_selector_unchecked(sel: Sel) -> Id<Self>;
|
||||
|
||||
unsafe fn from_selector(sel: Sel) -> Option<Id<Self>> {
|
||||
if Self::class_responds_to(sel) {
|
||||
Some(unsafe { Self::from_selector_unchecked(sel) })
|
||||
} else {
|
||||
warn!("Cursor `{:?}` appears to be invalid", sel);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def_undocumented_cursor!(
|
||||
// Undocumented cursors: https://stackoverflow.com/a/46635398/5435443
|
||||
pub fn _helpCursor();
|
||||
pub fn _zoomInCursor();
|
||||
pub fn _zoomOutCursor();
|
||||
pub fn _windowResizeNorthEastCursor();
|
||||
pub fn _windowResizeNorthWestCursor();
|
||||
pub fn _windowResizeSouthEastCursor();
|
||||
pub fn _windowResizeSouthWestCursor();
|
||||
pub fn _windowResizeNorthEastSouthWestCursor();
|
||||
pub fn _windowResizeNorthWestSouthEastCursor();
|
||||
|
||||
// While these two are available, the former just loads a white arrow,
|
||||
// and the latter loads an ugly deflated beachball!
|
||||
// pub fn _moveCursor();
|
||||
// pub fn _waitCursor();
|
||||
|
||||
// An even more undocumented cursor...
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=522349
|
||||
pub fn busyButClickableCursor();
|
||||
);
|
||||
}
|
||||
|
||||
/// Webkit cursors
|
||||
unsafe impl NSCursor {
|
||||
// Note that loading `busybutclickable` with this code won't animate
|
||||
// the frames; instead you'll just get them all in a column.
|
||||
unsafe fn load_webkit_cursor(name: &NSString) -> Id<Self> {
|
||||
// Snatch a cursor from WebKit; They fit the style of the native
|
||||
// cursors, and will seem completely standard to macOS users.
|
||||
//
|
||||
// https://stackoverflow.com/a/21786835/5435443
|
||||
let root = ns_string!("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors");
|
||||
let cursor_path = root.stringByAppendingPathComponent(name);
|
||||
|
||||
let pdf_path = cursor_path.stringByAppendingPathComponent(ns_string!("cursor.pdf"));
|
||||
let image = NSImage::new_by_referencing_file(&pdf_path);
|
||||
|
||||
// TODO: Handle PLists better
|
||||
let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist"));
|
||||
let info: Id<NSDictionary<NSObject, NSObject>> = unsafe {
|
||||
msg_send_id![
|
||||
<NSDictionary<NSObject, NSObject>>::class(),
|
||||
dictionaryWithContentsOfFile: &*info_path,
|
||||
]
|
||||
};
|
||||
let mut x = 0.0;
|
||||
if let Some(n) = info.get(&*ns_string!("hotx")) {
|
||||
if n.is_kind_of::<NSNumber>() {
|
||||
let ptr: *const NSObject = n;
|
||||
let ptr: *const NSNumber = ptr.cast();
|
||||
x = unsafe { &*ptr }.as_cgfloat()
|
||||
}
|
||||
}
|
||||
let mut y = 0.0;
|
||||
if let Some(n) = info.get(&*ns_string!("hotx")) {
|
||||
if n.is_kind_of::<NSNumber>() {
|
||||
let ptr: *const NSObject = n;
|
||||
let ptr: *const NSNumber = ptr.cast();
|
||||
y = unsafe { &*ptr }.as_cgfloat()
|
||||
}
|
||||
}
|
||||
|
||||
let hotspot = NSPoint::new(x, y);
|
||||
Self::new(&image, hotspot)
|
||||
}
|
||||
|
||||
pub fn moveCursor() -> Id<Self> {
|
||||
unsafe { Self::load_webkit_cursor(ns_string!("move")) }
|
||||
}
|
||||
|
||||
pub fn cellCursor() -> Id<Self> {
|
||||
unsafe { Self::load_webkit_cursor(ns_string!("cell")) }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl NSCursor {
|
||||
pub fn from_icon(icon: CursorIcon) -> Id<Self> {
|
||||
match icon {
|
||||
CursorIcon::Default => Default::default(),
|
||||
CursorIcon::Pointer => Self::pointingHandCursor(),
|
||||
CursorIcon::Grab => Self::openHandCursor(),
|
||||
CursorIcon::Grabbing => Self::closedHandCursor(),
|
||||
CursorIcon::Text => Self::IBeamCursor(),
|
||||
CursorIcon::VerticalText => Self::IBeamCursorForVerticalLayout(),
|
||||
CursorIcon::Copy => Self::dragCopyCursor(),
|
||||
CursorIcon::Alias => Self::dragLinkCursor(),
|
||||
CursorIcon::NotAllowed | CursorIcon::NoDrop => Self::operationNotAllowedCursor(),
|
||||
CursorIcon::ContextMenu => Self::contextualMenuCursor(),
|
||||
CursorIcon::Crosshair => Self::crosshairCursor(),
|
||||
CursorIcon::EResize => Self::resizeRightCursor(),
|
||||
CursorIcon::NResize => Self::resizeUpCursor(),
|
||||
CursorIcon::WResize => Self::resizeLeftCursor(),
|
||||
CursorIcon::SResize => Self::resizeDownCursor(),
|
||||
CursorIcon::EwResize | CursorIcon::ColResize => Self::resizeLeftRightCursor(),
|
||||
CursorIcon::NsResize | CursorIcon::RowResize => Self::resizeUpDownCursor(),
|
||||
CursorIcon::Help => Self::_helpCursor(),
|
||||
CursorIcon::ZoomIn => Self::_zoomInCursor(),
|
||||
CursorIcon::ZoomOut => Self::_zoomOutCursor(),
|
||||
CursorIcon::NeResize => Self::_windowResizeNorthEastCursor(),
|
||||
CursorIcon::NwResize => Self::_windowResizeNorthWestCursor(),
|
||||
CursorIcon::SeResize => Self::_windowResizeSouthEastCursor(),
|
||||
CursorIcon::SwResize => Self::_windowResizeSouthWestCursor(),
|
||||
CursorIcon::NeswResize => Self::_windowResizeNorthEastSouthWestCursor(),
|
||||
CursorIcon::NwseResize => Self::_windowResizeNorthWestSouthEastCursor(),
|
||||
// This is the wrong semantics for `Wait`, but it's the same as
|
||||
// what's used in Safari and Chrome.
|
||||
CursorIcon::Wait | CursorIcon::Progress => Self::busyButClickableCursor(),
|
||||
CursorIcon::Move | CursorIcon::AllScroll => Self::moveCursor(),
|
||||
CursorIcon::Cell => Self::cellCursor(),
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultId for NSCursor {
|
||||
fn default_id() -> Id<Self> {
|
||||
Self::arrowCursor()
|
||||
}
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
use std::os::raw::c_ushort;
|
||||
|
||||
use icrate::Foundation::{
|
||||
CGFloat, NSCopying, NSInteger, NSObject, NSPoint, NSString, NSTimeInterval, NSUInteger,
|
||||
};
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSEvent;
|
||||
|
||||
unsafe impl ClassType for NSEvent {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// > Safely handled only on the same thread, whether that be the main thread
|
||||
// > or a secondary thread; otherwise you run the risk of having events get
|
||||
// > out of sequence.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123383>
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSEvent {
|
||||
#[method_id(
|
||||
otherEventWithType:
|
||||
location:
|
||||
modifierFlags:
|
||||
timestamp:
|
||||
windowNumber:
|
||||
context:
|
||||
subtype:
|
||||
data1:
|
||||
data2:
|
||||
)]
|
||||
unsafe fn otherEventWithType(
|
||||
type_: NSEventType,
|
||||
location: NSPoint,
|
||||
flags: NSEventModifierFlags,
|
||||
time: NSTimeInterval,
|
||||
window_num: NSInteger,
|
||||
context: Option<&NSObject>, // NSGraphicsContext
|
||||
subtype: NSEventSubtype,
|
||||
data1: NSInteger,
|
||||
data2: NSInteger,
|
||||
) -> Id<Self>;
|
||||
|
||||
pub fn dummy() -> Id<Self> {
|
||||
unsafe {
|
||||
Self::otherEventWithType(
|
||||
NSEventType::NSApplicationDefined,
|
||||
NSPoint::new(0.0, 0.0),
|
||||
NSEventModifierFlags::empty(),
|
||||
0.0,
|
||||
0,
|
||||
None,
|
||||
NSEventSubtype::NSWindowExposedEventType,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[method_id(
|
||||
keyEventWithType:
|
||||
location:
|
||||
modifierFlags:
|
||||
timestamp:
|
||||
windowNumber:
|
||||
context:
|
||||
characters:
|
||||
charactersIgnoringModifiers:
|
||||
isARepeat:
|
||||
keyCode:
|
||||
)]
|
||||
pub fn keyEventWithType(
|
||||
type_: NSEventType,
|
||||
location: NSPoint,
|
||||
modifier_flags: NSEventModifierFlags,
|
||||
timestamp: NSTimeInterval,
|
||||
window_num: NSInteger,
|
||||
context: Option<&NSObject>,
|
||||
characters: &NSString,
|
||||
characters_ignoring_modifiers: &NSString,
|
||||
is_a_repeat: bool,
|
||||
scancode: c_ushort,
|
||||
) -> Id<Self>;
|
||||
|
||||
#[method(locationInWindow)]
|
||||
pub fn locationInWindow(&self) -> NSPoint;
|
||||
|
||||
// TODO: MainThreadMarker
|
||||
#[method(pressedMouseButtons)]
|
||||
pub fn pressedMouseButtons() -> NSUInteger;
|
||||
|
||||
#[method(modifierFlags)]
|
||||
pub fn modifierFlags(&self) -> NSEventModifierFlags;
|
||||
|
||||
#[method(type)]
|
||||
pub fn type_(&self) -> NSEventType;
|
||||
|
||||
#[method(keyCode)]
|
||||
pub fn key_code(&self) -> c_ushort;
|
||||
|
||||
#[method(magnification)]
|
||||
pub fn magnification(&self) -> CGFloat;
|
||||
|
||||
#[method(phase)]
|
||||
pub fn phase(&self) -> NSEventPhase;
|
||||
|
||||
#[method(momentumPhase)]
|
||||
pub fn momentumPhase(&self) -> NSEventPhase;
|
||||
|
||||
#[method(deltaX)]
|
||||
pub fn deltaX(&self) -> CGFloat;
|
||||
|
||||
#[method(deltaY)]
|
||||
pub fn deltaY(&self) -> CGFloat;
|
||||
|
||||
#[method(buttonNumber)]
|
||||
pub fn buttonNumber(&self) -> NSInteger;
|
||||
|
||||
#[method(scrollingDeltaX)]
|
||||
pub fn scrollingDeltaX(&self) -> CGFloat;
|
||||
|
||||
#[method(scrollingDeltaY)]
|
||||
pub fn scrollingDeltaY(&self) -> CGFloat;
|
||||
|
||||
#[method(hasPreciseScrollingDeltas)]
|
||||
pub fn hasPreciseScrollingDeltas(&self) -> bool;
|
||||
|
||||
#[method(rotation)]
|
||||
pub fn rotation(&self) -> f32;
|
||||
|
||||
#[method(pressure)]
|
||||
pub fn pressure(&self) -> f32;
|
||||
|
||||
#[method(stage)]
|
||||
pub fn stage(&self) -> NSInteger;
|
||||
|
||||
#[method(isARepeat)]
|
||||
pub fn is_a_repeat(&self) -> bool;
|
||||
|
||||
#[method(windowNumber)]
|
||||
pub fn window_number(&self) -> NSInteger;
|
||||
|
||||
#[method(timestamp)]
|
||||
pub fn timestamp(&self) -> NSTimeInterval;
|
||||
|
||||
#[method_id(characters)]
|
||||
pub fn characters(&self) -> Option<Id<NSString>>;
|
||||
|
||||
#[method_id(charactersIgnoringModifiers)]
|
||||
pub fn charactersIgnoringModifiers(&self) -> Option<Id<NSString>>;
|
||||
|
||||
pub fn lshift_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELSHIFTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn rshift_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERSHIFTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn lctrl_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELCTLKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn rctrl_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERCTLKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn lalt_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELALTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn ralt_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERALTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn lcmd_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELCMDKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn rcmd_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERCMDKEYMASK != 0
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl NSCopying for NSEvent {}
|
||||
|
||||
// The values are from the https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h#L258-L259
|
||||
const NX_DEVICELCTLKEYMASK: u32 = 0x00000001;
|
||||
const NX_DEVICELSHIFTKEYMASK: u32 = 0x00000002;
|
||||
const NX_DEVICERSHIFTKEYMASK: u32 = 0x00000004;
|
||||
const NX_DEVICELCMDKEYMASK: u32 = 0x00000008;
|
||||
const NX_DEVICERCMDKEYMASK: u32 = 0x00000010;
|
||||
const NX_DEVICELALTKEYMASK: u32 = 0x00000020;
|
||||
const NX_DEVICERALTKEYMASK: u32 = 0x00000040;
|
||||
const NX_DEVICERCTLKEYMASK: u32 = 0x00002000;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct NSEventModifierFlags: NSUInteger {
|
||||
const NSAlphaShiftKeyMask = 1 << 16;
|
||||
const NSShiftKeyMask = 1 << 17;
|
||||
const NSControlKeyMask = 1 << 18;
|
||||
const NSAlternateKeyMask = 1 << 19;
|
||||
const NSCommandKeyMask = 1 << 20;
|
||||
const NSNumericPadKeyMask = 1 << 21;
|
||||
const NSHelpKeyMask = 1 << 22;
|
||||
const NSFunctionKeyMask = 1 << 23;
|
||||
const NSDeviceIndependentModifierFlagsMask = 0xffff0000;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSEventModifierFlags {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct NSEventPhase: NSUInteger {
|
||||
const NSEventPhaseNone = 0;
|
||||
const NSEventPhaseBegan = 0x1 << 0;
|
||||
const NSEventPhaseStationary = 0x1 << 1;
|
||||
const NSEventPhaseChanged = 0x1 << 2;
|
||||
const NSEventPhaseEnded = 0x1 << 3;
|
||||
const NSEventPhaseCancelled = 0x1 << 4;
|
||||
const NSEventPhaseMayBegin = 0x1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSEventPhase {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(i16)] // short
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSEventSubtype {
|
||||
// TODO: Not sure what these values are
|
||||
// NSMouseEventSubtype = NX_SUBTYPE_DEFAULT,
|
||||
// NSTabletPointEventSubtype = NX_SUBTYPE_TABLET_POINT,
|
||||
// NSTabletProximityEventSubtype = NX_SUBTYPE_TABLET_PROXIMITY
|
||||
// NSTouchEventSubtype = NX_SUBTYPE_MOUSE_TOUCH,
|
||||
NSWindowExposedEventType = 0,
|
||||
NSApplicationActivatedEventType = 1,
|
||||
NSApplicationDeactivatedEventType = 2,
|
||||
NSWindowMovedEventType = 4,
|
||||
NSScreenChangedEventType = 8,
|
||||
NSAWTEventType = 16,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSEventSubtype {
|
||||
const ENCODING: Encoding = i16::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
pub enum NSEventType {
|
||||
NSLeftMouseDown = 1,
|
||||
NSLeftMouseUp = 2,
|
||||
NSRightMouseDown = 3,
|
||||
NSRightMouseUp = 4,
|
||||
NSMouseMoved = 5,
|
||||
NSLeftMouseDragged = 6,
|
||||
NSRightMouseDragged = 7,
|
||||
NSMouseEntered = 8,
|
||||
NSMouseExited = 9,
|
||||
NSKeyDown = 10,
|
||||
NSKeyUp = 11,
|
||||
NSFlagsChanged = 12,
|
||||
NSAppKitDefined = 13,
|
||||
NSSystemDefined = 14,
|
||||
NSApplicationDefined = 15,
|
||||
NSPeriodic = 16,
|
||||
NSCursorUpdate = 17,
|
||||
NSScrollWheel = 22,
|
||||
NSTabletPoint = 23,
|
||||
NSTabletProximity = 24,
|
||||
NSOtherMouseDown = 25,
|
||||
NSOtherMouseUp = 26,
|
||||
NSOtherMouseDragged = 27,
|
||||
NSEventTypeGesture = 29,
|
||||
NSEventTypeMagnify = 30,
|
||||
NSEventTypeSwipe = 31,
|
||||
NSEventTypeRotate = 18,
|
||||
NSEventTypeBeginGesture = 19,
|
||||
NSEventTypeEndGesture = 20,
|
||||
NSEventTypePressure = 34,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSEventType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use icrate::Foundation::{NSData, NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
// TODO: Can this be mutable?
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSImage;
|
||||
|
||||
unsafe impl ClassType for NSImage {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// Documented Thread-Unsafe, but:
|
||||
// > One thread can create an NSImage object, draw to the image buffer,
|
||||
// > and pass it off to the main thread for drawing. The underlying image
|
||||
// > cache is shared among all threads.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-126728>
|
||||
//
|
||||
// So really only unsafe to mutate on several threads.
|
||||
unsafe impl Send for NSImage {}
|
||||
unsafe impl Sync for NSImage {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSImage {
|
||||
pub fn new_by_referencing_file(path: &NSString) -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::alloc(), initByReferencingFile: path] }
|
||||
}
|
||||
|
||||
pub fn new_with_data(data: &NSData) -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::alloc(), initWithData: data] }
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,25 +0,0 @@
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::NSMenuItem;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSMenu;
|
||||
|
||||
unsafe impl ClassType for NSMenu {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSMenu {
|
||||
#[method_id(new)]
|
||||
pub fn new() -> Id<Self>;
|
||||
|
||||
#[method(addItem:)]
|
||||
pub fn addItem(&self, item: &NSMenuItem);
|
||||
}
|
||||
);
|
||||
@@ -1,43 +0,0 @@
|
||||
use icrate::Foundation::{NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::Sel;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||
|
||||
use super::{NSEventModifierFlags, NSMenu};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSMenuItem;
|
||||
|
||||
unsafe impl ClassType for NSMenuItem {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSMenuItem {
|
||||
#[method_id(new)]
|
||||
pub fn new() -> Id<Self>;
|
||||
|
||||
pub fn newWithTitle(title: &NSString, action: Sel, key_equivalent: &NSString) -> Id<Self> {
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
Self::alloc(),
|
||||
initWithTitle: title,
|
||||
action: action,
|
||||
keyEquivalent: key_equivalent,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[method_id(separatorItem)]
|
||||
pub fn separatorItem() -> Id<Self>;
|
||||
|
||||
#[method(setKeyEquivalentModifierMask:)]
|
||||
pub fn setKeyEquivalentModifierMask(&self, mask: NSEventModifierFlags);
|
||||
|
||||
#[method(setSubmenu:)]
|
||||
pub fn setSubmenu(&self, submenu: &NSMenu);
|
||||
}
|
||||
);
|
||||
@@ -1,66 +0,0 @@
|
||||
//! Safe bindings for the AppKit framework.
|
||||
//!
|
||||
//! These are split out from the rest of `winit` to make safety easier to review.
|
||||
//! In the future, these should probably live in another crate like `cacao`.
|
||||
//!
|
||||
//! TODO: Main thread safety.
|
||||
// Objective-C methods have different conventions, and it's much easier to
|
||||
// understand if we just use the same names
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::enum_variant_names)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
mod appearance;
|
||||
mod application;
|
||||
mod button;
|
||||
mod color;
|
||||
mod control;
|
||||
mod cursor;
|
||||
mod event;
|
||||
mod image;
|
||||
mod menu;
|
||||
mod menu_item;
|
||||
mod pasteboard;
|
||||
mod responder;
|
||||
mod screen;
|
||||
mod tab_group;
|
||||
mod text_input_client;
|
||||
mod text_input_context;
|
||||
mod version;
|
||||
mod view;
|
||||
mod window;
|
||||
|
||||
pub(crate) use self::appearance::NSAppearance;
|
||||
pub(crate) use self::application::{
|
||||
NSApp, NSApplication, NSApplicationActivationPolicy, NSApplicationPresentationOptions,
|
||||
NSRequestUserAttentionType,
|
||||
};
|
||||
pub(crate) use self::button::NSButton;
|
||||
pub(crate) use self::color::NSColor;
|
||||
pub(crate) use self::control::NSControl;
|
||||
pub(crate) use self::cursor::NSCursor;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use self::event::{
|
||||
NSEvent, NSEventModifierFlags, NSEventPhase, NSEventSubtype, NSEventType,
|
||||
};
|
||||
pub(crate) use self::image::NSImage;
|
||||
pub(crate) use self::menu::NSMenu;
|
||||
pub(crate) use self::menu_item::NSMenuItem;
|
||||
pub(crate) use self::pasteboard::{NSFilenamesPboardType, NSPasteboard, NSPasteboardType};
|
||||
pub(crate) use self::responder::NSResponder;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use self::screen::{NSDeviceDescriptionKey, NSScreen};
|
||||
pub(crate) use self::tab_group::NSWindowTabGroup;
|
||||
pub(crate) use self::text_input_client::NSTextInputClient;
|
||||
pub(crate) use self::text_input_context::NSTextInputContext;
|
||||
pub(crate) use self::version::NSAppKitVersion;
|
||||
pub(crate) use self::view::{NSTrackingRectTag, NSView};
|
||||
pub(crate) use self::window::{
|
||||
NSBackingStoreType, NSWindow, NSWindowButton, NSWindowLevel, NSWindowOcclusionState,
|
||||
NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode,
|
||||
NSWindowTitleVisibility,
|
||||
};
|
||||
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
extern "C" {}
|
||||
@@ -1,26 +0,0 @@
|
||||
use icrate::Foundation::{NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSPasteboard;
|
||||
|
||||
unsafe impl ClassType for NSPasteboard {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSPasteboard {
|
||||
#[method_id(propertyListForType:)]
|
||||
pub fn propertyListForType(&self, type_: &NSPasteboardType) -> Id<NSObject>;
|
||||
}
|
||||
);
|
||||
|
||||
pub type NSPasteboardType = NSString;
|
||||
|
||||
extern "C" {
|
||||
pub static NSFilenamesPboardType: &'static NSPasteboardType;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
use icrate::Foundation::{NSArray, NSObject};
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::NSEvent;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NSResponder;
|
||||
|
||||
unsafe impl ClassType for NSResponder {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// Documented as "Thread-Unsafe".
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSResponder {
|
||||
#[method(interpretKeyEvents:)]
|
||||
pub(crate) unsafe fn interpretKeyEvents(&self, events: &NSArray<NSEvent>);
|
||||
}
|
||||
);
|
||||
@@ -1,65 +0,0 @@
|
||||
use icrate::ns_string;
|
||||
use icrate::Foundation::{CGFloat, NSArray, NSDictionary, NSNumber, NSObject, NSRect, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSScreen;
|
||||
|
||||
unsafe impl ClassType for NSScreen {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: Main thread marker!
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSScreen {
|
||||
/// The application object must have been created.
|
||||
#[method_id(mainScreen)]
|
||||
pub fn main() -> Option<Id<Self>>;
|
||||
|
||||
/// The application object must have been created.
|
||||
#[method_id(screens)]
|
||||
pub fn screens() -> Id<NSArray<Self>>;
|
||||
|
||||
#[method(frame)]
|
||||
pub fn frame(&self) -> NSRect;
|
||||
|
||||
#[method(visibleFrame)]
|
||||
pub fn visibleFrame(&self) -> NSRect;
|
||||
|
||||
#[method_id(deviceDescription)]
|
||||
pub fn deviceDescription(&self) -> Id<NSDictionary<NSDeviceDescriptionKey, AnyObject>>;
|
||||
|
||||
pub fn display_id(&self) -> u32 {
|
||||
let key = ns_string!("NSScreenNumber");
|
||||
|
||||
objc2::rc::autoreleasepool(|_| {
|
||||
let device_description = self.deviceDescription();
|
||||
|
||||
// Retrieve the CGDirectDisplayID associated with this screen
|
||||
//
|
||||
// SAFETY: The value from @"NSScreenNumber" in deviceDescription is guaranteed
|
||||
// to be an NSNumber. See documentation for `deviceDescription` for details:
|
||||
// <https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription?language=objc>
|
||||
let obj = device_description
|
||||
.get(key)
|
||||
.expect("failed getting screen display id from device description");
|
||||
let obj: *const AnyObject = obj;
|
||||
let obj: *const NSNumber = obj.cast();
|
||||
let obj: &NSNumber = unsafe { &*obj };
|
||||
|
||||
obj.as_u32()
|
||||
})
|
||||
}
|
||||
|
||||
#[method(backingScaleFactor)]
|
||||
pub fn backingScaleFactor(&self) -> CGFloat;
|
||||
}
|
||||
);
|
||||
|
||||
pub type NSDeviceDescriptionKey = NSString;
|
||||
@@ -1,31 +0,0 @@
|
||||
use icrate::Foundation::{NSArray, NSObject};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::NSWindow;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSWindowTabGroup;
|
||||
|
||||
unsafe impl ClassType for NSWindowTabGroup {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSWindowTabGroup {
|
||||
#[method(selectNextTab)]
|
||||
pub fn selectNextTab(&self);
|
||||
|
||||
#[method(selectPreviousTab)]
|
||||
pub fn selectPreviousTab(&self);
|
||||
|
||||
#[method_id(windows)]
|
||||
pub fn tabbedWindows(&self) -> Option<Id<NSArray<NSWindow>>>;
|
||||
|
||||
#[method(setSelectedWindow:)]
|
||||
pub fn setSelectedWindow(&self, window: &NSWindow);
|
||||
}
|
||||
);
|
||||
@@ -1,9 +0,0 @@
|
||||
use objc2::{extern_protocol, ProtocolType};
|
||||
|
||||
extern_protocol!(
|
||||
pub(crate) unsafe trait NSTextInputClient {
|
||||
// TODO: Methods
|
||||
}
|
||||
|
||||
unsafe impl ProtocolType for dyn NSTextInputClient {}
|
||||
);
|
||||
@@ -1,29 +0,0 @@
|
||||
use icrate::Foundation::{NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
type NSTextInputSourceIdentifier = NSString;
|
||||
|
||||
extern_class!(
|
||||
/// Main-Thread-Only!
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSTextInputContext;
|
||||
|
||||
unsafe impl ClassType for NSTextInputContext {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSTextInputContext {
|
||||
#[method(invalidateCharacterCoordinates)]
|
||||
pub fn invalidateCharacterCoordinates(&self);
|
||||
|
||||
#[method(discardMarkedText)]
|
||||
pub fn discardMarkedText(&self);
|
||||
|
||||
#[method_id(selectedKeyboardInputSource)]
|
||||
pub fn selectedKeyboardInputSource(&self) -> Option<Id<NSTextInputSourceIdentifier>>;
|
||||
}
|
||||
);
|
||||
@@ -1,62 +0,0 @@
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
|
||||
pub struct NSAppKitVersion(f64);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
impl NSAppKitVersion {
|
||||
pub fn current() -> Self {
|
||||
extern "C" {
|
||||
static NSAppKitVersionNumber: NSAppKitVersion;
|
||||
}
|
||||
|
||||
unsafe { NSAppKitVersionNumber }
|
||||
}
|
||||
|
||||
pub fn floor(self) -> Self {
|
||||
Self(self.0.floor())
|
||||
}
|
||||
|
||||
pub const NSAppKitVersionNumber10_0: Self = Self(577.0);
|
||||
pub const NSAppKitVersionNumber10_1: Self = Self(620.0);
|
||||
pub const NSAppKitVersionNumber10_2: Self = Self(663.0);
|
||||
pub const NSAppKitVersionNumber10_2_3: Self = Self(663.6);
|
||||
pub const NSAppKitVersionNumber10_3: Self = Self(743.0);
|
||||
pub const NSAppKitVersionNumber10_3_2: Self = Self(743.14);
|
||||
pub const NSAppKitVersionNumber10_3_3: Self = Self(743.2);
|
||||
pub const NSAppKitVersionNumber10_3_5: Self = Self(743.24);
|
||||
pub const NSAppKitVersionNumber10_3_7: Self = Self(743.33);
|
||||
pub const NSAppKitVersionNumber10_3_9: Self = Self(743.36);
|
||||
pub const NSAppKitVersionNumber10_4: Self = Self(824.0);
|
||||
pub const NSAppKitVersionNumber10_4_1: Self = Self(824.1);
|
||||
pub const NSAppKitVersionNumber10_4_3: Self = Self(824.23);
|
||||
pub const NSAppKitVersionNumber10_4_4: Self = Self(824.33);
|
||||
pub const NSAppKitVersionNumber10_4_7: Self = Self(824.41);
|
||||
pub const NSAppKitVersionNumber10_5: Self = Self(949.0);
|
||||
pub const NSAppKitVersionNumber10_5_2: Self = Self(949.27);
|
||||
pub const NSAppKitVersionNumber10_5_3: Self = Self(949.33);
|
||||
pub const NSAppKitVersionNumber10_6: Self = Self(1038.0);
|
||||
pub const NSAppKitVersionNumber10_7: Self = Self(1138.0);
|
||||
pub const NSAppKitVersionNumber10_7_2: Self = Self(1138.23);
|
||||
pub const NSAppKitVersionNumber10_7_3: Self = Self(1138.32);
|
||||
pub const NSAppKitVersionNumber10_7_4: Self = Self(1138.47);
|
||||
pub const NSAppKitVersionNumber10_8: Self = Self(1187.0);
|
||||
pub const NSAppKitVersionNumber10_9: Self = Self(1265.0);
|
||||
pub const NSAppKitVersionNumber10_10: Self = Self(1343.0);
|
||||
pub const NSAppKitVersionNumber10_10_2: Self = Self(1344.0);
|
||||
pub const NSAppKitVersionNumber10_10_3: Self = Self(1347.0);
|
||||
pub const NSAppKitVersionNumber10_10_4: Self = Self(1348.0);
|
||||
pub const NSAppKitVersionNumber10_10_5: Self = Self(1348.0);
|
||||
pub const NSAppKitVersionNumber10_10_Max: Self = Self(1349.0);
|
||||
pub const NSAppKitVersionNumber10_11: Self = Self(1404.0);
|
||||
pub const NSAppKitVersionNumber10_11_1: Self = Self(1404.13);
|
||||
pub const NSAppKitVersionNumber10_11_2: Self = Self(1404.34);
|
||||
pub const NSAppKitVersionNumber10_11_3: Self = Self(1404.34);
|
||||
pub const NSAppKitVersionNumber10_12: Self = Self(1504.0);
|
||||
pub const NSAppKitVersionNumber10_12_1: Self = Self(1504.60);
|
||||
pub const NSAppKitVersionNumber10_12_2: Self = Self(1504.76);
|
||||
pub const NSAppKitVersionNumber10_13: Self = Self(1561.0);
|
||||
pub const NSAppKitVersionNumber10_13_1: Self = Self(1561.1);
|
||||
pub const NSAppKitVersionNumber10_13_2: Self = Self(1561.2);
|
||||
pub const NSAppKitVersionNumber10_13_4: Self = Self(1561.4);
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
use std::ffi::c_void;
|
||||
use std::num::NonZeroIsize;
|
||||
use std::ptr;
|
||||
|
||||
use icrate::Foundation::{NSObject, NSPoint, NSRect};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::{NSCursor, NSResponder, NSTextInputContext, NSWindow};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSView;
|
||||
|
||||
unsafe impl ClassType for NSView {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// Documented as "Main Thread Only".
|
||||
// > generally thread safe, although operations on views such as creating,
|
||||
// > resizing, and moving should happen on the main thread.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
|
||||
//
|
||||
// > If you want to use a thread to draw to a view, bracket all drawing code
|
||||
// > between the lockFocusIfCanDraw and unlockFocus methods of NSView.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123351-BBCFIIEB>
|
||||
|
||||
extern_methods!(
|
||||
/// Getter methods
|
||||
unsafe impl NSView {
|
||||
#[method(frame)]
|
||||
pub fn frame(&self) -> NSRect;
|
||||
|
||||
#[method(bounds)]
|
||||
pub fn bounds(&self) -> NSRect;
|
||||
|
||||
#[method_id(inputContext)]
|
||||
pub fn inputContext(
|
||||
&self,
|
||||
// _mtm: MainThreadMarker,
|
||||
) -> Option<Id<NSTextInputContext>>;
|
||||
|
||||
#[method(hasMarkedText)]
|
||||
pub fn hasMarkedText(&self) -> bool;
|
||||
|
||||
#[method(convertPoint:fromView:)]
|
||||
pub fn convertPoint_fromView(&self, point: NSPoint, view: Option<&NSView>) -> NSPoint;
|
||||
|
||||
#[method_id(window)]
|
||||
pub fn window(&self) -> Option<Id<NSWindow>>;
|
||||
}
|
||||
|
||||
unsafe impl NSView {
|
||||
#[method(setWantsBestResolutionOpenGLSurface:)]
|
||||
pub fn setWantsBestResolutionOpenGLSurface(&self, value: bool);
|
||||
|
||||
#[method(setWantsLayer:)]
|
||||
pub fn setWantsLayer(&self, wants_layer: bool);
|
||||
|
||||
#[method(setPostsFrameChangedNotifications:)]
|
||||
pub fn setPostsFrameChangedNotifications(&self, value: bool);
|
||||
|
||||
#[method(removeTrackingRect:)]
|
||||
pub fn removeTrackingRect(&self, tag: NSTrackingRectTag);
|
||||
|
||||
#[method(addTrackingRect:owner:userData:assumeInside:)]
|
||||
unsafe fn inner_addTrackingRect(
|
||||
&self,
|
||||
rect: NSRect,
|
||||
owner: &AnyObject,
|
||||
user_data: *mut c_void,
|
||||
assume_inside: bool,
|
||||
) -> Option<NSTrackingRectTag>;
|
||||
|
||||
pub fn add_tracking_rect(&self, rect: NSRect, assume_inside: bool) -> NSTrackingRectTag {
|
||||
// SAFETY: The user data is NULL, so it is valid
|
||||
unsafe { self.inner_addTrackingRect(rect, self, ptr::null_mut(), assume_inside) }
|
||||
.expect("failed creating tracking rect")
|
||||
}
|
||||
|
||||
#[method(addCursorRect:cursor:)]
|
||||
// NSCursor safe to take by shared reference since it is already immutable
|
||||
pub fn addCursorRect(&self, rect: NSRect, cursor: &NSCursor);
|
||||
|
||||
#[method(setHidden:)]
|
||||
pub fn setHidden(&self, hidden: bool);
|
||||
}
|
||||
);
|
||||
|
||||
/// <https://developer.apple.com/documentation/appkit/nstrackingrecttag?language=objc>
|
||||
pub type NSTrackingRectTag = NonZeroIsize; // NSInteger, but non-zero!
|
||||
@@ -1,440 +0,0 @@
|
||||
use icrate::Foundation::{
|
||||
CGFloat, NSArray, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, NSUInteger,
|
||||
};
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::{
|
||||
NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView, NSWindowTabGroup,
|
||||
};
|
||||
|
||||
extern_class!(
|
||||
/// Main-Thread-Only!
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NSWindow;
|
||||
|
||||
unsafe impl ClassType for NSWindow {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// Documented as "Main Thread Only", but:
|
||||
// > Thread safe in that you can create and manage them on a secondary thread.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123364>
|
||||
//
|
||||
// So could in theory be `Send`, and perhaps also `Sync` - but we would like
|
||||
// interior mutability on windows, since that's just much easier, and in that
|
||||
// case, they can't be!
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSWindow {
|
||||
#[method(frame)]
|
||||
pub(crate) fn frame(&self) -> NSRect;
|
||||
|
||||
#[method(windowNumber)]
|
||||
pub(crate) fn windowNumber(&self) -> NSInteger;
|
||||
|
||||
#[method(backingScaleFactor)]
|
||||
pub(crate) fn backingScaleFactor(&self) -> CGFloat;
|
||||
|
||||
#[method_id(contentView)]
|
||||
pub(crate) fn contentView(&self) -> Id<NSView>;
|
||||
|
||||
#[method(setContentView:)]
|
||||
pub(crate) fn setContentView(&self, view: &NSView);
|
||||
|
||||
#[method(setInitialFirstResponder:)]
|
||||
pub(crate) fn setInitialFirstResponder(&self, view: &NSView);
|
||||
|
||||
#[method(makeFirstResponder:)]
|
||||
#[must_use]
|
||||
pub(crate) fn makeFirstResponder(&self, responder: Option<&NSResponder>) -> bool;
|
||||
|
||||
#[method(contentRectForFrameRect:)]
|
||||
pub(crate) fn contentRectForFrameRect(&self, windowFrame: NSRect) -> NSRect;
|
||||
|
||||
#[method_id(screen)]
|
||||
pub(crate) fn screen(&self) -> Option<Id<NSScreen>>;
|
||||
|
||||
#[method(setContentSize:)]
|
||||
pub(crate) fn setContentSize(&self, contentSize: NSSize);
|
||||
|
||||
#[method(setFrameTopLeftPoint:)]
|
||||
pub(crate) fn setFrameTopLeftPoint(&self, point: NSPoint);
|
||||
|
||||
#[method(setMinSize:)]
|
||||
pub(crate) fn setMinSize(&self, minSize: NSSize);
|
||||
|
||||
#[method(setMaxSize:)]
|
||||
pub(crate) fn setMaxSize(&self, maxSize: NSSize);
|
||||
|
||||
#[method(setResizeIncrements:)]
|
||||
pub(crate) fn setResizeIncrements(&self, increments: NSSize);
|
||||
|
||||
#[method(contentResizeIncrements)]
|
||||
pub(crate) fn contentResizeIncrements(&self) -> NSSize;
|
||||
|
||||
#[method(setContentResizeIncrements:)]
|
||||
pub(crate) fn setContentResizeIncrements(&self, increments: NSSize);
|
||||
|
||||
#[method(setFrame:display:)]
|
||||
pub(crate) fn setFrame_display(&self, frameRect: NSRect, flag: bool);
|
||||
|
||||
#[method(setMovable:)]
|
||||
pub(crate) fn setMovable(&self, movable: bool);
|
||||
|
||||
#[method(setSharingType:)]
|
||||
pub(crate) fn setSharingType(&self, sharingType: NSWindowSharingType);
|
||||
|
||||
#[method(setTabbingMode:)]
|
||||
pub(crate) fn setTabbingMode(&self, tabbingMode: NSWindowTabbingMode);
|
||||
|
||||
#[method(setOpaque:)]
|
||||
pub(crate) fn setOpaque(&self, opaque: bool);
|
||||
|
||||
#[method(hasShadow)]
|
||||
pub(crate) fn hasShadow(&self) -> bool;
|
||||
|
||||
#[method(setHasShadow:)]
|
||||
pub(crate) fn setHasShadow(&self, has_shadow: bool);
|
||||
|
||||
#[method(setIgnoresMouseEvents:)]
|
||||
pub(crate) fn setIgnoresMouseEvents(&self, ignores: bool);
|
||||
|
||||
#[method(setBackgroundColor:)]
|
||||
pub(crate) fn setBackgroundColor(&self, color: &NSColor);
|
||||
|
||||
#[method(styleMask)]
|
||||
pub(crate) fn styleMask(&self) -> NSWindowStyleMask;
|
||||
|
||||
#[method(setStyleMask:)]
|
||||
pub(crate) fn setStyleMask(&self, mask: NSWindowStyleMask);
|
||||
|
||||
#[method(registerForDraggedTypes:)]
|
||||
pub(crate) fn registerForDraggedTypes(&self, types: &NSArray<NSPasteboardType>);
|
||||
|
||||
#[method(makeKeyAndOrderFront:)]
|
||||
pub(crate) fn makeKeyAndOrderFront(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(orderFront:)]
|
||||
pub(crate) fn orderFront(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(miniaturize:)]
|
||||
pub(crate) fn miniaturize(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(deminiaturize:)]
|
||||
pub(crate) fn deminiaturize(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(toggleFullScreen:)]
|
||||
pub(crate) fn toggleFullScreen(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(orderOut:)]
|
||||
pub(crate) fn orderOut(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(zoom:)]
|
||||
pub(crate) fn zoom(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(selectNextKeyView:)]
|
||||
pub(crate) fn selectNextKeyView(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(selectPreviousKeyView:)]
|
||||
pub(crate) fn selectPreviousKeyView(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method_id(firstResponder)]
|
||||
pub(crate) fn firstResponder(&self) -> Option<Id<NSResponder>>;
|
||||
|
||||
#[method_id(standardWindowButton:)]
|
||||
pub(crate) fn standardWindowButton(&self, kind: NSWindowButton) -> Option<Id<NSButton>>;
|
||||
|
||||
#[method(setTitle:)]
|
||||
pub(crate) fn setTitle(&self, title: &NSString);
|
||||
|
||||
#[method_id(title)]
|
||||
pub(crate) fn title_(&self) -> Id<NSString>;
|
||||
|
||||
#[method(setReleasedWhenClosed:)]
|
||||
pub(crate) fn setReleasedWhenClosed(&self, val: bool);
|
||||
|
||||
#[method(setAcceptsMouseMovedEvents:)]
|
||||
pub(crate) fn setAcceptsMouseMovedEvents(&self, val: bool);
|
||||
|
||||
#[method(setTitlebarAppearsTransparent:)]
|
||||
pub(crate) fn setTitlebarAppearsTransparent(&self, val: bool);
|
||||
|
||||
#[method(setTitleVisibility:)]
|
||||
pub(crate) fn setTitleVisibility(&self, visibility: NSWindowTitleVisibility);
|
||||
|
||||
#[method(setMovableByWindowBackground:)]
|
||||
pub(crate) fn setMovableByWindowBackground(&self, val: bool);
|
||||
|
||||
#[method(setLevel:)]
|
||||
pub(crate) fn setLevel(&self, level: NSWindowLevel);
|
||||
|
||||
#[method(setAllowsAutomaticWindowTabbing:)]
|
||||
pub(crate) fn setAllowsAutomaticWindowTabbing(val: bool);
|
||||
|
||||
#[method(setTabbingIdentifier:)]
|
||||
pub(crate) fn setTabbingIdentifier(&self, identifier: &NSString);
|
||||
|
||||
#[method(setDocumentEdited:)]
|
||||
pub(crate) fn setDocumentEdited(&self, val: bool);
|
||||
|
||||
#[method(occlusionState)]
|
||||
pub(crate) fn occlusionState(&self) -> NSWindowOcclusionState;
|
||||
|
||||
#[method(center)]
|
||||
pub(crate) fn center(&self);
|
||||
|
||||
#[method(isResizable)]
|
||||
pub(crate) fn isResizable(&self) -> bool;
|
||||
|
||||
#[method(isMiniaturizable)]
|
||||
pub(crate) fn isMiniaturizable(&self) -> bool;
|
||||
|
||||
#[method(hasCloseBox)]
|
||||
pub(crate) fn hasCloseBox(&self) -> bool;
|
||||
|
||||
#[method(isMiniaturized)]
|
||||
pub(crate) fn isMiniaturized(&self) -> bool;
|
||||
|
||||
#[method(isVisible)]
|
||||
pub(crate) fn isVisible(&self) -> bool;
|
||||
|
||||
#[method(isKeyWindow)]
|
||||
pub(crate) fn isKeyWindow(&self) -> bool;
|
||||
|
||||
#[method(isZoomed)]
|
||||
pub(crate) fn isZoomed(&self) -> bool;
|
||||
|
||||
#[method(allowsAutomaticWindowTabbing)]
|
||||
pub(crate) fn allowsAutomaticWindowTabbing() -> bool;
|
||||
|
||||
#[method(selectNextTab)]
|
||||
pub(crate) fn selectNextTab(&self);
|
||||
|
||||
#[method_id(tabbingIdentifier)]
|
||||
pub(crate) fn tabbingIdentifier(&self) -> Id<NSString>;
|
||||
|
||||
#[method_id(tabGroup)]
|
||||
pub(crate) fn tabGroup(&self) -> Option<Id<NSWindowTabGroup>>;
|
||||
|
||||
#[method(isDocumentEdited)]
|
||||
pub(crate) fn isDocumentEdited(&self) -> bool;
|
||||
|
||||
#[method(close)]
|
||||
pub(crate) fn close(&self);
|
||||
|
||||
#[method(performWindowDragWithEvent:)]
|
||||
// TODO: Can this actually accept NULL?
|
||||
pub(crate) fn performWindowDragWithEvent(&self, event: Option<&NSEvent>);
|
||||
|
||||
#[method(invalidateCursorRectsForView:)]
|
||||
pub(crate) fn invalidateCursorRectsForView(&self, view: &NSView);
|
||||
|
||||
#[method(setDelegate:)]
|
||||
pub(crate) fn setDelegate(&self, delegate: Option<&NSObject>);
|
||||
|
||||
#[method(sendEvent:)]
|
||||
pub(crate) unsafe fn sendEvent(&self, event: &NSEvent);
|
||||
|
||||
#[method(addChildWindow:ordered:)]
|
||||
pub(crate) unsafe fn addChildWindow(&self, child: &NSWindow, ordered: NSWindowOrderingMode);
|
||||
}
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowTitleVisibility {
|
||||
#[doc(alias = "NSWindowTitleVisible")]
|
||||
Visible = 0,
|
||||
#[doc(alias = "NSWindowTitleHidden")]
|
||||
Hidden = 1,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowTitleVisibility {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowButton {
|
||||
#[doc(alias = "NSWindowCloseButton")]
|
||||
Close = 0,
|
||||
#[doc(alias = "NSWindowMiniaturizeButton")]
|
||||
Miniaturize = 1,
|
||||
#[doc(alias = "NSWindowZoomButton")]
|
||||
Zoom = 2,
|
||||
#[doc(alias = "NSWindowToolbarButton")]
|
||||
Toolbar = 3,
|
||||
#[doc(alias = "NSWindowDocumentIconButton")]
|
||||
DocumentIcon = 4,
|
||||
#[doc(alias = "NSWindowDocumentVersionsButton")]
|
||||
DocumentVersions = 6,
|
||||
#[doc(alias = "NSWindowFullScreenButton")]
|
||||
#[deprecated = "Deprecated since macOS 10.12"]
|
||||
FullScreen = 7,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowButton {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
// CGWindowLevel.h
|
||||
//
|
||||
// Note: There are two different things at play in this header:
|
||||
// `CGWindowLevel` and `CGWindowLevelKey`.
|
||||
//
|
||||
// It seems like there was a push towards using "key" values instead of the
|
||||
// raw window level values, and then you were supposed to use
|
||||
// `CGWindowLevelForKey` to get the actual level.
|
||||
//
|
||||
// But the values that `NSWindowLevel` has are compiled in, and as such has
|
||||
// to remain ABI compatible, so they're safe for us to hardcode as well.
|
||||
#[allow(dead_code)]
|
||||
mod window_level {
|
||||
const kCGNumReservedWindowLevels: i32 = 16;
|
||||
const kCGNumReservedBaseWindowLevels: i32 = 5;
|
||||
|
||||
pub const kCGBaseWindowLevel: i32 = i32::MIN;
|
||||
pub const kCGMinimumWindowLevel: i32 = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels;
|
||||
pub const kCGMaximumWindowLevel: i32 = i32::MAX - kCGNumReservedWindowLevels;
|
||||
|
||||
pub const kCGDesktopWindowLevel: i32 = kCGMinimumWindowLevel + 20;
|
||||
pub const kCGDesktopIconWindowLevel: i32 = kCGDesktopWindowLevel + 20;
|
||||
pub const kCGBackstopMenuLevel: i32 = -20;
|
||||
pub const kCGNormalWindowLevel: i32 = 0;
|
||||
pub const kCGFloatingWindowLevel: i32 = 3;
|
||||
pub const kCGTornOffMenuWindowLevel: i32 = 3;
|
||||
pub const kCGModalPanelWindowLevel: i32 = 8;
|
||||
pub const kCGUtilityWindowLevel: i32 = 19;
|
||||
pub const kCGDockWindowLevel: i32 = 20;
|
||||
pub const kCGMainMenuWindowLevel: i32 = 24;
|
||||
pub const kCGStatusWindowLevel: i32 = 25;
|
||||
pub const kCGPopUpMenuWindowLevel: i32 = 101;
|
||||
pub const kCGOverlayWindowLevel: i32 = 102;
|
||||
pub const kCGHelpWindowLevel: i32 = 200;
|
||||
pub const kCGDraggingWindowLevel: i32 = 500;
|
||||
pub const kCGScreenSaverWindowLevel: i32 = 1000;
|
||||
pub const kCGAssistiveTechHighWindowLevel: i32 = 1500;
|
||||
pub const kCGCursorWindowLevel: i32 = kCGMaximumWindowLevel - 1;
|
||||
}
|
||||
use window_level::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct NSWindowLevel(pub NSInteger);
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl NSWindowLevel {
|
||||
#[doc(alias = "BelowNormalWindowLevel")]
|
||||
pub const BELOW_NORMAL: Self = Self((kCGNormalWindowLevel - 1) as _);
|
||||
#[doc(alias = "NSNormalWindowLevel")]
|
||||
pub const Normal: Self = Self(kCGNormalWindowLevel as _);
|
||||
#[doc(alias = "NSFloatingWindowLevel")]
|
||||
pub const Floating: Self = Self(kCGFloatingWindowLevel as _);
|
||||
#[doc(alias = "NSTornOffMenuWindowLevel")]
|
||||
pub const TornOffMenu: Self = Self(kCGTornOffMenuWindowLevel as _);
|
||||
#[doc(alias = "NSModalPanelWindowLevel")]
|
||||
pub const ModalPanel: Self = Self(kCGModalPanelWindowLevel as _);
|
||||
#[doc(alias = "NSMainMenuWindowLevel")]
|
||||
pub const MainMenu: Self = Self(kCGMainMenuWindowLevel as _);
|
||||
#[doc(alias = "NSStatusWindowLevel")]
|
||||
pub const Status: Self = Self(kCGStatusWindowLevel as _);
|
||||
#[doc(alias = "NSPopUpMenuWindowLevel")]
|
||||
pub const PopUpMenu: Self = Self(kCGPopUpMenuWindowLevel as _);
|
||||
#[doc(alias = "NSScreenSaverWindowLevel")]
|
||||
pub const ScreenSaver: Self = Self(kCGScreenSaverWindowLevel as _);
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowLevel {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct NSWindowOcclusionState: NSUInteger {
|
||||
const NSWindowOcclusionStateVisible = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowOcclusionState {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NSWindowStyleMask: NSUInteger {
|
||||
const NSBorderlessWindowMask = 0;
|
||||
const NSTitledWindowMask = 1 << 0;
|
||||
const NSClosableWindowMask = 1 << 1;
|
||||
const NSMiniaturizableWindowMask = 1 << 2;
|
||||
const NSResizableWindowMask = 1 << 3;
|
||||
const NSTexturedBackgroundWindowMask = 1 << 8;
|
||||
const NSUnifiedTitleAndToolbarWindowMask = 1 << 12;
|
||||
const NSFullScreenWindowMask = 1 << 14;
|
||||
const NSFullSizeContentViewWindowMask = 1 << 15;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowStyleMask {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSBackingStoreType {
|
||||
NSBackingStoreRetained = 0,
|
||||
NSBackingStoreNonretained = 1,
|
||||
NSBackingStoreBuffered = 2,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSBackingStoreType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowSharingType {
|
||||
NSWindowSharingNone = 0,
|
||||
NSWindowSharingReadOnly = 1,
|
||||
NSWindowSharingReadWrite = 2,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowSharingType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowOrderingMode {
|
||||
NSWindowAbove = 1,
|
||||
NSWindowBelow = -1,
|
||||
NSWindowOut = 0,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowOrderingMode {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowTabbingMode {
|
||||
NSWindowTabbingModeAutomatic = 0,
|
||||
NSWindowTabbingModeDisallowed = 2,
|
||||
NSWindowTabbingModePreferred = 1,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowTabbingMode {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
@@ -1,621 +0,0 @@
|
||||
use std::ffi::c_void;
|
||||
|
||||
use core_foundation::{
|
||||
base::CFRelease,
|
||||
data::{CFDataGetBytePtr, CFDataRef},
|
||||
};
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use super::appkit::{NSEvent, NSEventModifierFlags};
|
||||
use crate::{
|
||||
event::{ElementState, KeyEvent, Modifiers},
|
||||
keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey,
|
||||
NativeKeyCode, PhysicalKey,
|
||||
},
|
||||
platform::{
|
||||
modifier_supplement::KeyEventExtModifierSupplement, scancode::PhysicalKeyExtScancode,
|
||||
},
|
||||
platform_impl::platform::ffi,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
pub text_with_all_modifiers: Option<SmolStr>,
|
||||
pub key_without_modifiers: Key,
|
||||
}
|
||||
|
||||
impl KeyEventExtModifierSupplement for KeyEvent {
|
||||
fn text_with_all_modifiers(&self) -> Option<&str> {
|
||||
self.platform_specific
|
||||
.text_with_all_modifiers
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
}
|
||||
|
||||
fn key_without_modifiers(&self) -> Key {
|
||||
self.platform_specific.key_without_modifiers.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_modifierless_char(scancode: u16) -> Key {
|
||||
let mut string = [0; 16];
|
||||
let input_source;
|
||||
let layout;
|
||||
unsafe {
|
||||
input_source = ffi::TISCopyCurrentKeyboardLayoutInputSource();
|
||||
if input_source.is_null() {
|
||||
log::error!("`TISCopyCurrentKeyboardLayoutInputSource` returned null ptr");
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
let layout_data =
|
||||
ffi::TISGetInputSourceProperty(input_source, ffi::kTISPropertyUnicodeKeyLayoutData);
|
||||
if layout_data.is_null() {
|
||||
CFRelease(input_source as *mut c_void);
|
||||
log::error!("`TISGetInputSourceProperty` returned null ptr");
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
layout = CFDataGetBytePtr(layout_data as CFDataRef) as *const ffi::UCKeyboardLayout;
|
||||
}
|
||||
let keyboard_type = MainThreadMarker::run_on_main(|_mtm| unsafe { ffi::LMGetKbdType() });
|
||||
|
||||
let mut result_len = 0;
|
||||
let mut dead_keys = 0;
|
||||
let modifiers = 0;
|
||||
let translate_result = unsafe {
|
||||
ffi::UCKeyTranslate(
|
||||
layout,
|
||||
scancode,
|
||||
ffi::kUCKeyActionDisplay,
|
||||
modifiers,
|
||||
keyboard_type as u32,
|
||||
ffi::kUCKeyTranslateNoDeadKeysMask,
|
||||
&mut dead_keys,
|
||||
string.len() as ffi::UniCharCount,
|
||||
&mut result_len,
|
||||
string.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
CFRelease(input_source as *mut c_void);
|
||||
}
|
||||
if translate_result != 0 {
|
||||
log::error!(
|
||||
"`UCKeyTranslate` returned with the non-zero value: {}",
|
||||
translate_result
|
||||
);
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
if result_len == 0 {
|
||||
// This is fine - not all keys have text representation.
|
||||
// For instance, users that have mapped the `Fn` key to toggle
|
||||
// keyboard layouts will hit this code path.
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
let chars = String::from_utf16_lossy(&string[0..result_len as usize]);
|
||||
Key::Character(SmolStr::new(chars))
|
||||
}
|
||||
|
||||
fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
|
||||
let string = ns_event
|
||||
.charactersIgnoringModifiers()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_default();
|
||||
if string.is_empty() {
|
||||
// Probably a dead key
|
||||
let first_char = modifierless_chars.chars().next();
|
||||
return Key::Dead(first_char);
|
||||
}
|
||||
Key::Character(SmolStr::new(string))
|
||||
}
|
||||
|
||||
/// Create `KeyEvent` for the given `NSEvent`.
|
||||
///
|
||||
/// This function shouldn't be called when the IME input is in process.
|
||||
pub(crate) fn create_key_event(
|
||||
ns_event: &NSEvent,
|
||||
is_press: bool,
|
||||
is_repeat: bool,
|
||||
key_override: Option<PhysicalKey>,
|
||||
) -> KeyEvent {
|
||||
use ElementState::{Pressed, Released};
|
||||
let state = if is_press { Pressed } else { Released };
|
||||
|
||||
let scancode = ns_event.key_code();
|
||||
let mut physical_key =
|
||||
key_override.unwrap_or_else(|| PhysicalKey::from_scancode(scancode as u32));
|
||||
|
||||
let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() {
|
||||
None
|
||||
} else {
|
||||
let characters = ns_event
|
||||
.characters()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_default();
|
||||
if characters.is_empty() {
|
||||
None
|
||||
} else {
|
||||
if matches!(physical_key, PhysicalKey::Unidentified(_)) {
|
||||
// The key may be one of the funky function keys
|
||||
physical_key = extra_function_key_to_code(scancode, &characters);
|
||||
}
|
||||
Some(SmolStr::new(characters))
|
||||
}
|
||||
};
|
||||
|
||||
let key_from_code = code_to_key(physical_key, scancode);
|
||||
let (logical_key, key_without_modifiers) = if matches!(key_from_code, Key::Unidentified(_)) {
|
||||
let key_without_modifiers = get_modifierless_char(scancode);
|
||||
|
||||
let modifiers = NSEvent::modifierFlags(ns_event);
|
||||
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
|
||||
|
||||
let logical_key = match text_with_all_modifiers.as_ref() {
|
||||
// Only checking for ctrl here, not checking for alt because we DO want to
|
||||
// include its effect in the key. For example if -on the Germay layout- one
|
||||
// presses alt+8, the logical key should be "{"
|
||||
// Also not checking if this is a release event because then this issue would
|
||||
// still affect the key release.
|
||||
Some(text) if !has_ctrl => Key::Character(text.clone()),
|
||||
_ => match key_without_modifiers.as_ref() {
|
||||
Key::Character(ch) => get_logical_key_char(ns_event, ch),
|
||||
// Don't try to get text for events which likely don't have it.
|
||||
_ => key_without_modifiers.clone(),
|
||||
},
|
||||
};
|
||||
|
||||
(logical_key, key_without_modifiers)
|
||||
} else {
|
||||
(key_from_code.clone(), key_from_code)
|
||||
};
|
||||
|
||||
let text = if is_press {
|
||||
logical_key.to_text().map(SmolStr::new)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let location = code_to_location(physical_key);
|
||||
|
||||
KeyEvent {
|
||||
location,
|
||||
logical_key,
|
||||
physical_key,
|
||||
repeat: is_repeat,
|
||||
state,
|
||||
text,
|
||||
platform_specific: KeyEventExtra {
|
||||
key_without_modifiers,
|
||||
text_with_all_modifiers,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code_to_key(key: PhysicalKey, scancode: u16) -> Key {
|
||||
let code = match key {
|
||||
PhysicalKey::Code(code) => code,
|
||||
PhysicalKey::Unidentified(code) => return Key::Unidentified(code.into()),
|
||||
};
|
||||
|
||||
Key::Named(match code {
|
||||
KeyCode::Enter => NamedKey::Enter,
|
||||
KeyCode::Tab => NamedKey::Tab,
|
||||
KeyCode::Space => NamedKey::Space,
|
||||
KeyCode::Backspace => NamedKey::Backspace,
|
||||
KeyCode::Escape => NamedKey::Escape,
|
||||
KeyCode::SuperRight => NamedKey::Super,
|
||||
KeyCode::SuperLeft => NamedKey::Super,
|
||||
KeyCode::ShiftLeft => NamedKey::Shift,
|
||||
KeyCode::AltLeft => NamedKey::Alt,
|
||||
KeyCode::ControlLeft => NamedKey::Control,
|
||||
KeyCode::ShiftRight => NamedKey::Shift,
|
||||
KeyCode::AltRight => NamedKey::Alt,
|
||||
KeyCode::ControlRight => NamedKey::Control,
|
||||
|
||||
KeyCode::NumLock => NamedKey::NumLock,
|
||||
KeyCode::AudioVolumeUp => NamedKey::AudioVolumeUp,
|
||||
KeyCode::AudioVolumeDown => NamedKey::AudioVolumeDown,
|
||||
|
||||
// Other numpad keys all generate text on macOS (if I understand correctly)
|
||||
KeyCode::NumpadEnter => NamedKey::Enter,
|
||||
|
||||
KeyCode::F1 => NamedKey::F1,
|
||||
KeyCode::F2 => NamedKey::F2,
|
||||
KeyCode::F3 => NamedKey::F3,
|
||||
KeyCode::F4 => NamedKey::F4,
|
||||
KeyCode::F5 => NamedKey::F5,
|
||||
KeyCode::F6 => NamedKey::F6,
|
||||
KeyCode::F7 => NamedKey::F7,
|
||||
KeyCode::F8 => NamedKey::F8,
|
||||
KeyCode::F9 => NamedKey::F9,
|
||||
KeyCode::F10 => NamedKey::F10,
|
||||
KeyCode::F11 => NamedKey::F11,
|
||||
KeyCode::F12 => NamedKey::F12,
|
||||
KeyCode::F13 => NamedKey::F13,
|
||||
KeyCode::F14 => NamedKey::F14,
|
||||
KeyCode::F15 => NamedKey::F15,
|
||||
KeyCode::F16 => NamedKey::F16,
|
||||
KeyCode::F17 => NamedKey::F17,
|
||||
KeyCode::F18 => NamedKey::F18,
|
||||
KeyCode::F19 => NamedKey::F19,
|
||||
KeyCode::F20 => NamedKey::F20,
|
||||
|
||||
KeyCode::Insert => NamedKey::Insert,
|
||||
KeyCode::Home => NamedKey::Home,
|
||||
KeyCode::PageUp => NamedKey::PageUp,
|
||||
KeyCode::Delete => NamedKey::Delete,
|
||||
KeyCode::End => NamedKey::End,
|
||||
KeyCode::PageDown => NamedKey::PageDown,
|
||||
KeyCode::ArrowLeft => NamedKey::ArrowLeft,
|
||||
KeyCode::ArrowRight => NamedKey::ArrowRight,
|
||||
KeyCode::ArrowDown => NamedKey::ArrowDown,
|
||||
KeyCode::ArrowUp => NamedKey::ArrowUp,
|
||||
_ => return Key::Unidentified(NativeKey::MacOS(scancode)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn code_to_location(key: PhysicalKey) -> KeyLocation {
|
||||
let code = match key {
|
||||
PhysicalKey::Code(code) => code,
|
||||
PhysicalKey::Unidentified(_) => return KeyLocation::Standard,
|
||||
};
|
||||
|
||||
match code {
|
||||
KeyCode::SuperRight => KeyLocation::Right,
|
||||
KeyCode::SuperLeft => KeyLocation::Left,
|
||||
KeyCode::ShiftLeft => KeyLocation::Left,
|
||||
KeyCode::AltLeft => KeyLocation::Left,
|
||||
KeyCode::ControlLeft => KeyLocation::Left,
|
||||
KeyCode::ShiftRight => KeyLocation::Right,
|
||||
KeyCode::AltRight => KeyLocation::Right,
|
||||
KeyCode::ControlRight => KeyLocation::Right,
|
||||
|
||||
KeyCode::NumLock => KeyLocation::Numpad,
|
||||
KeyCode::NumpadDecimal => KeyLocation::Numpad,
|
||||
KeyCode::NumpadMultiply => KeyLocation::Numpad,
|
||||
KeyCode::NumpadAdd => KeyLocation::Numpad,
|
||||
KeyCode::NumpadDivide => KeyLocation::Numpad,
|
||||
KeyCode::NumpadEnter => KeyLocation::Numpad,
|
||||
KeyCode::NumpadSubtract => KeyLocation::Numpad,
|
||||
KeyCode::NumpadEqual => KeyLocation::Numpad,
|
||||
KeyCode::Numpad0 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad1 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad2 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad3 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad4 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad5 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad6 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad7 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad8 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad9 => KeyLocation::Numpad,
|
||||
|
||||
_ => KeyLocation::Standard,
|
||||
}
|
||||
}
|
||||
|
||||
// While F1-F20 have scancodes we can match on, we have to check against UTF-16
|
||||
// constants for the rest.
|
||||
// https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?preferredLanguage=occ
|
||||
pub fn extra_function_key_to_code(scancode: u16, string: &str) -> PhysicalKey {
|
||||
if let Some(ch) = string.encode_utf16().next() {
|
||||
match ch {
|
||||
0xf718 => PhysicalKey::Code(KeyCode::F21),
|
||||
0xf719 => PhysicalKey::Code(KeyCode::F22),
|
||||
0xf71a => PhysicalKey::Code(KeyCode::F23),
|
||||
0xf71b => PhysicalKey::Code(KeyCode::F24),
|
||||
_ => PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode)),
|
||||
}
|
||||
} else {
|
||||
PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
|
||||
let flags = event.modifierFlags();
|
||||
let mut state = ModifiersState::empty();
|
||||
let mut pressed_mods = ModifiersKeys::empty();
|
||||
|
||||
state.set(
|
||||
ModifiersState::SHIFT,
|
||||
flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||
);
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LSHIFT, event.lshift_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RSHIFT, event.rshift_pressed());
|
||||
|
||||
state.set(
|
||||
ModifiersState::CONTROL,
|
||||
flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||
);
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LCONTROL, event.lctrl_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RCONTROL, event.rctrl_pressed());
|
||||
|
||||
state.set(
|
||||
ModifiersState::ALT,
|
||||
flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||
);
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LALT, event.lalt_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RALT, event.ralt_pressed());
|
||||
|
||||
state.set(
|
||||
ModifiersState::SUPER,
|
||||
flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||
);
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LSUPER, event.lcmd_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RSUPER, event.rcmd_pressed());
|
||||
|
||||
Modifiers {
|
||||
state,
|
||||
pressed_mods,
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalKeyExtScancode for PhysicalKey {
|
||||
fn to_scancode(self) -> Option<u32> {
|
||||
let code = match self {
|
||||
PhysicalKey::Code(code) => code,
|
||||
PhysicalKey::Unidentified(_) => return None,
|
||||
};
|
||||
|
||||
match code {
|
||||
KeyCode::KeyA => Some(0x00),
|
||||
KeyCode::KeyS => Some(0x01),
|
||||
KeyCode::KeyD => Some(0x02),
|
||||
KeyCode::KeyF => Some(0x03),
|
||||
KeyCode::KeyH => Some(0x04),
|
||||
KeyCode::KeyG => Some(0x05),
|
||||
KeyCode::KeyZ => Some(0x06),
|
||||
KeyCode::KeyX => Some(0x07),
|
||||
KeyCode::KeyC => Some(0x08),
|
||||
KeyCode::KeyV => Some(0x09),
|
||||
KeyCode::KeyB => Some(0x0b),
|
||||
KeyCode::KeyQ => Some(0x0c),
|
||||
KeyCode::KeyW => Some(0x0d),
|
||||
KeyCode::KeyE => Some(0x0e),
|
||||
KeyCode::KeyR => Some(0x0f),
|
||||
KeyCode::KeyY => Some(0x10),
|
||||
KeyCode::KeyT => Some(0x11),
|
||||
KeyCode::Digit1 => Some(0x12),
|
||||
KeyCode::Digit2 => Some(0x13),
|
||||
KeyCode::Digit3 => Some(0x14),
|
||||
KeyCode::Digit4 => Some(0x15),
|
||||
KeyCode::Digit6 => Some(0x16),
|
||||
KeyCode::Digit5 => Some(0x17),
|
||||
KeyCode::Equal => Some(0x18),
|
||||
KeyCode::Digit9 => Some(0x19),
|
||||
KeyCode::Digit7 => Some(0x1a),
|
||||
KeyCode::Minus => Some(0x1b),
|
||||
KeyCode::Digit8 => Some(0x1c),
|
||||
KeyCode::Digit0 => Some(0x1d),
|
||||
KeyCode::BracketRight => Some(0x1e),
|
||||
KeyCode::KeyO => Some(0x1f),
|
||||
KeyCode::KeyU => Some(0x20),
|
||||
KeyCode::BracketLeft => Some(0x21),
|
||||
KeyCode::KeyI => Some(0x22),
|
||||
KeyCode::KeyP => Some(0x23),
|
||||
KeyCode::Enter => Some(0x24),
|
||||
KeyCode::KeyL => Some(0x25),
|
||||
KeyCode::KeyJ => Some(0x26),
|
||||
KeyCode::Quote => Some(0x27),
|
||||
KeyCode::KeyK => Some(0x28),
|
||||
KeyCode::Semicolon => Some(0x29),
|
||||
KeyCode::Backslash => Some(0x2a),
|
||||
KeyCode::Comma => Some(0x2b),
|
||||
KeyCode::Slash => Some(0x2c),
|
||||
KeyCode::KeyN => Some(0x2d),
|
||||
KeyCode::KeyM => Some(0x2e),
|
||||
KeyCode::Period => Some(0x2f),
|
||||
KeyCode::Tab => Some(0x30),
|
||||
KeyCode::Space => Some(0x31),
|
||||
KeyCode::Backquote => Some(0x32),
|
||||
KeyCode::Backspace => Some(0x33),
|
||||
KeyCode::Escape => Some(0x35),
|
||||
KeyCode::SuperRight => Some(0x36),
|
||||
KeyCode::SuperLeft => Some(0x37),
|
||||
KeyCode::ShiftLeft => Some(0x38),
|
||||
KeyCode::AltLeft => Some(0x3a),
|
||||
KeyCode::ControlLeft => Some(0x3b),
|
||||
KeyCode::ShiftRight => Some(0x3c),
|
||||
KeyCode::AltRight => Some(0x3d),
|
||||
KeyCode::ControlRight => Some(0x3e),
|
||||
KeyCode::F17 => Some(0x40),
|
||||
KeyCode::NumpadDecimal => Some(0x41),
|
||||
KeyCode::NumpadMultiply => Some(0x43),
|
||||
KeyCode::NumpadAdd => Some(0x45),
|
||||
KeyCode::NumLock => Some(0x47),
|
||||
KeyCode::AudioVolumeUp => Some(0x49),
|
||||
KeyCode::AudioVolumeDown => Some(0x4a),
|
||||
KeyCode::NumpadDivide => Some(0x4b),
|
||||
KeyCode::NumpadEnter => Some(0x4c),
|
||||
KeyCode::NumpadSubtract => Some(0x4e),
|
||||
KeyCode::F18 => Some(0x4f),
|
||||
KeyCode::F19 => Some(0x50),
|
||||
KeyCode::NumpadEqual => Some(0x51),
|
||||
KeyCode::Numpad0 => Some(0x52),
|
||||
KeyCode::Numpad1 => Some(0x53),
|
||||
KeyCode::Numpad2 => Some(0x54),
|
||||
KeyCode::Numpad3 => Some(0x55),
|
||||
KeyCode::Numpad4 => Some(0x56),
|
||||
KeyCode::Numpad5 => Some(0x57),
|
||||
KeyCode::Numpad6 => Some(0x58),
|
||||
KeyCode::Numpad7 => Some(0x59),
|
||||
KeyCode::F20 => Some(0x5a),
|
||||
KeyCode::Numpad8 => Some(0x5b),
|
||||
KeyCode::Numpad9 => Some(0x5c),
|
||||
KeyCode::IntlYen => Some(0x5d),
|
||||
KeyCode::F5 => Some(0x60),
|
||||
KeyCode::F6 => Some(0x61),
|
||||
KeyCode::F7 => Some(0x62),
|
||||
KeyCode::F3 => Some(0x63),
|
||||
KeyCode::F8 => Some(0x64),
|
||||
KeyCode::F9 => Some(0x65),
|
||||
KeyCode::F11 => Some(0x67),
|
||||
KeyCode::F13 => Some(0x69),
|
||||
KeyCode::F16 => Some(0x6a),
|
||||
KeyCode::F14 => Some(0x6b),
|
||||
KeyCode::F10 => Some(0x6d),
|
||||
KeyCode::F12 => Some(0x6f),
|
||||
KeyCode::F15 => Some(0x71),
|
||||
KeyCode::Insert => Some(0x72),
|
||||
KeyCode::Home => Some(0x73),
|
||||
KeyCode::PageUp => Some(0x74),
|
||||
KeyCode::Delete => Some(0x75),
|
||||
KeyCode::F4 => Some(0x76),
|
||||
KeyCode::End => Some(0x77),
|
||||
KeyCode::F2 => Some(0x78),
|
||||
KeyCode::PageDown => Some(0x79),
|
||||
KeyCode::F1 => Some(0x7a),
|
||||
KeyCode::ArrowLeft => Some(0x7b),
|
||||
KeyCode::ArrowRight => Some(0x7c),
|
||||
KeyCode::ArrowDown => Some(0x7d),
|
||||
KeyCode::ArrowUp => Some(0x7e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_scancode(scancode: u32) -> PhysicalKey {
|
||||
PhysicalKey::Code(match scancode {
|
||||
0x00 => KeyCode::KeyA,
|
||||
0x01 => KeyCode::KeyS,
|
||||
0x02 => KeyCode::KeyD,
|
||||
0x03 => KeyCode::KeyF,
|
||||
0x04 => KeyCode::KeyH,
|
||||
0x05 => KeyCode::KeyG,
|
||||
0x06 => KeyCode::KeyZ,
|
||||
0x07 => KeyCode::KeyX,
|
||||
0x08 => KeyCode::KeyC,
|
||||
0x09 => KeyCode::KeyV,
|
||||
//0x0a => World 1,
|
||||
0x0b => KeyCode::KeyB,
|
||||
0x0c => KeyCode::KeyQ,
|
||||
0x0d => KeyCode::KeyW,
|
||||
0x0e => KeyCode::KeyE,
|
||||
0x0f => KeyCode::KeyR,
|
||||
0x10 => KeyCode::KeyY,
|
||||
0x11 => KeyCode::KeyT,
|
||||
0x12 => KeyCode::Digit1,
|
||||
0x13 => KeyCode::Digit2,
|
||||
0x14 => KeyCode::Digit3,
|
||||
0x15 => KeyCode::Digit4,
|
||||
0x16 => KeyCode::Digit6,
|
||||
0x17 => KeyCode::Digit5,
|
||||
0x18 => KeyCode::Equal,
|
||||
0x19 => KeyCode::Digit9,
|
||||
0x1a => KeyCode::Digit7,
|
||||
0x1b => KeyCode::Minus,
|
||||
0x1c => KeyCode::Digit8,
|
||||
0x1d => KeyCode::Digit0,
|
||||
0x1e => KeyCode::BracketRight,
|
||||
0x1f => KeyCode::KeyO,
|
||||
0x20 => KeyCode::KeyU,
|
||||
0x21 => KeyCode::BracketLeft,
|
||||
0x22 => KeyCode::KeyI,
|
||||
0x23 => KeyCode::KeyP,
|
||||
0x24 => KeyCode::Enter,
|
||||
0x25 => KeyCode::KeyL,
|
||||
0x26 => KeyCode::KeyJ,
|
||||
0x27 => KeyCode::Quote,
|
||||
0x28 => KeyCode::KeyK,
|
||||
0x29 => KeyCode::Semicolon,
|
||||
0x2a => KeyCode::Backslash,
|
||||
0x2b => KeyCode::Comma,
|
||||
0x2c => KeyCode::Slash,
|
||||
0x2d => KeyCode::KeyN,
|
||||
0x2e => KeyCode::KeyM,
|
||||
0x2f => KeyCode::Period,
|
||||
0x30 => KeyCode::Tab,
|
||||
0x31 => KeyCode::Space,
|
||||
0x32 => KeyCode::Backquote,
|
||||
0x33 => KeyCode::Backspace,
|
||||
//0x34 => unknown,
|
||||
0x35 => KeyCode::Escape,
|
||||
0x36 => KeyCode::SuperRight,
|
||||
0x37 => KeyCode::SuperLeft,
|
||||
0x38 => KeyCode::ShiftLeft,
|
||||
0x39 => KeyCode::CapsLock,
|
||||
0x3a => KeyCode::AltLeft,
|
||||
0x3b => KeyCode::ControlLeft,
|
||||
0x3c => KeyCode::ShiftRight,
|
||||
0x3d => KeyCode::AltRight,
|
||||
0x3e => KeyCode::ControlRight,
|
||||
0x3f => KeyCode::Fn,
|
||||
0x40 => KeyCode::F17,
|
||||
0x41 => KeyCode::NumpadDecimal,
|
||||
//0x42 -> unknown,
|
||||
0x43 => KeyCode::NumpadMultiply,
|
||||
//0x44 => unknown,
|
||||
0x45 => KeyCode::NumpadAdd,
|
||||
//0x46 => unknown,
|
||||
0x47 => KeyCode::NumLock,
|
||||
//0x48 => KeyCode::NumpadClear,
|
||||
|
||||
// TODO: (Artur) for me, kVK_VolumeUp is 0x48
|
||||
// macOS 10.11
|
||||
// /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
|
||||
0x49 => KeyCode::AudioVolumeUp,
|
||||
0x4a => KeyCode::AudioVolumeDown,
|
||||
0x4b => KeyCode::NumpadDivide,
|
||||
0x4c => KeyCode::NumpadEnter,
|
||||
//0x4d => unknown,
|
||||
0x4e => KeyCode::NumpadSubtract,
|
||||
0x4f => KeyCode::F18,
|
||||
0x50 => KeyCode::F19,
|
||||
0x51 => KeyCode::NumpadEqual,
|
||||
0x52 => KeyCode::Numpad0,
|
||||
0x53 => KeyCode::Numpad1,
|
||||
0x54 => KeyCode::Numpad2,
|
||||
0x55 => KeyCode::Numpad3,
|
||||
0x56 => KeyCode::Numpad4,
|
||||
0x57 => KeyCode::Numpad5,
|
||||
0x58 => KeyCode::Numpad6,
|
||||
0x59 => KeyCode::Numpad7,
|
||||
0x5a => KeyCode::F20,
|
||||
0x5b => KeyCode::Numpad8,
|
||||
0x5c => KeyCode::Numpad9,
|
||||
0x5d => KeyCode::IntlYen,
|
||||
//0x5e => JIS Ro,
|
||||
//0x5f => unknown,
|
||||
0x60 => KeyCode::F5,
|
||||
0x61 => KeyCode::F6,
|
||||
0x62 => KeyCode::F7,
|
||||
0x63 => KeyCode::F3,
|
||||
0x64 => KeyCode::F8,
|
||||
0x65 => KeyCode::F9,
|
||||
//0x66 => JIS Eisuu (macOS),
|
||||
0x67 => KeyCode::F11,
|
||||
//0x68 => JIS Kanna (macOS),
|
||||
0x69 => KeyCode::F13,
|
||||
0x6a => KeyCode::F16,
|
||||
0x6b => KeyCode::F14,
|
||||
//0x6c => unknown,
|
||||
0x6d => KeyCode::F10,
|
||||
//0x6e => unknown,
|
||||
0x6f => KeyCode::F12,
|
||||
//0x70 => unknown,
|
||||
0x71 => KeyCode::F15,
|
||||
0x72 => KeyCode::Insert,
|
||||
0x73 => KeyCode::Home,
|
||||
0x74 => KeyCode::PageUp,
|
||||
0x75 => KeyCode::Delete,
|
||||
0x76 => KeyCode::F4,
|
||||
0x77 => KeyCode::End,
|
||||
0x78 => KeyCode::F2,
|
||||
0x79 => KeyCode::PageDown,
|
||||
0x7a => KeyCode::F1,
|
||||
0x7b => KeyCode::ArrowLeft,
|
||||
0x7c => KeyCode::ArrowRight,
|
||||
0x7d => KeyCode::ArrowDown,
|
||||
0x7e => KeyCode::ArrowUp,
|
||||
//0x7f => unknown,
|
||||
|
||||
// 0xA is the caret (^) an macOS's German QERTZ layout. This key is at the same location as
|
||||
// backquote (`) on Windows' US layout.
|
||||
0xa => KeyCode::Backquote,
|
||||
_ => return PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode as u16)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
use core_graphics::display::CGDisplay;
|
||||
use icrate::Foundation::{CGFloat, NSNotFound, NSPoint, NSRange, NSRect, NSUInteger};
|
||||
|
||||
use crate::dpi::LogicalPosition;
|
||||
|
||||
// Replace with `!` once stable
|
||||
#[derive(Debug)]
|
||||
pub enum Never {}
|
||||
|
||||
pub const EMPTY_RANGE: NSRange = NSRange {
|
||||
location: NSNotFound as NSUInteger,
|
||||
length: 0,
|
||||
};
|
||||
|
||||
macro_rules! trace_scope {
|
||||
($s:literal) => {
|
||||
let _crate = $crate::platform_impl::platform::util::TraceGuard::new(module_path!(), $s);
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) struct TraceGuard {
|
||||
module_path: &'static str,
|
||||
called_from_fn: &'static str,
|
||||
}
|
||||
|
||||
impl TraceGuard {
|
||||
#[inline]
|
||||
pub(crate) fn new(module_path: &'static str, called_from_fn: &'static str) -> Self {
|
||||
trace!(target: module_path, "Triggered `{}`", called_from_fn);
|
||||
Self {
|
||||
module_path,
|
||||
called_from_fn,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TraceGuard {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
trace!(target: self.module_path, "Completed `{}`", self.called_from_fn);
|
||||
}
|
||||
}
|
||||
|
||||
// For consistency with other platforms, this will...
|
||||
// 1. translate the bottom-left window corner into the top-left window corner
|
||||
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
|
||||
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height) as f64
|
||||
}
|
||||
|
||||
/// Converts from winit screen-coordinates to macOS screen-coordinates.
|
||||
/// Winit: top-left is (0, 0) and y increasing downwards
|
||||
/// macOS: bottom-left is (0, 0) and y increasing upwards
|
||||
pub fn window_position(position: LogicalPosition<f64>) -> NSPoint {
|
||||
NSPoint::new(
|
||||
position.x as CGFloat,
|
||||
CGDisplay::main().pixels_high() as CGFloat - position.y as CGFloat,
|
||||
)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,483 +0,0 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
use std::cell::Cell;
|
||||
use std::ptr::{self, NonNull};
|
||||
|
||||
use icrate::Foundation::{NSArray, NSObject, NSSize, NSString};
|
||||
use objc2::declare::{Ivar, IvarDrop};
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType};
|
||||
|
||||
use super::appkit::{
|
||||
NSApplicationPresentationOptions, NSFilenamesPboardType, NSPasteboard, NSWindowOcclusionState,
|
||||
};
|
||||
use super::{
|
||||
app_state::AppState,
|
||||
util,
|
||||
window::{get_ns_theme, WinitWindow},
|
||||
Fullscreen,
|
||||
};
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
event::{Event, WindowEvent},
|
||||
window::WindowId,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct State {
|
||||
// This is set when WindowBuilder::with_fullscreen was set,
|
||||
// see comments of `window_did_fail_to_enter_fullscreen`
|
||||
initial_fullscreen: Cell<bool>,
|
||||
|
||||
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
||||
previous_position: Cell<Option<(f64, f64)>>,
|
||||
|
||||
// Used to prevent redundant events.
|
||||
previous_scale_factor: Cell<f64>,
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct WinitWindowDelegate {
|
||||
window: IvarDrop<Id<WinitWindow>, "_window">,
|
||||
|
||||
// TODO: It may be possible for delegate methods to be called
|
||||
// asynchronously, causing data races panics?
|
||||
// TODO: Remove unnecessary boxing here
|
||||
state: IvarDrop<Box<State>, "_state">,
|
||||
}
|
||||
|
||||
mod ivars;
|
||||
|
||||
unsafe impl ClassType for WinitWindowDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
const NAME: &'static str = "WinitWindowDelegate";
|
||||
}
|
||||
|
||||
unsafe impl WinitWindowDelegate {
|
||||
#[method(initWithWindow:initialFullscreen:)]
|
||||
unsafe fn init_with_winit(
|
||||
this: *mut Self,
|
||||
window: &WinitWindow,
|
||||
initial_fullscreen: bool,
|
||||
) -> Option<NonNull<Self>> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![super(this), init] };
|
||||
this.map(|this| {
|
||||
let scale_factor = window.scale_factor();
|
||||
|
||||
Ivar::write(&mut this.window, window.retain());
|
||||
Ivar::write(
|
||||
&mut this.state,
|
||||
Box::new(State {
|
||||
initial_fullscreen: Cell::new(initial_fullscreen),
|
||||
previous_position: Cell::new(None),
|
||||
previous_scale_factor: Cell::new(scale_factor),
|
||||
}),
|
||||
);
|
||||
|
||||
if scale_factor != 1.0 {
|
||||
this.queue_static_scale_factor_changed_event();
|
||||
}
|
||||
this.window.setDelegate(Some(this));
|
||||
|
||||
// Enable theme change event
|
||||
let notification_center: Id<AnyObject> =
|
||||
unsafe { msg_send_id![class!(NSDistributedNotificationCenter), defaultCenter] };
|
||||
let notification_name =
|
||||
NSString::from_str("AppleInterfaceThemeChangedNotification");
|
||||
let _: () = unsafe {
|
||||
msg_send![
|
||||
¬ification_center,
|
||||
addObserver: &*this
|
||||
selector: sel!(effectiveAppearanceDidChange:)
|
||||
name: &*notification_name
|
||||
object: ptr::null::<AnyObject>()
|
||||
]
|
||||
};
|
||||
|
||||
NonNull::from(this)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// NSWindowDelegate + NSDraggingDestination protocols
|
||||
unsafe impl WinitWindowDelegate {
|
||||
#[method(windowShouldClose:)]
|
||||
fn window_should_close(&self, _: Option<&AnyObject>) -> bool {
|
||||
trace_scope!("windowShouldClose:");
|
||||
self.queue_event(WindowEvent::CloseRequested);
|
||||
false
|
||||
}
|
||||
|
||||
#[method(windowWillClose:)]
|
||||
fn window_will_close(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillClose:");
|
||||
// `setDelegate:` retains the previous value and then autoreleases it
|
||||
autoreleasepool(|_| {
|
||||
// Since El Capitan, we need to be careful that delegate methods can't
|
||||
// be called after the window closes.
|
||||
self.window.setDelegate(None);
|
||||
});
|
||||
self.queue_event(WindowEvent::Destroyed);
|
||||
}
|
||||
|
||||
#[method(windowDidResize:)]
|
||||
fn window_did_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidResize:");
|
||||
// NOTE: WindowEvent::Resized is reported in frameDidChange.
|
||||
self.emit_move_event();
|
||||
}
|
||||
|
||||
#[method(windowWillStartLiveResize:)]
|
||||
fn window_will_start_live_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillStartLiveResize:");
|
||||
|
||||
let increments = self
|
||||
.window
|
||||
.lock_shared_state("window_will_enter_fullscreen")
|
||||
.resize_increments;
|
||||
self.window.set_resize_increments_inner(increments);
|
||||
}
|
||||
|
||||
#[method(windowDidEndLiveResize:)]
|
||||
fn window_did_end_live_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidEndLiveResize:");
|
||||
self.window.set_resize_increments_inner(NSSize::new(1., 1.));
|
||||
}
|
||||
|
||||
// This won't be triggered if the move was part of a resize.
|
||||
#[method(windowDidMove:)]
|
||||
fn window_did_move(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidMove:");
|
||||
self.emit_move_event();
|
||||
}
|
||||
|
||||
#[method(windowDidChangeBackingProperties:)]
|
||||
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidChangeBackingProperties:");
|
||||
self.queue_static_scale_factor_changed_event();
|
||||
}
|
||||
|
||||
#[method(windowDidBecomeKey:)]
|
||||
fn window_did_become_key(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidBecomeKey:");
|
||||
// TODO: center the cursor if the window had mouse grab when it
|
||||
// lost focus
|
||||
self.queue_event(WindowEvent::Focused(true));
|
||||
}
|
||||
|
||||
#[method(windowDidResignKey:)]
|
||||
fn window_did_resign_key(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidResignKey:");
|
||||
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
|
||||
// NSWindowDelegate will receive a didResignKey event despite no event
|
||||
// being received when the modifiers are released. This is because
|
||||
// flagsChanged events are received by the NSView instead of the
|
||||
// NSWindowDelegate, and as a result a tracked modifiers state can quite
|
||||
// easily fall out of synchrony with reality. This requires us to emit
|
||||
// a synthetic ModifiersChanged event when we lose focus.
|
||||
self.window.view().reset_modifiers();
|
||||
|
||||
self.queue_event(WindowEvent::Focused(false));
|
||||
}
|
||||
|
||||
/// Invoked when the dragged image enters destination bounds or frame
|
||||
#[method(draggingEntered:)]
|
||||
fn dragging_entered(&self, sender: &NSObject) -> bool {
|
||||
trace_scope!("draggingEntered:");
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType });
|
||||
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
|
||||
|
||||
filenames.into_iter().for_each(|file| {
|
||||
let path = PathBuf::from(file.to_string());
|
||||
self.queue_event(WindowEvent::HoveredFile(path));
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked when the image is released
|
||||
#[method(prepareForDragOperation:)]
|
||||
fn prepare_for_drag_operation(&self, _sender: &NSObject) -> bool {
|
||||
trace_scope!("prepareForDragOperation:");
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked after the released image has been removed from the screen
|
||||
#[method(performDragOperation:)]
|
||||
fn perform_drag_operation(&self, sender: &NSObject) -> bool {
|
||||
trace_scope!("performDragOperation:");
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType });
|
||||
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
|
||||
|
||||
filenames.into_iter().for_each(|file| {
|
||||
let path = PathBuf::from(file.to_string());
|
||||
self.queue_event(WindowEvent::DroppedFile(path));
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked when the dragging operation is complete
|
||||
#[method(concludeDragOperation:)]
|
||||
fn conclude_drag_operation(&self, _sender: Option<&NSObject>) {
|
||||
trace_scope!("concludeDragOperation:");
|
||||
}
|
||||
|
||||
/// Invoked when the dragging operation is cancelled
|
||||
#[method(draggingExited:)]
|
||||
fn dragging_exited(&self, _sender: Option<&NSObject>) {
|
||||
trace_scope!("draggingExited:");
|
||||
self.queue_event(WindowEvent::HoveredFileCancelled);
|
||||
}
|
||||
|
||||
/// Invoked when before enter fullscreen
|
||||
#[method(windowWillEnterFullScreen:)]
|
||||
fn window_will_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillEnterFullScreen:");
|
||||
|
||||
let mut shared_state = self
|
||||
.window
|
||||
.lock_shared_state("window_will_enter_fullscreen");
|
||||
shared_state.maximized = self.window.is_zoomed();
|
||||
let fullscreen = shared_state.fullscreen.as_ref();
|
||||
match fullscreen {
|
||||
// Exclusive mode sets the state in `set_fullscreen` as the user
|
||||
// can't enter exclusive mode by other means (like the
|
||||
// fullscreen button on the window decorations)
|
||||
Some(Fullscreen::Exclusive(_)) => (),
|
||||
// `window_will_enter_fullscreen` was triggered and we're already
|
||||
// in fullscreen, so we must've reached here by `set_fullscreen`
|
||||
// as it updates the state
|
||||
Some(Fullscreen::Borderless(_)) => (),
|
||||
// Otherwise, we must've reached fullscreen by the user clicking
|
||||
// on the green fullscreen button. Update state!
|
||||
None => {
|
||||
let current_monitor = self.window.current_monitor_inner();
|
||||
shared_state.fullscreen = Some(Fullscreen::Borderless(current_monitor))
|
||||
}
|
||||
}
|
||||
shared_state.in_fullscreen_transition = true;
|
||||
}
|
||||
|
||||
/// Invoked when before exit fullscreen
|
||||
#[method(windowWillExitFullScreen:)]
|
||||
fn window_will_exit_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillExitFullScreen:");
|
||||
|
||||
let mut shared_state = self.window.lock_shared_state("window_will_exit_fullscreen");
|
||||
shared_state.in_fullscreen_transition = true;
|
||||
}
|
||||
|
||||
#[method(window:willUseFullScreenPresentationOptions:)]
|
||||
fn window_will_use_fullscreen_presentation_options(
|
||||
&self,
|
||||
_: Option<&AnyObject>,
|
||||
proposed_options: NSApplicationPresentationOptions,
|
||||
) -> NSApplicationPresentationOptions {
|
||||
trace_scope!("window:willUseFullScreenPresentationOptions:");
|
||||
// Generally, games will want to disable the menu bar and the dock. Ideally,
|
||||
// this would be configurable by the user. Unfortunately because of our
|
||||
// `CGShieldingWindowLevel() + 1` hack (see `set_fullscreen`), our window is
|
||||
// placed on top of the menu bar in exclusive fullscreen mode. This looks
|
||||
// broken so we always disable the menu bar in exclusive fullscreen. We may
|
||||
// still want to make this configurable for borderless fullscreen. Right now
|
||||
// we don't, for consistency. If we do, it should be documented that the
|
||||
// user-provided options are ignored in exclusive fullscreen.
|
||||
let mut options = proposed_options;
|
||||
let shared_state = self
|
||||
.window
|
||||
.lock_shared_state("window_will_use_fullscreen_presentation_options");
|
||||
if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen {
|
||||
options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||
}
|
||||
|
||||
options
|
||||
}
|
||||
|
||||
/// Invoked when entered fullscreen
|
||||
#[method(windowDidEnterFullScreen:)]
|
||||
fn window_did_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidEnterFullScreen:");
|
||||
self.state.initial_fullscreen.set(false);
|
||||
let mut shared_state = self.window.lock_shared_state("window_did_enter_fullscreen");
|
||||
shared_state.in_fullscreen_transition = false;
|
||||
let target_fullscreen = shared_state.target_fullscreen.take();
|
||||
drop(shared_state);
|
||||
if let Some(target_fullscreen) = target_fullscreen {
|
||||
self.window.set_fullscreen(target_fullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoked when exited fullscreen
|
||||
#[method(windowDidExitFullScreen:)]
|
||||
fn window_did_exit_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidExitFullScreen:");
|
||||
|
||||
self.window.restore_state_from_fullscreen();
|
||||
let mut shared_state = self.window.lock_shared_state("window_did_exit_fullscreen");
|
||||
shared_state.in_fullscreen_transition = false;
|
||||
let target_fullscreen = shared_state.target_fullscreen.take();
|
||||
drop(shared_state);
|
||||
if let Some(target_fullscreen) = target_fullscreen {
|
||||
self.window.set_fullscreen(target_fullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoked when fail to enter fullscreen
|
||||
///
|
||||
/// When this window launch from a fullscreen app (e.g. launch from VS Code
|
||||
/// terminal), it creates a new virtual destkop and a transition animation.
|
||||
/// This animation takes one second and cannot be disable without
|
||||
/// elevated privileges. In this animation time, all toggleFullscreen events
|
||||
/// will be failed. In this implementation, we will try again by using
|
||||
/// performSelector:withObject:afterDelay: until window_did_enter_fullscreen.
|
||||
/// It should be fine as we only do this at initialzation (i.e with_fullscreen
|
||||
/// was set).
|
||||
///
|
||||
/// From Apple doc:
|
||||
/// In some cases, the transition to enter full-screen mode can fail,
|
||||
/// due to being in the midst of handling some other animation or user gesture.
|
||||
/// This method indicates that there was an error, and you should clean up any
|
||||
/// work you may have done to prepare to enter full-screen mode.
|
||||
#[method(windowDidFailToEnterFullScreen:)]
|
||||
fn window_did_fail_to_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidFailToEnterFullScreen:");
|
||||
let mut shared_state = self
|
||||
.window
|
||||
.lock_shared_state("window_did_fail_to_enter_fullscreen");
|
||||
shared_state.in_fullscreen_transition = false;
|
||||
shared_state.target_fullscreen = None;
|
||||
if self.state.initial_fullscreen.get() {
|
||||
#[allow(clippy::let_unit_value)]
|
||||
unsafe {
|
||||
let _: () = msg_send![
|
||||
&*self.window,
|
||||
performSelector: sel!(toggleFullScreen:),
|
||||
withObject: ptr::null::<AnyObject>(),
|
||||
afterDelay: 0.5,
|
||||
];
|
||||
};
|
||||
} else {
|
||||
self.window.restore_state_from_fullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked when the occlusion state of the window changes
|
||||
#[method(windowDidChangeOcclusionState:)]
|
||||
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidChangeOcclusionState:");
|
||||
self.queue_event(WindowEvent::Occluded(
|
||||
!self
|
||||
.window
|
||||
.occlusionState()
|
||||
.contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible),
|
||||
))
|
||||
}
|
||||
|
||||
// Observe theme change
|
||||
#[method(effectiveAppearanceDidChange:)]
|
||||
fn effective_appearance_did_change(&self, sender: Option<&AnyObject>) {
|
||||
trace_scope!("Triggered `effectiveAppearanceDidChange:`");
|
||||
unsafe {
|
||||
msg_send![
|
||||
self,
|
||||
performSelectorOnMainThread: sel!(effectiveAppearanceDidChangedOnMainThread:),
|
||||
withObject: sender,
|
||||
waitUntilDone: false,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[method(effectiveAppearanceDidChangedOnMainThread:)]
|
||||
fn effective_appearance_did_changed_on_main_thread(&self, _: Option<&AnyObject>) {
|
||||
let theme = get_ns_theme();
|
||||
let mut shared_state = self
|
||||
.window
|
||||
.lock_shared_state("effective_appearance_did_change");
|
||||
let current_theme = shared_state.current_theme;
|
||||
shared_state.current_theme = Some(theme);
|
||||
drop(shared_state);
|
||||
if current_theme != Some(theme) {
|
||||
self.queue_event(WindowEvent::ThemeChanged(theme));
|
||||
}
|
||||
}
|
||||
|
||||
#[method(windowDidChangeScreen:)]
|
||||
fn window_did_change_screen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidChangeScreen:");
|
||||
let is_simple_fullscreen = self
|
||||
.window
|
||||
.lock_shared_state("window_did_change_screen")
|
||||
.is_simple_fullscreen;
|
||||
if is_simple_fullscreen {
|
||||
if let Some(screen) = self.window.screen() {
|
||||
self.window.setFrame_display(screen.frame(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl WinitWindowDelegate {
|
||||
pub fn new(window: &WinitWindow, initial_fullscreen: bool) -> Id<Self> {
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
Self::alloc(),
|
||||
initWithWindow: window,
|
||||
initialFullscreen: initial_fullscreen,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
||||
let event = Event::WindowEvent {
|
||||
window_id: WindowId(self.window.id()),
|
||||
event,
|
||||
};
|
||||
AppState::queue_event(event);
|
||||
}
|
||||
|
||||
fn queue_static_scale_factor_changed_event(&self) {
|
||||
let scale_factor = self.window.scale_factor();
|
||||
if scale_factor == self.state.previous_scale_factor.get() {
|
||||
return;
|
||||
};
|
||||
|
||||
self.state.previous_scale_factor.set(scale_factor);
|
||||
let suggested_size = self.view_size();
|
||||
AppState::queue_static_scale_factor_changed_event(
|
||||
self.window.clone(),
|
||||
suggested_size.to_physical(scale_factor),
|
||||
scale_factor,
|
||||
);
|
||||
}
|
||||
|
||||
fn emit_move_event(&self) {
|
||||
let rect = self.window.frame();
|
||||
let x = rect.origin.x as f64;
|
||||
let y = util::bottom_left_to_top_left(rect);
|
||||
if self.state.previous_position.get() != Some((x, y)) {
|
||||
self.state.previous_position.set(Some((x, y)));
|
||||
let scale_factor = self.window.scale_factor();
|
||||
let physical_pos = LogicalPosition::<f64>::from((x, y)).to_physical(scale_factor);
|
||||
self.queue_event(WindowEvent::Moved(physical_pos));
|
||||
}
|
||||
}
|
||||
|
||||
fn view_size(&self) -> LogicalSize<f64> {
|
||||
let size = self.window.contentView().frame().size;
|
||||
LogicalSize::new(size.width as f64, size.height as f64)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
mod channel;
|
||||
mod dispatcher;
|
||||
mod waker;
|
||||
mod wrapper;
|
||||
|
||||
use self::channel::{channel, AsyncReceiver, AsyncSender};
|
||||
pub use self::dispatcher::{DispatchRunner, Dispatcher};
|
||||
pub use self::waker::{Waker, WakerSpawner};
|
||||
use self::wrapper::Wrapper;
|
||||
@@ -1,8 +0,0 @@
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(pub i32);
|
||||
|
||||
impl DeviceId {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
@@ -1,522 +0,0 @@
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::keyboard::{Key, KeyCode, NamedKey, NativeKey, NativeKeyCode, PhysicalKey};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub(crate) struct KeyEventExtra;
|
||||
|
||||
impl Key {
|
||||
pub(crate) fn from_key_attribute_value(kav: &str) -> Self {
|
||||
Key::Named(match kav {
|
||||
"Unidentified" => return Key::Unidentified(NativeKey::Web(SmolStr::new(kav))),
|
||||
"Dead" => return Key::Dead(None),
|
||||
"Alt" => NamedKey::Alt,
|
||||
"AltGraph" => NamedKey::AltGraph,
|
||||
"CapsLock" => NamedKey::CapsLock,
|
||||
"Control" => NamedKey::Control,
|
||||
"Fn" => NamedKey::Fn,
|
||||
"FnLock" => NamedKey::FnLock,
|
||||
"NumLock" => NamedKey::NumLock,
|
||||
"ScrollLock" => NamedKey::ScrollLock,
|
||||
"Shift" => NamedKey::Shift,
|
||||
"Symbol" => NamedKey::Symbol,
|
||||
"SymbolLock" => NamedKey::SymbolLock,
|
||||
"Hyper" => NamedKey::Hyper,
|
||||
"Meta" => NamedKey::Super,
|
||||
"Enter" => NamedKey::Enter,
|
||||
"Tab" => NamedKey::Tab,
|
||||
" " => NamedKey::Space,
|
||||
"ArrowDown" => NamedKey::ArrowDown,
|
||||
"ArrowLeft" => NamedKey::ArrowLeft,
|
||||
"ArrowRight" => NamedKey::ArrowRight,
|
||||
"ArrowUp" => NamedKey::ArrowUp,
|
||||
"End" => NamedKey::End,
|
||||
"Home" => NamedKey::Home,
|
||||
"PageDown" => NamedKey::PageDown,
|
||||
"PageUp" => NamedKey::PageUp,
|
||||
"Backspace" => NamedKey::Backspace,
|
||||
"Clear" => NamedKey::Clear,
|
||||
"Copy" => NamedKey::Copy,
|
||||
"CrSel" => NamedKey::CrSel,
|
||||
"Cut" => NamedKey::Cut,
|
||||
"Delete" => NamedKey::Delete,
|
||||
"EraseEof" => NamedKey::EraseEof,
|
||||
"ExSel" => NamedKey::ExSel,
|
||||
"Insert" => NamedKey::Insert,
|
||||
"Paste" => NamedKey::Paste,
|
||||
"Redo" => NamedKey::Redo,
|
||||
"Undo" => NamedKey::Undo,
|
||||
"Accept" => NamedKey::Accept,
|
||||
"Again" => NamedKey::Again,
|
||||
"Attn" => NamedKey::Attn,
|
||||
"Cancel" => NamedKey::Cancel,
|
||||
"ContextMenu" => NamedKey::ContextMenu,
|
||||
"Escape" => NamedKey::Escape,
|
||||
"Execute" => NamedKey::Execute,
|
||||
"Find" => NamedKey::Find,
|
||||
"Help" => NamedKey::Help,
|
||||
"Pause" => NamedKey::Pause,
|
||||
"Play" => NamedKey::Play,
|
||||
"Props" => NamedKey::Props,
|
||||
"Select" => NamedKey::Select,
|
||||
"ZoomIn" => NamedKey::ZoomIn,
|
||||
"ZoomOut" => NamedKey::ZoomOut,
|
||||
"BrightnessDown" => NamedKey::BrightnessDown,
|
||||
"BrightnessUp" => NamedKey::BrightnessUp,
|
||||
"Eject" => NamedKey::Eject,
|
||||
"LogOff" => NamedKey::LogOff,
|
||||
"Power" => NamedKey::Power,
|
||||
"PowerOff" => NamedKey::PowerOff,
|
||||
"PrintScreen" => NamedKey::PrintScreen,
|
||||
"Hibernate" => NamedKey::Hibernate,
|
||||
"Standby" => NamedKey::Standby,
|
||||
"WakeUp" => NamedKey::WakeUp,
|
||||
"AllCandidates" => NamedKey::AllCandidates,
|
||||
"Alphanumeric" => NamedKey::Alphanumeric,
|
||||
"CodeInput" => NamedKey::CodeInput,
|
||||
"Compose" => NamedKey::Compose,
|
||||
"Convert" => NamedKey::Convert,
|
||||
"FinalMode" => NamedKey::FinalMode,
|
||||
"GroupFirst" => NamedKey::GroupFirst,
|
||||
"GroupLast" => NamedKey::GroupLast,
|
||||
"GroupNext" => NamedKey::GroupNext,
|
||||
"GroupPrevious" => NamedKey::GroupPrevious,
|
||||
"ModeChange" => NamedKey::ModeChange,
|
||||
"NextCandidate" => NamedKey::NextCandidate,
|
||||
"NonConvert" => NamedKey::NonConvert,
|
||||
"PreviousCandidate" => NamedKey::PreviousCandidate,
|
||||
"Process" => NamedKey::Process,
|
||||
"SingleCandidate" => NamedKey::SingleCandidate,
|
||||
"HangulMode" => NamedKey::HangulMode,
|
||||
"HanjaMode" => NamedKey::HanjaMode,
|
||||
"JunjaMode" => NamedKey::JunjaMode,
|
||||
"Eisu" => NamedKey::Eisu,
|
||||
"Hankaku" => NamedKey::Hankaku,
|
||||
"Hiragana" => NamedKey::Hiragana,
|
||||
"HiraganaKatakana" => NamedKey::HiraganaKatakana,
|
||||
"KanaMode" => NamedKey::KanaMode,
|
||||
"KanjiMode" => NamedKey::KanjiMode,
|
||||
"Katakana" => NamedKey::Katakana,
|
||||
"Romaji" => NamedKey::Romaji,
|
||||
"Zenkaku" => NamedKey::Zenkaku,
|
||||
"ZenkakuHankaku" => NamedKey::ZenkakuHankaku,
|
||||
"Soft1" => NamedKey::Soft1,
|
||||
"Soft2" => NamedKey::Soft2,
|
||||
"Soft3" => NamedKey::Soft3,
|
||||
"Soft4" => NamedKey::Soft4,
|
||||
"ChannelDown" => NamedKey::ChannelDown,
|
||||
"ChannelUp" => NamedKey::ChannelUp,
|
||||
"Close" => NamedKey::Close,
|
||||
"MailForward" => NamedKey::MailForward,
|
||||
"MailReply" => NamedKey::MailReply,
|
||||
"MailSend" => NamedKey::MailSend,
|
||||
"MediaClose" => NamedKey::MediaClose,
|
||||
"MediaFastForward" => NamedKey::MediaFastForward,
|
||||
"MediaPause" => NamedKey::MediaPause,
|
||||
"MediaPlay" => NamedKey::MediaPlay,
|
||||
"MediaPlayPause" => NamedKey::MediaPlayPause,
|
||||
"MediaRecord" => NamedKey::MediaRecord,
|
||||
"MediaRewind" => NamedKey::MediaRewind,
|
||||
"MediaStop" => NamedKey::MediaStop,
|
||||
"MediaTrackNext" => NamedKey::MediaTrackNext,
|
||||
"MediaTrackPrevious" => NamedKey::MediaTrackPrevious,
|
||||
"New" => NamedKey::New,
|
||||
"Open" => NamedKey::Open,
|
||||
"Print" => NamedKey::Print,
|
||||
"Save" => NamedKey::Save,
|
||||
"SpellCheck" => NamedKey::SpellCheck,
|
||||
"Key11" => NamedKey::Key11,
|
||||
"Key12" => NamedKey::Key12,
|
||||
"AudioBalanceLeft" => NamedKey::AudioBalanceLeft,
|
||||
"AudioBalanceRight" => NamedKey::AudioBalanceRight,
|
||||
"AudioBassBoostDown" => NamedKey::AudioBassBoostDown,
|
||||
"AudioBassBoostToggle" => NamedKey::AudioBassBoostToggle,
|
||||
"AudioBassBoostUp" => NamedKey::AudioBassBoostUp,
|
||||
"AudioFaderFront" => NamedKey::AudioFaderFront,
|
||||
"AudioFaderRear" => NamedKey::AudioFaderRear,
|
||||
"AudioSurroundModeNext" => NamedKey::AudioSurroundModeNext,
|
||||
"AudioTrebleDown" => NamedKey::AudioTrebleDown,
|
||||
"AudioTrebleUp" => NamedKey::AudioTrebleUp,
|
||||
"AudioVolumeDown" => NamedKey::AudioVolumeDown,
|
||||
"AudioVolumeUp" => NamedKey::AudioVolumeUp,
|
||||
"AudioVolumeMute" => NamedKey::AudioVolumeMute,
|
||||
"MicrophoneToggle" => NamedKey::MicrophoneToggle,
|
||||
"MicrophoneVolumeDown" => NamedKey::MicrophoneVolumeDown,
|
||||
"MicrophoneVolumeUp" => NamedKey::MicrophoneVolumeUp,
|
||||
"MicrophoneVolumeMute" => NamedKey::MicrophoneVolumeMute,
|
||||
"SpeechCorrectionList" => NamedKey::SpeechCorrectionList,
|
||||
"SpeechInputToggle" => NamedKey::SpeechInputToggle,
|
||||
"LaunchApplication1" => NamedKey::LaunchApplication1,
|
||||
"LaunchApplication2" => NamedKey::LaunchApplication2,
|
||||
"LaunchCalendar" => NamedKey::LaunchCalendar,
|
||||
"LaunchContacts" => NamedKey::LaunchContacts,
|
||||
"LaunchMail" => NamedKey::LaunchMail,
|
||||
"LaunchMediaPlayer" => NamedKey::LaunchMediaPlayer,
|
||||
"LaunchMusicPlayer" => NamedKey::LaunchMusicPlayer,
|
||||
"LaunchPhone" => NamedKey::LaunchPhone,
|
||||
"LaunchScreenSaver" => NamedKey::LaunchScreenSaver,
|
||||
"LaunchSpreadsheet" => NamedKey::LaunchSpreadsheet,
|
||||
"LaunchWebBrowser" => NamedKey::LaunchWebBrowser,
|
||||
"LaunchWebCam" => NamedKey::LaunchWebCam,
|
||||
"LaunchWordProcessor" => NamedKey::LaunchWordProcessor,
|
||||
"BrowserBack" => NamedKey::BrowserBack,
|
||||
"BrowserFavorites" => NamedKey::BrowserFavorites,
|
||||
"BrowserForward" => NamedKey::BrowserForward,
|
||||
"BrowserHome" => NamedKey::BrowserHome,
|
||||
"BrowserRefresh" => NamedKey::BrowserRefresh,
|
||||
"BrowserSearch" => NamedKey::BrowserSearch,
|
||||
"BrowserStop" => NamedKey::BrowserStop,
|
||||
"AppSwitch" => NamedKey::AppSwitch,
|
||||
"Call" => NamedKey::Call,
|
||||
"Camera" => NamedKey::Camera,
|
||||
"CameraFocus" => NamedKey::CameraFocus,
|
||||
"EndCall" => NamedKey::EndCall,
|
||||
"GoBack" => NamedKey::GoBack,
|
||||
"GoHome" => NamedKey::GoHome,
|
||||
"HeadsetHook" => NamedKey::HeadsetHook,
|
||||
"LastNumberRedial" => NamedKey::LastNumberRedial,
|
||||
"Notification" => NamedKey::Notification,
|
||||
"MannerMode" => NamedKey::MannerMode,
|
||||
"VoiceDial" => NamedKey::VoiceDial,
|
||||
"TV" => NamedKey::TV,
|
||||
"TV3DMode" => NamedKey::TV3DMode,
|
||||
"TVAntennaCable" => NamedKey::TVAntennaCable,
|
||||
"TVAudioDescription" => NamedKey::TVAudioDescription,
|
||||
"TVAudioDescriptionMixDown" => NamedKey::TVAudioDescriptionMixDown,
|
||||
"TVAudioDescriptionMixUp" => NamedKey::TVAudioDescriptionMixUp,
|
||||
"TVContentsMenu" => NamedKey::TVContentsMenu,
|
||||
"TVDataService" => NamedKey::TVDataService,
|
||||
"TVInput" => NamedKey::TVInput,
|
||||
"TVInputComponent1" => NamedKey::TVInputComponent1,
|
||||
"TVInputComponent2" => NamedKey::TVInputComponent2,
|
||||
"TVInputComposite1" => NamedKey::TVInputComposite1,
|
||||
"TVInputComposite2" => NamedKey::TVInputComposite2,
|
||||
"TVInputHDMI1" => NamedKey::TVInputHDMI1,
|
||||
"TVInputHDMI2" => NamedKey::TVInputHDMI2,
|
||||
"TVInputHDMI3" => NamedKey::TVInputHDMI3,
|
||||
"TVInputHDMI4" => NamedKey::TVInputHDMI4,
|
||||
"TVInputVGA1" => NamedKey::TVInputVGA1,
|
||||
"TVMediaContext" => NamedKey::TVMediaContext,
|
||||
"TVNetwork" => NamedKey::TVNetwork,
|
||||
"TVNumberEntry" => NamedKey::TVNumberEntry,
|
||||
"TVPower" => NamedKey::TVPower,
|
||||
"TVRadioService" => NamedKey::TVRadioService,
|
||||
"TVSatellite" => NamedKey::TVSatellite,
|
||||
"TVSatelliteBS" => NamedKey::TVSatelliteBS,
|
||||
"TVSatelliteCS" => NamedKey::TVSatelliteCS,
|
||||
"TVSatelliteToggle" => NamedKey::TVSatelliteToggle,
|
||||
"TVTerrestrialAnalog" => NamedKey::TVTerrestrialAnalog,
|
||||
"TVTerrestrialDigital" => NamedKey::TVTerrestrialDigital,
|
||||
"TVTimer" => NamedKey::TVTimer,
|
||||
"AVRInput" => NamedKey::AVRInput,
|
||||
"AVRPower" => NamedKey::AVRPower,
|
||||
"ColorF0Red" => NamedKey::ColorF0Red,
|
||||
"ColorF1Green" => NamedKey::ColorF1Green,
|
||||
"ColorF2Yellow" => NamedKey::ColorF2Yellow,
|
||||
"ColorF3Blue" => NamedKey::ColorF3Blue,
|
||||
"ColorF4Grey" => NamedKey::ColorF4Grey,
|
||||
"ColorF5Brown" => NamedKey::ColorF5Brown,
|
||||
"ClosedCaptionToggle" => NamedKey::ClosedCaptionToggle,
|
||||
"Dimmer" => NamedKey::Dimmer,
|
||||
"DisplaySwap" => NamedKey::DisplaySwap,
|
||||
"DVR" => NamedKey::DVR,
|
||||
"Exit" => NamedKey::Exit,
|
||||
"FavoriteClear0" => NamedKey::FavoriteClear0,
|
||||
"FavoriteClear1" => NamedKey::FavoriteClear1,
|
||||
"FavoriteClear2" => NamedKey::FavoriteClear2,
|
||||
"FavoriteClear3" => NamedKey::FavoriteClear3,
|
||||
"FavoriteRecall0" => NamedKey::FavoriteRecall0,
|
||||
"FavoriteRecall1" => NamedKey::FavoriteRecall1,
|
||||
"FavoriteRecall2" => NamedKey::FavoriteRecall2,
|
||||
"FavoriteRecall3" => NamedKey::FavoriteRecall3,
|
||||
"FavoriteStore0" => NamedKey::FavoriteStore0,
|
||||
"FavoriteStore1" => NamedKey::FavoriteStore1,
|
||||
"FavoriteStore2" => NamedKey::FavoriteStore2,
|
||||
"FavoriteStore3" => NamedKey::FavoriteStore3,
|
||||
"Guide" => NamedKey::Guide,
|
||||
"GuideNextDay" => NamedKey::GuideNextDay,
|
||||
"GuidePreviousDay" => NamedKey::GuidePreviousDay,
|
||||
"Info" => NamedKey::Info,
|
||||
"InstantReplay" => NamedKey::InstantReplay,
|
||||
"Link" => NamedKey::Link,
|
||||
"ListProgram" => NamedKey::ListProgram,
|
||||
"LiveContent" => NamedKey::LiveContent,
|
||||
"Lock" => NamedKey::Lock,
|
||||
"MediaApps" => NamedKey::MediaApps,
|
||||
"MediaAudioTrack" => NamedKey::MediaAudioTrack,
|
||||
"MediaLast" => NamedKey::MediaLast,
|
||||
"MediaSkipBackward" => NamedKey::MediaSkipBackward,
|
||||
"MediaSkipForward" => NamedKey::MediaSkipForward,
|
||||
"MediaStepBackward" => NamedKey::MediaStepBackward,
|
||||
"MediaStepForward" => NamedKey::MediaStepForward,
|
||||
"MediaTopMenu" => NamedKey::MediaTopMenu,
|
||||
"NavigateIn" => NamedKey::NavigateIn,
|
||||
"NavigateNext" => NamedKey::NavigateNext,
|
||||
"NavigateOut" => NamedKey::NavigateOut,
|
||||
"NavigatePrevious" => NamedKey::NavigatePrevious,
|
||||
"NextFavoriteChannel" => NamedKey::NextFavoriteChannel,
|
||||
"NextUserProfile" => NamedKey::NextUserProfile,
|
||||
"OnDemand" => NamedKey::OnDemand,
|
||||
"Pairing" => NamedKey::Pairing,
|
||||
"PinPDown" => NamedKey::PinPDown,
|
||||
"PinPMove" => NamedKey::PinPMove,
|
||||
"PinPToggle" => NamedKey::PinPToggle,
|
||||
"PinPUp" => NamedKey::PinPUp,
|
||||
"PlaySpeedDown" => NamedKey::PlaySpeedDown,
|
||||
"PlaySpeedReset" => NamedKey::PlaySpeedReset,
|
||||
"PlaySpeedUp" => NamedKey::PlaySpeedUp,
|
||||
"RandomToggle" => NamedKey::RandomToggle,
|
||||
"RcLowBattery" => NamedKey::RcLowBattery,
|
||||
"RecordSpeedNext" => NamedKey::RecordSpeedNext,
|
||||
"RfBypass" => NamedKey::RfBypass,
|
||||
"ScanChannelsToggle" => NamedKey::ScanChannelsToggle,
|
||||
"ScreenModeNext" => NamedKey::ScreenModeNext,
|
||||
"Settings" => NamedKey::Settings,
|
||||
"SplitScreenToggle" => NamedKey::SplitScreenToggle,
|
||||
"STBInput" => NamedKey::STBInput,
|
||||
"STBPower" => NamedKey::STBPower,
|
||||
"Subtitle" => NamedKey::Subtitle,
|
||||
"Teletext" => NamedKey::Teletext,
|
||||
"VideoModeNext" => NamedKey::VideoModeNext,
|
||||
"Wink" => NamedKey::Wink,
|
||||
"ZoomToggle" => NamedKey::ZoomToggle,
|
||||
"F1" => NamedKey::F1,
|
||||
"F2" => NamedKey::F2,
|
||||
"F3" => NamedKey::F3,
|
||||
"F4" => NamedKey::F4,
|
||||
"F5" => NamedKey::F5,
|
||||
"F6" => NamedKey::F6,
|
||||
"F7" => NamedKey::F7,
|
||||
"F8" => NamedKey::F8,
|
||||
"F9" => NamedKey::F9,
|
||||
"F10" => NamedKey::F10,
|
||||
"F11" => NamedKey::F11,
|
||||
"F12" => NamedKey::F12,
|
||||
"F13" => NamedKey::F13,
|
||||
"F14" => NamedKey::F14,
|
||||
"F15" => NamedKey::F15,
|
||||
"F16" => NamedKey::F16,
|
||||
"F17" => NamedKey::F17,
|
||||
"F18" => NamedKey::F18,
|
||||
"F19" => NamedKey::F19,
|
||||
"F20" => NamedKey::F20,
|
||||
"F21" => NamedKey::F21,
|
||||
"F22" => NamedKey::F22,
|
||||
"F23" => NamedKey::F23,
|
||||
"F24" => NamedKey::F24,
|
||||
"F25" => NamedKey::F25,
|
||||
"F26" => NamedKey::F26,
|
||||
"F27" => NamedKey::F27,
|
||||
"F28" => NamedKey::F28,
|
||||
"F29" => NamedKey::F29,
|
||||
"F30" => NamedKey::F30,
|
||||
"F31" => NamedKey::F31,
|
||||
"F32" => NamedKey::F32,
|
||||
"F33" => NamedKey::F33,
|
||||
"F34" => NamedKey::F34,
|
||||
"F35" => NamedKey::F35,
|
||||
string => return Key::Character(SmolStr::new(string)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalKey {
|
||||
pub fn from_key_code_attribute_value(kcav: &str) -> Self {
|
||||
PhysicalKey::Code(match kcav {
|
||||
"Backquote" => KeyCode::Backquote,
|
||||
"Backslash" => KeyCode::Backslash,
|
||||
"BracketLeft" => KeyCode::BracketLeft,
|
||||
"BracketRight" => KeyCode::BracketRight,
|
||||
"Comma" => KeyCode::Comma,
|
||||
"Digit0" => KeyCode::Digit0,
|
||||
"Digit1" => KeyCode::Digit1,
|
||||
"Digit2" => KeyCode::Digit2,
|
||||
"Digit3" => KeyCode::Digit3,
|
||||
"Digit4" => KeyCode::Digit4,
|
||||
"Digit5" => KeyCode::Digit5,
|
||||
"Digit6" => KeyCode::Digit6,
|
||||
"Digit7" => KeyCode::Digit7,
|
||||
"Digit8" => KeyCode::Digit8,
|
||||
"Digit9" => KeyCode::Digit9,
|
||||
"Equal" => KeyCode::Equal,
|
||||
"IntlBackslash" => KeyCode::IntlBackslash,
|
||||
"IntlRo" => KeyCode::IntlRo,
|
||||
"IntlYen" => KeyCode::IntlYen,
|
||||
"KeyA" => KeyCode::KeyA,
|
||||
"KeyB" => KeyCode::KeyB,
|
||||
"KeyC" => KeyCode::KeyC,
|
||||
"KeyD" => KeyCode::KeyD,
|
||||
"KeyE" => KeyCode::KeyE,
|
||||
"KeyF" => KeyCode::KeyF,
|
||||
"KeyG" => KeyCode::KeyG,
|
||||
"KeyH" => KeyCode::KeyH,
|
||||
"KeyI" => KeyCode::KeyI,
|
||||
"KeyJ" => KeyCode::KeyJ,
|
||||
"KeyK" => KeyCode::KeyK,
|
||||
"KeyL" => KeyCode::KeyL,
|
||||
"KeyM" => KeyCode::KeyM,
|
||||
"KeyN" => KeyCode::KeyN,
|
||||
"KeyO" => KeyCode::KeyO,
|
||||
"KeyP" => KeyCode::KeyP,
|
||||
"KeyQ" => KeyCode::KeyQ,
|
||||
"KeyR" => KeyCode::KeyR,
|
||||
"KeyS" => KeyCode::KeyS,
|
||||
"KeyT" => KeyCode::KeyT,
|
||||
"KeyU" => KeyCode::KeyU,
|
||||
"KeyV" => KeyCode::KeyV,
|
||||
"KeyW" => KeyCode::KeyW,
|
||||
"KeyX" => KeyCode::KeyX,
|
||||
"KeyY" => KeyCode::KeyY,
|
||||
"KeyZ" => KeyCode::KeyZ,
|
||||
"Minus" => KeyCode::Minus,
|
||||
"Period" => KeyCode::Period,
|
||||
"Quote" => KeyCode::Quote,
|
||||
"Semicolon" => KeyCode::Semicolon,
|
||||
"Slash" => KeyCode::Slash,
|
||||
"AltLeft" => KeyCode::AltLeft,
|
||||
"AltRight" => KeyCode::AltRight,
|
||||
"Backspace" => KeyCode::Backspace,
|
||||
"CapsLock" => KeyCode::CapsLock,
|
||||
"ContextMenu" => KeyCode::ContextMenu,
|
||||
"ControlLeft" => KeyCode::ControlLeft,
|
||||
"ControlRight" => KeyCode::ControlRight,
|
||||
"Enter" => KeyCode::Enter,
|
||||
"MetaLeft" => KeyCode::SuperLeft,
|
||||
"MetaRight" => KeyCode::SuperRight,
|
||||
"ShiftLeft" => KeyCode::ShiftLeft,
|
||||
"ShiftRight" => KeyCode::ShiftRight,
|
||||
"Space" => KeyCode::Space,
|
||||
"Tab" => KeyCode::Tab,
|
||||
"Convert" => KeyCode::Convert,
|
||||
"KanaMode" => KeyCode::KanaMode,
|
||||
"Lang1" => KeyCode::Lang1,
|
||||
"Lang2" => KeyCode::Lang2,
|
||||
"Lang3" => KeyCode::Lang3,
|
||||
"Lang4" => KeyCode::Lang4,
|
||||
"Lang5" => KeyCode::Lang5,
|
||||
"NonConvert" => KeyCode::NonConvert,
|
||||
"Delete" => KeyCode::Delete,
|
||||
"End" => KeyCode::End,
|
||||
"Help" => KeyCode::Help,
|
||||
"Home" => KeyCode::Home,
|
||||
"Insert" => KeyCode::Insert,
|
||||
"PageDown" => KeyCode::PageDown,
|
||||
"PageUp" => KeyCode::PageUp,
|
||||
"ArrowDown" => KeyCode::ArrowDown,
|
||||
"ArrowLeft" => KeyCode::ArrowLeft,
|
||||
"ArrowRight" => KeyCode::ArrowRight,
|
||||
"ArrowUp" => KeyCode::ArrowUp,
|
||||
"NumLock" => KeyCode::NumLock,
|
||||
"Numpad0" => KeyCode::Numpad0,
|
||||
"Numpad1" => KeyCode::Numpad1,
|
||||
"Numpad2" => KeyCode::Numpad2,
|
||||
"Numpad3" => KeyCode::Numpad3,
|
||||
"Numpad4" => KeyCode::Numpad4,
|
||||
"Numpad5" => KeyCode::Numpad5,
|
||||
"Numpad6" => KeyCode::Numpad6,
|
||||
"Numpad7" => KeyCode::Numpad7,
|
||||
"Numpad8" => KeyCode::Numpad8,
|
||||
"Numpad9" => KeyCode::Numpad9,
|
||||
"NumpadAdd" => KeyCode::NumpadAdd,
|
||||
"NumpadBackspace" => KeyCode::NumpadBackspace,
|
||||
"NumpadClear" => KeyCode::NumpadClear,
|
||||
"NumpadClearEntry" => KeyCode::NumpadClearEntry,
|
||||
"NumpadComma" => KeyCode::NumpadComma,
|
||||
"NumpadDecimal" => KeyCode::NumpadDecimal,
|
||||
"NumpadDivide" => KeyCode::NumpadDivide,
|
||||
"NumpadEnter" => KeyCode::NumpadEnter,
|
||||
"NumpadEqual" => KeyCode::NumpadEqual,
|
||||
"NumpadHash" => KeyCode::NumpadHash,
|
||||
"NumpadMemoryAdd" => KeyCode::NumpadMemoryAdd,
|
||||
"NumpadMemoryClear" => KeyCode::NumpadMemoryClear,
|
||||
"NumpadMemoryRecall" => KeyCode::NumpadMemoryRecall,
|
||||
"NumpadMemoryStore" => KeyCode::NumpadMemoryStore,
|
||||
"NumpadMemorySubtract" => KeyCode::NumpadMemorySubtract,
|
||||
"NumpadMultiply" => KeyCode::NumpadMultiply,
|
||||
"NumpadParenLeft" => KeyCode::NumpadParenLeft,
|
||||
"NumpadParenRight" => KeyCode::NumpadParenRight,
|
||||
"NumpadStar" => KeyCode::NumpadStar,
|
||||
"NumpadSubtract" => KeyCode::NumpadSubtract,
|
||||
"Escape" => KeyCode::Escape,
|
||||
"Fn" => KeyCode::Fn,
|
||||
"FnLock" => KeyCode::FnLock,
|
||||
"PrintScreen" => KeyCode::PrintScreen,
|
||||
"ScrollLock" => KeyCode::ScrollLock,
|
||||
"Pause" => KeyCode::Pause,
|
||||
"BrowserBack" => KeyCode::BrowserBack,
|
||||
"BrowserFavorites" => KeyCode::BrowserFavorites,
|
||||
"BrowserForward" => KeyCode::BrowserForward,
|
||||
"BrowserHome" => KeyCode::BrowserHome,
|
||||
"BrowserRefresh" => KeyCode::BrowserRefresh,
|
||||
"BrowserSearch" => KeyCode::BrowserSearch,
|
||||
"BrowserStop" => KeyCode::BrowserStop,
|
||||
"Eject" => KeyCode::Eject,
|
||||
"LaunchApp1" => KeyCode::LaunchApp1,
|
||||
"LaunchApp2" => KeyCode::LaunchApp2,
|
||||
"LaunchMail" => KeyCode::LaunchMail,
|
||||
"MediaPlayPause" => KeyCode::MediaPlayPause,
|
||||
"MediaSelect" => KeyCode::MediaSelect,
|
||||
"MediaStop" => KeyCode::MediaStop,
|
||||
"MediaTrackNext" => KeyCode::MediaTrackNext,
|
||||
"MediaTrackPrevious" => KeyCode::MediaTrackPrevious,
|
||||
"Power" => KeyCode::Power,
|
||||
"Sleep" => KeyCode::Sleep,
|
||||
"AudioVolumeDown" => KeyCode::AudioVolumeDown,
|
||||
"AudioVolumeMute" => KeyCode::AudioVolumeMute,
|
||||
"AudioVolumeUp" => KeyCode::AudioVolumeUp,
|
||||
"WakeUp" => KeyCode::WakeUp,
|
||||
"Hyper" => KeyCode::Hyper,
|
||||
"Turbo" => KeyCode::Turbo,
|
||||
"Abort" => KeyCode::Abort,
|
||||
"Resume" => KeyCode::Resume,
|
||||
"Suspend" => KeyCode::Suspend,
|
||||
"Again" => KeyCode::Again,
|
||||
"Copy" => KeyCode::Copy,
|
||||
"Cut" => KeyCode::Cut,
|
||||
"Find" => KeyCode::Find,
|
||||
"Open" => KeyCode::Open,
|
||||
"Paste" => KeyCode::Paste,
|
||||
"Props" => KeyCode::Props,
|
||||
"Select" => KeyCode::Select,
|
||||
"Undo" => KeyCode::Undo,
|
||||
"Hiragana" => KeyCode::Hiragana,
|
||||
"Katakana" => KeyCode::Katakana,
|
||||
"F1" => KeyCode::F1,
|
||||
"F2" => KeyCode::F2,
|
||||
"F3" => KeyCode::F3,
|
||||
"F4" => KeyCode::F4,
|
||||
"F5" => KeyCode::F5,
|
||||
"F6" => KeyCode::F6,
|
||||
"F7" => KeyCode::F7,
|
||||
"F8" => KeyCode::F8,
|
||||
"F9" => KeyCode::F9,
|
||||
"F10" => KeyCode::F10,
|
||||
"F11" => KeyCode::F11,
|
||||
"F12" => KeyCode::F12,
|
||||
"F13" => KeyCode::F13,
|
||||
"F14" => KeyCode::F14,
|
||||
"F15" => KeyCode::F15,
|
||||
"F16" => KeyCode::F16,
|
||||
"F17" => KeyCode::F17,
|
||||
"F18" => KeyCode::F18,
|
||||
"F19" => KeyCode::F19,
|
||||
"F20" => KeyCode::F20,
|
||||
"F21" => KeyCode::F21,
|
||||
"F22" => KeyCode::F22,
|
||||
"F23" => KeyCode::F23,
|
||||
"F24" => KeyCode::F24,
|
||||
"F25" => KeyCode::F25,
|
||||
"F26" => KeyCode::F26,
|
||||
"F27" => KeyCode::F27,
|
||||
"F28" => KeyCode::F28,
|
||||
"F29" => KeyCode::F29,
|
||||
"F30" => KeyCode::F30,
|
||||
"F31" => KeyCode::F31,
|
||||
"F32" => KeyCode::F32,
|
||||
"F33" => KeyCode::F33,
|
||||
"F34" => KeyCode::F34,
|
||||
"F35" => KeyCode::F35,
|
||||
_ => return PhysicalKey::Unidentified(NativeKeyCode::Unidentified),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use js_sys::Promise;
|
||||
use once_cell::unsync::OnceCell;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{Document, Element, HtmlCanvasElement};
|
||||
|
||||
use super::EventListenerHandle;
|
||||
|
||||
thread_local! {
|
||||
static FULLSCREEN_API_SUPPORT: OnceCell<bool> = OnceCell::new();
|
||||
}
|
||||
|
||||
pub struct FullscreenHandler {
|
||||
document: Document,
|
||||
canvas: HtmlCanvasElement,
|
||||
fullscreen_requested: Rc<Cell<bool>>,
|
||||
_fullscreen_change: EventListenerHandle<dyn FnMut()>,
|
||||
}
|
||||
|
||||
impl FullscreenHandler {
|
||||
pub fn new(document: Document, canvas: HtmlCanvasElement) -> Self {
|
||||
let fullscreen_requested = Rc::new(Cell::new(false));
|
||||
let fullscreen_change = EventListenerHandle::new(
|
||||
canvas.clone(),
|
||||
if has_fullscreen_api_support(&canvas) {
|
||||
"fullscreenchange"
|
||||
} else {
|
||||
"webkitfullscreenchange"
|
||||
},
|
||||
Closure::new({
|
||||
let fullscreen_requested = fullscreen_requested.clone();
|
||||
move || {
|
||||
// It doesn't matter if the canvas entered or exitted fullscreen mode,
|
||||
// we don't want to request it again later.
|
||||
fullscreen_requested.set(false);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
Self {
|
||||
document,
|
||||
canvas,
|
||||
fullscreen_requested,
|
||||
_fullscreen_change: fullscreen_change,
|
||||
}
|
||||
}
|
||||
|
||||
fn internal_request_fullscreen(&self) {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
type RequestFullscreen;
|
||||
|
||||
#[wasm_bindgen(method, js_name = requestFullscreen)]
|
||||
fn request_fullscreen(this: &RequestFullscreen) -> Promise;
|
||||
|
||||
#[wasm_bindgen(method, js_name = webkitRequestFullscreen)]
|
||||
fn webkit_request_fullscreen(this: &RequestFullscreen);
|
||||
}
|
||||
|
||||
let canvas: &RequestFullscreen = self.canvas.unchecked_ref();
|
||||
|
||||
if has_fullscreen_api_support(&self.canvas) {
|
||||
thread_local! {
|
||||
static REJECT_HANDLER: Closure<dyn FnMut(JsValue)> = Closure::new(|_| ());
|
||||
}
|
||||
REJECT_HANDLER.with(|handler| {
|
||||
let _ = canvas.request_fullscreen().catch(handler);
|
||||
});
|
||||
} else {
|
||||
canvas.webkit_request_fullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_fullscreen(&self) {
|
||||
if !self.is_fullscreen() {
|
||||
self.internal_request_fullscreen();
|
||||
self.fullscreen_requested.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transient_activation(&self) {
|
||||
if self.fullscreen_requested.get() {
|
||||
self.internal_request_fullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_fullscreen(&self) -> bool {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
type FullscreenElement;
|
||||
|
||||
#[wasm_bindgen(method, getter, js_name = webkitFullscreenElement)]
|
||||
fn webkit_fullscreen_element(this: &FullscreenElement) -> Option<Element>;
|
||||
}
|
||||
|
||||
let element = if has_fullscreen_api_support(&self.canvas) {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
self.document.fullscreen_element()
|
||||
} else {
|
||||
let document: &FullscreenElement = self.document.unchecked_ref();
|
||||
document.webkit_fullscreen_element()
|
||||
};
|
||||
|
||||
match element {
|
||||
Some(element) => {
|
||||
let canvas: &Element = &self.canvas;
|
||||
canvas == &element
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit_fullscreen(&self) {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
type ExitFullscreen;
|
||||
|
||||
#[wasm_bindgen(method, js_name = webkitExitFullscreen)]
|
||||
fn webkit_exit_fullscreen(this: &ExitFullscreen);
|
||||
}
|
||||
|
||||
if has_fullscreen_api_support(&self.canvas) {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
self.document.exit_fullscreen()
|
||||
} else {
|
||||
let document: &ExitFullscreen = self.document.unchecked_ref();
|
||||
document.webkit_exit_fullscreen()
|
||||
}
|
||||
|
||||
self.fullscreen_requested.set(false);
|
||||
}
|
||||
|
||||
pub fn cancel(&self) {
|
||||
self.fullscreen_requested.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
fn has_fullscreen_api_support(canvas: &HtmlCanvasElement) -> bool {
|
||||
FULLSCREEN_API_SUPPORT.with(|support| {
|
||||
*support.get_or_init(|| {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
type CanvasFullScreenApiSupport;
|
||||
|
||||
#[wasm_bindgen(method, getter, js_name = requestFullscreen)]
|
||||
fn has_request_fullscreen(this: &CanvasFullScreenApiSupport) -> JsValue;
|
||||
}
|
||||
|
||||
let support: &CanvasFullScreenApiSupport = canvas.unchecked_ref();
|
||||
!support.has_request_fullscreen().is_undefined()
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -28,3 +28,9 @@ fn ids_send() {
|
||||
needs_send::<winit::event::DeviceId>();
|
||||
needs_send::<winit::monitor::MonitorHandle>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_cursor_send() {
|
||||
needs_send::<winit::window::CustomCursorBuilder>();
|
||||
needs_send::<winit::window::CustomCursor>();
|
||||
}
|
||||
|
||||
@@ -11,3 +11,9 @@ fn window_sync() {
|
||||
fn window_builder_sync() {
|
||||
needs_sync::<winit::window::WindowBuilder>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_cursor_sync() {
|
||||
needs_sync::<winit::window::CustomCursorBuilder>();
|
||||
needs_sync::<winit::window::CustomCursor>();
|
||||
}
|
||||
|
||||
32
winit-core/Cargo.toml
Normal file
32
winit-core/Cargo.toml
Normal file
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "winit-core"
|
||||
version = "0.1.0"
|
||||
authors = ["The winit contributors"]
|
||||
description = "Base types for windowing libraries"
|
||||
edition = "2021"
|
||||
keywords = ["windowing"]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/rust-windowing/winit"
|
||||
documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
rust-version = "1.70.0"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["alloc"]
|
||||
alloc = []
|
||||
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"]
|
||||
|
||||
[dependencies]
|
||||
bitflags.workspace = true
|
||||
cursor-icon.workspace = true
|
||||
mint = { version = "0.5.6", optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
smol_str.workspace = true
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
web-time.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases.workspace = true
|
||||
3
winit-core/README.md
Normal file
3
winit-core/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# winit-core - Base types for a windowing system
|
||||
|
||||
TODO
|
||||
@@ -8,7 +8,7 @@ fn main() {
|
||||
cfg_aliases! {
|
||||
// Systems.
|
||||
android_platform: { target_os = "android" },
|
||||
wasm_platform: { all(target_family = "wasm", not(target_os = "emscripten")) },
|
||||
web_platform: { all(target_family = "wasm", target_os = "unknown") },
|
||||
macos_platform: { target_os = "macos" },
|
||||
ios_platform: { target_os = "ios" },
|
||||
windows_platform: { target_os = "windows" },
|
||||
@@ -17,8 +17,8 @@ fn main() {
|
||||
redox: { target_os = "redox" },
|
||||
|
||||
// Native displays.
|
||||
x11_platform: { all(feature = "x11", free_unix, not(wasm), not(redox)) },
|
||||
wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
|
||||
x11_platform: { all(feature = "x11", free_unix, not(redox)) },
|
||||
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
|
||||
orbital_platform: { redox },
|
||||
}
|
||||
}
|
||||
@@ -97,13 +97,16 @@
|
||||
//! [points]: https://en.wikipedia.org/wiki/Point_(typography)
|
||||
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
|
||||
//! [`ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged
|
||||
//! [`window.scale_factor()`]: crate::window::Window::scale_factor
|
||||
//! [`window.scale_factor()`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.scale_factor
|
||||
//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
|
||||
//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html
|
||||
//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/
|
||||
//! [android_1]: https://developer.android.com/training/multiscreen/screendensities
|
||||
//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub trait Pixel: Copy + Into<f64> {
|
||||
fn from_f64(f: f64) -> Self;
|
||||
fn cast<P: Pixel>(self) -> P {
|
||||
54
winit-core/src/error.rs
Normal file
54
winit-core/src/error.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
//! Common error types.
|
||||
|
||||
use std::{error, fmt};
|
||||
|
||||
/// The error type for when the requested operation is not supported by the backend.
|
||||
#[derive(Clone)]
|
||||
pub struct NotSupportedError {
|
||||
_marker: (),
|
||||
}
|
||||
|
||||
impl Default for NotSupportedError {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl NotSupportedError {
|
||||
/// Create a new [`NotSupportedError`].
|
||||
#[inline]
|
||||
pub fn new() -> NotSupportedError {
|
||||
NotSupportedError { _marker: () }
|
||||
}
|
||||
}
|
||||
|
||||
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 error::Error for NotSupportedError {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::redundant_clone)]
|
||||
|
||||
use super::*;
|
||||
|
||||
// Eat attributes for testing
|
||||
#[test]
|
||||
fn ensure_fmt_does_not_panic() {
|
||||
let _ = format!(
|
||||
"{:?}, {}",
|
||||
NotSupportedError::new(),
|
||||
NotSupportedError::new().clone()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +1,64 @@
|
||||
//! The [`Event`] enum and assorted supporting types.
|
||||
//!
|
||||
//! These are sent to the closure given to [`EventLoop::run(...)`], where they get
|
||||
//! processed and used to modify the program state. For more details, see the root-level documentation.
|
||||
//!
|
||||
//! Some of these events represent different "parts" of a traditional event-handling loop. You could
|
||||
//! approximate the basic ordering loop of [`EventLoop::run(...)`] like this:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! let mut start_cause = StartCause::Init;
|
||||
//!
|
||||
//! while !elwt.exiting() {
|
||||
//! event_handler(NewEvents(start_cause), elwt);
|
||||
//!
|
||||
//! for e in (window events, user events, device events) {
|
||||
//! event_handler(e, elwt);
|
||||
//! }
|
||||
//!
|
||||
//! for w in (redraw windows) {
|
||||
//! event_handler(RedrawRequested(w), elwt);
|
||||
//! }
|
||||
//!
|
||||
//! event_handler(AboutToWait, elwt);
|
||||
//! start_cause = wait_if_necessary();
|
||||
//! }
|
||||
//!
|
||||
//! event_handler(LoopExiting, elwt);
|
||||
//! ```
|
||||
//!
|
||||
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
|
||||
//! describes what happens in what order.
|
||||
//!
|
||||
//! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run
|
||||
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
|
||||
//! Incoming notifications from the GUI system.
|
||||
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState};
|
||||
use crate::window::{ActivationToken, Theme, WindowId};
|
||||
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Mutex, Weak};
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time::Instant;
|
||||
|
||||
use smol_str::SmolStr;
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time::Instant;
|
||||
#[cfg(web_platform)]
|
||||
use web_time::Instant;
|
||||
|
||||
use crate::error::ExternalError;
|
||||
#[cfg(doc)]
|
||||
use crate::window::Window;
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
event_loop::AsyncRequestSerial,
|
||||
keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState},
|
||||
platform_impl,
|
||||
window::{ActivationToken, Theme, WindowId},
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
pub use serde::{Deserialize, Serialize};
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
||||
/// Identifier of an input device.
|
||||
///
|
||||
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
|
||||
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
|
||||
/// physical. Virtual devices typically aggregate inputs from multiple physical devices.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(u64);
|
||||
|
||||
impl DeviceId {
|
||||
/// Returns a dummy id, useful for unit testing.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The only guarantee made about the return value of this function is that
|
||||
/// it will always be equal to itself and to future values returned by this function.
|
||||
/// No other guarantees are made. This may be equal to a real `DeviceId`.
|
||||
///
|
||||
/// **Passing this into a winit function will result in undefined behavior.**
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
DeviceId(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for DeviceId {
|
||||
fn from(value: u64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeviceId> for u64 {
|
||||
fn from(value: DeviceId) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a generic event.
|
||||
///
|
||||
/// See the module-level docs for more information on the event loop manages each event.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Event<T: 'static> {
|
||||
pub enum Event<T: 'static, KeyExtra> {
|
||||
/// Emitted when new events arrive from the OS to be processed.
|
||||
///
|
||||
/// This event type is useful as a place to put code that should be done before you start
|
||||
@@ -68,7 +70,7 @@ pub enum Event<T: 'static> {
|
||||
/// Emitted when the OS sends an event to a winit window.
|
||||
WindowEvent {
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
event: WindowEvent<KeyExtra>,
|
||||
},
|
||||
|
||||
/// Emitted when the OS sends an event to a device.
|
||||
@@ -77,7 +79,7 @@ pub enum Event<T: 'static> {
|
||||
event: DeviceEvent,
|
||||
},
|
||||
|
||||
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event)
|
||||
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](https://docs.rs/winit/latest/winit/event_loop/struct.EventLoopProxy.html#method.send_event)
|
||||
UserEvent(T),
|
||||
|
||||
/// Emitted when the application has been suspended.
|
||||
@@ -253,9 +255,9 @@ pub enum Event<T: 'static> {
|
||||
MemoryWarning,
|
||||
}
|
||||
|
||||
impl<T> Event<T> {
|
||||
impl<T, Extra> Event<T, Extra> {
|
||||
#[allow(clippy::result_large_err)]
|
||||
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
|
||||
pub fn map_nonuser_event<U>(self) -> Result<Event<U, Extra>, Event<T, Extra>> {
|
||||
use self::Event::*;
|
||||
match self {
|
||||
UserEvent(_) => Err(self),
|
||||
@@ -302,8 +304,10 @@ pub enum StartCause {
|
||||
}
|
||||
|
||||
/// Describes an event from a [`Window`].
|
||||
///
|
||||
/// [`Window`]: https://docs.rs/winit/latest/winit/window/struct.Window.html
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum WindowEvent {
|
||||
pub enum WindowEvent<KeyExtra> {
|
||||
/// The activation token was delivered back and now could be used.
|
||||
///
|
||||
#[cfg_attr(
|
||||
@@ -365,7 +369,7 @@ pub enum WindowEvent {
|
||||
/// events which are not marked as `is_synthetic`.
|
||||
KeyboardInput {
|
||||
device_id: DeviceId,
|
||||
event: KeyEvent,
|
||||
event: KeyEvent<KeyExtra>,
|
||||
|
||||
/// If `true`, the event was generated synthetically by winit
|
||||
/// in one of the following circumstances:
|
||||
@@ -389,6 +393,8 @@ pub enum WindowEvent {
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web / Orbital:** Unsupported.
|
||||
///
|
||||
/// [`Window::set_ime_allowed`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_allowed
|
||||
Ime(Ime),
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
@@ -445,21 +451,23 @@ pub enum WindowEvent {
|
||||
button: MouseButton,
|
||||
},
|
||||
|
||||
/// Touchpad magnification event with two-finger pinch gesture.
|
||||
///
|
||||
/// Positive delta values indicate magnification (zooming in) and
|
||||
/// negative delta values indicate shrinking (zooming out).
|
||||
/// Two-finger pinch gesture, often used for magnification.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - Only available on **macOS**.
|
||||
TouchpadMagnify {
|
||||
/// - Only available on **macOS** and **iOS**.
|
||||
/// - On iOS, not recognized by default. It must be enabled when needed.
|
||||
PinchGesture {
|
||||
device_id: DeviceId,
|
||||
/// Positive values indicate magnification (zooming in) and negative
|
||||
/// values indicate shrinking (zooming out).
|
||||
///
|
||||
/// This value may be NaN.
|
||||
delta: f64,
|
||||
phase: TouchPhase,
|
||||
},
|
||||
|
||||
/// Smart magnification event.
|
||||
/// Double tap gesture.
|
||||
///
|
||||
/// On a Mac, smart magnification is triggered by a double tap with two fingers
|
||||
/// on the trackpad and is commonly used to zoom on a certain object
|
||||
@@ -475,18 +483,20 @@ pub enum WindowEvent {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - Only available on **macOS 10.8** and later.
|
||||
SmartMagnify { device_id: DeviceId },
|
||||
/// - Only available on **macOS 10.8** and later, and **iOS**.
|
||||
/// - On iOS, not recognized by default. It must be enabled when needed.
|
||||
DoubleTapGesture { device_id: DeviceId },
|
||||
|
||||
/// Touchpad rotation event with two-finger rotation gesture.
|
||||
/// Two-finger rotation gesture.
|
||||
///
|
||||
/// Positive delta values indicate rotation counterclockwise and
|
||||
/// negative delta values indicate rotation clockwise.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - Only available on **macOS**.
|
||||
TouchpadRotate {
|
||||
/// - Only available on **macOS** and **iOS**.
|
||||
/// - On iOS, not recognized by default. It must be enabled when needed.
|
||||
RotationGesture {
|
||||
device_id: DeviceId,
|
||||
delta: f32,
|
||||
phase: TouchPhase,
|
||||
@@ -574,7 +584,7 @@ pub enum WindowEvent {
|
||||
/// ### Others
|
||||
///
|
||||
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
|
||||
/// - **Android / Windows / Orbital:** Unsupported.
|
||||
/// - **Android / Wayland / Windows / Orbital:** Unsupported.
|
||||
///
|
||||
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||
@@ -590,33 +600,11 @@ pub enum WindowEvent {
|
||||
///
|
||||
/// Winit will aggregate duplicate redraw requests into a single event, to
|
||||
/// help avoid duplicating rendering work.
|
||||
///
|
||||
/// [`Window::request_redraw`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.request_redraw
|
||||
RedrawRequested,
|
||||
}
|
||||
|
||||
/// Identifier of an input device.
|
||||
///
|
||||
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
|
||||
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
|
||||
/// physical. Virtual devices typically aggregate inputs from multiple physical devices.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(pub(crate) platform_impl::DeviceId);
|
||||
|
||||
impl DeviceId {
|
||||
/// Returns a dummy id, useful for unit testing.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The only guarantee made about the return value of this function is that
|
||||
/// it will always be equal to itself and to future values returned by this function.
|
||||
/// No other guarantees are made. This may be equal to a real `DeviceId`.
|
||||
///
|
||||
/// **Passing this into a winit function will result in undefined behavior.**
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
#[allow(unused_unsafe)]
|
||||
DeviceId(unsafe { platform_impl::DeviceId::dummy() })
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents raw hardware events that are not associated with any particular window.
|
||||
///
|
||||
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person
|
||||
@@ -677,7 +665,7 @@ pub struct RawKeyEvent {
|
||||
|
||||
/// Describes a keyboard input targeting a window.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KeyEvent {
|
||||
pub struct KeyEvent<Extra> {
|
||||
/// Represents the position of a key independent of the currently active layout.
|
||||
///
|
||||
/// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode).
|
||||
@@ -727,7 +715,7 @@ pub struct KeyEvent {
|
||||
/// - **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
|
||||
/// [`key_without_modifiers`]: https://docs.rs/winit/latest/winit/platform/modifier_supplement/trait.KeyEventExtModifierSupplement.html#tymethod.key_without_modifiers
|
||||
pub logical_key: keyboard::Key,
|
||||
|
||||
/// Contains the text produced by this keypress.
|
||||
@@ -784,7 +772,7 @@ pub struct KeyEvent {
|
||||
/// modifiers applied.
|
||||
///
|
||||
/// On Android, iOS, Redox and Web, this type is a no-op.
|
||||
pub(crate) platform_specific: platform_impl::KeyEventExtra,
|
||||
pub extra: Extra,
|
||||
}
|
||||
|
||||
/// Describes keyboard modifiers event.
|
||||
@@ -799,6 +787,15 @@ pub struct Modifiers {
|
||||
}
|
||||
|
||||
impl Modifiers {
|
||||
/// Only `winit` should instantiate this!
|
||||
#[doc(hidden)]
|
||||
pub fn new(state: ModifiersState, pressed_mods: ModifiersKeys) -> Self {
|
||||
Self {
|
||||
state,
|
||||
pressed_mods,
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of the modifiers.
|
||||
pub fn state(&self) -> ModifiersState {
|
||||
self.state
|
||||
@@ -898,6 +895,8 @@ impl From<ModifiersState> for Modifiers {
|
||||
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
|
||||
/// Ime::Commit("啊不")
|
||||
/// ```
|
||||
///
|
||||
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Ime {
|
||||
@@ -906,6 +905,8 @@ pub enum Ime {
|
||||
/// After getting this event you could receive [`Preedit`](Self::Preedit) and
|
||||
/// [`Commit`](Self::Commit) events. You should also start performing IME related requests
|
||||
/// like [`Window::set_ime_cursor_area`].
|
||||
///
|
||||
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
|
||||
Enabled,
|
||||
|
||||
/// Notifies when a new composing text should be set at the cursor position.
|
||||
@@ -928,6 +929,8 @@ pub enum Ime {
|
||||
/// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You should
|
||||
/// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear pending
|
||||
/// preedit text.
|
||||
///
|
||||
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
|
||||
Disabled,
|
||||
}
|
||||
|
||||
@@ -1116,7 +1119,7 @@ pub struct InnerSizeWriter {
|
||||
|
||||
impl InnerSizeWriter {
|
||||
#[cfg(not(orbital_platform))]
|
||||
pub(crate) fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
|
||||
pub fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
|
||||
Self { new_inner_size }
|
||||
}
|
||||
|
||||
@@ -1124,14 +1127,19 @@ impl InnerSizeWriter {
|
||||
pub fn request_inner_size(
|
||||
&mut self,
|
||||
new_inner_size: PhysicalSize<u32>,
|
||||
) -> Result<(), ExternalError> {
|
||||
) -> Result<(), InnerSizeIgnored> {
|
||||
if let Some(inner) = self.new_inner_size.upgrade() {
|
||||
*inner.lock().unwrap() = new_inner_size;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ExternalError::Ignored)
|
||||
Err(InnerSizeIgnored { _private: () })
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the underlying size.
|
||||
pub fn get(&self) -> PhysicalSize<u32> {
|
||||
*self.new_inner_size.upgrade().unwrap().lock().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InnerSizeWriter {
|
||||
@@ -1140,6 +1148,25 @@ impl PartialEq for InnerSizeWriter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Could not write the inner size.
|
||||
pub struct InnerSizeIgnored {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl fmt::Debug for InnerSizeIgnored {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("InnerSizeIgnored").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InnerSizeIgnored {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("tried to write inner size after")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for InnerSizeIgnored {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::event;
|
||||
@@ -1199,13 +1226,13 @@ mod tests {
|
||||
state: event::ElementState::Pressed,
|
||||
button: event::MouseButton::Other(0),
|
||||
});
|
||||
with_window_event(TouchpadMagnify {
|
||||
with_window_event(PinchGesture {
|
||||
device_id: did,
|
||||
delta: 0.0,
|
||||
phase: event::TouchPhase::Started,
|
||||
});
|
||||
with_window_event(SmartMagnify { device_id: did });
|
||||
with_window_event(TouchpadRotate {
|
||||
with_window_event(DoubleTapGesture { device_id: did });
|
||||
with_window_event(RotationGesture {
|
||||
device_id: did,
|
||||
delta: 0.0,
|
||||
phase: event::TouchPhase::Started,
|
||||
@@ -1265,7 +1292,7 @@ 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);
|
||||
})
|
||||
@@ -1273,7 +1300,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_map_nonuser_event() {
|
||||
foreach_event!(|event: event::Event<()>| {
|
||||
foreach_event!(|event: event::Event<(), ()>| {
|
||||
let is_user = matches!(event, event::Event::UserEvent(()));
|
||||
let event2 = event.map_nonuser_event::<()>();
|
||||
if is_user {
|
||||
@@ -1307,7 +1334,7 @@ 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();
|
||||
80
winit-core/src/event_loop.rs
Normal file
80
winit-core/src/event_loop.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
//! Types needed to define the event loop.
|
||||
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time::{Duration, Instant};
|
||||
#[cfg(web_platform)]
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
/// A unique identifier of the winit's async request.
|
||||
///
|
||||
/// This could be used to identify the async request once it's done
|
||||
/// and a specific action must be taken.
|
||||
///
|
||||
/// One of the handling scenarious could be to maintain a working list
|
||||
/// containing [`AsyncRequestSerial`] and some closure associated with it.
|
||||
/// Then once event is arriving the working list is being traversed and a job
|
||||
/// executed and removed from the list.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct AsyncRequestSerial {
|
||||
serial: u64,
|
||||
}
|
||||
|
||||
impl AsyncRequestSerial {
|
||||
/// Get the next serial in the sequence.
|
||||
pub fn get() -> Self {
|
||||
static CURRENT_SERIAL: AtomicU64 = AtomicU64::new(0);
|
||||
// NOTE: we rely on wrap around here, while the user may just request
|
||||
// in the loop u64::MAX times that's issue is considered on them.
|
||||
let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
|
||||
Self { serial }
|
||||
}
|
||||
}
|
||||
|
||||
/// Set through [`EventLoopWindowTarget::set_control_flow()`].
|
||||
///
|
||||
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
|
||||
///
|
||||
/// Defaults to [`Wait`].
|
||||
///
|
||||
/// [`Wait`]: Self::Wait
|
||||
///
|
||||
/// [`EventLoopWindowTarget::set_control_flow()`]: https://docs.rs/winit/latest/winit/event_loop/struct.EventLoopWindowTarget.html#method.set_control_flow
|
||||
/// [`Event::AboutToWait`]: crate::event::Event::AboutToWait
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
/// whether or not new events are available to process.
|
||||
Poll,
|
||||
|
||||
/// When the current loop iteration finishes, suspend the thread until another event arrives.
|
||||
#[default]
|
||||
Wait,
|
||||
|
||||
/// When the current loop iteration finishes, suspend the thread until either another event
|
||||
/// 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.
|
||||
///
|
||||
/// [`Poll`]: Self::Poll
|
||||
WaitUntil(Instant),
|
||||
}
|
||||
|
||||
impl ControlFlow {
|
||||
/// Creates a [`ControlFlow`] that waits until a timeout has expired.
|
||||
///
|
||||
/// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
|
||||
/// instead set to [`Wait`].
|
||||
///
|
||||
/// [`WaitUntil`]: Self::WaitUntil
|
||||
/// [`Wait`]: Self::Wait
|
||||
pub fn wait_duration(timeout: Duration) -> Self {
|
||||
match Instant::now().checked_add(timeout) {
|
||||
Some(instant) => Self::WaitUntil(instant),
|
||||
None => Self::Wait,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,9 @@
|
||||
//
|
||||
// --------- END OF W3C SHORT NOTICE ---------------------------------------------------------------
|
||||
|
||||
use bitflags::bitflags;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use smol_str::SmolStr;
|
||||
|
||||
/// Contains the platform-native physical key identifier
|
||||
@@ -1451,6 +1454,29 @@ pub enum NamedKey {
|
||||
F35,
|
||||
}
|
||||
|
||||
// NOTE: the exact modifier key is not used to represent modifiers state in the
|
||||
// first place due to a fact that modifiers state could be changed without any
|
||||
// key being pressed and on some platforms like Wayland/X11 which key resulted
|
||||
// in modifiers change is hidden, also, not that it really matters.
|
||||
//
|
||||
// The reason this API is even exposed is mostly to provide a way for users
|
||||
// to treat modifiers differently based on their position, which is required
|
||||
// on macOS due to their AltGr/Option situation.
|
||||
bitflags! {
|
||||
#[doc(hidden)]
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ModifiersKeys: u8 {
|
||||
const LSHIFT = 0b0000_0001;
|
||||
const RSHIFT = 0b0000_0010;
|
||||
const LCONTROL = 0b0000_0100;
|
||||
const RCONTROL = 0b0000_1000;
|
||||
const LALT = 0b0001_0000;
|
||||
const RALT = 0b0010_0000;
|
||||
const LSUPER = 0b0100_0000;
|
||||
const RSUPER = 0b1000_0000;
|
||||
}
|
||||
}
|
||||
|
||||
/// Key represents the meaning of a keypress.
|
||||
///
|
||||
/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with
|
||||
@@ -1562,7 +1588,7 @@ impl NamedKey {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use winit::keyboard::NamedKey;
|
||||
/// use winit_core::keyboard::NamedKey;
|
||||
///
|
||||
/// assert_eq!(NamedKey::Enter.to_text(), Some("\r"));
|
||||
/// assert_eq!(NamedKey::F20.to_text(), None);
|
||||
@@ -1585,7 +1611,7 @@ impl Key {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use winit::keyboard::{NamedKey, Key};
|
||||
/// use winit_core::keyboard::{NamedKey, Key};
|
||||
///
|
||||
/// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
|
||||
/// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
|
||||
@@ -1724,28 +1750,6 @@ pub enum ModifiersKeyState {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
// NOTE: the exact modifier key is not used to represent modifiers state in the
|
||||
// first place due to a fact that modifiers state could be changed without any
|
||||
// key being pressed and on some platforms like Wayland/X11 which key resulted
|
||||
// in modifiers change is hidden, also, not that it really matters.
|
||||
//
|
||||
// The reason this API is even exposed is mostly to provide a way for users
|
||||
// to treat modifiers differently based on their position, which is required
|
||||
// on macOS due to their AltGr/Option situation.
|
||||
bitflags! {
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct ModifiersKeys: u8 {
|
||||
const LSHIFT = 0b0000_0001;
|
||||
const RSHIFT = 0b0000_0010;
|
||||
const LCONTROL = 0b0000_0100;
|
||||
const RCONTROL = 0b0000_1000;
|
||||
const LALT = 0b0001_0000;
|
||||
const RALT = 0b0010_0000;
|
||||
const LSUPER = 0b0100_0000;
|
||||
const RSUPER = 0b1000_0000;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod modifiers_serde {
|
||||
use super::ModifiersState;
|
||||
18
winit-core/src/lib.rs
Normal file
18
winit-core/src/lib.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
//! Base types for a windowing library.
|
||||
//!
|
||||
//! This crate contains types, traits and basic functions from [`winit`] that are platform
|
||||
//! independent. It is intended to allow for other crates to build abstractions around [`winit`]
|
||||
//! without needing to pull in all of [`winit`]'s dependencies, as well as to provide an
|
||||
//! interface for alternative backends for [`winit`] to be constructed.
|
||||
//!
|
||||
//! [`winit`]: https://docs.rs/winit
|
||||
|
||||
#[cfg(any(not(feature = "std"), not(feature = "alloc")))]
|
||||
compile_error! { "no-std and no-alloc usage are not yet supported" }
|
||||
|
||||
pub mod dpi;
|
||||
pub mod error;
|
||||
pub mod event;
|
||||
pub mod event_loop;
|
||||
pub mod keyboard;
|
||||
pub mod window;
|
||||
229
winit-core/src/window.rs
Normal file
229
winit-core/src/window.rs
Normal file
@@ -0,0 +1,229 @@
|
||||
//! Types used in window construction.
|
||||
|
||||
#[doc(inline)]
|
||||
pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Identifier of a window. Unique for each window.
|
||||
///
|
||||
/// Can be obtained with [`window.id()`](https://docs.rs/winit/latest/winit/window/struct.Window.html#method.id).
|
||||
///
|
||||
/// Whenever you receive an event specific to a window, this event contains a `WindowId` which you
|
||||
/// can then compare to the ids of your windows.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(u64);
|
||||
|
||||
impl WindowId {
|
||||
/// Returns a dummy id, useful for unit testing.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The only guarantee made about the return value of this function is that
|
||||
/// it will always be equal to itself and to future values returned by this function.
|
||||
/// No other guarantees are made. This may be equal to a real [`WindowId`].
|
||||
///
|
||||
/// **Passing this into a winit function will result in undefined behavior.**
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
WindowId(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WindowId> for u64 {
|
||||
fn from(window_id: WindowId) -> Self {
|
||||
window_id.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for WindowId {
|
||||
fn from(raw_id: u64) -> Self {
|
||||
Self(raw_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// The behavior of cursor grabbing.
|
||||
///
|
||||
/// Use this enum with [`Window::set_cursor_grab`] to grab the cursor.
|
||||
///
|
||||
/// [`Window::set_cursor_grab`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_cursor_grab
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum CursorGrabMode {
|
||||
/// No grabbing of the cursor is performed.
|
||||
None,
|
||||
|
||||
/// The cursor is confined to the window area.
|
||||
///
|
||||
/// There's no guarantee that the cursor will be hidden. You should hide it by yourself if you
|
||||
/// want to do so.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
|
||||
/// - **iOS / Android / Web / Orbital:** Always returns an [`ExternalError::NotSupported`].
|
||||
///
|
||||
/// [`ExternalError::NotSupported`]: https://docs.rs/winit/latest/winit/error/enum.ExternalError.html#variant.NotSupported
|
||||
Confined,
|
||||
|
||||
/// The cursor is locked inside the window area to the certain position.
|
||||
///
|
||||
/// There's no guarantee that the cursor will be hidden. You should hide it by yourself if you
|
||||
/// want to do so.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
|
||||
/// - **iOS / Android / Orbital:** Always returns an [`ExternalError::NotSupported`].
|
||||
///
|
||||
/// [`ExternalError::NotSupported`]: https://docs.rs/winit/latest/winit/error/enum.ExternalError.html#variant.NotSupported
|
||||
Locked,
|
||||
}
|
||||
|
||||
/// Defines the orientation that a window resize will be performed.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ResizeDirection {
|
||||
East,
|
||||
North,
|
||||
NorthEast,
|
||||
NorthWest,
|
||||
South,
|
||||
SouthEast,
|
||||
SouthWest,
|
||||
West,
|
||||
}
|
||||
|
||||
impl From<ResizeDirection> for CursorIcon {
|
||||
fn from(direction: ResizeDirection) -> Self {
|
||||
use ResizeDirection::*;
|
||||
match direction {
|
||||
East => CursorIcon::EResize,
|
||||
North => CursorIcon::NResize,
|
||||
NorthEast => CursorIcon::NeResize,
|
||||
NorthWest => CursorIcon::NwResize,
|
||||
South => CursorIcon::SResize,
|
||||
SouthEast => CursorIcon::SeResize,
|
||||
SouthWest => CursorIcon::SwResize,
|
||||
West => CursorIcon::WResize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The theme variant to use.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Theme {
|
||||
/// Use the light variant.
|
||||
Light,
|
||||
|
||||
/// Use the dark variant.
|
||||
Dark,
|
||||
}
|
||||
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between [`Critical`] and [`Informational`].
|
||||
///
|
||||
/// [`Critical`]: Self::Critical
|
||||
/// [`Informational`]: Self::Informational
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub enum UserAttentionType {
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS:** Bounces the dock icon until the application is in focus.
|
||||
/// - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
|
||||
Critical,
|
||||
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS:** Bounces the dock icon once.
|
||||
/// - **Windows:** Flashes the taskbar button until the application is in focus.
|
||||
#[default]
|
||||
Informational,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct WindowButtons: u32 {
|
||||
const CLOSE = 1 << 0;
|
||||
const MINIMIZE = 1 << 1;
|
||||
const MAXIMIZE = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// A window level groups windows with respect to their z-position.
|
||||
///
|
||||
/// The relative ordering between windows in different window levels is fixed.
|
||||
/// The z-order of a window within the same window level may change dynamically on user interaction.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web / Wayland:** Unsupported.
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum WindowLevel {
|
||||
/// The window will always be below normal windows.
|
||||
///
|
||||
/// This is useful for a widget-based app.
|
||||
AlwaysOnBottom,
|
||||
|
||||
/// The default.
|
||||
#[default]
|
||||
Normal,
|
||||
|
||||
/// The window will always be on top of normal windows.
|
||||
AlwaysOnTop,
|
||||
}
|
||||
|
||||
/// Generic IME purposes for use in [`Window::set_ime_purpose`].
|
||||
///
|
||||
/// The purpose may improve UX by optimizing the IME for the specific use case,
|
||||
/// if winit can express the purpose to the platform and the platform reacts accordingly.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web / Windows / X11 / macOS / Orbital:** Unsupported.
|
||||
///
|
||||
/// [`Window::set_ime_purpose`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_purpose
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub enum ImePurpose {
|
||||
/// No special hints for the IME (default).
|
||||
Normal,
|
||||
/// The IME is used for password input.
|
||||
Password,
|
||||
/// The IME is used to input into a terminal.
|
||||
///
|
||||
/// For example, that could alter OSK on Wayland to show extra buttons.
|
||||
Terminal,
|
||||
}
|
||||
|
||||
impl Default for ImePurpose {
|
||||
fn default() -> Self {
|
||||
Self::Normal
|
||||
}
|
||||
}
|
||||
|
||||
/// An stringly-typed token used to activate the [`Window`].
|
||||
///
|
||||
/// [`Window`]: https://docs.rs/winit/latest/winit/window/struct.Window.html
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct ActivationToken {
|
||||
pub(crate) token: String,
|
||||
}
|
||||
|
||||
impl ActivationToken {
|
||||
/// Create a new [`ActivationToken`].
|
||||
pub fn new(token: String) -> Self {
|
||||
Self { token }
|
||||
}
|
||||
|
||||
/// Get the underlying token.
|
||||
pub fn token(&self) -> &str {
|
||||
&self.token
|
||||
}
|
||||
|
||||
/// Convert into the underlying token.
|
||||
pub fn into_token(self) -> String {
|
||||
self.token
|
||||
}
|
||||
}
|
||||
261
winit/Cargo.toml
Normal file
261
winit/Cargo.toml
Normal file
@@ -0,0 +1,261 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.29.10"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2021"
|
||||
keywords = ["windowing"]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/rust-windowing/winit"
|
||||
documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
rust-version = "1.70.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [
|
||||
"rwh_04",
|
||||
"rwh_05",
|
||||
"rwh_06",
|
||||
"serde",
|
||||
"mint",
|
||||
# Enabled to get docs to compile
|
||||
"android-native-activity",
|
||||
]
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
# These are all tested in CI
|
||||
targets = [
|
||||
# Windows
|
||||
"i686-pc-windows-msvc",
|
||||
"x86_64-pc-windows-msvc",
|
||||
# macOS
|
||||
"x86_64-apple-darwin",
|
||||
# Unix (X11 & Wayland)
|
||||
"i686-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
# iOS
|
||||
"x86_64-apple-ios",
|
||||
# Android
|
||||
"aarch64-linux-android",
|
||||
# Web
|
||||
"wasm32-unknown-unknown",
|
||||
]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
|
||||
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"]
|
||||
wayland-dlopen = ["wayland-backend/dlopen"]
|
||||
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
|
||||
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
|
||||
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
|
||||
android-native-activity = ["android-activity/native-activity"]
|
||||
android-game-activity = ["android-activity/game-activity"]
|
||||
mint = ["winit-core/mint"]
|
||||
serde = ["dep:serde", "winit-core/serde", "cursor-icon/serde", "smol_str/serde"]
|
||||
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
|
||||
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
|
||||
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitflags.workspace = true
|
||||
cursor-icon.workspace = true
|
||||
log = "0.4"
|
||||
once_cell = "1.12"
|
||||
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 }
|
||||
serde = { workspace = true, optional = true }
|
||||
smol_str.workspace = true
|
||||
winit-core.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
image = { version = "0.24.0", default-features = false, features = ["png"] }
|
||||
simple_logger = { version = "4.2.0", default_features = false }
|
||||
winit = { path = ".", features = ["rwh_05"] }
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
|
||||
softbuffer = { version = "0.3.0", default-features = false, features = ["x11", "x11-dlopen", "wayland", "wayland-dlopen"] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android-activity = "0.5.0"
|
||||
ndk = { version = "0.8.0", default-features = false }
|
||||
ndk-sys = "0.5.0"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = "0.5.0"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-graphics = "0.23.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.icrate]
|
||||
version = "0.1.0"
|
||||
features = [
|
||||
"dispatch",
|
||||
"Foundation",
|
||||
"Foundation_NSArray",
|
||||
"Foundation_NSAttributedString",
|
||||
"Foundation_NSMutableAttributedString",
|
||||
"Foundation_NSData",
|
||||
"Foundation_NSDictionary",
|
||||
"Foundation_NSString",
|
||||
"Foundation_NSProcessInfo",
|
||||
"Foundation_NSThread",
|
||||
"Foundation_NSNumber",
|
||||
"AppKit",
|
||||
"AppKit_NSAppearance",
|
||||
"AppKit_NSApplication",
|
||||
"AppKit_NSBitmapImageRep",
|
||||
"AppKit_NSButton",
|
||||
"AppKit_NSColor",
|
||||
"AppKit_NSControl",
|
||||
"AppKit_NSCursor",
|
||||
"AppKit_NSEvent",
|
||||
"AppKit_NSGraphicsContext",
|
||||
"AppKit_NSImage",
|
||||
"AppKit_NSImageRep",
|
||||
"AppKit_NSMenu",
|
||||
"AppKit_NSMenuItem",
|
||||
"AppKit_NSPasteboard",
|
||||
"AppKit_NSResponder",
|
||||
"AppKit_NSScreen",
|
||||
"AppKit_NSTextInputContext",
|
||||
"AppKit_NSView",
|
||||
"AppKit_NSWindow",
|
||||
"AppKit_NSWindowTabGroup",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies.icrate]
|
||||
version = "0.1.0"
|
||||
features = [
|
||||
"dispatch",
|
||||
"Foundation",
|
||||
"Foundation_NSArray",
|
||||
"Foundation_NSString",
|
||||
"Foundation_NSProcessInfo",
|
||||
"Foundation_NSThread",
|
||||
"Foundation_NSSet",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
unicode-segmentation = "1.7.1"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
|
||||
version = "0.48"
|
||||
features = [
|
||||
"Win32_Devices_HumanInterfaceDevice",
|
||||
"Win32_Foundation",
|
||||
"Win32_Globalization",
|
||||
"Win32_Graphics_Dwm",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_Media",
|
||||
"Win32_System_Com_StructuredStorage",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Ole",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_WindowsProgramming",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_UI_Controls",
|
||||
"Win32_UI_HiDpi",
|
||||
"Win32_UI_Input_Ime",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
"Win32_UI_Input_Pointer",
|
||||
"Win32_UI_Input_Touch",
|
||||
"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]
|
||||
ahash = { version = "0.8.3", features = ["no-rng"], optional = true }
|
||||
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
||||
calloop = "0.12.3"
|
||||
libc = "0.2.64"
|
||||
memmap2 = { version = "0.9.0", optional = true }
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
|
||||
sctk-adwaita = { version = "0.8.0", default_features = false, optional = true }
|
||||
wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
|
||||
wayland-client = { version = "0.31.1", optional = true }
|
||||
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
|
||||
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
|
||||
xkbcommon-dl = "0.4.0"
|
||||
|
||||
[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',
|
||||
]
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
js-sys = "0.3.64"
|
||||
pin-project = "1"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-time.workspace = true
|
||||
|
||||
[target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies]
|
||||
atomic-waker = "1"
|
||||
concurrent-queue = { version = "2", default-features = false }
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dev-dependencies]
|
||||
console_log = "1"
|
||||
web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
|
||||
|
||||
[[example]]
|
||||
doc-scrape-examples = true
|
||||
name = "window"
|
||||
1
winit/README.md
Symbolic link
1
winit/README.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../README.md
|
||||
24
winit/build.rs
Normal file
24
winit/build.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use cfg_aliases::cfg_aliases;
|
||||
|
||||
fn main() {
|
||||
// The script doesn't depend on our code
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
// Setup cfg aliases
|
||||
cfg_aliases! {
|
||||
// Systems.
|
||||
android_platform: { target_os = "android" },
|
||||
web_platform: { all(target_family = "wasm", target_os = "unknown") },
|
||||
macos_platform: { target_os = "macos" },
|
||||
ios_platform: { target_os = "ios" },
|
||||
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")) },
|
||||
redox: { target_os = "redox" },
|
||||
|
||||
// Native displays.
|
||||
x11_platform: { all(feature = "x11", free_unix, not(redox)) },
|
||||
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
|
||||
orbital_platform: { redox },
|
||||
}
|
||||
}
|
||||
@@ -18,16 +18,16 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
raw_window_handle::HasRawWindowHandle,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
window::{Window, WindowId},
|
||||
};
|
||||
|
||||
fn spawn_child_window(
|
||||
parent: &Window,
|
||||
event_loop: &EventLoopWindowTarget<()>,
|
||||
event_loop: &EventLoopWindowTarget,
|
||||
windows: &mut HashMap<WindowId, Window>,
|
||||
) {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let mut builder = WindowBuilder::new()
|
||||
let mut builder = Window::builder()
|
||||
.with_title("child window")
|
||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
@@ -44,7 +44,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
let mut windows = HashMap::new();
|
||||
|
||||
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
|
||||
let parent_window = WindowBuilder::new()
|
||||
let parent_window = Window::builder()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32))
|
||||
@@ -1,9 +1,9 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::thread;
|
||||
#[cfg(not(wasm_platform))]
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time;
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
use web_time as time;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
@@ -11,7 +11,7 @@ use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
keyboard::{Key, NamedKey},
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -37,7 +37,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
println!("Press 'Esc' to close the window.");
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::{CursorIcon, WindowBuilder},
|
||||
window::{CursorIcon, Window},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
let window = Window::builder().build(&event_loop).unwrap();
|
||||
window.set_title("A fantastic window!");
|
||||
|
||||
let mut cursor_idx = 0;
|
||||
@@ -31,7 +31,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
..
|
||||
} => {
|
||||
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
|
||||
window.set_cursor_icon(CURSORS[cursor_idx]);
|
||||
window.set_cursor(CURSORS[cursor_idx]);
|
||||
if cursor_idx < CURSORS.len() - 1 {
|
||||
cursor_idx += 1;
|
||||
} else {
|
||||
@@ -5,7 +5,7 @@ use winit::{
|
||||
event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState, NamedKey},
|
||||
window::{CursorGrabMode, WindowBuilder},
|
||||
window::{CursorGrabMode, Window},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -15,7 +15,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
143
winit/examples/custom_cursors.rs
Normal file
143
winit/examples/custom_cursors.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
#![allow(clippy::single_match, clippy::disallowed_methods)]
|
||||
|
||||
#[cfg(not(web_platform))]
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
keyboard::Key,
|
||||
window::{CursorIcon, CustomCursor, Window},
|
||||
};
|
||||
#[cfg(web_platform)]
|
||||
use {
|
||||
std::sync::atomic::{AtomicU64, Ordering},
|
||||
std::time::Duration,
|
||||
winit::platform::web::CustomCursorExtWebSys,
|
||||
};
|
||||
|
||||
#[cfg(web_platform)]
|
||||
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
fn decode_cursor(bytes: &[u8], window_target: &EventLoopWindowTarget) -> CustomCursor {
|
||||
let img = image::load_from_memory(bytes).unwrap().to_rgba8();
|
||||
let samples = img.into_flat_samples();
|
||||
let (_, w, h) = samples.extents();
|
||||
let (w, h) = (w as u16, h as u16);
|
||||
let builder = CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap();
|
||||
|
||||
builder.build(window_target)
|
||||
}
|
||||
|
||||
#[cfg(not(web_platform))]
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
#[cfg(not(web_platform))]
|
||||
SimpleLogger::new()
|
||||
.with_level(log::LevelFilter::Info)
|
||||
.init()
|
||||
.unwrap();
|
||||
#[cfg(web_platform)]
|
||||
console_log::init_with_level(log::Level::Debug).unwrap();
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let builder = Window::builder().with_title("A fantastic window!");
|
||||
#[cfg(web_platform)]
|
||||
let builder = {
|
||||
use winit::platform::web::WindowBuilderExtWebSys;
|
||||
builder.with_append(true)
|
||||
};
|
||||
let window = builder.build(&event_loop).unwrap();
|
||||
|
||||
let mut cursor_idx = 0;
|
||||
let mut cursor_visible = true;
|
||||
|
||||
let custom_cursors = [
|
||||
decode_cursor(include_bytes!("data/cross.png"), &event_loop),
|
||||
decode_cursor(include_bytes!("data/cross2.png"), &event_loop),
|
||||
decode_cursor(include_bytes!("data/gradient.png"), &event_loop),
|
||||
];
|
||||
|
||||
event_loop.run(move |event, _elwt| match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
logical_key: key,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("1") => {
|
||||
log::debug!("Setting cursor to {:?}", cursor_idx);
|
||||
window.set_cursor(custom_cursors[cursor_idx].clone());
|
||||
cursor_idx = (cursor_idx + 1) % 3;
|
||||
}
|
||||
Key::Character("2") => {
|
||||
log::debug!("Setting cursor icon to default");
|
||||
window.set_cursor(CursorIcon::default());
|
||||
}
|
||||
Key::Character("3") => {
|
||||
cursor_visible = !cursor_visible;
|
||||
log::debug!("Setting cursor visibility to {:?}", cursor_visible);
|
||||
window.set_cursor_visible(cursor_visible);
|
||||
}
|
||||
#[cfg(web_platform)]
|
||||
Key::Character("4") => {
|
||||
log::debug!("Setting cursor to a random image from an URL");
|
||||
window.set_cursor(
|
||||
CustomCursor::from_url(
|
||||
format!(
|
||||
"https://picsum.photos/128?random={}",
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||
),
|
||||
64,
|
||||
64,
|
||||
)
|
||||
.build(_elwt),
|
||||
);
|
||||
}
|
||||
#[cfg(web_platform)]
|
||||
Key::Character("5") => {
|
||||
log::debug!("Setting cursor to an animation");
|
||||
window.set_cursor(
|
||||
CustomCursor::from_animation(
|
||||
Duration::from_secs(3),
|
||||
vec![
|
||||
custom_cursors[0].clone(),
|
||||
custom_cursors[1].clone(),
|
||||
CustomCursor::from_url(
|
||||
format!(
|
||||
"https://picsum.photos/128?random={}",
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||
),
|
||||
64,
|
||||
64,
|
||||
)
|
||||
.build(_elwt),
|
||||
],
|
||||
)
|
||||
.unwrap()
|
||||
.build(_elwt),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
#[cfg(not(web_platform))]
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
#[cfg(not(web_platform))]
|
||||
_elwt.exit();
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
#[cfg(not(web_platform))]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoopBuilder,
|
||||
window::WindowBuilder,
|
||||
event_loop::EventLoop,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -18,11 +18,9 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoopBuilder::<CustomEvent>::with_user_event()
|
||||
.build()
|
||||
.unwrap();
|
||||
let event_loop = EventLoop::<CustomEvent>::with_user_event().build().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -56,7 +54,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
fn main() {
|
||||
panic!("This example is not supported on web.");
|
||||
}
|
||||
BIN
winit/examples/data/cross.png
Normal file
BIN
winit/examples/data/cross.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 B |
BIN
winit/examples/data/cross2.png
Normal file
BIN
winit/examples/data/cross2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 129 B |
BIN
winit/examples/data/gradient.png
Normal file
BIN
winit/examples/data/gradient.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 229 B |
@@ -5,7 +5,7 @@ use winit::{
|
||||
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
window::{Window, WindowId},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -15,8 +15,8 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window_1 = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
let window_2 = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
let window_1 = Window::builder().build(&event_loop).unwrap();
|
||||
let window_2 = Window::builder().build(&event_loop).unwrap();
|
||||
|
||||
let mut switched = false;
|
||||
let mut entered_id = window_2.id();
|
||||
@@ -3,14 +3,14 @@
|
||||
//! Example for focusing a window.
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
#[cfg(not(wasm_platform))]
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time;
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
use web_time as time;
|
||||
use winit::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(&event_loop)
|
||||
@@ -1,11 +1,11 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::dpi::PhysicalSize;
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::{Fullscreen, WindowBuilder};
|
||||
use winit::window::{Fullscreen, Window};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::platform::macos::WindowExtMacOS;
|
||||
@@ -22,7 +22,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
let mut with_min_size = false;
|
||||
let mut with_max_size = false;
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("Hello world!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -126,7 +126,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
"i" => {
|
||||
with_min_size = !with_min_size;
|
||||
let min_size = if with_min_size {
|
||||
Some(PhysicalSize::new(100, 100))
|
||||
Some(LogicalSize::new(100, 100))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -139,7 +139,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
"a" => {
|
||||
with_max_size = !with_max_size;
|
||||
let max_size = if with_max_size {
|
||||
Some(PhysicalSize::new(200, 200))
|
||||
Some(LogicalSize::new(200, 200))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -5,7 +5,7 @@ use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -15,7 +15,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("Your faithful window")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -7,7 +7,7 @@ use winit::{
|
||||
event::{ElementState, Event, Ime, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::NamedKey,
|
||||
window::{ImePurpose, WindowBuilder},
|
||||
window::{ImePurpose, Window},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -26,7 +26,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -8,7 +8,7 @@ use winit::{
|
||||
keyboard::{Key, ModifiersState},
|
||||
// WARNING: This is not available on all platforms (for example on the web).
|
||||
platform::modifier_supplement::KeyEventExtModifierSupplement,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
@@ -24,7 +24,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
simple_logger::SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_inner_size(LogicalSize::new(400.0, 200.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -3,12 +3,12 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use winit::monitor::MonitorHandle;
|
||||
use winit::{event_loop::EventLoop, window::WindowBuilder};
|
||||
use winit::{event_loop::EventLoop, window::Window};
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
let window = Window::builder().build(&event_loop).unwrap();
|
||||
|
||||
if let Some(mon) = window.primary_monitor() {
|
||||
print_info("Primary output", mon);
|
||||
@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("Mouse Wheel events")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
#[cfg(not(web_platform))]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
|
||||
|
||||
@@ -10,7 +10,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState, NamedKey},
|
||||
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel},
|
||||
window::{CursorGrabMode, CursorIcon, Fullscreen, Window, WindowLevel},
|
||||
};
|
||||
|
||||
const WINDOW_COUNT: usize = 3;
|
||||
@@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
|
||||
for _ in 0..WINDOW_COUNT {
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_inner_size(WINDOW_SIZE)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -84,7 +84,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
"1" => window.set_window_level(WindowLevel::AlwaysOnTop),
|
||||
"2" => window.set_window_level(WindowLevel::AlwaysOnBottom),
|
||||
"3" => window.set_window_level(WindowLevel::Normal),
|
||||
"c" => window.set_cursor_icon(match state {
|
||||
"c" => window.set_cursor(match state {
|
||||
true => CursorIcon::Progress,
|
||||
false => CursorIcon::Default,
|
||||
}),
|
||||
@@ -96,21 +96,13 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
)),
|
||||
(false, _) => None,
|
||||
}),
|
||||
"l" if state => {
|
||||
if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked)
|
||||
{
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
"g" if state => {
|
||||
if let Err(err) =
|
||||
window.set_cursor_grab(CursorGrabMode::Confined)
|
||||
{
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
"g" | "l" if !state => {
|
||||
if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
|
||||
ch @ ("g" | "l") => {
|
||||
let mode = match (ch, state) {
|
||||
("l", true) => CursorGrabMode::Locked,
|
||||
("g", true) => CursorGrabMode::Confined,
|
||||
(_, _) => CursorGrabMode::None,
|
||||
};
|
||||
if let Err(err) = window.set_cursor_grab(mode) {
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
@@ -123,10 +115,6 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
println!("-> inner_size : {:?}", window.inner_size());
|
||||
println!("-> fullscreen : {:?}", window.fullscreen());
|
||||
}
|
||||
"l" => window.set_min_inner_size(match state {
|
||||
true => Some(WINDOW_SIZE),
|
||||
false => None,
|
||||
}),
|
||||
"m" => window.set_maximized(state),
|
||||
"p" => window.set_outer_position({
|
||||
let mut position = window.outer_position().unwrap();
|
||||
@@ -140,12 +128,26 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
"s" => {
|
||||
let _ = window.request_inner_size(match state {
|
||||
true => PhysicalSize::new(
|
||||
WINDOW_SIZE.width + 100,
|
||||
WINDOW_SIZE.height + 100,
|
||||
WINDOW_SIZE.width + 50,
|
||||
WINDOW_SIZE.height + 50,
|
||||
),
|
||||
false => WINDOW_SIZE,
|
||||
});
|
||||
}
|
||||
"k" => window.set_min_inner_size(match state {
|
||||
true => Some(PhysicalSize::new(
|
||||
WINDOW_SIZE.width - 100,
|
||||
WINDOW_SIZE.height - 100,
|
||||
)),
|
||||
false => None,
|
||||
}),
|
||||
"o" => window.set_max_inner_size(match state {
|
||||
true => Some(PhysicalSize::new(
|
||||
WINDOW_SIZE.width + 100,
|
||||
WINDOW_SIZE.height + 100,
|
||||
)),
|
||||
false => None,
|
||||
}),
|
||||
"w" => {
|
||||
if let Size::Physical(size) = WINDOW_SIZE.into() {
|
||||
window
|
||||
@@ -203,7 +205,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
fn main() {
|
||||
panic!("Example not supported on Wasm");
|
||||
panic!("Example not supported on Web");
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
#[cfg(not(web_platform))]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::{sync::Arc, thread, time};
|
||||
|
||||
@@ -8,7 +8,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -18,7 +18,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = {
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -53,7 +53,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
fn main() {
|
||||
unimplemented!() // `Window` can't be sent between threads
|
||||
}
|
||||
@@ -6,7 +6,7 @@ use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{KeyCode, PhysicalKey},
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -18,7 +18,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let mut resizable = false;
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("Hit space to toggle resizability.")
|
||||
.with_inner_size(LogicalSize::new(600.0, 300.0))
|
||||
.with_min_inner_size(LogicalSize::new(400.0, 200.0))
|
||||
@@ -14,7 +14,7 @@ mod example {
|
||||
use winit::platform::startup_notify::{
|
||||
EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify,
|
||||
};
|
||||
use winit::window::{Window, WindowBuilder, WindowId};
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
pub(super) fn main() -> Result<(), impl std::error::Error> {
|
||||
// Create the event loop and get the activation token.
|
||||
@@ -84,8 +84,7 @@ mod example {
|
||||
if current_token.is_some() || create_first_window {
|
||||
// Create the initial window.
|
||||
let window = {
|
||||
let mut builder =
|
||||
WindowBuilder::new().with_title(format!("Window {}", counter));
|
||||
let mut builder = Window::builder().with_title(format!("Window {}", counter));
|
||||
|
||||
if let Some(token) = current_token.take() {
|
||||
println!("Creating a window with token {token:?}");
|
||||
@@ -5,7 +5,7 @@ use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{Theme, WindowBuilder},
|
||||
window::{Theme, Window},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -15,7 +15,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.with_theme(Some(Theme::Dark))
|
||||
.build(&event_loop)
|
||||
@@ -1,16 +1,16 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::time::Duration;
|
||||
#[cfg(not(wasm_platform))]
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time::Instant;
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
use web_time::Instant;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -2,7 +2,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -12,32 +12,44 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("Touchpad gestures")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
#[cfg(target_os = "ios")]
|
||||
{
|
||||
use winit::platform::ios::WindowExtIOS;
|
||||
window.recognize_doubletap_gesture(true);
|
||||
window.recognize_pinch_gesture(true);
|
||||
window.recognize_rotation_gesture(true);
|
||||
}
|
||||
|
||||
println!("Only supported on macOS at the moment.");
|
||||
println!("Only supported on macOS/iOS at the moment.");
|
||||
|
||||
let mut zoom = 0.0;
|
||||
let mut rotated = 0.0;
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::TouchpadMagnify { delta, .. } => {
|
||||
WindowEvent::PinchGesture { delta, .. } => {
|
||||
zoom += delta;
|
||||
if delta > 0.0 {
|
||||
println!("Zoomed in {delta}");
|
||||
println!("Zoomed in {delta:.5} (now: {zoom:.5})");
|
||||
} else {
|
||||
println!("Zoomed out {delta}");
|
||||
println!("Zoomed out {delta:.5} (now: {zoom:.5})");
|
||||
}
|
||||
}
|
||||
WindowEvent::SmartMagnify { .. } => {
|
||||
WindowEvent::DoubleTapGesture { .. } => {
|
||||
println!("Smart zoom");
|
||||
}
|
||||
WindowEvent::TouchpadRotate { delta, .. } => {
|
||||
WindowEvent::RotationGesture { delta, .. } => {
|
||||
rotated += delta;
|
||||
if delta > 0.0 {
|
||||
println!("Rotated counterclockwise {delta}");
|
||||
println!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
|
||||
} else {
|
||||
println!("Rotated clockwise {delta}");
|
||||
println!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_decorations(false)
|
||||
.with_transparent(true)
|
||||
.build(&event_loop)
|
||||
116
winit/examples/util/fill.rs
Normal file
116
winit/examples/util/fill.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
//! Fill the window buffer with a solid color.
|
||||
//!
|
||||
//! Launching a window without drawing to it has unpredictable results varying from platform to
|
||||
//! platform. In order to have well-defined examples, this module provides an easy way to
|
||||
//! fill the window buffer with a solid color.
|
||||
//!
|
||||
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
|
||||
//! also be used to fill the window buffer, but they are more complicated to use.
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use platform::cleanup_window;
|
||||
pub use platform::fill_window;
|
||||
|
||||
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
|
||||
mod platform {
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use softbuffer::{Context, Surface};
|
||||
use winit::window::Window;
|
||||
use winit::window::WindowId;
|
||||
|
||||
thread_local! {
|
||||
// NOTE: You should never do things like that, create context and drop it before
|
||||
// you drop the event loop. We do this for brevity to not blow up examples. We use
|
||||
// ManuallyDrop to prevent destructors from running.
|
||||
//
|
||||
// A static, thread-local map of graphics contexts to open windows.
|
||||
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
|
||||
}
|
||||
|
||||
/// The graphics context used to draw to a window.
|
||||
struct GraphicsContext {
|
||||
/// The global softbuffer context.
|
||||
context: Context,
|
||||
|
||||
/// The hash map of window IDs to surfaces.
|
||||
surfaces: HashMap<WindowId, Surface>,
|
||||
}
|
||||
|
||||
impl GraphicsContext {
|
||||
fn new(w: &Window) -> Self {
|
||||
Self {
|
||||
context: unsafe { Context::new(w) }.expect("Failed to create a softbuffer context"),
|
||||
surfaces: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_surface(&mut self, window: &Window) -> &mut Surface {
|
||||
self.surfaces.entry(window.id()).or_insert_with(|| {
|
||||
unsafe { Surface::new(&self.context, window) }
|
||||
.expect("Failed to create a softbuffer surface")
|
||||
})
|
||||
}
|
||||
|
||||
fn destroy_surface(&mut self, window: &Window) {
|
||||
self.surfaces.remove(&window.id());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_window(window: &Window) {
|
||||
GC.with(|gc| {
|
||||
let size = window.inner_size();
|
||||
let (Some(width), Some(height)) =
|
||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Either get the last context used or create a new one.
|
||||
let mut gc = gc.borrow_mut();
|
||||
let surface = gc
|
||||
.get_or_insert_with(|| GraphicsContext::new(window))
|
||||
.create_surface(window);
|
||||
|
||||
// Fill a buffer with a solid color.
|
||||
const DARK_GRAY: u32 = 0xFF181818;
|
||||
|
||||
surface
|
||||
.resize(width, height)
|
||||
.expect("Failed to resize the softbuffer surface");
|
||||
|
||||
let mut buffer = surface
|
||||
.buffer_mut()
|
||||
.expect("Failed to get the softbuffer buffer");
|
||||
buffer.fill(DARK_GRAY);
|
||||
buffer
|
||||
.present()
|
||||
.expect("Failed to present the softbuffer buffer");
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(window: &Window) {
|
||||
GC.with(|gc| {
|
||||
let mut gc = gc.borrow_mut();
|
||||
if let Some(context) = gc.as_mut() {
|
||||
context.destroy_surface(window);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
|
||||
mod platform {
|
||||
pub fn fill_window(_window: &winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(_window: &winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,13 @@ use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
window::{Fullscreen, Window},
|
||||
};
|
||||
|
||||
pub fn main() -> Result<(), impl std::error::Error> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let builder = WindowBuilder::new().with_title("A fantastic window!");
|
||||
let builder = Window::builder().with_title("A fantastic window!");
|
||||
#[cfg(wasm_platform)]
|
||||
let builder = {
|
||||
use winit::platform::web::WindowBuilderExtWebSys;
|
||||
@@ -18,11 +18,11 @@ pub fn main() -> Result<(), impl std::error::Error> {
|
||||
};
|
||||
let window = builder.build(&event_loop).unwrap();
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
let log_list = wasm::insert_canvas_and_create_log_list(&window);
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
wasm::log_event(&log_list, &event);
|
||||
|
||||
match event {
|
||||
@@ -57,7 +57,7 @@ pub fn main() -> Result<(), impl std::error::Error> {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
mod wasm {
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
@@ -4,7 +4,7 @@ pub fn main() {
|
||||
println!("This example must be run with cargo run-wasm --example web_aspect_ratio")
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
#[cfg(web_platform)]
|
||||
mod wasm {
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
@@ -14,7 +14,7 @@ mod wasm {
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
platform::web::WindowBuilderExtWebSys,
|
||||
window::{Window, WindowBuilder},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
const EXPLANATION: &str = "
|
||||
@@ -33,7 +33,7 @@ This example demonstrates the desired future functionality which will possibly b
|
||||
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
// When running in a non-wasm environment this would set the window size to 100x100.
|
||||
// However in this example it just sets a default initial size of 100x100 that is immediately overwritten due to the layout + styling of the page.
|
||||
@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(&event_loop)
|
||||
@@ -8,7 +8,7 @@ use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{DeviceEvents, EventLoop},
|
||||
keyboard::Key,
|
||||
window::{WindowBuilder, WindowButtons},
|
||||
window::{Window, WindowButtons},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -18,7 +18,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(LogicalSize::new(300.0, 300.0))
|
||||
.build(&event_loop)
|
||||
@@ -8,7 +8,7 @@ use winit::{
|
||||
event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent},
|
||||
event_loop::{DeviceEvents, EventLoop},
|
||||
keyboard::{Key, KeyCode, PhysicalKey},
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
window::{Fullscreen, Window},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -18,7 +18,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(LogicalSize::new(100.0, 100.0))
|
||||
.build(&event_loop)
|
||||
@@ -5,7 +5,7 @@ use winit::{
|
||||
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{CursorIcon, ResizeDirection, WindowBuilder},
|
||||
window::{CursorIcon, ResizeDirection, Window},
|
||||
};
|
||||
|
||||
const BORDER: f64 = 8.0;
|
||||
@@ -17,7 +17,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0))
|
||||
.with_min_inner_size(winit::dpi::LogicalSize::new(400.0, 200.0))
|
||||
.with_decorations(false)
|
||||
@@ -40,7 +40,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
if new_location != cursor_location {
|
||||
cursor_location = new_location;
|
||||
window.set_cursor_icon(cursor_direction_icon(cursor_location))
|
||||
window.set_cursor(cursor_direction_icon(cursor_location))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::{Icon, WindowBuilder},
|
||||
window::{Icon, Window},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -25,7 +25,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("An iconic window!")
|
||||
// At present, this only does anything on Windows and X11, so if you want to save load
|
||||
// time, you can put icon loading behind a function that returns `None` on other platforms.
|
||||
@@ -12,7 +12,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
platform::run_on_demand::EventLoopExtRunOnDemand,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
window::{Window, WindowId},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -40,6 +40,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
window_id,
|
||||
} if window.id() == window_id => {
|
||||
println!("--------------------------------------------------------- Window {idx} CloseRequested");
|
||||
fill::cleanup_window(window);
|
||||
app.window = None;
|
||||
}
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
@@ -64,7 +65,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
_ => (),
|
||||
}
|
||||
} else if let Event::Resumed = event {
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("Fantastic window number one!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(elwt)
|
||||
@@ -8,7 +8,7 @@ use winit::{
|
||||
event::ElementState,
|
||||
event::{Event, MouseButton, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -21,7 +21,7 @@ mod fill;
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(&event_loop)
|
||||
@@ -16,7 +16,7 @@ fn main() -> std::process::ExitCode {
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
|
||||
window::WindowBuilder,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -25,7 +25,7 @@ fn main() -> std::process::ExitCode {
|
||||
let mut event_loop = EventLoop::new().unwrap();
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let window = WindowBuilder::new()
|
||||
let window = Window::builder()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -68,7 +68,7 @@ fn main() -> std::process::ExitCode {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(ios_platform, wasm_platform, orbital_platform))]
|
||||
#[cfg(any(ios_platform, web_platform, orbital_platform))]
|
||||
fn main() {
|
||||
println!("This platform doesn't support pump_events.");
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user