Compare commits

..

4 Commits

Author SHA1 Message Date
Mads Marquart
d7b6f0c63d TMP 2024-03-15 16:27:29 +01:00
Mads Marquart
f40f87ff12 TMP 2024-03-15 16:04:37 +01:00
Mads Marquart
aa3127e460 Test
super long message  awdjbhkwbegfkhjewvbfhre vgfhj ejbsfhjkr besfhjkr besjkf breshjk gberj fn
2024-03-15 15:59:59 +01:00
Mads Marquart
295a789c42 Check commit message length in CI 2024-03-15 15:59:26 +01:00
230 changed files with 10065 additions and 10449 deletions

View File

@@ -1,6 +1,2 @@
# Allow rust-analyzer and local `cargo doc` invocations to pick up unreleased changelog entries [alias]
# run-wasm = ["run", "--release", "--package", "run-wasm", "--"]
# Note that these flags are (intentionally) not included when building from the downloaded crate.
[build]
rustflags = ["--cfg=unreleased_changelogs"]
rustdocflags = ["--cfg=unreleased_changelogs"]

View File

@@ -1,4 +0,0 @@
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
# chore(rustfmt): use nightly
7b0c7b6cb2c62767ca0c73c857b299883f55a883

8
.github/CODEOWNERS vendored
View File

@@ -1,6 +1,6 @@
# Android # Android
/src/platform/android.rs @MarijnS95 /src/platform/android.rs @msiglreith @MarijnS95
/src/platform_impl/android @MarijnS95 /src/platform_impl/android @msiglreith @MarijnS95
# iOS # iOS
/src/platform/ios.rs @madsmtm /src/platform/ios.rs @madsmtm
@@ -26,8 +26,8 @@
/src/platform_impl/web @daxpedda /src/platform_impl/web @daxpedda
# Windows # Windows
/src/platform/windows.rs @notgull /src/platform/windows.rs @msiglreith
/src/platform_impl/windows @notgull /src/platform_impl/windows @msiglreith
# Orbital (Redox OS) # Orbital (Redox OS)
/src/platform/orbital.rs @jackpot51 /src/platform/orbital.rs @jackpot51

View File

@@ -1,4 +0,0 @@
---
name: Blank Issue
about: Create a blank issue.
---

View File

@@ -1,36 +0,0 @@
name: Android bug
description: Create an Android-specific bug report
labels:
- B - bug
- DS - android
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: description
attributes:
label: Description
description: Description of the problem you're having
validations:
required: true
- type: textarea
id: device
attributes:
label: Device and Android version
description: Which devices and Android versions are you seeing the problem on?
placeholder: |
Samsung Galaxy Z running Android Pie (API level 28),
Samsung Galaxy Z running Android 14 (API level 34),
Pixel 8 running Android 14 (API level 34)
validations:
required: true
- type: textarea
id: winit-version
attributes:
label: Winit version
description: What version of Winit are you using?
placeholder: 0.29.11
validations:
required: true

View File

@@ -1,37 +0,0 @@
name: iOS bug
description: Create an iOS-specific bug report
labels:
- B - bug
- DS - ios
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: description
attributes:
label: Description
description: Description of the problem you're having
validations:
required: true
- type: textarea
id: device
attributes:
label: Device and iOS version
description: Which devices and iOS versions are you seeing the problem on?
placeholder: |
iPhone 15 running iOS 14.0,
iPhone 15 running iOS 17.0,
MacBook Pro M2 Mac Catalyst running macOS 14.2,
iPhone simulator running iOS 17.0
validations:
required: true
- type: textarea
id: winit-version
attributes:
label: Winit version
description: What version of Winit are you using?
placeholder: 0.29.11
validations:
required: true

View File

@@ -1,37 +0,0 @@
name: MacOS bug
description: Create a macOS-specific bug report
labels:
- B - bug
- DS - macos
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: description
attributes:
label: Description
description: Description of the problem you're having
validations:
required: true
- type: textarea
id: os-version
attributes:
label: macOS version
description: What version of macOS are you using? Please paste in the output of `sw_vers`.
placeholder: |
ProductName: macOS
ProductVersion: 14.2.1
BuildVersion: 23C71
render: shell
validations:
required: true
- type: textarea
id: winit-version
attributes:
label: Winit version
description: What version of Winit are you using?
placeholder: 0.29.11
validations:
required: true

View File

@@ -1,41 +0,0 @@
name: Wayland bug
description: Create a Wayland-specific bug report
labels:
- B - bug
- DS - wayland
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: description
attributes:
label: Description
description: Description of the problem you're having
validations:
required: true
- type: textarea
id: debug
attributes:
label: Debugging output
description: Output of a binary run with `WAYLAND_DEBUG=1`
placeholder: |
[1234.5678] -> wl_display@1.get_registry(new id wl_registry@2)
[1234.5678] -> wl_display@1.sync(new id wl_callback@3)
...
render: shell
- type: checkboxes
attributes:
label: Window isn't shown unless you draw
options:
- label: I understand that windows aren't shown on Wayland unless I draw and present to them.
required: true
- type: textarea
id: winit-version
attributes:
label: Winit version
description: What version of Winit are you using?
placeholder: 0.29.11
validations:
required: true

View File

@@ -1,51 +0,0 @@
name: Web bug
description: Create a web-specific bug report
labels:
- B - bug
- DS - web
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: description
attributes:
label: Description
description: Description of the problem you're having
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: Tested browsers
description: What browsers are you seeing the problem on?
options:
- Firefox
- Chrome
- Microsoft Edge
- Safari 13
- Safari 14
- Safari 15
- Safari 16
- Safari 17
- Safari (newer than listed)
multiple: true
validations:
required: true
- type: textarea
id: device
attributes:
label: Tested devices
description: Which device(s) are you using?
placeholder: 'iPhone 15, Lenovo ThinkPad X1, MacBook Pro M2, Samsung Galaxy Z, ...'
validations:
required: true
- type: textarea
id: winit-version
attributes:
label: Winit version
description: What version of Winit are you using?
placeholder: 0.29.11
validations:
required: true

View File

@@ -1,35 +0,0 @@
name: Windows bug
description: Create a Windows-specific bug report
labels:
- B - bug
- DS - windows
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: description
attributes:
label: Description
description: Description of the problem you're having
validations:
required: true
- type: textarea
id: os-version
attributes:
label: Windows version
description: What version of Windows are you using? Please paste in the output of the `ver` command.
placeholder: |
Microsoft Windows [Version 10.0.19042.2251]
render: shell
validations:
required: true
- type: textarea
id: winit-version
attributes:
label: Winit version
description: What version of Winit are you using?
placeholder: 0.29.11
validations:
required: true

View File

@@ -1,32 +0,0 @@
name: X11 bug
description: Create a X11-specific bug report
labels:
- B - bug
- DS - x11
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: description
attributes:
label: Description
description: Description of the problem you're having
validations:
required: true
- type: textarea
id: os-info
attributes:
label: OS and window mananger
description: Which operating system and window manager are you using?
validations:
required: true
- type: textarea
id: winit-version
attributes:
label: Winit version
description: What version of Winit are you using?
placeholder: 0.29.11
validations:
required: true

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Question
url: https://matrix.to/#/#rust-windowing:matrix.org
about: Please ask questions on the Matrix channel.

View File

@@ -1,26 +0,0 @@
name: Feature request
description: Propose a new feature
labels:
- S - enhancement
body:
- type: textarea
id: description
attributes:
label: Description
description: Description of the problem does this solve or what need does it fill? Please be mindful of the [scope](https://github.com/rust-windowing/winit/blob/master/FEATURES.md) of Winit.
validations:
required: true
- type: dropdown
id: platforms
attributes:
label: Relevant platforms
description: On which platforms is this feature relevant?
options:
- Windows
- macOS
- Wayland
- X11
- Web
- iOS
- Android
multiple: true

View File

@@ -1,5 +1,5 @@
- [ ] Tested on all platforms changed - [ ] Tested on all platforms changed
- [ ] Added an entry to the `changelog` module if knowledge of this change could be valuable to users - [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior - [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
- [ ] Created or updated an example program if it would help users understand this functionality - [ ] Created or updated an example program if it would help users understand this functionality
- [ ] Updated [feature matrix](https://github.com/rust-windowing/winit/blob/master/FEATURES.md), if new features were added or implemented - [ ] Updated [feature matrix](https://github.com/rust-windowing/winit/blob/master/FEATURES.md), if new features were added or implemented

24
.github/workflows/check-commit-msg.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Check Commit Messages
on:
pull_request:
jobs:
length:
name: Check that length of commit messages are <72 characters
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Fetch branch data from origin
run: git fetch origin
- run: |
COMMIT_MESSAGES=$(git log --combined --format=%s origin/$GITHUB_BASE_REF..HEAD)
echo $COMMIT_MESSAGES
for MESSAGE in $COMMIT_MESSAGES; do
LENGTH=$(echo $MESSAGE | wc -c)
if [ $LENGTH -gt 72 ]; then
echo "Commit message \"$MESSAGE\" is too long ($LENGTH characters). Please keep commit messages under 72 characters."
exit 1
fi
done

View File

@@ -10,8 +10,8 @@ jobs:
name: Check formatting name: Check formatting
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: taiki-e/checkout-action@v1 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/rust-toolchain@stable
with: with:
components: rustfmt components: rustfmt
- name: Check Formatting - name: Check Formatting
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- uses: taiki-e/checkout-action@v1 - uses: actions/checkout@v4
- uses: taiki-e/install-action@v2 - uses: taiki-e/install-action@v2
with: with:
tool: typos-cli tool: typos-cli
@@ -88,7 +88,7 @@ jobs:
CMD: ${{ matrix.platform.cmd }} CMD: ${{ matrix.platform.cmd }}
steps: steps:
- uses: taiki-e/checkout-action@v1 - uses: actions/checkout@v3
- name: Restore cache of cargo folder - name: Restore cache of cargo folder
# We use `restore` and later `save`, so that we can create the key after # We use `restore` and later `save`, so that we can create the key after
@@ -107,17 +107,7 @@ jobs:
- name: Generate lockfile - name: Generate lockfile
# Also updates the crates.io index # Also updates the crates.io index
run: | run: cargo generate-lockfile && cargo update -p ahash --precise 0.8.7 && cargo update -p bumpalo --precise 3.14.0
cargo generate-lockfile
cargo update -p ahash --precise 0.8.7
cargo update -p bumpalo --precise 3.14.0
cargo update -p softbuffer --precise 0.4.0
cargo update -p objc2-encode --precise 4.0.3
cargo update -p orbclient --precise 0.3.47
cargo update -p image --precise 0.25.0
cargo update -p gethostname@1.1.0 --precise 1.0.2
cargo update -p unicode-ident --precise 1.0.10
cargo update -p syn --precise 2.0.114
- name: Install GCC Multilib - name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686') if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
@@ -200,7 +190,7 @@ jobs:
if: matrix.toolchain == 'nightly' if: matrix.toolchain == 'nightly'
run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
env: env:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs --cfg=unreleased_changelogs' RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs'
# See restore step above # See restore step above
- name: Save cache of cargo folder - name: Save cache of cargo folder
@@ -230,24 +220,9 @@ jobs:
- { name: 'Windows', target: x86_64-pc-windows-gnu } - { name: 'Windows', target: x86_64-pc-windows-gnu }
steps: steps:
- uses: taiki-e/checkout-action@v1 - uses: actions/checkout@v3
- uses: EmbarkStudios/cargo-deny-action@v2 - uses: EmbarkStudios/cargo-deny-action@v1
with: with:
command: check command: check
log-level: error log-level: error
arguments: --all-features --target ${{ matrix.platform.target }} arguments: --all-features --target ${{ matrix.platform.target }}
swc:
name: Minimize JavaScript
runs-on: ubuntu-latest
steps:
- uses: taiki-e/checkout-action@v1
- name: Install SWC
run: sudo npm i -g @swc/cli
- name: Run SWC
run: |
swc src/platform_impl/web/web_sys/worker.js -o src/platform_impl/web/web_sys/worker.min.js
- name: Check for diff
run: |
[[ -z $(git status -s) ]]

View File

@@ -17,9 +17,9 @@ jobs:
contents: read contents: read
pages: write pages: write
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:
@@ -27,7 +27,7 @@ jobs:
- name: Run Rustdoc - name: Run Rustdoc
env: env:
RUSTDOCFLAGS: --crate-version master --cfg=docsrs --cfg=unreleased_changelogs RUSTDOCFLAGS: --crate-version master --cfg=docsrs
run: | run: |
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity

3
.gitignore vendored
View File

@@ -3,5 +3,8 @@ target/
rls/ rls/
.vscode/ .vscode/
*~ *~
*.wasm
*.ts
*.js
#*# #*#
.DS_Store .DS_Store

12
.swcrc
View File

@@ -1,12 +0,0 @@
{
"minify": true,
"jsc": {
"target": "es2022",
"minify": {
"compress": {
"unused": true
},
"mangle": true
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,150 +1,69 @@
# Contribution Guidelines # Winit Contributing Guidelines
This document contains guidelines for contributing code to winit. It has to be ## Scope
followed in order for your patch to be approved and applied. [See `FEATURES.md`](./FEATURES.md). When requesting or implementing a new Winit feature, you should
consider whether or not it's directly related to window creation or input handling. If it isn't, it
may be worth creating a separate crate that extends Winit's API to add that functionality.
## Contributing
Anyone can contribute to winit, however given that it's a cross platform ## Reporting an issue
windowing toolkit getting certain changes incorporated could be challenging.
To save your time it's wise to check already opened [pull requests][prs] and When reporting an issue, in order to help the maintainers understand what the problem is, please make
[issues][issues]. In general, bug fixes and missing implementations are always your description of the issue as detailed as possible:
accepted, however larger new API proposals should go into the issue first. When
in doubt contact us on [Matrix][matrix] or via opening an issue.
### Submitting your work and handling review - if it is a bug, please provide a clear explanation of what happens, what should happen, and how to
reproduce the issue, ideally by providing a minimal program exhibiting the problem
- if it is a feature request, please provide a clear argumentation about why you believe this feature
should be supported by winit
All patches have to be sent on Github as [pull requests][prs]. To simplify your ## Making a pull request
life during review it's recommended to check the "give contributors write access
to the branch" checkbox.
We use unstable Rustfmt options across the project, so please run When making a code contribution to winit, before opening your pull request, please make sure that:
`cargo +nightly fmt` before submitting your work. If you are unable to do so,
the maintainers can do it for you before merging, just state so in your pull
request description.
#### Handling review - 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
- you left comments in your code explaining any part that is not straightforward, so that the
maintainers and future contributors don't have to try to guess what your code is supposed to do
- your PR adds an entry to the changelog file if the introduced change is relevant to winit users.
During the review process certain events could require an action from your side, You needn't worry about the added entry causing conflicts, the maintainer that merges the PR will
common patterns and reactions are described below. handle those for you when merging (see below).
- if your PR affects the platform compatibility of one or more features or adds another feature, the
relevant sections in [`FEATURES.md`](https://github.com/rust-windowing/winit/blob/master/FEATURES.md#features)
should be updated.
_Event:_ The CI fails to build, but it looks like it is not your fault. Not Once your PR is open, you can ask for a review by a maintainer of your platform. Winit's merging policy
communicating so could result in maintainers not looking into your patch, is that a PR must be approved by at least two maintainers of winit before being merged, including
since they may assume that you're still working on it.\ at least a maintainer of the platform (a maintainer making a PR themselves counts as approving it).
_Desired behaviour:_ Write a message saying roughly the following "The CI
failure is unrelated", so that the maintainers will fix it for you.
_Event:_ Maintainer requested changes to your PR.\ Once your PR is deemed ready, the merging maintainer will take care of resolving conflicts in
_Desired behavior:_ Once you address the request, you should re-request a review `CHANGELOG.md` (but you must resolve other conflicts yourself). Doing this requires that you check the
with GitHub's UI. If you don't agree with what maintainer suggested, you "give contributors write access to the branch" checkbox when creating the PR.
should state your objections and re-request the review. That will indicate that
the ball is on maintainer's side.
_Event:_ You've opened a PR, but maintainer shortly after commented that they
want to work on that themselves.\
_Desired behavior:_ Discuss with the maintainer regarding their plans if they
were not outlined in the initial response, because such response means that they
are not interested in reviewing your code. Such thing could happen when
underestimating complexity of the task you're solving or when your patch
mandate certain downstream designs. In general, the maintainer will likely
close your PR in order to prevent work being done on it.
[prs]: https://github.com/rust-windowing/winit/pulls
[issues]: https://github.com/rust-windowing/winit/issues
[matrix]: https://matrix.to/#/#rust-windowing:matrix.org
## Maintainers ## Maintainers
Winit has plenty of maintainers with different backgrounds, different time The current maintainers for each platform are listed in the [CODEOWNERS](.github/CODEOWNERS) file.
available to work on Winit, and reasons to be winit maintainer in the first
place. To ensure that Winit's code quality does not decrease over time and to
make it easier to teach new maintainers the "winit way of doing things" the
common policies and routines are defined in this section.
The current maintainers for each platform are listed in [this file][CODEOWNERS]. ## Release process
### Contributions handling
The maintainers must ensure that the external contributions meet Winit's
quality standards. If it's not, it **is the maintainer's responsibility** to
bring it on par, which includes:
- Ensure that formatting is consistent and `CHANGELOG` messages are clear
for the end users.
- Improve the commit message, so it'll be easier for other maintainers to
understand the motivation without going through all the discussions on the
particular patch/PR.
- Ensure that the proposed patch doesn't break platform parity. If the
breakage is desired by contributor, an issue should be opened to discuss
with other maintainers before merging.
- Always fix CI issues before merging if they don't originate from the
submitted work.
However, maintainers should give some leeway for external contributors, so they
don't feel discouraged contributing, for example:
- Suggest a patch to resolve style issues, if it's the only issue with the
submitted work. Keep in mind that pushing the resolution yourself is not
desired, because the contributor might not agree with what you did.
- Be more explicit on how things should be done if you don't like the
approach.
- Suggest to finish the PR for them if they're absent for a while and you need
the proposed changes to move forward with something. In such cases the
maintainer must preserve attribution with `Co-authored-by`, `Suggested-by`,
or keep the original committer.
- Rebase their work for them when massive changes to the Winit codebase were
introduced.
When reviewing code of other maintainers all of the above is on the maintainer
who submitted the patch. Interested maintainers could help push the work over
the finish line, but teaching other maintainers should be preferred.
For a _regular_ contributor to winit, the maintainer should slowly start
requiring contributor to match *maintainer* quality standards when writing
patches and commit messages.
### Contributing
When submitting a patch, the maintainer should follow the general contributing
guidelines, however greater attention to detail is expected in this case.
To make life simpler for other maintainers it's suggested to create your branch
under the project repository instead of your own fork. The naming scheme is
`github_user_name/branch_name`. Doing so will make your work easier to rebase
for other maintainers when you're absent.
### Administrative Actions
Some things (such as changing required CI steps, adding contributors, ...)
require administrative permissions. If you don't have those, ask about the
change in an issue. If you have the permissions, discuss it with at least one
other admin before making the change.
### Release process
Given that winit is a widely used library, we should be able to make a patch Given that winit is a widely used library, we should be able to make a patch
releases at any time we want without blocking the development of new features. releases at any time we want without blocking the development of new features.
To achieve these goals, a new branch is created for every new release. Releases To achieve these goals, a new branch is created for every new release. Releases and later patch releases are committed and tagged in this branch.
and later patch releases are committed and tagged in this branch.
The exact steps for an exemplary `0.2.0` release might look like this: The exact steps for an exemplary `0.2.0` release might look like this:
1. Initially, the version on the latest master is `0.1.0` 1. Initially, the version on the latest master is `0.1.0`
2. A new `v0.2.x` branch is created for the release 2. A new `v0.2.x` branch is created for the release
3. Update released `cfg_attr` in `src/changelog/mod.rs` to `v0.2.md` 3. In the branch, the version is bumped to `v0.2.0`
4. Move entries from `src/changelog/unreleased.md` into 4. The new commit in the branch is tagged `v0.2.0`
`src/changelog/v0.2.md` 5. The version is pushed to crates.io
5. In the branch, the version is bumped to `v0.2.0` 6. A GitHub release is created for the `v0.2.0` tag
6. The new commit in the branch is tagged `v0.2.0` 7. On master, the version is bumped to `0.2.0`, and the CHANGELOG is updated
7. The version is pushed to crates.io
8. A GitHub release is created for the `v0.2.0` tag
9. On master, the version is bumped to `0.2.0`, and the changelog is updated
When doing a patch release, the process is similar: When doing a patch release, the process is similar:
1. Initially, the version of the latest release is `0.2.0` 1. Initially, the version of the latest release is `0.2.0`
2. Checkout the `v0.2.x` branch 2. Checkout the `v0.2.x` branch
3. Cherry-pick the required non-breaking changes into the `v0.2.x` 3. Cherry-pick the required non-breaking changes into the `v0.2.x`
4. Follow steps 4-9 of the regular release example 4. Follow steps 3-7 of the regular release example
[CODEOWNERS]: .github/CODEOWNERS

View File

@@ -1,10 +1,7 @@
[package] [package]
name = "winit" name = "winit"
version = "0.30.13" version = "0.29.15"
authors = [ authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
"The winit contributors",
"Pierre Krieger <pierre.krieger1708@gmail.com>",
]
description = "Cross-platform window creation library." description = "Cross-platform window creation library."
keywords = ["windowing"] keywords = ["windowing"]
readme = "README.md" readme = "README.md"
@@ -14,17 +11,6 @@ rust-version.workspace = true
repository.workspace = true repository.workspace = true
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true
include = [
"/build.rs",
"/docs",
"/examples",
"/FEATURES.md",
"/LICENSE",
"/src",
"!/src/platform_impl/web/script",
"/src/platform_impl/web/script/**/*.min.js",
"/tests",
]
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = [ features = [
@@ -59,15 +45,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"] x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
wayland = [ wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"]
"wayland-client",
"wayland-backend",
"wayland-protocols",
"wayland-protocols-plasma",
"sctk",
"ahash",
"memmap2",
]
wayland-dlopen = ["wayland-backend/dlopen"] wayland-dlopen = ["wayland-backend/dlopen"]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"] wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"] wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
@@ -81,148 +59,94 @@ rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"] rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
[build-dependencies] [build-dependencies]
cfg_aliases = "0.2.1" cfg_aliases = "0.2.0"
[dependencies] [dependencies]
bitflags = "2" bitflags = "2"
cursor-icon = "1.1.0" cursor-icon = "1.1.0"
dpi = { version = "0.1.1", path = "dpi" }
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true } rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = [ rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true }
"std", rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = [
"std",
], optional = true }
serde = { workspace = true, optional = true } serde = { workspace = true, optional = true }
smol_str = "0.2.0" smol_str = "0.2.0"
tracing = { version = "0.1.40", default-features = false } dpi = { path = "dpi" }
tracing = { version = "0.1.40", default_features = false }
[dev-dependencies] [dev-dependencies]
image = { version = "0.25.0", default-features = false, features = ["png"] } image = { version = "0.24.0", default-features = false, features = ["png"] }
tracing = { version = "0.1.40", default-features = false, features = ["log"] } tracing = { version = "0.1.40", default_features = false, features = ["log"] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
winit = { path = ".", features = ["rwh_05"] } winit = { path = ".", features = ["rwh_05"] }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
softbuffer = { version = "0.4.0", default-features = false, features = [ softbuffer = { version = "0.3.0", default-features = false, features = ["x11", "x11-dlopen", "wayland", "wayland-dlopen"] }
"x11",
"x11-dlopen",
"wayland",
"wayland-dlopen",
] }
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.6.0" android-activity = "0.5.0"
ndk = { version = "0.9.0", default-features = false } ndk = { version = "0.8.0", default-features = false }
ndk-sys = "0.5.0"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
block2 = "0.5.1"
core-foundation = "0.9.3" core-foundation = "0.9.3"
objc2 = { version = "0.5.2", features = ["relax-sign-encoding"] } objc2 = "0.5.0"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.23.1" core-graphics = "0.23.1"
[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation] [target.'cfg(target_os = "macos")'.dependencies.icrate]
version = "0.2.2" version = "0.1.0"
features = [ features = [
"block2",
"dispatch", "dispatch",
"NSArray", "Foundation",
"NSAttributedString", "Foundation_NSArray",
"NSData", "Foundation_NSAttributedString",
"NSDictionary", "Foundation_NSMutableAttributedString",
"NSDistributedNotificationCenter", "Foundation_NSData",
"NSEnumerator", "Foundation_NSDictionary",
"NSKeyValueObserving", "Foundation_NSString",
"NSNotification", "Foundation_NSProcessInfo",
"NSObjCRuntime", "Foundation_NSThread",
"NSPathUtilities", "Foundation_NSNumber",
"NSProcessInfo", "AppKit",
"NSRunLoop", "AppKit_NSAppearance",
"NSString", "AppKit_NSApplication",
"NSThread", "AppKit_NSBitmapImageRep",
"NSValue", "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 = "macos")'.dependencies.objc2-app-kit] [target.'cfg(target_os = "ios")'.dependencies.icrate]
version = "0.2.2" version = "0.1.0"
features = [ features = [
"NSAppearance",
"NSApplication",
"NSBitmapImageRep",
"NSButton",
"NSColor",
"NSControl",
"NSCursor",
"NSDragging",
"NSEvent",
"NSGraphics",
"NSGraphicsContext",
"NSImage",
"NSImageRep",
"NSMenu",
"NSMenuItem",
"NSOpenGLView",
"NSPasteboard",
"NSResponder",
"NSRunningApplication",
"NSScreen",
"NSTextInputClient",
"NSTextInputContext",
"NSView",
"NSWindow",
"NSWindowScripting",
"NSWindowTabGroup",
]
[target.'cfg(target_os = "ios")'.dependencies.objc2-foundation]
version = "0.2.2"
features = [
"block2",
"dispatch", "dispatch",
"NSArray", "Foundation",
"NSEnumerator", "Foundation_NSArray",
"NSGeometry", "Foundation_NSString",
"NSObjCRuntime", "Foundation_NSProcessInfo",
"NSOperation", "Foundation_NSThread",
"NSString", "Foundation_NSSet",
"NSProcessInfo",
"NSThread",
"NSSet",
]
[target.'cfg(target_os = "ios")'.dependencies.objc2-ui-kit]
version = "0.2.2"
features = [
"UIApplication",
"UIDevice",
"UIEvent",
"UIGeometry",
"UIGestureRecognizer",
"UITextInput",
"UITextInputTraits",
"UIOrientation",
"UIPanGestureRecognizer",
"UIPinchGestureRecognizer",
"UIResponder",
"UIRotationGestureRecognizer",
"UIScreen",
"UIScreenMode",
"UITapGestureRecognizer",
"UITouch",
"UITraitCollection",
"UIView",
"UIViewController",
"UIWindow",
] ]
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
unicode-segmentation = "1.7.1" unicode-segmentation = "1.7.1"
[target.'cfg(target_os = "windows")'.dependencies.windows-sys] [target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.52.0" version = "0.48"
features = [ features = [
"Win32_Devices_HumanInterfaceDevice", "Win32_Devices_HumanInterfaceDevice",
"Win32_Foundation", "Win32_Foundation",
@@ -234,7 +158,6 @@ features = [
"Win32_System_Com", "Win32_System_Com",
"Win32_System_LibraryLoader", "Win32_System_LibraryLoader",
"Win32_System_Ole", "Win32_System_Ole",
"Win32_Security",
"Win32_System_SystemInformation", "Win32_System_SystemInformation",
"Win32_System_SystemServices", "Win32_System_SystemServices",
"Win32_System_Threading", "Win32_System_Threading",
@@ -254,110 +177,84 @@ features = [
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies] [target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
ahash = { version = "0.8.7", features = ["no-rng"], optional = true } ahash = { version = "0.8.7", features = ["no-rng"], optional = true }
bytemuck = { version = "1.13.1", default-features = false, optional = true } bytemuck = { version = "1.13.1", default-features = false, optional = true }
calloop = "0.13.0" calloop = "0.12.3"
libc = "0.2.64" libc = "0.2.64"
memmap2 = { version = "0.9.0", optional = true } memmap2 = { version = "0.9.0", optional = true }
percent-encoding = { version = "2.0", optional = true } percent-encoding = { version = "2.0", optional = true }
rustix = { version = "0.38.4", default-features = false, features = [ rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
"std", sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
"system", sctk-adwaita = { version = "0.8.0", default_features = false, optional = true }
"thread", wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
"process", wayland-client = { version = "0.31.1", optional = true }
] } wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
sctk = { package = "smithay-client-toolkit", version = "0.19.2", default-features = false, features = [ wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
"calloop",
], optional = true }
sctk-adwaita = { version = "0.10.1", default-features = false, optional = true }
wayland-backend = { version = "0.3.10", default-features = false, features = [
"client_system",
], optional = true }
wayland-client = { version = "0.31.10", optional = true }
wayland-protocols = { version = "0.32.8", features = [
"staging",
], optional = true }
wayland-protocols-plasma = { version = "0.3.8", features = [
"client",
], optional = true }
x11-dl = { version = "2.19.1", optional = true } x11-dl = { version = "2.19.1", optional = true }
x11rb = { version = "0.13.0", default-features = false, features = [ x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
"allow-unsafe-code",
"dl-libxcb",
"randr",
"resource_manager",
"xinput",
"xkb",
], optional = true }
xkbcommon-dl = "0.4.2" xkbcommon-dl = "0.4.2"
[target.'cfg(target_os = "redox")'.dependencies] [target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.47", default-features = false } orbclient = { version = "0.3.47", default-features = false }
redox_syscall = "0.4.1" 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] [target.'cfg(target_family = "wasm")'.dependencies]
js-sys = "0.3.70" js-sys = "0.3.64"
pin-project = "1" pin-project = "1"
wasm-bindgen = "0.2.93" wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4.43" wasm-bindgen-futures = "0.4"
web-time = "1" web-time = "1"
web_sys = { package = "web-sys", version = "0.3.70", features = [
"AbortController",
"AbortSignal",
"Blob",
"BlobPropertyBag",
"console",
"CssStyleDeclaration",
"Document",
"DomException",
"DomRect",
"DomRectReadOnly",
"Element",
"Event",
"EventTarget",
"FocusEvent",
"HtmlCanvasElement",
"HtmlElement",
"HtmlImageElement",
"ImageBitmap",
"ImageBitmapOptions",
"ImageBitmapRenderingContext",
"ImageData",
"IntersectionObserver",
"IntersectionObserverEntry",
"KeyboardEvent",
"MediaQueryList",
"MessageChannel",
"MessagePort",
"Navigator",
"Node",
"OrientationLockType",
"OrientationType",
"PageTransitionEvent",
"Permissions",
"PermissionState",
"PermissionStatus",
"PointerEvent",
"PremultiplyAlpha",
"ResizeObserver",
"ResizeObserverBoxOptions",
"ResizeObserverEntry",
"ResizeObserverOptions",
"ResizeObserverSize",
"Screen",
"ScreenOrientation",
"Url",
"VisibilityState",
"WheelEvent",
"Window",
"Worker",
] }
[target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies] [target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies]
atomic-waker = "1" atomic-waker = "1"
concurrent-queue = { version = "2", default-features = false } concurrent-queue = { version = "2", default-features = false }
[target.'cfg(target_family = "wasm")'.dev-dependencies] [target.'cfg(target_family = "wasm")'.dev-dependencies]
console_error_panic_hook = "0.1" console_log = "1"
tracing-web = "0.1" web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
[[example]] [[example]]
doc-scrape-examples = true doc-scrape-examples = true
@@ -365,7 +262,10 @@ name = "window"
[workspace] [workspace]
resolver = "2" resolver = "2"
members = ["dpi"] members = [
"dpi",
"run-wasm",
]
[workspace.package] [workspace.package]
rust-version = "1.70.0" rust-version = "1.70.0"

View File

@@ -179,7 +179,7 @@ Legend:
|Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ | |Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ |
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** | |Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ | |Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|Window resize increments |✔️ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** | |Window resize increments | |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ | |Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ | |Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ |
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** | |Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |

View File

@@ -8,7 +8,7 @@
```toml ```toml
[dependencies] [dependencies]
winit = "0.30.13" winit = "0.29.15"
``` ```
## [Documentation](https://docs.rs/winit) ## [Documentation](https://docs.rs/winit)
@@ -19,7 +19,7 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
## Contact Us ## Contact Us
Join us in our [![Matrix](https://img.shields.io/badge/Matrix-%23rust--windowing%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#rust-windowing:matrix.org) room. Join us in our [![Matrix](https://img.shields.io/badge/Matrix-%23rust--windowing%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#rust-windowing:matrix.org) room. If you don't get an answer there, try [![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit).
The maintainers have a meeting every friday at UTC 15. The meeting notes can be found [here](https://hackmd.io/@winit-meetings). The maintainers have a meeting every friday at UTC 15. The meeting notes can be found [here](https://hackmd.io/@winit-meetings).
@@ -33,10 +33,6 @@ Winit is designed to be a low-level brick in a hierarchy of libraries. Consequen
show something on the window you need to use the platform-specific getters provided by winit, or show something on the window you need to use the platform-specific getters provided by winit, or
another library. another library.
## CONTRIBUTING
For contributing guidelines see [CONTRIBUTING.md](./CONTRIBUTING.md).
## MSRV Policy ## MSRV Policy
This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to

View File

@@ -1,10 +1,10 @@
use cfg_aliases::cfg_aliases; use cfg_aliases::cfg_aliases;
fn main() { fn main() {
// The script doesn't depend on our code. // The script doesn't depend on our code
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=build.rs");
// Setup cfg aliases. // Setup cfg aliases
cfg_aliases! { cfg_aliases! {
// Systems. // Systems.
android_platform: { target_os = "android" }, android_platform: { target_os = "android" },
@@ -21,7 +21,4 @@ fn main() {
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) }, wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
orbital_platform: { redox }, orbital_platform: { redox },
} }
// Winit defined cfgs.
println!("cargo:rustc-check-cfg=cfg(unreleased_changelogs)");
} }

View File

@@ -10,6 +10,6 @@ disallowed-methods = [
{ path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" }, { path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Document::exit_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 = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
{ path = "objc2_app_kit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." }, { path = "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 = "objc2_app_kit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" }, { path = "icrate::AppKit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
] ]

View File

@@ -1,20 +1,15 @@
# https://embarkstudios.github.io/cargo-deny # https://embarkstudios.github.io/cargo-deny/
# cargo install cargo-deny # cargo install cargo-deny
# cargo update && cargo deny --target aarch64-apple-ios check # cargo update && cargo deny --all-features --log-level error --target aarch64-apple-ios check
# Note: running just `cargo deny check` without a `--target` will result in # Note: running just `cargo deny check` without a `--target` will result in
# false positives due to https://github.com/EmbarkStudios/cargo-deny/issues/324 # false positives due to https://github.com/EmbarkStudios/cargo-deny/issues/324
[graph]
all-features = true
exclude-dev = true
targets = [ targets = [
{ triple = "aarch64-apple-ios" }, { triple = "aarch64-apple-ios" },
{ triple = "aarch64-linux-android" }, { triple = "aarch64-linux-android" },
{ triple = "i686-pc-windows-gnu" }, { triple = "i686-pc-windows-gnu" },
{ triple = "i686-pc-windows-msvc" }, { triple = "i686-pc-windows-msvc" },
{ triple = "i686-unknown-linux-gnu" }, { triple = "i686-unknown-linux-gnu" },
{ triple = "wasm32-unknown-unknown", features = [ { triple = "wasm32-unknown-unknown" },
"atomics",
] },
{ triple = "x86_64-apple-darwin" }, { triple = "x86_64-apple-darwin" },
{ triple = "x86_64-apple-ios" }, { triple = "x86_64-apple-ios" },
{ triple = "x86_64-pc-windows-gnu" }, { triple = "x86_64-pc-windows-gnu" },
@@ -23,58 +18,46 @@ targets = [
{ triple = "x86_64-unknown-redox" }, { triple = "x86_64-unknown-redox" },
] ]
[licenses]
allow = [ [advisories]
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0) vulnerability = "deny"
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd) unmaintained = "warn"
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised) yanked = "deny"
"ISC", # https://tldrlegal.com/license/isc-license ignore = []
"MIT", # https://tldrlegal.com/license/mit-license
"Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html
]
confidence-threshold = 1.0
private = { ignore = true }
[bans] [bans]
multiple-versions = "deny" multiple-versions = "deny"
skip = [
{ crate = "raw-window-handle", reason = "we depend on multiple behind features" },
{ crate = "bitflags@1", reason = "the ecosystem is in the process of migrating" },
{ crate = "rustix@0.38", reason = "the ecosystem is in the process of migrating" },
{ crate = "linux-raw-sys@0.4", reason = "the ecosystem is in the process of migrating" },
]
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
deny = []
[bans.build] skip = [
include-archives = true { name = "raw-window-handle" }, # we intentionally have multiple versions of this
interpreted = "deny" { name = "bitflags" }, # the ecosystem is in the process of migrating.
{ name = "libloading" }, # x11rb uses a different version until the next update
[[bans.build.bypass]]
allow = [
{ path = "generate-bindings.sh", checksum = "268ec23248218d779e33853cdc60e2985e70214ff004716cd734270de1f6b561" },
] ]
crate = "android-activity" skip-tree = []
[[bans.build.bypass]]
allow-globs = ["ci/*", "githooks/*", "cargo.sh"]
crate = "zerocopy"
[[bans.build.bypass]] [licenses]
allow-globs = ["freetype2/*"] private = { ignore = true }
crate = "freetype-sys" unlicensed = "deny"
allow-osi-fsf-free = "neither"
[[bans.build.bypass]] confidence-threshold = 0.92 # We want really high confidence when inferring licenses from text
allow-globs = ["lib/*.a"] copyleft = "deny"
crate = "windows_i686_gnu" allow = [
"Apache-2.0 WITH LLVM-exception", # https://spdx.org/licenses/LLVM-exception.html
[[bans.build.bypass]] "Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
allow-globs = ["lib/*.lib"] "BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
crate = "windows_i686_msvc" "BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
"BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained
[[bans.build.bypass]] "CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/
allow-globs = ["lib/*.a"] "ISC", # https://tldrlegal.com/license/-isc-license
crate = "windows_x86_64_gnu" "LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 - no official SPDX, see https://github.com/emilk/egui/issues/2321
"MIT-0", # https://choosealicense.com/licenses/mit-0/
[[bans.build.bypass]] "MIT", # https://tldrlegal.com/license/mit-license
allow-globs = ["lib/*.lib"] "MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11. Used by webpki-roots on Linux.
crate = "windows_x86_64_msvc" "OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html
"OpenSSL", # https://www.openssl.org/source/license.html - used on Linux
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
"Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib)
]

View File

@@ -9,12 +9,8 @@ PR's changelog entry would go into the wrong section).
And please only add new entries to the top of this list, right below the `# And please only add new entries to the top of this list, right below the `#
Unreleased` header. Unreleased` header.
## Unreleased # Unreleased
## 0.1.1 # 0.1.0
- Derive `Debug`, `Copy`, `Clone`, `PartialEq`, `Serialize`, `Deserialize` traits for `PixelUnit`.
## 0.1.0
- Add `LogicalUnit`, `PhysicalUnit` and `PixelUnit` types and related functions. - Add `LogicalUnit`, `PhysicalUnit` and `PixelUnit` types and related functions.

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "dpi" name = "dpi"
version = "0.1.1" version = "0.1.0"
description = "Types for handling UI scaling" description = "Types for handling UI scaling"
keywords = ["DPI", "HiDPI", "scale-factor"] keywords = ["DPI", "HiDPI", "scale-factor"]
categories = ["gui"] categories = ["gui"]

View File

@@ -35,9 +35,9 @@
//! //!
//! ### Position and Size types //! ### Position and Size types
//! //!
//! The [`PhysicalPosition`] / [`PhysicalSize`] / [`PhysicalUnit`] types correspond with the actual //! The [`PhysicalPosition`] / [`PhysicalSize`] / [`PhysicalUnit`] types correspond with the actual pixels on the
//! pixels on the device, and the [`LogicalPosition`] / [`LogicalSize`] / [`LogicalUnit`] types //! device, and the [`LogicalPosition`] / [`LogicalSize`] / [`LogicalUnit`] types correspond to the physical pixels
//! correspond to the physical pixels divided by the scale factor. //! divided by the scale factor.
//! //!
//! The position and size types are generic over their exact pixel type, `P`, to allow the //! The position and size types are generic over their exact pixel type, `P`, to allow the
//! API to have integer precision where appropriate (e.g. most window manipulation functions) and //! API to have integer precision where appropriate (e.g. most window manipulation functions) and
@@ -52,14 +52,19 @@
//! //!
//! This crate provides the following Cargo features: //! This crate provides the following Cargo features:
//! //!
//! * `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde). //! * `serde`: Enables serialization/deserialization of certain types with
//! [Serde](https://crates.io/crates/serde).
//! * `mint`: Enables mint (math interoperability standard types) conversions. //! * `mint`: Enables mint (math interoperability standard types) conversions.
//! //!
//! //!
//! [points]: https://en.wikipedia.org/wiki/Point_(typography) //! [points]: https://en.wikipedia.org/wiki/Point_(typography)
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography) //! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))] #![cfg_attr(
docsrs,
feature(doc_auto_cfg, doc_cfg_hide),
doc(cfg_hide(doc, docsrs))
)]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@@ -115,9 +120,9 @@ impl Pixel for f64 {
/// Checks that the scale factor is a normal positive `f64`. /// Checks that the scale factor is a normal positive `f64`.
/// ///
/// All functions that take a scale factor assert that this will return `true`. If you're sourcing /// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from
/// scale factors from anywhere other than winit, it's recommended to validate them using this /// anywhere other than winit, it's recommended to validate them using this function before passing them to winit;
/// function before passing them to winit; otherwise, you risk panics. /// otherwise, you risk panics.
#[inline] #[inline]
pub fn validate_scale_factor(scale_factor: f64) -> bool { pub fn validate_scale_factor(scale_factor: f64) -> bool {
scale_factor.is_sign_positive() && scale_factor.is_normal() scale_factor.is_sign_positive() && scale_factor.is_normal()
@@ -129,12 +134,12 @@ pub fn validate_scale_factor(scale_factor: f64) -> bool {
pub struct LogicalUnit<P>(pub P); pub struct LogicalUnit<P>(pub P);
impl<P> LogicalUnit<P> { impl<P> LogicalUnit<P> {
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
/// Represents a minimum logical unit of [`f64::MAX`]. /// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN); pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a logical unit of `0_f64`. /// Represents a logical unit of `0_f64`.
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0); pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
#[inline] #[inline]
pub const fn new(v: P) -> Self { pub const fn new(v: P) -> Self {
@@ -223,12 +228,12 @@ impl<P: Pixel> From<LogicalUnit<P>> for f64 {
pub struct PhysicalUnit<P>(pub P); pub struct PhysicalUnit<P>(pub P);
impl<P> PhysicalUnit<P> { impl<P> PhysicalUnit<P> {
/// Represents a maximum physical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
/// Represents a minimum physical unit of [`f64::MAX`]. /// Represents a minimum physical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN); pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a physical unit of `0_f64`. /// Represents a physical unit of `0_f64`.
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0); pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
/// Represents a maximum physical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
#[inline] #[inline]
pub const fn new(v: P) -> Self { pub const fn new(v: P) -> Self {
@@ -309,20 +314,18 @@ impl<P: Pixel> From<PhysicalUnit<P>> for f64 {
} }
/// A pixel unit that's either physical or logical. /// A pixel unit that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PixelUnit { pub enum PixelUnit {
Physical(PhysicalUnit<i32>), Physical(PhysicalUnit<i32>),
Logical(LogicalUnit<f64>), Logical(LogicalUnit<f64>),
} }
impl PixelUnit { impl PixelUnit {
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MAX));
/// Represents a minimum logical unit of [`f64::MAX`]. /// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MIN)); pub const MIN: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MIN));
/// Represents a logical unit of `0_f64`. /// Represents a logical unit of `0_f64`.
pub const ZERO: PixelUnit = PixelUnit::Logical(LogicalUnit::new(0.0)); pub const ZERO: PixelUnit = PixelUnit::Logical(LogicalUnit::new(0.0));
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MAX));
pub fn new<S: Into<PixelUnit>>(unit: S) -> PixelUnit { pub fn new<S: Into<PixelUnit>>(unit: S) -> PixelUnit {
unit.into() unit.into()
@@ -395,7 +398,10 @@ impl<P: Pixel> LogicalPosition<P> {
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> { pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> {
LogicalPosition { x: self.x.cast(), y: self.y.cast() } LogicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
} }
} }
@@ -471,7 +477,10 @@ impl<P: Pixel> PhysicalPosition<P> {
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> { pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> {
PhysicalPosition { x: self.x.cast(), y: self.y.cast() } PhysicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
} }
} }
@@ -547,7 +556,10 @@ impl<P: Pixel> LogicalSize<P> {
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> LogicalSize<X> { pub fn cast<X: Pixel>(&self) -> LogicalSize<X> {
LogicalSize { width: self.width.cast(), height: self.height.cast() } LogicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
} }
} }
@@ -585,7 +597,10 @@ impl<P: Pixel> From<mint::Vector2<P>> for LogicalSize<P> {
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> { impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> {
fn from(s: LogicalSize<P>) -> Self { fn from(s: LogicalSize<P>) -> Self {
mint::Vector2 { x: s.width, y: s.height } mint::Vector2 {
x: s.width,
y: s.height,
}
} }
} }
@@ -620,7 +635,10 @@ impl<P: Pixel> PhysicalSize<P> {
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> { pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> {
PhysicalSize { width: self.width.cast(), height: self.height.cast() } PhysicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
} }
} }
@@ -658,7 +676,10 @@ impl<P: Pixel> From<mint::Vector2<P>> for PhysicalSize<P> {
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> { impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> {
fn from(s: PhysicalSize<P>) -> Self { fn from(s: PhysicalSize<P>) -> Self {
mint::Vector2 { x: s.width, y: s.height } mint::Vector2 {
x: s.width,
y: s.height,
}
} }
} }
@@ -823,7 +844,12 @@ mod tests {
macro_rules! assert_approx_eq { macro_rules! assert_approx_eq {
($a:expr, $b:expr $(,)?) => { ($a:expr, $b:expr $(,)?) => {
assert!(($a - $b).abs() < 0.001, "{} is not approximately equal to {}", $a, $b); assert!(
($a - $b).abs() < 0.001,
"{} is not approximately equal to {}",
$a,
$b
);
}; };
} }
@@ -942,8 +968,14 @@ mod tests {
assert_eq!(log_unit.to_physical::<u32>(1.0), PhysicalUnit::new(1)); assert_eq!(log_unit.to_physical::<u32>(1.0), PhysicalUnit::new(1));
assert_eq!(log_unit.to_physical::<u32>(2.0), PhysicalUnit::new(2)); assert_eq!(log_unit.to_physical::<u32>(2.0), PhysicalUnit::new(2));
assert_eq!(log_unit.cast::<u32>(), LogicalUnit::new(1)); assert_eq!(log_unit.cast::<u32>(), LogicalUnit::new(1));
assert_eq!(log_unit, LogicalUnit::from_physical(PhysicalUnit::new(1.0), 1.0)); assert_eq!(
assert_eq!(log_unit, LogicalUnit::from_physical(PhysicalUnit::new(2.0), 2.0)); log_unit,
LogicalUnit::from_physical(PhysicalUnit::new(1.0), 1.0)
);
assert_eq!(
log_unit,
LogicalUnit::from_physical(PhysicalUnit::new(2.0), 2.0)
);
assert_eq!(LogicalUnit::from(2.0), LogicalUnit::new(2.0)); assert_eq!(LogicalUnit::from(2.0), LogicalUnit::new(2.0));
let x: f64 = log_unit.into(); let x: f64 = log_unit.into();
@@ -952,8 +984,14 @@ mod tests {
#[test] #[test]
fn test_physical_unit() { fn test_physical_unit() {
assert_eq!(PhysicalUnit::from_logical(LogicalUnit::new(1.0), 1.0), PhysicalUnit::new(1)); assert_eq!(
assert_eq!(PhysicalUnit::from_logical(LogicalUnit::new(2.0), 0.5), PhysicalUnit::new(1)); PhysicalUnit::from_logical(LogicalUnit::new(1.0), 1.0),
PhysicalUnit::new(1)
);
assert_eq!(
PhysicalUnit::from_logical(LogicalUnit::new(2.0), 0.5),
PhysicalUnit::new(1)
);
assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0,)); assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0,));
assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0)); assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0));
@@ -967,10 +1005,22 @@ mod tests {
assert_eq!(log_pos.to_physical::<u32>(1.0), PhysicalPosition::new(1, 2)); assert_eq!(log_pos.to_physical::<u32>(1.0), PhysicalPosition::new(1, 2));
assert_eq!(log_pos.to_physical::<u32>(2.0), PhysicalPosition::new(2, 4)); assert_eq!(log_pos.to_physical::<u32>(2.0), PhysicalPosition::new(2, 4));
assert_eq!(log_pos.cast::<u32>(), LogicalPosition::new(1, 2)); assert_eq!(log_pos.cast::<u32>(), LogicalPosition::new(1, 2));
assert_eq!(log_pos, LogicalPosition::from_physical(PhysicalPosition::new(1.0, 2.0), 1.0)); assert_eq!(
assert_eq!(log_pos, LogicalPosition::from_physical(PhysicalPosition::new(2.0, 4.0), 2.0)); log_pos,
assert_eq!(LogicalPosition::from((2.0, 2.0)), LogicalPosition::new(2.0, 2.0)); LogicalPosition::from_physical(PhysicalPosition::new(1.0, 2.0), 1.0)
assert_eq!(LogicalPosition::from([2.0, 3.0]), LogicalPosition::new(2.0, 3.0)); );
assert_eq!(
log_pos,
LogicalPosition::from_physical(PhysicalPosition::new(2.0, 4.0), 2.0)
);
assert_eq!(
LogicalPosition::from((2.0, 2.0)),
LogicalPosition::new(2.0, 2.0)
);
assert_eq!(
LogicalPosition::from([2.0, 3.0]),
LogicalPosition::new(2.0, 3.0)
);
let x: (f64, f64) = log_pos.into(); let x: (f64, f64) = log_pos.into();
assert_eq!(x, (1.0, 2.0)); assert_eq!(x, (1.0, 2.0));
@@ -988,8 +1038,14 @@ mod tests {
PhysicalPosition::from_logical(LogicalPosition::new(2.0, 4.0), 0.5), PhysicalPosition::from_logical(LogicalPosition::new(2.0, 4.0), 0.5),
PhysicalPosition::new(1, 2) PhysicalPosition::new(1, 2)
); );
assert_eq!(PhysicalPosition::from((2.0, 2.0)), PhysicalPosition::new(2.0, 2.0)); assert_eq!(
assert_eq!(PhysicalPosition::from([2.0, 3.0]), PhysicalPosition::new(2.0, 3.0)); PhysicalPosition::from((2.0, 2.0)),
PhysicalPosition::new(2.0, 2.0)
);
assert_eq!(
PhysicalPosition::from([2.0, 3.0]),
PhysicalPosition::new(2.0, 3.0)
);
let x: (f64, f64) = PhysicalPosition::new(1, 2).into(); let x: (f64, f64) = PhysicalPosition::new(1, 2).into();
assert_eq!(x, (1.0, 2.0)); assert_eq!(x, (1.0, 2.0));
@@ -1003,8 +1059,14 @@ mod tests {
assert_eq!(log_size.to_physical::<u32>(1.0), PhysicalSize::new(1, 2)); assert_eq!(log_size.to_physical::<u32>(1.0), PhysicalSize::new(1, 2));
assert_eq!(log_size.to_physical::<u32>(2.0), PhysicalSize::new(2, 4)); assert_eq!(log_size.to_physical::<u32>(2.0), PhysicalSize::new(2, 4));
assert_eq!(log_size.cast::<u32>(), LogicalSize::new(1, 2)); assert_eq!(log_size.cast::<u32>(), LogicalSize::new(1, 2));
assert_eq!(log_size, LogicalSize::from_physical(PhysicalSize::new(1.0, 2.0), 1.0)); assert_eq!(
assert_eq!(log_size, LogicalSize::from_physical(PhysicalSize::new(2.0, 4.0), 2.0)); log_size,
LogicalSize::from_physical(PhysicalSize::new(1.0, 2.0), 1.0)
);
assert_eq!(
log_size,
LogicalSize::from_physical(PhysicalSize::new(2.0, 4.0), 2.0)
);
assert_eq!(LogicalSize::from((2.0, 2.0)), LogicalSize::new(2.0, 2.0)); assert_eq!(LogicalSize::from((2.0, 2.0)), LogicalSize::new(2.0, 2.0));
assert_eq!(LogicalSize::from([2.0, 3.0]), LogicalSize::new(2.0, 3.0)); assert_eq!(LogicalSize::from([2.0, 3.0]), LogicalSize::new(2.0, 3.0));
@@ -1035,7 +1097,10 @@ mod tests {
#[test] #[test]
fn test_size() { fn test_size() {
assert_eq!(Size::new(PhysicalSize::new(1, 2)), Size::Physical(PhysicalSize::new(1, 2))); assert_eq!(
Size::new(PhysicalSize::new(1, 2)),
Size::Physical(PhysicalSize::new(1, 2))
);
assert_eq!( assert_eq!(
Size::new(LogicalSize::new(1.0, 2.0)), Size::new(LogicalSize::new(1.0, 2.0)),
Size::Logical(LogicalSize::new(1.0, 2.0)) Size::Logical(LogicalSize::new(1.0, 2.0))
@@ -1140,38 +1205,4 @@ mod tests {
let _ = format!("{:?}", Size::Physical((1, 2).into()).clone()); let _ = format!("{:?}", Size::Physical((1, 2).into()).clone());
let _ = format!("{:?}", Position::Physical((1, 2).into()).clone()); let _ = format!("{:?}", Position::Physical((1, 2).into()).clone());
} }
#[test]
fn ensure_copy_trait() {
fn is_copy<T: Copy>() {}
is_copy::<LogicalUnit<i32>>();
is_copy::<PhysicalUnit<f64>>();
is_copy::<PixelUnit>();
is_copy::<LogicalSize<i32>>();
is_copy::<PhysicalSize<f64>>();
is_copy::<Size>();
is_copy::<LogicalPosition<i32>>();
is_copy::<PhysicalPosition<f64>>();
is_copy::<Position>();
}
#[test]
fn ensure_partial_eq_trait() {
fn is_partial_eq<T: PartialEq>() {}
is_partial_eq::<LogicalUnit<i32>>();
is_partial_eq::<PhysicalUnit<f64>>();
is_partial_eq::<PixelUnit>();
is_partial_eq::<LogicalSize<i32>>();
is_partial_eq::<PhysicalSize<f64>>();
is_partial_eq::<Size>();
is_partial_eq::<LogicalPosition<i32>>();
is_partial_eq::<PhysicalPosition<f64>>();
is_partial_eq::<Position>();
}
} }

View File

@@ -1,4 +1,7 @@
#[cfg(all(feature = "rwh_06", any(x11_platform, macos_platform, windows_platform)))] #[cfg(all(
feature = "rwh_06",
any(x11_platform, macos_platform, windows_platform)
))]
#[allow(deprecated)] #[allow(deprecated)]
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
use std::collections::HashMap; use std::collections::HashMap;
@@ -43,22 +46,25 @@ fn main() -> Result<(), impl std::error::Error> {
println!("Parent window id: {parent_window_id:?})"); println!("Parent window id: {parent_window_id:?})");
windows.insert(window.id(), window); windows.insert(window.id(), window);
}, }
Event::WindowEvent { window_id, event } => match event { Event::WindowEvent { window_id, event } => match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
windows.clear(); windows.clear();
event_loop.exit(); event_loop.exit();
}, }
WindowEvent::CursorEntered { device_id: _ } => { WindowEvent::CursorEntered { device_id: _ } => {
// On x11, println when the cursor entered in a window even if the child window // On x11, println when the cursor entered in a window even if the child window is created
// is created by some key inputs. // by some key inputs.
// the child windows are always placed at (0, 0) with size (200, 200) in the // the child windows are always placed at (0, 0) with size (200, 200) in the parent window,
// parent window, so we also can see this log when we move // so we also can see this log when we move the cursor around (200, 200) in parent window.
// the cursor around (200, 200) in parent window.
println!("cursor entered in the window {window_id:?}"); println!("cursor entered in the window {window_id:?}");
}, }
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
event: KeyEvent { state: ElementState::Pressed, .. }, event:
KeyEvent {
state: ElementState::Pressed,
..
},
.. ..
} => { } => {
let parent_window = windows.get(&parent_window_id.unwrap()).unwrap(); let parent_window = windows.get(&parent_window_id.unwrap()).unwrap();
@@ -66,12 +72,12 @@ fn main() -> Result<(), impl std::error::Error> {
let child_id = child_window.id(); let child_id = child_window.id();
println!("Child window created with id: {child_id:?}"); println!("Child window created with id: {child_id:?}");
windows.insert(child_id, child_window); windows.insert(child_id, child_window);
}, }
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
if let Some(window) = windows.get(&window_id) { if let Some(window) = windows.get(&window_id) {
fill::fill_window(window); fill::fill_window(window);
} }
}, }
_ => (), _ => (),
}, },
_ => (), _ => (),
@@ -79,10 +85,10 @@ fn main() -> Result<(), impl std::error::Error> {
}) })
} }
#[cfg(all(feature = "rwh_06", not(any(x11_platform, macos_platform, windows_platform))))] #[cfg(all(
feature = "rwh_06",
not(any(x11_platform, macos_platform, windows_platform))
))]
fn main() { fn main() {
panic!( panic!("This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature enabled.");
"This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature \
enabled."
);
} }

View File

@@ -4,7 +4,6 @@ use std::thread;
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
use std::time; use std::time;
use ::tracing::{info, warn};
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time as time; use web_time as time;
@@ -16,8 +15,6 @@ use winit::window::{Window, WindowId};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
#[path = "util/tracing.rs"]
mod tracing;
const WAIT_TIME: time::Duration = time::Duration::from_millis(100); const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100); const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
@@ -31,16 +28,13 @@ enum Mode {
} }
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
#[cfg(web_platform)] tracing_subscriber::fmt::init();
console_error_panic_hook::set_once();
tracing::init(); println!("Press '1' to switch to Wait mode.");
println!("Press '2' to switch to WaitUntil mode.");
info!("Press '1' to switch to Wait mode."); println!("Press '3' to switch to Poll mode.");
info!("Press '2' to switch to WaitUntil mode."); println!("Press 'R' to toggle request_redraw() calls.");
info!("Press '3' to switch to Poll mode."); println!("Press 'Esc' to close the window.");
info!("Press 'R' to toggle request_redraw() calls.");
info!("Press 'Esc' to close the window.");
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
@@ -59,7 +53,7 @@ struct ControlFlowDemo {
impl ApplicationHandler for ControlFlowDemo { impl ApplicationHandler for ControlFlowDemo {
fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) { fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) {
info!("new_events: {cause:?}"); println!("new_events: {cause:?}");
self.wait_cancelled = match cause { self.wait_cancelled = match cause {
StartCause::WaitCancelled { .. } => self.mode == Mode::WaitUntil, StartCause::WaitCancelled { .. } => self.mode == Mode::WaitUntil,
@@ -80,44 +74,49 @@ impl ApplicationHandler for ControlFlowDemo {
_window_id: WindowId, _window_id: WindowId,
event: WindowEvent, event: WindowEvent,
) { ) {
info!("{event:?}"); println!("{event:?}");
match event { match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
self.close_requested = true; self.close_requested = true;
}, }
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
event: KeyEvent { logical_key: key, state: ElementState::Pressed, .. }, event:
KeyEvent {
logical_key: key,
state: ElementState::Pressed,
..
},
.. ..
} => match key.as_ref() { } => match key.as_ref() {
// WARNING: Consider using `key_without_modifiers()` if available on your platform. // WARNING: Consider using `key_without_modifiers()` if available on your platform.
// See the `key_binding` example // See the `key_binding` example
Key::Character("1") => { Key::Character("1") => {
self.mode = Mode::Wait; self.mode = Mode::Wait;
warn!("mode: {:?}", self.mode); println!("\nmode: {:?}\n", self.mode);
}, }
Key::Character("2") => { Key::Character("2") => {
self.mode = Mode::WaitUntil; self.mode = Mode::WaitUntil;
warn!("mode: {:?}", self.mode); println!("\nmode: {:?}\n", self.mode);
}, }
Key::Character("3") => { Key::Character("3") => {
self.mode = Mode::Poll; self.mode = Mode::Poll;
warn!("mode: {:?}", self.mode); println!("\nmode: {:?}\n", self.mode);
}, }
Key::Character("r") => { Key::Character("r") => {
self.request_redraw = !self.request_redraw; self.request_redraw = !self.request_redraw;
warn!("request_redraw: {}", self.request_redraw); println!("\nrequest_redraw: {}\n", self.request_redraw);
}, }
Key::Named(NamedKey::Escape) => { Key::Named(NamedKey::Escape) => {
self.close_requested = true; self.close_requested = true;
}, }
_ => (), _ => (),
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
let window = self.window.as_ref().unwrap(); let window = self.window.as_ref().unwrap();
window.pre_present_notify(); window.pre_present_notify();
fill::fill_window(window); fill::fill_window(window);
}, }
_ => (), _ => (),
} }
} }
@@ -134,11 +133,11 @@ impl ApplicationHandler for ControlFlowDemo {
event_loop event_loop
.set_control_flow(ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME)); .set_control_flow(ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME));
} }
}, }
Mode::Poll => { Mode::Poll => {
thread::sleep(POLL_SLEEP_TIME); thread::sleep(POLL_SLEEP_TIME);
event_loop.set_control_flow(ControlFlow::Poll); event_loop.set_control_flow(ControlFlow::Poll);
}, }
}; };
if self.close_requested { if self.close_requested {

View File

@@ -1,11 +1,15 @@
#![allow(clippy::single_match)] #![allow(clippy::single_match)]
// Limit this example to only compatible platforms. // Limit this example to only compatible platforms.
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, android_platform,))] #[cfg(any(
windows_platform,
macos_platform,
x11_platform,
wayland_platform,
android_platform,
))]
fn main() -> std::process::ExitCode { fn main() -> std::process::ExitCode {
use std::process::ExitCode; use std::{process::ExitCode, thread::sleep, time::Duration};
use std::thread::sleep;
use std::time::Duration;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::WindowEvent;
@@ -45,7 +49,7 @@ fn main() -> std::process::ExitCode {
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
fill::fill_window(window); fill::fill_window(window);
window.request_redraw(); window.request_redraw();
}, }
_ => (), _ => (),
} }
} }

View File

@@ -60,17 +60,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
match event { match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
println!( println!("--------------------------------------------------------- Window {} CloseRequested", self.idx);
"--------------------------------------------------------- Window {} \
CloseRequested",
self.idx
);
fill::cleanup_window(window); fill::cleanup_window(window);
self.window = None; self.window = None;
}, }
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
fill::fill_window(window); fill::fill_window(window);
}, }
_ => (), _ => (),
} }
} }
@@ -80,7 +76,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
let mut app = App { idx: 1, ..Default::default() }; let mut app = App {
idx: 1,
..Default::default()
};
event_loop.run_app_on_demand(&mut app)?; event_loop.run_app_on_demand(&mut app)?;
println!("--------------------------------------------------------- Finished first loop"); println!("--------------------------------------------------------- Finished first loop");

View File

@@ -15,12 +15,12 @@ pub use platform::fill_window;
mod platform { mod platform {
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::mem;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use softbuffer::{Context, Surface}; use softbuffer::{Context, Surface};
use winit::window::{Window, WindowId}; use winit::window::Window;
use winit::window::WindowId;
thread_local! { thread_local! {
// NOTE: You should never do things like that, create context and drop it before // NOTE: You should never do things like that, create context and drop it before
@@ -34,32 +34,24 @@ mod platform {
/// The graphics context used to draw to a window. /// The graphics context used to draw to a window.
struct GraphicsContext { struct GraphicsContext {
/// The global softbuffer context. /// The global softbuffer context.
context: RefCell<Context<&'static Window>>, context: Context,
/// The hash map of window IDs to surfaces. /// The hash map of window IDs to surfaces.
surfaces: HashMap<WindowId, Surface<&'static Window, &'static Window>>, surfaces: HashMap<WindowId, Surface>,
} }
impl GraphicsContext { impl GraphicsContext {
fn new(w: &Window) -> Self { fn new(w: &Window) -> Self {
Self { Self {
context: RefCell::new( context: unsafe { Context::new(w) }.expect("Failed to create a softbuffer context"),
Context::new(unsafe { mem::transmute::<&'_ Window, &'static Window>(w) })
.expect("Failed to create a softbuffer context"),
),
surfaces: HashMap::new(), surfaces: HashMap::new(),
} }
} }
fn create_surface( fn create_surface(&mut self, window: &Window) -> &mut Surface {
&mut self,
window: &Window,
) -> &mut Surface<&'static Window, &'static Window> {
self.surfaces.entry(window.id()).or_insert_with(|| { self.surfaces.entry(window.id()).or_insert_with(|| {
Surface::new(&self.context.borrow(), unsafe { unsafe { Surface::new(&self.context, window) }
mem::transmute::<&'_ Window, &'static Window>(window) .expect("Failed to create a softbuffer surface")
})
.expect("Failed to create a softbuffer surface")
}) })
} }
@@ -79,17 +71,24 @@ mod platform {
// Either get the last context used or create a new one. // Either get the last context used or create a new one.
let mut gc = gc.borrow_mut(); let mut gc = gc.borrow_mut();
let surface = let surface = gc
gc.get_or_insert_with(|| GraphicsContext::new(window)).create_surface(window); .get_or_insert_with(|| GraphicsContext::new(window))
.create_surface(window);
// Fill a buffer with a solid color. // Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xff181818; const DARK_GRAY: u32 = 0xFF181818;
surface.resize(width, height).expect("Failed to resize the softbuffer surface"); surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");
let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer"); let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY); buffer.fill(DARK_GRAY);
buffer.present().expect("Failed to present the softbuffer buffer"); buffer
.present()
.expect("Failed to present the softbuffer buffer");
}) })
} }

View File

@@ -1,25 +0,0 @@
#[cfg(not(web_platform))]
pub fn init() {
use tracing_subscriber::filter::{EnvFilter, LevelFilter};
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::builder().with_default_directive(LevelFilter::INFO.into()).from_env_lossy(),
)
.init();
}
#[cfg(web_platform)]
pub fn init() {
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
tracing_subscriber::registry()
.with(
tracing_subscriber::fmt::layer()
.with_ansi(false)
.without_time()
.with_writer(tracing_web::MakeWebConsoleWriter::new()),
)
.init();
}

View File

@@ -2,28 +2,29 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::fmt;
use std::fmt::Debug; use std::fmt::Debug;
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::sync::Arc; use std::path::Path;
use std::{fmt, mem};
use ::tracing::{error, info};
use cursor_icon::CursorIcon; use cursor_icon::CursorIcon;
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
use rwh_06::{DisplayHandle, HasDisplayHandle}; use rwh_05::HasRawDisplayHandle;
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
use softbuffer::{Context, Surface}; use softbuffer::{Context, Surface};
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize}; use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent}; use winit::event::{DeviceEvent, DeviceId, Ime, WindowEvent};
use winit::event::{MouseButton, MouseScrollDelta};
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::keyboard::{Key, ModifiersState}; use winit::keyboard::{Key, ModifiersState};
use winit::window::{ use winit::window::{
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection, Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection,
Theme, Window, WindowId, Theme,
}; };
use winit::window::{Window, WindowId};
#[cfg(macos_platform)] #[cfg(macos_platform)]
use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS}; use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS};
@@ -31,21 +32,11 @@ use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMac
use winit::platform::startup_notify::{ use winit::platform::startup_notify::{
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify, self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
}; };
#[cfg(x11_platform)]
use winit::platform::x11::WindowAttributesExtX11;
#[path = "util/tracing.rs"]
mod tracing;
/// The amount of points to around the window for drag resize direction calculations. /// The amount of points to around the window for drag resize direction calculations.
const BORDER_SIZE: f64 = 20.; const BORDER_SIZE: f64 = 20.;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
#[cfg(web_platform)]
console_error_panic_hook::set_once();
tracing::init();
let event_loop = EventLoop::<UserEvent>::with_user_event().build()?; let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
let _event_loop_proxy = event_loop.create_proxy(); let _event_loop_proxy = event_loop.create_proxy();
@@ -54,7 +45,7 @@ fn main() -> Result<(), Box<dyn Error>> {
std::thread::spawn(move || { std::thread::spawn(move || {
// Wake up the `event_loop` once every second and dispatch a custom event // Wake up the `event_loop` once every second and dispatch a custom event
// from a different thread. // from a different thread.
info!("Starting to send user event every second"); println!("Starting to send user event every second");
loop { loop {
let _ = _event_loop_proxy.send_event(UserEvent::WakeUp); let _ = _event_loop_proxy.send_event(UserEvent::WakeUp);
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
@@ -83,30 +74,24 @@ struct Application {
/// ///
/// With OpenGL it could be EGLDisplay. /// With OpenGL it could be EGLDisplay.
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
context: Option<Context<DisplayHandle<'static>>>, context: Option<Context>,
} }
impl Application { impl Application {
fn new<T>(event_loop: &EventLoop<T>) -> Self { fn new<T>(event_loop: &EventLoop<T>) -> Self {
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe. // SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
let context = Some( let context = Some(unsafe { Context::from_raw(event_loop.raw_display_handle()).unwrap() });
Context::new(unsafe {
std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(
event_loop.display_handle().unwrap(),
)
})
.unwrap(),
);
// You'll have to choose an icon size at your own discretion. On X11, the desired size // You'll have to choose an icon size at your own discretion. On X11, the desired size varies
// varies by WM, and on Windows, you still have to account for screen scaling. Here // by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
// we use 32px, since it seems to work well enough in most cases. Be careful about // since it seems to work well enough in most cases. Be careful about going too high, or
// going too high, or you'll be bitten by the low-quality downscaling built into the // you'll be bitten by the low-quality downscaling built into the WM.
// WM. let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/data/icon.png");
let icon = load_icon(include_bytes!("data/icon.png"));
info!("Loading cursor assets"); let icon = load_icon(Path::new(path));
println!("Loading cursor assets");
let custom_cursors = vec![ let custom_cursors = vec![
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross.png"))), event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross.png"))),
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross2.png"))), event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross2.png"))),
@@ -138,43 +123,15 @@ impl Application {
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
if let Some(token) = event_loop.read_token_from_env() { if let Some(token) = event_loop.read_token_from_env() {
startup_notify::reset_activation_token_env(); startup_notify::reset_activation_token_env();
info!("Using token {:?} to activate a window", token); println!("Using token {:?} to activate a window", token);
window_attributes = window_attributes.with_activation_token(token); window_attributes = window_attributes.with_activation_token(token);
} }
#[cfg(x11_platform)]
match std::env::var("X11_VISUAL_ID") {
Ok(visual_id_str) => {
info!("Using X11 visual id {visual_id_str}");
let visual_id = visual_id_str.parse()?;
window_attributes = window_attributes.with_x11_visual(visual_id);
},
Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"),
}
#[cfg(x11_platform)]
match std::env::var("X11_SCREEN_ID") {
Ok(screen_id_str) => {
info!("Placing the window on X11 screen {screen_id_str}");
let screen_id = screen_id_str.parse()?;
window_attributes = window_attributes.with_x11_screen(screen_id);
},
Err(_) => info!(
"Set the X11_SCREEN_ID env variable to place the window on non-default screen"
),
}
#[cfg(macos_platform)] #[cfg(macos_platform)]
if let Some(tab_id) = _tab_id { if let Some(tab_id) = _tab_id {
window_attributes = window_attributes.with_tabbing_identifier(&tab_id); window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
} }
#[cfg(web_platform)]
{
use winit::platform::web::WindowAttributesExtWebSys;
window_attributes = window_attributes.with_append(true);
}
let window = event_loop.create_window(window_attributes)?; let window = event_loop.create_window(window_attributes)?;
#[cfg(ios_platform)] #[cfg(ios_platform)]
@@ -183,12 +140,11 @@ impl Application {
window.recognize_doubletap_gesture(true); window.recognize_doubletap_gesture(true);
window.recognize_pinch_gesture(true); window.recognize_pinch_gesture(true);
window.recognize_rotation_gesture(true); window.recognize_rotation_gesture(true);
window.recognize_pan_gesture(true, 2, 2);
} }
let window_state = WindowState::new(self, window)?; let window_state = WindowState::new(self, window)?;
let window_id = window_state.window.id(); let window_id = window_state.window.id();
info!("Created new window with id={window_id:?}"); println!("Created new window with id={window_id:?}");
self.windows.insert(window_id, window_state); self.windows.insert(window_id, window_state);
Ok(window_id) Ok(window_id)
} }
@@ -196,23 +152,23 @@ impl Application {
fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) { fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
// let cursor_position = self.cursor_position; // let cursor_position = self.cursor_position;
let window = self.windows.get_mut(&window_id).unwrap(); let window = self.windows.get_mut(&window_id).unwrap();
info!("Executing action: {action:?}"); println!("Executing action: {action:?}");
match action { match action {
Action::CloseWindow => { Action::CloseWindow => {
let _ = self.windows.remove(&window_id); let _ = self.windows.remove(&window_id);
}, }
Action::CreateNewWindow => { Action::CreateNewWindow => {
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
if let Err(err) = window.window.request_activation_token() { if let Err(err) = window.window.request_activation_token() {
info!("Failed to get activation token: {err}"); println!("Failed to get activation token: {err}");
} else { } else {
return; return;
} }
if let Err(err) = self.create_window(event_loop, None) { if let Err(err) = self.create_window(event_loop, None) {
error!("Error creating new window: {err}"); eprintln!("Error creating new window: {err}");
} }
}, }
Action::ToggleResizeIncrements => window.toggle_resize_increments(), Action::ToggleResizeIncrements => window.toggle_resize_increments(),
Action::ToggleCursorVisibility => window.toggle_cursor_visibility(), Action::ToggleCursorVisibility => window.toggle_cursor_visibility(),
Action::ToggleResizable => window.toggle_resizable(), Action::ToggleResizable => window.toggle_resizable(),
@@ -223,12 +179,6 @@ impl Application {
Action::Minimize => window.minimize(), Action::Minimize => window.minimize(),
Action::NextCursor => window.next_cursor(), Action::NextCursor => window.next_cursor(),
Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors), Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors),
#[cfg(web_platform)]
Action::UrlCustomCursor => window.url_custom_cursor(event_loop),
#[cfg(web_platform)]
Action::AnimationCustomCursor => {
window.animation_custom_cursor(event_loop, &self.custom_cursors)
},
Action::CycleCursorGrab => window.cycle_cursor_grab(), Action::CycleCursorGrab => window.cycle_cursor_grab(),
Action::DragWindow => window.drag_window(), Action::DragWindow => window.drag_window(),
Action::DragResizeWindow => window.drag_resize_window(), Action::DragResizeWindow => window.drag_resize_window(),
@@ -236,25 +186,18 @@ impl Application {
Action::PrintHelp => self.print_help(), Action::PrintHelp => self.print_help(),
#[cfg(macos_platform)] #[cfg(macos_platform)]
Action::CycleOptionAsAlt => window.cycle_option_as_alt(), Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
Action::SetTheme(theme) => {
window.window.set_theme(theme);
// Get the resulting current theme to draw with
let actual_theme = theme.or_else(|| window.window.theme()).unwrap_or(Theme::Dark);
window.set_draw_theme(actual_theme);
},
#[cfg(macos_platform)] #[cfg(macos_platform)]
Action::CreateNewTab => { Action::CreateNewTab => {
let tab_id = window.window.tabbing_identifier(); let tab_id = window.window.tabbing_identifier();
if let Err(err) = self.create_window(event_loop, Some(tab_id)) { if let Err(err) = self.create_window(event_loop, Some(tab_id)) {
error!("Error creating new window: {err}"); eprintln!("Error creating new window: {err}");
} }
}, }
Action::RequestResize => window.swap_dimensions(),
} }
} }
fn dump_monitors(&self, event_loop: &ActiveEventLoop) { fn dump_monitors(&self, event_loop: &ActiveEventLoop) {
info!("Monitors information"); println!("Monitors information");
let primary_monitor = event_loop.primary_monitor(); let primary_monitor = event_loop.primary_monitor();
for monitor in event_loop.available_monitors() { for monitor in event_loop.available_monitors() {
let intro = if primary_monitor.as_ref() == Some(&monitor) { let intro = if primary_monitor.as_ref() == Some(&monitor) {
@@ -264,54 +207,60 @@ impl Application {
}; };
if let Some(name) = monitor.name() { if let Some(name) = monitor.name() {
info!("{intro}: {name}"); println!("{intro}: {name}");
} else { } else {
info!("{intro}: [no name]"); println!("{intro}: [no name]");
} }
let PhysicalSize { width, height } = monitor.size(); let PhysicalSize { width, height } = monitor.size();
info!( print!(" Current mode: {width}x{height}");
" Current mode: {width}x{height}{}", if let Some(m_hz) = monitor.refresh_rate_millihertz() {
if let Some(m_hz) = monitor.refresh_rate_millihertz() { println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000) } else {
} else { println!();
String::new() }
}
);
let PhysicalPosition { x, y } = monitor.position(); let PhysicalPosition { x, y } = monitor.position();
info!(" Position: {x},{y}"); println!(" Position: {x},{y}");
info!(" Scale factor: {}", monitor.scale_factor()); println!(" Scale factor: {}", monitor.scale_factor());
info!(" Available modes (width x height x bit-depth):"); println!(" Available modes (width x height x bit-depth):");
for mode in monitor.video_modes() { for mode in monitor.video_modes() {
let PhysicalSize { width, height } = mode.size(); let PhysicalSize { width, height } = mode.size();
let bits = mode.bit_depth(); let bits = mode.bit_depth();
let m_hz = mode.refresh_rate_millihertz(); let m_hz = mode.refresh_rate_millihertz();
info!(" {width}x{height}x{bits} @ {}.{} Hz", m_hz / 1000, m_hz % 1000); println!(
" {width}x{height}x{bits} @ {}.{} Hz",
m_hz / 1000,
m_hz % 1000
);
} }
} }
} }
/// Process the key binding. /// Process the key binding.
fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> { fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
KEY_BINDINGS KEY_BINDINGS.iter().find_map(|binding| {
.iter() binding
.find_map(|binding| binding.is_triggered_by(&key, mods).then_some(binding.action)) .is_triggered_by(&key, mods)
.then_some(binding.action)
})
} }
/// Process mouse binding. /// Process mouse binding.
fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> { fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> {
MOUSE_BINDINGS MOUSE_BINDINGS.iter().find_map(|binding| {
.iter() binding
.find_map(|binding| binding.is_triggered_by(&button, mods).then_some(binding.action)) .is_triggered_by(&button, mods)
.then_some(binding.action)
})
} }
fn print_help(&self) { fn print_help(&self) {
info!("Keyboard bindings:"); println!("Keyboard bindings:");
for binding in KEY_BINDINGS { for binding in KEY_BINDINGS {
info!( println!(
"{}{:<10} - {} ({})", "{}{:<10} - {} ({})",
modifiers_to_string(binding.mods), modifiers_to_string(binding.mods),
binding.trigger, binding.trigger,
@@ -319,9 +268,9 @@ impl Application {
binding.action.help(), binding.action.help(),
); );
} }
info!("Mouse bindings:"); println!("Mouse bindings:");
for binding in MOUSE_BINDINGS { for binding in MOUSE_BINDINGS {
info!( println!(
"{}{:<10} - {} ({})", "{}{:<10} - {} ({})",
modifiers_to_string(binding.mods), modifiers_to_string(binding.mods),
mouse_button_to_string(binding.trigger), mouse_button_to_string(binding.trigger),
@@ -334,7 +283,7 @@ impl Application {
impl ApplicationHandler<UserEvent> for Application { impl ApplicationHandler<UserEvent> for Application {
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) { fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
info!("User event: {event:?}"); println!("User event: {event:?}");
} }
fn window_event( fn window_event(
@@ -351,46 +300,50 @@ impl ApplicationHandler<UserEvent> for Application {
match event { match event {
WindowEvent::Resized(size) => { WindowEvent::Resized(size) => {
window.resize(size); window.resize(size);
}, }
WindowEvent::Focused(focused) => { WindowEvent::Focused(focused) => {
if focused { if focused {
info!("Window={window_id:?} focused"); println!("Window={window_id:?} fosused");
} else { } else {
info!("Window={window_id:?} unfocused"); println!("Window={window_id:?} unfosused");
} }
}, }
WindowEvent::ScaleFactorChanged { scale_factor, .. } => { WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
info!("Window={window_id:?} changed scale to {scale_factor}"); println!("Window={window_id:?} changed scale to {scale_factor}");
}, }
WindowEvent::ThemeChanged(theme) => { WindowEvent::ThemeChanged(theme) => {
info!("Theme changed to {theme:?}"); println!("Theme changed to {theme:?}");
window.set_draw_theme(theme); window.set_theme(theme);
}, }
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
if let Err(err) = window.draw() { if let Err(err) = window.draw() {
error!("Error drawing window: {err}"); eprintln!("Error drawing window: {err}");
} }
}, }
WindowEvent::Occluded(occluded) => { WindowEvent::Occluded(occluded) => {
window.set_occluded(occluded); window.set_occluded(occluded);
}, }
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
info!("Closing Window={window_id:?}"); println!("Closing Window={window_id:?}");
self.windows.remove(&window_id); self.windows.remove(&window_id);
}, }
WindowEvent::ModifiersChanged(modifiers) => { WindowEvent::ModifiersChanged(modifiers) => {
window.modifiers = modifiers.state(); window.modifiers = modifiers.state();
info!("Modifiers changed to {:?}", window.modifiers); println!("Modifiers changed to {:?}", window.modifiers);
}, }
WindowEvent::MouseWheel { delta, .. } => match delta { WindowEvent::MouseWheel { delta, .. } => match delta {
MouseScrollDelta::LineDelta(x, y) => { MouseScrollDelta::LineDelta(x, y) => {
info!("Mouse wheel Line Delta: ({x},{y})"); println!("Mouse wheel Line Delta: ({x},{y})");
}, }
MouseScrollDelta::PixelDelta(px) => { MouseScrollDelta::PixelDelta(px) => {
info!("Mouse wheel Pixel Delta: ({},{})", px.x, px.y); println!("Mouse wheel Pixel Delta: ({},{})", px.x, px.y);
}, }
}, },
WindowEvent::KeyboardInput { event, is_synthetic: false, .. } => { WindowEvent::KeyboardInput {
event,
is_synthetic: false,
..
} => {
let mods = window.modifiers; let mods = window.modifiers;
// Dispatch actions only on press. // Dispatch actions only on press.
@@ -405,68 +358,65 @@ impl ApplicationHandler<UserEvent> for Application {
self.handle_action(event_loop, window_id, action); self.handle_action(event_loop, window_id, action);
} }
} }
}, }
WindowEvent::MouseInput { button, state, .. } => { WindowEvent::MouseInput { button, state, .. } => {
let mods = window.modifiers; let mods = window.modifiers;
if let Some(action) = if let Some(action) = state
state.is_pressed().then(|| Self::process_mouse_binding(button, &mods)).flatten() .is_pressed()
.then(|| Self::process_mouse_binding(button, &mods))
.flatten()
{ {
self.handle_action(event_loop, window_id, action); self.handle_action(event_loop, window_id, action);
} }
}, }
WindowEvent::CursorLeft { .. } => { WindowEvent::CursorLeft { .. } => {
info!("Cursor left Window={window_id:?}"); println!("Cursor left Window={window_id:?}");
window.cursor_left(); window.cursor_left();
}, }
WindowEvent::CursorMoved { position, .. } => { WindowEvent::CursorMoved { position, .. } => {
info!("Moved cursor to {position:?}"); println!("Moved cursor to {position:?}");
window.cursor_moved(position); window.cursor_moved(position);
}, }
WindowEvent::ActivationTokenDone { token: _token, .. } => { WindowEvent::ActivationTokenDone { token: _token, .. } => {
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
{ {
startup_notify::set_activation_token_env(_token); startup_notify::set_activation_token_env(_token);
if let Err(err) = self.create_window(event_loop, None) { if let Err(err) = self.create_window(event_loop, None) {
error!("Error creating new window: {err}"); eprintln!("Error creating new window: {err}");
} }
} }
}, }
WindowEvent::Ime(event) => match event { WindowEvent::Ime(event) => match event {
Ime::Enabled => info!("IME enabled for Window={window_id:?}"), Ime::Enabled => println!("IME enabled for Window={window_id:?}"),
Ime::Preedit(text, caret_pos) => { Ime::Preedit(text, caret_pos) => {
info!("Preedit: {}, with caret at {:?}", text, caret_pos); println!("Preedit: {}, with caret at {:?}", text, caret_pos);
}, }
Ime::Commit(text) => { Ime::Commit(text) => {
info!("Committed: {}", text); println!("Committed: {}", text);
}, }
Ime::Disabled => info!("IME disabled for Window={window_id:?}"), Ime::Disabled => println!("IME disabled for Window={window_id:?}"),
}, },
WindowEvent::PinchGesture { delta, .. } => { WindowEvent::PinchGesture { delta, .. } => {
window.zoom += delta; window.zoom += delta;
let zoom = window.zoom; let zoom = window.zoom;
if delta > 0.0 { if delta > 0.0 {
info!("Zoomed in {delta:.5} (now: {zoom:.5})"); println!("Zoomed in {delta:.5} (now: {zoom:.5})");
} else { } else {
info!("Zoomed out {delta:.5} (now: {zoom:.5})"); println!("Zoomed out {delta:.5} (now: {zoom:.5})");
} }
}, }
WindowEvent::RotationGesture { delta, .. } => { WindowEvent::RotationGesture { delta, .. } => {
window.rotated += delta; window.rotated += delta;
let rotated = window.rotated; let rotated = window.rotated;
if delta > 0.0 { if delta > 0.0 {
info!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})"); println!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
} else { } else {
info!("Rotated clockwise {delta:.5} (now: {rotated:.5})"); println!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
} }
}, }
WindowEvent::PanGesture { delta, phase, .. } => {
window.panned.x += delta.x;
window.panned.y += delta.y;
info!("Panned ({delta:?})) (now: {:?}), {phase:?}", window.panned);
},
WindowEvent::DoubleTapGesture { .. } => { WindowEvent::DoubleTapGesture { .. } => {
info!("Smart zoom"); println!("Smart zoom");
}, }
WindowEvent::TouchpadPressure { .. } WindowEvent::TouchpadPressure { .. }
| WindowEvent::HoveredFileCancelled | WindowEvent::HoveredFileCancelled
| WindowEvent::KeyboardInput { .. } | WindowEvent::KeyboardInput { .. }
@@ -486,22 +436,23 @@ impl ApplicationHandler<UserEvent> for Application {
device_id: DeviceId, device_id: DeviceId,
event: DeviceEvent, event: DeviceEvent,
) { ) {
info!("Device {device_id:?} event: {event:?}"); println!("Device {device_id:?} event: {event:?}");
} }
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn resumed(&mut self, event_loop: &ActiveEventLoop) {
info!("Resumed the event loop"); println!("Resumed the event loop");
self.dump_monitors(event_loop); self.dump_monitors(event_loop);
// Create initial window. // Create initial window.
self.create_window(event_loop, None).expect("failed to create initial window"); self.create_window(event_loop, None)
.expect("failed to create initial window");
self.print_help(); self.print_help();
} }
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if self.windows.is_empty() { if self.windows.is_empty() {
info!("No windows left, exiting..."); println!("No windows left, exiting...");
event_loop.exit(); event_loop.exit();
} }
} }
@@ -521,9 +472,9 @@ struct WindowState {
/// ///
/// NOTE: This surface must be dropped before the `Window`. /// NOTE: This surface must be dropped before the `Window`.
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
surface: Surface<DisplayHandle<'static>, Arc<Window>>, surface: Surface,
/// The actual winit Window. /// The actual winit Window.
window: Arc<Window>, window: Window,
/// The window theme we're drawing with. /// The window theme we're drawing with.
theme: Theme, theme: Theme,
/// Cursor position over the window. /// Cursor position over the window.
@@ -538,8 +489,6 @@ struct WindowState {
zoom: f64, zoom: f64,
/// The amount of rotation of the window. /// The amount of rotation of the window.
rotated: f32, rotated: f32,
/// The amount of pan of the window.
panned: PhysicalPosition<f32>,
#[cfg(macos_platform)] #[cfg(macos_platform)]
option_as_alt: OptionAsAlt, option_as_alt: OptionAsAlt,
@@ -552,15 +501,13 @@ struct WindowState {
impl WindowState { impl WindowState {
fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> { fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
let window = Arc::new(window);
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus // SAFETY: the surface is dropped before the `window` which provided it with handle, thus
// it doesn't outlive it. // it doesn't outlive it.
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?; let surface = unsafe { Surface::new(app.context.as_ref().unwrap(), &window)? };
let theme = window.theme().unwrap_or(Theme::Dark); let theme = window.theme().unwrap_or(Theme::Dark);
info!("Theme: {theme:?}"); println!("Theme: {theme:?}");
let named_idx = 0; let named_idx = 0;
window.set_cursor(CURSORS[named_idx]); window.set_cursor(CURSORS[named_idx]);
@@ -585,7 +532,6 @@ impl WindowState {
modifiers: Default::default(), modifiers: Default::default(),
occluded: Default::default(), occluded: Default::default(),
rotated: Default::default(), rotated: Default::default(),
panned: Default::default(),
zoom: Default::default(), zoom: Default::default(),
}; };
@@ -597,7 +543,8 @@ impl WindowState {
self.ime = !self.ime; self.ime = !self.ime;
self.window.set_ime_allowed(self.ime); self.window.set_ime_allowed(self.ime);
if let Some(position) = self.ime.then_some(self.cursor_position).flatten() { if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20)); self.window
.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
} }
} }
@@ -608,7 +555,8 @@ impl WindowState {
pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) { pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
self.cursor_position = Some(position); self.cursor_position = Some(position);
if self.ime { if self.ime {
self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20)); self.window
.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
} }
} }
@@ -646,7 +594,7 @@ impl WindowState {
Some(_) => None, Some(_) => None,
None => Some(LogicalSize::new(25.0, 25.0)), None => Some(LogicalSize::new(25.0, 25.0)),
}; };
info!("Had increments: {}", new_increments.is_none()); println!("Had increments: {}", new_increments.is_none());
self.window.set_resize_increments(new_increments); self.window.set_resize_increments(new_increments);
} }
@@ -668,9 +616,9 @@ impl WindowState {
CursorGrabMode::Confined => CursorGrabMode::Locked, CursorGrabMode::Confined => CursorGrabMode::Locked,
CursorGrabMode::Locked => CursorGrabMode::None, CursorGrabMode::Locked => CursorGrabMode::None,
}; };
info!("Changing cursor grab mode to {:?}", self.cursor_grab); println!("Changing cursor grab mode to {:?}", self.cursor_grab);
if let Err(err) = self.window.set_cursor_grab(self.cursor_grab) { if let Err(err) = self.window.set_cursor_grab(self.cursor_grab) {
error!("Error setting cursor grab: {err}"); eprintln!("Error setting cursor grab: {err}");
} }
} }
@@ -682,34 +630,16 @@ impl WindowState {
OptionAsAlt::OnlyRight => OptionAsAlt::Both, OptionAsAlt::OnlyRight => OptionAsAlt::Both,
OptionAsAlt::Both => OptionAsAlt::None, OptionAsAlt::Both => OptionAsAlt::None,
}; };
info!("Setting option as alt {:?}", self.option_as_alt); println!("Setting option as alt {:?}", self.option_as_alt);
self.window.set_option_as_alt(self.option_as_alt); self.window.set_option_as_alt(self.option_as_alt);
} }
/// Swap the window dimensions with `request_inner_size`.
fn swap_dimensions(&mut self) {
let old_inner_size = self.window.inner_size();
let mut inner_size = old_inner_size;
mem::swap(&mut inner_size.width, &mut inner_size.height);
info!("Requesting resize from {old_inner_size:?} to {inner_size:?}");
if let Some(new_inner_size) = self.window.request_inner_size(inner_size) {
if old_inner_size == new_inner_size {
info!("Inner size change got ignored");
} else {
self.resize(new_inner_size);
}
} else {
info!("Request inner size is asynchronous");
}
}
/// Pick the next cursor. /// Pick the next cursor.
fn next_cursor(&mut self) { fn next_cursor(&mut self) {
self.named_idx = (self.named_idx + 1) % CURSORS.len(); self.named_idx = (self.named_idx + 1) % CURSORS.len();
info!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]); println!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]);
self.window.set_cursor(Cursor::Icon(CURSORS[self.named_idx])); self.window
.set_cursor(Cursor::Icon(CURSORS[self.named_idx]));
} }
/// Pick the next custom cursor. /// Pick the next custom cursor.
@@ -719,52 +649,24 @@ impl WindowState {
self.window.set_cursor(cursor); self.window.set_cursor(cursor);
} }
/// Custom cursor from an URL.
#[cfg(web_platform)]
fn url_custom_cursor(&mut self, event_loop: &ActiveEventLoop) {
let cursor = event_loop.create_custom_cursor(url_custom_cursor());
self.window.set_cursor(cursor);
}
/// Custom cursor from a URL.
#[cfg(web_platform)]
fn animation_custom_cursor(
&mut self,
event_loop: &ActiveEventLoop,
custom_cursors: &[CustomCursor],
) {
use std::time::Duration;
use winit::platform::web::CustomCursorExtWebSys;
let cursors = vec![
custom_cursors[0].clone(),
custom_cursors[1].clone(),
event_loop.create_custom_cursor(url_custom_cursor()),
];
let cursor = CustomCursor::from_animation(Duration::from_secs(3), cursors).unwrap();
let cursor = event_loop.create_custom_cursor(cursor);
self.window.set_cursor(cursor);
}
/// Resize the window to the new size. /// Resize the window to the new size.
fn resize(&mut self, size: PhysicalSize<u32>) { fn resize(&mut self, _size: PhysicalSize<u32>) {
info!("Resized to {size:?}");
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
{ {
let (width, height) = match (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) let (width, height) =
{ match (NonZeroU32::new(_size.width), NonZeroU32::new(_size.height)) {
(Some(width), Some(height)) => (width, height), (Some(width), Some(height)) => (width, height),
_ => return, _ => return,
}; };
self.surface.resize(width, height).expect("failed to resize inner buffer"); self.surface
.resize(width, height)
.expect("failed to resize inner buffer");
} }
self.window.request_redraw(); self.window.request_redraw();
} }
/// Change the theme that things are drawn in. /// Change the theme.
fn set_draw_theme(&mut self, theme: Theme) { fn set_theme(&mut self, theme: Theme) {
self.theme = theme; self.theme = theme;
self.window.request_redraw(); self.window.request_redraw();
} }
@@ -779,9 +681,9 @@ impl WindowState {
/// Drag the window. /// Drag the window.
fn drag_window(&self) { fn drag_window(&self) {
if let Err(err) = self.window.drag_window() { if let Err(err) = self.window.drag_window() {
info!("Error starting window drag: {err}"); println!("Error starting window drag: {err}");
} else { } else {
info!("Dragging window Window={:?}", self.window.id()); println!("Dragging window Window={:?}", self.window.id());
} }
} }
@@ -790,9 +692,9 @@ impl WindowState {
let position = match self.cursor_position { let position = match self.cursor_position {
Some(position) => position, Some(position) => position,
None => { None => {
info!("Drag-resize requires cursor to be inside the window"); println!("Drag-resize requires cursor to be inside the window");
return; return;
}, }
}; };
let win_size = self.window.inner_size(); let win_size = self.window.inner_size();
@@ -829,9 +731,9 @@ impl WindowState {
}; };
if let Err(err) = self.window.drag_resize_window(direction) { if let Err(err) = self.window.drag_resize_window(direction) {
info!("Error starting window drag-resize: {err}"); println!("Error starting window drag-resize: {err}");
} else { } else {
info!("Drag-resizing window Window={:?}", self.window.id()); println!("Drag-resizing window Window={:?}", self.window.id());
} }
} }
@@ -847,12 +749,12 @@ impl WindowState {
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
fn draw(&mut self) -> Result<(), Box<dyn Error>> { fn draw(&mut self) -> Result<(), Box<dyn Error>> {
if self.occluded { if self.occluded {
info!("Skipping drawing occluded window={:?}", self.window.id()); println!("Skipping drawing occluded window={:?}", self.window.id());
return Ok(()); return Ok(());
} }
const WHITE: u32 = 0xffffffff; const WHITE: u32 = 0xFFFFFFFF;
const DARK_GRAY: u32 = 0xff181818; const DARK_GRAY: u32 = 0xFF181818;
let color = match self.theme { let color = match self.theme {
Theme::Light => WHITE, Theme::Light => WHITE,
@@ -868,7 +770,7 @@ impl WindowState {
#[cfg(any(android_platform, ios_platform))] #[cfg(any(android_platform, ios_platform))]
fn draw(&mut self) -> Result<(), Box<dyn Error>> { fn draw(&mut self) -> Result<(), Box<dyn Error>> {
info!("Drawing but without rendering..."); println!("Drawing but without rendering...");
Ok(()) Ok(())
} }
} }
@@ -881,7 +783,11 @@ struct Binding<T: Eq> {
impl<T: Eq> Binding<T> { impl<T: Eq> Binding<T> {
const fn new(trigger: T, mods: ModifiersState, action: Action) -> Self { const fn new(trigger: T, mods: ModifiersState, action: Action) -> Self {
Self { trigger, mods, action } Self {
trigger,
mods,
action,
}
} }
fn is_triggered_by(&self, trigger: &T, mods: &ModifiersState) -> bool { fn is_triggered_by(&self, trigger: &T, mods: &ModifiersState) -> bool {
@@ -903,10 +809,6 @@ enum Action {
Minimize, Minimize,
NextCursor, NextCursor,
NextCustomCursor, NextCustomCursor,
#[cfg(web_platform)]
UrlCustomCursor,
#[cfg(web_platform)]
AnimationCustomCursor,
CycleCursorGrab, CycleCursorGrab,
PrintHelp, PrintHelp,
DragWindow, DragWindow,
@@ -914,10 +816,8 @@ enum Action {
ShowWindowMenu, ShowWindowMenu,
#[cfg(macos_platform)] #[cfg(macos_platform)]
CycleOptionAsAlt, CycleOptionAsAlt,
SetTheme(Option<Theme>),
#[cfg(macos_platform)] #[cfg(macos_platform)]
CreateNewTab, CreateNewTab,
RequestResize,
} }
impl Action { impl Action {
@@ -935,10 +835,6 @@ impl Action {
Action::ToggleResizeIncrements => "Use resize increments when resizing window", Action::ToggleResizeIncrements => "Use resize increments when resizing window",
Action::NextCursor => "Advance the cursor to the next value", Action::NextCursor => "Advance the cursor to the next value",
Action::NextCustomCursor => "Advance custom cursor to the next value", Action::NextCustomCursor => "Advance custom cursor to the next value",
#[cfg(web_platform)]
Action::UrlCustomCursor => "Custom cursor from an URL",
#[cfg(web_platform)]
Action::AnimationCustomCursor => "Custom cursor from an animation",
Action::CycleCursorGrab => "Cycle through cursor grab mode", Action::CycleCursorGrab => "Cycle through cursor grab mode",
Action::PrintHelp => "Print help", Action::PrintHelp => "Print help",
Action::DragWindow => "Start window drag", Action::DragWindow => "Start window drag",
@@ -946,12 +842,8 @@ impl Action {
Action::ShowWindowMenu => "Show window menu", Action::ShowWindowMenu => "Show window menu",
#[cfg(macos_platform)] #[cfg(macos_platform)]
Action::CycleOptionAsAlt => "Cycle option as alt mode", Action::CycleOptionAsAlt => "Cycle option as alt mode",
Action::SetTheme(None) => "Change to the system theme",
Action::SetTheme(Some(Theme::Light)) => "Change to a light theme",
Action::SetTheme(Some(Theme::Dark)) => "Change to a dark theme",
#[cfg(macos_platform)] #[cfg(macos_platform)]
Action::CreateNewTab => "Create new tab", Action::CreateNewTab => "Create new tab",
Action::RequestResize => "Request a resize",
} }
} }
} }
@@ -970,24 +862,11 @@ fn decode_cursor(bytes: &[u8]) -> CustomCursorSource {
CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap() CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap()
} }
#[cfg(web_platform)] fn load_icon(path: &Path) -> Icon {
fn url_custom_cursor() -> CustomCursorSource {
use std::sync::atomic::{AtomicU64, Ordering};
use winit::platform::web::CustomCursorExtWebSys;
static URL_COUNTER: AtomicU64 = AtomicU64::new(0);
CustomCursor::from_url(
format!("https://picsum.photos/128?random={}", URL_COUNTER.fetch_add(1, Ordering::Relaxed)),
64,
64,
)
}
fn load_icon(bytes: &[u8]) -> Icon {
let (icon_rgba, icon_width, icon_height) = { let (icon_rgba, icon_width, icon_height) = {
let image = image::load_from_memory(bytes).unwrap().into_rgba8(); let image = image::open(path)
.expect("Failed to open icon path")
.into_rgba8();
let (width, height) = image.dimensions(); let (width, height) = image.dimensions();
let rgba = image.into_raw(); let rgba = image.into_raw();
(rgba, width, height) (rgba, width, height)
@@ -1071,7 +950,6 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
Binding::new("L", ModifiersState::CONTROL, Action::CycleCursorGrab), Binding::new("L", ModifiersState::CONTROL, Action::CycleCursorGrab),
Binding::new("P", ModifiersState::CONTROL, Action::ToggleResizeIncrements), Binding::new("P", ModifiersState::CONTROL, Action::ToggleResizeIncrements),
Binding::new("R", ModifiersState::CONTROL, Action::ToggleResizable), Binding::new("R", ModifiersState::CONTROL, Action::ToggleResizable),
Binding::new("R", ModifiersState::ALT, Action::RequestResize),
// M. // M.
Binding::new("M", ModifiersState::CONTROL, Action::ToggleMaximize), Binding::new("M", ModifiersState::CONTROL, Action::ToggleMaximize),
Binding::new("M", ModifiersState::ALT, Action::Minimize), Binding::new("M", ModifiersState::ALT, Action::Minimize),
@@ -1080,23 +958,7 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
// C. // C.
Binding::new("C", ModifiersState::CONTROL, Action::NextCursor), Binding::new("C", ModifiersState::CONTROL, Action::NextCursor),
Binding::new("C", ModifiersState::ALT, Action::NextCustomCursor), Binding::new("C", ModifiersState::ALT, Action::NextCustomCursor),
#[cfg(web_platform)]
Binding::new(
"C",
ModifiersState::CONTROL.union(ModifiersState::SHIFT),
Action::UrlCustomCursor,
),
#[cfg(web_platform)]
Binding::new(
"C",
ModifiersState::ALT.union(ModifiersState::SHIFT),
Action::AnimationCustomCursor,
),
Binding::new("Z", ModifiersState::CONTROL, Action::ToggleCursorVisibility), Binding::new("Z", ModifiersState::CONTROL, Action::ToggleCursorVisibility),
// K.
Binding::new("K", ModifiersState::empty(), Action::SetTheme(None)),
Binding::new("K", ModifiersState::SUPER, Action::SetTheme(Some(Theme::Light))),
Binding::new("K", ModifiersState::CONTROL, Action::SetTheme(Some(Theme::Dark))),
#[cfg(macos_platform)] #[cfg(macos_platform)]
Binding::new("T", ModifiersState::SUPER, Action::CreateNewTab), Binding::new("T", ModifiersState::SUPER, Action::CreateNewTab),
#[cfg(macos_platform)] #[cfg(macos_platform)]
@@ -1104,7 +966,19 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
]; ];
const MOUSE_BINDINGS: &[Binding<MouseButton>] = &[ const MOUSE_BINDINGS: &[Binding<MouseButton>] = &[
Binding::new(MouseButton::Left, ModifiersState::ALT, Action::DragResizeWindow), Binding::new(
Binding::new(MouseButton::Left, ModifiersState::CONTROL, Action::DragWindow), MouseButton::Left,
Binding::new(MouseButton::Right, ModifiersState::CONTROL, Action::ShowWindowMenu), ModifiersState::ALT,
Action::DragResizeWindow,
),
Binding::new(
MouseButton::Left,
ModifiersState::CONTROL,
Action::DragWindow,
),
Binding::new(
MouseButton::Right,
ModifiersState::CONTROL,
Action::ShowWindowMenu,
),
]; ];

View File

@@ -39,7 +39,7 @@ fn main() -> Result<(), Box<dyn Error>> {
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
window.pre_present_notify(); window.pre_present_notify();
fill::fill_window(window); fill::fill_window(window);
}, }
_ => (), _ => (),
} }
} }
@@ -58,7 +58,10 @@ fn main() -> Result<(), Box<dyn Error>> {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let event_loop = EventLoop::new()?; let event_loop = EventLoop::new()?;
let mut app = XEmbedDemo { parent_window_id, window: None }; let mut app = XEmbedDemo {
parent_window_id,
window: None,
};
event_loop.run_app(&mut app).map_err(Into::into) event_loop.run_app(&mut app).map_err(Into::into)
} }

11
run-wasm/Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "run-wasm"
version = "0.1.0"
rust-version.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
publish = false
[dependencies]
cargo-run-wasm = "0.2.0"

3
run-wasm/src/main.rs Normal file
View File

@@ -0,0 +1,3 @@
fn main() {
cargo_run_wasm::run_wasm_with_css("body { margin: 0px; }");
}

View File

@@ -1,19 +1,3 @@
format_code_in_doc_comments = true force_explicit_abi=true
match_block_trailing_comma = true use_field_init_shorthand=true
condense_wildcard_suffixes = true # merge_imports=true
use_field_init_shorthand = true
normalize_doc_attributes = true
overflow_delimited_expr = true
imports_granularity = "Module"
use_small_heuristics = "Max"
format_macro_matchers = true
error_on_unformatted = true
format_macro_bodies = true
hex_literal_case = "Lower"
normalize_comments = true
# Some macros break with this.
reorder_impl_items = false
newline_style = "Unix"
format_strings = true
wrap_comments = true
comment_width = 100

View File

@@ -11,7 +11,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// This is a useful place to put code that should be done before you start processing /// This is a useful place to put code that should be done before you start processing
/// events, such as updating frame timing information for benchmarking or checking the /// events, such as updating frame timing information for benchmarking or checking the
/// [`StartCause`] to see if a timer set by /// [`StartCause`] to see if a timer set by
/// [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil] has elapsed. /// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed.
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
let _ = (event_loop, cause); let _ = (event_loop, cause);
} }
@@ -20,8 +20,8 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// ///
/// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a /// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a
/// formal suspend/resume lifecycle. For systems without a formal suspend/resume lifecycle /// formal suspend/resume lifecycle. For systems without a formal suspend/resume lifecycle
/// the `Resumed` event is always emitted after the /// the `Resumed` event is always emitted after the [`NewEvents(StartCause::Init)`][StartCause::Init]
/// [`NewEvents(StartCause::Init)`][StartCause::Init] event. /// event.
/// ///
/// # Portability /// # Portability
/// ///
@@ -33,16 +33,15 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally /// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across /// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications /// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` /// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events.
/// events.
/// ///
/// Also see [`Suspended`] notes. /// Also see [`Suspended`] notes.
/// ///
/// ## Android /// ## Android
/// ///
/// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is /// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is
/// expected to closely correlate with the [`onResume`] lifecycle event but there may /// expected to closely correlate with the [`onResume`] lifecycle event but there may technically
/// technically be a discrepancy. /// be a discrepancy.
/// ///
/// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume() /// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
/// ///
@@ -110,8 +109,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// Emitted when the event loop is about to block and wait for new events. /// Emitted when the event loop is about to block and wait for new events.
/// ///
/// Most applications shouldn't need to hook into this event since there is no real relationship /// Most applications shouldn't need to hook into this event since there is no real relationship
/// between how often the event loop needs to wake up and the dispatching of any specific /// between how often the event loop needs to wake up and the dispatching of any specific events.
/// events.
/// ///
/// High frequency event sources, such as input devices could potentially lead to lots of wake /// High frequency event sources, such as input devices could potentially lead to lots of wake
/// ups and also lots of corresponding `AboutToWait` events. /// ups and also lots of corresponding `AboutToWait` events.
@@ -135,8 +133,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally /// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across /// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications /// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] /// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] events.
/// events.
/// ///
/// Also see [`Resumed`] notes. /// Also see [`Resumed`] notes.
/// ///
@@ -153,8 +150,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]). /// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
/// ///
/// After being `Suspended` on Android applications must drop all render surfaces before /// After being `Suspended` on Android applications must drop all render surfaces before
/// the event callback completes, which may be re-created when the application is next /// the event callback completes, which may be re-created when the application is next [`Resumed`].
/// [`Resumed`].
/// ///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
@@ -190,7 +186,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// Emitted when the event loop is being shut down. /// Emitted when the event loop is being shut down.
/// ///
/// This is irreversible - if this method is called, it is guaranteed that the event loop /// This is irreversible - if this method is called, it is guaranteed that the event loop
/// will exit right after. /// will exist right after.
fn exiting(&mut self, event_loop: &ActiveEventLoop) { fn exiting(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop; let _ = event_loop;
} }
@@ -201,17 +197,17 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// ///
/// ### Android /// ### Android
/// ///
/// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The /// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application
/// application must [release memory] or risk being killed. /// must [release memory] or risk being killed.
/// ///
/// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory() /// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory()
/// [release memory]: https://developer.android.com/topic/performance/memory#release /// [release memory]: https://developer.android.com/topic/performance/memory#release
/// ///
/// ### iOS /// ### iOS
/// ///
/// On iOS, the `MemoryWarning` event is emitted in response to an /// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`]
/// [`applicationDidReceiveMemoryWarning`] callback. The application must free as much /// callback. The application must free as much memory as possible or risk being terminated, see
/// memory as possible or risk being terminated, see [how to respond to memory warnings]. /// [how to respond to memory warnings].
/// ///
/// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni /// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni
/// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings /// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings
@@ -223,117 +219,3 @@ pub trait ApplicationHandler<T: 'static = ()> {
let _ = event_loop; let _ = event_loop;
} }
} }
impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for &mut A {
#[inline]
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
}
#[inline]
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
(**self).resumed(event_loop);
}
#[inline]
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
(**self).user_event(event_loop, event);
}
#[inline]
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, event);
}
#[inline]
fn device_event(
&mut self,
event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
}
#[inline]
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
(**self).about_to_wait(event_loop);
}
#[inline]
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
(**self).suspended(event_loop);
}
#[inline]
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
}
impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for Box<A> {
#[inline]
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
}
#[inline]
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
(**self).resumed(event_loop);
}
#[inline]
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
(**self).user_event(event_loop, event);
}
#[inline]
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, event);
}
#[inline]
fn device_event(
&mut self,
event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
}
#[inline]
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
(**self).about_to_wait(event_loop);
}
#[inline]
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
(**self).suspended(event_loop);
}
#[inline]
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
}

View File

@@ -1,77 +0,0 @@
//! # Changelog and migrations
//!
//! All notable changes to this project will be documented in this module,
//! along with migration instructions for larger changes.
// Put the current entry at the top of this page, for discoverability.
// See `.cargo/config.toml` for details about `unreleased_changelogs`.
#![cfg_attr(unreleased_changelogs, doc = include_str!("unreleased.md"))]
#![cfg_attr(not(unreleased_changelogs), doc = include_str!("v0.30.md"))]
#[doc = include_str!("v0.30.md")]
pub mod v0_30 {}
#[doc = include_str!("v0.29.md")]
pub mod v0_29 {}
#[doc = include_str!("v0.28.md")]
pub mod v0_28 {}
#[doc = include_str!("v0.27.md")]
pub mod v0_27 {}
#[doc = include_str!("v0.26.md")]
pub mod v0_26 {}
#[doc = include_str!("v0.25.md")]
pub mod v0_25 {}
#[doc = include_str!("v0.24.md")]
pub mod v0_24 {}
#[doc = include_str!("v0.23.md")]
pub mod v0_23 {}
#[doc = include_str!("v0.22.md")]
pub mod v0_22 {}
#[doc = include_str!("v0.21.md")]
pub mod v0_21 {}
#[doc = include_str!("v0.20.md")]
pub mod v0_20 {}
#[doc = include_str!("v0.19.md")]
pub mod v0_19 {}
#[doc = include_str!("v0.18.md")]
pub mod v0_18 {}
#[doc = include_str!("v0.17.md")]
pub mod v0_17 {}
#[doc = include_str!("v0.16.md")]
pub mod v0_16 {}
#[doc = include_str!("v0.15.md")]
pub mod v0_15 {}
#[doc = include_str!("v0.14.md")]
pub mod v0_14 {}
#[doc = include_str!("v0.13.md")]
pub mod v0_13 {}
#[doc = include_str!("v0.12.md")]
pub mod v0_12 {}
#[doc = include_str!("v0.11.md")]
pub mod v0_11 {}
#[doc = include_str!("v0.10.md")]
pub mod v0_10 {}
#[doc = include_str!("v0.9.md")]
pub mod v0_9 {}
#[doc = include_str!("v0.8.md")]
pub mod v0_8 {}

View File

@@ -1,41 +0,0 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
The sections should follow the order `Added`, `Changed`, `Deprecated`,
`Removed`, and `Fixed`.
Platform specific changed should be added to the end of the section and grouped
by platform name. Common API additions should have `, implemented` at the end
for platforms where the API was initially implemented. See the following example
on how to add them:
```md
### Added
- Add `Window::turbo()`, implemented on X11, Wayland, and Web.
- On X11, add `Window::some_rare_api`.
- On X11, add `Window::even_more_rare_api`.
- On Wayland, add `Window::common_api`.
- On Windows, add `Window::some_rare_api`.
```
When the change requires non-trivial amount of work for users to comply
with it, the migration guide should be added below the entry, like:
```md
- Deprecate `Window` creation outside of `EventLoop::run`
This was done to simply migration in the future. Consider the
following code:
// Code snippet.
To migrate it we should do X, Y, and then Z, for example:
// Code snippet.
```
The migration guide could reference other migration examples in the current
changelog entry.
## Unreleased

View File

@@ -1,13 +0,0 @@
## 0.10.1
_Yanked_
## 0.10.0
- Add support for `Touch` for emscripten backend.
- Added support for `DroppedFile`, `HoveredFile`, and `HoveredFileCancelled` to X11 backend.
- **Breaking:** `unix::WindowExt` no longer returns pointers for things that aren't actually pointers; `get_xlib_window` now returns `Option<std::os::raw::c_ulong>` and `get_xlib_screen_id` returns `Option<std::os::raw::c_int>`. Additionally, methods that previously returned `libc::c_void` have been changed to return `std::os::raw::c_void`, which are not interchangeable types, so users wanting the former will need to explicitly cast.
- Added `set_decorations` method to `Window` to allow decorations to be toggled after the window is built. Presently only implemented on X11.
- Raised the minimum supported version of Rust to 1.20 on MacOS due to usage of associated constants in new versions of cocoa and core-graphics.
- Added `modifiers` field to `MouseInput`, `MouseWheel`, and `CursorMoved` events to track the modifiers state (`ModifiersState`).
- Fixed the emscripten backend to return the size of the canvas instead of the size of the window.

View File

@@ -1,27 +0,0 @@
## 0.11.3
- Added `set_min_dimensions` and `set_max_dimensions` methods to `Window`, and implemented on Windows, X11, Wayland, and OSX.
- On X11, dropping a `Window` actually closes it now, and clicking the window's × button (or otherwise having the WM signal to close it) will result in the window closing.
- Added `WindowBuilderExt` methods for macos: `with_titlebar_transparent`,
`with_title_hidden`, `with_titlebar_buttons_hidden`,
`with_fullsize_content_view`.
- Mapped X11 numpad keycodes (arrows, Home, End, PageUp, PageDown, Insert and Delete) to corresponding virtual keycodes
## 0.11.2
- Impl `Hash`, `PartialEq`, and `Eq` for `events::ModifiersState`.
- Implement `MonitorId::get_hidpi_factor` for MacOS.
- Added method `os::macos::MonitorIdExt::get_nsscreen() -> *mut c_void` that gets a `NSScreen` object matching the monitor ID.
- Send `Awakened` event on Android when event loop is woken up.
## 0.11.1
- Fixed windows not receiving mouse events when click-dragging the mouse outside the client area of a window, on Windows platforms.
- Added method `os::android::EventsLoopExt:set_suspend_callback(Option<Box<Fn(bool) -> ()>>)` that allows glutin to register a callback when a suspend event happens
## 0.11.0
- Implement `MonitorId::get_dimensions` for Android.
- Added method `os::macos::WindowBuilderExt::with_movable_by_window_background(bool)` that allows to move a window without a titlebar - `with_decorations(false)`
- Implement `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Wayland.
- Added `Caret` as VirtualKeyCode and support OSX ^-Key with german input.

View File

@@ -1,8 +0,0 @@
## 0.12.0
- Added subclass to macos windows so they can be made resizable even with no decorations.
- Dead keys now work properly on X11, no longer resulting in a panic.
- On X11, input method creation first tries to use the value from the user's `XMODIFIERS` environment variable, so application developers should no longer need to manually call `XSetLocaleModifiers`. If that fails, fallbacks are tried, which should prevent input method initialization from ever outright failing.
- Fixed thread safety issues with input methods on X11.
- Add support for `Touch` for win32 backend.
- Fixed `Window::get_inner_size` and friends to return the size in pixels instead of points when using HIDPI displays on OSX.

View File

@@ -1,20 +0,0 @@
## 0.13.1
- Ensure necessary `x11-dl` version is used.
## 0.13.0
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for MacOS.
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Windows.
- On Windows, `WindowBuilder::with_fullscreen` no longer changing monitor display resolution.
- Overhauled X11 window geometry calculations. `get_position` and `set_position` are more universally accurate across different window managers, and `get_outer_size` actually works now.
- Fixed SIGSEGV/SIGILL crashes on macOS caused by stabilization of the `!` (never) type.
- Implement `WindowEvent::HiDPIFactorChanged` for macOS
- On X11, input methods now work completely out of the box, no longer requiring application developers to manually call `setlocale`. Additionally, when input methods are started, stopped, or restarted on the server end, it's correctly handled.
- Implemented `Refresh` event on Windows.
- Properly calculate the minimum and maximum window size on Windows, including window decorations.
- Map more `MouseCursor` variants to cursor icons on Windows.
- Corrected `get_position` on macOS to return outer frame position, not content area position.
- Corrected `set_position` on macOS to set outer frame position, not content area position.
- Added `get_inner_position` method to `Window`, which gets the position of the window's client area. This is implemented on all applicable platforms (all desktop platforms other than Wayland, where this isn't possible).
- **Breaking:** the `Closed` event has been replaced by `CloseRequested` and `Destroyed`. To migrate, you typically just need to replace all usages of `Closed` with `CloseRequested`; see example programs for more info. The exception is iOS, where `Closed` must be replaced by `Destroyed`.

View File

@@ -1,21 +0,0 @@
## 0.14.0
- Created the `Copy`, `Paste` and `Cut` `VirtualKeyCode`s and added support for them on X11 and Wayland
- Fix `.with_decorations(false)` in macOS
- On Mac, `NSWindow` and supporting objects might be alive long after they were `closed` which resulted in apps consuming more heap then needed. Mainly it was affecting multi window applications. Not expecting any user visible change of behaviour after the fix.
- Fix regression of Window platform extensions for macOS where `NSFullSizeContentViewWindowMask` was not being correctly applied to `.fullsize_content_view`.
- Corrected `get_position` on Windows to be relative to the screen rather than to the taskbar.
- Corrected `Moved` event on Windows to use position values equivalent to those returned by `get_position`. It previously supplied client area positions instead of window positions, and would additionally interpret negative values as being very large (around `u16::MAX`).
- Implemented `Moved` event on macOS.
- On X11, the `Moved` event correctly use window positions rather than client area positions. Additionally, a stray `Moved` that unconditionally accompanied `Resized` with the client area position relative to the parent has been eliminated; `Moved` is still received alongside `Resized`, but now only once and always correctly.
- On Windows, implemented all variants of `DeviceEvent` other than `Text`. Mouse `DeviceEvent`s are now received even if the window isn't in the foreground.
- `DeviceId` on Windows is no longer a unit struct, and now contains a `u32`. For `WindowEvent`s, this will always be 0, but on `DeviceEvent`s it will be the handle to that device. `DeviceIdExt::get_persistent_identifier` can be used to acquire a unique identifier for that device that persists across replugs/reboots/etc.
- Corrected `run_forever` on X11 to stop discarding `Awakened` events.
- Various safety and correctness improvements to the X11 backend internals.
- Fixed memory leak on X11 every time the mouse entered the window.
- On X11, drag and drop now works reliably in release mode.
- Added `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` to X11, allowing for more optional hints to be set.
- Rework of the wayland backend, migrating it to use [Smithay's Client Toolkit](https://github.com/Smithay/client-toolkit).
- Added `WindowBuilder::with_window_icon` and `Window::set_window_icon`, finally making it possible to set the window icon on Windows and X11. The `icon_loading` feature can be enabled to allow for icons to be easily loaded; see example program `window_icon.rs` for usage.
- Windows additionally has `WindowBuilderExt::with_taskbar_icon` and `WindowExt::set_taskbar_icon`.
- On Windows, fix panic when trying to call `set_fullscreen(None)` on a window that has not been fullscreened prior.

View File

@@ -1,42 +0,0 @@
## 0.15.1
- On X11, the `Moved` event is no longer sent when the window is resized without changing position.
- `MouseCursor` and `CursorState` now implement `Default`.
- `WindowBuilder::with_resizable` implemented for Windows, X11, Wayland, and macOS.
- `Window::set_resizable` implemented for Windows, X11, Wayland, and macOS.
- On X11, if the monitor's width or height in millimeters is reported as 0, the DPI is now 1.0 instead of +inf.
- On X11, the environment variable `WINIT_HIDPI_FACTOR` has been added for overriding DPI factor.
- On X11, enabling transparency no longer causes the window contents to flicker when resizing.
- On X11, `with_override_redirect` now actually enables override redirect.
- macOS now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead of `None` for both.
- On macOS, `VirtualKeyCode::RWin` and `VirtualKeyCode::LWin` are no longer switched.
- On macOS, windows without decorations can once again be resized.
- Fixed race conditions when creating an `EventsLoop` on X11, most commonly manifesting as `"[xcb] Unknown sequence number while processing queue"`.
- On macOS, `CursorMoved` and `MouseInput` events are only generated if they occurs within the window's client area.
- On macOS, resizing the window no longer generates a spurious `MouseInput` event.
## 0.15.0
- `Icon::to_cardinals` is no longer public, since it was never supposed to be.
- Wayland: improve diagnostics if initialization fails
- Fix some system event key doesn't work when focused, do not block keyevent forward to system on macOS
- On X11, the scroll wheel position is now correctly reset on i3 and other WMs that have the same quirk.
- On X11, `Window::get_current_monitor` now reliably returns the correct monitor.
- On X11, `Window::hidpi_factor` returns values from XRandR rather than the inaccurate values previously queried from the core protocol.
- On X11, the primary monitor is detected correctly even when using versions of XRandR less than 1.5.
- `MonitorId` now implements `Debug`.
- Fixed bug on macOS where using `with_decorations(false)` would cause `set_decorations(true)` to produce a transparent titlebar with no title.
- Implemented `MonitorId::get_position` on macOS.
- On macOS, `Window::get_current_monitor` now returns accurate values.
- Added `WindowBuilderExt::with_resize_increments` to macOS.
- **Breaking:** On X11, `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` now take `u32` values rather than `i32`.
- macOS keyboard handling has been overhauled, allowing for the use of dead keys, IME, etc. Right modifier keys are also no longer reported as being left.
- Added the `Window::set_ime_spot(x: i32, y: i32)` method, which is implemented on X11 and macOS.
- **Breaking**: `os::unix::WindowExt::send_xim_spot(x: i16, y: i16)` no longer exists. Switch to the new `Window::set_ime_spot(x: i32, y: i32)`, which has equivalent functionality.
- Fixed detection of `Pause` and `Scroll` keys on Windows.
- On Windows, alt-tabbing while the cursor is grabbed no longer makes it impossible to re-grab the cursor.
- On Windows, using `CursorState::Hide` when the cursor is grabbed now ungrabs the cursor first.
- Implemented `MouseCursor::NoneCursor` on Windows.
- Added `WindowBuilder::with_always_on_top` and `Window::set_always_on_top`. Implemented on Windows, macOS, and X11.
- On X11, `WindowBuilderExt` now has `with_class`, `with_override_redirect`, and `with_x11_window_type` to allow for more control over window creation. `WindowExt` additionally has `set_urgent`.
- More hints are set by default on X11, including `_NET_WM_PID` and `WM_CLIENT_MACHINE`. Note that prior to this, the `WM_CLASS` hint was automatically set to whatever value was passed to `with_title`. It's now set to the executable name to better conform to expectations and the specification; if this is undesirable, you must explicitly use `WindowBuilderExt::with_class`.

View File

@@ -1,32 +0,0 @@
## 0.16.2
- On Windows, non-resizable windows now have the maximization button disabled. This is consistent with behavior on macOS and popular X11 WMs.
- Corrected incorrect `unreachable!` usage when guessing the DPI factor with no detected monitors.
## 0.16.1
- Added logging through `log`. Logging will become more extensive over time.
- On X11 and Windows, the window's DPI factor is guessed before creating the window. This _greatly_ cuts back on unsightly auto-resizing that would occur immediately after window creation.
- Fixed X11 backend compilation for environments where `c_char` is unsigned.
## 0.16.0
- Windows additionally has `WindowBuilderExt::with_no_redirection_bitmap`.
- **Breaking:** Removed `VirtualKeyCode::LMenu` and `VirtualKeyCode::RMenu`; Windows now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead.
- On X11, exiting fullscreen no longer leaves the window in the monitor's top left corner.
- **Breaking:** `Window::hidpi_factor` has been renamed to `Window::get_hidpi_factor` for better consistency. `WindowEvent::HiDPIFactorChanged` has been renamed to `WindowEvent::HiDpiFactorChanged`. DPI factors are always represented as `f64` instead of `f32` now.
- The Windows backend is now DPI aware. `WindowEvent::HiDpiFactorChanged` is implemented, and `MonitorId::get_hidpi_factor` and `Window::hidpi_factor` return accurate values.
- Implemented `WindowEvent::HiDpiFactorChanged` on X11.
- On macOS, `Window::set_cursor_position` is now relative to the client area.
- On macOS, setting the maximum and minimum dimensions now applies to the client area dimensions rather than to the window dimensions.
- On iOS, `MonitorId::get_dimensions` has been implemented and both `MonitorId::get_hidpi_factor` and `Window::get_hidpi_factor` return accurate values.
- On Emscripten, `MonitorId::get_hidpi_factor` now returns the same value as `Window::get_hidpi_factor` (it previously would always return 1.0).
- **Breaking:** The entire API for sizes, positions, etc. has changed. In the majority of cases, winit produces and consumes positions and sizes as `LogicalPosition` and `LogicalSize`, respectively. The notable exception is `MonitorId` methods, which deal in `PhysicalPosition` and `PhysicalSize`. See the documentation for specifics and explanations of the types. Additionally, winit automatically conserves logical size when the DPI factor changes.
- **Breaking:** All deprecated methods have been removed. For `Window::platform_display` and `Window::platform_window`, switch to the appropriate platform-specific `WindowExt` methods. For `Window::get_inner_size_points` and `Window::get_inner_size_pixels`, use the `LogicalSize` returned by `Window::get_inner_size` and convert as needed.
- HiDPI support for Wayland.
- `EventsLoop::get_available_monitors` and `EventsLoop::get_primary_monitor` now have identical counterparts on `Window`, so this information can be acquired without an `EventsLoop` borrow.
- `AvailableMonitorsIter` now implements `Debug`.
- Fixed quirk on macOS where certain keys would generate characters at twice the normal rate when held down.
- On X11, all event loops now share the same `XConnection`.
- **Breaking:** `Window::set_cursor_state` and `CursorState` enum removed in favor of the more composable `Window::grab_cursor` and `Window::hide_cursor`. As a result, grabbing the cursor no longer automatically hides it; you must call both methods to retain the old behavior on Windows and macOS. `Cursor::NoneCursor` has been removed, as it's no longer useful.
- **Breaking:** `Window::set_cursor_position` now returns `Result<(), String>`, thus allowing for `Box<Error>` conversion via `?`.

View File

@@ -1,23 +0,0 @@
## 0.17.2
- On macOS, fix `<C-Tab>` so applications receive the event.
- On macOS, fix `<Cmd-{key}>` so applications receive the event.
- On Wayland, key press events will now be repeated.
## 0.17.1
- On X11, prevent a compilation failure in release mode for versions of Rust greater than or equal to 1.30.
- Fixed deadlock that broke fullscreen mode on Windows.
## 0.17.0
- Cocoa and core-graphics updates.
- Fixed thread-safety issues in several `Window` functions on Windows.
- On MacOS, the key state for modifiers key events is now properly set.
- On iOS, the view is now set correctly. This makes it possible to render things (instead of being stuck on a black screen), and touch events work again.
- Added NetBSD support.
- **Breaking:** On iOS, `UIView` is now the default root view. `WindowBuilderExt::with_root_view_class` can be used to set the root view objective-c class to `GLKView` (OpenGLES) or `MTKView` (Metal/MoltenVK).
- On iOS, the `UIApplication` is not started until `Window::new` is called.
- Fixed thread unsafety with cursor hiding on macOS.
- On iOS, fixed the size of the `JmpBuf` type used for `setjmp`/`longjmp` calls. Previously this was a buffer overflow on most architectures.
- On Windows, use cached window DPI instead of repeatedly querying the system. This fixes sporadic crashes on Windows 7.

View File

@@ -1,52 +0,0 @@
## 0.18.1
- On macOS, fix `Yen` (JIS) so applications receive the event.
- On X11 with a tiling WM, fixed high CPU usage when moving windows across monitors.
- On X11, fixed panic caused by dropping the window before running the event loop.
- on macOS, added `WindowExt::set_simple_fullscreen` which does not require a separate space
- Introduce `WindowBuilderExt::with_app_id` to allow setting the application ID on Wayland.
- On Windows, catch panics in event loop child thread and forward them to the parent thread. This prevents an invocation of undefined behavior due to unwinding into foreign code.
- On Windows, fix issue where resizing or moving window combined with grabbing the cursor would freeze program.
- On Windows, fix issue where resizing or moving window would eat `Awakened` events.
- On Windows, exiting fullscreen after entering fullscreen with disabled decorations no longer shrinks window.
- On X11, fixed a segfault when using virtual monitors with XRandR.
- Derive `Ord` and `PartialOrd` for `VirtualKeyCode` enum.
- On Windows, fix issue where hovering or dropping a non file item would create a panic.
- On Wayland, fix resizing and DPI calculation when a `wl_output` is removed without sending a `leave` event to the `wl_surface`, such as disconnecting a monitor from a laptop.
- On Wayland, DPI calculation is handled by smithay-client-toolkit.
- On X11, `WindowBuilder::with_min_dimensions` and `WindowBuilder::with_max_dimensions` now correctly account for DPI.
- Added support for generating dummy `DeviceId`s and `WindowId`s to better support unit testing.
- On macOS, fixed unsoundness in drag-and-drop that could result in drops being rejected.
- On macOS, implemented `WindowEvent::Refresh`.
- On macOS, all `MouseCursor` variants are now implemented and the cursor will no longer reset after unfocusing.
- Removed minimum supported Rust version guarantee.
## 0.18.0
- **Breaking:** `image` crate upgraded to 0.20. This is exposed as part of the `icon_loading` API.
- On Wayland, pointer events will now provide the current modifiers state.
- On Wayland, titles will now be displayed in the window header decoration.
- On Wayland, key repetition is now ended when keyboard loses focus.
- On Wayland, windows will now use more stylish and modern client side decorations.
- On Wayland, windows will use server-side decorations when available.
- **Breaking:** Added support for F16-F24 keys (variants were added to the `VirtualKeyCode` enum).
- Fixed graphical glitches when resizing on Wayland.
- On Windows, fix freezes when performing certain actions after a window resize has been triggered. Reintroduces some visual artifacts when resizing.
- Updated window manager hints under X11 to v1.5 of [Extended Window Manager Hints](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html#idm140200472629520).
- Added `WindowBuilderExt::with_gtk_theme_variant` to X11-specific `WindowBuilder` functions.
- Fixed UTF8 handling bug in X11 `set_title` function.
- On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first.
- On Windows, the `HoveredFile` and `HoveredFileCancelled` events are now implemented.
- On Windows, fix `Window::set_maximized`.
- On Windows 10, fix transparency (#260).
- On macOS, fix modifiers during key repeat.
- Implemented the `Debug` trait for `Window`, `EventsLoop`, `EventsLoopProxy` and `WindowBuilder`.
- On X11, now a `Resized` event will always be generated after a DPI change to ensure the window's logical size is consistent with the new DPI.
- Added further clarifications to the DPI docs.
- On Linux, if neither X11 nor Wayland manage to initialize, the corresponding panic now consists of a single line only.
- Add optional `serde` feature with implementations of `Serialize`/`Deserialize` for DPI types and various event types.
- Add `PartialEq`, `Eq`, and `Hash` implementations on public types that could have them but were missing them.
- On X11, drag-and-drop receiving an unsupported drop type can no longer cause the WM to freeze.
- Fix issue whereby the OpenGL context would not appear at startup on macOS Mojave (#1069).
- **Breaking:** Removed `From<NSApplicationActivationPolicy>` impl from `ActivationPolicy` on macOS.
- On macOS, the application can request the user's attention with `WindowExt::request_user_attention`.

View File

@@ -1,27 +0,0 @@
## 0.19.1
- On Wayland, added a `get_wayland_display` function to `EventsLoopExt`.
- On Windows, fix `CursorMoved(0, 0)` getting dispatched on window focus.
- On macOS, fix command key event left and right reverse.
- On FreeBSD, NetBSD, and OpenBSD, fix build of X11 backend.
- On Linux, the numpad's add, subtract and divide keys are now mapped to the `Add`, `Subtract` and `Divide` virtual key codes
- On macOS, the numpad's subtract key has been added to the `Subtract` mapping
- On Wayland, the numpad's home, end, page up and page down keys are now mapped to the `Home`, `End`, `PageUp` and `PageDown` virtual key codes
- On Windows, fix icon not showing up in corner of window.
- On X11, change DPI scaling factor behavior. First, winit tries to read it from "Xft.dpi" XResource, and uses DPI calculation from xrandr dimensions as fallback behavior.
## 0.19.0
- On X11, we will use the faster `XRRGetScreenResourcesCurrent` function instead of `XRRGetScreenResources` when available.
- On macOS, fix keycodes being incorrect when using a non-US keyboard layout.
- On Wayland, fix `with_title()` not setting the windows title
- On Wayland, add `set_wayland_theme()` to control client decoration color theme
- Added serde serialization to `os::unix::XWindowType`.
- **Breaking:** Remove the `icon_loading` feature and the associated `image` dependency.
- On X11, make event loop thread safe by replacing XNextEvent with select(2) and XCheckIfEvent
- On Windows, fix malformed function pointer typecast that could invoke undefined behavior.
- Refactored Windows state/flag-setting code.
- On Windows, hiding the cursor no longer hides the cursor for all Winit windows - just the one `hide_cursor` was called on.
- On Windows, cursor grabs used to get perpetually canceled when the grabbing window lost focus. Now, cursor grabs automatically get re-initialized when the window regains focus and the mouse moves over the client area.
- On Windows, only vertical mouse wheel events were handled. Now, horizontal mouse wheel events are also handled.
- On Windows, ignore the AltGr key when populating the `ModifiersState` type.

View File

@@ -1,200 +0,0 @@
## 0.20.0
- On X11, fix `ModifiersChanged` emitting incorrect modifier change events
- **Breaking**: Overhaul how Winit handles DPI:
- Window functions and events now return `PhysicalSize` instead of `LogicalSize`.
- Functions that take `Size` or `Position` types can now take either `Logical` or `Physical` types.
- `hidpi_factor` has been renamed to `scale_factor`.
- `HiDpiFactorChanged` has been renamed to `ScaleFactorChanged`, and lets you control how the OS
resizes the window in response to the change.
- On X11, deprecate `WINIT_HIDPI_FACTOR` environment variable in favor of `WINIT_X11_SCALE_FACTOR`.
- `Size` and `Position` types are now generic over their exact pixel type.
## 0.20.0-alpha6
- On macOS, fix `set_cursor_visible` hides cursor outside of window.
- On macOS, fix `CursorEntered` and `CursorLeft` events fired at old window size.
- On macOS, fix error when `set_fullscreen` is called during fullscreen transition.
- On all platforms except mobile and WASM, implement `Window::set_minimized`.
- On X11, fix `CursorEntered` event being generated for non-winit windows.
- On macOS, fix crash when starting maximized without decorations.
- On macOS, fix application not terminating on `run_return`.
- On Wayland, fix cursor icon updates on window borders when using CSD.
- On Wayland, under mutter(GNOME Wayland), fix CSD being behind the status bar, when starting window in maximized mode.
- On Windows, theme the title bar according to whether the system theme is "Light" or "Dark".
- Added `WindowEvent::ThemeChanged` variant to handle changes to the system theme. Currently only implemented on Windows.
- **Breaking**: Changes to the `RedrawRequested` event (#1041):
- `RedrawRequested` has been moved from `WindowEvent` to `Event`.
- `EventsCleared` has been renamed to `MainEventsCleared`.
- `RedrawRequested` is now issued only after `MainEventsCleared`.
- `RedrawEventsCleared` is issued after each set of `RedrawRequested` events.
- Implement synthetic window focus key events on Windows.
- **Breaking**: Change `ModifiersState` to a `bitflags` struct.
- On Windows, implement `VirtualKeyCode` translation for `LWin` and `RWin`.
- On Windows, fix closing the last opened window causing `DeviceEvent`s to stop getting emitted.
- On Windows, fix `Window::set_visible` not setting internal flags correctly. This resulted in some weird behavior.
- Add `DeviceEvent::ModifiersChanged`.
- Deprecate `modifiers` fields in other events in favor of `ModifiersChanged`.
- On X11, `WINIT_HIDPI_FACTOR` now dominates `Xft.dpi` when picking DPI factor for output.
- On X11, add special value `randr` for `WINIT_HIDPI_FACTOR` to make winit use self computed DPI factor instead of the one from `Xft.dpi`.
## 0.20.0-alpha5
- On macOS, fix application termination on `ControlFlow::Exit`
- On Windows, fix missing `ReceivedCharacter` events when Alt is held.
- On macOS, stop emitting private corporate characters in `ReceivedCharacter` events.
- On X11, fix misreporting DPI factor at startup.
- On X11, fix events not being reported when using `run_return`.
- On X11, fix key modifiers being incorrectly reported.
- On X11, fix window creation hanging when another window is fullscreen.
- On Windows, fix focusing unfocused windows when switching from fullscreen to windowed.
- On X11, fix reporting incorrect DPI factor when waking from suspend.
- Change `EventLoopClosed` to contain the original event.
- **Breaking**: Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`,
indicating that the event is generated by winit.
- On X11, generate synthetic key events for keys held when a window gains or loses focus.
- On X11, issue a `CursorMoved` event when a `Touch` event occurs,
as X11 implicitly moves the cursor for such events.
## 0.20.0-alpha4
- Add web support via the 'stdweb' or 'web-sys' features
- On Windows, implemented function to get HINSTANCE
- On macOS, implement `run_return`.
- On iOS, fix inverted parameter in `set_prefers_home_indicator_hidden`.
- On X11, performance is improved when rapidly calling `Window::set_cursor_icon`.
- On iOS, fix improper `msg_send` usage that was UB and/or would break if `!` is stabilized.
- On Windows, unset `maximized` when manually changing the window's position or size.
- On Windows, add touch pressure information for touch events.
- On macOS, differentiate between `CursorIcon::Grab` and `CursorIcon::Grabbing`.
- On Wayland, fix event processing sometimes stalling when using OpenGL with vsync.
- Officially remove the Emscripten backend.
- On Windows, fix handling of surrogate pairs when dispatching `ReceivedCharacter`.
- On macOS 10.15, fix freeze upon exiting exclusive fullscreen mode.
- On iOS, fix panic upon closing the app.
- On X11, allow setting multiple `XWindowType`s.
- On iOS, fix null window on initial `HiDpiFactorChanged` event.
- On Windows, fix fullscreen window shrinking upon getting restored to a normal window.
- On macOS, fix events not being emitted during modal loops, such as when windows are being resized
by the user.
- On Windows, fix hovering the mouse over the active window creating an endless stream of CursorMoved events.
- Always dispatch a `RedrawRequested` event after creating a new window.
- On X11, return dummy monitor data to avoid panicking when no monitors exist.
- On X11, prevent stealing input focus when creating a new window.
Only steal input focus when entering fullscreen mode.
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced
- On Wayland, add support for set_cursor_visible and set_cursor_grab.
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced.
- Removed `derivative` crate dependency.
- On Wayland, add support for set_cursor_icon.
- Use `impl Iterator<Item = MonitorHandle>` instead of `AvailableMonitorsIter` consistently.
- On macOS, fix fullscreen state being updated after entering fullscreen instead of before,
resulting in `Window::fullscreen` returning the old state in `Resized` events instead of
reflecting the new fullscreen state
- On X11, fix use-after-free during window creation
- On Windows, disable monitor change keyboard shortcut while in exclusive fullscreen.
- On Windows, ensure that changing a borderless fullscreen window's monitor via keyboard shortcuts keeps the window fullscreen on the new monitor.
- Prevent `EventLoop::new` and `EventLoop::with_user_event` from getting called outside the main thread.
- This is because some platforms cannot run the event loop outside the main thread. Preventing this
reduces the potential for cross-platform compatibility gotchyas.
- On Windows and Linux X11/Wayland, add platform-specific functions for creating an `EventLoop` outside the main thread.
- On Wayland, drop resize events identical to the current window size.
- On Windows, fix window rectangle not getting set correctly on high-DPI systems.
## 0.20.0-alpha3
- On macOS, drop the run closure on exit.
- On Windows, location of `WindowEvent::Touch` are window client coordinates instead of screen coordinates.
- On X11, fix delayed events after window redraw.
- On macOS, add `WindowBuilderExt::with_disallow_hidpi` to have the option to turn off best resolution openGL surface.
- On Windows, screen saver won't start if the window is in fullscreen mode.
- Change all occurrences of the `new_user_event` method to `with_user_event`.
- On macOS, the dock and the menu bar are now hidden in fullscreen mode.
- `Window::set_fullscreen` now takes `Option<Fullscreen>` where `Fullscreen`
consists of `Fullscreen::Exclusive(VideoMode)` and
`Fullscreen::Borderless(MonitorHandle)` variants.
- Adds support for exclusive fullscreen mode.
- On iOS, add support for hiding the home indicator.
- On iOS, add support for deferring system gestures.
- On iOS, fix a crash that occurred while acquiring a monitor's name.
- On iOS, fix armv7-apple-ios compile target.
- Removed the `T: Clone` requirement from the `Clone` impl of `EventLoopProxy<T>`.
- On iOS, disable overscan compensation for external displays (removes black
bars surrounding the image).
- On Linux, the functions `is_wayland`, `is_x11`, `xlib_xconnection` and `wayland_display` have been moved to a new `EventLoopWindowTargetExtUnix` trait.
- On iOS, add `set_prefers_status_bar_hidden` extension function instead of
hijacking `set_decorations` for this purpose.
- On macOS and iOS, corrected the auto trait impls of `EventLoopProxy`.
- On iOS, add touch pressure information for touch events.
- Implement `raw_window_handle::HasRawWindowHandle` for `Window` type on all supported platforms.
- On macOS, fix the signature of `-[NSView drawRect:]`.
- On iOS, fix the behavior of `ControlFlow::Poll`. It wasn't polling if that was the only mode ever used by the application.
- On iOS, fix DPI sent out by views on creation was `0.0` - now it gives a reasonable number.
- On iOS, RedrawRequested now works for gl/metal backed views.
- On iOS, RedrawRequested is generally ordered after EventsCleared.
## 0.20.0-alpha2
- On X11, non-resizable windows now have maximize explicitly disabled.
- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile`
and `WindowEvent::HoveredFile`.
- On Mac, implement `DeviceEvent::Button`.
- Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`.
- On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor.
- Revert the use of invisible surfaces in Wayland, which introduced graphical glitches with OpenGL (#835)
- On X11, implement `_NET_WM_PING` to allow desktop environment to kill unresponsive programs.
- On Windows, when a window is initially invisible, it won't take focus from the existing visible windows.
- On Windows, fix multiple calls to `request_redraw` during `EventsCleared` sending multiple `RedrawRequested events.`
- On Windows, fix edge case where `RedrawRequested` could be dispatched before input events in event loop iteration.
- On Windows, fix timing issue that could cause events to be improperly dispatched after `RedrawRequested` but before `EventsCleared`.
- On macOS, drop unused Metal dependency.
- On Windows, fix the trail effect happening on transparent decorated windows. Borderless (or un-decorated) windows were not affected.
- On Windows, fix `with_maximized` not properly setting window size to entire window.
- On macOS, change `WindowExtMacOS::request_user_attention()` to take an `enum` instead of a `bool`.
## 0.20.0-alpha1
- Changes below are considered **breaking**.
- Change all occurrences of `EventsLoop` to `EventLoop`.
- Previously flat API is now exposed through `event`, `event_loop`, `monitor`, and `window` modules.
- `os` module changes:
- Renamed to `platform`.
- All traits now have platform-specific suffixes.
- Exposes new `desktop` module on Windows, Mac, and Linux.
- Changes to event loop types:
- `EventLoopProxy::wakeup` has been removed in favor of `send_event`.
- **Major:** New `run` method drives winit event loop.
- Returns `!` to ensure API behaves identically across all supported platforms.
- This allows `emscripten` implementation to work without lying about the API.
- `ControlFlow`'s variants have been replaced with `Wait`, `WaitUntil(Instant)`, `Poll`, and `Exit`.
- Is read after `EventsCleared` is processed.
- `Wait` waits until new events are available.
- `WaitUntil` waits until either new events are available or the provided time has been reached.
- `Poll` instantly resumes the event loop.
- `Exit` aborts the event loop.
- Takes a closure that implements `'static + FnMut(Event<T>, &EventLoop<T>, &mut ControlFlow)`.
- `&EventLoop<T>` is provided to allow new `Window`s to be created.
- **Major:** `platform::desktop` module exposes `EventLoopExtDesktop` trait with `run_return` method.
- Behaves identically to `run`, but returns control flow to the calling context and can take non-`'static` closures.
- `EventLoop`'s `poll_events` and `run_forever` methods have been removed in favor of `run` and `run_return`.
- Changes to events:
- Remove `Event::Awakened` in favor of `Event::UserEvent(T)`.
- Can be sent with `EventLoopProxy::send_event`.
- Rename `WindowEvent::Refresh` to `WindowEvent::RedrawRequested`.
- `RedrawRequested` can be sent by the user with the `Window::request_redraw` method.
- `EventLoop`, `EventLoopProxy`, and `Event` are now generic over `T`, for use in `UserEvent`.
- **Major:** Add `NewEvents(StartCause)`, `EventsCleared`, and `LoopDestroyed` variants to `Event`.
- `NewEvents` is emitted when new events are ready to be processed by event loop.
- `StartCause` describes why new events are available, with `ResumeTimeReached`, `Poll`, `WaitCancelled`, and `Init` (sent once at start of loop).
- `EventsCleared` is emitted when all available events have been processed.
- Can be used to perform logic that depends on all events being processed (e.g. an iteration of a game loop).
- `LoopDestroyed` is emitted when the `run` or `run_return` method is about to exit.
- Rename `MonitorId` to `MonitorHandle`.
- Removed `serde` implementations from `ControlFlow`.
- Rename several functions to improve both internal consistency and compliance with Rust API guidelines.
- Remove `WindowBuilder::multitouch` field, since it was only implemented on a few platforms. Multitouch is always enabled now.
- **Breaking:** On macOS, change `ns` identifiers to use snake_case for consistency with iOS's `ui` identifiers.
- Add `MonitorHandle::video_modes` method for retrieving supported video modes for the given monitor.
- On Wayland, the window now exists even if nothing has been drawn.
- On Windows, fix initial dimensions of a fullscreen window.
- On Windows, Fix transparent borderless windows rendering wrong.

View File

@@ -1,16 +0,0 @@
## 0.21.0
- On Windows, fixed "error: linking with `link.exe` failed: exit code: 1120" error on older versions of windows.
- On macOS, fix set_minimized(true) works only with decorations.
- On macOS, add `hide_application` to `EventLoopWindowTarget` via a new `EventLoopWindowTargetExtMacOS` trait. `hide_application` will hide the entire application by calling `-[NSApplication hide: nil]`.
- On macOS, fix not sending ReceivedCharacter event for specific keys combinations.
- On macOS, fix `CursorMoved` event reporting the cursor position using logical coordinates.
- On macOS, fix issue where unbundled applications would sometimes open without being focused.
- On macOS, fix `run_return` does not return unless it receives a message.
- On Windows, fix bug where `RedrawRequested` would only get emitted every other iteration of the event loop.
- On X11, fix deadlock on window state when handling certain window events.
- `WindowBuilder` now implements `Default`.
- **Breaking:** `WindowEvent::CursorMoved` changed to `f64` units, preserving high-precision data supplied by most backends
- On Wayland, fix coordinates in mouse events when scale factor isn't 1
- On Web, add the ability to provide a custom canvas
- **Breaking:** On Wayland, the `WaylandTheme` struct has been replaced with a `Theme` trait, allowing for extra configuration

View File

@@ -1,37 +0,0 @@
## 0.22.2
- Added Clone implementation for 'static events.
- On Windows, fix window intermittently hanging when `ControlFlow` was set to `Poll`.
- On Windows, fix `WindowBuilder::with_maximized` being ignored.
- On Android, minimal platform support.
- On iOS, touch positions are now properly converted to physical pixels.
- On macOS, updated core-* dependencies and cocoa
## 0.22.1
- On X11, fix `ResumeTimeReached` being fired too early.
- On Web, replaced zero timeout for `ControlFlow::Poll` with `requestAnimationFrame`
- On Web, fix a possible panic during event handling
- On macOS, fix `EventLoopProxy` leaking memory for every instance.
## 0.22.0
- On Windows, fix minor timing issue in wait_until_time_or_msg
- On Windows, rework handling of request_redraw() to address panics.
- On macOS, fix `set_simple_screen` to remember frame excluding title bar.
- On Wayland, fix coordinates in touch events when scale factor isn't 1.
- On Wayland, fix color from `close_button_icon_color` not applying.
- Ignore locale if unsupported by X11 backend
- On Wayland, Add HiDPI cursor support
- On Web, add the ability to query "Light" or "Dark" system theme send `ThemeChanged` on change.
- Fix `Event::to_static` returning `None` for user events.
- On Wayland, Hide CSD for fullscreen windows.
- On Windows, ignore spurious mouse move messages.
- **Breaking:** Move `ModifiersChanged` variant from `DeviceEvent` to `WindowEvent`.
- On Windows, add `IconExtWindows` trait which exposes creating an `Icon` from an external file or embedded resource
- Add `BadIcon::OsError` variant for when OS icon functionality fails
- On Windows, fix crash at startup on systems that do not properly support Windows' Dark Mode
- Revert On macOS, fix not sending ReceivedCharacter event for specific keys combinations.
- on macOS, fix incorrect ReceivedCharacter events for some key combinations.
- **Breaking:** Use `i32` instead of `u32` for position type in `WindowEvent::Moved`.
- On macOS, a mouse motion event is now generated before every mouse click.

View File

@@ -1,65 +0,0 @@
## 0.23.0
- On iOS, fixed support for the "Debug View Hierarchy" feature in Xcode.
- On all platforms, `available_monitors` and `primary_monitor` are now on `EventLoopWindowTarget` rather than `EventLoop` to list monitors event in the event loop.
- On Unix, X11 and Wayland are now optional features (enabled by default)
- On X11, fix deadlock when calling `set_fullscreen_inner`.
- On Web, prevent the webpage from scrolling when the user is focused on a winit canvas
- On Web, calling `window.set_cursor_icon` no longer breaks HiDPI scaling
- On Windows, drag and drop is now optional (enabled by default) and can be disabled with `WindowBuilderExtWindows::with_drag_and_drop(false)`.
- On Wayland, fix deadlock when calling to `set_inner_size` from a callback.
- On macOS, add `hide__other_applications` to `EventLoopWindowTarget` via existing `EventLoopWindowTargetExtMacOS` trait. `hide_other_applications` will hide other applications by calling `-[NSApplication hideOtherApplications: nil]`.
- On android added support for `run_return`.
- On MacOS, Fixed fullscreen and dialog support for `run_return`.
- On Windows, fix bug where we'd try to emit `MainEventsCleared` events during nested win32 event loops.
- On Web, use mouse events if pointer events aren't supported. This affects Safari.
- On Windows, `set_ime_position` is now a no-op instead of a runtime crash.
- On Android, `set_fullscreen` is now a no-op instead of a runtime crash.
- On iOS and Android, `set_inner_size` is now a no-op instead of a runtime crash.
- On Android, fix `ControlFlow::Poll` not polling the Android event queue.
- On macOS, add `NSWindow.hasShadow` support.
- On Web, fix vertical mouse wheel scrolling being inverted.
- On Web, implement mouse capturing for click-dragging out of the canvas.
- On Web, fix `ControlFlow::Exit` not properly handled.
- On Web (web-sys only), send `WindowEvent::ScaleFactorChanged` event when `window.devicePixelRatio` is changed.
- **Breaking:** On Web, `set_cursor_position` and `set_cursor_grab` will now always return an error.
- **Breaking:** `PixelDelta` scroll events now return a `PhysicalPosition`.
- On NetBSD, fixed crash due to incorrect detection of the main thread.
- **Breaking:** On X11, `-` key is mapped to the `Minus` virtual key code, instead of `Subtract`.
- On macOS, fix inverted horizontal scroll.
- **Breaking:** `current_monitor` now returns `Option<MonitorHandle>`.
- **Breaking:** `primary_monitor` now returns `Option<MonitorHandle>`.
- On macOS, updated core-* dependencies and cocoa.
- Bump `parking_lot` to 0.11
- On Android, bump `ndk`, `ndk-sys` and `ndk-glue` to 0.2. Checkout the new ndk-glue main proc attribute.
- On iOS, fixed starting the app in landscape where the view still had portrait dimensions.
- Deprecate the stdweb backend, to be removed in a future release
- **Breaking:** Prefixed virtual key codes `Add`, `Multiply`, `Divide`, `Decimal`, and `Subtract` with `Numpad`.
- Added `Asterisk` and `Plus` virtual key codes.
- On Web (web-sys only), the `Event::LoopDestroyed` event is correctly emitted when leaving the page.
- On Web, the `WindowEvent::Destroyed` event now gets emitted when a `Window` is dropped.
- On Web (web-sys only), the event listeners are now removed when a `Window` is dropped or when the event loop is destroyed.
- On Web, the event handler closure passed to `EventLoop::run` now gets dropped after the event loop is destroyed.
- **Breaking:** On Web, the canvas element associated to a `Window` is no longer removed from the DOM when the `Window` is dropped.
- On Web, `WindowEvent::Resized` is now emitted when `Window::set_inner_size` is called.
- **Breaking:** `Fullscreen` enum now uses `Borderless(Option<MonitorHandle>)` instead of `Borderless(MonitorHandle)` to allow picking the current monitor.
- On MacOS, fix `WindowEvent::Moved` ignoring the scale factor.
- On Wayland, add missing virtual keycodes.
- On Wayland, implement proper `set_cursor_grab`.
- On Wayland, the cursor will use similar icons if the requested one isn't available.
- On Wayland, right clicking on client side decorations will request application menu.
- On Wayland, fix tracking of window size after state changes.
- On Wayland, fix client side decorations not being hidden properly in fullscreen.
- On Wayland, fix incorrect size event when entering fullscreen with client side decorations.
- On Wayland, fix `resizable` attribute not being applied properly on startup.
- On Wayland, fix disabled repeat rate not being handled.
- On Wayland, fix decoration buttons not working after tty switch.
- On Wayland, fix scaling not being applied on output re-enable.
- On Wayland, fix crash when `XCURSOR_SIZE` is `0`.
- On Wayland, fix pointer getting created in some cases without pointer capability.
- On Wayland, on kwin, fix space between window and decorations on startup.
- **Breaking:** On Wayland, `Theme` trait was reworked.
- On Wayland, disable maximize button for non-resizable window.
- On Wayland, added support for `set_ime_position`.
- On Wayland, fix crash on startup since GNOME 3.37.90.
- On X11, fix incorrect modifiers state on startup.

View File

@@ -1,28 +0,0 @@
## 0.24.0
- On Windows, fix applications not exiting gracefully due to thread_event_target_callback accessing corrupted memory.
- On Windows, implement `Window::set_ime_position`.
- **Breaking:** On Windows, Renamed `WindowBuilderExtWindows`'s `is_dark_mode` to `theme`.
- **Breaking:** On Windows, renamed `WindowBuilderExtWindows::is_dark_mode` to `theme`.
- On Windows, add `WindowBuilderExtWindows::with_theme` to set a preferred theme.
- On Windows, fix bug causing message boxes to appear delayed.
- On Android, calling `WindowEvent::Focused` now works properly instead of always returning false.
- On Windows, fix Alt-Tab behaviour by removing borderless fullscreen "always on top" flag.
- On Windows, fix bug preventing windows with transparency enabled from having fully-opaque regions.
- **Breaking:** On Windows, include prefix byte in scancodes.
- On Wayland, fix window not being resizeable when using `WindowBuilder::with_min_inner_size`.
- On Unix, fix cross-compiling to wasm32 without enabling X11 or Wayland.
- On Windows, fix use-after-free crash during window destruction.
- On Web, fix `WindowEvent::ReceivedCharacter` never being sent on key input.
- On macOS, fix compilation when targeting aarch64.
- On X11, fix `Window::request_redraw` not waking the event loop.
- On Wayland, the keypad arrow keys are now recognized.
- **Breaking** Rename `desktop::EventLoopExtDesktop` to `run_return::EventLoopExtRunReturn`.
- Added `request_user_attention` method to `Window`.
- **Breaking:** On macOS, removed `WindowExt::request_user_attention`, use `Window::request_user_attention`.
- **Breaking:** On X11, removed `WindowExt::set_urgent`, use `Window::request_user_attention`.
- On Wayland, default font size in CSD increased from 11 to 17.
- On Windows, fix bug causing message boxes to appear delayed.
- On Android, support multi-touch.
- On Wayland, extra mouse buttons are not dropped anymore.
- **Breaking**: `MouseButton::Other` now uses `u16`.

View File

@@ -1,31 +0,0 @@
## 0.25.0
- **Breaking:** On macOS, replace `WindowBuilderExtMacOS::with_activation_policy` with `EventLoopExtMacOS::set_activation_policy`
- On macOS, wait with activating the application until the application has initialized.
- On macOS, fix creating new windows when the application has a main menu.
- On Windows, fix fractional deltas for mouse wheel device events.
- On macOS, fix segmentation fault after dropping the main window.
- On Android, `InputEvent::KeyEvent` is partially implemented providing the key scancode.
- Added `is_maximized` method to `Window`.
- On Windows, fix bug where clicking the decoration bar would make the cursor blink.
- On Windows, fix bug causing newly created windows to erroneously display the "wait" (spinning) cursor.
- On macOS, wake up the event loop immediately when a redraw is requested.
- On Windows, change the default window size (1024x768) to match the default on other desktop platforms (800x600).
- On Windows, fix bug causing mouse capture to not be released.
- On Windows, fix fullscreen not preserving minimized/maximized state.
- On Android, unimplemented events are marked as unhandled on the native event loop.
- On Windows, added `WindowBuilderExtWindows::with_menu` to set a custom menu at window creation time.
- On Android, bump `ndk` and `ndk-glue` to 0.3: use predefined constants for event `ident`.
- On macOS, fix objects captured by the event loop closure not being dropped on panic.
- On Windows, fixed `WindowEvent::ThemeChanged` not properly firing and fixed `Window::theme` returning the wrong theme.
- On Web, added support for `DeviceEvent::MouseMotion` to listen for relative mouse movements.
- Added `WindowBuilder::with_position` to allow setting the position of a `Window` on creation. Supported on Windows, macOS and X11.
- Added `Window::drag_window`. Implemented on Windows, macOS, X11 and Wayland.
- On X11, bump `mio` to 0.7.
- On Windows, added `WindowBuilderExtWindows::with_owner_window` to allow creating popup windows.
- On Windows, added `WindowExtWindows::set_enable` to allow creating modal popup windows.
- On macOS, emit `RedrawRequested` events immediately while the window is being resized.
- Implement `Default`, `Hash`, and `Eq` for `LogicalPosition`, `PhysicalPosition`, `LogicalSize`, and `PhysicalSize`.
- On macOS, initialize the Menu Bar with minimal defaults. (Can be prevented using `enable_default_menu_creation`)
- On macOS, change the default behavior for first click when the window was unfocused. Now the window becomes focused and then emits a `MouseInput` event on a "first mouse click".
- Implement mint (math interoperability standard types) conversions (under feature flag `mint`).

View File

@@ -1,36 +0,0 @@
## 0.26.1
- Fix linking to the `ColorSync` framework on macOS 10.7, and in newer Rust versions.
- On Web, implement cursor grabbing through the pointer lock API.
- On X11, add mappings for numpad comma, numpad enter, numlock and pause.
- On macOS, fix Pinyin IME input by reverting a change that intended to improve IME.
- On Windows, fix a crash with transparent windows on Windows 11.
## 0.26.0
- Update `raw-window-handle` to `v0.4`. This is _not_ a breaking change, we still implement `HasRawWindowHandle` from `v0.3`, see [rust-windowing/raw-window-handle#74](https://github.com/rust-windowing/raw-window-handle/pull/74). Note that you might have to run `cargo update -p raw-window-handle` after upgrading.
- On X11, bump `mio` to 0.8.
- On Android, fixed `WindowExtAndroid::config` initially returning an empty `Configuration`.
- On Android, fixed `Window::scale_factor` and `MonitorHandle::scale_factor` initially always returning 1.0.
- On X11, select an appropriate visual for transparency if is requested
- On Wayland and X11, fix diagonal window resize cursor orientation.
- On macOS, drop the event callback before exiting.
- On Android, implement `Window::request_redraw`
- **Breaking:** On Web, remove the `stdweb` backend.
- Added `Window::focus_window`to bring the window to the front and set input focus.
- On Wayland and X11, implement `is_maximized` method on `Window`.
- On Windows, prevent ghost window from showing up in the taskbar after either several hours of use or restarting `explorer.exe`.
- On macOS, fix issue where `ReceivedCharacter` was not being emitted during some key repeat events.
- On Wayland, load cursor icons `hand2` and `hand1` for `CursorIcon::Hand`.
- **Breaking:** On Wayland, Theme trait and its support types are dropped.
- On Wayland, bump `smithay-client-toolkit` to 0.15.1.
- On Wayland, implement `request_user_attention` with `xdg_activation_v1`.
- On X11, emit missing `WindowEvent::ScaleFactorChanged` when the only monitor gets reconnected.
- On X11, if RANDR based scale factor is higher than 20 reset it to 1
- On Wayland, add an enabled-by-default feature called `wayland-dlopen` so users can opt out of using `dlopen` to load system libraries.
- **Breaking:** On Android, bump `ndk` and `ndk-glue` to 0.5.
- On Windows, increase wait timer resolution for more accurate timing when using `WaitUntil`.
- On macOS, fix native file dialogs hanging the event loop.
- On Wayland, implement a workaround for wrong configure size when using `xdg_decoration` in `kwin_wayland`
- On macOS, fix an issue that prevented the menu bar from showing in borderless fullscreen mode.
- On X11, EINTR while polling for events no longer causes a panic. Instead it will be treated as a spurious wakeup.

View File

@@ -1,107 +0,0 @@
## 0.27.5
- On Wayland, fix byte offset in `Ime::Preedit` pointing to invalid bytes.
## 0.27.4
- On Windows, emit `ReceivedCharacter` events on system keybindings.
- On Windows, fixed focus event emission on minimize.
- On X11, fixed IME crashing during reload.
## 0.27.3
- On Windows, added `WindowExtWindows::set_undecorated_shadow` and `WindowBuilderExtWindows::with_undecorated_shadow` to draw the drop shadow behind a borderless window.
- On Windows, fixed default window features (ie snap, animations, shake, etc.) when decorations are disabled.
- On Windows, fixed ALT+Space shortcut to open window menu.
- On Wayland, fixed `Ime::Preedit` not being sent on IME reset.
- Fixed unbound version specified for `raw-window-handle` leading to compilation failures.
- Empty `Ime::Preedit` event will be sent before `Ime::Commit` to help clearing preedit.
- On X11, fixed IME context picking by querying for supported styles beforehand.
## 0.27.2
- On macOS, fixed touch phase reporting when scrolling.
- On X11, fix min, max and resize increment hints not persisting for resizable windows (e.g. on DPI change).
- On Windows, respect min/max inner sizes when creating the window.
- For backwards compatibility, `Window` now (additionally) implements the old version (`0.4`) of the `HasRawWindowHandle` trait
- On Windows, added support for `EventLoopWindowTarget::set_device_event_filter`.
- On Wayland, fix user requested `WindowEvent::RedrawRequested` being delayed by a frame.
## 0.27.1
- The minimum supported Rust version was lowered to `1.57.0` and now explicitly tested.
- On X11, fix crash on start due to inability to create an IME context without any preedit.
## 0.27.0
- On Windows, fix hiding a maximized window.
- On Android, `ndk-glue`'s `NativeWindow` lock is now held between `Event::Resumed` and `Event::Suspended`.
- On Web, added `EventLoopExtWebSys` with a `spawn` method to start the event loop without throwing an exception.
- Added `WindowEvent::Occluded(bool)`, currently implemented on macOS and X11.
- On X11, fix events for caps lock key not being sent
- Build docs on `docs.rs` for iOS and Android as well.
- **Breaking:** Removed the `WindowAttributes` struct, since all its functionality is accessible from `WindowBuilder`.
- Added `WindowBuilder::transparent` getter to check if the user set `transparent` attribute.
- On macOS, Fix emitting `Event::LoopDestroyed` on CMD+Q.
- On macOS, fixed an issue where having multiple windows would prevent run_return from ever returning.
- On Wayland, fix bug where the cursor wouldn't hide in GNOME.
- On macOS, Windows, and Wayland, add `set_cursor_hittest` to let the window ignore mouse events.
- On Windows, added `WindowExtWindows::set_skip_taskbar` and `WindowBuilderExtWindows::with_skip_taskbar`.
- On Windows, added `EventLoopBuilderExtWindows::with_msg_hook`.
- On Windows, remove internally unique DC per window.
- On macOS, remove the need to call `set_ime_position` after moving the window.
- Added `Window::is_visible`.
- Added `Window::is_resizable`.
- Added `Window::is_decorated`.
- On X11, fix for repeated event loop iteration when `ControlFlow` was `Wait`
- On X11, fix scale factor calculation when the only monitor is reconnected
- On Wayland, report unaccelerated mouse deltas in `DeviceEvent::MouseMotion`.
- On Web, a focused event is manually generated when a click occurs to emulate behaviour of other backends.
- **Breaking:** Bump `ndk` version to 0.6, ndk-sys to `v0.3`, `ndk-glue` to `0.6`.
- Remove no longer needed `WINIT_LINK_COLORSYNC` environment variable.
- **Breaking:** Rename the `Exit` variant of `ControlFlow` to `ExitWithCode`, which holds a value to control the exit code after running. Add an `Exit` constant which aliases to `ExitWithCode(0)` instead to avoid major breakage. This shouldn't affect most existing programs.
- Add `EventLoopBuilder`, which allows you to create and tweak the settings of an event loop before creating it.
- Deprecated `EventLoop::with_user_event`; use `EventLoopBuilder::with_user_event` instead.
- **Breaking:** Replaced `EventLoopExtMacOS` with `EventLoopBuilderExtMacOS` (which also has renamed methods).
- **Breaking:** Replaced `EventLoopExtWindows` with `EventLoopBuilderExtWindows` (which also has renamed methods).
- **Breaking:** Replaced `EventLoopExtUnix` with `EventLoopBuilderExtUnix` (which also has renamed methods).
- **Breaking:** The platform specific extensions for Windows `winit::platform::windows` have changed. All `HANDLE`-like types e.g. `HWND` and `HMENU` were converted from winapi types or `*mut c_void` to `isize`. This was done to be consistent with the type definitions in windows-sys and to not expose internal dependencies.
- The internal bindings to the [Windows API](https://docs.microsoft.com/en-us/windows/) were changed from the unofficial [winapi](https://github.com/retep998/winapi-rs) bindings to the official Microsoft [windows-sys](https://github.com/microsoft/windows-rs) bindings.
- On Wayland, fix polling during consecutive `EventLoop::run_return` invocations.
- On Windows, fix race issue creating fullscreen windows with `WindowBuilder::with_fullscreen`
- On Android, `virtual_keycode` for `KeyboardInput` events is now filled in where a suitable match is found.
- Added helper methods on `ControlFlow` to set its value.
- On Wayland, fix `TouchPhase::Ended` always reporting the location of the first touch down, unless the compositor
sent a cancel or frame event.
- On iOS, send `RedrawEventsCleared` even if there are no redraw events, consistent with other platforms.
- **Breaking:** Replaced `Window::with_app_id` and `Window::with_class` with `Window::with_name` on `WindowBuilderExtUnix`.
- On Wayland, fallback CSD was replaced with proper one:
- `WindowBuilderExtUnix::with_wayland_csd_theme` to set color theme in builder.
- `WindowExtUnix::wayland_set_csd_theme` to set color theme when creating a window.
- `WINIT_WAYLAND_CSD_THEME` env variable was added, it can be used to set "dark"/"light" theme in apps that don't expose theme setting.
- `wayland-csd-adwaita` feature that enables proper CSD with title rendering using FreeType system library.
- `wayland-csd-adwaita-notitle` feature that enables CSD but without title rendering.
- On Wayland and X11, fix window not resizing with `Window::set_inner_size` after calling `Window:set_resizable(false)`.
- On Windows, fix wrong fullscreen monitors being recognized when handling WM_WINDOWPOSCHANGING messages
- **Breaking:** Added new `WindowEvent::Ime` supported on desktop platforms.
- Added `Window::set_ime_allowed` supported on desktop platforms.
- **Breaking:** IME input on desktop platforms won't be received unless it's explicitly allowed via `Window::set_ime_allowed` and new `WindowEvent::Ime` events are handled.
- On macOS, `WindowEvent::Resized` is now emitted in `frameDidChange` instead of `windowDidResize`.
- **Breaking:** On X11, device events are now ignored for unfocused windows by default, use `EventLoopWindowTarget::set_device_event_filter` to set the filter level.
- Implemented `Default` on `EventLoop<()>`.
- Implemented `Eq` for `Fullscreen`, `Theme`, and `UserAttentionType`.
- **Breaking:** `Window::set_cursor_grab` now accepts `CursorGrabMode` to control grabbing behavior.
- On Wayland, add support for `Window::set_cursor_position`.
- Fix on macOS `WindowBuilder::with_disallow_hidpi`, setting true or false by the user no matter the SO default value.
- `EventLoopBuilder::build` will now panic when the `EventLoop` is being created more than once.
- Added `From<u64>` for `WindowId` and `From<WindowId>` for `u64`.
- Added `MonitorHandle::refresh_rate_millihertz` to get monitor's refresh rate.
- **Breaking**, Replaced `VideoMode::refresh_rate` with `VideoMode::refresh_rate_millihertz` providing better precision.
- On Web, add `with_prevent_default` and `with_focusable` to `WindowBuilderExtWebSys` to control whether events should be propagated.
- On Windows, fix focus events being sent to inactive windows.
- **Breaking**, update `raw-window-handle` to `v0.5` and implement `HasRawDisplayHandle` for `Window` and `EventLoopWindowTarget`.
- On X11, add function `register_xlib_error_hook` into `winit::platform::unix` to subscribe for errors coming from Xlib.
- On Android, upgrade `ndk` and `ndk-glue` dependencies to the recently released `0.7.0`.
- All platforms can now be relied on to emit a `Resumed` event. Applications are recommended to lazily initialize graphics state and windows on first resume for portability.
- **Breaking:**: Reverse horizontal scrolling sign in `MouseScrollDelta` to match the direction of vertical scrolling. A positive X value now means moving the content to the right. The meaning of vertical scrolling stays the same: a positive Y value means moving the content down.
- On MacOS, fix deadlock when calling `set_maximized` from event loop.

View File

@@ -1,100 +0,0 @@
## 0.28.7
- Fix window size sometimes being invalid when resizing on macOS 14 Sonoma.
## 0.28.6
- On macOS, fixed memory leak when getting monitor handle.
- On macOS, fix `Backspace` being emitted when clearing preedit with it.
## 0.28.5
- On macOS, fix `key_up` being ignored when `Ime` is disabled.
## 0.28.4
- On macOS, fix empty marked text blocking regular input.
- On macOS, fix potential panic when getting refresh rate.
- On macOS, fix crash when calling `Window::set_ime_position` from another thread.
## 0.28.3
- Fix macOS memory leaks.
## 0.28.2
- Implement `HasRawDisplayHandle` for `EventLoop`.
- On macOS, set resize increments only for live resizes.
- On Wayland, fix rare crash on DPI change
- Web: Added support for `Window::theme`.
- On Wayland, fix rounding issues when doing resize.
- On macOS, fix wrong focused state on startup.
- On Windows, fix crash on setting taskbar when using Visual Studio debugger.
- On macOS, resize simple fullscreen windows on windowDidChangeScreen events.
## 0.28.1
- On Wayland, fix crash when dropping a window in multi-window setup.
## 0.28.0
- On macOS, fixed `Ime::Commit` persisting for all input after interacting with `Ime`.
- On macOS, added `WindowExtMacOS::option_as_alt` and `WindowExtMacOS::set_option_as_alt`.
- On Windows, fix window size for maximized, undecorated windows.
- On Windows and macOS, add `WindowBuilder::with_active`.
- Add `Window::is_minimized`.
- On X11, fix errors handled during `register_xlib_error_hook` invocation bleeding into winit.
- Add `Window::has_focus`.
- On Windows, fix `Window::set_minimized(false)` not working for windows minimized by `Win + D` hotkey.
- **Breaking:** On Web, touch input no longer fires `WindowEvent::Cursor*`, `WindowEvent::MouseInput`, or `DeviceEvent::MouseMotion` like other platforms, but instead it fires `WindowEvent::Touch`.
- **Breaking:** Removed platform specific `WindowBuilder::with_parent` API in favor of `WindowBuilder::with_parent_window`.
- On Windows, retain `WS_MAXIMIZE` window style when un-minimizing a maximized window.
- On Windows, fix left mouse button release event not being sent after `Window::drag_window`.
- On macOS, run most actions on the main thread, which is strictly more correct, but might make multithreaded applications block slightly more.
- On macOS, fix panic when getting current monitor without any monitor attached.
- On Windows and MacOS, add API to enable/disable window buttons (close, minimize, ...etc).
- On Windows, macOS, X11 and Wayland, add `Window::set_theme`.
- **Breaking:** Remove `WindowExtWayland::wayland_set_csd_theme` and `WindowBuilderExtX11::with_gtk_theme_variant`.
- On Windows, revert window background to an empty brush to avoid white flashes when changing scaling.
- **Breaking:** Removed `Window::set_always_on_top` and related APIs in favor of `Window::set_window_level`.
- On Windows, MacOS and X11, add always on bottom APIs.
- On Windows, fix the value in `MouseButton::Other`.
- On macOS, add `WindowExtMacOS::is_document_edited` and `WindowExtMacOS::set_document_edited` APIs.
- **Breaking:** Removed `WindowBuilderExtIOS::with_root_view_class`; instead, you should use `[[view layer] addSublayer: ...]` to add an instance of the desired layer class (e.g. `CAEAGLLayer` or `CAMetalLayer`). See `vulkano-win` or `wgpu` for examples of this.
- On MacOS and Windows, add `Window::set_content_protected`.
- On MacOS, add `EventLoopBuilderExtMacOS::with_activate_ignoring_other_apps`.
- On Windows, fix icons specified on `WindowBuilder` not taking effect for windows created after the first one.
- On Windows and macOS, add `Window::title` to query the current window title.
- On Windows, fix focusing menubar when pressing `Alt`.
- On MacOS, made `accepts_first_mouse` configurable.
- Migrated `WindowBuilderExtUnix::with_resize_increments` to `WindowBuilder`.
- Added `Window::resize_increments`/`Window::set_resize_increments` to update resize increments at runtime for X11/macOS.
- macOS/iOS: Use `objc2` instead of `objc` internally.
- **Breaking:** Bump MSRV from `1.57` to `1.60`.
- **Breaking:** Split the `platform::unix` module into `platform::x11` and `platform::wayland`. The extension types are similarly renamed.
- **Breaking:**: Removed deprecated method `platform::unix::WindowExtUnix::is_ready`.
- Removed `parking_lot` dependency.
- **Breaking:** On macOS, add support for two-finger touchpad magnification and rotation gestures with new events `WindowEvent::TouchpadMagnify` and `WindowEvent::TouchpadRotate`. Also add support for touchpad smart-magnification gesture with a new event `WindowEvent::SmartMagnify`.
- **Breaking:** On web, the `WindowBuilderExtWebSys::with_prevent_default` setting (enabled by default), now additionally prevents scrolling of the webpage in mobile browsers, previously it only disabled scrolling on desktop.
- On Wayland, `wayland-csd-adwaita` now uses `ab_glyph` instead of `crossfont` to render the title for decorations.
- On Wayland, a new `wayland-csd-adwaita-crossfont` feature was added to use `crossfont` instead of `ab_glyph` for decorations.
- On Wayland, if not otherwise specified use upstream automatic CSD theme selection.
- On X11, added `WindowExtX11::with_parent` to create child windows.
- Added support for `WindowBuilder::with_theme` and `Window::theme` to support per-window dark/light/system theme configuration on macos, windows and wayland.
- On macOS, added support for `WindowEvent::ThemeChanged`.
- **Breaking:** Removed `WindowBuilderExtWindows::with_theme` and `WindowBuilderExtWayland::with_wayland_csd_theme` in favour of `WindowBuilder::with_theme`.
- **Breaking:** Removed `WindowExtWindows::theme` in favour of `Window::theme`.
- Enabled `doc_auto_cfg` when generating docs on docs.rs for feature labels.
- **Breaking:** On Android, switched to using [`android-activity`](https://github.com/rib/android-activity) crate as a glue layer instead of [`ndk-glue`](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue). See [README.md#Android](https://github.com/rust-windowing/winit#Android) for more details. ([#2444](https://github.com/rust-windowing/winit/pull/2444))
- **Breaking:** Removed support for `raw-window-handle` version `0.4`
- On Wayland, `RedrawRequested` not emitted during resize.
- Add a `set_wait_timeout` function to `ControlFlow` to allow waiting for a `Duration`.
- **Breaking:** Remove the unstable `xlib_xconnection()` function from the private interface.
- Added Orbital support for Redox OS
- On X11, added `drag_resize_window` method.
- Added `Window::set_transparent` to provide a hint about transparency of the window on Wayland and macOS.
- On macOS, fix the mouse buttons other than left/right/middle being reported as middle.
- On Wayland, support fractional scaling via the wp-fractional-scale protocol.
- On web, fix removal of mouse event listeners from the global object upon window destruction.
- Add WindowAttributes getter to WindowBuilder to allow introspection of default values.
- Added `Window::set_ime_purpose` for setting the IME purpose, currently implemented on Wayland only.

View File

@@ -1,289 +0,0 @@
## 0.29.15
- On X11, fix crash due to xsettings query on systems with incomplete xsettings.
## 0.29.14
- On X11/Wayland, fix `text` and `text_with_all_modifiers` not being `None` during compose.
- On Wayland, don't reapply cursor grab when unchanged.
- On X11, fix a bug where some mouse events would be unexpectedly filtered out.
## 0.29.13
- On Web, fix possible crash with `ControlFlow::Wait` and `ControlFlow::WaitUntil`.
## 0.29.12
- On X11, fix use after free during xinput2 handling.
- On X11, filter close to zero values in mouse device events
## 0.29.11
- Fix compatibility with 32-bit platforms without 64-bit atomics.
- On macOS, fix incorrect IME cursor rect origin.
- On Windows, fixed a race condition when sending an event through the loop proxy.
- On X11, fix swapped instance and general class names.
- On X11, don't require XIM to run.
- On X11, fix xkb state not being updated correctly sometimes leading to wrong input.
- On X11, reload dpi on `_XSETTINGS_SETTINGS` update.
- On X11, fix deadlock when adjusting DPI and resizing at the same time.
- On Wayland, disable `Occluded` event handling.
- On Wayland, fix DeviceEvent::Motion not being sent
- 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 Wayland, fix title in CSD not updated from `AboutToWait`.
- 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.
- On Orbital, fix `logical_key` and `text` not reported in `KeyEvent`.
- On Orbital, implement `KeyEventExtModifierSupplement`.
- On Orbital, map keys to `NamedKey` when possible.
- On Orbital, implement `set_cursor_grab`.
- On Orbital, implement `set_cursor_visible`.
- On Orbital, implement `drag_window`.
- On Orbital, implement `drag_resize_window`.
- On Orbital, implement `set_transparent`.
- On Orbital, implement `set_visible`.
- On Orbital, implement `is_visible`.
- On Orbital, implement `set_resizable`.
- On Orbital, implement `is_resizable`.
- On Orbital, implement `set_maximized`.
- On Orbital, implement `is_maximized`.
- On Orbital, implement `set_decorations`.
- On Orbital, implement `is_decorated`.
- On Orbital, implement `set_window_level`.
- On Orbital, emit `DeviceEvent::MouseMotion`.
## 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`.
- On X11, fix an issue where floating point data from the server is
misinterpreted during a drag and drop operation.
- On X11, fix a bug where focusing the window would panic.
- On macOS, fix `refresh_rate_millihertz`.
- On Wayland, disable Client Side Decorations when `wl_subcompositor` is not supported.
- On X11, fix `Xft.dpi` detection from Xresources.
- On Windows, fix consecutive calls to `window.set_fullscreen(Some(Fullscreen::Borderless(None)))` resulting in losing previous window state when eventually exiting fullscreen using `window.set_fullscreen(None)`.
- On Wayland, fix resize being sent on focus change.
- On Windows, fix `set_ime_cursor_area`.
## 0.29.4
- Fix crash when running iOS app on macOS.
- On X11, check common alternative cursor names when loading cursor.
- On X11, reload the DPI after a property change event.
- On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread.
- On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account.
- 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
- On Wayland, apply correct scale to `PhysicalSize` passed in `WindowBuilder::with_inner_size` when possible.
- On Wayland, fix `RedrawRequested` being always sent without decorations and `sctk-adwaita` feature.
- On Wayland, ignore resize requests when the window is fully tiled.
- On Wayland, use `configure_bounds` to constrain `with_inner_size` when compositor wants users to pick size.
- On Windows, fix deadlock when accessing the state during `Cursor{Enter,Leave}`.
- On Windows, add support for `Window::set_transparent`.
- On macOS, fix deadlock when entering a nested event loop from an event handler.
- On macOS, add support for `Window::set_blur`.
## 0.29.2
- **Breaking:** Bump MSRV from `1.60` to `1.65`.
- **Breaking:** Add `Event::MemoryWarning`; implemented on iOS/Android.
- **Breaking:** Bump `ndk` version to `0.8.0`, ndk-sys to `0.5.0`, `android-activity` to `0.5.0`.
- **Breaking:** Change default `ControlFlow` from `Poll` to `Wait`.
- **Breaking:** Move `Event::RedrawRequested` to `WindowEvent::RedrawRequested`.
- **Breaking:** Moved `ControlFlow::Exit` to `EventLoopWindowTarget::exit()` and `EventLoopWindowTarget::exiting()` and removed `ControlFlow::ExitWithCode(_)` entirely.
- **Breaking:** Moved `ControlFlow` to `EventLoopWindowTarget::set_control_flow()` and `EventLoopWindowTarget::control_flow()`.
- **Breaking:** `EventLoop::new` and `EventLoopBuilder::build` now return `Result<Self, EventLoopError>`
- **Breaking:** `WINIT_UNIX_BACKEND` was removed in favor of standard `WAYLAND_DISPLAY` and `DISPLAY` variables.
- **Breaking:** on Wayland, dispatching user created Wayland queue won't wake up the loop unless winit has event to send back.
- **Breaking:** remove `DeviceEvent::Text`.
- **Breaking:** Remove lifetime parameter from `Event` and `WindowEvent`.
- **Breaking:** Rename `Window::set_inner_size` to `Window::request_inner_size` and indicate if the size was applied immediately.
- **Breaking:** `ActivationTokenDone` event which could be requested with the new `startup_notify` module, see its docs for more.
- **Breaking:** `ScaleFactorChanged` now contains a writer instead of a reference to update inner size.
- **Breaking** `run() -> !` has been replaced by `run() -> Result<(), EventLoopError>` for returning errors without calling `std::process::exit()` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
- **Breaking** Removed `EventLoopExtRunReturn` / `run_return` in favor of `EventLoopExtPumpEvents` / `pump_events` and `EventLoopExtRunOnDemand` / `run_on_demand` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
- `RedrawRequested` is no longer guaranteed to be emitted after `MainEventsCleared`, it is now platform-specific when the event is emitted after being requested via `redraw_request()`.
- On Windows, `RedrawRequested` is now driven by `WM_PAINT` messages which are requested via `redraw_request()`
- **Breaking** `LoopDestroyed` renamed to `LoopExiting` ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking** `RedrawEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking** `MainEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking:** Remove all deprecated `modifiers` fields.
- **Breaking:** Rename `DeviceEventFilter` to `DeviceEvents` reversing the behavior of variants.
- **Breaking** Add `AboutToWait` event which is emitted when the event loop is about to block and wait for new events ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking:** Rename `EventLoopWindowTarget::set_device_event_filter` to `listen_device_events`.
- **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone.
- **Breaking:** `with_x11_visual` now takes the visual ID instead of the bare pointer.
- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events on Wayland, X11, Windows, macOS and Web.
- **Breaking:** On Web, `instant` is now replaced by `web_time`.
- **Breaking:** On Web, dropped support for Safari versions below 13.1.
- **Breaking:** On Web, the canvas output bitmap size is no longer adjusted.
- **Breaking:** On Web, the canvas size is not controlled by Winit anymore and external changes to the canvas size will be reported through `WindowEvent::Resized`.
- **Breaking:** Updated `bitflags` crate version to `2`, which changes the API on exposed types.
- **Breaking:** `CursorIcon::Arrow` was removed.
- **Breaking:** `CursorIcon::Hand` is now named `CursorIcon::Pointer`.
- **Breaking:** `CursorIcon` is now used from the `cursor-icon` crate.
- **Breaking:** `WindowExtWebSys::canvas()` now returns an `Option`.
- **Breaking:** Overhaul keyboard input handling.
- Replace `KeyboardInput` with `KeyEvent` and `RawKeyEvent`.
- Change `WindowEvent::KeyboardInput` to contain a `KeyEvent`.
- Change `Event::Key` to contain a `RawKeyEvent`.
- Remove `Event::ReceivedCharacter`. In its place, you should use
`KeyEvent.text` in combination with `WindowEvent::Ime`.
- Replace `VirtualKeyCode` with the `Key` enum.
- Replace `ScanCode` with the `KeyCode` enum.
- Rename `ModifiersState::LOGO` to `SUPER` and `ModifiersState::CTRL` to `CONTROL`.
- Add `PhysicalKey` wrapping `KeyCode` and `NativeKeyCode`.
- Add `KeyCode` to refer to keys (roughly) by their physical location.
- Add `NativeKeyCode` to represent raw `KeyCode`s which Winit doesn't
understand.
- Add `Key` to represent the keys after they've been interpreted by the
active (software) keyboard layout.
- Add `NamedKey` to represent the categorized keys.
- Add `NativeKey` to represent raw `Key`s which Winit doesn't understand.
- Add `KeyLocation` to tell apart `Key`s which usually "mean" the same thing,
but can appear simultaneously in different spots on the same keyboard
layout.
- Add `Window::reset_dead_keys` to enable application-controlled cancellation
of dead key sequences.
- Add `KeyEventExtModifierSupplement` to expose additional (and less
portable) interpretations of a given key-press.
- Add `PhysicalKeyExtScancode`, which lets you convert between scancodes and
`PhysicalKey`.
- `ModifiersChanged` now uses dedicated `Modifiers` struct.
- Removed platform-specific extensions that should be retrieved through `raw-window-handle` trait implementations instead:
- `platform::windows::HINSTANCE`.
- `WindowExtWindows::hinstance`.
- `WindowExtWindows::hwnd`.
- `WindowExtIOS::ui_window`.
- `WindowExtIOS::ui_view_controller`.
- `WindowExtIOS::ui_view`.
- `WindowExtMacOS::ns_window`.
- `WindowExtMacOS::ns_view`.
- `EventLoopWindowTargetExtWayland::wayland_display`.
- `WindowExtWayland::wayland_surface`.
- `WindowExtWayland::wayland_display`.
- `WindowExtX11::xlib_window`.
- `WindowExtX11::xlib_display`.
- `WindowExtX11::xlib_screen_id`.
- `WindowExtX11::xcb_connection`.
- Reexport `raw-window-handle` in `window` module.
- Add `ElementState::is_pressed`.
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now.
- Add `Window::show_window_menu` to request a titlebar/system menu; implemented on Wayland/Windows for now.
- Implement `AsFd`/`AsRawFd` for `EventLoop<T>` on X11 and Wayland.
- Implement `PartialOrd` and `Ord` for `MouseButton`.
- Implement `PartialOrd` and `Ord` on types in the `dpi` module.
- Make `WindowBuilder` `Send + Sync`.
- Make iOS `MonitorHandle` and `VideoMode` usable from other threads.
- Make iOS windows usable from other threads.
- On Android, add force data to touch events.
- On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually.
- On Android, fix `DeviceId` to contain device id's.
- On Orbital, fix `ModifiersChanged` not being sent.
- On Wayland, `Window::outer_size` now accounts for **client side** decorations.
- On Wayland, add `Window::drag_resize_window` method.
- On Wayland, remove `WINIT_WAYLAND_CSD_THEME` variable.
- On Wayland, fix `TouchPhase::Canceled` being sent for moved events.
- On Wayland, fix forward compatibility issues.
- On Wayland, fix initial window size not restored for maximized/fullscreened on startup window.
- On Wayland, fix maximized startup not taking full size on GNOME.
- On Wayland, fix maximized window creation and window geometry handling.
- On Wayland, fix window not checking that it actually got initial configure event.
- On Wayland, make double clicking and moving the CSD frame more reliable.
- On Wayland, support `Occluded` event with xdg-shell v6
- On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor.
- On Web, `ControlFlow::WaitUntil` now uses the Prioritized Task Scheduling API. `setTimeout()`, with a trick to circumvent throttling to 4ms, is used as a fallback.
- On Web, `EventLoopProxy` now implements `Send`.
- On Web, `Window` now implements `Send` and `Sync`.
- On Web, account for CSS `padding`, `border`, and `margin` when getting or setting the canvas position.
- On Web, add Fullscreen API compatibility for Safari.
- On Web, add `DeviceEvent::Motion`, `DeviceEvent::MouseWheel`, `DeviceEvent::Button` and `DeviceEvent::Key` support.
- On Web, add `EventLoopWindowTargetExtWebSys` and `PollStrategy`, which allows to set different strategies for `ControlFlow::Poll`. By default the Prioritized Task Scheduling API is used, but an option to use `Window.requestIdleCallback` is available as well. Both use `setTimeout()`, with a trick to circumvent throttling to 4ms, as a fallback.
- On Web, add `WindowBuilderExtWebSys::with_append()` to append the canvas element to the web page on creation.
- On Web, allow event loops to be recreated with `spawn`.
- On Web, enable event propagation.
- On Web, fix `ControlFlow::WaitUntil` to never wake up **before** the given time.
- On Web, fix `DeviceEvent::MouseMotion` only being emitted for each canvas instead of the whole window.
- On Web, fix `Window:::set_fullscreen` doing nothing when called outside the event loop but during transient activation.
- On Web, fix pen treated as mouse input.
- On Web, fix pointer button events not being processed when a buttons is already pressed.
- On Web, fix scale factor resize suggestion always overwriting the canvas size.
- On Web, fix some `WindowBuilder` methods doing nothing.
- On Web, fix some `Window` methods using incorrect HTML attributes instead of CSS properties.
- On Web, fix the bfcache by not using the `beforeunload` event and map bfcache loading/unloading to `Suspended`/`Resumed` events.
- On Web, fix touch input not gaining or losing focus.
- On Web, fix touch location to be as accurate as mouse position.
- On Web, handle coalesced pointer events, which increases the resolution of pointer inputs.
- On Web, implement `Window::focus_window()`.
- On Web, implement `Window::set_(min|max)_inner_size()`.
- On Web, implement `WindowEvent::Occluded`.
- On Web, never return a `MonitorHandle`.
- On Web, prevent clicks on the canvas to select text.
- On Web, remove any fullscreen requests from the queue when an external fullscreen activation was detected.
- On Web, remove unnecessary `Window::is_dark_mode()`, which was replaced with `Window::theme()`.
- On Web, respect `EventLoopWindowTarget::listen_device_events()` settings.
- On Web, scale factor and dark mode detection are now more robust.
- On Web, send mouse position on button release as well.
- On Web, take all transient activations on the canvas and window into account to queue a fullscreen request.
- On Web, use `Window.requestAnimationFrame()` to throttle `RedrawRequested` events.
- On Web, use the correct canvas size when calculating the new size during scale factor change, instead of using the output bitmap size.
- On Web: fix `Window::request_redraw` not waking the event loop when called from outside the loop.
- On Web: fix position of touch events to be relative to the canvas.
- On Windows, add `drag_resize_window` method support.
- On Windows, add horizontal MouseWheel `DeviceEvent`.
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.
- On Windows, fix IME APIs not working when from non event loop thread.
- On Windows, fix `CursorEnter/Left` not being sent when grabbing the mouse.
- On Windows, fix `RedrawRequested` not being delivered when calling `Window::request_redraw` from `RedrawRequested`.
- On Windows, port to `windows-sys` version 0.48.0.
- On X11, add a `with_embedded_parent_window` function to the window builder to allow embedding a window into another window.
- On X11, fix event loop not waking up on `ControlFlow::Poll` and `ControlFlow::WaitUntil`.
- On X11, fix false positive flagging of key repeats when pressing different keys with no release between presses.
- On X11, set `visual_id` in returned `raw-window-handle`.
- On iOS, add ability to change the status bar style.
- On iOS, add force data to touch events when using the Apple Pencil.
- On iOS, always wake the event loop when transitioning from `ControlFlow::Poll` to `ControlFlow::Poll`.
- On iOS, send events `WindowEvent::Occluded(false)`, `WindowEvent::Occluded(true)` when application enters/leaves foreground.
- On macOS, add tabbing APIs on `WindowExtMacOS` and `EventLoopWindowTargetExtMacOS`.
- On macOS, fix assertion when pressing `Globe` key.
- On macOS, fix crash in `window.set_minimized(false)`.
- On macOS, fix crash when dropping `Window`.

View File

@@ -1,413 +0,0 @@
## 0.30.13
### Added
- On Wayland, add `Window::set_resize_increments`.
### Fixed
- On macOS, fixed crash when dragging non-file content onto window.
- On X11, fix `set_hittest` not working on some window managers.
- On X11, fix debug mode overflow panic in `set_timestamp`.
- On macOS, fix crash in `set_marked_text` when native Pinyin IME sends out-of-bounds `selected_range`.
- On Windows, fix `WM_IME_SETCONTEXT` IME UI flag masking on `lParam`.
- On Android, populate `KeyEvent::text` and `KeyEvent::text_with_all_modifiers` via `Key::to_text()`.
## 0.30.12
### Fixed
- On macOS, fix crash on macOS 26 by using objc2's `relax-sign-encoding` feature.
## 0.30.11
### Fixed
- On Windows, fixed crash in should_apps_use_dark_mode() for Windows versions < 17763.
- On Wayland, fixed `pump_events` driven loop deadlocking when loop was not drained before exit.
## 0.30.10
### Added
- On Windows, add `IconExtWindows::from_resource_name`.
- On Windows, add `CursorGrabMode::Locked`.
- On Wayland, add `WindowExtWayland::xdg_toplevel`.
### Changed
- On macOS, no longer need control of the main `NSApplication` class (which means you can now override it yourself).
- On iOS, remove custom application delegates. You are now allowed to override the
application delegate yourself.
- On iOS, no longer act as-if the application successfully open all URLs. Override
`application:didFinishLaunchingWithOptions:` and provide the desired behaviour yourself.
### Fixed
- On Windows, fixed ~500 ms pause when clicking the title bar during continuous redraw.
- On macOS, `WindowExtMacOS::set_simple_fullscreen` now honors `WindowExtMacOS::set_borderless_game`
- On X11 and Wayland, fixed pump_events with `Some(Duration::Zero)` blocking with `Wait` polling mode
- On Wayland, fixed a crash when consequently calling `set_cursor_grab` without pointer focus.
- On Wayland, ensure that external event loop is woken-up when using pump_events and integrating via `FD`.
- On Wayland, apply fractional scaling to custom cursors.
- On macOS, fixed `run_app_on_demand` returning without closing open windows.
- On macOS, fixed `VideoMode::refresh_rate_millihertz` for fractional refresh rates.
- On macOS, store monitor handle to avoid panics after going in/out of sleep.
- On macOS, allow certain invalid monitor handles and return `None` instead of panicking.
- On Windows, fixed `Ime::Preedit` cursor offset calculation.
## 0.30.9
### Changed
- On Wayland, no longer send an explicit clearing `Ime::Preedit` just prior to a new `Ime::Preedit`.
### Fixed
- On X11, fix crash with uim.
- On X11, fix modifiers for keys that were sent by the same X11 request.
- On iOS, fix high CPU usage even when using `ControlFlow::Wait`.
## 0.30.8
### Added
- `ActivationToken::from_raw` and `ActivationToken::into_raw`.
- On X11, add a workaround for disabling IME on GNOME.
### Fixed
- On Windows, fixed the event loop not waking on accessibility requests.
- On X11, fixed cursor grab mode state tracking on error.
## 0.30.7
### Fixed
- On X11, fixed KeyboardInput delivered twice when IME enabled.
## 0.30.6
### Added
- On macOS, add `WindowExtMacOS::set_borderless_game` and `WindowAttributesExtMacOS::with_borderless_game`
to fully disable the menu bar and dock in Borderless Fullscreen as commonly done in games.
- On X11, the `window` example now understands the `X11_VISUAL_ID` and `X11_SCREEN_ID` env
variables to test the respective modifiers of window creation.
- On Android, the soft keyboard can now be shown using `Window::set_ime_allowed`.
- Add basic iOS IME support. The soft keyboard can now be shown using `Window::set_ime_allowed`.
### Fixed
- On macOS, fix `WindowEvent::Moved` sometimes being triggered unnecessarily on resize.
- On macOS, package manifest definitions of `LSUIElement` will no longer be overridden with the
default activation policy, unless explicitly provided during initialization.
- On macOS, fix crash when calling `drag_window()` without a left click present.
- On X11, key events forward to IME anyway, even when it's disabled.
- On Windows, make `ControlFlow::WaitUntil` work more precisely using `CREATE_WAITABLE_TIMER_HIGH_RESOLUTION`.
- On X11, creating windows on screen that is not the first one (e.g. `DISPLAY=:0.1`) works again.
- On X11, creating windows while passing `with_x11_screen(non_default_screen)` works again.
- On X11, fix XInput handling that prevented a new window from getting the focus in some cases.
- On macOS, fix crash when pressing Caps Lock in certain configurations.
- On iOS, fixed `MonitorHandle`'s `PartialEq` and `Hash` implementations.
- On macOS, fixed undocumented cursors (e.g. zoom, resize, help) always appearing to be invalid and falling back to the default cursor.
## 0.30.5
### Added
- Add `ActiveEventLoop::system_theme()`, returning the current system theme.
- On Web, implement `Error` for `platform::web::CustomCursorError`.
- On Android, add `{Active,}EventLoopExtAndroid::android_app()` to access the app used to create the loop.
### Fixed
- On MacOS, fix building with `feature = "rwh_04"`.
- On Web, pen events are now routed through to `WindowEvent::Cursor*`.
- On macOS, fix panic when releasing not available monitor.
- On MacOS, return the system theme in `Window::theme()` if no theme override is set.
## 0.30.4
### Changed
- `DeviceId::dummy()` and `WindowId::dummy()` are no longer marked `unsafe`.
### Fixed
- On Wayland, avoid crashing when compositor is misbehaving.
- On Web, fix `WindowEvent::Resized` not using `requestAnimationFrame` when sending
`WindowEvent::RedrawRequested` and also potentially causing `WindowEvent::RedrawRequested`
to not be de-duplicated.
- Account for different browser engine implementations of pointer movement coordinate space.
## 0.30.3
### Added
- On Web, add `EventLoopExtWebSys::(set_)poll_strategy()` to allow setting
control flow strategies before starting the event loop.
- On Web, add `WaitUntilStrategy`, which allows to set different strategies for
`ControlFlow::WaitUntil`. By default the Prioritized Task Scheduling API is
used, with a fallback to `setTimeout()` with a trick to circumvent throttling
to 4ms. But an option to use a Web worker to schedule the timer is available
as well, which commonly prevents any throttling when the window is not focused.
### Changed
- On macOS, set the window theme on the `NSWindow` instead of application-wide.
### Fixed
- On X11, build on arm platforms.
- On macOS, fixed `WindowBuilder::with_theme` not having any effect on the window.
## 0.30.2
### Fixed
- On Web, fix `EventLoopProxy::send_event()` triggering event loop immediately
when not called from inside the event loop. Now queues a microtask instead.
- On Web, stop overwriting default cursor with `CursorIcon::Default`.
- On Web, prevent crash when using `InnerSizeWriter::request_inner_size()`.
- On macOS, fix not working opacity for entire window.
## 0.30.1
### Added
- Reexport `raw-window-handle` versions 0.4 and 0.5 as `raw_window_handle_04` and `raw_window_handle_05`.
- Implement `ApplicationHandler` for `&mut` references and heap allocations to something that implements `ApplicationHandler`.
### Fixed
- On macOS, fix panic on exit when dropping windows outside the event loop.
- On macOS, fix window dragging glitches when dragging across a monitor boundary with different scale factor.
- On macOS, fix the range in `Ime::Preedit`.
- On macOS, use the system's internal mechanisms for queuing events.
- On macOS, handle events directly instead of queuing when possible.
## 0.30.0
### Added
- Add `OwnedDisplayHandle` type for allowing safe display handle usage outside of
trivial cases.
- Add `ApplicationHandler<T>` trait which mimics `Event<T>`.
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a
`CursorIcon` or `CustomCursor`.
- Add `Sync` implementation for `EventLoopProxy<T: Send>`.
- Add `Window::default_attributes` to get default `WindowAttributes`.
- Add `EventLoop::builder` to get `EventLoopBuilder` without export.
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated
cursors from other `CustomCursor`s.
- Add `{Active,}EventLoop::create_custom_cursor` to load custom cursor image sources.
- Add `ActiveEventLoop::create_window` and `EventLoop::create_window`.
- Add `CustomCursor` which could be set via `Window::set_cursor`, implemented on
Windows, macOS, X11, Wayland, and Web.
- On Web, add to toggle calling `Event.preventDefault()` on `Window`.
- On iOS, add `PinchGesture`, `DoubleTapGesture`, `PanGesture` and `RotationGesture`.
- on iOS, use `UIGestureRecognizerDelegate` for fine grained control of gesture recognizers.
- On macOS, add services menu.
- On Windows, add `with_title_text_color`, and `with_corner_preference` on
`WindowAttributesExtWindows`.
- On Windows, implement resize increments.
- On Windows, add `AnyThread` API to access window handle off the main thread.
### Changed
- Bump MSRV from `1.65` to `1.70`.
- On Wayland, bump `sctk-adwaita` to `0.9.0`, which changed system library
crates. This change is a **cascading breaking change**, you must do breaking
change as well, even if you don't expose winit.
- Rename `TouchpadMagnify` to `PinchGesture`.
- Rename `SmartMagnify` to `DoubleTapGesture`.
- Rename `TouchpadRotate` to `RotationGesture`.
- Rename `EventLoopWindowTarget` to `ActiveEventLoop`.
- Rename `platform::x11::XWindowType` to `platform::x11::WindowType`.
- Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold
static data.
- Make `Debug` formatting of `WindowId` more concise.
- Move `dpi` types to its own crate, and re-export it from the root crate.
- Replace `log` with `tracing`, use `log` feature on `tracing` to restore old
behavior.
- `EventLoop::with_user_event` now returns `EventLoopBuilder`.
- On Web, return `HandleError::Unavailable` when a window handle is not available.
- On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
- On Web, remove queuing fullscreen request in absence of transient activation.
- On iOS, return `HandleError::Unavailable` when a window handle is not available.
- On macOS, return `HandleError::Unavailable` when a window handle is not available.
- On Windows, remove `WS_CAPTION`, `WS_BORDER`, and `WS_EX_WINDOWEDGE` styles
for child windows without decorations.
- On Android, bump `ndk` to `0.9.0` and `android-activity` to `0.6.0`,
and remove unused direct dependency on `ndk-sys`.
### Deprecated
- Deprecate `EventLoop::run`, use `EventLoop::run_app`.
- Deprecate `EventLoopExtRunOnDemand::run_on_demand`, use `EventLoop::run_app_on_demand`.
- Deprecate `EventLoopExtPumpEvents::pump_events`, use `EventLoopExtPumpEvents::pump_app_events`.
The new `app` APIs accept a newly added `ApplicationHandler<T>` instead of
`Fn`. The semantics are mostly the same, given that the capture list of the
closure is your new `State`. Consider the following code:
```rust,no_run
use winit::event::Event;
use winit::event_loop::EventLoop;
use winit::window::Window;
struct MyUserEvent;
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
let window = event_loop.create_window(Window::default_attributes()).unwrap();
let mut counter = 0;
let _ = event_loop.run(move |event, event_loop| {
match event {
Event::AboutToWait => {
window.request_redraw();
counter += 1;
}
Event::WindowEvent { window_id, event } => {
// Handle window event.
}
Event::UserEvent(event) => {
// Handle user event.
}
Event::DeviceEvent { device_id, event } => {
// Handle device event.
}
_ => (),
}
});
```
To migrate this code, you should move all the captured values into some
newtype `State` and implement `ApplicationHandler` for this type. Finally,
we move particular `match event` arms into methods on `ApplicationHandler`,
for example:
```rust,no_run
use winit::application::ApplicationHandler;
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
use winit::event_loop::{EventLoop, ActiveEventLoop};
use winit::window::{Window, WindowId};
struct MyUserEvent;
struct State {
window: Window,
counter: i32,
}
impl ApplicationHandler<MyUserEvent> for State {
fn user_event(&mut self, event_loop: &ActiveEventLoop, user_event: MyUserEvent) {
// Handle user event.
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
// Your application got resumed.
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
// Handle window event.
}
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
// Handle device event.
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
self.window.request_redraw();
self.counter += 1;
}
}
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
#[allow(deprecated)]
let window = event_loop.create_window(Window::default_attributes()).unwrap();
let mut state = State { window, counter: 0 };
let _ = event_loop.run_app(&mut state);
```
Please submit your feedback after migrating in [this issue](https://github.com/rust-windowing/winit/issues/3626).
- Deprecate `Window::set_cursor_icon`, use `Window::set_cursor`.
### Removed
- Remove `Window::new`, use `ActiveEventLoop::create_window` instead.
You now have to create your windows inside the actively running event loop
(usually the `new_events(cause: StartCause::Init)` or `resumed()` events),
and can no longer do it before the application has properly launched.
This change is done to fix many long-standing issues on iOS and macOS, and
will improve things on Wayland once fully implemented.
To ease migration, we provide the deprecated `EventLoop::create_window` that
will allow you to bypass this restriction in this release.
Using the migration example from above, you can change your code as follows:
```rust,no_run
use winit::application::ApplicationHandler;
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
use winit::event_loop::{EventLoop, ActiveEventLoop};
use winit::window::{Window, WindowId};
#[derive(Default)]
struct State {
// Use an `Option` to allow the window to not be available until the
// application is properly running.
window: Option<Window>,
counter: i32,
}
impl ApplicationHandler for State {
// This is a common indicator that you can create a window.
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
// `unwrap` is fine, the window will always be available when
// receiving a window event.
let window = self.window.as_ref().unwrap();
// Handle window event.
}
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
// Handle window event.
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
self.counter += 1;
}
}
}
let event_loop = EventLoop::new().unwrap();
let mut state = State::default();
let _ = event_loop.run_app(&mut state);
```
- Remove `Deref` implementation for `EventLoop` that gave `EventLoopWindowTarget`.
- Remove `WindowBuilder` in favor of `WindowAttributes`.
- Remove Generic parameter `T` from `ActiveEventLoop`.
- Remove `EventLoopBuilder::with_user_event`, use `EventLoop::with_user_event`.
- Remove Redundant `EventLoopError::AlreadyRunning`.
- Remove `WindowAttributes::fullscreen` and expose as field directly.
- On X11, remove `platform::x11::XNotSupported` export.
### Fixed
- On Web, fix setting cursor icon overriding cursor visibility.
- On Windows, fix cursor not confined to center of window when grabbed and hidden.
- On macOS, fix sequence of mouse events being out of order when dragging on the trackpad.
- On Wayland, fix decoration glitch on close with some compositors.
- On Android, fix a regression introduced in #2748 to allow volume key events to be received again.
- On Windows, don't return a valid window handle outside of the GUI thread.
- On macOS, don't set the background color when initializing a window with transparency.

View File

@@ -1,33 +0,0 @@
## 0.8.3
- Fixed issue of calls to `set_inner_size` blocking on Windows.
- Mapped `ISO_Left_Tab` to `VirtualKeyCode::Tab` to make the key work with modifiers
- Fixed the X11 backed on 32bit targets
## 0.8.2
- Uniformize keyboard scancode values across Wayland and X11 (#297).
- Internal rework of the wayland event loop
- Added method `os::linux::WindowExt::is_ready`
## 0.8.1
- Added various methods to `os::linux::EventsLoopExt`, plus some hidden items necessary to make
glutin work.
## 0.8.0
- Added `Window::set_maximized`, `WindowAttributes::maximized` and `WindowBuilder::with_maximized`.
- Added `Window::set_fullscreen`.
- Changed `with_fullscreen` to take a `Option<MonitorId>` instead of a `MonitorId`.
- Removed `MonitorId::get_native_identifier()` in favor of platform-specific traits in the `os`
module.
- Changed `get_available_monitors()` and `get_primary_monitor()` to be methods of `EventsLoop`
instead of stand-alone methods.
- Changed `EventsLoop` to be tied to a specific X11 or Wayland connection.
- Added a `os::linux::EventsLoopExt` trait that makes it possible to configure the connection.
- Fixed the emscripten code, which now compiles.
- Changed the X11 fullscreen code to use `xrandr` instead of `xxf86vm`.
- Fixed the Wayland backend to produce `Refresh` event after window creation.
- Changed the `Suspended` event to be outside of `WindowEvent`.
- Fixed the X11 backend sometimes reporting the wrong virtual key (#273).

View File

@@ -1,22 +0,0 @@
## 0.9.0
- Added event `WindowEvent::HiDPIFactorChanged`.
- Added method `MonitorId::get_hidpi_factor`.
- Deprecated `get_inner_size_pixels` and `get_inner_size_points` methods of `Window` in favor of
`get_inner_size`.
- **Breaking:** `EventsLoop` is `!Send` and `!Sync` because of platform-dependant constraints,
but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`.
- `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`.
- Rewrite of the wayland backend to use wayland-client-0.11
- Support for dead keys on wayland for keyboard utf8 input
- Monitor enumeration on Windows is now implemented using `EnumDisplayMonitors` instead of
`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`.
- On Windows added `MonitorIdExt::hmonitor` method
- Impl `Clone` for `EventsLoopProxy`
- `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found
- Support for touch event on wayland
- `WindowEvent`s `MouseMoved`, `MouseEntered`, and `MouseLeft` have been renamed to
`CursorMoved`, `CursorEntered`, and `CursorLeft`.
- New `DeviceEvent`s added, `MouseMotion` and `MouseWheel`.
- Send `CursorMoved` event after `CursorEntered` and `Focused` events.
- Add support for `ModifiersState`, `MouseMove`, `MouseInput`, `MouseMotion` for emscripten backend.

View File

@@ -1,7 +1,7 @@
use core::fmt; use core::fmt;
use std::error::Error; use std::hash::Hasher;
use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use std::{error::Error, hash::Hash};
use cursor_icon::CursorIcon; use cursor_icon::CursorIcon;
@@ -12,7 +12,7 @@ pub const MAX_CURSOR_SIZE: u16 = 2048;
const PIXEL_SIZE: usize = 4; const PIXEL_SIZE: usize = 4;
/// See [`Window::set_cursor()`][crate::window::Window::set_cursor] for more details. /// See [`Window::set_cursor()`](crate::window::Window::set_cursor) for more details.
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Cursor { pub enum Cursor {
Icon(CursorIcon), Icon(CursorIcon),
@@ -88,9 +88,14 @@ impl CustomCursor {
hotspot_x: u16, hotspot_x: u16,
hotspot_y: u16, hotspot_y: u16,
) -> Result<CustomCursorSource, BadImage> { ) -> Result<CustomCursorSource, BadImage> {
let _span = let _span = tracing::debug_span!(
tracing::debug_span!("winit::Cursor::from_rgba", width, height, hotspot_x, hotspot_y) "winit::Cursor::from_rgba",
.entered(); width,
height,
hotspot_x,
hotspot_y
)
.entered();
Ok(CustomCursorSource { Ok(CustomCursorSource {
inner: PlatformCustomCursorSource::from_rgba( inner: PlatformCustomCursorSource::from_rgba(
@@ -124,36 +129,45 @@ pub enum BadImage {
ByteCountNotDivisibleBy4 { byte_count: usize }, ByteCountNotDivisibleBy4 { byte_count: usize },
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`. /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
/// At least one of your arguments is incorrect. /// At least one of your arguments is incorrect.
DimensionsVsPixelCount { width: u16, height: u16, width_x_height: u64, pixel_count: u64 }, DimensionsVsPixelCount {
width: u16,
height: u16,
width_x_height: u64,
pixel_count: u64,
},
/// Produced when the hotspot is outside the image bounds /// Produced when the hotspot is outside the image bounds
HotspotOutOfBounds { width: u16, height: u16, hotspot_x: u16, hotspot_y: u16 }, HotspotOutOfBounds {
width: u16,
height: u16,
hotspot_x: u16,
hotspot_y: u16,
},
} }
impl fmt::Display for BadImage { impl fmt::Display for BadImage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
BadImage::TooLarge { width, height } => write!( BadImage::TooLarge { width, height } => write!(f,
f, "The specified dimensions ({width:?}x{height:?}) are too large. The maximum is {MAX_CURSOR_SIZE:?}x{MAX_CURSOR_SIZE:?}.",
"The specified dimensions ({width:?}x{height:?}) are too large. The maximum is \
{MAX_CURSOR_SIZE:?}x{MAX_CURSOR_SIZE:?}.",
), ),
BadImage::ByteCountNotDivisibleBy4 { byte_count } => write!( BadImage::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
f, "The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
"The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making \
it impossible to interpret as 32bpp RGBA pixels.",
), ),
BadImage::DimensionsVsPixelCount { width, height, width_x_height, pixel_count } => { BadImage::DimensionsVsPixelCount {
write!( width,
f, height,
"The specified dimensions ({width:?}x{height:?}) don't match the number of \ width_x_height,
pixels supplied by the `rgba` argument ({pixel_count:?}). For those \ pixel_count,
dimensions, the expected pixel count is {width_x_height:?}.", } => write!(f,
) "The specified dimensions ({width:?}x{height:?}) don't match the number of pixels supplied by the `rgba` argument ({pixel_count:?}). For those dimensions, the expected pixel count is {width_x_height:?}.",
}, ),
BadImage::HotspotOutOfBounds { width, height, hotspot_x, hotspot_y } => write!( BadImage::HotspotOutOfBounds {
f, width,
"The specified hotspot ({hotspot_x:?}, {hotspot_y:?}) is outside the image bounds \ height,
({width:?}x{height:?}).", hotspot_x,
hotspot_y,
} => write!(f,
"The specified hotspot ({hotspot_x:?}, {hotspot_y:?}) is outside the image bounds ({width:?}x{height:?}).",
), ),
} }
} }
@@ -222,7 +236,9 @@ impl CursorImage {
} }
if rgba.len() % PIXEL_SIZE != 0 { if rgba.len() % PIXEL_SIZE != 0 {
return Err(BadImage::ByteCountNotDivisibleBy4 { byte_count: rgba.len() }); return Err(BadImage::ByteCountNotDivisibleBy4 {
byte_count: rgba.len(),
});
} }
let pixel_count = (rgba.len() / PIXEL_SIZE) as u64; let pixel_count = (rgba.len() / PIXEL_SIZE) as u64;
@@ -237,10 +253,21 @@ impl CursorImage {
} }
if hotspot_x >= width || hotspot_y >= height { if hotspot_x >= width || hotspot_y >= height {
return Err(BadImage::HotspotOutOfBounds { width, height, hotspot_x, hotspot_y }); return Err(BadImage::HotspotOutOfBounds {
width,
height,
hotspot_x,
hotspot_y,
});
} }
Ok(CursorImage { rgba, width, height, hotspot_x, hotspot_y }) Ok(CursorImage {
rgba,
width,
height,
hotspot_x,
hotspot_y,
})
} }
} }

View File

@@ -71,7 +71,10 @@ macro_rules! os_error {
impl fmt::Display for OsError { impl fmt::Display for OsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.pad(&format!("os error at {}:{}: {}", self.file, self.line, self.error)) f.pad(&format!(
"os error at {}:{}: {}",
self.file, self.line, self.error
))
} }
} }
@@ -114,14 +117,19 @@ impl error::Error for NotSupportedError {}
impl error::Error for EventLoopError {} impl error::Error for EventLoopError {}
#[cfg(test)] #[cfg(test)]
#[allow(clippy::redundant_clone)]
mod tests { mod tests {
#![allow(clippy::redundant_clone)]
use super::*; use super::*;
// Eat attributes for testing // Eat attributes for testing
#[test] #[test]
fn ensure_fmt_does_not_panic() { fn ensure_fmt_does_not_panic() {
let _ = format!("{:?}, {}", NotSupportedError::new(), NotSupportedError::new().clone()); let _ = format!(
"{:?}, {}",
NotSupportedError::new(),
NotSupportedError::new().clone()
);
let _ = format!( let _ = format!(
"{:?}, {}", "{:?}, {}",
ExternalError::NotSupported(NotSupportedError::new()), ExternalError::NotSupported(NotSupportedError::new()),

View File

@@ -1,8 +1,7 @@
//! The [`Event`] enum and assorted supporting types. //! The [`Event`] enum and assorted supporting types.
//! //!
//! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get //! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get
//! processed and used to modify the program state. For more details, see the root-level //! processed and used to modify the program state. For more details, see the root-level documentation.
//! documentation.
//! //!
//! Some of these events represent different "parts" of a traditional event-handling loop. You could //! Some of these events represent different "parts" of a traditional event-handling loop. You could
//! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this: //! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this:
@@ -45,14 +44,16 @@ use smol_str::SmolStr;
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time::Instant; use web_time::Instant;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::error::ExternalError; use crate::error::ExternalError;
use crate::event_loop::AsyncRequestSerial;
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState};
use crate::platform_impl;
#[cfg(doc)] #[cfg(doc)]
use crate::window::Window; use crate::window::Window;
use crate::window::{ActivationToken, Theme, WindowId}; use crate::{
dpi::{PhysicalPosition, PhysicalSize},
event_loop::AsyncRequestSerial,
keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState},
platform_impl,
window::{ActivationToken, Theme, WindowId},
};
/// Describes a generic event. /// Describes a generic event.
/// ///
@@ -67,12 +68,18 @@ pub enum Event<T: 'static> {
/// See [`ApplicationHandler::window_event`] for details. /// See [`ApplicationHandler::window_event`] for details.
/// ///
/// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event /// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event
WindowEvent { window_id: WindowId, event: WindowEvent }, WindowEvent {
window_id: WindowId,
event: WindowEvent,
},
/// See [`ApplicationHandler::device_event`] for details. /// See [`ApplicationHandler::device_event`] for details.
/// ///
/// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event /// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event
DeviceEvent { device_id: DeviceId, event: DeviceEvent }, DeviceEvent {
device_id: DeviceId,
event: DeviceEvent,
},
/// See [`ApplicationHandler::user_event`] for details. /// See [`ApplicationHandler::user_event`] for details.
/// ///
@@ -131,11 +138,17 @@ pub enum StartCause {
/// guaranteed to be equal to or after the requested resume time. /// guaranteed to be equal to or after the requested resume time.
/// ///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil /// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
ResumeTimeReached { start: Instant, requested_resume: Instant }, ResumeTimeReached {
start: Instant,
requested_resume: Instant,
},
/// Sent if the OS has new events to send to the window, after a wait was requested. Contains /// Sent if the OS has new events to send to the window, after a wait was requested. Contains
/// the moment the wait was requested and the resume time, if requested. /// the moment the wait was requested and the resume time, if requested.
WaitCancelled { start: Instant, requested_resume: Option<Instant> }, WaitCancelled {
start: Instant,
requested_resume: Option<Instant>,
},
/// Sent if the event loop is being resumed after the loop's control flow was set to /// Sent if the event loop is being resumed after the loop's control flow was set to
/// [`ControlFlow::Poll`]. /// [`ControlFlow::Poll`].
@@ -151,11 +164,18 @@ pub enum StartCause {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum WindowEvent { pub enum WindowEvent {
/// The activation token was delivered back and now could be used. /// The activation token was delivered back and now could be used.
#[cfg_attr(not(any(x11_platform, wayland_platform)), allow(rustdoc::broken_intra_doc_links))] ///
#[cfg_attr(
not(any(x11_platform, wayland_platform)),
allow(rustdoc::broken_intra_doc_links)
)]
/// Delivered in response to [`request_activation_token`]. /// Delivered in response to [`request_activation_token`].
/// ///
/// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token /// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token
ActivationTokenDone { serial: AsyncRequestSerial, token: ActivationToken }, ActivationTokenDone {
serial: AsyncRequestSerial,
token: ActivationToken,
},
/// The size of the window has changed. Contains the client area's new dimensions. /// The size of the window has changed. Contains the client area's new dimensions.
Resized(PhysicalSize<u32>), Resized(PhysicalSize<u32>),
@@ -209,10 +229,10 @@ pub enum WindowEvent {
/// If `true`, the event was generated synthetically by winit /// If `true`, the event was generated synthetically by winit
/// in one of the following circumstances: /// in one of the following circumstances:
/// ///
/// * Synthetic key press events are generated for all keys pressed when a window gains /// * Synthetic key press events are generated for all keys pressed
/// focus. Likewise, synthetic key release events are generated for all keys pressed when /// when a window gains focus. Likewise, synthetic key release events
/// a window goes out of focus. ***Currently, this is only functional on X11 and /// are generated for all keys pressed when a window goes out of focus.
/// Windows*** /// ***Currently, this is only functional on X11 and Windows***
/// ///
/// Otherwise, this value is always `false`. /// Otherwise, this value is always `false`.
is_synthetic: bool, is_synthetic: bool,
@@ -242,10 +262,9 @@ pub enum WindowEvent {
CursorMoved { CursorMoved {
device_id: DeviceId, device_id: DeviceId,
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range /// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is
/// of this data is limited by the display area and it may have been transformed by /// limited by the display area and it may have been transformed by the OS to implement effects such as cursor
/// the OS to implement effects such as cursor acceleration, it should not be used /// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
/// to implement non-cursor-like interactions such as 3D camera control.
position: PhysicalPosition<f64>, position: PhysicalPosition<f64>,
}, },
@@ -272,10 +291,18 @@ pub enum WindowEvent {
CursorLeft { device_id: DeviceId }, CursorLeft { device_id: DeviceId },
/// A mouse wheel movement or touchpad scroll occurred. /// A mouse wheel movement or touchpad scroll occurred.
MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase }, MouseWheel {
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
},
/// An mouse button press has been received. /// An mouse button press has been received.
MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton }, MouseInput {
device_id: DeviceId,
state: ElementState,
button: MouseButton,
},
/// Two-finger pinch gesture, often used for magnification. /// Two-finger pinch gesture, often used for magnification.
/// ///
@@ -293,19 +320,6 @@ pub enum WindowEvent {
phase: TouchPhase, phase: TouchPhase,
}, },
/// N-finger pan gesture
///
/// ## Platform-specific
///
/// - Only available on **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
PanGesture {
device_id: DeviceId,
/// Change in pixels of pan gesture from last update.
delta: PhysicalPosition<f32>,
phase: TouchPhase,
},
/// Double tap gesture. /// Double tap gesture.
/// ///
/// On a Mac, smart magnification is triggered by a double tap with two fingers /// On a Mac, smart magnification is triggered by a double tap with two fingers
@@ -337,7 +351,6 @@ pub enum WindowEvent {
/// - On iOS, not recognized by default. It must be enabled when needed. /// - On iOS, not recognized by default. It must be enabled when needed.
RotationGesture { RotationGesture {
device_id: DeviceId, device_id: DeviceId,
/// change in rotation in degrees
delta: f32, delta: f32,
phase: TouchPhase, phase: TouchPhase,
}, },
@@ -345,12 +358,20 @@ pub enum WindowEvent {
/// Touchpad pressure event. /// Touchpad pressure event.
/// ///
/// At the moment, only supported on Apple forcetouch-capable macbooks. /// At the moment, only supported on Apple forcetouch-capable macbooks.
/// The parameters are: pressure level (value between 0 and 1 representing how hard the /// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad
/// touchpad is being pressed) and stage (integer representing the click level). /// is being pressed) and stage (integer representing the click level).
TouchpadPressure { device_id: DeviceId, pressure: f32, stage: i64 }, TouchpadPressure {
device_id: DeviceId,
pressure: f32,
stage: i64,
},
/// Motion on some analog axis. May report data redundant to other, more specific events. /// Motion on some analog axis. May report data redundant to other, more specific events.
AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 }, AxisMotion {
device_id: DeviceId,
axis: AxisId,
value: f64,
},
/// Touch event has been received /// Touch event has been received
/// ///
@@ -372,8 +393,8 @@ pub enum WindowEvent {
/// * Changing the display's scale factor (e.g. in Control Panel on Windows). /// * Changing the display's scale factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different scale factor. /// * Moving the window to a display with a different scale factor.
/// ///
/// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the /// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the window is
/// window is resized to the value suggested by the OS, but it can be changed to any value. /// resized to the value suggested by the OS, but it can be changed to any value.
/// ///
/// For more information about DPI in general, see the [`dpi`] crate. /// For more information about DPI in general, see the [`dpi`] crate.
ScaleFactorChanged { ScaleFactorChanged {
@@ -389,8 +410,6 @@ pub enum WindowEvent {
/// Applications might wish to react to this to change the theme of the content of the window /// Applications might wish to react to this to change the theme of the content of the window
/// when the system changes the window theme. /// when the system changes the window theme.
/// ///
/// This only reports a change if the window theme was not overridden by [`Window::set_theme`].
///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported. /// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported.
@@ -405,11 +424,10 @@ pub enum WindowEvent {
/// ///
/// ### iOS /// ### iOS
/// ///
/// On iOS, the `Occluded(false)` event is emitted in response to an /// On iOS, the `Occluded(false)` event is emitted in response to an [`applicationWillEnterForeground`]
/// [`applicationWillEnterForeground`] callback which means the application should start /// callback which means the application should start preparing its data. The `Occluded(true)` event is
/// preparing its data. The `Occluded(true)` event is emitted in response to an /// emitted in response to an [`applicationDidEnterBackground`] callback which means the application
/// [`applicationDidEnterBackground`] callback which means the application should free /// should free resources (according to the [iOS application lifecycle]).
/// resources (according to the [iOS application lifecycle]).
/// ///
/// [`applicationWillEnterForeground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground /// [`applicationWillEnterForeground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground
/// [`applicationDidEnterBackground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground /// [`applicationDidEnterBackground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground
@@ -439,32 +457,34 @@ pub enum WindowEvent {
/// Identifier of an input device. /// Identifier of an input device.
/// ///
/// Whenever you receive an event arising from a particular input device, this event contains a /// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
/// `DeviceId` which identifies its origin. Note that devices may be virtual (representing an /// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
/// on-screen cursor and keyboard focus) or physical. Virtual devices typically aggregate inputs /// physical. Virtual devices typically aggregate inputs from multiple physical devices.
/// from multiple physical devices.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(pub(crate) platform_impl::DeviceId); pub struct DeviceId(pub(crate) platform_impl::DeviceId);
impl DeviceId { impl DeviceId {
/// Returns a dummy id, useful for unit testing. /// Returns a dummy id, useful for unit testing.
/// ///
/// # Notes /// # Safety
/// ///
/// The only guarantee made about the return value of this function is that /// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function. /// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `DeviceId`. /// No other guarantees are made. This may be equal to a real `DeviceId`.
pub const fn dummy() -> Self { ///
DeviceId(platform_impl::DeviceId::dummy()) /// **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. /// 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 /// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person
/// or first-person game controls. Many physical actions, such as mouse movement, can produce both /// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because
/// device and window events. Because window events typically arise from virtual devices /// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs
/// (corresponding to GUI cursors and keyboard focus) the device IDs may not match. /// may not match.
/// ///
/// Note that these events are delivered regardless of input focus. /// Note that these events are delivered regardless of input focus.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@@ -474,8 +494,7 @@ pub enum DeviceEvent {
/// Change in physical position of a pointing device. /// Change in physical position of a pointing device.
/// ///
/// This represents raw, unfiltered physical motion. Not to be confused with /// This represents raw, unfiltered physical motion. Not to be confused with [`WindowEvent::CursorMoved`].
/// [`WindowEvent::CursorMoved`].
MouseMotion { MouseMotion {
/// (x, y) change in position in unspecified units. /// (x, y) change in position in unspecified units.
/// ///
@@ -532,11 +551,11 @@ pub struct KeyEvent {
/// ## Caveats /// ## Caveats
/// ///
/// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that /// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that
/// implements DVORAK in hardware (or firmware) /// implements DVORAK in hardware (or firmware)
/// - Your application will likely have to handle keyboards which are missing keys that your /// - Your application will likely have to handle keyboards which are missing keys that your
/// own keyboard has. /// own keyboard has.
/// - Certain `KeyCode`s will move between a couple of different positions depending on what /// - Certain `KeyCode`s will move between a couple of different positions depending on what
/// layout the keyboard was manufactured to support. /// layout the keyboard was manufactured to support.
/// ///
/// **Because of these caveats, it is important that you provide users with a way to configure /// **Because of these caveats, it is important that you provide users with a way to configure
/// most (if not all) keybinds in your application.** /// most (if not all) keybinds in your application.**
@@ -558,7 +577,8 @@ pub struct KeyEvent {
/// ///
/// This has two use cases: /// This has two use cases:
/// - Allows querying whether the current input is a Dead key. /// - Allows querying whether the current input is a Dead key.
/// - Allows handling key-bindings on platforms which don't support [`key_without_modifiers`]. /// - Allows handling key-bindings on platforms which don't
/// support [`key_without_modifiers`].
/// ///
/// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard /// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard
/// shortcuts, **it is important that you provide users with a way to configure your /// shortcuts, **it is important that you provide users with a way to configure your
@@ -566,8 +586,8 @@ pub struct KeyEvent {
/// incompatible keyboard layout.** /// incompatible keyboard layout.**
/// ///
/// ## Platform-specific /// ## Platform-specific
/// - **Web:** Dead keys might be reported as the real key instead of `Dead` depending on the /// - **Web:** Dead keys might be reported as the real key instead
/// browser/OS. /// of `Dead` depending on the browser/OS.
/// ///
/// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
pub logical_key: keyboard::Key, pub logical_key: keyboard::Key,
@@ -595,13 +615,13 @@ pub struct KeyEvent {
/// Contains the location of this key on the keyboard. /// Contains the location of this key on the keyboard.
/// ///
/// Certain keys on the keyboard may appear in more than once place. For example, the "Shift" /// Certain keys on the keyboard may appear in more than once place. For example, the "Shift" key
/// key appears on the left side of the QWERTY keyboard as well as the right side. However, /// appears on the left side of the QWERTY keyboard as well as the right side. However, both keys
/// both keys have the same symbolic value. Another example of this phenomenon is the "1" /// have the same symbolic value. Another example of this phenomenon is the "1" key, which appears
/// key, which appears both above the "Q" key and as the "Keypad 1" key. /// both above the "Q" key and as the "Keypad 1" key.
/// ///
/// This field allows the user to differentiate between keys like this that have the same /// This field allows the user to differentiate between keys like this that have the same symbolic
/// symbolic value but different locations on the keyboard. /// value but different locations on the keyboard.
/// ///
/// See the [`KeyLocation`] type for more details. /// See the [`KeyLocation`] type for more details.
/// ///
@@ -616,40 +636,39 @@ pub struct KeyEvent {
/// Whether or not this key is a key repeat event. /// Whether or not this key is a key repeat event.
/// ///
/// On some systems, holding down a key for some period of time causes that key to be repeated /// On some systems, holding down a key for some period of time causes that key to be repeated
/// as though it were being pressed and released repeatedly. This field is `true` if and only /// as though it were being pressed and released repeatedly. This field is `true` if and only if
/// if this event is the result of one of those repeats. /// this event is the result of one of those repeats.
/// ///
/// # Example /// # Example
/// ///
/// In games, you often want to ignore repeated key events - this can be /// In games, you often want to ignore repated key events - this can be
/// done by ignoring events where this property is set. /// done by ignoring events where this property is set.
/// ///
/// ``` /// ```
/// use winit::event::{ElementState, KeyEvent, WindowEvent}; /// use winit::event::{WindowEvent, KeyEvent, ElementState};
/// use winit::keyboard::{KeyCode, PhysicalKey}; /// use winit::keyboard::{KeyCode, PhysicalKey};
/// # let window_event = WindowEvent::RedrawRequested; // To make the example compile /// # let window_event = WindowEvent::RedrawRequested; // To make the example compile
/// match window_event { /// match window_event {
/// WindowEvent::KeyboardInput { /// WindowEvent::KeyboardInput {
/// event: /// event: KeyEvent {
/// KeyEvent { /// physical_key: PhysicalKey::Code(KeyCode::KeyW),
/// physical_key: PhysicalKey::Code(KeyCode::KeyW), /// state: ElementState::Pressed,
/// state: ElementState::Pressed, /// repeat: false,
/// repeat: false, /// ..
/// .. /// },
/// },
/// .. /// ..
/// } => { /// } => {
/// // The physical key `W` was pressed, and it was not a repeat /// // The physical key `W` was pressed, and it was not a repeat
/// }, /// }
/// _ => {}, // Handle other events /// _ => {} // Handle other events
/// } /// }
/// ``` /// ```
pub repeat: bool, pub repeat: bool,
/// Platform-specific key event information. /// Platform-specific key event information.
/// ///
/// On Windows, Linux and macOS, this type contains the key without modifiers and the text with /// On Windows, Linux and macOS, this type contains the key without modifiers and the text with all
/// all modifiers applied. /// modifiers applied.
/// ///
/// On Android, iOS, Redox and Web, this type is a no-op. /// On Android, iOS, Redox and Web, this type is a no-op.
pub(crate) platform_specific: platform_impl::KeyEventExtra, pub(crate) platform_specific: platform_impl::KeyEventExtra,
@@ -723,7 +742,10 @@ impl Modifiers {
impl From<ModifiersState> for Modifiers { impl From<ModifiersState> for Modifiers {
fn from(value: ModifiersState) -> Self { fn from(value: ModifiersState) -> Self {
Self { state: value, pressed_mods: Default::default() } Self {
state: value,
pressed_mods: Default::default(),
}
} }
} }
@@ -731,16 +753,12 @@ impl From<ModifiersState> for Modifiers {
/// ///
/// This is also called a "composition event". /// This is also called a "composition event".
/// ///
/// Most keypresses using a latin-like keyboard layout simply generate a /// Most keypresses using a latin-like keyboard layout simply generate a [`WindowEvent::KeyboardInput`].
/// [`WindowEvent::KeyboardInput`]. However, one couldn't possibly have a key for every single /// However, one couldn't possibly have a key for every single unicode character that the user might want to type
/// unicode character that the user might want to type /// - so the solution operating systems employ is to allow the user to type these using _a sequence of keypresses_ instead.
/// - so the solution operating systems employ is to allow the user to type these using _a sequence
/// of keypresses_ instead.
///
/// A prominent example of this is accents - many keyboard layouts allow you to first click the
/// "accent key", and then the character you want to apply the accent to. In this case, some
/// platforms will generate the following event sequence:
/// ///
/// A prominent example of this is accents - many keyboard layouts allow you to first click the "accent key", and then
/// the character you want to apply the accent to. In this case, some platforms will generate the following event sequence:
/// ```ignore /// ```ignore
/// // Press "`" key /// // Press "`" key
/// Ime::Preedit("`", Some((0, 0))) /// Ime::Preedit("`", Some((0, 0)))
@@ -749,13 +767,11 @@ impl From<ModifiersState> for Modifiers {
/// Ime::Commit("é") /// Ime::Commit("é")
/// ``` /// ```
/// ///
/// Additionally, certain input devices are configured to display a candidate box that allow the /// Additionally, certain input devices are configured to display a candidate box that allow the user to select the
/// user to select the desired character interactively. (To properly position this box, you must use /// desired character interactively. (To properly position this box, you must use [`Window::set_ime_cursor_area`].)
/// [`Window::set_ime_cursor_area`].)
///
/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keyboard the
/// following event sequence could be obtained:
/// ///
/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keyboard the following event
/// sequence could be obtained:
/// ```ignore /// ```ignore
/// // Press "A" key /// // Press "A" key
/// Ime::Preedit("a", Some((1, 1))) /// Ime::Preedit("a", Some((1, 1)))
@@ -774,8 +790,8 @@ impl From<ModifiersState> for Modifiers {
pub enum Ime { pub enum Ime {
/// Notifies when the IME was enabled. /// Notifies when the IME was enabled.
/// ///
/// After getting this event you could receive [`Preedit`][Self::Preedit] and /// After getting this event you could receive [`Preedit`](Self::Preedit) and
/// [`Commit`][Self::Commit] events. You should also start performing IME related requests /// [`Commit`](Self::Commit) events. You should also start performing IME related requests
/// like [`Window::set_ime_cursor_area`]. /// like [`Window::set_ime_cursor_area`].
Enabled, Enabled,
@@ -795,10 +811,10 @@ pub enum Ime {
/// Notifies when the IME was disabled. /// Notifies when the IME was disabled.
/// ///
/// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or /// After receiving this event you won't get any more [`Preedit`](Self::Preedit) or
/// [`Commit`][Self::Commit] events until the next [`Enabled`][Self::Enabled] event. You should /// [`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 /// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear pending
/// pending preedit text. /// preedit text.
Disabled, Disabled,
} }
@@ -849,8 +865,8 @@ pub struct Touch {
/// ///
/// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**. /// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**.
/// - **Android**: This will never be [None]. If the device doesn't support pressure /// - **Android**: This will never be [None]. If the device doesn't support pressure
/// sensitivity, force will either be 0.0 or 1.0. Also see the /// sensitivity, force will either be 0.0 or 1.0. Also see the
/// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE). /// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE).
pub force: Option<Force>, pub force: Option<Force>,
/// Unique identifier of a finger. /// Unique identifier of a finger.
pub id: u64, pub id: u64,
@@ -897,13 +913,17 @@ impl Force {
/// consistent across devices. /// consistent across devices.
pub fn normalized(&self) -> f64 { pub fn normalized(&self) -> f64 {
match self { match self {
Force::Calibrated { force, max_possible_force, altitude_angle } => { Force::Calibrated {
force,
max_possible_force,
altitude_angle,
} => {
let force = match altitude_angle { let force = match altitude_angle {
Some(altitude_angle) => force / altitude_angle.sin(), Some(altitude_angle) => force / altitude_angle.sin(),
None => *force, None => *force,
}; };
force / max_possible_force force / max_possible_force
}, }
Force::Normalized(force) => *force, Force::Normalized(force) => *force,
} }
} }
@@ -1009,7 +1029,6 @@ impl PartialEq for InnerSizeWriter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::dpi::PhysicalPosition;
use crate::event; use crate::event;
use std::collections::{BTreeSet, HashSet}; use std::collections::{BTreeSet, HashSet};
@@ -1017,17 +1036,15 @@ mod tests {
($closure:expr) => {{ ($closure:expr) => {{
#[allow(unused_mut)] #[allow(unused_mut)]
let mut x = $closure; let mut x = $closure;
let did = event::DeviceId::dummy(); let did = unsafe { event::DeviceId::dummy() };
#[allow(deprecated)] #[allow(deprecated)]
{ {
use crate::event::Event::*; use crate::event::{Event::*, Ime::Enabled, WindowEvent::*};
use crate::event::Ime::Enabled;
use crate::event::WindowEvent::*;
use crate::window::WindowId; use crate::window::WindowId;
// Mainline events. // Mainline events.
let wid = WindowId::dummy(); let wid = unsafe { WindowId::dummy() };
x(UserEvent(())); x(UserEvent(()));
x(NewEvents(event::StartCause::Init)); x(NewEvents(event::StartCause::Init));
x(AboutToWait); x(AboutToWait);
@@ -1036,7 +1053,12 @@ mod tests {
x(Resumed); x(Resumed);
// Window events. // Window events.
let with_window_event = |wev| x(WindowEvent { window_id: wid, event: wev }); let with_window_event = |wev| {
x(WindowEvent {
window_id: wid,
event: wev,
})
};
with_window_event(CloseRequested); with_window_event(CloseRequested);
with_window_event(Destroyed); with_window_event(Destroyed);
@@ -1047,7 +1069,10 @@ mod tests {
with_window_event(HoveredFile("x.txt".into())); with_window_event(HoveredFile("x.txt".into()));
with_window_event(HoveredFileCancelled); with_window_event(HoveredFileCancelled);
with_window_event(Ime(Enabled)); with_window_event(Ime(Enabled));
with_window_event(CursorMoved { device_id: did, position: (0, 0).into() }); with_window_event(CursorMoved {
device_id: did,
position: (0, 0).into(),
});
with_window_event(ModifiersChanged(event::Modifiers::default())); with_window_event(ModifiersChanged(event::Modifiers::default()));
with_window_event(CursorEntered { device_id: did }); with_window_event(CursorEntered { device_id: did });
with_window_event(CursorLeft { device_id: did }); with_window_event(CursorLeft { device_id: did });
@@ -1072,13 +1097,16 @@ mod tests {
delta: 0.0, delta: 0.0,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
}); });
with_window_event(PanGesture { with_window_event(TouchpadPressure {
device_id: did, device_id: did,
delta: PhysicalPosition::<f32>::new(0.0, 0.0), pressure: 0.0,
phase: event::TouchPhase::Started, stage: 0,
});
with_window_event(AxisMotion {
device_id: did,
axis: 0,
value: 0.0,
}); });
with_window_event(TouchpadPressure { device_id: did, pressure: 0.0, stage: 0 });
with_window_event(AxisMotion { device_id: did, axis: 0, value: 0.0 });
with_window_event(Touch(event::Touch { with_window_event(Touch(event::Touch {
device_id: did, device_id: did,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
@@ -1094,17 +1122,29 @@ mod tests {
{ {
use event::DeviceEvent::*; use event::DeviceEvent::*;
let with_device_event = let with_device_event = |dev_ev| {
|dev_ev| x(event::Event::DeviceEvent { device_id: did, event: dev_ev }); x(event::Event::DeviceEvent {
device_id: did,
event: dev_ev,
})
};
with_device_event(Added); with_device_event(Added);
with_device_event(Removed); with_device_event(Removed);
with_device_event(MouseMotion { delta: (0.0, 0.0).into() }); with_device_event(MouseMotion {
delta: (0.0, 0.0).into(),
});
with_device_event(MouseWheel { with_device_event(MouseWheel {
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
}); });
with_device_event(Motion { axis: 0, value: 0.0 }); with_device_event(Motion {
with_device_event(Button { button: 0, state: event::ElementState::Pressed }); axis: 0,
value: 0.0,
});
with_device_event(Button {
button: 0,
state: event::ElementState::Pressed,
});
} }
}}; }};
} }
@@ -1136,8 +1176,11 @@ mod tests {
let force = event::Force::Normalized(0.0); let force = event::Force::Normalized(0.0);
assert_eq!(force.normalized(), 0.0); assert_eq!(force.normalized(), 0.0);
let force2 = let force2 = event::Force::Calibrated {
event::Force::Calibrated { force: 5.0, max_possible_force: 2.5, altitude_angle: None }; force: 5.0,
max_possible_force: 2.5,
altitude_angle: None,
};
assert_eq!(force2.normalized(), 2.0); assert_eq!(force2.normalized(), 2.0);
let force3 = event::Force::Calibrated { let force3 = event::Force::Calibrated {
@@ -1152,11 +1195,11 @@ mod tests {
#[test] #[test]
fn ensure_attrs_do_not_panic() { fn ensure_attrs_do_not_panic() {
foreach_event!(|event: event::Event<()>| { foreach_event!(|event: event::Event<()>| {
let _ = format!("{event:?}"); let _ = format!("{:?}", event);
}); });
let _ = event::StartCause::Init.clone(); let _ = event::StartCause::Init.clone();
let did = crate::event::DeviceId::dummy().clone(); let did = unsafe { crate::event::DeviceId::dummy() }.clone();
HashSet::new().insert(did); HashSet::new().insert(did);
let mut set = [did, did, did]; let mut set = [did, did, did];
set.sort_unstable(); set.sort_unstable();
@@ -1176,8 +1219,11 @@ mod tests {
force: Some(event::Force::Normalized(0.0)), force: Some(event::Force::Normalized(0.0)),
} }
.clone(); .clone();
let _ = let _ = event::Force::Calibrated {
event::Force::Calibrated { force: 0.0, max_possible_force: 0.0, altitude_angle: None } force: 0.0,
.clone(); max_possible_force: 0.0,
altitude_angle: None,
}
.clone();
} }
} }

View File

@@ -3,7 +3,7 @@
//! //!
//! If you want to send custom events to the event loop, use //! If you want to send custom events to the event loop, use
//! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its //! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its
//! [`send_event`][EventLoopProxy::send_event] method. //! [`send_event`](`EventLoopProxy::send_event`) method.
//! //!
//! See the root-level documentation for information on how to create and use an event loop to //! See the root-level documentation for information on how to create and use an event loop to
//! handle events. //! handle events.
@@ -20,10 +20,8 @@ use web_time::{Duration, Instant};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, OsError}; use crate::error::{EventLoopError, OsError};
use crate::event::Event; use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
use crate::monitor::MonitorHandle; use crate::{event::Event, monitor::MonitorHandle, platform_impl};
use crate::platform_impl;
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes};
/// Provides a way to retrieve events from the system and from the windows that were registered to /// Provides a way to retrieve events from the system and from the windows that were registered to
/// the events loop. /// the events loop.
@@ -35,8 +33,8 @@ use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttri
/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs. /// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs.
/// ///
/// Note that this cannot be shared across threads (due to platform-dependant logic /// Note that this cannot be shared across threads (due to platform-dependant logic
/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access, /// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access, the
/// the [`Window`] created from this _can_ be sent to an other thread, and the /// [`Window`] created from this _can_ be sent to an other thread, and the
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread. /// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
/// ///
/// [`Window`]: crate::window::Window /// [`Window`]: crate::window::Window
@@ -96,19 +94,18 @@ impl<T> EventLoopBuilder<T> {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY` or /// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY`
/// `DISPLAY` respectively when building the event loop. /// or `DISPLAY` respectively when building the event loop.
/// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling /// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling
/// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic. /// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic.
/// ///
/// [`platform`]: crate::platform /// [`platform`]: crate::platform
#[cfg_attr( #[cfg_attr(
android_platform, android,
doc = "[`.with_android_app(app)`]: \ doc = "[`.with_android_app(app)`]: crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
)] )]
#[cfg_attr( #[cfg_attr(
not(android_platform), not(android),
doc = "[`.with_android_app(app)`]: #only-available-on-android" doc = "[`.with_android_app(app)`]: #only-available-on-android"
)] )]
#[inline] #[inline]
@@ -165,9 +162,9 @@ pub enum ControlFlow {
/// When the current loop iteration finishes, suspend the thread until either another event /// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the given time is reached. /// arrives or the given time is reached.
/// ///
/// Useful for implementing efficient timers. Applications which want to render at the /// Useful for implementing efficient timers. Applications which want to render at the display's
/// display's native refresh rate should instead use [`Poll`] and the VSync functionality /// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
/// of a graphics API to reduce odds of missed frames. /// to reduce odds of missed frames.
/// ///
/// [`Poll`]: Self::Poll /// [`Poll`]: Self::Poll
WaitUntil(Instant), WaitUntil(Instant),
@@ -213,7 +210,10 @@ impl<T> EventLoop<T> {
/// Start building a new event loop, with the given type as the user event /// Start building a new event loop, with the given type as the user event
/// type. /// type.
pub fn with_user_event() -> EventLoopBuilder<T> { pub fn with_user_event() -> EventLoopBuilder<T> {
EventLoopBuilder { platform_specific: Default::default(), _p: PhantomData } EventLoopBuilder {
platform_specific: Default::default(),
_p: PhantomData,
}
} }
/// See [`run_app`]. /// See [`run_app`].
@@ -239,9 +239,9 @@ impl<T> EventLoop<T> {
/// ///
/// - **iOS:** Will never return to the caller and so values not passed to this function will /// - **iOS:** Will never return to the caller and so values not passed to this function will
/// *not* be dropped before the process exits. /// *not* be dropped before the process exits.
/// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript /// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript exception
/// exception (that Rust doesn't see) that will also mean that the rest of the function is /// (that Rust doesn't see) that will also mean that the rest of the function is never executed
/// never executed and any values not passed to this function will *not* be dropped. /// and any values not passed to this function will *not* be dropped.
/// ///
/// Web applications are recommended to use /// Web applications are recommended to use
#[cfg_attr( #[cfg_attr(
@@ -262,20 +262,25 @@ impl<T> EventLoop<T> {
#[inline] #[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))] #[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> { pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> {
self.event_loop.run(|event, event_loop| dispatch_event_for_app(app, event_loop, event)) self.event_loop
.run(|event, event_loop| dispatch_event_for_app(app, event_loop, event))
} }
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread. /// to the main event loop, possibly from another thread.
pub fn create_proxy(&self) -> EventLoopProxy<T> { pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy { event_loop_proxy: self.event_loop.create_proxy() } EventLoopProxy {
event_loop_proxy: self.event_loop.create_proxy(),
}
} }
/// Gets a persistent reference to the underlying platform display. /// Gets a persistent reference to the underlying platform display.
/// ///
/// See the [`OwnedDisplayHandle`] type for more information. /// See the [`OwnedDisplayHandle`] type for more information.
pub fn owned_display_handle(&self) -> OwnedDisplayHandle { pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle { platform: self.event_loop.window_target().p.owned_display_handle() } OwnedDisplayHandle {
platform: self.event_loop.window_target().p.owned_display_handle(),
}
} }
/// Change if or when [`DeviceEvent`]s are captured. /// Change if or when [`DeviceEvent`]s are captured.
@@ -290,12 +295,18 @@ impl<T> EventLoop<T> {
) )
.entered(); .entered();
self.event_loop.window_target().p.listen_device_events(allowed); self.event_loop
.window_target()
.p
.listen_device_events(allowed);
} }
/// Sets the [`ControlFlow`]. /// Sets the [`ControlFlow`].
pub fn set_control_flow(&self, control_flow: ControlFlow) { pub fn set_control_flow(&self, control_flow: ControlFlow) {
self.event_loop.window_target().p.set_control_flow(control_flow) self.event_loop
.window_target()
.p
.set_control_flow(control_flow)
} }
/// Create a window. /// Create a window.
@@ -318,7 +329,10 @@ impl<T> EventLoop<T> {
/// Create custom cursor. /// Create custom cursor.
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor { pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
self.event_loop.window_target().p.create_custom_cursor(custom_cursor) self.event_loop
.window_target()
.p
.create_custom_cursor(custom_cursor)
} }
} }
@@ -399,7 +413,10 @@ impl ActiveEventLoop {
let _span = tracing::debug_span!("winit::ActiveEventLoop::available_monitors",).entered(); let _span = tracing::debug_span!("winit::ActiveEventLoop::available_monitors",).entered();
#[allow(clippy::useless_conversion)] // false positive on some platforms #[allow(clippy::useless_conversion)] // false positive on some platforms
self.p.available_monitors().into_iter().map(|inner| MonitorHandle { inner }) self.p
.available_monitors()
.into_iter()
.map(|inner| MonitorHandle { inner })
} }
/// Returns the primary monitor of the system. /// Returns the primary monitor of the system.
@@ -413,7 +430,9 @@ impl ActiveEventLoop {
pub fn primary_monitor(&self) -> Option<MonitorHandle> { pub fn primary_monitor(&self) -> Option<MonitorHandle> {
let _span = tracing::debug_span!("winit::ActiveEventLoop::primary_monitor",).entered(); let _span = tracing::debug_span!("winit::ActiveEventLoop::primary_monitor",).entered();
self.p.primary_monitor().map(|inner| MonitorHandle { inner }) self.p
.primary_monitor()
.map(|inner| MonitorHandle { inner })
} }
/// Change if or when [`DeviceEvent`]s are captured. /// Change if or when [`DeviceEvent`]s are captured.
@@ -437,17 +456,6 @@ impl ActiveEventLoop {
self.p.listen_device_events(allowed); self.p.listen_device_events(allowed);
} }
/// Returns the current system theme.
///
/// Returns `None` if it cannot be determined on the current platform.
///
/// ## Platform-specific
///
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
pub fn system_theme(&self) -> Option<Theme> {
self.p.system_theme()
}
/// Sets the [`ControlFlow`]. /// Sets the [`ControlFlow`].
pub fn set_control_flow(&self, control_flow: ControlFlow) { pub fn set_control_flow(&self, control_flow: ControlFlow) {
self.p.set_control_flow(control_flow) self.p.set_control_flow(control_flow)
@@ -460,7 +468,7 @@ impl ActiveEventLoop {
/// This exits the event loop. /// This exits the event loop.
/// ///
/// See [`LoopExiting`][Event::LoopExiting]. /// See [`LoopExiting`](Event::LoopExiting).
pub fn exit(&self) { pub fn exit(&self) {
let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered(); let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered();
@@ -469,7 +477,7 @@ impl ActiveEventLoop {
/// Returns if the [`EventLoop`] is about to stop. /// Returns if the [`EventLoop`] is about to stop.
/// ///
/// See [`exit()`][Self::exit]. /// See [`exit()`](Self::exit).
pub fn exiting(&self) -> bool { pub fn exiting(&self) -> bool {
self.p.exiting() self.p.exiting()
} }
@@ -478,7 +486,9 @@ impl ActiveEventLoop {
/// ///
/// See the [`OwnedDisplayHandle`] type for more information. /// See the [`OwnedDisplayHandle`] type for more information.
pub fn owned_display_handle(&self) -> OwnedDisplayHandle { pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle { platform: self.p.owned_display_handle() } OwnedDisplayHandle {
platform: self.p.owned_display_handle(),
}
} }
} }
@@ -501,7 +511,7 @@ unsafe impl rwh_05::HasRawDisplayHandle for ActiveEventLoop {
/// A proxy for the underlying display handle. /// A proxy for the underlying display handle.
/// ///
/// The purpose of this type is to provide a cheaply cloneable handle to the underlying /// The purpose of this type is to provide a cheaply clonable handle to the underlying
/// display handle. This is often used by graphics APIs to connect to the underlying APIs. /// display handle. This is often used by graphics APIs to connect to the underlying APIs.
/// It is difficult to keep a handle to the [`EventLoop`] type or the [`ActiveEventLoop`] /// It is difficult to keep a handle to the [`EventLoop`] type or the [`ActiveEventLoop`]
/// type. In contrast, this type involves no lifetimes and can be persisted for as long as /// type. In contrast, this type involves no lifetimes and can be persisted for as long as
@@ -552,7 +562,9 @@ pub struct EventLoopProxy<T: 'static> {
impl<T: 'static> Clone for EventLoopProxy<T> { impl<T: 'static> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { event_loop_proxy: self.event_loop_proxy.clone() } Self {
event_loop_proxy: self.event_loop_proxy.clone(),
}
} }
} }

View File

@@ -1,6 +1,5 @@
use crate::platform_impl::PlatformIcon; use crate::platform_impl::PlatformIcon;
use std::error::Error; use std::{error::Error, fmt, io, mem};
use std::{fmt, io, mem};
#[repr(C)] #[repr(C)]
#[derive(Debug)] #[derive(Debug)]
@@ -21,7 +20,12 @@ pub enum BadIcon {
ByteCountNotDivisibleBy4 { byte_count: usize }, ByteCountNotDivisibleBy4 { byte_count: usize },
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`. /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
/// At least one of your arguments is incorrect. /// At least one of your arguments is incorrect.
DimensionsVsPixelCount { width: u32, height: u32, width_x_height: usize, pixel_count: usize }, DimensionsVsPixelCount {
width: u32,
height: u32,
width_x_height: usize,
pixel_count: usize,
},
/// Produced when underlying OS functionality failed to create the icon /// Produced when underlying OS functionality failed to create the icon
OsError(io::Error), OsError(io::Error),
} }
@@ -29,19 +33,17 @@ pub enum BadIcon {
impl fmt::Display for BadIcon { impl fmt::Display for BadIcon {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!( BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
f, "The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
"The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making \ ),
it impossible to interpret as 32bpp RGBA pixels.", BadIcon::DimensionsVsPixelCount {
width,
height,
width_x_height,
pixel_count,
} => write!(f,
"The specified dimensions ({width:?}x{height:?}) don't match the number of pixels supplied by the `rgba` argument ({pixel_count:?}). For those dimensions, the expected pixel count is {width_x_height:?}.",
), ),
BadIcon::DimensionsVsPixelCount { width, height, width_x_height, pixel_count } => {
write!(
f,
"The specified dimensions ({width:?}x{height:?}) don't match the number of \
pixels supplied by the `rgba` argument ({pixel_count:?}). For those \
dimensions, the expected pixel count is {width_x_height:?}.",
)
},
BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {e:?}"), BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {e:?}"),
} }
} }
@@ -67,7 +69,9 @@ mod constructors {
impl RgbaIcon { impl RgbaIcon {
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> { pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
if rgba.len() % PIXEL_SIZE != 0 { if rgba.len() % PIXEL_SIZE != 0 {
return Err(BadIcon::ByteCountNotDivisibleBy4 { byte_count: rgba.len() }); return Err(BadIcon::ByteCountNotDivisibleBy4 {
byte_count: rgba.len(),
});
} }
let pixel_count = rgba.len() / PIXEL_SIZE; let pixel_count = rgba.len() / PIXEL_SIZE;
if pixel_count != (width * height) as usize { if pixel_count != (width * height) as usize {
@@ -78,7 +82,11 @@ mod constructors {
pixel_count, pixel_count,
}) })
} else { } else {
Ok(RgbaIcon { rgba, width, height }) Ok(RgbaIcon {
rgba,
width,
height,
})
} }
} }
} }
@@ -112,6 +120,8 @@ impl Icon {
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> { pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
let _span = tracing::debug_span!("winit::Icon::from_rgba", width, height).entered(); let _span = tracing::debug_span!("winit::Icon::from_rgba", width, height).entered();
Ok(Icon { inner: PlatformIcon::from_rgba(rgba, width, height)? }) Ok(Icon {
inner: PlatformIcon::from_rgba(rgba, width, height)?,
})
} }
} }

View File

@@ -106,23 +106,23 @@ impl std::fmt::Debug for NativeKeyCode {
match self { match self {
Unidentified => { Unidentified => {
debug_tuple = f.debug_tuple("Unidentified"); debug_tuple = f.debug_tuple("Unidentified");
}, }
Android(code) => { Android(code) => {
debug_tuple = f.debug_tuple("Android"); debug_tuple = f.debug_tuple("Android");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
MacOS(code) => { MacOS(code) => {
debug_tuple = f.debug_tuple("MacOS"); debug_tuple = f.debug_tuple("MacOS");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Windows(code) => { Windows(code) => {
debug_tuple = f.debug_tuple("Windows"); debug_tuple = f.debug_tuple("Windows");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Xkb(code) => { Xkb(code) => {
debug_tuple = f.debug_tuple("Xkb"); debug_tuple = f.debug_tuple("Xkb");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
} }
debug_tuple.finish() debug_tuple.finish()
} }
@@ -162,27 +162,27 @@ impl std::fmt::Debug for NativeKey {
match self { match self {
Unidentified => { Unidentified => {
debug_tuple = f.debug_tuple("Unidentified"); debug_tuple = f.debug_tuple("Unidentified");
}, }
Android(code) => { Android(code) => {
debug_tuple = f.debug_tuple("Android"); debug_tuple = f.debug_tuple("Android");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
MacOS(code) => { MacOS(code) => {
debug_tuple = f.debug_tuple("MacOS"); debug_tuple = f.debug_tuple("MacOS");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Windows(code) => { Windows(code) => {
debug_tuple = f.debug_tuple("Windows"); debug_tuple = f.debug_tuple("Windows");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Xkb(code) => { Xkb(code) => {
debug_tuple = f.debug_tuple("Xkb"); debug_tuple = f.debug_tuple("Xkb");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Web(code) => { Web(code) => {
debug_tuple = f.debug_tuple("Web"); debug_tuple = f.debug_tuple("Web");
debug_tuple.field(code); debug_tuple.field(code);
}, }
} }
debug_tuple.finish() debug_tuple.finish()
} }
@@ -218,7 +218,7 @@ impl PartialEq<NativeKeyCode> for NativeKey {
/// Represents the location of a physical key. /// Represents the location of a physical key.
/// ///
/// This type is a superset of [`KeyCode`], including an [`Unidentified`][Self::Unidentified] /// This type is a superset of [`KeyCode`], including an [`Unidentified`](Self::Unidentified)
/// variant. /// variant.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -442,8 +442,7 @@ pub enum KeyCode {
Tab, Tab,
/// Japanese: <kbd>変</kbd> (henkan) /// Japanese: <kbd>変</kbd> (henkan)
Convert, Convert,
/// Japanese: <kbd>カタカナ</kbd>/<kbd>ひらがな</kbd>/<kbd>ローマ字</kbd> /// Japanese: <kbd>カタカナ</kbd>/<kbd>ひらがな</kbd>/<kbd>ローマ字</kbd> (katakana/hiragana/romaji)
/// (katakana/hiragana/romaji)
KanaMode, KanaMode,
/// Korean: HangulMode <kbd>한/영</kbd> (han/yeong) /// Korean: HangulMode <kbd>한/영</kbd> (han/yeong)
/// ///
@@ -491,8 +490,7 @@ pub enum KeyCode {
NumLock, NumLock,
/// <kbd>0 Ins</kbd> on a keyboard. <kbd>0</kbd> on a phone or remote control /// <kbd>0 Ins</kbd> on a keyboard. <kbd>0</kbd> on a phone or remote control
Numpad0, Numpad0,
/// <kbd>1 End</kbd> on a keyboard. <kbd>1</kbd> or <kbd>1 QZ</kbd> on a phone or remote /// <kbd>1 End</kbd> on a keyboard. <kbd>1</kbd> or <kbd>1 QZ</kbd> on a phone or remote control
/// control
Numpad1, Numpad1,
/// <kbd>2 ↓</kbd> on a keyboard. <kbd>2 ABC</kbd> on a phone or remote control /// <kbd>2 ↓</kbd> on a keyboard. <kbd>2 ABC</kbd> on a phone or remote control
Numpad2, Numpad2,
@@ -796,14 +794,13 @@ pub enum NamedKey {
// Legacy modifier key. // Legacy modifier key.
Hyper, Hyper,
/// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard /// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard
/// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` /// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` key.
/// key.
/// ///
/// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key. /// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key.
Super, Super,
/// The `Enter` or `↵` key. Used to activate current selection or accept current input. This /// The `Enter` or `↵` key. Used to activate current selection or accept current input. This key
/// key value is also used for the `Return` (Macintosh numpad) key. This key value is also /// value is also used for the `Return` (Macintosh numpad) key. This key value is also used for
/// used for the Android `KEYCODE_DPAD_CENTER`. /// the Android `KEYCODE_DPAD_CENTER`.
Enter, Enter,
/// The Horizontal Tabulation `Tab` key. /// The Horizontal Tabulation `Tab` key.
Tab, Tab,
@@ -839,8 +836,8 @@ pub enum NamedKey {
CrSel, CrSel,
/// Cut the current selection. (`APPCOMMAND_CUT`) /// Cut the current selection. (`APPCOMMAND_CUT`)
Cut, Cut,
/// Used to delete the character to the right of the cursor. This key value is also used for /// Used to delete the character to the right of the cursor. This key value is also used for the
/// the key labeled `Delete` on MacOS keyboards when `Fn` is active. /// key labeled `Delete` on MacOS keyboards when `Fn` is active.
Delete, Delete,
/// The Erase to End of Field key. This key deletes all characters from the current cursor /// The Erase to End of Field key. This key deletes all characters from the current cursor
/// position to the end of the current field. /// position to the end of the current field.
@@ -924,8 +921,8 @@ pub enum NamedKey {
/// their code points. /// their code points.
CodeInput, CodeInput,
/// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a /// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a
/// manner similar to a dead key, triggering a mode where subsequent key presses are combined /// manner similar to a dead key, triggering a mode where subsequent key presses are combined to
/// to produce a different character. /// produce a different character.
Compose, Compose,
/// Convert the current input method sequence. /// Convert the current input method sequence.
Convert, Convert,
@@ -964,9 +961,9 @@ pub enum NamedKey {
/// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from /// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from
/// romaji mode). /// romaji mode).
KanaMode, KanaMode,
/// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key is
/// is typically used to switch to a hiragana keyboard for the purpose of converting input /// typically used to switch to a hiragana keyboard for the purpose of converting input into
/// into kanji. (`KEYCODE_KANA`) /// kanji. (`KEYCODE_KANA`)
KanjiMode, KanjiMode,
/// The Katakana (Japanese Kana characters) key. /// The Katakana (Japanese Kana characters) key.
Katakana, Katakana,
@@ -1232,7 +1229,7 @@ pub enum NamedKey {
Dimmer, Dimmer,
/// Swap video sources. (`VK_DISPLAY_SWAP`) /// Swap video sources. (`VK_DISPLAY_SWAP`)
DisplaySwap, DisplaySwap,
/// Select Digital Video Recorder. (`KEYCODE_DVR`) /// Select Digital Video Rrecorder. (`KEYCODE_DVR`)
DVR, DVR,
/// Exit the current application. (`VK_EXIT`) /// Exit the current application. (`VK_EXIT`)
Exit, Exit,
@@ -1591,7 +1588,7 @@ impl Key {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use winit::keyboard::{Key, NamedKey}; /// use winit::keyboard::{NamedKey, Key};
/// ///
/// assert_eq!(Key::Character("a".into()).to_text(), Some("a")); /// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
/// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r")); /// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
@@ -1613,8 +1610,7 @@ impl Key {
/// keys can be above the letters or on the numpad. This enum allows the user to differentiate /// keys can be above the letters or on the numpad. This enum allows the user to differentiate
/// them. /// them.
/// ///
/// See the documentation for the [`location`] field on the [`KeyEvent`] struct for more /// See the documentation for the [`location`] field on the [`KeyEvent`] struct for more information.
/// information.
/// ///
/// [`location`]: ../event/struct.KeyEvent.html#structfield.location /// [`location`]: ../event/struct.KeyEvent.html#structfield.location
/// [`KeyEvent`]: crate::event::KeyEvent /// [`KeyEvent`]: crate::event::KeyEvent
@@ -1623,8 +1619,8 @@ impl Key {
pub enum KeyLocation { pub enum KeyLocation {
/// The key is in its "normal" location on the keyboard. /// The key is in its "normal" location on the keyboard.
/// ///
/// For instance, the "1" key above the "Q" key on a QWERTY keyboard will use this location. /// For instance, the "1" key above the "Q" key on a QWERTY keyboard will use this location. This
/// This invariant is also returned when the location of the key cannot be identified. /// invariant is also returned when the location of the key cannot be identified.
/// ///
/// ![Standard 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_standard_1_key.svg) /// ![Standard 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_standard_1_key.svg)
/// ///
@@ -1707,17 +1703,14 @@ impl ModifiersState {
pub fn shift_key(&self) -> bool { pub fn shift_key(&self) -> bool {
self.intersects(Self::SHIFT) self.intersects(Self::SHIFT)
} }
/// Returns `true` if the control key is pressed. /// Returns `true` if the control key is pressed.
pub fn control_key(&self) -> bool { pub fn control_key(&self) -> bool {
self.intersects(Self::CONTROL) self.intersects(Self::CONTROL)
} }
/// Returns `true` if the alt key is pressed. /// Returns `true` if the alt key is pressed.
pub fn alt_key(&self) -> bool { pub fn alt_key(&self) -> bool {
self.intersects(Self::ALT) self.intersects(Self::ALT)
} }
/// Returns `true` if the super key is pressed. /// Returns `true` if the super key is pressed.
pub fn super_key(&self) -> bool { pub fn super_key(&self) -> bool {
self.intersects(Self::SUPER) self.intersects(Self::SUPER)
@@ -1791,8 +1784,12 @@ mod modifiers_serde {
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let ModifiersStateSerialize { shift_key, control_key, alt_key, super_key } = let ModifiersStateSerialize {
ModifiersStateSerialize::deserialize(deserializer)?; shift_key,
control_key,
alt_key,
super_key,
} = ModifiersStateSerialize::deserialize(deserializer)?;
let mut m = ModifiersState::empty(); let mut m = ModifiersState::empty();
m.set(ModifiersState::SHIFT, shift_key); m.set(ModifiersState::SHIFT, shift_key);
m.set(ModifiersState::CONTROL, control_key); m.set(ModifiersState::CONTROL, control_key);

View File

@@ -7,12 +7,7 @@
//! //!
//! ```no_run //! ```no_run
//! use winit::event_loop::EventLoop; //! use winit::event_loop::EventLoop;
//! //! let event_loop = EventLoop::new().unwrap();
//! # // Intentionally use `fn main` for clarity
//! fn main() {
//! let event_loop = EventLoop::new().unwrap();
//! // ...
//! }
//! ``` //! ```
//! //!
//! Then you create a [`Window`] with [`create_window`]. //! Then you create a [`Window`] with [`create_window`].
@@ -24,22 +19,33 @@
//! window or a key getting pressed while the window is focused. Devices can generate //! window or a key getting pressed while the window is focused. Devices can generate
//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window. //! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window.
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a //! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if //! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if desired.
//! desired.
//! //!
//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will //! You can retrieve events by calling [`EventLoop::run_app()`]. This function will
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and //! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
//! will run until [`exit()`] is used, at which point [`Event::LoopExiting`]. //! will run until [`exit()`] is used, at which point [`Event::LoopExiting`].
//! //!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop //! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works //! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on
//! poorly on most other platforms. However, this model can be re-implemented to an extent with //! most other platforms. However, this model can be re-implemented to an extent with
#![cfg_attr( #![cfg_attr(
any(windows_platform, macos_platform, android_platform, x11_platform, wayland_platform), any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
),
doc = "[`EventLoopExtPumpEvents::pump_app_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_app_events()]" doc = "[`EventLoopExtPumpEvents::pump_app_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_app_events()]"
)] )]
#![cfg_attr( #![cfg_attr(
not(any(windows_platform, macos_platform, android_platform, x11_platform, wayland_platform)), not(any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
)),
doc = "`EventLoopExtPumpEvents::pump_app_events()`" doc = "`EventLoopExtPumpEvents::pump_app_events()`"
)] )]
//! [^1]. See that method's documentation for more reasons about why //! [^1]. See that method's documentation for more reasons about why
@@ -89,22 +95,19 @@
//! } //! }
//! } //! }
//! //!
//! # // Intentionally use `fn main` for clarity //! let event_loop = EventLoop::new().unwrap();
//! fn main() {
//! let event_loop = EventLoop::new().unwrap();
//! //!
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't //! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications. //! // dispatched any events. This is ideal for games and similar applications.
//! event_loop.set_control_flow(ControlFlow::Poll); //! event_loop.set_control_flow(ControlFlow::Poll);
//! //!
//! // ControlFlow::Wait pauses the event loop if no events are available to process. //! // ControlFlow::Wait pauses the event loop if no events are available to process.
//! // This is ideal for non-game applications that only update in response to user //! // This is ideal for non-game applications that only update in response to user
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll. //! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! event_loop.set_control_flow(ControlFlow::Wait); //! event_loop.set_control_flow(ControlFlow::Wait);
//! //!
//! let mut app = App::default(); //! let mut app = App::default();
//! event_loop.run_app(&mut app); //! event_loop.run_app(&mut app);
//! }
//! ``` //! ```
//! //!
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be //! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
@@ -113,16 +116,16 @@
//! //!
//! # Drawing on the window //! # Drawing on the window
//! //!
//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However, it allows you //! Winit doesn't directly provide any methods for drawing on a [`Window`]. However, it allows you to
//! to retrieve the raw handle of the window and display (see the [`platform`] module and/or the //! retrieve the raw handle of the window and display (see the [`platform`] module and/or the
//! [`raw_window_handle`] and [`raw_display_handle`] methods), which in turn allows //! [`raw_window_handle`] and [`raw_display_handle`] methods), which in turn allows
//! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics. //! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
//! //!
//! Note that many platforms will display garbage data in the window's client area if the //! Note that many platforms will display garbage data in the window's client area if the
//! application doesn't render anything to the window by the time the desktop compositor is ready to //! application doesn't render anything to the window by the time the desktop compositor is ready to
//! display the window to the user. If you notice this happening, you should create the window with //! display the window to the user. If you notice this happening, you should create the window with
//! [`visible` set to `false`][crate::window::WindowAttributes::with_visible] and explicitly make //! [`visible` set to `false`](crate::window::WindowAttributes::with_visible) and explicitly make the
//! the window visible only once you're ready to render into it. //! window visible only once you're ready to render into it.
//! //!
//! # UI scaling //! # UI scaling
//! //!
@@ -148,11 +151,13 @@
//! Winit provides the following Cargo features: //! Winit provides the following Cargo features:
//! //!
//! * `x11` (enabled by default): On Unix platforms, enables the X11 backend. //! * `x11` (enabled by default): On Unix platforms, enables the X11 backend.
//! * `wayland` (enabled by default): On Unix platforms, enables the Wayland backend. //! * `wayland` (enabled by default): On Unix platforms, enables the Wayland
//! backend.
//! * `rwh_04`: Implement `raw-window-handle v0.4` traits. //! * `rwh_04`: Implement `raw-window-handle v0.4` traits.
//! * `rwh_05`: Implement `raw-window-handle v0.5` traits. //! * `rwh_05`: Implement `raw-window-handle v0.5` traits.
//! * `rwh_06`: Implement `raw-window-handle v0.6` traits. //! * `rwh_06`: Implement `raw-window-handle v0.6` traits.
//! * `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde). //! * `serde`: Enables serialization/deserialization of certain types with
//! [Serde](https://crates.io/crates/serde).
//! * `mint`: Enables mint (math interoperability standard types) conversions. //! * `mint`: Enables mint (math interoperability standard types) conversions.
//! //!
//! See the [`platform`] module for documentation on platform-specific cargo //! See the [`platform`] module for documentation on platform-specific cargo
@@ -165,6 +170,7 @@
//! [`Window`]: window::Window //! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId //! [`WindowId`]: window::WindowId
//! [`WindowAttributes`]: window::WindowAttributes //! [`WindowAttributes`]: window::WindowAttributes
//! [window_new]: window::Window::new
//! [`create_window`]: event_loop::ActiveEventLoop::create_window //! [`create_window`]: event_loop::ActiveEventLoop::create_window
//! [`Window::id()`]: window::Window::id //! [`Window::id()`]: window::Window::id
//! [`WindowEvent`]: event::WindowEvent //! [`WindowEvent`]: event::WindowEvent
@@ -180,18 +186,14 @@
#![deny(clippy::all)] #![deny(clippy::all)]
#![deny(unsafe_op_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)]
#![cfg_attr(clippy, deny(warnings))] #![cfg_attr(clippy, deny(warnings))]
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly // Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
// doc #![cfg_attr(
#![cfg_attr(docsrs, feature(doc_cfg), doc(auto_cfg(hide(doc, docsrs))))] docsrs,
feature(doc_auto_cfg, doc_cfg_hide),
doc(cfg_hide(doc, docsrs))
)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#![warn(clippy::uninlined_format_args)]
// TODO: wasm-binding needs to be updated for that to be resolved, for now just silence it.
#![cfg_attr(web_platform, allow(unknown_lints, renamed_and_removed_lints, wasm_c_abi))]
#[cfg(feature = "rwh_04")]
pub use rwh_04 as raw_window_handle_04;
#[cfg(feature = "rwh_05")]
pub use rwh_05 as raw_window_handle_05;
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
pub use rwh_06 as raw_window_handle; pub use rwh_06 as raw_window_handle;
@@ -200,8 +202,6 @@ pub use rwh_06 as raw_window_handle;
pub use dpi; pub use dpi;
pub mod application; pub mod application;
#[cfg(any(doc, doctest, test))]
pub mod changelog;
#[macro_use] #[macro_use]
pub mod error; pub mod error;
mod cursor; mod cursor;

View File

@@ -3,10 +3,12 @@
//! If you want to get basic information about a monitor, you can use the //! If you want to get basic information about a monitor, you can use the
//! [`MonitorHandle`] type. This is retrieved from one of the following //! [`MonitorHandle`] type. This is retrieved from one of the following
//! methods, which return an iterator of [`MonitorHandle`]: //! methods, which return an iterator of [`MonitorHandle`]:
//! - [`ActiveEventLoop::available_monitors`][crate::event_loop::ActiveEventLoop::available_monitors]. //! - [`ActiveEventLoop::available_monitors`](crate::event_loop::ActiveEventLoop::available_monitors).
//! - [`Window::available_monitors`][crate::window::Window::available_monitors]. //! - [`Window::available_monitors`](crate::window::Window::available_monitors).
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::{
use crate::platform_impl; dpi::{PhysicalPosition, PhysicalSize},
platform_impl,
};
/// Deprecated! Use `VideoModeHandle` instead. /// Deprecated! Use `VideoModeHandle` instead.
#[deprecated = "Renamed to `VideoModeHandle`"] #[deprecated = "Renamed to `VideoModeHandle`"]
@@ -77,7 +79,9 @@ impl VideoModeHandle {
/// a separate set of valid video modes. /// a separate set of valid video modes.
#[inline] #[inline]
pub fn monitor(&self) -> MonitorHandle { pub fn monitor(&self) -> MonitorHandle {
MonitorHandle { inner: self.video_mode.monitor() } MonitorHandle {
inner: self.video_mode.monitor(),
}
} }
} }
@@ -162,6 +166,8 @@ impl MonitorHandle {
/// - **Web:** Always returns an empty iterator /// - **Web:** Always returns an empty iterator
#[inline] #[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
self.inner.video_modes().map(|video_mode| VideoModeHandle { video_mode }) self.inner
.video_modes()
.map(|video_mode| VideoModeHandle { video_mode })
} }
} }

View File

@@ -19,7 +19,6 @@
//! //!
//! | winit | ndk-glue | //! | winit | ndk-glue |
//! | :---: | :--------------------------: | //! | :---: | :--------------------------: |
//! | 0.30 | `android-activity = "0.6"` |
//! | 0.29 | `android-activity = "0.5"` | //! | 0.29 | `android-activity = "0.5"` |
//! | 0.28 | `android-activity = "0.4"` | //! | 0.28 | `android-activity = "0.4"` |
//! | 0.27 | `ndk-glue = "0.7"` | //! | 0.27 | `ndk-glue = "0.7"` |
@@ -59,39 +58,26 @@
//! //!
//! ## Converting from `ndk-glue` to `android-activity` //! ## Converting from `ndk-glue` to `android-activity`
//! //!
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building //! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
//! with `cargo apk`, then the minimal changes would be:
//! 1. Remove `ndk-glue` from your `Cargo.toml` //! 1. Remove `ndk-glue` from your `Cargo.toml`
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.13", //! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.15", features = [ "android-native-activity" ] }`
//! 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).
//! 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc //! 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
//! 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).
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; use crate::{
use crate::window::{Window, WindowAttributes}; event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder},
window::{Window, WindowAttributes},
};
use self::activity::{AndroidApp, ConfigurationRef, Rect}; use self::activity::{AndroidApp, ConfigurationRef, Rect};
/// Additional methods on [`EventLoop`] that are specific to Android. /// Additional methods on [`EventLoop`] that are specific to Android.
pub trait EventLoopExtAndroid { pub trait EventLoopExtAndroid {}
/// Get the [`AndroidApp`] which was used to create this event loop.
fn android_app(&self) -> &AndroidApp;
}
impl<T> EventLoopExtAndroid for EventLoop<T> { impl<T> EventLoopExtAndroid for EventLoop<T> {}
fn android_app(&self) -> &AndroidApp {
&self.event_loop.android_app
}
}
/// Additional methods on [`ActiveEventLoop`] that are specific to Android. /// Additional methods on [`ActiveEventLoop`] that are specific to Android.
pub trait ActiveEventLoopExtAndroid { pub trait ActiveEventLoopExtAndroid {}
/// Get the [`AndroidApp`] which was used to create this event loop.
fn android_app(&self) -> &AndroidApp;
}
/// Additional methods on [`Window`] that are specific to Android. /// Additional methods on [`Window`] that are specific to Android.
pub trait WindowExtAndroid { pub trait WindowExtAndroid {
@@ -110,11 +96,7 @@ impl WindowExtAndroid for Window {
} }
} }
impl ActiveEventLoopExtAndroid for ActiveEventLoop { impl ActiveEventLoopExtAndroid for ActiveEventLoop {}
fn android_app(&self) -> &AndroidApp {
&self.p.app
}
}
/// Additional methods on [`WindowAttributes`] that are specific to Android. /// Additional methods on [`WindowAttributes`] that are specific to Android.
pub trait WindowAttributesExtAndroid {} pub trait WindowAttributesExtAndroid {}
@@ -122,9 +104,9 @@ pub trait WindowAttributesExtAndroid {}
impl WindowAttributesExtAndroid for WindowAttributes {} impl WindowAttributesExtAndroid for WindowAttributes {}
pub trait EventLoopBuilderExtAndroid { pub trait EventLoopBuilderExtAndroid {
/// Associates the [`AndroidApp`] that was passed to `android_main()` with the event loop /// Associates the `AndroidApp` that was passed to `android_main()` with the event loop
/// ///
/// This must be called on Android since the [`AndroidApp`] is not global state. /// This must be called on Android since the `AndroidApp` is not global state.
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self; fn with_android_app(&mut self, app: AndroidApp) -> &mut Self;
/// Calling this will mark the volume keys to be manually handled by the application /// Calling this will mark the volume keys to be manually handled by the application
@@ -161,10 +143,10 @@ impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
/// depending on the `android_activity` crate, and instead consume the API that /// depending on the `android_activity` crate, and instead consume the API that
/// is re-exported by Winit. /// is re-exported by Winit.
/// ///
/// For compatibility applications should then import the [`AndroidApp`] type for /// For compatibility applications should then import the `AndroidApp` type for
/// their `android_main(app: AndroidApp)` function like: /// their `android_main(app: AndroidApp)` function like:
/// ```rust /// ```rust
/// #[cfg(target_os = "android")] /// #[cfg(target_os="android")]
/// use winit::platform::android::activity::AndroidApp; /// use winit::platform::android::activity::AndroidApp;
/// ``` /// ```
pub mod activity { pub mod activity {

View File

@@ -3,14 +3,11 @@
//! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on //! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on
//! iOS 9.3. //! iOS 9.3.
//! //!
//! ## Window initialization
//!
//! iOS's main `UIApplicationMain` does some init work that's required by all //! iOS's main `UIApplicationMain` does some init work that's required by all
//! UI-related code (see issue [#1705]). It is best to create your windows //! UI-related code (see issue [#1705]). It is best to create your windows
//! inside [`ApplicationHandler::resumed`]. //! inside `Event::Resumed`.
//! //!
//! [#1705]: https://github.com/rust-windowing/winit/issues/1705 //! [#1705]: https://github.com/rust-windowing/winit/issues/1705
//! [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
//! //!
//! ## Building app //! ## Building app
//! //!
@@ -66,22 +63,14 @@
//! opengl will result in segfault. //! opengl will result in segfault.
//! //!
//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed. //! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
//!
//! ## Custom `UIApplicationDelegate`
//!
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
//! though, you might want to access some of the more niche stuff that [the application
//! delegate][app-delegate] provides. This functionality is not exposed directly in Winit, since it
//! would increase the API surface by quite a lot. Instead, Winit guarantees that it will not
//! register an application delegate, so you can set up a custom one in a nib file instead.
//!
//! [app-delegate]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate?language=objc
use std::os::raw::c_void; use std::os::raw::c_void;
use crate::event_loop::EventLoop; use crate::{
use crate::monitor::{MonitorHandle, VideoModeHandle}; event_loop::EventLoop,
use crate::window::{Window, WindowAttributes}; monitor::{MonitorHandle, VideoModeHandle},
window::{Window, WindowAttributes},
};
/// Additional methods on [`EventLoop`] that are specific to iOS. /// Additional methods on [`EventLoop`] that are specific to iOS.
pub trait EventLoopExtIOS { pub trait EventLoopExtIOS {
@@ -168,21 +157,6 @@ pub trait WindowExtIOS {
/// The default is to not recognize gestures. /// The default is to not recognize gestures.
fn recognize_pinch_gesture(&self, should_recognize: bool); fn recognize_pinch_gesture(&self, should_recognize: bool);
/// Sets whether the [`Window`] should recognize pan gestures.
///
/// The default is to not recognize gestures.
/// Installs [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer) onto view
///
/// Set the minimum number of touches required: [`minimumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-minimumnumberoftouches)
///
/// Set the maximum number of touches recognized: [`maximumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-maximumnumberoftouches)
fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
);
/// Sets whether the [`Window`] should recognize double tap gestures. /// Sets whether the [`Window`] should recognize double tap gestures.
/// ///
/// The default is to not recognize gestures. /// The default is to not recognize gestures.
@@ -197,17 +171,20 @@ pub trait WindowExtIOS {
impl WindowExtIOS for Window { impl WindowExtIOS for Window {
#[inline] #[inline]
fn set_scale_factor(&self, scale_factor: f64) { fn set_scale_factor(&self, scale_factor: f64) {
self.window.maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor)) self.window
.maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor))
} }
#[inline] #[inline]
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) { fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
self.window.maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations)) self.window
.maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations))
} }
#[inline] #[inline]
fn set_prefers_home_indicator_hidden(&self, hidden: bool) { fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
self.window.maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden)) self.window
.maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden))
} }
#[inline] #[inline]
@@ -219,43 +196,32 @@ impl WindowExtIOS for Window {
#[inline] #[inline]
fn set_prefers_status_bar_hidden(&self, hidden: bool) { fn set_prefers_status_bar_hidden(&self, hidden: bool) {
self.window.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden)) self.window
.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
} }
#[inline] #[inline]
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) { fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
self.window.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style)) self.window
.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
} }
#[inline] #[inline]
fn recognize_pinch_gesture(&self, should_recognize: bool) { fn recognize_pinch_gesture(&self, should_recognize: bool) {
self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize)); self.window
} .maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
#[inline]
fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
) {
self.window.maybe_queue_on_main(move |w| {
w.recognize_pan_gesture(
should_recognize,
minimum_number_of_touches,
maximum_number_of_touches,
)
});
} }
#[inline] #[inline]
fn recognize_doubletap_gesture(&self, should_recognize: bool) { fn recognize_doubletap_gesture(&self, should_recognize: bool) {
self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize)); self.window
.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
} }
#[inline] #[inline]
fn recognize_rotation_gesture(&self, should_recognize: bool) { fn recognize_rotation_gesture(&self, should_recognize: bool) {
self.window.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize)); self.window
.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
} }
} }
@@ -335,7 +301,8 @@ impl WindowAttributesExtIOS for WindowAttributes {
#[inline] #[inline]
fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self { fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
self.platform_specific.preferred_screen_edges_deferring_system_gestures = edges; self.platform_specific
.preferred_screen_edges_deferring_system_gestures = edges;
self self
} }
@@ -369,13 +336,15 @@ impl MonitorHandleExtIOS for MonitorHandle {
#[inline] #[inline]
fn ui_screen(&self) -> *mut c_void { fn ui_screen(&self) -> *mut c_void {
// SAFETY: The marker is only used to get the pointer of the screen // SAFETY: The marker is only used to get the pointer of the screen
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() };
objc2::rc::Retained::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void objc2::rc::Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
} }
#[inline] #[inline]
fn preferred_video_mode(&self) -> VideoModeHandle { fn preferred_video_mode(&self) -> VideoModeHandle {
VideoModeHandle { video_mode: self.inner.preferred_video_mode() } VideoModeHandle {
video_mode: self.inner.preferred_video_mode(),
}
} }
} }

View File

@@ -3,93 +3,27 @@
//! Winit has an OS requirement of macOS 10.11 or higher (same as Rust //! Winit has an OS requirement of macOS 10.11 or higher (same as Rust
//! itself), and is regularly tested on macOS 10.14. //! itself), and is regularly tested on macOS 10.14.
//! //!
//! ## Window initialization
//!
//! A lot of functionality expects the application to be ready before you //! A lot of functionality expects the application to be ready before you
//! start doing anything; this includes creating windows, fetching monitors, //! start doing anything; this includes creating windows, fetching monitors,
//! drawing, and so on, see issues [#2238], [#2051] and [#2087]. //! drawing, and so on, see issues [#2238], [#2051] and [#2087].
//! //!
//! If you encounter problems, you should try doing your initialization inside //! If you encounter problems, you should try doing your initialization inside
//! [`ApplicationHandler::resumed`]. //! `Event::Resumed`.
//! //!
//! [#2238]: https://github.com/rust-windowing/winit/issues/2238 //! [#2238]: https://github.com/rust-windowing/winit/issues/2238
//! [#2051]: https://github.com/rust-windowing/winit/issues/2051 //! [#2051]: https://github.com/rust-windowing/winit/issues/2051
//! [#2087]: https://github.com/rust-windowing/winit/issues/2087 //! [#2087]: https://github.com/rust-windowing/winit/issues/2087
//! [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
//!
//! ## Custom `NSApplicationDelegate`
//!
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
//! though, you might want to do more niche stuff, such as [handle when the user re-activates the
//! application][reopen]. Such functionality is not exposed directly in Winit, since it would
//! increase the API surface by quite a lot.
//!
//! [reopen]: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc
//!
//! Instead, Winit guarantees that it will not register an application delegate, so the solution is
//! to register your own application delegate, as outlined in the following example (see
//! `objc2-app-kit` for more detailed information).
#![cfg_attr(target_os = "macos", doc = "```")]
#![cfg_attr(not(target_os = "macos"), doc = "```ignore")]
//! use objc2::rc::Retained;
//! use objc2::runtime::ProtocolObject;
//! use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
//! use objc2_app_kit::{NSApplication, NSApplicationDelegate};
//! use objc2_foundation::{NSArray, NSURL, MainThreadMarker, NSObject, NSObjectProtocol};
//! use winit::event_loop::EventLoop;
//!
//! declare_class!(
//! struct AppDelegate;
//!
//! unsafe impl ClassType for AppDelegate {
//! type Super = NSObject;
//! type Mutability = mutability::MainThreadOnly;
//! const NAME: &'static str = "MyAppDelegate";
//! }
//!
//! impl DeclaredClass for AppDelegate {}
//!
//! unsafe impl NSObjectProtocol for AppDelegate {}
//!
//! unsafe impl NSApplicationDelegate for AppDelegate {
//! #[method(application:openURLs:)]
//! fn application_openURLs(&self, application: &NSApplication, urls: &NSArray<NSURL>) {
//! // Note: To specifically get `application:openURLs:` to work, you _might_
//! // have to bundle your application. This is not done in this example.
//! println!("open urls: {application:?}, {urls:?}");
//! }
//! }
//! );
//!
//! impl AppDelegate {
//! fn new(mtm: MainThreadMarker) -> Retained<Self> {
//! unsafe { msg_send_id![super(mtm.alloc().set_ivars(())), init] }
//! }
//! }
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let event_loop = EventLoop::new()?;
//!
//! let mtm = MainThreadMarker::new().unwrap();
//! let delegate = AppDelegate::new(mtm);
//! // Important: Call `sharedApplication` after `EventLoop::new`,
//! // doing it before is not yet supported.
//! let app = NSApplication::sharedApplication(mtm);
//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
//!
//! // event_loop.run_app(&mut my_app);
//! Ok(())
//! }
//! ```
use std::os::raw::c_void; use std::os::raw::c_void;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::event_loop::{ActiveEventLoop, EventLoopBuilder}; use crate::{
use crate::monitor::MonitorHandle; event_loop::{ActiveEventLoop, EventLoopBuilder},
use crate::window::{Window, WindowAttributes}; monitor::MonitorHandle,
window::{Window, WindowAttributes},
};
/// Additional methods on [`Window`] that are specific to MacOS. /// Additional methods on [`Window`] that are specific to MacOS.
pub trait WindowExtMacOS { pub trait WindowExtMacOS {
@@ -162,14 +96,6 @@ pub trait WindowExtMacOS {
/// Getter for the [`WindowExtMacOS::set_option_as_alt`]. /// Getter for the [`WindowExtMacOS::set_option_as_alt`].
fn option_as_alt(&self) -> OptionAsAlt; fn option_as_alt(&self) -> OptionAsAlt;
/// Disable the Menu Bar and Dock in Simple or Borderless Fullscreen mode. Useful for games.
/// The effect is applied when [`WindowExtMacOS::set_simple_fullscreen`] or
/// [`Window::set_fullscreen`] is called.
fn set_borderless_game(&self, borderless_game: bool);
/// Getter for the [`WindowExtMacOS::set_borderless_game`].
fn is_borderless_game(&self) -> bool;
} }
impl WindowExtMacOS for Window { impl WindowExtMacOS for Window {
@@ -180,7 +106,8 @@ impl WindowExtMacOS for Window {
#[inline] #[inline]
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool { fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
self.window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen)) self.window
.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
} }
#[inline] #[inline]
@@ -190,12 +117,14 @@ impl WindowExtMacOS for Window {
#[inline] #[inline]
fn set_has_shadow(&self, has_shadow: bool) { fn set_has_shadow(&self, has_shadow: bool) {
self.window.maybe_queue_on_main(move |w| w.set_has_shadow(has_shadow)) self.window
.maybe_queue_on_main(move |w| w.set_has_shadow(has_shadow))
} }
#[inline] #[inline]
fn set_tabbing_identifier(&self, identifier: &str) { fn set_tabbing_identifier(&self, identifier: &str) {
self.window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier)) self.window
.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
} }
#[inline] #[inline]
@@ -215,7 +144,8 @@ impl WindowExtMacOS for Window {
#[inline] #[inline]
fn select_tab_at_index(&self, index: usize) { fn select_tab_at_index(&self, index: usize) {
self.window.maybe_queue_on_main(move |w| w.select_tab_at_index(index)) self.window
.maybe_queue_on_main(move |w| w.select_tab_at_index(index))
} }
#[inline] #[inline]
@@ -230,28 +160,20 @@ impl WindowExtMacOS for Window {
#[inline] #[inline]
fn set_document_edited(&self, edited: bool) { fn set_document_edited(&self, edited: bool) {
self.window.maybe_queue_on_main(move |w| w.set_document_edited(edited)) self.window
.maybe_queue_on_main(move |w| w.set_document_edited(edited))
} }
#[inline] #[inline]
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) { fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
self.window.maybe_queue_on_main(move |w| w.set_option_as_alt(option_as_alt)) self.window
.maybe_queue_on_main(move |w| w.set_option_as_alt(option_as_alt))
} }
#[inline] #[inline]
fn option_as_alt(&self) -> OptionAsAlt { fn option_as_alt(&self) -> OptionAsAlt {
self.window.maybe_wait_on_main(|w| w.option_as_alt()) self.window.maybe_wait_on_main(|w| w.option_as_alt())
} }
#[inline]
fn set_borderless_game(&self, borderless_game: bool) {
self.window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game))
}
#[inline]
fn is_borderless_game(&self) -> bool {
self.window.maybe_wait_on_main(|w| w.is_borderless_game())
}
} }
/// Corresponds to `NSApplicationActivationPolicy`. /// Corresponds to `NSApplicationActivationPolicy`.
@@ -270,8 +192,7 @@ pub enum ActivationPolicy {
/// Additional methods on [`WindowAttributes`] that are specific to MacOS. /// Additional methods on [`WindowAttributes`] that are specific to MacOS.
/// ///
/// **Note:** Properties dealing with the titlebar will be overwritten by the /// **Note:** Properties dealing with the titlebar will be overwritten by the [`WindowAttributes::with_decorations`] method:
/// [`WindowAttributes::with_decorations`] method:
/// - `with_titlebar_transparent` /// - `with_titlebar_transparent`
/// - `with_title_hidden` /// - `with_title_hidden`
/// - `with_titlebar_hidden` /// - `with_titlebar_hidden`
@@ -302,8 +223,6 @@ pub trait WindowAttributesExtMacOS {
/// ///
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set. /// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self; fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
/// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
fn with_borderless_game(self, borderless_game: bool) -> Self;
} }
impl WindowAttributesExtMacOS for WindowAttributes { impl WindowAttributesExtMacOS for WindowAttributes {
@@ -363,7 +282,9 @@ impl WindowAttributesExtMacOS for WindowAttributes {
#[inline] #[inline]
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self { fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
self.platform_specific.tabbing_identifier.replace(tabbing_identifier.to_string()); self.platform_specific
.tabbing_identifier
.replace(tabbing_identifier.to_string());
self self
} }
@@ -372,21 +293,12 @@ impl WindowAttributesExtMacOS for WindowAttributes {
self.platform_specific.option_as_alt = option_as_alt; self.platform_specific.option_as_alt = option_as_alt;
self self
} }
#[inline]
fn with_borderless_game(mut self, borderless_game: bool) -> Self {
self.platform_specific.borderless_game = borderless_game;
self
}
} }
pub trait EventLoopBuilderExtMacOS { pub trait EventLoopBuilderExtMacOS {
/// Sets the activation policy for the application. If used, this will override /// Sets the activation policy for the application.
/// any relevant settings provided in the package manifest.
/// For instance, `with_activation_policy(ActivationPolicy::Regular)` will prevent
/// the application from running as an "agent", even if LSUIElement is set to true.
/// ///
/// If unused, the Winit will honor the package manifest. /// It is set to [`ActivationPolicy::Regular`] by default.
/// ///
/// # Example /// # Example
/// ///
@@ -395,7 +307,7 @@ pub trait EventLoopBuilderExtMacOS {
/// ``` /// ```
/// use winit::event_loop::EventLoopBuilder; /// use winit::event_loop::EventLoopBuilder;
/// #[cfg(target_os = "macos")] /// #[cfg(target_os = "macos")]
/// use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS}; /// use winit::platform::macos::{EventLoopBuilderExtMacOS, ActivationPolicy};
/// ///
/// let mut builder = EventLoopBuilder::new(); /// let mut builder = EventLoopBuilder::new();
/// #[cfg(target_os = "macos")] /// #[cfg(target_os = "macos")]
@@ -438,7 +350,7 @@ pub trait EventLoopBuilderExtMacOS {
impl<T> EventLoopBuilderExtMacOS for EventLoopBuilder<T> { impl<T> EventLoopBuilderExtMacOS for EventLoopBuilder<T> {
#[inline] #[inline]
fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self { fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self {
self.platform_specific.activation_policy = Some(activation_policy); self.platform_specific.activation_policy = activation_policy;
self self
} }
@@ -471,18 +383,18 @@ impl MonitorHandleExtMacOS for MonitorHandle {
fn ns_screen(&self) -> Option<*mut c_void> { fn ns_screen(&self) -> Option<*mut c_void> {
// SAFETY: We only use the marker to get a pointer // SAFETY: We only use the marker to get a pointer
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() };
self.inner.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _) self.inner
.ns_screen(mtm)
.map(|s| objc2::rc::Id::as_ptr(&s) as _)
} }
} }
/// Additional methods on [`ActiveEventLoop`] that are specific to macOS. /// Additional methods on [`ActiveEventLoop`] that are specific to macOS.
pub trait ActiveEventLoopExtMacOS { pub trait ActiveEventLoopExtMacOS {
/// Hide the entire application. In most applications this is typically triggered with /// Hide the entire application. In most applications this is typically triggered with Command-H.
/// Command-H.
fn hide_application(&self); fn hide_application(&self);
/// Hide the other applications. In most applications this is typically triggered with /// Hide the other applications. In most applications this is typically triggered with Command+Option-H.
/// Command+Option-H.
fn hide_other_applications(&self); fn hide_other_applications(&self);
/// Set whether the system can automatically organize windows into tabs. /// Set whether the system can automatically organize windows into tabs.
/// ///

View File

@@ -51,5 +51,11 @@ pub mod pump_events;
))] ))]
pub mod modifier_supplement; pub mod modifier_supplement;
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, docsrs))] #[cfg(any(
windows_platform,
macos_platform,
x11_platform,
wayland_platform,
docsrs
))]
pub mod scancode; pub mod scancode;

View File

@@ -25,7 +25,10 @@ pub trait KeyEventExtModifierSupplement {
impl KeyEventExtModifierSupplement for KeyEvent { impl KeyEventExtModifierSupplement for KeyEvent {
#[inline] #[inline]
fn text_with_all_modifiers(&self) -> Option<&str> { fn text_with_all_modifiers(&self) -> Option<&str> {
self.platform_specific.text_with_all_modifiers.as_ref().map(|s| s.as_str()) self.platform_specific
.text_with_all_modifiers
.as_ref()
.map(|s| s.as_str())
} }
#[inline] #[inline]

View File

@@ -51,19 +51,19 @@ pub trait EventLoopExtPumpEvents {
/// buffered and handled outside of Winit include: /// buffered and handled outside of Winit include:
/// - `RedrawRequested` events, used to schedule rendering. /// - `RedrawRequested` events, used to schedule rendering.
/// ///
/// macOS for example uses a `drawRect` callback to drive rendering /// macOS for example uses a `drawRect` callback to drive rendering
/// within applications and expects rendering to be finished before /// within applications and expects rendering to be finished before
/// the `drawRect` callback returns. /// the `drawRect` callback returns.
/// ///
/// For portability it's strongly recommended that applications should /// For portability it's strongly recommended that applications should
/// keep their rendering inside the closure provided to Winit. /// keep their rendering inside the closure provided to Winit.
/// - Any lifecycle events, such as `Suspended` / `Resumed`. /// - Any lifecycle events, such as `Suspended` / `Resumed`.
/// ///
/// The handling of these events needs to be synchronized with the /// The handling of these events needs to be synchronized with the
/// operating system and it would never be appropriate to buffer a /// operating system and it would never be appropriate to buffer a
/// notification that your application has been suspended or resumed and /// notification that your application has been suspended or resumed and
/// then handled that later since there would always be a chance that /// then handled that later since there would always be a chance that
/// other lifecycle events occur while the event is buffered. /// other lifecycle events occur while the event is buffered.
/// ///
/// ## Supported Platforms /// ## Supported Platforms
/// ///
@@ -74,21 +74,22 @@ pub trait EventLoopExtPumpEvents {
/// ///
/// ## Unsupported Platforms /// ## Unsupported Platforms
/// ///
/// - **Web:** This API is fundamentally incompatible with the event-based way in which Web /// - **Web:** This API is fundamentally incompatible with the event-based way in which
/// browsers work because it's not possible to have a long-running external loop that would /// Web browsers work because it's not possible to have a long-running external
/// block the browser and there is nothing that can be polled to ask for new new events. /// loop that would block the browser and there is nothing that can be
/// Events are delivered via callbacks based on an event loop that is internal to the browser /// polled to ask for new new events. Events are delivered via callbacks based
/// itself. /// on an event loop that is internal to the browser itself.
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so /// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so
/// there's no way to support the same approach to polling as on MacOS. /// there's no way to support the same approach to polling as on MacOS.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Windows**: The implementation will use `PeekMessage` when checking for window messages /// - **Windows**: The implementation will use `PeekMessage` when checking for
/// to avoid blocking your external event loop. /// window messages to avoid blocking your external event loop.
/// ///
/// - **MacOS**: The implementation works in terms of stopping the global application whenever /// - **MacOS**: The implementation works in terms of stopping the global application
/// the application `RunLoop` indicates that it is preparing to block and wait for new events. /// whenever the application `RunLoop` indicates that it is preparing to block
/// and wait for new events.
/// ///
/// This is very different to the polling APIs that are available on other /// This is very different to the polling APIs that are available on other
/// platforms (the lower level polling primitives on MacOS are private /// platforms (the lower level polling primitives on MacOS are private

View File

@@ -21,8 +21,8 @@ pub trait EventLoopExtRunOnDemand {
/// Run the application with the event loop on the calling thread. /// Run the application with the event loop on the calling thread.
/// ///
/// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`) /// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`) closures
/// closures and it is possible to return control back to the caller without /// and it is possible to return control back to the caller without
/// consuming the `EventLoop` (by using [`exit()`]) and /// consuming the `EventLoop` (by using [`exit()`]) and
/// so the event loop can be re-run after it has exit. /// so the event loop can be re-run after it has exit.
/// ///
@@ -42,9 +42,7 @@ pub trait EventLoopExtRunOnDemand {
/// # Caveats /// # Caveats
/// - This extension isn't available on all platforms, since it's not always possible to return /// - This extension isn't available on all platforms, since it's not always possible to return
/// to the caller (specifically this is impossible on iOS and Web - though with the Web /// to the caller (specifically this is impossible on iOS and Web - though with the Web
/// backend it is possible to use `EventLoopExtWebSys::spawn()` /// backend it is possible to use `EventLoopExtWebSys::spawn()`[^1] more than once instead).
#[cfg_attr(not(web_platform), doc = "[^1]")]
/// more than once instead).
/// - No [`Window`] state can be carried between separate runs of the event loop. /// - No [`Window`] state can be carried between separate runs of the event loop.
/// ///
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you /// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you
@@ -57,14 +55,18 @@ pub trait EventLoopExtRunOnDemand {
/// - Android /// - Android
/// ///
/// # Unsupported Platforms /// # Unsupported Platforms
/// - **Web:** This API is fundamentally incompatible with the event-based way in which Web /// - **Web:** This API is fundamentally incompatible with the event-based way in which
/// browsers work because it's not possible to have a long-running external loop that would /// Web browsers work because it's not possible to have a long-running external
/// block the browser and there is nothing that can be polled to ask for new events. Events /// loop that would block the browser and there is nothing that can be
/// are delivered via callbacks based on an event loop that is internal to the browser itself. /// polled to ask for new events. Events are delivered via callbacks based
/// on an event loop that is internal to the browser itself.
/// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS. /// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS.
#[cfg_attr(not(web_platform), doc = "[^1]: `spawn()` is only available on `wasm` platforms.")]
/// ///
#[rustfmt::skip] #[cfg_attr(
not(web_platform),
doc = "[^1]: `spawn()` is only available on `wasm` platforms."
)]
///
/// [`exit()`]: ActiveEventLoop::exit() /// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>( fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>(

View File

@@ -2,8 +2,8 @@ use crate::keyboard::{KeyCode, PhysicalKey};
// TODO: Describe what this value contains for each platform // TODO: Describe what this value contains for each platform
/// Additional methods for the [`PhysicalKey`] type that allow the user to access the /// Additional methods for the [`PhysicalKey`] type that allow the user to access the platform-specific
/// platform-specific scancode. /// scancode.
/// ///
/// [`PhysicalKey`]: crate::keyboard::PhysicalKey /// [`PhysicalKey`]: crate::keyboard::PhysicalKey
pub trait PhysicalKeyExtScancode { pub trait PhysicalKeyExtScancode {
@@ -23,7 +23,7 @@ pub trait PhysicalKeyExtScancode {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// - **Wayland/X11**: A 32-bit linux scancode. When building from X11/Wayland keycode subtract /// - **Wayland/X11**: A 32-bit linux scancode. When building from X11/Wayland keycode subtract
/// `8` to get the value you wanted. /// `8` to get the value you wanted.
fn from_scancode(scancode: u32) -> PhysicalKey; fn from_scancode(scancode: u32) -> PhysicalKey;
} }

View File

@@ -64,7 +64,7 @@ impl EventLoopExtStartupNotify for ActiveEventLoop {
crate::platform_impl::ActiveEventLoop::X(_) => env::var(X11_VAR), crate::platform_impl::ActiveEventLoop::X(_) => env::var(X11_VAR),
} }
.ok() .ok()
.map(ActivationToken::from_raw) .map(ActivationToken::_new)
} }
} }
@@ -94,6 +94,6 @@ pub fn reset_activation_token_env() {
/// ///
/// This could be used before running daemon processes. /// This could be used before running daemon processes.
pub fn set_activation_token_env(token: ActivationToken) { pub fn set_activation_token_env(token: ActivationToken) {
env::set_var(X11_VAR, &token.token); env::set_var(X11_VAR, &token._token);
env::set_var(WAYLAND_VAR, token.token); env::set_var(WAYLAND_VAR, token._token);
} }

View File

@@ -13,13 +13,11 @@
//! * `wayland-csd-adwaita` (default). //! * `wayland-csd-adwaita` (default).
//! * `wayland-csd-adwaita-crossfont`. //! * `wayland-csd-adwaita-crossfont`.
//! * `wayland-csd-adwaita-notitle`. //! * `wayland-csd-adwaita-notitle`.
use crate::{
use std::ffi::c_void; event_loop::{ActiveEventLoop, EventLoopBuilder},
use std::ptr::NonNull; monitor::MonitorHandle,
window::{Window, WindowAttributes},
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; };
use crate::monitor::MonitorHandle;
use crate::window::{Window, WindowAttributes};
pub use crate::window::Theme; pub use crate::window::Theme;
@@ -36,19 +34,6 @@ impl ActiveEventLoopExtWayland for ActiveEventLoop {
} }
} }
/// Additional methods on [`EventLoop`] that are specific to Wayland.
pub trait EventLoopExtWayland {
/// True if the [`EventLoop`] uses Wayland.
fn is_wayland(&self) -> bool;
}
impl<T: 'static> EventLoopExtWayland for EventLoop<T> {
#[inline]
fn is_wayland(&self) -> bool {
self.event_loop.is_wayland()
}
}
/// Additional methods on [`EventLoopBuilder`] that are specific to Wayland. /// Additional methods on [`EventLoopBuilder`] that are specific to Wayland.
pub trait EventLoopBuilderExtWayland { pub trait EventLoopBuilderExtWayland {
/// Force using Wayland. /// Force using Wayland.
@@ -76,25 +61,9 @@ impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> {
} }
/// Additional methods on [`Window`] that are specific to Wayland. /// Additional methods on [`Window`] that are specific to Wayland.
/// pub trait WindowExtWayland {}
/// [`Window`]: crate::window::Window
pub trait WindowExtWayland {
/// Returns `xdg_toplevel` of the window or [`None`] if the window is X11 window.
fn xdg_toplevel(&self) -> Option<NonNull<c_void>>;
}
impl WindowExtWayland for Window { impl WindowExtWayland for Window {}
#[inline]
fn xdg_toplevel(&self) -> Option<NonNull<c_void>> {
#[allow(clippy::single_match)]
match &self.window {
#[cfg(x11_platform)]
crate::platform_impl::Window::X(_) => None,
#[cfg(wayland_platform)]
crate::platform_impl::Window::Wayland(window) => window.xdg_toplevel(),
}
}
}
/// Additional methods on [`WindowAttributes`] that are specific to Wayland. /// Additional methods on [`WindowAttributes`] that are specific to Wayland.
pub trait WindowAttributesExtWayland { pub trait WindowAttributesExtWayland {
@@ -111,8 +80,10 @@ pub trait WindowAttributesExtWayland {
impl WindowAttributesExtWayland for WindowAttributes { impl WindowAttributesExtWayland for WindowAttributes {
#[inline] #[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self { fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.platform_specific.name = self.platform_specific.name = Some(crate::platform_impl::ApplicationName::new(
Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into())); general.into(),
instance.into(),
));
self self
} }
} }

View File

@@ -30,8 +30,8 @@
//! The following APIs can't take them into account and will therefore provide inaccurate results: //! The following APIs can't take them into account and will therefore provide inaccurate results:
//! - [`WindowEvent::Resized`] and [`Window::(set_)inner_size()`] //! - [`WindowEvent::Resized`] and [`Window::(set_)inner_size()`]
//! - [`WindowEvent::Occluded`] //! - [`WindowEvent::Occluded`]
//! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`], and //! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`],
//! [`WindowEvent::Touch`]. //! and [`WindowEvent::Touch`].
//! - [`Window::set_outer_position()`] //! - [`Window::set_outer_position()`]
//! //!
//! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized //! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
@@ -109,7 +109,11 @@ pub trait WindowAttributesExtWebSys {
/// In any case, the canvas won't be automatically inserted into the web page. /// In any case, the canvas won't be automatically inserted into the web page.
/// ///
/// [`None`] by default. /// [`None`] by default.
#[cfg_attr(not(web_platform), doc = "", doc = "[`HtmlCanvasElement`]: #only-available-on-wasm")] #[cfg_attr(
not(web_platform),
doc = "",
doc = "[`HtmlCanvasElement`]: #only-available-on-wasm"
)]
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self; fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
/// Sets whether `event.preventDefault()` should be called on events on the /// Sets whether `event.preventDefault()` should be called on events on the
@@ -162,7 +166,10 @@ pub trait EventLoopExtWebSys {
/// Initializes the winit event loop. /// Initializes the winit event loop.
/// ///
/// Unlike /// Unlike
#[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run_app()`")] #[cfg_attr(
all(web_platform, target_feature = "exception-handling"),
doc = "`run_app()`"
)]
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run_app()`]" doc = "[`run_app()`]"
@@ -174,7 +181,6 @@ pub trait EventLoopExtWebSys {
/// by calling this function again. This can be useful if you want to recreate the event loop /// by calling this function again. This can be useful if you want to recreate the event loop
/// while the WebAssembly module is still loaded. For example, this can be used to recreate the /// while the WebAssembly module is still loaded. For example, this can be used to recreate the
/// event loop when switching between tabs on a single page application. /// event loop when switching between tabs on a single page application.
#[rustfmt::skip]
/// ///
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
@@ -190,34 +196,6 @@ pub trait EventLoopExtWebSys {
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop); F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
/// Sets the strategy for [`ControlFlow::Poll`].
///
/// See [`PollStrategy`].
///
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
fn set_poll_strategy(&self, strategy: PollStrategy);
/// Gets the strategy for [`ControlFlow::Poll`].
///
/// See [`PollStrategy`].
///
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
fn poll_strategy(&self) -> PollStrategy;
/// Sets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);
/// Gets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn wait_until_strategy(&self) -> WaitUntilStrategy;
} }
impl<T> EventLoopExtWebSys for EventLoop<T> { impl<T> EventLoopExtWebSys for EventLoop<T> {
@@ -235,22 +213,6 @@ impl<T> EventLoopExtWebSys for EventLoop<T> {
{ {
self.event_loop.spawn(event_handler) self.event_loop.spawn(event_handler)
} }
fn set_poll_strategy(&self, strategy: PollStrategy) {
self.event_loop.set_poll_strategy(strategy);
}
fn poll_strategy(&self) -> PollStrategy {
self.event_loop.poll_strategy()
}
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
self.event_loop.set_wait_until_strategy(strategy);
}
fn wait_until_strategy(&self) -> WaitUntilStrategy {
self.event_loop.wait_until_strategy()
}
} }
pub trait ActiveEventLoopExtWebSys { pub trait ActiveEventLoopExtWebSys {
@@ -268,20 +230,6 @@ pub trait ActiveEventLoopExtWebSys {
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll /// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
fn poll_strategy(&self) -> PollStrategy; fn poll_strategy(&self) -> PollStrategy;
/// Sets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);
/// Gets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn wait_until_strategy(&self) -> WaitUntilStrategy;
/// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the /// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the
/// cursor has completely finished loading. /// cursor has completely finished loading.
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture; fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture;
@@ -302,19 +250,9 @@ impl ActiveEventLoopExtWebSys for ActiveEventLoop {
fn poll_strategy(&self) -> PollStrategy { fn poll_strategy(&self) -> PollStrategy {
self.p.poll_strategy() self.p.poll_strategy()
} }
#[inline]
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
self.p.set_wait_until_strategy(strategy);
}
#[inline]
fn wait_until_strategy(&self) -> WaitUntilStrategy {
self.p.wait_until_strategy()
}
} }
/// Strategy used for [`ControlFlow::Poll`][crate::event_loop::ControlFlow::Poll]. /// Strategy used for [`ControlFlow::Poll`](crate::event_loop::ControlFlow::Poll).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum PollStrategy { pub enum PollStrategy {
/// Uses [`Window.requestIdleCallback()`] to queue the next event loop. If not available /// Uses [`Window.requestIdleCallback()`] to queue the next event loop. If not available
@@ -340,29 +278,6 @@ pub enum PollStrategy {
Scheduler, Scheduler,
} }
/// Strategy used for [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil].
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum WaitUntilStrategy {
/// Uses the [Prioritized Task Scheduling API] to queue the next event loop. If not available
/// this will fallback to [`setTimeout()`].
///
/// This strategy is commonly not affected by browser throttling unless the window is not
/// focused.
///
/// This is the default strategy.
///
/// [Prioritized Task Scheduling API]: https://developer.mozilla.org/en-US/docs/Web/API/Prioritized_Task_Scheduling_API
/// [`setTimeout()`]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
#[default]
Scheduler,
/// Equal to [`Scheduler`][Self::Scheduler] but wakes up the event loop from a [worker].
///
/// This strategy is commonly not affected by browser throttling regardless of window focus.
///
/// [worker]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
Worker,
}
pub trait CustomCursorExtWebSys { pub trait CustomCursorExtWebSys {
/// Returns if this cursor is an animation. /// Returns if this cursor is an animation.
fn is_animation(&self) -> bool; fn is_animation(&self) -> bool;
@@ -388,7 +303,13 @@ impl CustomCursorExtWebSys for CustomCursor {
} }
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource { fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource {
CustomCursorSource { inner: PlatformCustomCursorSource::Url { url, hotspot_x, hotspot_y } } CustomCursorSource {
inner: PlatformCustomCursorSource::Url {
url,
hotspot_x,
hotspot_y,
},
}
} }
fn from_animation( fn from_animation(
@@ -422,7 +343,7 @@ impl fmt::Display for BadAnimation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Empty => write!(f, "No cursors supplied"), Self::Empty => write!(f, "No cursors supplied"),
Self::Animation => write!(f, "A supplied cursor is an animation"), Self::Animation => write!(f, "A supplied cursor is an animtion"),
} }
} }
} }
@@ -439,7 +360,9 @@ impl Future for CustomCursorFuture {
type Output = Result<CustomCursor, CustomCursorError>; type Output = Result<CustomCursor, CustomCursorError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(cx).map_ok(|cursor| CustomCursor { inner: cursor }) Pin::new(&mut self.0)
.poll(cx)
.map_ok(|cursor| CustomCursor { inner: cursor })
} }
} }
@@ -455,11 +378,10 @@ impl Display for CustomCursorError {
match self { match self {
Self::Blob => write!(f, "failed to create `Blob`"), Self::Blob => write!(f, "failed to create `Blob`"),
Self::Decode(error) => write!(f, "failed to decode image: {error}"), Self::Decode(error) => write!(f, "failed to decode image: {error}"),
Self::Animation => { Self::Animation => write!(
write!(f, "found `CustomCursor` that is an animation when building an animation") f,
}, "found `CustomCursor` that is an animation when building an animation"
),
} }
} }
} }
impl Error for CustomCursorError {}

View File

@@ -2,15 +2,15 @@
//! //!
//! The supported OS version is Windows 7 or higher, though Windows 10 is //! The supported OS version is Windows 7 or higher, though Windows 10 is
//! tested regularly. //! tested regularly.
use std::borrow::Borrow; use std::{ffi::c_void, path::Path};
use std::ffi::c_void;
use std::path::Path;
use crate::dpi::PhysicalSize; use crate::{
use crate::event::DeviceId; dpi::PhysicalSize,
use crate::event_loop::EventLoopBuilder; event::DeviceId,
use crate::monitor::MonitorHandle; event_loop::EventLoopBuilder,
use crate::window::{BadIcon, Icon, Window, WindowAttributes}; monitor::MonitorHandle,
window::{BadIcon, Icon, Window, WindowAttributes},
};
/// Window Handle type used by Win32 API /// Window Handle type used by Win32 API
pub type HWND = isize; pub type HWND = isize;
@@ -57,11 +57,11 @@ pub enum BackdropType {
pub struct Color(u32); pub struct Color(u32);
impl Color { impl Color {
// Special constant only valid for the window border and therefore modeled using Option<Color>
// for user facing code
const NONE: Color = Color(0xfffffffe);
/// Use the system's default color /// Use the system's default color
pub const SYSTEM_DEFAULT: Color = Color(0xffffffff); pub const SYSTEM_DEFAULT: Color = Color(0xFFFFFFFF);
//Special constant only valid for the window border and therefore modeled using Option<Color> for user facing code
const NONE: Color = Color(0xFFFFFFFE);
/// Create a new color from the given RGB values /// Create a new color from the given RGB values
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self { pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
@@ -105,60 +105,6 @@ pub enum CornerPreference {
RoundSmall = 3, RoundSmall = 3,
} }
/// A wrapper around a [`Window`] that ignores thread-specific window handle limitations.
///
/// See [`WindowBorrowExtWindows::any_thread`] for more information.
#[derive(Debug)]
pub struct AnyThread<W>(W);
impl<W: Borrow<Window>> AnyThread<W> {
/// Get a reference to the inner window.
#[inline]
pub fn get_ref(&self) -> &Window {
self.0.borrow()
}
/// Get a reference to the inner object.
#[inline]
pub fn inner(&self) -> &W {
&self.0
}
/// Unwrap and get the inner window.
#[inline]
pub fn into_inner(self) -> W {
self.0
}
}
impl<W: Borrow<Window>> AsRef<Window> for AnyThread<W> {
fn as_ref(&self) -> &Window {
self.get_ref()
}
}
impl<W: Borrow<Window>> Borrow<Window> for AnyThread<W> {
fn borrow(&self) -> &Window {
self.get_ref()
}
}
impl<W: Borrow<Window>> std::ops::Deref for AnyThread<W> {
type Target = Window;
fn deref(&self) -> &Self::Target {
self.get_ref()
}
}
#[cfg(feature = "rwh_06")]
impl<W: Borrow<Window>> rwh_06::HasWindowHandle for AnyThread<W> {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
// SAFETY: The top level user has asserted this is only used safely.
unsafe { self.get_ref().window_handle_any_thread() }
}
}
/// Additional methods on `EventLoop` that are specific to Windows. /// Additional methods on `EventLoop` that are specific to Windows.
pub trait EventLoopBuilderExtWindows { pub trait EventLoopBuilderExtWindows {
/// Whether to allow the event loop to be created off of the main thread. /// Whether to allow the event loop to be created off of the main thread.
@@ -256,8 +202,8 @@ pub trait WindowExtWindows {
/// ///
/// A window must be enabled before it can be activated. /// A window must be enabled before it can be activated.
/// If an application has create a modal dialog box by disabling its owner window /// If an application has create a modal dialog box by disabling its owner window
/// (as described in [`WindowAttributesExtWindows::with_owner_window`]), the application must /// (as described in [`WindowAttributesExtWindows::with_owner_window`]), the application must enable
/// enable the owner window before destroying the dialog box. /// the owner window before destroying the dialog box.
/// Otherwise, another window will receive the keyboard focus and be activated. /// Otherwise, another window will receive the keyboard focus and be activated.
/// ///
/// If a child window is disabled, it is ignored when the system tries to determine which /// If a child window is disabled, it is ignored when the system tries to determine which
@@ -302,60 +248,6 @@ pub trait WindowExtWindows {
/// ///
/// Supported starting with Windows 11 Build 22000. /// Supported starting with Windows 11 Build 22000.
fn set_corner_preference(&self, preference: CornerPreference); fn set_corner_preference(&self, preference: CornerPreference);
/// Get the raw window handle for this [`Window`] without checking for thread affinity.
///
/// Window handles in Win32 have a property called "thread affinity" that ties them to their
/// origin thread. Some operations can only happen on the window's origin thread, while others
/// can be called from any thread. For example, [`SetWindowSubclass`] is not thread safe while
/// [`GetDC`] is thread safe.
///
/// In Rust terms, the window handle is `Send` sometimes but `!Send` other times.
///
/// Therefore, in order to avoid confusing threading errors, [`Window`] only returns the
/// window handle when the [`window_handle`] function is called from the thread that created
/// the window. In other cases, it returns an [`Unavailable`] error.
///
/// However in some cases you may already know that you are using the window handle for
/// operations that are guaranteed to be thread-safe. In which case this function aims
/// to provide an escape hatch so these functions are still accessible from other threads.
///
/// # Safety
///
/// It is the responsibility of the user to only pass the window handle into thread-safe
/// Win32 APIs.
///
/// [`SetWindowSubclass`]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-setwindowsubclass
/// [`GetDC`]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc
/// [`Window`]: crate::window::Window
/// [`window_handle`]: https://docs.rs/raw-window-handle/latest/raw_window_handle/trait.HasWindowHandle.html#tymethod.window_handle
/// [`Unavailable`]: https://docs.rs/raw-window-handle/latest/raw_window_handle/enum.HandleError.html#variant.Unavailable
///
/// ## Example
///
/// ```no_run
/// # use winit::window::Window;
/// # fn scope(window: Window) {
/// use std::thread;
/// use winit::platform::windows::WindowExtWindows;
/// use winit::raw_window_handle::HasWindowHandle;
///
/// // We can get the window handle on the current thread.
/// let handle = window.window_handle().unwrap();
///
/// // However, on another thread, we can't!
/// thread::spawn(move || {
/// assert!(window.window_handle().is_err());
///
/// // We can use this function as an escape hatch.
/// let handle = unsafe { window.window_handle_any_thread().unwrap() };
/// });
/// # }
/// ```
#[cfg(feature = "rwh_06")]
unsafe fn window_handle_any_thread(
&self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>;
} }
impl WindowExtWindows for Window { impl WindowExtWindows for Window {
@@ -391,10 +283,10 @@ impl WindowExtWindows for Window {
#[inline] #[inline]
fn set_title_background_color(&self, color: Option<Color>) { fn set_title_background_color(&self, color: Option<Color>) {
// The windows docs don't mention NONE as a valid options but it works in practice and is // The windows docs don't mention NONE as a valid options but it works in practice and is useful
// useful to circumvent the Windows option "Show accent color on title bars and // to circumvent the Windows option "Show accent color on title bars and window borders"
// window borders" self.window
self.window.set_title_background_color(color.unwrap_or(Color::NONE)) .set_title_background_color(color.unwrap_or(Color::NONE))
} }
#[inline] #[inline]
@@ -406,57 +298,15 @@ impl WindowExtWindows for Window {
fn set_corner_preference(&self, preference: CornerPreference) { fn set_corner_preference(&self, preference: CornerPreference) {
self.window.set_corner_preference(preference) self.window.set_corner_preference(preference)
} }
#[cfg(feature = "rwh_06")]
unsafe fn window_handle_any_thread(
&self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
unsafe {
let handle = self.window.rwh_06_no_thread_check()?;
// SAFETY: The handle is valid in this context.
Ok(rwh_06::WindowHandle::borrow_raw(handle))
}
}
} }
/// Additional methods for anything that dereference to [`Window`].
///
/// [`Window`]: crate::window::Window
pub trait WindowBorrowExtWindows: Borrow<Window> + Sized {
/// Create an object that allows accessing the inner window handle in a thread-unsafe way.
///
/// It is possible to call [`window_handle_any_thread`] to get around Windows's thread
/// affinity limitations. However, it may be desired to pass the [`Window`] into something
/// that requires the [`HasWindowHandle`] trait, while ignoring thread affinity limitations.
///
/// This function wraps anything that implements `Borrow<Window>` into a structure that
/// uses the inner window handle as a mean of implementing [`HasWindowHandle`]. It wraps
/// `Window`, `&Window`, `Arc<Window>`, and other reference types.
///
/// # Safety
///
/// It is the responsibility of the user to only pass the window handle into thread-safe
/// Win32 APIs.
///
/// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread
/// [`Window`]: crate::window::Window
/// [`HasWindowHandle`]: rwh_06::HasWindowHandle
unsafe fn any_thread(self) -> AnyThread<Self> {
AnyThread(self)
}
}
impl<W: Borrow<Window> + Sized> WindowBorrowExtWindows for W {}
/// Additional methods on `WindowAttributes` that are specific to Windows. /// Additional methods on `WindowAttributes` that are specific to Windows.
#[allow(rustdoc::broken_intra_doc_links)] #[allow(rustdoc::broken_intra_doc_links)]
pub trait WindowAttributesExtWindows { pub trait WindowAttributesExtWindows {
/// Set an owner to the window to be created. Can be used to create a dialog box, for example. /// Set an owner to the window to be created. Can be used to create a dialog box, for example.
/// This only works when [`WindowAttributes::with_parent_window`] isn't called or set to `None`. /// This only works when [`WindowAttributes::with_parent_window`] isn't called or set to `None`.
/// Can be used in combination with /// Can be used in combination with [`WindowExtWindows::set_enable(false)`](WindowExtWindows::set_enable)
/// [`WindowExtWindows::set_enable(false)`][WindowExtWindows::set_enable] on the owner /// on the owner window to create a modal dialog box.
/// window to create a modal dialog box.
/// ///
/// From MSDN: /// From MSDN:
/// - An owned window is always above its owner in the z-order. /// - An owned window is always above its owner in the z-order.
@@ -472,14 +322,17 @@ pub trait WindowAttributesExtWindows {
/// ///
/// The menu must have been manually created beforehand with [`CreateMenu`] or similar. /// The menu must have been manually created beforehand with [`CreateMenu`] or similar.
/// ///
/// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how /// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look.
/// the menus look. If you use this, it is recommended that you combine it with /// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect.
/// `with_theme(Some(Theme::Light))` to avoid a jarring effect. ///
#[cfg_attr( #[cfg_attr(
windows_platform, platform_windows,
doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu" doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
)] )]
#[cfg_attr(not(windows_platform), doc = "[`CreateMenu`]: #only-available-on-windows")] #[cfg_attr(
not(platform_windows),
doc = "[`CreateMenu`]: #only-available-on-windows"
)]
fn with_menu(self, menu: HMENU) -> Self; fn with_menu(self, menu: HMENU) -> Self;
/// This sets `ICON_BIG`. A good ceiling here is 256x256. /// This sets `ICON_BIG`. A good ceiling here is 256x256.
@@ -488,12 +341,12 @@ pub trait WindowAttributesExtWindows {
/// This sets `WS_EX_NOREDIRECTIONBITMAP`. /// This sets `WS_EX_NOREDIRECTIONBITMAP`.
fn with_no_redirection_bitmap(self, flag: bool) -> Self; fn with_no_redirection_bitmap(self, flag: bool) -> Self;
/// Enables or disables drag and drop support (enabled by default). Will interfere with other /// Enables or disables drag and drop support (enabled by default). Will interfere with other crates
/// crates that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` /// that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` instead of
/// instead of `COINIT_APARTMENTTHREADED`) on the same thread. Note that winit may still /// `COINIT_APARTMENTTHREADED`) on the same thread. Note that winit may still attempt to initialize
/// attempt to initialize COM API regardless of this option. Currently only fullscreen mode /// COM API regardless of this option. Currently only fullscreen mode does that, but there may be more in the future.
/// does that, but there may be more in the future. If you need COM API with /// If you need COM API with `COINIT_MULTITHREADED` you must initialize it before calling any winit functions.
/// `COINIT_MULTITHREADED` you must initialize it before calling any winit functions. See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information. /// See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information.
fn with_drag_and_drop(self, flag: bool) -> Self; fn with_drag_and_drop(self, flag: bool) -> Self;
/// Whether show or hide the window icon in the taskbar. /// Whether show or hide the window icon in the taskbar.
@@ -660,17 +513,6 @@ impl DeviceIdExtWindows for DeviceId {
} }
/// Additional methods on `Icon` that are specific to Windows. /// Additional methods on `Icon` that are specific to Windows.
///
/// Windows icons can be created from files, or from the [`embedded resources`](https://learn.microsoft.com/en-us/windows/win32/menurc/about-resource-files).
///
/// The `ICON` resource definition statement use the following syntax:
/// ```rc
/// nameID ICON filename
/// ```
/// `nameID` is a unique name or a 16-bit unsigned integer value identifying the resource,
/// `filename` is the name of the file that contains the resource.
///
/// More information about the `ICON` resource can be found at [`Microsoft Learn`](https://learn.microsoft.com/en-us/windows/win32/menurc/icon-resource) portal.
pub trait IconExtWindows: Sized { pub trait IconExtWindows: Sized {
/// Create an icon from a file path. /// Create an icon from a file path.
/// ///
@@ -682,12 +524,7 @@ pub trait IconExtWindows: Sized {
fn from_path<P: AsRef<Path>>(path: P, size: Option<PhysicalSize<u32>>) fn from_path<P: AsRef<Path>>(path: P, size: Option<PhysicalSize<u32>>)
-> Result<Self, BadIcon>; -> Result<Self, BadIcon>;
/// Create an icon from a resource embedded in this executable or library by its ordinal id. /// Create an icon from a resource embedded in this executable or library.
///
/// The valid `ordinal` values range from 1 to [`u16::MAX`] (inclusive). The value `0` is an
/// invalid ordinal id, but it can be used with [`from_resource_name`] as `"0"`.
///
/// [`from_resource_name`]: IconExtWindows::from_resource_name
/// ///
/// Specify `size` to load a specific icon size from the file, or `None` to load the default /// Specify `size` to load a specific icon size from the file, or `None` to load the default
/// icon size from the file. /// icon size from the file.
@@ -695,55 +532,6 @@ pub trait IconExtWindows: Sized {
/// In cases where the specified size does not exist in the file, Windows may perform scaling /// In cases where the specified size does not exist in the file, Windows may perform scaling
/// to get an icon of the desired size. /// to get an icon of the desired size.
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>; fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
/// Create an icon from a resource embedded in this executable or library by its name.
///
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
/// icon size from the file.
///
/// In cases where the specified size does not exist in the file, Windows may perform scaling
/// to get an icon of the desired size.
///
/// # Notes
///
/// Consider the following resource definition statements:
/// ```rc
/// app ICON "app.ico"
/// 1 ICON "a.ico"
/// 0027 ICON "custom.ico"
/// 0 ICON "alt.ico"
/// ```
///
/// Due to some internal implementation details of the resource embedding/loading process on
/// Windows platform, strings that can be interpreted as 16-bit unsigned integers (`"1"`,
/// `"002"`, etc.) cannot be used as valid resource names, and instead should be passed into
/// [`from_resource`]:
///
/// [`from_resource`]: IconExtWindows::from_resource
///
/// ```rust,no_run
/// use winit::platform::windows::IconExtWindows;
/// use winit::window::Icon;
///
/// assert!(Icon::from_resource_name("app", None).is_ok());
/// assert!(Icon::from_resource(1, None).is_ok());
/// assert!(Icon::from_resource(27, None).is_ok());
/// assert!(Icon::from_resource_name("27", None).is_err());
/// assert!(Icon::from_resource_name("0027", None).is_err());
/// ```
///
/// While `0` cannot be used as an ordinal id (see [`from_resource`]), it can be used as a
/// name:
///
/// [`from_resource`]: IconExtWindows::from_resource
///
/// ```rust,no_run
/// # use winit::platform::windows::IconExtWindows;
/// # use winit::window::Icon;
/// assert!(Icon::from_resource_name("0", None).is_ok());
/// assert!(Icon::from_resource(0, None).is_err());
/// ```
fn from_resource_name(name: &str, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
} }
impl IconExtWindows for Icon { impl IconExtWindows for Icon {
@@ -759,9 +547,4 @@ impl IconExtWindows for Icon {
let win_icon = crate::platform_impl::WinIcon::from_resource(ordinal, size)?; let win_icon = crate::platform_impl::WinIcon::from_resource(ordinal, size)?;
Ok(Icon { inner: win_icon }) Ok(Icon { inner: win_icon })
} }
fn from_resource_name(name: &str, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
let win_icon = crate::platform_impl::WinIcon::from_resource_name(name, size)?;
Ok(Icon { inner: win_icon })
}
} }

View File

@@ -2,9 +2,11 @@
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; use crate::{
use crate::monitor::MonitorHandle; event_loop::{ActiveEventLoop, EventLoopBuilder},
use crate::window::{Window, WindowAttributes}; monitor::MonitorHandle,
window::{Window, WindowAttributes},
};
use crate::dpi::Size; use crate::dpi::Size;
@@ -13,12 +15,11 @@ use crate::dpi::Size;
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum WindowType { pub enum WindowType {
/// A desktop feature. This can include a single window containing desktop icons with the same /// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
/// dimensions as the screen, allowing the desktop environment to have full control of the /// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
/// desktop, without the need for proxying root window clicks. /// root window clicks.
Desktop, Desktop,
/// A dock or panel feature. Typically a Window Manager would keep such windows on top of all /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
/// other windows.
Dock, Dock,
/// Toolbar windows. "Torn off" from the main application. /// Toolbar windows. "Torn off" from the main application.
Toolbar, Toolbar,
@@ -36,8 +37,8 @@ pub enum WindowType {
/// A popup menu that usually appears when the user right clicks on an object. /// A popup menu that usually appears when the user right clicks on an object.
/// This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
PopupMenu, PopupMenu,
/// A tooltip window. Usually used to show additional information when hovering over an object /// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.
/// with the cursor. This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
Tooltip, Tooltip,
/// The window is a notification. /// The window is a notification.
/// This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
@@ -45,7 +46,7 @@ pub enum WindowType {
/// This should be used on the windows that are popped up by combo boxes. /// This should be used on the windows that are popped up by combo boxes.
/// This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
Combo, Combo,
/// This indicates the window is being dragged. /// This indicates the the window is being dragged.
/// This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
Dnd, Dnd,
/// This is a normal, top-level window. /// This is a normal, top-level window.
@@ -81,7 +82,12 @@ pub type XWindow = u32;
#[inline] #[inline]
pub fn register_xlib_error_hook(hook: XlibErrorHook) { pub fn register_xlib_error_hook(hook: XlibErrorHook) {
// Append new hook. // Append new hook.
crate::platform_impl::XLIB_ERROR_HOOKS.lock().unwrap().push(hook); unsafe {
crate::platform_impl::XLIB_ERROR_HOOKS
.lock()
.unwrap()
.push(hook);
}
} }
/// Additional methods on [`ActiveEventLoop`] that are specific to X11. /// Additional methods on [`ActiveEventLoop`] that are specific to X11.
@@ -97,19 +103,6 @@ impl ActiveEventLoopExtX11 for ActiveEventLoop {
} }
} }
/// Additional methods on [`EventLoop`] that are specific to X11.
pub trait EventLoopExtX11 {
/// True if the [`EventLoop`] uses X11.
fn is_x11(&self) -> bool;
}
impl<T: 'static> EventLoopExtX11 for EventLoop<T> {
#[inline]
fn is_x11(&self) -> bool {
!self.event_loop.is_wayland()
}
}
/// Additional methods on [`EventLoopBuilder`] that are specific to X11. /// Additional methods on [`EventLoopBuilder`] that are specific to X11.
pub trait EventLoopBuilderExtX11 { pub trait EventLoopBuilderExtX11 {
/// Force using X11. /// Force using X11.
@@ -151,8 +144,7 @@ pub trait WindowAttributesExtX11 {
/// Build window with the given `general` and `instance` names. /// Build window with the given `general` and `instance` names.
/// ///
/// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the /// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the
/// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "instance", /// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "instance", "general"`.
/// "general"`.
/// ///
/// For details about application ID conventions, see the /// For details about application ID conventions, see the
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id) /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
@@ -210,8 +202,10 @@ impl WindowAttributesExtX11 for WindowAttributes {
#[inline] #[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self { fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.platform_specific.name = self.platform_specific.name = Some(crate::platform_impl::ApplicationName::new(
Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into())); general.into(),
instance.into(),
));
self self
} }

View File

@@ -1,5 +1,7 @@
use android_activity::input::{KeyAction, KeyEvent, KeyMapChar, Keycode}; use android_activity::{
use android_activity::AndroidApp; input::{KeyAction, KeyEvent, KeyMapChar, Keycode},
AndroidApp,
};
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey}; use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey};
@@ -103,7 +105,7 @@ pub fn to_physical_key(keycode: Keycode) -> PhysicalKey {
Keycode::VolumeUp => KeyCode::AudioVolumeUp, Keycode::VolumeUp => KeyCode::AudioVolumeUp,
Keycode::VolumeDown => KeyCode::AudioVolumeDown, Keycode::VolumeDown => KeyCode::AudioVolumeDown,
Keycode::VolumeMute => KeyCode::AudioVolumeMute, Keycode::VolumeMute => KeyCode::AudioVolumeMute,
// Keycode::Mute => None, // Microphone mute //Keycode::Mute => None, // Microphone mute
Keycode::MediaPlayPause => KeyCode::MediaPlayPause, Keycode::MediaPlayPause => KeyCode::MediaPlayPause,
Keycode::MediaStop => KeyCode::MediaStop, Keycode::MediaStop => KeyCode::MediaStop,
Keycode::MediaNext => KeyCode::MediaTrackNext, Keycode::MediaNext => KeyCode::MediaTrackNext,
@@ -174,7 +176,7 @@ pub fn character_map_and_combine_key(
Err(err) => { Err(err) => {
tracing::warn!("Failed to look up `KeyCharacterMap` for device {device_id}: {err:?}"); tracing::warn!("Failed to look up `KeyCharacterMap` for device {device_id}: {err:?}");
return None; return None;
}, }
}; };
match key_map.get(key_event.key_code(), key_event.meta_state()) { match key_map.get(key_event.key_code(), key_event.meta_state()) {
@@ -186,12 +188,9 @@ pub fn character_map_and_combine_key(
Ok(Some(key)) => Some(key), Ok(Some(key)) => Some(key),
Ok(None) => None, Ok(None) => None,
Err(err) => { Err(err) => {
tracing::warn!( tracing::warn!("KeyEvent: Failed to combine 'dead key' accent '{accent}' with '{unicode}': {err:?}");
"KeyEvent: Failed to combine 'dead key' accent '{accent}' with \
'{unicode}': {err:?}"
);
None None
}, }
} }
} else { } else {
Some(unicode) Some(unicode)
@@ -201,23 +200,23 @@ pub fn character_map_and_combine_key(
} else { } else {
Some(KeyMapChar::Unicode(unicode)) Some(KeyMapChar::Unicode(unicode))
} }
}, }
Ok(KeyMapChar::CombiningAccent(accent)) => { Ok(KeyMapChar::CombiningAccent(accent)) => {
if key_event.action() == KeyAction::Down { if key_event.action() == KeyAction::Down {
*combining_accent = Some(accent); *combining_accent = Some(accent);
} }
Some(KeyMapChar::CombiningAccent(accent)) Some(KeyMapChar::CombiningAccent(accent))
}, }
Ok(KeyMapChar::None) => { Ok(KeyMapChar::None) => {
// Leave any combining_accent state in tact (seems to match how other // Leave any combining_accent state in tact (seems to match how other
// Android apps work) // Android apps work)
None None
}, }
Err(err) => { Err(err) => {
tracing::warn!("KeyEvent: Failed to get key map character: {err:?}"); tracing::warn!("KeyEvent: Failed to get key map character: {err:?}");
*combining_accent = None; *combining_accent = None;
None None
}, }
} }
} }

View File

@@ -1,10 +1,16 @@
use std::cell::Cell; #![cfg(android_platform)]
use std::collections::VecDeque;
use std::hash::Hash; use std::{
use std::marker::PhantomData; cell::Cell,
use std::sync::atomic::{AtomicBool, Ordering}; collections::VecDeque,
use std::sync::{mpsc, Arc, Mutex}; hash::Hash,
use std::time::{Duration, Instant}; marker::PhantomData,
sync::{
atomic::{AtomicBool, Ordering},
mpsc, Arc, Mutex,
},
time::{Duration, Instant},
};
use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction}; use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
use android_activity::{ use android_activity::{
@@ -12,24 +18,24 @@ use android_activity::{
}; };
use tracing::{debug, trace, warn}; use tracing::{debug, trace, warn};
use crate::cursor::Cursor; use crate::{
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; cursor::Cursor,
use crate::error; dpi::{PhysicalPosition, PhysicalSize, Position, Size},
use crate::error::EventLoopError; error,
use crate::event::{self, Force, InnerSizeWriter, StartCause}; event::{self, Force, InnerSizeWriter, StartCause},
use crate::event_loop::{self, ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents}; event_loop::{self, ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents},
use crate::platform::pump_events::PumpStatus; platform::pump_events::PumpStatus,
use crate::platform_impl::Fullscreen; window::{
use crate::window::{ self, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose, ResizeDirection, Theme,
self, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
WindowButtons, WindowLevel, },
}; };
use crate::{error::EventLoopError, platform_impl::Fullscreen};
mod keycodes; mod keycodes;
pub(crate) use crate::cursor::{ pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource, pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorSource;
};
pub(crate) use crate::icon::NoIcon as PlatformIcon; pub(crate) use crate::icon::NoIcon as PlatformIcon;
static HAS_FOCUS: AtomicBool = AtomicBool::new(true); static HAS_FOCUS: AtomicBool = AtomicBool::new(true);
@@ -38,7 +44,9 @@ static HAS_FOCUS: AtomicBool = AtomicBool::new(true);
/// equates to an infinite timeout, not a zero timeout (so can't just use /// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`) /// `Option::min`)
fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> { fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout)))) a.map_or(b, |a_timeout| {
b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout)))
})
} }
struct PeekableReceiver<T> { struct PeekableReceiver<T> {
@@ -50,7 +58,6 @@ impl<T> PeekableReceiver<T> {
pub fn from_recv(recv: mpsc::Receiver<T>) -> Self { pub fn from_recv(recv: mpsc::Receiver<T>) -> Self {
Self { recv, first: None } Self { recv, first: None }
} }
pub fn has_incoming(&mut self) -> bool { pub fn has_incoming(&mut self) -> bool {
if self.first.is_some() { if self.first.is_some() {
return true; return true;
@@ -59,15 +66,14 @@ impl<T> PeekableReceiver<T> {
Ok(v) => { Ok(v) => {
self.first = Some(v); self.first = Some(v);
true true
}, }
Err(mpsc::TryRecvError::Empty) => false, Err(mpsc::TryRecvError::Empty) => false,
Err(mpsc::TryRecvError::Disconnected) => { Err(mpsc::TryRecvError::Disconnected) => {
warn!("Channel was disconnected when checking incoming"); warn!("Channel was disconnected when checking incoming");
false false
}, }
} }
} }
pub fn try_recv(&mut self) -> Result<T, mpsc::TryRecvError> { pub fn try_recv(&mut self) -> Result<T, mpsc::TryRecvError> {
if let Some(first) = self.first.take() { if let Some(first) = self.first.take() {
return Ok(first); return Ok(first);
@@ -82,7 +88,9 @@ struct SharedFlagSetter {
} }
impl SharedFlagSetter { impl SharedFlagSetter {
pub fn set(&self) -> bool { pub fn set(&self) -> bool {
self.flag.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed).is_ok() self.flag
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
.is_ok()
} }
} }
@@ -96,13 +104,15 @@ struct SharedFlag {
// was queued and be able to read and clear the state atomically) // was queued and be able to read and clear the state atomically)
impl SharedFlag { impl SharedFlag {
pub fn new() -> Self { pub fn new() -> Self {
Self { flag: Arc::new(AtomicBool::new(false)) } Self {
flag: Arc::new(AtomicBool::new(false)),
}
} }
pub fn setter(&self) -> SharedFlagSetter { pub fn setter(&self) -> SharedFlagSetter {
SharedFlagSetter { flag: self.flag.clone() } SharedFlagSetter {
flag: self.flag.clone(),
}
} }
pub fn get_and_reset(&self) -> bool { pub fn get_and_reset(&self) -> bool {
self.flag.swap(false, std::sync::atomic::Ordering::AcqRel) self.flag.swap(false, std::sync::atomic::Ordering::AcqRel)
} }
@@ -116,9 +126,11 @@ pub struct RedrawRequester {
impl RedrawRequester { impl RedrawRequester {
fn new(flag: &SharedFlag, waker: AndroidAppWaker) -> Self { fn new(flag: &SharedFlag, waker: AndroidAppWaker) -> Self {
RedrawRequester { flag: flag.setter(), waker } RedrawRequester {
flag: flag.setter(),
waker,
}
} }
pub fn request_redraw(&self) { pub fn request_redraw(&self) {
if self.flag.set() { if self.flag.set() {
// Only explicitly try to wake up the main loop when the flag // Only explicitly try to wake up the main loop when the flag
@@ -132,11 +144,11 @@ impl RedrawRequester {
pub struct KeyEventExtra {} pub struct KeyEventExtra {}
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
pub(crate) android_app: AndroidApp, android_app: AndroidApp,
window_target: event_loop::ActiveEventLoop, window_target: event_loop::ActiveEventLoop,
redraw_flag: SharedFlag, redraw_flag: SharedFlag,
user_events_sender: mpsc::Sender<T>, user_events_sender: mpsc::Sender<T>,
user_events_receiver: PeekableReceiver<T>, // must wake looper whenever something gets sent user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent
loop_running: bool, // Dispatched `NewEvents<Init>` loop_running: bool, // Dispatched `NewEvents<Init>`
running: bool, running: bool,
pending_redraw: bool, pending_redraw: bool,
@@ -153,7 +165,10 @@ pub(crate) struct PlatformSpecificEventLoopAttributes {
impl Default for PlatformSpecificEventLoopAttributes { impl Default for PlatformSpecificEventLoopAttributes {
fn default() -> Self { fn default() -> Self {
Self { android_app: Default::default(), ignore_volume_keys: true } Self {
android_app: Default::default(),
ignore_volume_keys: true,
}
} }
} }
@@ -163,10 +178,7 @@ impl<T: 'static> EventLoop<T> {
) -> Result<Self, EventLoopError> { ) -> Result<Self, EventLoopError> {
let (user_events_sender, user_events_receiver) = mpsc::channel(); let (user_events_sender, user_events_receiver) = mpsc::channel();
let android_app = attributes.android_app.as_ref().expect( let android_app = attributes.android_app.as_ref().expect("An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on Android");
"An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on \
Android",
);
let redraw_flag = SharedFlag::new(); let redraw_flag = SharedFlag::new();
Ok(Self { Ok(Self {
@@ -213,15 +225,15 @@ impl<T: 'static> EventLoop<T> {
match event { match event {
MainEvent::InitWindow { .. } => { MainEvent::InitWindow { .. } => {
callback(event::Event::Resumed, self.window_target()); callback(event::Event::Resumed, self.window_target());
}, }
MainEvent::TerminateWindow { .. } => { MainEvent::TerminateWindow { .. } => {
callback(event::Event::Suspended, self.window_target()); callback(event::Event::Suspended, self.window_target());
}, }
MainEvent::WindowResized { .. } => resized = true, MainEvent::WindowResized { .. } => resized = true,
MainEvent::RedrawNeeded { .. } => pending_redraw = true, MainEvent::RedrawNeeded { .. } => pending_redraw = true,
MainEvent::ContentRectChanged { .. } => { MainEvent::ContentRectChanged { .. } => {
warn!("TODO: find a way to notify application of content rect change"); warn!("TODO: find a way to notify application of content rect change");
}, }
MainEvent::GainedFocus => { MainEvent::GainedFocus => {
HAS_FOCUS.store(true, Ordering::Relaxed); HAS_FOCUS.store(true, Ordering::Relaxed);
callback( callback(
@@ -231,7 +243,7 @@ impl<T: 'static> EventLoop<T> {
}, },
self.window_target(), self.window_target(),
); );
}, }
MainEvent::LostFocus => { MainEvent::LostFocus => {
HAS_FOCUS.store(false, Ordering::Relaxed); HAS_FOCUS.store(false, Ordering::Relaxed);
callback( callback(
@@ -241,7 +253,7 @@ impl<T: 'static> EventLoop<T> {
}, },
self.window_target(), self.window_target(),
); );
}, }
MainEvent::ConfigChanged { .. } => { MainEvent::ConfigChanged { .. } => {
let monitor = MonitorHandle::new(self.android_app.clone()); let monitor = MonitorHandle::new(self.android_app.clone());
let old_scale_factor = monitor.scale_factor(); let old_scale_factor = monitor.scale_factor();
@@ -261,43 +273,43 @@ impl<T: 'static> EventLoop<T> {
}; };
callback(event, self.window_target()); callback(event, self.window_target());
} }
}, }
MainEvent::LowMemory => { MainEvent::LowMemory => {
callback(event::Event::MemoryWarning, self.window_target()); callback(event::Event::MemoryWarning, self.window_target());
}, }
MainEvent::Start => { MainEvent::Start => {
// XXX: how to forward this state to applications? // XXX: how to forward this state to applications?
warn!("TODO: forward onStart notification to application"); warn!("TODO: forward onStart notification to application");
}, }
MainEvent::Resume { .. } => { MainEvent::Resume { .. } => {
debug!("App Resumed - is running"); debug!("App Resumed - is running");
self.running = true; self.running = true;
}, }
MainEvent::SaveState { .. } => { MainEvent::SaveState { .. } => {
// XXX: how to forward this state to applications? // XXX: how to forward this state to applications?
// XXX: also how do we expose state restoration to apps? // XXX: also how do we expose state restoration to apps?
warn!("TODO: forward saveState notification to application"); warn!("TODO: forward saveState notification to application");
}, }
MainEvent::Pause => { MainEvent::Pause => {
debug!("App Paused - stopped running"); debug!("App Paused - stopped running");
self.running = false; self.running = false;
}, }
MainEvent::Stop => { MainEvent::Stop => {
// XXX: how to forward this state to applications? // XXX: how to forward this state to applications?
warn!("TODO: forward onStop notification to application"); warn!("TODO: forward onStop notification to application");
}, }
MainEvent::Destroy => { MainEvent::Destroy => {
// XXX: maybe exit mainloop to drop things before being // XXX: maybe exit mainloop to drop things before being
// killed by the OS? // killed by the OS?
warn!("TODO: forward onDestroy notification to application"); warn!("TODO: forward onDestroy notification to application");
}, }
MainEvent::InsetsChanged { .. } => { MainEvent::InsetsChanged { .. } => {
// XXX: how to forward this state to applications? // XXX: how to forward this state to applications?
warn!("TODO: handle Android InsetsChanged notification"); warn!("TODO: handle Android InsetsChanged notification");
}, }
unknown => { unknown => {
trace!("Unknown MainEvent {unknown:?} (ignored)"); trace!("Unknown MainEvent {unknown:?} (ignored)");
}, }
} }
} else { } else {
trace!("No main event to handle"); trace!("No main event to handle");
@@ -319,7 +331,7 @@ impl<T: 'static> EventLoop<T> {
}, },
Err(err) => { Err(err) => {
tracing::warn!("Failed to get input events iterator: {err:?}"); tracing::warn!("Failed to get input events iterator: {err:?}");
}, }
} }
// Empty the user event buffer // Empty the user event buffer
@@ -380,13 +392,13 @@ impl<T: 'static> EventLoop<T> {
let phase = match motion_event.action() { let phase = match motion_event.action() {
MotionAction::Down | MotionAction::PointerDown => { MotionAction::Down | MotionAction::PointerDown => {
Some(event::TouchPhase::Started) Some(event::TouchPhase::Started)
}, }
MotionAction::Up | MotionAction::PointerUp => Some(event::TouchPhase::Ended), MotionAction::Up | MotionAction::PointerUp => Some(event::TouchPhase::Ended),
MotionAction::Move => Some(event::TouchPhase::Moved), MotionAction::Move => Some(event::TouchPhase::Moved),
MotionAction::Cancel => Some(event::TouchPhase::Cancelled), MotionAction::Cancel => Some(event::TouchPhase::Cancelled),
_ => { _ => {
None // TODO mouse events None // TODO mouse events
}, }
}; };
if let Some(phase) = phase { if let Some(phase) = phase {
let pointers: Box<dyn Iterator<Item = android_activity::input::Pointer<'_>>> = let pointers: Box<dyn Iterator<Item = android_activity::input::Pointer<'_>>> =
@@ -395,19 +407,18 @@ impl<T: 'static> EventLoop<T> {
Box::new(std::iter::once( Box::new(std::iter::once(
motion_event.pointer_at_index(motion_event.pointer_index()), motion_event.pointer_at_index(motion_event.pointer_index()),
)) ))
}, }
event::TouchPhase::Moved | event::TouchPhase::Cancelled => { event::TouchPhase::Moved | event::TouchPhase::Cancelled => {
Box::new(motion_event.pointers()) Box::new(motion_event.pointers())
}, }
}; };
for pointer in pointers { for pointer in pointers {
let location = let location = PhysicalPosition {
PhysicalPosition { x: pointer.x() as _, y: pointer.y() as _ }; x: pointer.x() as _,
trace!( y: pointer.y() as _,
"Input event {device_id:?}, {phase:?}, loc={location:?}, \ };
pointer={pointer:?}" trace!("Input event {device_id:?}, {phase:?}, loc={location:?}, pointer={pointer:?}");
);
let event = event::Event::WindowEvent { let event = event::Event::WindowEvent {
window_id, window_id,
event: event::WindowEvent::Touch(event::Touch { event: event::WindowEvent::Touch(event::Touch {
@@ -421,18 +432,17 @@ impl<T: 'static> EventLoop<T> {
callback(event, self.window_target()); callback(event, self.window_target());
} }
} }
}, }
InputEvent::KeyEvent(key) => { InputEvent::KeyEvent(key) => {
match key.key_code() { match key.key_code() {
// Flag keys related to volume as unhandled. While winit does not have a way for // Flag keys related to volume as unhandled. While winit does not have a way for applications
// applications to configure what keys to flag as handled, // to configure what keys to flag as handled, this appears to be a good default until winit
// this appears to be a good default until winit
// can be configured. // can be configured.
Keycode::VolumeUp | Keycode::VolumeDown | Keycode::VolumeMute Keycode::VolumeUp | Keycode::VolumeDown | Keycode::VolumeMute => {
if self.ignore_volume_keys => if self.ignore_volume_keys {
{ input_status = InputStatus::Unhandled
input_status = InputStatus::Unhandled }
}, }
keycode => { keycode => {
let state = match key.action() { let state = match key.action() {
KeyAction::Down => event::ElementState::Pressed, KeyAction::Down => event::ElementState::Pressed,
@@ -446,13 +456,6 @@ impl<T: 'static> EventLoop<T> {
&mut self.combining_accent, &mut self.combining_accent,
); );
let logical_key = keycodes::to_logical(key_char, keycode);
let text = if state == event::ElementState::Pressed {
logical_key.to_text().map(smol_str::SmolStr::new)
} else {
None
};
let event = event::Event::WindowEvent { let event = event::Event::WindowEvent {
window_id: window::WindowId(WindowId), window_id: window::WindowId(WindowId),
event: event::WindowEvent::KeyboardInput { event: event::WindowEvent::KeyboardInput {
@@ -460,22 +463,22 @@ impl<T: 'static> EventLoop<T> {
event: event::KeyEvent { event: event::KeyEvent {
state, state,
physical_key: keycodes::to_physical_key(keycode), physical_key: keycodes::to_physical_key(keycode),
logical_key, logical_key: keycodes::to_logical(key_char, keycode),
location: keycodes::to_location(keycode), location: keycodes::to_location(keycode),
repeat: key.repeat_count() > 0, repeat: key.repeat_count() > 0,
text, text: None,
platform_specific: KeyEventExtra {}, platform_specific: KeyEventExtra {},
}, },
is_synthetic: false, is_synthetic: false,
}, },
}; };
callback(event, self.window_target()); callback(event, self.window_target());
}, }
} }
}, }
_ => { _ => {
warn!("Unknown android_activity input event {event:?}") warn!("Unknown android_activity input event {event:?}")
}, }
} }
input_status input_status
@@ -496,13 +499,13 @@ impl<T: 'static> EventLoop<T> {
match self.pump_events(None, &mut event_handler) { match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
break Ok(()); break Ok(());
}, }
PumpStatus::Exit(code) => { PumpStatus::Exit(code) => {
break Err(EventLoopError::ExitFailure(code)); break Err(EventLoopError::ExitFailure(code));
}, }
_ => { _ => {
continue; continue;
}, }
} }
} }
} }
@@ -558,7 +561,7 @@ impl<T: 'static> EventLoop<T> {
ControlFlow::Poll => Some(Duration::ZERO), ControlFlow::Poll => Some(Duration::ZERO),
ControlFlow::WaitUntil(wait_deadline) => { ControlFlow::WaitUntil(wait_deadline) => {
Some(wait_deadline.saturating_duration_since(start)) Some(wait_deadline.saturating_duration_since(start))
}, }
}; };
min_timeout(control_flow_timeout, timeout) min_timeout(control_flow_timeout, timeout)
@@ -571,9 +574,8 @@ impl<T: 'static> EventLoop<T> {
match poll_event { match poll_event {
android_activity::PollEvent::Wake => { android_activity::PollEvent::Wake => {
// In the X11 backend it's noted that too many false-positive wake ups // In the X11 backend it's noted that too many false-positive wake ups
// would cause the event loop to run continuously. They handle this by // would cause the event loop to run continuously. They handle this by re-checking
// re-checking for pending events (assuming they cover all // for pending events (assuming they cover all valid reasons for a wake up).
// valid reasons for a wake up).
// //
// For now, user_events and redraw_requests are the only reasons to expect // For now, user_events and redraw_requests are the only reasons to expect
// a wake up here so we can ignore the wake up if there are no events/requests. // a wake up here so we can ignore the wake up if there are no events/requests.
@@ -584,26 +586,35 @@ impl<T: 'static> EventLoop<T> {
{ {
return; return;
} }
}, }
android_activity::PollEvent::Timeout => {}, android_activity::PollEvent::Timeout => {}
android_activity::PollEvent::Main(event) => { android_activity::PollEvent::Main(event) => {
main_event = Some(event); main_event = Some(event);
}, }
unknown_event => { unknown_event => {
warn!("Unknown poll event {unknown_event:?} (ignored)"); warn!("Unknown poll event {unknown_event:?} (ignored)");
}, }
} }
self.cause = match self.control_flow() { self.cause = match self.control_flow() {
ControlFlow::Poll => StartCause::Poll, ControlFlow::Poll => StartCause::Poll,
ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None }, ControlFlow::Wait => StartCause::WaitCancelled {
start,
requested_resume: None,
},
ControlFlow::WaitUntil(deadline) => { ControlFlow::WaitUntil(deadline) => {
if Instant::now() < deadline { if Instant::now() < deadline {
StartCause::WaitCancelled { start, requested_resume: Some(deadline) } StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
}
} else { } else {
StartCause::ResumeTimeReached { start, requested_resume: deadline } StartCause::ResumeTimeReached {
start,
requested_resume: deadline,
}
} }
}, }
}; };
self.single_iteration(main_event, &mut callback); self.single_iteration(main_event, &mut callback);
@@ -646,14 +657,16 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
impl<T> EventLoopProxy<T> { impl<T> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed<T>> { pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed<T>> {
self.user_events_sender.send(event).map_err(|err| event_loop::EventLoopClosed(err.0))?; self.user_events_sender
.send(event)
.map_err(|err| event_loop::EventLoopClosed(err.0))?;
self.waker.wake(); self.waker.wake();
Ok(()) Ok(())
} }
} }
pub struct ActiveEventLoop { pub struct ActiveEventLoop {
pub(crate) app: AndroidApp, app: AndroidApp,
control_flow: Cell<ControlFlow>, control_flow: Cell<ControlFlow>,
exit: Cell<bool>, exit: Cell<bool>,
redraw_requester: RedrawRequester, redraw_requester: RedrawRequester,
@@ -666,7 +679,9 @@ impl ActiveEventLoop {
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor { pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor {
let _ = source.inner; let _ = source.inner;
CustomCursor { inner: PlatformCustomCursor } CustomCursor {
inner: PlatformCustomCursor,
}
} }
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> { pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
@@ -684,17 +699,14 @@ impl ActiveEventLoop {
rwh_05::RawDisplayHandle::Android(rwh_05::AndroidDisplayHandle::empty()) rwh_05::RawDisplayHandle::Android(rwh_05::AndroidDisplayHandle::empty())
} }
#[inline]
pub fn system_theme(&self) -> Option<Theme> {
None
}
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
#[inline] #[inline]
pub fn raw_display_handle_rwh_06( pub fn raw_display_handle_rwh_06(
&self, &self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> { ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::Android(rwh_06::AndroidDisplayHandle::new())) Ok(rwh_06::RawDisplayHandle::Android(
rwh_06::AndroidDisplayHandle::new(),
))
} }
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
@@ -786,7 +798,10 @@ impl Window {
) -> Result<Self, error::OsError> { ) -> Result<Self, error::OsError> {
// FIXME this ignores requested window attributes // FIXME this ignores requested window attributes
Ok(Self { app: el.app.clone(), redraw_requester: el.redraw_requester.clone() }) Ok(Self {
app: el.app.clone(),
redraw_requester: el.redraw_requester.clone(),
})
} }
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) { pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) {
@@ -915,13 +930,7 @@ impl Window {
pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {} pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
pub fn set_ime_allowed(&self, allowed: bool) { pub fn set_ime_allowed(&self, _allowed: bool) {}
if allowed {
self.app.show_soft_input(true);
} else {
self.app.hide_soft_input(true);
}
}
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {} pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}
@@ -932,31 +941,41 @@ impl Window {
pub fn set_cursor(&self, _: Cursor) {} pub fn set_cursor(&self, _: Cursor) {}
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> { pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(error::NotSupportedError::new())) Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
} }
pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), error::ExternalError> { pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(error::NotSupportedError::new())) Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
} }
pub fn set_cursor_visible(&self, _: bool) {} pub fn set_cursor_visible(&self, _: bool) {}
pub fn drag_window(&self) -> Result<(), error::ExternalError> { pub fn drag_window(&self) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(error::NotSupportedError::new())) Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
} }
pub fn drag_resize_window( pub fn drag_resize_window(
&self, &self,
_direction: ResizeDirection, _direction: ResizeDirection,
) -> Result<(), error::ExternalError> { ) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(error::NotSupportedError::new())) Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
} }
#[inline] #[inline]
pub fn show_window_menu(&self, _position: Position) {} pub fn show_window_menu(&self, _position: Position) {}
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> { pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(error::NotSupportedError::new())) Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
} }
#[cfg(feature = "rwh_04")] #[cfg(feature = "rwh_04")]
@@ -966,11 +985,7 @@ impl Window {
if let Some(native_window) = self.app.native_window().as_ref() { if let Some(native_window) = self.app.native_window().as_ref() {
native_window.raw_window_handle() native_window.raw_window_handle()
} else { } else {
panic!( panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
"Cannot get the native window, it's null and will always be null before \
Event::Resumed and after Event::Suspended. Make sure you only call this function \
between those events."
);
} }
} }
@@ -981,11 +996,7 @@ impl Window {
if let Some(native_window) = self.app.native_window().as_ref() { if let Some(native_window) = self.app.native_window().as_ref() {
native_window.raw_window_handle() native_window.raw_window_handle()
} else { } else {
panic!( panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
"Cannot get the native window, it's null and will always be null before \
Event::Resumed and after Event::Suspended. Make sure you only call this function \
between those events."
);
} }
} }
@@ -1003,11 +1014,7 @@ impl Window {
if let Some(native_window) = self.app.native_window().as_ref() { if let Some(native_window) = self.app.native_window().as_ref() {
native_window.raw_window_handle() native_window.raw_window_handle()
} else { } else {
tracing::error!( tracing::error!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
"Cannot get the native window, it's null and will always be null before \
Event::Resumed and after Event::Suspended. Make sure you only call this function \
between those events."
);
Err(rwh_06::HandleError::Unavailable) Err(rwh_06::HandleError::Unavailable)
} }
} }
@@ -1016,7 +1023,9 @@ impl Window {
pub fn raw_display_handle_rwh_06( pub fn raw_display_handle_rwh_06(
&self, &self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> { ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::Android(rwh_06::AndroidDisplayHandle::new())) Ok(rwh_06::RawDisplayHandle::Android(
rwh_06::AndroidDisplayHandle::new(),
))
} }
pub fn config(&self) -> ConfigurationRef { pub fn config(&self) -> ConfigurationRef {
@@ -1093,7 +1102,11 @@ impl MonitorHandle {
} }
pub fn scale_factor(&self) -> f64 { pub fn scale_factor(&self) -> f64 {
self.app.config().density().map(|dpi| dpi as f64 / 160.0).unwrap_or(1.0) self.app
.config()
.density()
.map(|dpi| dpi as f64 / 160.0)
.unwrap_or(1.0)
} }
pub fn refresh_rate_millihertz(&self) -> Option<u32> { pub fn refresh_rate_millihertz(&self) -> Option<u32> {

View File

@@ -0,0 +1,103 @@
use icrate::Foundation::{MainThreadMarker, NSObject, NSObjectProtocol};
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
use super::app_state::{self, EventWrapper};
use super::uikit::{UIApplication, UIWindow};
use super::window::WinitUIWindow;
use crate::{
event::{Event, WindowEvent},
window::WindowId as RootWindowId,
};
declare_class!(
pub struct AppDelegate;
unsafe impl ClassType for AppDelegate {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "WinitApplicationDelegate";
}
impl DeclaredClass for AppDelegate {}
// UIApplicationDelegate protocol
unsafe impl AppDelegate {
#[method(application:didFinishLaunchingWithOptions:)]
fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
app_state::did_finish_launching(MainThreadMarker::new().unwrap());
true
}
#[method(applicationDidBecomeActive:)]
fn did_become_active(&self, _application: &UIApplication) {
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed))
}
#[method(applicationWillResignActive:)]
fn will_resign_active(&self, _application: &UIApplication) {
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended))
}
#[method(applicationWillEnterForeground:)]
fn will_enter_foreground(&self, application: &UIApplication) {
self.send_occluded_event_for_all_windows(application, false);
}
#[method(applicationDidEnterBackground:)]
fn did_enter_background(&self, application: &UIApplication) {
self.send_occluded_event_for_all_windows(application, true);
}
#[method(applicationWillTerminate:)]
fn will_terminate(&self, application: &UIApplication) {
let mut events = Vec::new();
for window in application.windows().iter() {
if window.is_kind_of::<WinitUIWindow>() {
// SAFETY: We just checked that the window is a `winit` window
let window = unsafe {
let ptr: *const UIWindow = window;
let ptr: *const WinitUIWindow = ptr.cast();
&*ptr
};
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::Destroyed,
}));
}
}
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_events(mtm, events);
app_state::terminated(mtm);
}
#[method(applicationDidReceiveMemoryWarning:)]
fn did_receive_memory_warning(&self, _application: &UIApplication) {
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning))
}
}
);
impl AppDelegate {
fn send_occluded_event_for_all_windows(&self, application: &UIApplication, occluded: bool) {
let mut events = Vec::new();
for window in application.windows().iter() {
if window.is_kind_of::<WinitUIWindow>() {
// SAFETY: We just checked that the window is a `winit` window
let window = unsafe {
let ptr: *const UIWindow = window;
let ptr: *const WinitUIWindow = ptr.cast();
&*ptr
};
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::Occluded(occluded),
}));
}
}
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_events(mtm, events);
}
}

View File

@@ -1,11 +1,14 @@
#![deny(unused_results)] #![deny(unused_results)]
use std::cell::{RefCell, RefMut}; use std::{
use std::collections::HashSet; cell::{RefCell, RefMut},
use std::os::raw::c_void; collections::HashSet,
use std::sync::{Arc, Mutex, OnceLock}; fmt, mem,
use std::time::Instant; os::raw::c_void,
use std::{fmt, mem, ptr}; ptr,
sync::{Arc, Mutex, OnceLock},
time::Instant,
};
use core_foundation::base::CFRelease; use core_foundation::base::CFRelease;
use core_foundation::date::CFAbsoluteTimeGetCurrent; use core_foundation::date::CFAbsoluteTimeGetCurrent;
@@ -13,20 +16,21 @@ use core_foundation::runloop::{
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate, kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
}; };
use objc2::rc::Retained; use icrate::Foundation::{
CGRect, CGSize, MainThreadMarker, NSInteger, NSOperatingSystemVersion, NSProcessInfo,
};
use objc2::rc::Id;
use objc2::runtime::AnyObject; use objc2::runtime::AnyObject;
use objc2::{msg_send, sel}; use objc2::{msg_send, sel};
use objc2_foundation::{
CGRect, CGSize, MainThreadMarker, NSInteger, NSObjectProtocol, NSOperatingSystemVersion,
NSProcessInfo,
};
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
use super::uikit::UIView;
use super::window::WinitUIWindow; use super::window::WinitUIWindow;
use crate::dpi::PhysicalSize; use crate::{
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent}; dpi::PhysicalSize,
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow}; event::{Event, InnerSizeWriter, StartCause, WindowEvent},
use crate::window::WindowId as RootWindowId; event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow},
window::WindowId as RootWindowId,
};
macro_rules! bug { macro_rules! bug {
($($msg:tt)*) => { ($($msg:tt)*) => {
@@ -72,7 +76,7 @@ pub(crate) enum EventWrapper {
#[derive(Debug)] #[derive(Debug)]
pub struct ScaleFactorChanged { pub struct ScaleFactorChanged {
pub(super) window: Retained<WinitUIWindow>, pub(super) window: Id<WinitUIWindow>,
pub(super) suggested_size: PhysicalSize<u32>, pub(super) suggested_size: PhysicalSize<u32>,
pub(super) scale_factor: f64, pub(super) scale_factor: f64,
} }
@@ -90,7 +94,13 @@ enum UserCallbackTransitionResult<'a> {
impl Event<HandlePendingUserEvents> { impl Event<HandlePendingUserEvents> {
fn is_redraw(&self) -> bool { fn is_redraw(&self) -> bool {
matches!(self, Event::WindowEvent { event: WindowEvent::RedrawRequested, .. }) matches!(
self,
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
}
)
} }
} }
@@ -99,25 +109,25 @@ impl Event<HandlePendingUserEvents> {
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"] #[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
enum AppStateImpl { enum AppStateImpl {
NotLaunched { NotLaunched {
queued_windows: Vec<Retained<WinitUIWindow>>, queued_windows: Vec<Id<WinitUIWindow>>,
queued_events: Vec<EventWrapper>, queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
}, },
Launching { Launching {
queued_windows: Vec<Retained<WinitUIWindow>>, queued_windows: Vec<Id<WinitUIWindow>>,
queued_events: Vec<EventWrapper>, queued_events: Vec<EventWrapper>,
queued_handler: EventLoopHandler, queued_handler: EventLoopHandler,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
}, },
ProcessingEvents { ProcessingEvents {
handler: EventLoopHandler, handler: EventLoopHandler,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
// special state to deal with reentrancy and prevent mutable aliasing. // special state to deal with reentrancy and prevent mutable aliasing.
InUserCallback { InUserCallback {
queued_events: Vec<EventWrapper>, queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
}, },
ProcessingRedraws { ProcessingRedraws {
handler: EventLoopHandler, handler: EventLoopHandler,
@@ -147,8 +157,6 @@ impl AppState {
// must be mut because plain `static` requires `Sync` // must be mut because plain `static` requires `Sync`
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None); static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
#[allow(unknown_lints)] // New lint below
#[allow(static_mut_refs)] // TODO: Use `MainThreadBound` instead.
let mut guard = unsafe { APP_STATE.borrow_mut() }; let mut guard = unsafe { APP_STATE.borrow_mut() };
if guard.is_none() { if guard.is_none() {
#[inline(never)] #[inline(never)]
@@ -208,7 +216,10 @@ impl AppState {
} }
fn has_launched(&self) -> bool { fn has_launched(&self) -> bool {
!matches!(self.state(), AppStateImpl::NotLaunched { .. } | AppStateImpl::Launching { .. }) !matches!(
self.state(),
AppStateImpl::NotLaunched { .. } | AppStateImpl::Launching { .. }
)
} }
fn has_terminated(&self) -> bool { fn has_terminated(&self) -> bool {
@@ -217,9 +228,11 @@ impl AppState {
fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) { fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) {
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() { let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::NotLaunched { queued_windows, queued_events, queued_gpu_redraws } => { AppStateImpl::NotLaunched {
(queued_windows, queued_events, queued_gpu_redraws) queued_windows,
}, queued_events,
queued_gpu_redraws,
} => (queued_windows, queued_events, queued_gpu_redraws),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::Launching { self.set_state(AppStateImpl::Launching {
@@ -230,16 +243,19 @@ impl AppState {
}); });
} }
fn did_finish_launching_transition( fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) {
&mut self,
) -> (Vec<Retained<WinitUIWindow>>, Vec<EventWrapper>) {
let (windows, events, handler, queued_gpu_redraws) = match self.take_state() { let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Launching { AppStateImpl::Launching {
queued_windows, queued_windows,
queued_events, queued_events,
queued_handler, queued_handler,
queued_gpu_redraws, queued_gpu_redraws,
} => (queued_windows, queued_events, queued_handler, queued_gpu_redraws), } => (
queued_windows,
queued_events,
queued_handler,
queued_gpu_redraws,
),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingEvents { self.set_state(AppStateImpl::ProcessingEvents {
@@ -258,10 +274,17 @@ impl AppState {
} }
let (handler, event) = match (self.control_flow, self.take_state()) { let (handler, event) = match (self.control_flow, self.take_state()) {
(ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => { (ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => (
(waiting_handler, EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll))) waiting_handler,
}, EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)),
(ControlFlow::Wait, AppStateImpl::Waiting { waiting_handler, start }) => ( ),
(
ControlFlow::Wait,
AppStateImpl::Waiting {
waiting_handler,
start,
},
) => (
waiting_handler, waiting_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled { EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
start, start,
@@ -270,7 +293,10 @@ impl AppState {
), ),
( (
ControlFlow::WaitUntil(requested_resume), ControlFlow::WaitUntil(requested_resume),
AppStateImpl::Waiting { waiting_handler, start }, AppStateImpl::Waiting {
waiting_handler,
start,
},
) => { ) => {
let event = if Instant::now() >= requested_resume { let event = if Instant::now() >= requested_resume {
EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached { EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached {
@@ -284,7 +310,7 @@ impl AppState {
})) }))
}; };
(waiting_handler, event) (waiting_handler, event)
}, }
s => bug!("`EventHandler` unexpectedly woke up {:?}", s), s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
}; };
@@ -300,9 +326,18 @@ impl AppState {
// If we're not able to process an event due to recursion or `Init` not having been sent out // If we're not able to process an event due to recursion or `Init` not having been sent out
// yet, then queue the events up. // yet, then queue the events up.
match self.state_mut() { match self.state_mut() {
&mut AppStateImpl::Launching { ref mut queued_events, .. } &mut AppStateImpl::Launching {
| &mut AppStateImpl::NotLaunched { ref mut queued_events, .. } ref mut queued_events,
| &mut AppStateImpl::InUserCallback { ref mut queued_events, .. } => { ..
}
| &mut AppStateImpl::NotLaunched {
ref mut queued_events,
..
}
| &mut AppStateImpl::InUserCallback {
ref mut queued_events,
..
} => {
// A lifetime cast: early returns are not currently handled well with NLL, but // A lifetime cast: early returns are not currently handled well with NLL, but
// polonius handles them well. This transmute is a safe workaround. // polonius handles them well. This transmute is a safe workaround.
return unsafe { return unsafe {
@@ -313,49 +348,60 @@ impl AppState {
queued_events, queued_events,
}) })
}; };
}, }
&mut AppStateImpl::ProcessingEvents { .. } &mut AppStateImpl::ProcessingEvents { .. }
| &mut AppStateImpl::ProcessingRedraws { .. } => {}, | &mut AppStateImpl::ProcessingRedraws { .. } => {}
s @ &mut AppStateImpl::PollFinished { .. } s @ &mut AppStateImpl::PollFinished { .. }
| s @ &mut AppStateImpl::Waiting { .. } | s @ &mut AppStateImpl::Waiting { .. }
| s @ &mut AppStateImpl::Terminated => { | s @ &mut AppStateImpl::Terminated => {
bug!("unexpected attempted to process an event {:?}", s) bug!("unexpected attempted to process an event {:?}", s)
}, }
} }
let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) = match self let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) =
.take_state() match self.take_state() {
{ AppStateImpl::Launching { .. }
AppStateImpl::Launching { .. } | AppStateImpl::NotLaunched { .. }
| AppStateImpl::NotLaunched { .. } | AppStateImpl::InUserCallback { .. } => unreachable!(),
| AppStateImpl::InUserCallback { .. } => unreachable!(), AppStateImpl::ProcessingEvents {
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => { handler,
(handler, queued_gpu_redraws, active_control_flow, false) queued_gpu_redraws,
}, active_control_flow,
AppStateImpl::ProcessingRedraws { handler, active_control_flow } => { } => (handler, queued_gpu_redraws, active_control_flow, false),
(handler, Default::default(), active_control_flow, true) AppStateImpl::ProcessingRedraws {
}, handler,
AppStateImpl::PollFinished { .. } active_control_flow,
| AppStateImpl::Waiting { .. } } => (handler, Default::default(), active_control_flow, true),
| AppStateImpl::Terminated => unreachable!(), AppStateImpl::PollFinished { .. }
}; | AppStateImpl::Waiting { .. }
| AppStateImpl::Terminated => unreachable!(),
};
self.set_state(AppStateImpl::InUserCallback { self.set_state(AppStateImpl::InUserCallback {
queued_events: Vec::new(), queued_events: Vec::new(),
queued_gpu_redraws, queued_gpu_redraws,
}); });
UserCallbackTransitionResult::Success { handler, active_control_flow, processing_redraws } UserCallbackTransitionResult::Success {
handler,
active_control_flow,
processing_redraws,
}
} }
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> { fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> {
let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() { let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => { AppStateImpl::ProcessingEvents {
(handler, queued_gpu_redraws, active_control_flow) handler,
}, queued_gpu_redraws,
active_control_flow,
} => (handler, queued_gpu_redraws, active_control_flow),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingRedraws { handler, active_control_flow }); self.set_state(AppStateImpl::ProcessingRedraws {
handler,
active_control_flow,
});
queued_gpu_redraws queued_gpu_redraws
} }
@@ -364,9 +410,10 @@ impl AppState {
return; return;
} }
let (waiting_handler, old) = match self.take_state() { let (waiting_handler, old) = match self.take_state() {
AppStateImpl::ProcessingRedraws { handler, active_control_flow } => { AppStateImpl::ProcessingRedraws {
(handler, active_control_flow) handler,
}, active_control_flow,
} => (handler, active_control_flow),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
@@ -374,30 +421,41 @@ impl AppState {
match (old, new) { match (old, new) {
(ControlFlow::Wait, ControlFlow::Wait) => { (ControlFlow::Wait, ControlFlow::Wait) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { waiting_handler, start }); self.set_state(AppStateImpl::Waiting {
self.waker.stop() waiting_handler,
}, start,
});
}
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant)) (ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
if old_instant == new_instant => if old_instant == new_instant =>
{ {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { waiting_handler, start }); self.set_state(AppStateImpl::Waiting {
}, waiting_handler,
start,
});
}
(_, ControlFlow::Wait) => { (_, ControlFlow::Wait) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { waiting_handler, start }); self.set_state(AppStateImpl::Waiting {
waiting_handler,
start,
});
self.waker.stop() self.waker.stop()
}, }
(_, ControlFlow::WaitUntil(new_instant)) => { (_, ControlFlow::WaitUntil(new_instant)) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { waiting_handler, start }); self.set_state(AppStateImpl::Waiting {
waiting_handler,
start,
});
self.waker.start_at(new_instant) self.waker.start_at(new_instant)
}, }
// Unlike on macOS, handle Poll to Poll transition here to call the waker // Unlike on macOS, handle Poll to Poll transition here to call the waker
(_, ControlFlow::Poll) => { (_, ControlFlow::Poll) => {
self.set_state(AppStateImpl::PollFinished { waiting_handler }); self.set_state(AppStateImpl::PollFinished { waiting_handler });
self.waker.start() self.waker.start()
}, }
} }
} }
@@ -417,41 +475,54 @@ impl AppState {
} }
} }
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWindow>) { pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id<WinitUIWindow>) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
match this.state_mut() { match this.state_mut() {
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } => { &mut AppStateImpl::NotLaunched {
return queued_windows.push(window.clone()) ref mut queued_windows,
}, ..
} => return queued_windows.push(window.clone()),
&mut AppStateImpl::ProcessingEvents { .. } &mut AppStateImpl::ProcessingEvents { .. }
| &mut AppStateImpl::InUserCallback { .. } | &mut AppStateImpl::InUserCallback { .. }
| &mut AppStateImpl::ProcessingRedraws { .. } => {}, | &mut AppStateImpl::ProcessingRedraws { .. } => {}
s @ &mut AppStateImpl::Launching { .. } s @ &mut AppStateImpl::Launching { .. }
| s @ &mut AppStateImpl::Waiting { .. } | s @ &mut AppStateImpl::Waiting { .. }
| s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s), | s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s),
&mut AppStateImpl::Terminated => { &mut AppStateImpl::Terminated => {
panic!("Attempt to create a `Window` after the app has terminated") panic!("Attempt to create a `Window` after the app has terminated")
}, }
} }
drop(this); drop(this);
window.makeKeyAndVisible(); window.makeKeyAndVisible();
} }
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) { pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUIWindow>) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
match this.state_mut() { match this.state_mut() {
&mut AppStateImpl::NotLaunched { ref mut queued_gpu_redraws, .. } &mut AppStateImpl::NotLaunched {
| &mut AppStateImpl::Launching { ref mut queued_gpu_redraws, .. } ref mut queued_gpu_redraws,
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. } ..
| &mut AppStateImpl::InUserCallback { ref mut queued_gpu_redraws, .. } => { }
| &mut AppStateImpl::Launching {
ref mut queued_gpu_redraws,
..
}
| &mut AppStateImpl::ProcessingEvents {
ref mut queued_gpu_redraws,
..
}
| &mut AppStateImpl::InUserCallback {
ref mut queued_gpu_redraws,
..
} => {
let _ = queued_gpu_redraws.insert(window); let _ = queued_gpu_redraws.insert(window);
}, }
s @ &mut AppStateImpl::ProcessingRedraws { .. } s @ &mut AppStateImpl::ProcessingRedraws { .. }
| s @ &mut AppStateImpl::Waiting { .. } | s @ &mut AppStateImpl::Waiting { .. }
| s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s), | s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s),
&mut AppStateImpl::Terminated => { &mut AppStateImpl::Terminated => {
panic!("Attempt to create a `Window` after the app has terminated") panic!("Attempt to create a `Window` after the app has terminated")
}, }
} }
} }
@@ -495,8 +566,10 @@ pub fn did_finish_launching(mtm: MainThreadMarker) {
let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition(); let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition();
let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init))) let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents(
.chain(events); StartCause::Init,
)))
.chain(events);
handle_nonuser_events(mtm, events); handle_nonuser_events(mtm, events);
// the above window dance hack, could possibly trigger new windows to be created. // the above window dance hack, could possibly trigger new windows to be created.
@@ -536,7 +609,7 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => { UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
queued_events.extend(events); queued_events.extend(events);
return; return;
}, }
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
handler, handler,
active_control_flow, active_control_flow,
@@ -557,7 +630,7 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
); );
} }
handler.handle_event(event) handler.handle_event(event)
}, }
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event), EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
} }
} }
@@ -565,16 +638,18 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let queued_events = match this.state_mut() { let queued_events = match this.state_mut() {
&mut AppStateImpl::InUserCallback { ref mut queued_events, queued_gpu_redraws: _ } => { &mut AppStateImpl::InUserCallback {
mem::take(queued_events) ref mut queued_events,
}, queued_gpu_redraws: _,
} => mem::take(queued_events),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
if queued_events.is_empty() { if queued_events.is_empty() {
let queued_gpu_redraws = match this.take_state() { let queued_gpu_redraws = match this.take_state() {
AppStateImpl::InUserCallback { queued_events: _, queued_gpu_redraws } => { AppStateImpl::InUserCallback {
queued_gpu_redraws queued_events: _,
}, queued_gpu_redraws,
} => queued_gpu_redraws,
_ => unreachable!(), _ => unreachable!(),
}; };
this.app_state = Some(if processing_redraws { this.app_state = Some(if processing_redraws {
@@ -582,9 +657,16 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
queued_gpu_redraws.is_empty(), queued_gpu_redraws.is_empty(),
"redraw queued while processing redraws" "redraw queued while processing redraws"
); );
AppStateImpl::ProcessingRedraws { handler, active_control_flow } AppStateImpl::ProcessingRedraws {
handler,
active_control_flow,
}
} else { } else {
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } AppStateImpl::ProcessingEvents {
handler,
queued_gpu_redraws,
active_control_flow,
}
}); });
break; break;
} }
@@ -597,13 +679,12 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
tracing::info!("processing `RedrawRequested` during the main event loop"); tracing::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !event.is_redraw() { } else if processing_redraws && !event.is_redraw() {
tracing::warn!( tracing::warn!(
"processing non-`RedrawRequested` event after the main event loop: \ "processing non-`RedrawRequested` event after the main event loop: {:#?}",
{:#?}",
event event
); );
} }
handler.handle_event(event) handler.handle_event(event)
}, }
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event), EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
} }
} }
@@ -616,7 +697,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
match this.try_user_callback_transition() { match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { .. } => { UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
bug!("unexpected attempted to process an event") bug!("unexpected attempted to process an event")
}, }
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
handler, handler,
active_control_flow, active_control_flow,
@@ -633,16 +714,18 @@ fn handle_user_events(mtm: MainThreadMarker) {
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let queued_events = match this.state_mut() { let queued_events = match this.state_mut() {
&mut AppStateImpl::InUserCallback { ref mut queued_events, queued_gpu_redraws: _ } => { &mut AppStateImpl::InUserCallback {
mem::take(queued_events) ref mut queued_events,
}, queued_gpu_redraws: _,
} => mem::take(queued_events),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
if queued_events.is_empty() { if queued_events.is_empty() {
let queued_gpu_redraws = match this.take_state() { let queued_gpu_redraws = match this.take_state() {
AppStateImpl::InUserCallback { queued_events: _, queued_gpu_redraws } => { AppStateImpl::InUserCallback {
queued_gpu_redraws queued_events: _,
}, queued_gpu_redraws,
} => queued_gpu_redraws,
_ => unreachable!(), _ => unreachable!(),
}; };
this.app_state = Some(AppStateImpl::ProcessingEvents { this.app_state = Some(AppStateImpl::ProcessingEvents {
@@ -665,35 +748,13 @@ fn handle_user_events(mtm: MainThreadMarker) {
} }
} }
pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, occluded: bool) {
let mtm = MainThreadMarker::from(application);
let mut events = Vec::new();
#[allow(deprecated)]
for window in application.windows().iter() {
if window.is_kind_of::<WinitUIWindow>() {
// SAFETY: We just checked that the window is a `winit` window
let window = unsafe {
let ptr: *const UIWindow = window;
let ptr: *const WinitUIWindow = ptr.cast();
&*ptr
};
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::Occluded(occluded),
}));
}
}
handle_nonuser_events(mtm, events);
}
pub fn handle_main_events_cleared(mtm: MainThreadMarker) { pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
if !this.has_launched() || this.has_terminated() { if !this.has_launched() || this.has_terminated() {
return; return;
} }
match this.state_mut() { match this.state_mut() {
AppStateImpl::ProcessingEvents { .. } => {}, AppStateImpl::ProcessingEvents { .. } => {}
_ => bug!("`ProcessingRedraws` happened unexpectedly"), _ => bug!("`ProcessingRedraws` happened unexpectedly"),
}; };
drop(this); drop(this);
@@ -721,27 +782,7 @@ pub fn handle_events_cleared(mtm: MainThreadMarker) {
AppState::get_mut(mtm).events_cleared_transition(); AppState::get_mut(mtm).events_cleared_transition();
} }
pub(crate) fn terminated(application: &UIApplication) { pub fn terminated(mtm: MainThreadMarker) {
let mtm = MainThreadMarker::from(application);
let mut events = Vec::new();
#[allow(deprecated)]
for window in application.windows().iter() {
if window.is_kind_of::<WinitUIWindow>() {
// SAFETY: We just checked that the window is a `winit` window
let window = unsafe {
let ptr: *const UIWindow = window;
let ptr: *const WinitUIWindow = ptr.cast();
&*ptr
};
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::Destroyed,
}));
}
}
handle_nonuser_events(mtm, events);
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let mut handler = this.terminated_transition(); let mut handler = this.terminated_transition();
drop(this); drop(this);
@@ -750,7 +791,11 @@ pub(crate) fn terminated(application: &UIApplication) {
} }
fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged) { fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged) {
let ScaleFactorChanged { suggested_size, scale_factor, window } = event; let ScaleFactorChanged {
suggested_size,
scale_factor,
window,
} = event;
let new_inner_size = Arc::new(Mutex::new(suggested_size)); let new_inner_size = Arc::new(Mutex::new(suggested_size));
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id: RootWindowId(window.id()), window_id: RootWindowId(window.id()),
@@ -769,7 +814,7 @@ fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged)
view.setFrame(new_frame); view.setFrame(new_frame);
} }
fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Retained<UIView>, CGRect) { fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Id<UIView>, CGRect) {
let view_controller = window.rootViewController().unwrap(); let view_controller = window.rootViewController().unwrap();
let view = view_controller.view().unwrap(); let view = view_controller.view().unwrap();
let bounds = window.bounds(); let bounds = window.bounds();
@@ -801,7 +846,7 @@ impl EventLoopWaker {
// future, but that gets changed to fire immediately in did_finish_launching // future, but that gets changed to fire immediately in did_finish_launching
let timer = CFRunLoopTimerCreate( let timer = CFRunLoopTimerCreate(
ptr::null_mut(), ptr::null_mut(),
f64::MAX, std::f64::MAX,
0.000_000_1, 0.000_000_1,
0, 0,
0, 0,
@@ -815,11 +860,11 @@ impl EventLoopWaker {
} }
fn stop(&mut self) { fn stop(&mut self) {
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, f64::MAX) } unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MAX) }
} }
fn start(&mut self) { fn start(&mut self) {
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, f64::MIN) } unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MIN) }
} }
fn start_at(&mut self, instant: Instant) { fn start_at(&mut self, instant: Instant) {
@@ -905,21 +950,28 @@ fn meets_requirements(
} }
fn get_version() -> NSOperatingSystemVersion { fn get_version() -> NSOperatingSystemVersion {
let process_info = NSProcessInfo::processInfo(); unsafe {
let atleast_ios_8 = process_info.respondsToSelector(sel!(operatingSystemVersion)); let process_info = NSProcessInfo::processInfo();
// Winit requires atleast iOS 8 because no one has put the time into supporting earlier os let atleast_ios_8: bool = msg_send![
// versions. Older iOS versions are increasingly difficult to test. For example, Xcode 11 does &process_info,
// not support debugging on devices with an iOS version of less than 8. Another example, in respondsToSelector: sel!(operatingSystemVersion)
// order to use an iOS simulator older than iOS 8, you must download an older version of Xcode ];
// (<9), and at least Xcode 7 has been tested to not even run on macOS 10.15 - Xcode 8 might? // winit requires atleast iOS 8 because no one has put the time into supporting earlier os versions.
// // Older iOS versions are increasingly difficult to test. For example, Xcode 11 does not support
// The minimum required iOS version is likely to grow in the future. // debugging on devices with an iOS version of less than 8. Another example, in order to use an iOS
assert!(atleast_ios_8, "`winit` requires iOS version 8 or greater"); // simulator older than iOS 8, you must download an older version of Xcode (<9), and at least Xcode 7
process_info.operatingSystemVersion() // has been tested to not even run on macOS 10.15 - Xcode 8 might?
//
// The minimum required iOS version is likely to grow in the future.
assert!(atleast_ios_8, "`winit` requires iOS version 8 or greater");
process_info.operatingSystemVersion()
}
} }
pub fn os_capabilities() -> OSCapabilities { pub fn os_capabilities() -> OSCapabilities {
// Cache the version lookup for efficiency // Cache the version lookup for efficiency
static OS_CAPABILITIES: OnceLock<OSCapabilities> = OnceLock::new(); static OS_CAPABILITIES: OnceLock<OSCapabilities> = OnceLock::new();
OS_CAPABILITIES.get_or_init(|| OSCapabilities::from_os_version(get_version())).clone() OS_CAPABILITIES
.get_or_init(|| OSCapabilities::from_os_version(get_version()))
.clone()
} }

View File

@@ -1,8 +1,10 @@
use std::collections::VecDeque; use std::{
use std::ffi::{c_char, c_int, c_void}; collections::VecDeque,
use std::marker::PhantomData; ffi::c_void,
use std::ptr::{self, NonNull}; marker::PhantomData,
use std::sync::mpsc::{self, Receiver, Sender}; ptr,
sync::mpsc::{self, Receiver, Sender},
};
use core_foundation::base::{CFIndex, CFRelease}; use core_foundation::base::{CFIndex, CFRelease};
use core_foundation::runloop::{ use core_foundation::runloop::{
@@ -11,29 +13,26 @@ use core_foundation::runloop::{
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate, CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp, CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
}; };
use objc2::rc::Retained; use icrate::Foundation::{MainThreadMarker, NSString};
use objc2::{msg_send_id, ClassType}; use objc2::ClassType;
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject};
use objc2_ui_kit::{ use crate::{
UIApplication, UIApplicationDidBecomeActiveNotification, error::EventLoopError,
UIApplicationDidEnterBackgroundNotification, UIApplicationDidFinishLaunchingNotification, event::Event,
UIApplicationDidReceiveMemoryWarningNotification, UIApplicationMain, event_loop::{
UIApplicationWillEnterForegroundNotification, UIApplicationWillResignActiveNotification, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed,
UIApplicationWillTerminateNotification, UIDevice, UIScreen, UIUserInterfaceIdiom, },
platform::ios::Idiom,
platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents},
window::{CustomCursor, CustomCursorSource},
}; };
use crate::error::EventLoopError; use super::{app_delegate::AppDelegate, uikit::UIUserInterfaceIdiom};
use crate::event::Event;
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed,
};
use crate::platform::ios::Idiom;
use crate::platform_impl::ios::app_state::{EventLoopHandler, HandlePendingUserEvents};
use crate::window::{CustomCursor, CustomCursorSource, Theme};
use super::app_state::{send_occluded_event_for_all_windows, AppState, EventWrapper};
use super::notification_center::create_observer;
use super::{app_state, monitor, MonitorHandle}; use super::{app_state, monitor, MonitorHandle};
use super::{
app_state::AppState,
uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen},
};
#[derive(Debug)] #[derive(Debug)]
pub struct ActiveEventLoop { pub struct ActiveEventLoop {
@@ -43,7 +42,9 @@ pub struct ActiveEventLoop {
impl ActiveEventLoop { impl ActiveEventLoop {
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor { pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor {
let _ = source.inner; let _ = source.inner;
CustomCursor { inner: super::PlatformCustomCursor } CustomCursor {
inner: super::PlatformCustomCursor,
}
} }
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> { pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
@@ -51,8 +52,7 @@ impl ActiveEventLoop {
} }
pub fn primary_monitor(&self) -> Option<MonitorHandle> { pub fn primary_monitor(&self) -> Option<MonitorHandle> {
#[allow(deprecated)] Some(MonitorHandle::new(UIScreen::main(self.mtm)))
Some(MonitorHandle::new(UIScreen::mainScreen(self.mtm)))
} }
#[inline] #[inline]
@@ -64,17 +64,14 @@ impl ActiveEventLoop {
rwh_05::RawDisplayHandle::UiKit(rwh_05::UiKitDisplayHandle::empty()) rwh_05::RawDisplayHandle::UiKit(rwh_05::UiKitDisplayHandle::empty())
} }
#[inline]
pub fn system_theme(&self) -> Option<Theme> {
None
}
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
#[inline] #[inline]
pub fn raw_display_handle_rwh_06( pub fn raw_display_handle_rwh_06(
&self, &self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> { ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::UiKit(rwh_06::UiKitDisplayHandle::new())) Ok(rwh_06::RawDisplayHandle::UiKit(
rwh_06::UiKitDisplayHandle::new(),
))
} }
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
@@ -129,7 +126,7 @@ fn map_user_event<T: 'static>(
for event in receiver.try_iter() { for event in receiver.try_iter() {
(handler)(Event::UserEvent(event), window_target); (handler)(Event::UserEvent(event), window_target);
} }
}, }
} }
} }
@@ -138,18 +135,6 @@ pub struct EventLoop<T: 'static> {
sender: Sender<T>, sender: Sender<T>,
receiver: Receiver<T>, receiver: Receiver<T>,
window_target: RootActiveEventLoop, window_target: RootActiveEventLoop,
// Since iOS 9.0, we no longer need to remove the observers before they are deallocated; the
// system instead cleans it up next time it would have posted a notification to it.
//
// Though we do still need to keep the observers around to prevent them from being deallocated.
_did_finish_launching_observer: Retained<NSObject>,
_did_become_active_observer: Retained<NSObject>,
_will_resign_active_observer: Retained<NSObject>,
_will_enter_foreground_observer: Retained<NSObject>,
_did_enter_background_observer: Retained<NSObject>,
_will_terminate_observer: Retained<NSObject>,
_did_receive_memory_warning_observer: Retained<NSObject>,
} }
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -166,7 +151,8 @@ impl<T: 'static> EventLoop<T> {
unsafe { unsafe {
assert!( assert!(
!SINGLETON_INIT, !SINGLETON_INIT,
"Only one `EventLoop` is supported on iOS. `EventLoopProxy` might be helpful" "Only one `EventLoop` is supported on iOS. \
`EventLoopProxy` might be helpful"
); );
SINGLETON_INIT = true; SINGLETON_INIT = true;
} }
@@ -176,97 +162,14 @@ impl<T: 'static> EventLoop<T> {
// this line sets up the main run loop before `UIApplicationMain` // this line sets up the main run loop before `UIApplicationMain`
setup_control_flow_observers(); setup_control_flow_observers();
let center = unsafe { NSNotificationCenter::defaultCenter() };
let _did_finish_launching_observer = create_observer(
&center,
// `application:didFinishLaunchingWithOptions:`
unsafe { UIApplicationDidFinishLaunchingNotification },
move |_| {
app_state::did_finish_launching(mtm);
},
);
let _did_become_active_observer = create_observer(
&center,
// `applicationDidBecomeActive:`
unsafe { UIApplicationDidBecomeActiveNotification },
move |_| {
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed));
},
);
let _will_resign_active_observer = create_observer(
&center,
// `applicationWillResignActive:`
unsafe { UIApplicationWillResignActiveNotification },
move |_| {
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended));
},
);
let _will_enter_foreground_observer = create_observer(
&center,
// `applicationWillEnterForeground:`
unsafe { UIApplicationWillEnterForegroundNotification },
move |notification| {
let app = unsafe { notification.object() }.expect(
"UIApplicationWillEnterForegroundNotification to have application object",
);
// SAFETY: The `object` in `UIApplicationWillEnterForegroundNotification` is
// documented to be `UIApplication`.
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
send_occluded_event_for_all_windows(&app, false);
},
);
let _did_enter_background_observer = create_observer(
&center,
// `applicationDidEnterBackground:`
unsafe { UIApplicationDidEnterBackgroundNotification },
move |notification| {
let app = unsafe { notification.object() }.expect(
"UIApplicationDidEnterBackgroundNotification to have application object",
);
// SAFETY: The `object` in `UIApplicationDidEnterBackgroundNotification` is
// documented to be `UIApplication`.
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
send_occluded_event_for_all_windows(&app, true);
},
);
let _will_terminate_observer = create_observer(
&center,
// `applicationWillTerminate:`
unsafe { UIApplicationWillTerminateNotification },
move |notification| {
let app = unsafe { notification.object() }
.expect("UIApplicationWillTerminateNotification to have application object");
// SAFETY: The `object` in `UIApplicationWillTerminateNotification` is
// (somewhat) documented to be `UIApplication`.
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
app_state::terminated(&app);
},
);
let _did_receive_memory_warning_observer = create_observer(
&center,
// `applicationDidReceiveMemoryWarning:`
unsafe { UIApplicationDidReceiveMemoryWarningNotification },
move |_| {
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::MemoryWarning),
);
},
);
Ok(EventLoop { Ok(EventLoop {
mtm, mtm,
sender, sender,
receiver, receiver,
window_target: RootActiveEventLoop { p: ActiveEventLoop { mtm }, _marker: PhantomData }, window_target: RootActiveEventLoop {
_did_finish_launching_observer, p: ActiveEventLoop { mtm },
_did_become_active_observer, _marker: PhantomData,
_will_resign_active_observer, },
_will_enter_foreground_observer,
_did_enter_background_observer,
_will_terminate_observer,
_did_receive_memory_warning_observer,
}) })
} }
@@ -274,13 +177,12 @@ impl<T: 'static> EventLoop<T> {
where where
F: FnMut(Event<T>, &RootActiveEventLoop), F: FnMut(Event<T>, &RootActiveEventLoop),
{ {
let application: Option<Retained<UIApplication>> = let application = UIApplication::shared(self.mtm);
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
assert!( assert!(
application.is_none(), application.is_none(),
"\ "\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\nNote: \ `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
`EventLoop::run_app` calls `UIApplicationMain` on iOS", Note: `EventLoop::run_app` calls `UIApplicationMain` on iOS",
); );
let handler = map_user_event(handler, self.receiver); let handler = map_user_event(handler, self.receiver);
@@ -292,24 +194,22 @@ impl<T: 'static> EventLoop<T> {
>(Box::new(handler)) >(Box::new(handler))
}; };
let handler = EventLoopHandler { handler, event_loop: self.window_target }; let handler = EventLoopHandler {
handler,
event_loop: self.window_target,
};
app_state::will_launch(self.mtm, handler); app_state::will_launch(self.mtm, handler);
extern "C" { // Ensure application delegate is initialized
// These functions are in crt_externs.h. let _ = AppDelegate::class();
fn _NSGetArgc() -> *mut c_int;
fn _NSGetArgv() -> *mut *mut *mut c_char;
}
unsafe { unsafe {
UIApplicationMain( UIApplicationMain(
*_NSGetArgc(), 0,
NonNull::new(*_NSGetArgv()).unwrap(), ptr::null(),
// We intentionally override neither the application nor the delegate, to allow the
// user to do so themselves!
None,
None, None,
Some(&NSString::from_str(AppDelegate::NAME)),
) )
}; };
unreachable!() unreachable!()
@@ -327,7 +227,7 @@ impl<T: 'static> EventLoop<T> {
// EventLoopExtIOS // EventLoopExtIOS
impl<T: 'static> EventLoop<T> { impl<T: 'static> EventLoop<T> {
pub fn idiom(&self) -> Idiom { pub fn idiom(&self) -> Idiom {
match UIDevice::currentDevice(self.mtm).userInterfaceIdiom() { match UIDevice::current(self.mtm).userInterfaceIdiom() {
UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified, UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified,
UIUserInterfaceIdiom::Phone => Idiom::Phone, UIUserInterfaceIdiom::Phone => Idiom::Phone,
UIUserInterfaceIdiom::Pad => Idiom::Pad, UIUserInterfaceIdiom::Pad => Idiom::Pad,
@@ -382,7 +282,8 @@ impl<T> EventLoopProxy<T> {
cancel: None, cancel: None,
perform: event_loop_proxy_handler, perform: event_loop_proxy_handler,
}; };
let source = CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::MAX - 1, &mut context); let source =
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl); CFRunLoopWakeUp(rl);
@@ -391,7 +292,9 @@ impl<T> EventLoopProxy<T> {
} }
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender.send(event).map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?; self.sender
.send(event)
.map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?;
unsafe { unsafe {
// let the main thread know there's a new event // let the main thread know there's a new event
CFRunLoopSourceSignal(self.source); CFRunLoopSourceSignal(self.source);
@@ -404,8 +307,7 @@ impl<T> EventLoopProxy<T> {
fn setup_control_flow_observers() { fn setup_control_flow_observers() {
unsafe { unsafe {
// begin is queued with the highest priority to ensure it is processed before other // begin is queued with the highest priority to ensure it is processed before other observers
// observers
extern "C" fn control_flow_begin_handler( extern "C" fn control_flow_begin_handler(
_: CFRunLoopObserverRef, _: CFRunLoopObserverRef,
activity: CFRunLoopActivity, activity: CFRunLoopActivity,
@@ -439,7 +341,7 @@ fn setup_control_flow_observers() {
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
match activity { match activity {
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm), kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm),
kCFRunLoopExit => {}, // may happen when running on macOS kCFRunLoopExit => {} // may happen when running on macOS
_ => unreachable!(), _ => unreachable!(),
} }
} }
@@ -454,7 +356,7 @@ fn setup_control_flow_observers() {
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
match activity { match activity {
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm), kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm),
kCFRunLoopExit => {}, // may happen when running on macOS kCFRunLoopExit => {} // may happen when running on macOS
_ => unreachable!(), _ => unreachable!(),
} }
} }
@@ -465,7 +367,7 @@ fn setup_control_flow_observers() {
ptr::null_mut(), ptr::null_mut(),
kCFRunLoopAfterWaiting, kCFRunLoopAfterWaiting,
1, // repeat = true 1, // repeat = true
CFIndex::MIN, CFIndex::min_value(),
control_flow_begin_handler, control_flow_begin_handler,
ptr::null_mut(), ptr::null_mut(),
); );
@@ -485,7 +387,7 @@ fn setup_control_flow_observers() {
ptr::null_mut(), ptr::null_mut(),
kCFRunLoopExit | kCFRunLoopBeforeWaiting, kCFRunLoopExit | kCFRunLoopBeforeWaiting,
1, // repeat = true 1, // repeat = true
CFIndex::MAX, CFIndex::max_value(),
control_flow_end_handler, control_flow_end_handler,
ptr::null_mut(), ptr::null_mut(),
); );

View File

@@ -1,9 +1,11 @@
#![cfg(ios_platform)]
#![allow(clippy::let_unit_value)] #![allow(clippy::let_unit_value)]
mod app_delegate;
mod app_state; mod app_state;
mod event_loop; mod event_loop;
mod monitor; mod monitor;
mod notification_center; mod uikit;
mod view; mod view;
mod view_controller; mod view_controller;
mod window; mod window;
@@ -12,15 +14,16 @@ use std::fmt;
use crate::event::DeviceId as RootDeviceId; use crate::event::DeviceId as RootDeviceId;
pub(crate) use self::event_loop::{ pub(crate) use self::{
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle, event_loop::{
PlatformSpecificEventLoopAttributes, ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
}; PlatformSpecificEventLoopAttributes,
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle}; },
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window, WindowId}; monitor::{MonitorHandle, VideoModeHandle},
pub(crate) use crate::cursor::{ window::{PlatformSpecificWindowAttributes, Window, WindowId},
NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource,
}; };
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorSource;
pub(crate) use crate::icon::NoIcon as PlatformIcon; pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; pub(crate) use crate::platform_impl::Fullscreen;
@@ -32,7 +35,7 @@ pub(crate) use crate::platform_impl::Fullscreen;
pub struct DeviceId; pub struct DeviceId;
impl DeviceId { impl DeviceId {
pub const fn dummy() -> Self { pub const unsafe fn dummy() -> Self {
DeviceId DeviceId
} }
} }

View File

@@ -1,25 +1,31 @@
#![allow(clippy::unnecessary_cast)] #![allow(clippy::unnecessary_cast)]
use std::collections::{BTreeSet, VecDeque}; use std::{
use std::{fmt, hash, ptr}; collections::{BTreeSet, VecDeque},
fmt, hash, ptr,
};
use icrate::Foundation::{MainThreadBound, MainThreadMarker, NSInteger};
use objc2::mutability::IsRetainable; use objc2::mutability::IsRetainable;
use objc2::rc::Retained; use objc2::rc::Id;
use objc2::Message; use objc2::Message;
use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker, NSInteger};
use objc2_ui_kit::{UIScreen, UIScreenMode};
use crate::dpi::{PhysicalPosition, PhysicalSize}; use super::uikit::{UIScreen, UIScreenMode};
use crate::monitor::VideoModeHandle as RootVideoModeHandle; use crate::{
use crate::platform_impl::platform::app_state; dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoModeHandle as RootVideoModeHandle,
platform_impl::platform::app_state,
};
// Workaround for `MainThreadBound` implementing almost no traits // Workaround for `MainThreadBound` implementing almost no traits
#[derive(Debug)] #[derive(Debug)]
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Retained<T>>); struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Id<T>>);
impl<T: IsRetainable + Message> Clone for MainThreadBoundDelegateImpls<T> { impl<T: IsRetainable + Message> Clone for MainThreadBoundDelegateImpls<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(run_on_main(|mtm| MainThreadBound::new(Retained::clone(self.0.get(mtm)), mtm))) Self(MainThreadMarker::run_on_main(|mtm| {
MainThreadBound::new(Id::clone(self.0.get(mtm)), mtm)
}))
} }
} }
@@ -27,7 +33,7 @@ impl<T: IsRetainable + Message> hash::Hash for MainThreadBoundDelegateImpls<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) { fn hash<H: hash::Hasher>(&self, state: &mut H) {
// SAFETY: Marker only used to get the pointer // SAFETY: Marker only used to get the pointer
let mtm = unsafe { MainThreadMarker::new_unchecked() }; let mtm = unsafe { MainThreadMarker::new_unchecked() };
Retained::as_ptr(self.0.get(mtm)).hash(state); Id::as_ptr(self.0.get(mtm)).hash(state);
} }
} }
@@ -35,7 +41,7 @@ impl<T: IsRetainable + Message> PartialEq for MainThreadBoundDelegateImpls<T> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
// SAFETY: Marker only used to get the pointer // SAFETY: Marker only used to get the pointer
let mtm = unsafe { MainThreadMarker::new_unchecked() }; let mtm = unsafe { MainThreadMarker::new_unchecked() };
Retained::as_ptr(self.0.get(mtm)) == Retained::as_ptr(other.0.get(mtm)) Id::as_ptr(self.0.get(mtm)) == Id::as_ptr(other.0.get(mtm))
} }
} }
@@ -52,8 +58,8 @@ pub struct VideoModeHandle {
impl VideoModeHandle { impl VideoModeHandle {
fn new( fn new(
uiscreen: Retained<UIScreen>, uiscreen: Id<UIScreen>,
screen_mode: Retained<UIScreenMode>, screen_mode: Id<UIScreenMode>,
mtm: MainThreadMarker, mtm: MainThreadMarker,
) -> VideoModeHandle { ) -> VideoModeHandle {
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen); let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
@@ -83,18 +89,18 @@ impl VideoModeHandle {
self.monitor.clone() self.monitor.clone()
} }
pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Retained<UIScreenMode> { pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Id<UIScreenMode> {
self.screen_mode.0.get(mtm) self.screen_mode.0.get(mtm)
} }
} }
pub struct MonitorHandle { pub struct MonitorHandle {
ui_screen: MainThreadBound<Retained<UIScreen>>, ui_screen: MainThreadBound<Id<UIScreen>>,
} }
impl Clone for MonitorHandle { impl Clone for MonitorHandle {
fn clone(&self) -> Self { fn clone(&self) -> Self {
run_on_main(|mtm| Self { MainThreadMarker::run_on_main(|mtm| Self {
ui_screen: MainThreadBound::new(self.ui_screen.get(mtm).clone(), mtm), ui_screen: MainThreadBound::new(self.ui_screen.get(mtm).clone(), mtm),
}) })
} }
@@ -102,20 +108,13 @@ impl Clone for MonitorHandle {
impl hash::Hash for MonitorHandle { impl hash::Hash for MonitorHandle {
fn hash<H: hash::Hasher>(&self, state: &mut H) { fn hash<H: hash::Hasher>(&self, state: &mut H) {
// SAFETY: Only getting the pointer. (self as *const Self).hash(state);
let mtm = unsafe { MainThreadMarker::new_unchecked() };
Retained::as_ptr(self.ui_screen.get(mtm)).hash(state);
} }
} }
impl PartialEq for MonitorHandle { impl PartialEq for MonitorHandle {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
// SAFETY: Only getting the pointer. ptr::eq(self, other)
let mtm = unsafe { MainThreadMarker::new_unchecked() };
ptr::eq(
Retained::as_ptr(self.ui_screen.get(mtm)),
Retained::as_ptr(other.ui_screen.get(mtm)),
)
} }
} }
@@ -129,10 +128,8 @@ impl PartialOrd for MonitorHandle {
impl Ord for MonitorHandle { impl Ord for MonitorHandle {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { fn cmp(&self, other: &Self) -> std::cmp::Ordering {
// SAFETY: Only getting the pointer.
// TODO: Make a better ordering // TODO: Make a better ordering
let mtm = unsafe { MainThreadMarker::new_unchecked() }; (self as *const Self).cmp(&(other as *const Self))
Retained::as_ptr(self.ui_screen.get(mtm)).cmp(&Retained::as_ptr(other.ui_screen.get(mtm)))
} }
} }
@@ -149,22 +146,22 @@ impl fmt::Debug for MonitorHandle {
} }
impl MonitorHandle { impl MonitorHandle {
pub(crate) fn new(ui_screen: Retained<UIScreen>) -> Self { pub(crate) fn new(ui_screen: Id<UIScreen>) -> Self {
// Holding `Retained<UIScreen>` implies we're on the main thread. // Holding `Id<UIScreen>` implies we're on the main thread.
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
Self { ui_screen: MainThreadBound::new(ui_screen, mtm) } Self {
ui_screen: MainThreadBound::new(ui_screen, mtm),
}
} }
pub fn name(&self) -> Option<String> { pub fn name(&self) -> Option<String> {
run_on_main(|mtm| { MainThreadMarker::run_on_main(|mtm| {
#[allow(deprecated)] let main = UIScreen::main(mtm);
let main = UIScreen::mainScreen(mtm);
if *self.ui_screen(mtm) == main { if *self.ui_screen(mtm) == main {
Some("Primary".to_string()) Some("Primary".to_string())
} else if Some(self.ui_screen(mtm)) == main.mirroredScreen().as_ref() { } else if *self.ui_screen(mtm) == main.mirroredScreen() {
Some("Mirrored".to_string()) Some("Mirrored".to_string())
} else { } else {
#[allow(deprecated)]
UIScreen::screens(mtm) UIScreen::screens(mtm)
.iter() .iter()
.position(|rhs| rhs == &**self.ui_screen(mtm)) .position(|rhs| rhs == &**self.ui_screen(mtm))
@@ -174,25 +171,33 @@ impl MonitorHandle {
} }
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
let bounds = self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeBounds()); let bounds = self
.ui_screen
.get_on_main(|ui_screen| ui_screen.nativeBounds());
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32) PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
} }
pub fn position(&self) -> PhysicalPosition<i32> { pub fn position(&self) -> PhysicalPosition<i32> {
let bounds = self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeBounds()); let bounds = self
.ui_screen
.get_on_main(|ui_screen| ui_screen.nativeBounds());
(bounds.origin.x as f64, bounds.origin.y as f64).into() (bounds.origin.x as f64, bounds.origin.y as f64).into()
} }
pub fn scale_factor(&self) -> f64 { pub fn scale_factor(&self) -> f64 {
self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64 self.ui_screen
.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64
} }
pub fn refresh_rate_millihertz(&self) -> Option<u32> { pub fn refresh_rate_millihertz(&self) -> Option<u32> {
Some(self.ui_screen.get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen))) Some(
self.ui_screen
.get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen)),
)
} }
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
run_on_main(|mtm| { MainThreadMarker::run_on_main(|mtm| {
let ui_screen = self.ui_screen(mtm); let ui_screen = self.ui_screen(mtm);
// Use Ord impl of RootVideoModeHandle // Use Ord impl of RootVideoModeHandle
@@ -208,12 +213,12 @@ impl MonitorHandle {
}) })
} }
pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Retained<UIScreen> { pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Id<UIScreen> {
self.ui_screen.get(mtm) self.ui_screen.get(mtm)
} }
pub fn preferred_video_mode(&self) -> VideoModeHandle { pub fn preferred_video_mode(&self) -> VideoModeHandle {
run_on_main(|mtm| { MainThreadMarker::run_on_main(|mtm| {
VideoModeHandle::new( VideoModeHandle::new(
self.ui_screen(mtm).clone(), self.ui_screen(mtm).clone(),
self.ui_screen(mtm).preferredMode().unwrap(), self.ui_screen(mtm).preferredMode().unwrap(),
@@ -248,30 +253,8 @@ fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 {
} }
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> { pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
#[allow(deprecated)] UIScreen::screens(mtm)
UIScreen::screens(mtm).into_iter().map(MonitorHandle::new).collect() .into_iter()
} .map(MonitorHandle::new)
.collect()
#[cfg(test)]
mod tests {
use objc2_foundation::NSSet;
use super::*;
// Test that UIScreen pointer comparisons are correct.
#[test]
#[allow(deprecated)]
fn screen_comparisons() {
// Test code, doesn't matter that it's not thread safe
let mtm = unsafe { MainThreadMarker::new_unchecked() };
assert!(ptr::eq(&*UIScreen::mainScreen(mtm), &*UIScreen::mainScreen(mtm)));
let main = UIScreen::mainScreen(mtm);
assert!(UIScreen::screens(mtm).iter().any(|screen| ptr::eq(screen, &*main)));
assert!(unsafe {
NSSet::setWithArray(&UIScreen::screens(mtm)).containsObject(&UIScreen::mainScreen(mtm))
});
}
} }

View File

@@ -1,27 +0,0 @@
use std::ptr::NonNull;
use block2::RcBlock;
use objc2::rc::Retained;
use objc2_foundation::{NSNotification, NSNotificationCenter, NSNotificationName, NSObject};
/// Observe the given notification.
///
/// This is used in Winit as an alternative to declaring an application delegate, as we want to
/// give the user full control over those.
pub fn create_observer(
center: &NSNotificationCenter,
name: &NSNotificationName,
handler: impl Fn(&NSNotification) + 'static,
) -> Retained<NSObject> {
let block = RcBlock::new(move |notification: NonNull<NSNotification>| {
handler(unsafe { notification.as_ref() });
});
unsafe {
center.addObserverForName_object_queue_usingBlock(
Some(name),
None, // No sender filter
None, // No queue, run on posting thread (i.e. main thread)
&block,
)
}
}

View File

@@ -0,0 +1,31 @@
use icrate::Foundation::{CGRect, MainThreadMarker, NSArray, NSObject};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use super::{UIResponder, UIWindow};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIApplication;
unsafe impl ClassType for UIApplication {
#[inherits(NSObject)]
type Super = UIResponder;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIApplication {
pub fn shared(_mtm: MainThreadMarker) -> Option<Id<Self>> {
unsafe { msg_send_id![Self::class(), sharedApplication] }
}
pub fn windows(&self) -> Id<NSArray<UIWindow>> {
unsafe { msg_send_id![self, windows] }
}
#[method(statusBarFrame)]
pub fn statusBarFrame(&self) -> CGRect;
}
);

View File

@@ -0,0 +1,12 @@
use icrate::Foundation::NSObject;
use objc2::{extern_class, mutability, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UICoordinateSpace;
unsafe impl ClassType for UICoordinateSpace {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);

View File

@@ -0,0 +1,41 @@
use icrate::Foundation::{MainThreadMarker, NSInteger, NSObject};
use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIDevice;
unsafe impl ClassType for UIDevice {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIDevice {
pub fn current(_mtm: MainThreadMarker) -> Id<Self> {
unsafe { msg_send_id![Self::class(), currentDevice] }
}
#[method(userInterfaceIdiom)]
pub fn userInterfaceIdiom(&self) -> UIUserInterfaceIdiom;
}
);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIUserInterfaceIdiom(NSInteger);
unsafe impl Encode for UIUserInterfaceIdiom {
const ENCODING: Encoding = NSInteger::ENCODING;
}
impl UIUserInterfaceIdiom {
pub const Unspecified: UIUserInterfaceIdiom = UIUserInterfaceIdiom(-1);
pub const Phone: UIUserInterfaceIdiom = UIUserInterfaceIdiom(0);
pub const Pad: UIUserInterfaceIdiom = UIUserInterfaceIdiom(1);
pub const TV: UIUserInterfaceIdiom = UIUserInterfaceIdiom(2);
pub const CarPlay: UIUserInterfaceIdiom = UIUserInterfaceIdiom(3);
}

View File

@@ -0,0 +1,12 @@
use icrate::Foundation::NSObject;
use objc2::{extern_class, mutability, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIEvent;
unsafe impl ClassType for UIEvent {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);

Some files were not shown because too many files have changed in this diff Show More