Compare commits

..

24 Commits

Author SHA1 Message Date
John Nunley
68acedcdda feat: Add Docker support to integration tests
This allows the tests to be run inside of a Docker container with linux
with X11 inside.

Signed-off-by: John Nunley <dev@notgull.net>
2024-03-17 19:26:52 -07:00
John Nunley
facef799d3 it: Don't capture stdout
Signed-off-by: John Nunley <dev@notgull.net>
2024-03-17 19:26:52 -07:00
John Nunley
25fab64f6e it: Add runner and common tests
Still needs some work, but the idea should be clear.

Signed-off-by: John Nunley <dev@notgull.net>
2024-03-17 19:26:52 -07:00
John Nunley
b3333b47e1 it: Add basic handlers
Signed-off-by: John Nunley <dev@notgull.net>
2024-03-17 19:26:52 -07:00
John Nunley
1bdba3cacc it: Create the 'gui-test' crate
The 'gui-test' crate is intended to provide a test framework for process
isolated and remote test cases. Like how I intend to test winit.

Signed-off-by: John Nunley <dev@notgull.net>
2024-03-17 19:26:52 -07:00
daxpedda
baf10de958 Prefer [foo][bar] over [foo](bar) for intra-doc links (#3191) 2024-03-16 10:22:29 +01:00
Kirill Chibisov
c10f9c32d3 DPI version 0.1.1 2024-03-15 18:42:52 +04:00
Mads Marquart
975cf068c7 chore: add issue templates
Provide issue templates to ask for platform specific information to
make bugs more actionable.
2024-03-15 18:15:49 +04:00
amrbashir
8e69a8fa40 dpi: derive common traits for PixelUnit
Derive `Debug`, `Copy`, `Clone`	, `PartialEq`, `Serialize`,
`Deserialize` for `PixelUnit`, which follows other types in dpi crate.
2024-03-15 17:57:55 +04:00
Mads Marquart
fedb944d57 chore(docs): make changelogs visible on docsrs
Split changelog file to make it more comprehensible when reading and
also make it a part of documentation so it'll be more discoverable
by the users. This change also makes it possible for rust code inside
the changelogs to be tested with `cargo`.

Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2024-03-15 17:19:19 +04:00
Kirill Chibisov
a63b04385a Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-03-13 14:48:20 +04:00
Kirill Chibisov
06f4e28de9 DPI version 0.1.0 2024-03-13 13:56:58 +04:00
Kirill Chibisov
249d5d8bff bugfix(dpi): PhysicalUnit::to_logical computation
The conversion of PhysicalUnit was wrongly computed and the tests were
not running on the CI for the dpi crate, thus it was missed, even though
the test was correctly failing.

Signed-off-by: John Nunley <dev@notgull.net>
Signed-off-by: Kirill Chibisov <contact@kchibisov.com>
2024-03-13 12:58:39 +04:00
Kirill Chibisov
66df2c22ba chore(nigthly): fix errors
The structure is used by other backends, `cfg`-out it is pointless.
2024-03-13 12:58:39 +04:00
Kirill Chibisov
359a38844b bugfix(x11): crash when querying xsettings prop fails
Don't crash when xsettings query fails with _present_ xsettings.

Closes: https://github.com/rust-windowing/winit/issues/3573
2024-03-10 21:43:53 -07:00
amrbashir
563b0bf5e3 dpi: add LogicalUnit, PhysicalUnit, and Unit
Part-off: https://github.com/rust-windowing/winit/issues/2395
2024-03-08 21:15:27 +04:00
AmeKnite
b2f9fad654 ci: add typos-cli
Given that typos are frequent and may appear in the public API spell
check code on CI.
2024-03-07 18:03:38 +04:00
John Nunley
4ade1a7518 docs: add a code of conduct
As previously discussed in today's meeting, this commit adds a code
of conduct to winit. I've elected to re-use the Rust project's Code
of Conduct, as it is also in use by other popular Rust projects and I
agree with its intentions. The goal is to set forward a set of explicit
expectations for how discourse in the rust-windowing project should be
conducted.

I've deliberately left out a mechanism for enforcing this code of
conduct. My hope is that, by the act of setting these expectations,
contributors will voluntarily follow them. In addition I have no
interest in being an internet janitor. If this becomes necessary in the
future we can look into it later.

rust-windowing collaborators, please read the linked Code of Conduct.
Not only for expectations of behavior, but also for expectations of how
it will be enforced should it come to it.

Signed-off-by: John Nunley <dev@notgull.net>
2024-03-07 17:32:06 +04:00
Kirill Chibisov
e06ecf4d72 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-03-06 14:37:46 +04:00
Kirill Chibisov
4a8050289d fix(xkb): text not being None when composing
When composing the text was not reset to `None` leading to input in
some applications e.g. alacritty.

Links: https://github.com/alacritty/alacritty/issues/7806
2024-03-05 20:34:59 +04:00
daxpedda
99e238065e Bump x11-dl to v2.19.1 2024-03-05 16:53:35 +04:00
Kirill Chibisov
d123cd2f8e api: add ApplicationHandler and matching run APIs
Add a simple `ApplicationHandler` trait since winit is moving towards
trait based API. Add `run_app` group of APIs to accept `&mut impl
ApplicationHandler` deprecating the old `run` APIs.

Part-of: https://github.com/rust-windowing/winit/issues/3432
2024-03-05 16:13:14 +04:00
Kirill Chibisov
fc8a008b25 chore(wayland): don't reapply same cursor grab
Some compositors break when re-taking the same grab.

Closes: https://github.com/rust-windowing/winit/issues/3566
2024-03-05 15:38:08 +04:00
John Nunley
f6f1c45a72 bugfix(x11): fix incorrect delta filtering
Invert the mouse delta filter, so it aligns with the intention of
filtering values lower than epsilon.

Signed-off-by: John Nunley <dev@notgull.net>
Closes: https://github.com/rust-windowing/winit/issues/3558
2024-03-04 20:49:04 +04:00
128 changed files with 4397 additions and 2882 deletions

View File

@@ -1,2 +1,9 @@
[alias] [alias]
run-wasm = ["run", "--release", "--package", "run-wasm", "--"] run-wasm = ["run", "--release", "--package", "run-wasm", "--"]
# Allow rust-analyzer and local `cargo doc` invocations to pick up unreleased changelog entries
#
# 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,13 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
"version": "0.2",
"dictionaryDefinitions": [
{
"name": "project-words",
"path": "./project-words.txt",
"addWords": true
}
],
"dictionaries": ["project-words"],
"ignorePaths": ["/target", "/project-words.txt"]
}

3
.github/CODEOWNERS vendored
View File

@@ -32,3 +32,6 @@
# Orbital (Redox OS) # Orbital (Redox OS)
/src/platform/orbital.rs @jackpot51 /src/platform/orbital.rs @jackpot51
/src/platform_impl/orbital @jackpot51 /src/platform_impl/orbital @jackpot51
# Integration tests
/it @notgull

4
.github/ISSUE_TEMPLATE/blank_issue.md vendored Normal file
View File

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

36
.github/ISSUE_TEMPLATE/bug_android.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
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

37
.github/ISSUE_TEMPLATE/bug_ios.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
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

37
.github/ISSUE_TEMPLATE/bug_macos.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
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

41
.github/ISSUE_TEMPLATE/bug_wayland.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
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

51
.github/ISSUE_TEMPLATE/bug_web.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
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

35
.github/ISSUE_TEMPLATE/bug_windows.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
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

32
.github/ISSUE_TEMPLATE/bug_x11.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
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

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
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

@@ -0,0 +1,26 @@
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 `CHANGELOG.md` if knowledge of this change could be valuable to users - [ ] Added an entry to the `changelog` module 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

View File

@@ -7,7 +7,7 @@ on:
jobs: jobs:
fmt: fmt:
name: Tidy Code name: Check formatting
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@@ -16,13 +16,28 @@ jobs:
components: rustfmt components: rustfmt
- name: Check Formatting - name: Check Formatting
run: cargo fmt -- --check run: cargo fmt -- --check
- name: Check Spelling
run: npx -y cspell --no-progress --no-summary '**/*.rs' '**/*.md' typos:
name: Check for typos
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: taiki-e/install-action@v2
with:
tool: typos-cli
- name: run typos
run: typos
- name: Typos info
if: failure()
run: |
echo 'To fix typos, please run `typos -w`'
echo 'To check for a diff, run `typos`'
echo 'You can find typos here: https://crates.io/crates/typos'
tests: tests:
name: Test ${{ matrix.toolchain }} ${{ matrix.platform.name }} name: Test ${{ matrix.toolchain }} ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }} runs-on: ${{ matrix.platform.os }}
needs: fmt
strategy: strategy:
fail-fast: false fail-fast: false
@@ -130,6 +145,13 @@ jobs:
- name: Build crate - name: Build crate
run: cargo $CMD build $OPTIONS run: cargo $CMD build $OPTIONS
# Test only on Linux x86_64, so we avoid spending unnecessary CI hours.
- name: Test dpi crate
if: >
contains(matrix.platform.name, 'Linux 64bit') &&
matrix.toolchain != '1.70.0'
run: cargo test -p dpi
- name: Build tests - name: Build tests
if: > if: >
!contains(matrix.platform.target, 'redox') && !contains(matrix.platform.target, 'redox') &&
@@ -168,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' RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs --cfg=unreleased_changelogs'
# See restore step above # See restore step above
- name: Save cache of cargo folder - name: Save cache of cargo folder
@@ -180,6 +202,41 @@ jobs:
~/.cargo/git/db/ ~/.cargo/git/db/
key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }} key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}
it:
name: Run integration tests on ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }}
strategy:
fail-fast: false
matrix:
toolchain: [stable, nightly]
platform:
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
- { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
env:
# Set more verbose terminal output
CARGO_TERM_VERBOSE: true
RUST_BACKTRACE: 1
# Faster compilation and error on warnings
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}'
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
- name: Log into GHCR
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
- name: Common tests
run: cargo run -p gui-test-runner -- common-tests ${{ matrix.platform.target }}
cargo-deny: cargo-deny:
name: Run cargo-deny on ${{ matrix.platform.name }} name: Run cargo-deny on ${{ matrix.platform.name }}
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -17,7 +17,7 @@ jobs:
contents: read contents: read
pages: write pages: write
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@@ -27,7 +27,7 @@ jobs:
- name: Run Rustdoc - name: Run Rustdoc
env: env:
RUSTDOCFLAGS: --crate-version master --cfg=docsrs RUSTDOCFLAGS: --crate-version master --cfg=docsrs --cfg=unreleased_changelogs
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

File diff suppressed because it is too large Load Diff

6
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,6 @@
# Code of Conduct
The `rust-windowing` project adheres to the [Rust Code of Conduct]. This
describes the minimum behavior expected from all contributors.
[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct

View File

@@ -26,7 +26,7 @@ When making a code contribution to winit, before opening your pull request, plea
- you updated any relevant documentation in winit - you updated any relevant documentation in winit
- you left comments in your code explaining any part that is not straightforward, so that the - 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 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. - your PR adds an entry to the current changelog if the introduced change is relevant to winit users.
You needn't worry about the added entry causing conflicts, the maintainer that merges the PR will You needn't worry about the added entry causing conflicts, the maintainer that merges the PR will
handle those for you when merging (see below). handle those for you when merging (see below).
@@ -38,8 +38,8 @@ Once your PR is open, you can ask for a review by a maintainer of your platform.
is that a PR must be approved by at least two maintainers of winit before being merged, including is that a PR must be approved by at least two maintainers of winit before being merged, including
at least a maintainer of the platform (a maintainer making a PR themselves counts as approving it). at least a maintainer of the platform (a maintainer making a PR themselves counts as approving it).
Once your PR is deemed ready, the merging maintainer will take care of resolving conflicts in Once your PR is deemed ready, the merging maintainer will take care of resolving conflicts in the
`CHANGELOG.md` (but you must resolve other conflicts yourself). Doing this requires that you check the `changelog` module (but you must resolve other conflicts yourself). Doing this requires that you check the
"give contributors write access to the branch" checkbox when creating the PR. "give contributors write access to the branch" checkbox when creating the PR.
## Maintainers ## Maintainers
@@ -51,19 +51,22 @@ The current maintainers for each platform are listed in the [CODEOWNERS](.github
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 and later patch releases are committed and tagged in this branch. 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.
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. In the branch, the version is bumped to `v0.2.0` 3. Update released `cfg_attr` in `src/changelog/mod.rs` to `v0.2.md`
4. The new commit in the branch is tagged `v0.2.0` 4. Move entries from `src/changelog/unreleased.md` into `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 3-7 of the regular release example 4. Follow steps 4-9 of the regular release example

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "winit" name = "winit"
version = "0.29.13" version = "0.29.15"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"] authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library." description = "Cross-platform window creation library."
keywords = ["windowing"] keywords = ["windowing"]
@@ -64,12 +64,12 @@ 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 = ["std"], optional = true } rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
serde = { workspace = true, optional = true } serde = { workspace = true, optional = true }
smol_str = "0.2.0" smol_str = "0.2.0"
dpi = { path = "dpi" }
tracing = { version = "0.1.40", default_features = false } tracing = { version = "0.1.40", default_features = false }
[dev-dependencies] [dev-dependencies]
@@ -188,7 +188,7 @@ wayland-backend = { version = "0.3.0", default_features = false, features = ["cl
wayland-client = { version = "0.31.1", optional = true } wayland-client = { version = "0.31.1", optional = true }
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true } wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true } wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
x11-dl = { version = "2.18.5", optional = true } x11-dl = { version = "2.19.1", optional = true }
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true } x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
xkbcommon-dl = "0.4.2" xkbcommon-dl = "0.4.2"
@@ -264,6 +264,9 @@ name = "window"
resolver = "2" resolver = "2"
members = [ members = [
"dpi", "dpi",
"it/common-tests",
"it/gui-test",
"it/gui-test-runner",
"run-wasm", "run-wasm",
] ]
@@ -274,5 +277,9 @@ license = "Apache-2.0"
edition = "2021" edition = "2021"
[workspace.dependencies] [workspace.dependencies]
serde = { version = "1", features = ["serde_derive"] } async-io = "2.3.1"
gui-test = { path = "it/gui-test" }
mint = "0.5.6" mint = "0.5.6"
serde = { version = "1", features = ["serde_derive"] }
serde_json = "1.0.114"
winit = { path = "." }

View File

@@ -8,7 +8,7 @@
```toml ```toml
[dependencies] [dependencies]
winit = "0.29.13" winit = "0.29.15"
``` ```
## [Documentation](https://docs.rs/winit) ## [Documentation](https://docs.rs/winit)

View File

@@ -0,0 +1,40 @@
# syntax=docker/dockerfile:1
# Copyright 2024 The Winit Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
ARG DISTRO=ubuntu
ARG DISTRO_VERSION=22.04
FROM "${DISTRO}":"${DISTRO_VERSION}"
SHELL ["/bin/bash", "-eEuxo", "pipefail", "-c"]
ARG DEBIAN_FRONTEND=noninteractive
RUN \
apt-get -o Acquire::Retries=10 -qq update && \
apt-get -o Acquire::Retries=10 -o Dpkg::Use-Pty=0 install -y --no-install-recommends \
cargo \
ca-certificates \
libx11-dev \
libxcursor-dev \
libxcb1-dev \
libxi-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
xvfb && \
rm -rf \
/var/lib/apt/lists/* \
/var/cache/* \
/var/log/* \
/usr/share/{doc,man}

20
dpi/CHANGELOG.md Normal file
View File

@@ -0,0 +1,20 @@
# Changelog
All notable changes to this project will be documented in this file.
Please keep one empty line before and after all headers. (This is required for
`git` to produce a conflict when a release is made while a PR is open and the
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 `#
Unreleased` header.
## Unreleased
## 0.1.1
- Derive `Debug`, `Copy`, `Clone`, `PartialEq`, `Serialize`, `Deserialize` traits for `PixelUnit`.
## 0.1.0
- Add `LogicalUnit`, `PhysicalUnit` and `PixelUnit` types and related functions.

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "dpi" name = "dpi"
version = "0.0.0" version = "0.1.1"
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,8 +35,8 @@
//! //!
//! ### Position and Size types //! ### Position and Size types
//! //!
//! The [`PhysicalPosition`] / [`PhysicalSize`] types correspond with the actual pixels on the //! The [`PhysicalPosition`] / [`PhysicalSize`] / [`PhysicalUnit`] types correspond with the actual pixels on the
//! device, and the [`LogicalPosition`] / [`LogicalSize`] types correspond to the physical pixels //! device, and the [`LogicalPosition`] / [`LogicalSize`] / [`LogicalUnit`] types 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
@@ -128,6 +128,240 @@ 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()
} }
/// A logical pixel unit.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalUnit<P>(pub P);
impl<P> LogicalUnit<P> {
/// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a logical unit of `0_f64`.
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]
pub const fn new(v: P) -> Self {
LogicalUnit(v)
}
}
impl<P: Pixel> LogicalUnit<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalUnit<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<X> {
assert!(validate_scale_factor(scale_factor));
PhysicalUnit::new(self.0.into() * scale_factor).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalUnit<X> {
LogicalUnit(self.0.cast())
}
}
impl<P: Pixel, X: Pixel> From<X> for LogicalUnit<P> {
fn from(v: X) -> LogicalUnit<P> {
LogicalUnit::new(v.cast())
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u8 {
fn from(v: LogicalUnit<P>) -> u8 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u16 {
fn from(v: LogicalUnit<P>) -> u16 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u32 {
fn from(v: LogicalUnit<P>) -> u32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i8 {
fn from(v: LogicalUnit<P>) -> i8 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i16 {
fn from(v: LogicalUnit<P>) -> i16 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i32 {
fn from(v: LogicalUnit<P>) -> i32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for f32 {
fn from(v: LogicalUnit<P>) -> f32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for f64 {
fn from(v: LogicalUnit<P>) -> f64 {
v.0.cast()
}
}
/// A physical pixel unit.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalUnit<P>(pub P);
impl<P> PhysicalUnit<P> {
/// Represents a minimum physical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a physical unit of `0_f64`.
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]
pub const fn new(v: P) -> Self {
PhysicalUnit(v)
}
}
impl<P: Pixel> PhysicalUnit<P> {
#[inline]
pub fn from_logical<T: Into<LogicalUnit<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalUnit<X> {
assert!(validate_scale_factor(scale_factor));
LogicalUnit::new(self.0.into() / scale_factor).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalUnit<X> {
PhysicalUnit(self.0.cast())
}
}
impl<P: Pixel, X: Pixel> From<X> for PhysicalUnit<P> {
fn from(v: X) -> PhysicalUnit<P> {
PhysicalUnit::new(v.cast())
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u8 {
fn from(v: PhysicalUnit<P>) -> u8 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u16 {
fn from(v: PhysicalUnit<P>) -> u16 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u32 {
fn from(v: PhysicalUnit<P>) -> u32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i8 {
fn from(v: PhysicalUnit<P>) -> i8 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i16 {
fn from(v: PhysicalUnit<P>) -> i16 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i32 {
fn from(v: PhysicalUnit<P>) -> i32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for f32 {
fn from(v: PhysicalUnit<P>) -> f32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for f64 {
fn from(v: PhysicalUnit<P>) -> f64 {
v.0.cast()
}
}
/// A pixel unit that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PixelUnit {
Physical(PhysicalUnit<i32>),
Logical(LogicalUnit<f64>),
}
impl PixelUnit {
/// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MIN));
/// Represents a logical unit of `0_f64`.
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 {
unit.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalUnit<P> {
match *self {
PixelUnit::Physical(unit) => unit.to_logical(scale_factor),
PixelUnit::Logical(unit) => unit.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<P> {
match *self {
PixelUnit::Physical(unit) => unit.cast(),
PixelUnit::Logical(unit) => unit.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for PixelUnit {
#[inline]
fn from(unit: PhysicalUnit<P>) -> PixelUnit {
PixelUnit::Physical(unit.cast())
}
}
impl<P: Pixel> From<LogicalUnit<P>> for PixelUnit {
#[inline]
fn from(unit: LogicalUnit<P>) -> PixelUnit {
PixelUnit::Logical(unit.cast())
}
}
/// A position represented in logical pixels. /// A position represented in logical pixels.
/// ///
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the /// The position is stored as floats, so please be careful. Casting floats to integers truncates the
@@ -730,6 +964,43 @@ mod tests {
assert!(!validate_scale_factor(f64::NEG_INFINITY)); assert!(!validate_scale_factor(f64::NEG_INFINITY));
} }
#[test]
fn test_logical_unity() {
let log_unit = LogicalUnit::new(1.0);
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.cast::<u32>(), LogicalUnit::new(1));
assert_eq!(
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));
let x: f64 = log_unit.into();
assert_eq!(x, 1.0);
}
#[test]
fn test_physical_unit() {
assert_eq!(
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));
let x: f64 = PhysicalUnit::new(1).into();
assert_eq!(x, 1.0);
}
#[test] #[test]
fn test_logical_position() { fn test_logical_position() {
let log_pos = LogicalPosition::new(1.0, 2.0); let log_pos = LogicalPosition::new(1.0, 2.0);
@@ -936,4 +1207,38 @@ 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

@@ -3,29 +3,30 @@
use std::thread; use std::thread;
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
use std::time; use std::time;
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time as time; use web_time as time;
use winit::{ use winit::application::ApplicationHandler;
event::{ElementState, Event, KeyEvent, WindowEvent}, use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
event_loop::{ControlFlow, EventLoop}, use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
keyboard::{Key, NamedKey}, use winit::keyboard::{Key, NamedKey};
window::Window, use winit::window::{Window, WindowId};
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
enum Mode { enum Mode {
#[default]
Wait, Wait,
WaitUntil, WaitUntil,
Poll, Poll,
} }
const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
@@ -37,96 +38,110 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut mode = Mode::Wait; let mut app = ControlFlowDemo::default();
let mut request_redraw = false; event_loop.run_app(&mut app)
let mut wait_cancelled = false; }
let mut close_requested = false;
let mut window = None; #[derive(Default)]
event_loop.run(move |event, event_loop| { struct ControlFlowDemo {
use winit::event::StartCause; mode: Mode,
request_redraw: bool,
wait_cancelled: bool,
close_requested: bool,
window: Option<Window>,
}
impl ApplicationHandler for ControlFlowDemo {
fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) {
println!("new_events: {cause:?}");
self.wait_cancelled = match cause {
StartCause::WaitCancelled { .. } => self.mode == Mode::WaitUntil,
_ => false,
}
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes().with_title(
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
);
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event(
&mut self,
_event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
println!("{event:?}"); println!("{event:?}");
match event { match event {
Event::NewEvents(start_cause) => { WindowEvent::CloseRequested => {
wait_cancelled = match start_cause { self.close_requested = true;
StartCause::WaitCancelled { .. } => mode == Mode::WaitUntil,
_ => false,
}
} }
Event::Resumed => { WindowEvent::KeyboardInput {
let window_attributes = Window::default_attributes().with_title( event:
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.", KeyEvent {
); logical_key: key,
window = Some(event_loop.create_window(window_attributes).unwrap()); state: ElementState::Pressed,
} ..
Event::WindowEvent { event, .. } => match event { },
WindowEvent::CloseRequested => { ..
close_requested = true; } => match key.as_ref() {
// WARNING: Consider using `key_without_modifiers()` if available on your platform.
// See the `key_binding` example
Key::Character("1") => {
self.mode = Mode::Wait;
println!("\nmode: {:?}\n", self.mode);
} }
WindowEvent::KeyboardInput { Key::Character("2") => {
event: self.mode = Mode::WaitUntil;
KeyEvent { println!("\nmode: {:?}\n", self.mode);
logical_key: key, }
state: ElementState::Pressed, Key::Character("3") => {
.. self.mode = Mode::Poll;
}, println!("\nmode: {:?}\n", self.mode);
.. }
} => match key.as_ref() { Key::Character("r") => {
// WARNING: Consider using `key_without_modifiers()` if available on your platform. self.request_redraw = !self.request_redraw;
// See the `key_binding` example println!("\nrequest_redraw: {}\n", self.request_redraw);
Key::Character("1") => { }
mode = Mode::Wait; Key::Named(NamedKey::Escape) => {
println!("\nmode: {mode:?}\n"); self.close_requested = true;
}
Key::Character("2") => {
mode = Mode::WaitUntil;
println!("\nmode: {mode:?}\n");
}
Key::Character("3") => {
mode = Mode::Poll;
println!("\nmode: {mode:?}\n");
}
Key::Character("r") => {
request_redraw = !request_redraw;
println!("\nrequest_redraw: {request_redraw}\n");
}
Key::Named(NamedKey::Escape) => {
close_requested = true;
}
_ => (),
},
WindowEvent::RedrawRequested => {
let window = window.as_ref().unwrap();
window.pre_present_notify();
fill::fill_window(window);
} }
_ => (), _ => (),
}, },
Event::AboutToWait => { WindowEvent::RedrawRequested => {
if request_redraw && !wait_cancelled && !close_requested { let window = self.window.as_ref().unwrap();
window.as_ref().unwrap().request_redraw(); window.pre_present_notify();
} fill::fill_window(window);
match mode {
Mode::Wait => event_loop.set_control_flow(ControlFlow::Wait),
Mode::WaitUntil => {
if !wait_cancelled {
event_loop.set_control_flow(ControlFlow::WaitUntil(
time::Instant::now() + WAIT_TIME,
));
}
}
Mode::Poll => {
thread::sleep(POLL_SLEEP_TIME);
event_loop.set_control_flow(ControlFlow::Poll);
}
};
if close_requested {
event_loop.exit();
}
} }
_ => (), _ => (),
} }
}) }
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if self.request_redraw && !self.wait_cancelled && !self.close_requested {
self.window.as_ref().unwrap().request_redraw();
}
match self.mode {
Mode::Wait => event_loop.set_control_flow(ControlFlow::Wait),
Mode::WaitUntil => {
if !self.wait_cancelled {
event_loop
.set_control_flow(ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME));
}
}
Mode::Poll => {
thread::sleep(POLL_SLEEP_TIME);
event_loop.set_control_flow(ControlFlow::Poll);
}
};
if self.close_requested {
event_loop.exit();
}
}
} }

View File

@@ -11,50 +11,59 @@
fn main() -> std::process::ExitCode { fn main() -> std::process::ExitCode {
use std::{process::ExitCode, thread::sleep, time::Duration}; use std::{process::ExitCode, thread::sleep, time::Duration};
use winit::{ use winit::application::ApplicationHandler;
event::{Event, WindowEvent}, use winit::event::WindowEvent;
event_loop::EventLoop, use winit::event_loop::{ActiveEventLoop, EventLoop};
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus}, use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus};
window::Window, use winit::window::{Window, WindowId};
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
#[derive(Default)]
struct PumpDemo {
window: Option<Window>,
}
impl ApplicationHandler for PumpDemo {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
println!("{event:?}");
let window = match self.window.as_ref() {
Some(window) => window,
None => return,
};
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
fill::fill_window(window);
window.request_redraw();
}
_ => (),
}
}
}
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let mut window = None; let mut app = PumpDemo::default();
loop { loop {
let timeout = Some(Duration::ZERO); let timeout = Some(Duration::ZERO);
let status = event_loop.pump_events(timeout, |event, event_loop| { let status = event_loop.pump_app_events(timeout, &mut app);
if let Event::WindowEvent { event, .. } = &event {
// Print only Window events to reduce noise
println!("{event:?}");
}
match event {
Event::Resumed => {
let window_attributes =
Window::default_attributes().with_title("A fantastic window!");
window = Some(event_loop.create_window(window_attributes).unwrap());
}
Event::WindowEvent { event, .. } => {
let window = window.as_ref().unwrap();
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => fill::fill_window(window),
_ => (),
}
}
Event::AboutToWait => {
window.as_ref().unwrap().request_redraw();
}
_ => (),
}
});
if let PumpStatus::Exit(exit_code) = status { if let PumpStatus::Exit(exit_code) = status {
break ExitCode::from(exit_code as u8); break ExitCode::from(exit_code as u8);

View File

@@ -2,86 +2,94 @@
// Limit this example to only compatible platforms. // Limit this example to only compatible platforms.
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))] #[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))]
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
use std::time::Duration; use std::time::Duration;
use winit::{ use winit::application::ApplicationHandler;
error::EventLoopError, use winit::event::WindowEvent;
event::{Event, WindowEvent}, use winit::event_loop::{ActiveEventLoop, EventLoop};
event_loop::EventLoop, use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
platform::run_on_demand::EventLoopExtRunOnDemand, use winit::window::{Window, WindowId};
window::{Window, WindowId},
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
#[derive(Default)] #[derive(Default)]
struct App { struct App {
idx: usize,
window_id: Option<WindowId>, window_id: Option<WindowId>,
window: Option<Window>, window: Option<Window>,
} }
tracing_subscriber::fmt::init(); impl ApplicationHandler for App {
let mut event_loop = EventLoop::new().unwrap(); fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
fn run_app(event_loop: &mut EventLoop<()>, idx: usize) -> Result<(), EventLoopError> { window.request_redraw();
let mut app = App::default();
event_loop.run_on_demand(move |event, event_loop| {
println!("Run {idx}: {:?}", event);
if let Some(window) = &app.window {
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window.id() == window_id => {
println!("--------------------------------------------------------- Window {idx} CloseRequested");
fill::cleanup_window(window);
app.window = None;
}
Event::AboutToWait => window.request_redraw(),
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
} => {
fill::fill_window(window);
}
_ => (),
}
} else if let Some(id) = app.window_id {
match event {
Event::WindowEvent {
event: WindowEvent::Destroyed,
window_id,
} if id == window_id => {
println!("--------------------------------------------------------- Window {idx} Destroyed");
app.window_id = None;
event_loop.exit();
}
_ => (),
}
} else if let Event::Resumed = event {
let window_attributes = Window::default_attributes()
.with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0));
let window = event_loop.create_window(window_attributes).unwrap();
app.window_id = Some(window.id());
app.window = Some(window);
} }
}) }
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes()
.with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0));
let window = event_loop.create_window(window_attributes).unwrap();
self.window_id = Some(window.id());
self.window = Some(window);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
if event == WindowEvent::Destroyed && self.window_id == Some(window_id) {
println!(
"--------------------------------------------------------- Window {} Destroyed",
self.idx
);
self.window_id = None;
event_loop.exit();
return;
}
let window = match self.window.as_mut() {
Some(window) => window,
None => return,
};
match event {
WindowEvent::CloseRequested => {
println!("--------------------------------------------------------- Window {} CloseRequested", self.idx);
fill::cleanup_window(window);
self.window = None;
}
WindowEvent::RedrawRequested => {
fill::fill_window(window);
}
_ => (),
}
}
} }
run_app(&mut event_loop, 1)?; tracing_subscriber::fmt::init();
let mut event_loop = EventLoop::new().unwrap();
let mut app = App {
idx: 1,
..Default::default()
};
event_loop.run_app_on_demand(&mut app)?;
println!("--------------------------------------------------------- Finished first loop"); println!("--------------------------------------------------------- Finished first loop");
println!("--------------------------------------------------------- Waiting 5 seconds"); println!("--------------------------------------------------------- Waiting 5 seconds");
std::thread::sleep(Duration::from_secs(5)); std::thread::sleep(Duration::from_secs(5));
let ret = run_app(&mut event_loop, 2); app.idx += 1;
event_loop.run_app_on_demand(&mut app)?;
println!("--------------------------------------------------------- Finished second loop"); println!("--------------------------------------------------------- Finished second loop");
ret Ok(())
} }
#[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))] #[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))]

View File

@@ -14,8 +14,9 @@ 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::dpi::{LogicalSize, PhysicalPosition, PhysicalSize}; use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event::{DeviceEvent, DeviceId, Event, Ime, WindowEvent}; use winit::event::{DeviceEvent, DeviceId, Ime, WindowEvent};
use winit::event::{MouseButton, MouseScrollDelta}; 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};
@@ -53,38 +54,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let mut state = Application::new(&event_loop); let mut state = Application::new(&event_loop);
event_loop.run(move |event, event_loop| match event { event_loop.run_app(&mut state).map_err(Into::into)
Event::NewEvents(_) => (),
Event::Resumed => {
println!("Resumed the event loop");
state.dump_monitors(event_loop);
// Create initial window.
state
.create_window(event_loop, None)
.expect("failed to create initial window");
state.print_help();
}
Event::AboutToWait => {
if state.windows.is_empty() {
println!("No windows left, exiting...");
event_loop.exit();
}
}
Event::WindowEvent { window_id, event } => {
state.handle_window_event(event_loop, window_id, event)
}
Event::DeviceEvent { device_id, event } => {
state.handle_device_event(event_loop, device_id, event)
}
Event::UserEvent(event) => {
println!("User event: {event:?}");
}
Event::Suspended | Event::LoopExiting | Event::MemoryWarning => (),
})?;
Ok(())
} }
#[allow(dead_code)] #[allow(dead_code)]
@@ -104,15 +74,14 @@ 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: Context, context: Option<Context>,
} }
impl Application { impl Application {
fn new<T>(event_loop: &EventLoop<T>) -> Self { fn new<T>(event_loop: &EventLoop<T>) -> Self {
// SAFETY: the context is dropped inside the loop, since the state we're using // SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
// is moved inside the closure.
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
let context = unsafe { Context::from_raw(event_loop.raw_display_handle()).unwrap() }; let context = Some(unsafe { Context::from_raw(event_loop.raw_display_handle()).unwrap() });
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies // You'll have to choose an icon size at your own discretion. On X11, the desired size varies
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px, // by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
@@ -227,7 +196,97 @@ impl Application {
} }
} }
fn handle_window_event( fn dump_monitors(&self, event_loop: &ActiveEventLoop) {
println!("Monitors information");
let primary_monitor = event_loop.primary_monitor();
for monitor in event_loop.available_monitors() {
let intro = if primary_monitor.as_ref() == Some(&monitor) {
"Primary monitor"
} else {
"Monitor"
};
if let Some(name) = monitor.name() {
println!("{intro}: {name}");
} else {
println!("{intro}: [no name]");
}
let PhysicalSize { width, height } = monitor.size();
print!(" Current mode: {width}x{height}");
if let Some(m_hz) = monitor.refresh_rate_millihertz() {
println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
} else {
println!();
}
let PhysicalPosition { x, y } = monitor.position();
println!(" Position: {x},{y}");
println!(" Scale factor: {}", monitor.scale_factor());
println!(" Available modes (width x height x bit-depth):");
for mode in monitor.video_modes() {
let PhysicalSize { width, height } = mode.size();
let bits = mode.bit_depth();
let m_hz = mode.refresh_rate_millihertz();
println!(
" {width}x{height}x{bits} @ {}.{} Hz",
m_hz / 1000,
m_hz % 1000
);
}
}
}
/// Process the key binding.
fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
KEY_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&key, mods)
.then_some(binding.action)
})
}
/// Process mouse binding.
fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> {
MOUSE_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&button, mods)
.then_some(binding.action)
})
}
fn print_help(&self) {
println!("Keyboard bindings:");
for binding in KEY_BINDINGS {
println!(
"{}{:<10} - {} ({})",
modifiers_to_string(binding.mods),
binding.trigger,
binding.action,
binding.action.help(),
);
}
println!("Mouse bindings:");
for binding in MOUSE_BINDINGS {
println!(
"{}{:<10} - {} ({})",
modifiers_to_string(binding.mods),
mouse_button_to_string(binding.trigger),
binding.action,
binding.action.help(),
);
}
}
}
impl ApplicationHandler<UserEvent> for Application {
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
println!("User event: {event:?}");
}
fn window_event(
&mut self, &mut self,
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
window_id: WindowId, window_id: WindowId,
@@ -244,9 +303,9 @@ impl Application {
} }
WindowEvent::Focused(focused) => { WindowEvent::Focused(focused) => {
if focused { if focused {
println!("Window={window_id:?} focused"); println!("Window={window_id:?} fosused");
} else { } else {
println!("Window={window_id:?} unfocused"); println!("Window={window_id:?} unfosused");
} }
} }
WindowEvent::ScaleFactorChanged { scale_factor, .. } => { WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
@@ -371,92 +430,37 @@ impl Application {
} }
} }
fn handle_device_event(&mut self, _: &ActiveEventLoop, _: DeviceId, event: DeviceEvent) { fn device_event(
println!("Device event: {event:?}"); &mut self,
_event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
println!("Device {device_id:?} event: {event:?}");
} }
fn dump_monitors(&self, event_loop: &ActiveEventLoop) { fn resumed(&mut self, event_loop: &ActiveEventLoop) {
println!("Monitors information"); println!("Resumed the event loop");
let primary_monitor = event_loop.primary_monitor(); self.dump_monitors(event_loop);
for monitor in event_loop.available_monitors() {
let intro = if primary_monitor.as_ref() == Some(&monitor) {
"Primary monitor"
} else {
"Monitor"
};
if let Some(name) = monitor.name() { // Create initial window.
println!("{intro}: {name}"); self.create_window(event_loop, None)
} else { .expect("failed to create initial window");
println!("{intro}: [no name]");
}
let PhysicalSize { width, height } = monitor.size(); self.print_help();
print!(" Current mode: {width}x{height}"); }
if let Some(m_hz) = monitor.refresh_rate_millihertz() {
println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
} else {
println!();
}
let PhysicalPosition { x, y } = monitor.position(); fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
println!(" Position: {x},{y}"); if self.windows.is_empty() {
println!("No windows left, exiting...");
println!(" Scale factor: {}", monitor.scale_factor()); event_loop.exit();
println!(" Available modes (width x height x bit-depth):");
for mode in monitor.video_modes() {
let PhysicalSize { width, height } = mode.size();
let bits = mode.bit_depth();
let m_hz = mode.refresh_rate_millihertz();
println!(
" {width}x{height}x{bits} @ {}.{} Hz",
m_hz / 1000,
m_hz % 1000
);
}
} }
} }
/// Process the key binding. #[cfg(not(any(android_platform, ios_platform)))]
fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> { fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
KEY_BINDINGS.iter().find_map(|binding| { // We must drop the context here.
binding self.context = None;
.is_triggered_by(&key, mods)
.then_some(binding.action)
})
}
/// Process mouse binding.
fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> {
MOUSE_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&button, mods)
.then_some(binding.action)
})
}
fn print_help(&self) {
println!("Keyboard bindings:");
for binding in KEY_BINDINGS {
println!(
"{}{:<10} - {} ({})",
modifiers_to_string(binding.mods),
binding.trigger,
binding.action,
binding.action.help(),
);
}
println!("Mouse bindings:");
for binding in MOUSE_BINDINGS {
println!(
"{}{:<10} - {} ({})",
modifiers_to_string(binding.mods),
mouse_button_to_string(binding.trigger),
binding.action,
binding.action.help(),
);
}
} }
} }
@@ -496,11 +500,11 @@ struct WindowState {
} }
impl WindowState { impl WindowState {
fn new(application: &Application, window: Window) -> Result<Self, Box<dyn Error>> { fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
// 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 = unsafe { Surface::new(&application.context, &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);
println!("Theme: {theme:?}"); println!("Theme: {theme:?}");
@@ -515,7 +519,7 @@ impl WindowState {
let mut state = Self { let mut state = Self {
#[cfg(macos_platform)] #[cfg(macos_platform)]
option_as_alt: window.option_as_alt(), option_as_alt: window.option_as_alt(),
custom_idx: application.custom_cursors.len() - 1, custom_idx: app.custom_cursors.len() - 1,
cursor_grab: CursorGrabMode::None, cursor_grab: CursorGrabMode::None,
named_idx, named_idx,
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]

View File

@@ -3,16 +3,52 @@ use std::error::Error;
#[cfg(x11_platform)] #[cfg(x11_platform)]
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
use winit::{ use winit::application::ApplicationHandler;
event::{Event, WindowEvent}, use winit::event::WindowEvent;
event_loop::EventLoop, use winit::event_loop::{ActiveEventLoop, EventLoop};
platform::x11::WindowAttributesExtX11, use winit::platform::x11::WindowAttributesExtX11;
window::Window, use winit::window::{Window, WindowId};
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
pub struct XEmbedDemo {
parent_window_id: u32,
window: Option<Window>,
}
impl ApplicationHandler for XEmbedDemo {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes()
.with_title("An embedded window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(self.parent_window_id);
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let window = self.window.as_ref().unwrap();
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
window.pre_present_notify();
fill::fill_window(window);
}
_ => (),
}
}
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
self.window.as_ref().unwrap().request_redraw();
}
}
// First argument should be a 32-bit X11 window ID. // First argument should be a 32-bit X11 window ID.
let parent_window_id = std::env::args() let parent_window_id = std::env::args()
.nth(1) .nth(1)
@@ -22,35 +58,11 @@ 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 window = None; let mut app = XEmbedDemo {
event_loop.run(move |event, event_loop| match event { parent_window_id,
Event::Resumed => { window: None,
let window_attributes = Window::default_attributes() };
.with_title("An embedded window!") event_loop.run_app(&mut app).map_err(Into::into)
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(parent_window_id);
window = Some(event_loop.create_window(window_attributes).unwrap());
}
Event::WindowEvent { event, .. } => {
let window = window.as_ref().unwrap();
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
window.pre_present_notify();
fill::fill_window(window);
}
_ => (),
}
}
Event::AboutToWait => {
window.as_ref().unwrap().request_redraw();
}
_ => (),
})?;
Ok(())
} }
#[cfg(not(x11_platform))] #[cfg(not(x11_platform))]

View File

@@ -0,0 +1,12 @@
[package]
name = "common-tests"
version = "0.1.0"
rust-version.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
[dependencies]
gui-test.workspace = true
macro_rules_attribute = "0.2.0"
winit.workspace = true

View File

@@ -0,0 +1,23 @@
//! Run the test.
use gui_test::{test, Harness};
use macro_rules_attribute::apply;
use winit::event_loop::EventLoop;
#[allow(deprecated)]
#[apply(test)]
fn initialize(harness: &mut Harness) {
let mut group = harness.group("sanity");
group.harness().with_test("startup/shutdown", || {
let evl = EventLoop::new().expect("initialization");
evl.run(|_event, elwt| {
elwt.exit();
})
.expect("running");
});
}
gui_test::main! {
gui_test::remote::handler()
}

View File

@@ -0,0 +1,13 @@
[package]
name = "gui-test-runner"
version = "0.1.0"
rust-version.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
[dependencies]
camino = "1.1.6"
fastrand = "2.0.1"
gui-test.workspace = true
serde_json.workspace = true

View File

@@ -0,0 +1,81 @@
// Copyright 2024 The Winit Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! A wrapper around the `Command` type that dumps the command to stderr.
//!
//! Essentially it's like `set -x` in Bash.
use std::ffi::{OsStr, OsString};
use std::io::{self, prelude::*};
use std::process::Child;
/// Simple `Command` wrapper.
pub(super) struct Command {
/// Actual inner command.
inner: std::process::Command,
/// Command to run.
text: Vec<OsString>,
}
impl Command {
/// Create a new `Command`.
pub(super) fn new(cmd: impl AsRef<OsStr>) -> Self {
let cmd = cmd.as_ref();
Self {
inner: std::process::Command::new(cmd),
text: vec![cmd.to_os_string()],
}
}
/// Add an argument to the `Command`.
pub(super) fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
let arg = arg.as_ref();
self.inner.arg(arg);
self.text.push(arg.to_os_string());
self
}
/// Add multiple arguments to the `Command`.
pub(super) fn args<T: AsRef<OsStr>>(&mut self, args: impl IntoIterator<Item = T>) -> &mut Self {
for arg in args {
let arg = arg.as_ref();
self.inner.arg(arg);
self.text.push(arg.to_os_string());
}
self
}
/// Spawn the process.
pub(super) fn spawn(&mut self) -> io::Result<Child> {
dump_text(&self.text);
self.inner.spawn()
}
}
/// Dump `OsString` list to stderr.
fn dump_text(text: &[OsString]) {
let mut cerr = io::stderr().lock();
write!(&mut cerr, "+").unwrap();
for arg in text {
match arg.to_str() {
Some(arg) => write!(&mut cerr, " {}", arg).unwrap(),
None => write!(&mut cerr, " {:?}", arg).unwrap(),
}
}
writeln!(&mut cerr).unwrap();
}

View File

@@ -0,0 +1,91 @@
// Copyright 2024 The Winit Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Run the actual Docker command.
use crate::command::Command;
use camino::Utf8Path;
use std::ffi::OsStr;
use std::io;
use std::process::Child;
/// The Docker command line.
pub(super) struct DockerRun {
command: Command,
}
impl DockerRun {
/// Start the command.
pub(super) fn new() -> Self {
let mut command = Command::new("docker");
command.arg("run");
Self { command }
}
/// Run with an environment variable.
pub(super) fn env(&mut self, name: impl AsRef<str>, value: impl AsRef<str>) -> &mut Self {
let env_arg = format!("{}={}", name.as_ref(), value.as_ref());
self.command.args(["--env", &env_arg]);
self
}
/// Run with a simple `init` process.
pub(super) fn init(&mut self) -> &mut Self {
self.command.arg("--init");
self
}
/// Set the working directory.
pub(super) fn workdir(&mut self, dir: impl AsRef<OsStr>) -> &mut Self {
self.command.arg("--workdir");
self.command.arg(dir);
self
}
/// Remove the container once it is complete.
pub(super) fn rm(&mut self) -> &mut Self {
self.command.arg("--rm");
self
}
/// Pass a volume into the container.
pub(super) fn volume(
&mut self,
host: impl AsRef<Utf8Path>,
container: impl AsRef<Utf8Path>,
) -> &mut Self {
let list = format!("{}:{}", host.as_ref(), container.as_ref());
self.command.args(["--volume", &list]);
self
}
/// Run the container with a command.
pub(super) fn run_with_command<T: AsRef<OsStr>>(
&mut self,
container_name: impl AsRef<str>,
container_version: impl AsRef<str>,
command: impl IntoIterator<Item = T>,
) -> io::Result<Child> {
self.command.arg(format!(
"{}:{}",
container_name.as_ref(),
container_version.as_ref()
));
self.command.args(command);
self.command.spawn()
}
}

View File

@@ -0,0 +1,114 @@
// Copyright 2024 The Winit Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Run tests inside of a Linux docker container.
use super::command::DockerRun;
use crate::stream::StreamReader;
use gui_test::remote::handler;
use gui_test::TestHandler;
use std::io;
use std::os::unix::net::UnixListener;
use std::path::Path;
use std::thread;
const UBUNTU_DOCKERFILE: &str = "ghcr.io/rust-windowing/testubuntu";
const LATEST: &str = "latest";
/// Run the provided test in a Linux docker container.
pub(crate) fn linux_test(test_name: &str) -> io::Result<()> {
// Create a Unix socket to listen for events on.
let unix_path = format!("/tmp/gui_test_{}.sock", fastrand::u16(..));
let listener = UnixListener::bind(&unix_path)?;
// Spawn the Docker container.
let mut container = {
let mut docker = DockerRun::new();
// Usual options.
docker.rm().init();
// Pass through the socket as a volume.
docker.volume(&unix_path, &unix_path);
// Pass through the winit directory.
let winit_directory = Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.find_map(|path| {
let cargo_toml = path.join("Cargo.toml");
let contents = std::fs::read(cargo_toml).ok()?;
if std::str::from_utf8(&contents)
.ok()?
.contains("name = \"winit\"")
{
Some(path)
} else {
None
}
})
.unwrap();
docker.volume(
camino::Utf8Path::from_path(winit_directory).unwrap(),
"/app/winit/",
);
// Set the working dir to this directory.
docker.workdir("/app/winit/");
// Set GUI_TEST_UNIX_STREAM to the socket.
docker.env("GUI_TEST_UNIX_STREAM", &unix_path);
// Set CARGO_TARGET_DIR to a random other directory.
docker.env("CARGO_TARGET_DIR", "/tmp/");
// The command to run the test.
let command = ["xvfb-run", "cargo", "run", "-p", test_name];
// Spawn the test container.
docker.run_with_command(UBUNTU_DOCKERFILE, LATEST, command)?
};
// Run the console listener in another thread.
let handle = thread::spawn(move || {
// Attach to the listener.
let (event_reader, _) = listener.accept().unwrap();
// Read events and output them as we get them.
let input = StreamReader::new(event_reader);
let mut output = handler();
for event in input {
let event = event?;
output.handle_test(event);
}
io::Result::Ok(())
});
// Wait for the container to finish.
if !container.wait()?.success() {
return Err(io::Error::new(
io::ErrorKind::Other,
"docker exited with a failure exit code",
));
}
// Stop the thread.
handle.join().unwrap().unwrap();
Ok(())
}

View File

@@ -0,0 +1,20 @@
// Copyright 2024 The Winit Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Dealing with Docker.
mod command;
#[cfg(unix)]
pub(super) mod linux;

View File

@@ -0,0 +1,69 @@
//! Runner for the `gui-test` system.
mod command;
mod docker;
mod stream;
use std::env;
use std::process::{Command, Stdio};
fn main() {
let mut args = env::args();
// Get the test crate name.
let test_crate = args.nth(1).unwrap();
// Get the target.
let target_tag = args.next().unwrap();
// Split the target into the target and the tag.
let (target, tag) = {
let mut split = target_tag.splitn(1, ':');
let target = split.next().unwrap();
let tag = split.next();
(target, tag)
};
// Get the current target.
let current_target = current_target();
// If we are building for Linux, run the Linux Docker container.
// TODO: Architecture differences.
if target.contains("linux") {
docker::linux::linux_test(&test_crate).unwrap();
return;
}
// For now, we only support building for the current target.
assert_eq!(target, current_target);
assert!(tag.is_none());
// Just run the crate.
if !Command::new("cargo")
.args(["run", "-p", &test_crate])
.status()
.unwrap()
.success()
{
panic!("test failed");
}
}
/// Get the current target.
fn current_target() -> String {
let output = Command::new("rustc")
.arg("-vV")
.stdout(Stdio::piped())
.output()
.unwrap();
// Look for the line that starts with "host".
let stdout = String::from_utf8(output.stdout).unwrap();
for line in stdout.lines() {
if let Some(host) = line.strip_prefix("host: ") {
return host.to_string();
}
}
panic!("failed to find host: line in rustc output")
}

View File

@@ -0,0 +1,81 @@
// Copyright 2024 The Winit Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Read test events from a stream.
use gui_test::{TestEvent, TestEventType};
use std::io::{self, Read};
/// Read events from a stream.
pub(super) struct StreamReader<R> {
/// The inner reader.
reader: Option<R>,
/// Reused buffer.
buffer: Vec<u8>,
}
impl<R: Read> StreamReader<R> {
/// Create a new stream reader.
pub(super) fn new(reader: R) -> Self {
Self {
reader: Some(reader),
buffer: vec![0u8; 1024],
}
}
}
macro_rules! leap {
($self:expr, $e:expr) => {{
match ($e) {
Ok(x) => x,
Err(err) => {
($self).reader = None;
return Some(Err(err));
}
}
}};
}
impl<R: Read> Iterator for StreamReader<R> {
type Item = io::Result<TestEvent>;
fn next(&mut self) -> Option<Self::Item> {
let reader = self.reader.as_mut()?;
// Read eight bytes from the reader to get payload length.
let mut len_buffer = [0u8; 8];
leap!(self, reader.read_exact(&mut len_buffer));
// Parse that, then read the length's worth of bytes.
let length = u64::from_be_bytes(len_buffer);
self.buffer.resize(length as usize, 0);
leap!(self, reader.read_exact(&mut self.buffer));
// Parse as a test event.
let event: TestEvent = leap!(
self,
serde_json::from_slice(&self.buffer)
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
);
// If this is complete, stop running.
if matches!(event.ty, TestEventType::Complete { .. }) {
self.reader = None;
}
// We are okay.
Some(Ok(event))
}
}

17
it/gui-test/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "gui-test"
version = "0.1.0"
rust-version.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
[dependencies]
async-executor = "1.8.0"
async-io.workspace = true
async-lock = "3.3.0"
async-process = "2.1.0"
inventory = "0.3.15"
owo-colors = "4.0.0"
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true

443
it/gui-test/src/lib.rs Normal file
View File

@@ -0,0 +1,443 @@
//! A testing framework that can be run remotely.
pub mod remote;
pub mod stream;
pub mod user;
use serde::{Deserialize, Serialize};
use std::env;
use std::ffi::{OsStr, OsString};
use std::mem;
use std::num::NonZeroUsize;
use std::panic;
const GUI_TEST_CURRENT_TEST_NAME: &str = "GUI_TEST_CURRENT_TEST_NAME";
const GUI_TEST_SUBPROCESS_LIMIT: &str = "GUI_TEST_SUBPROCESS_LIMIT";
const DEFAULT_LIMIT: usize = 1;
#[doc(hidden)]
pub use inventory as __inventory;
/// Replacement for the `main` function.
#[macro_export]
macro_rules! main {
($handler:expr) => {
fn main() {
$crate::__entry(|| $handler)
}
};
}
/// Set up a test for the test framework.
#[macro_export]
macro_rules! test {
(
$(#[$attr:meta])*
fn $name:ident ($hname:ident : $htype:ty) $bl:block
) => {
const _: () = {
$(#[$attr])*
fn $name ($hname: $htype) $bl
$crate::__inventory::submit! {
$crate::__TestStart::__new(
stringify!($name),
$name
)
}
};
};
}
/// Test start.
#[doc(hidden)]
pub struct __TestStart {
/// The name of the test.
name: &'static str,
/// The function to call.
func: fn(&mut Harness),
}
impl __TestStart {
/// Create a new test start.
#[doc(hidden)]
pub const fn __new(name: &'static str, func: fn(&mut Harness)) -> Self {
Self { name, func }
}
}
inventory::collect! {
__TestStart
}
/// A harness for running the tests.
pub struct Harness {
/// Name of the test start.
name: String,
/// The inner test handler.
handler: Box<dyn TestHandler + Send + 'static>,
/// Number of tests that have been run so far.
test_count: usize,
/// Number of tests that have failed so far.
test_fails: usize,
/// Number of tests that have succeeded.
test_passed: usize,
/// Current state of the test harness.
state: State,
}
impl Harness {
/// Create a new test harness.
fn new<H: TestHandler + Send + 'static>(name: &str, handler: H) -> Self {
Self {
name: name.to_string(),
handler: Box::new(handler),
test_count: 0,
test_fails: 0,
test_passed: 0,
state: State::Default,
}
}
/// Begin a test.
pub fn test(&mut self, name: impl Into<String>) -> Testing<'_> {
// Make sure we aren't mid test.
match mem::replace(&mut self.state, State::Default) {
State::InTest { past_groups } => {
self.state = State::InTest { past_groups };
panic!("tried to start a test while another was underway");
}
State::InGroups(groups) => {
self.state = State::InTest {
past_groups: Some(groups),
};
}
State::Default => {
self.state = State::InTest { past_groups: None };
}
}
// Send the "test started" event to the handler.
self.send_event(TestEventType::TestStarted { name: name.into() });
// Return the handle.
Testing {
harness: Some(self),
}
}
/// Run a closure as a test.
pub fn with_test<T>(&mut self, name: impl Into<String>, f: impl FnOnce() -> T) -> T {
let test = self.test(name.into());
match panic::catch_unwind(panic::AssertUnwindSafe(f)) {
Ok(x) => x,
Err(err) => {
if let Some(panic) = err.downcast_ref::<&'static str>() {
test.fail(panic.to_string());
} else if let Some(panic) = err.downcast_ref::<String>() {
test.fail(panic.clone());
} else {
test.fail("unintelligible error".to_string());
}
panic::resume_unwind(err)
}
}
}
/// Begin a test group.
pub fn group(&mut self, name: impl Into<String>) -> Grouping<'_> {
// Make sure we can begin a group.
match mem::replace(&mut self.state, State::Default) {
State::Default => {
self.state = State::InGroups(NonZeroUsize::new(1).unwrap());
}
State::InGroups(groups) => {
self.state = State::InGroups(groups.checked_add(1).unwrap());
}
State::InTest { past_groups } => {
self.state = State::InTest { past_groups };
panic!("cannot start group mid-test")
}
}
// Send the "group started" event to the handler.
self.send_event(TestEventType::GroupStarted { name: name.into() });
// Return the handle.
Grouping { harness: self }
}
/// Run a closure inside of a group.
pub fn with_group<T>(&mut self, name: impl Into<String>, f: impl FnOnce(&mut Self) -> T) -> T {
let mut group = self.group(name.into());
f(group.harness())
}
/// End an ongoing test.
fn end_test(&mut self, reason: TestResult) {
self.test_count += 1;
match &reason {
TestResult::Passed => self.test_passed += 1,
TestResult::Failed(..) => self.test_fails += 1,
_ => {}
}
self.send_event(TestEventType::TestEnded { result: reason });
let count = match mem::replace(&mut self.state, State::Default) {
State::InTest { past_groups } => past_groups,
_ => unreachable!(),
};
self.state = match count {
None => State::Default,
Some(count) => State::InGroups(count),
};
}
/// End the current group.
fn end_group(&mut self) {
self.send_event(TestEventType::GroupEnded);
let count = match mem::replace(&mut self.state, State::Default) {
State::InGroups(groups) => groups,
_ => unreachable!(),
};
self.state = match NonZeroUsize::new(count.get() - 1) {
None => State::Default,
Some(groups) => State::InGroups(groups),
};
}
/// Send a test event of the provided type.
fn send_event(&mut self, ty: TestEventType) {
let event = TestEvent {
runner: self.name.clone(),
ty,
};
self.handler.handle_test(event);
}
}
impl Drop for Harness {
fn drop(&mut self) {
self.send_event(TestEventType::Complete {
total: self.test_count,
fail: self.test_fails,
pass: self.test_passed,
});
}
}
/// An in-progress test.
pub struct Testing<'a> {
harness: Option<&'a mut Harness>,
}
impl Testing<'_> {
/// Skip this test.
pub fn skip(mut self) {
// Send the "skipped" event.
self.harness.take().unwrap().end_test(TestResult::Skipped);
}
/// Fail this test.
fn fail(mut self, panic: String) {
self.harness
.take()
.unwrap()
.end_test(TestResult::Failed(panic));
}
}
impl Drop for Testing<'_> {
fn drop(&mut self) {
if let Some(harness) = self.harness.take() {
let result = if std::thread::panicking() {
TestResult::Failed("thread panicked".into())
} else {
TestResult::Passed
};
harness.end_test(result);
}
}
}
/// We are running a test group.
pub struct Grouping<'a> {
harness: &'a mut Harness,
}
impl Grouping<'_> {
/// Get the underlying test harness.
pub fn harness(&mut self) -> &mut Harness {
&mut self.harness
}
}
impl Drop for Grouping<'_> {
fn drop(&mut self) {
self.harness.end_group();
}
}
/// Current testing state.
enum State {
/// We are in the middle of this many groups.
InGroups(NonZeroUsize),
/// We are in the middle of a test.
InTest { past_groups: Option<NonZeroUsize> },
/// We are in the default state.
Default,
}
/// A handler for incoming test events.
pub trait TestHandler {
/// Handle a test.
fn handle_test(&mut self, event: TestEvent);
}
impl<T: TestHandler + ?Sized> TestHandler for Box<T> {
fn handle_test(&mut self, event: TestEvent) {
(**self).handle_test(event)
}
}
/// An event produced by the test harness.
#[derive(Debug, Serialize, Deserialize)]
pub struct TestEvent {
/// The name of the runner associated with the event.
pub runner: String,
/// The type of the event.
pub ty: TestEventType,
}
/// The type of the event.
#[non_exhaustive]
#[derive(Debug, Serialize, Deserialize)]
pub enum TestEventType {
/// The tests are complete and the harness can be disconnected.
Complete {
/// Total number of tests.
total: usize,
/// Total number of passing tests.
pass: usize,
/// Total number of failed tests.
fail: usize,
},
/// A test has started.
TestStarted { name: String },
/// A test has completed.
TestEnded { result: TestResult },
/// A test group has started.
GroupStarted { name: String },
/// A test group has ended.
GroupEnded,
}
/// The result of a test.
#[non_exhaustive]
#[derive(Debug, Serialize, Deserialize)]
pub enum TestResult {
/// The test passed.
Passed,
/// The test failed with the provided error.
Failed(String),
/// The test was skipped.
Skipped,
}
/// Entry point of the test.
#[doc(hidden)]
pub fn __entry<H: TestHandler + Send + 'static>(handler: impl FnOnce() -> H) {
// Look for the test name environment variable.
if let Some(test_name) = env::var(GUI_TEST_CURRENT_TEST_NAME)
.ok()
.filter(|test_name| !test_name.is_empty())
{
// Find the provided test.
let test_to_run = inventory::iter::<__TestStart>
.into_iter()
.find(|test| test.name == test_name)
.unwrap_or_else(|| panic!("unable to find test '{test_name}'"));
// Create a harness.
let mut harness = Harness::new(test_to_run.name, handler());
// Run the test.
panic::catch_unwind(panic::AssertUnwindSafe(move || {
(test_to_run.func)(&mut harness)
}))
.ok();
} else {
// Run a subprocess for every test.
let limit = env::var(GUI_TEST_SUBPROCESS_LIMIT)
.ok()
.and_then(|limit| limit.parse::<usize>().ok())
.unwrap_or(DEFAULT_LIMIT);
let process_name = env::args_os().next().unwrap();
let sema = async_lock::Semaphore::new(limit);
let ex = async_executor::Executor::new();
async_io::block_on(ex.run(async {
let mut tasks = vec![];
// Set up an environment variable for this.
for test in inventory::iter::<__TestStart> {
// Acquire a guard.
let guard = sema.acquire().await;
// Spawn a subprocess.
let mut process = async_process::Command::new(&process_name)
.envs(env::vars_os().chain(Some({
(path(&GUI_TEST_CURRENT_TEST_NAME), path(&test.name))
})))
.spawn()
.expect("failed to spawn child process");
// Spawn a task to poll that subprocess.
let task = ex.spawn(async move {
let _guard = guard;
process.status().await.unwrap()
});
tasks.push(task);
}
// Finish all of the tasks.
for task in tasks {
task.await;
}
}));
}
}
fn path<A: AsRef<OsStr>>(s: &A) -> OsString {
s.as_ref().into()
}

31
it/gui-test/src/remote.rs Normal file
View File

@@ -0,0 +1,31 @@
//! Create a test handler that can be run remotely.
use crate::stream::WriteHandler;
use crate::user::UserHandler;
use crate::TestHandler;
use std::env;
use std::net::TcpStream;
/// Create a test handler adjusted for the current environment.
pub fn handler() -> Box<dyn TestHandler + Send + 'static> {
// If GUI_TEST_UNIX_STREAM is enabled, use that as a Unix stream.
#[cfg(unix)]
if let Some(stream_path) = env::var_os("GUI_TEST_UNIX_STREAM").filter(|s| !s.is_empty()) {
let stream = std::os::unix::net::UnixStream::connect(stream_path)
.expect("unable to connect to gui-test handler");
return Box::new(WriteHandler::new(stream));
}
// If GUI_TEST_TCP_STREAM is enabled, use that as a TCP stream.
if let Some(tcp_ip) = env::var("GUI_TEST_TCP_STREAM")
.ok()
.filter(|s| !s.is_empty())
{
let stream = TcpStream::connect(tcp_ip).unwrap();
return Box::new(WriteHandler::new(stream));
}
// By default, use the user handler.
Box::new(UserHandler::new())
}

39
it/gui-test/src/stream.rs Normal file
View File

@@ -0,0 +1,39 @@
//! Write events to an output stream.
//!
//! The format is as follows:
//! - First 8 bytes: big-endian length of payload.
//! - Next {len} bytes: JSON payload to deserialize from.
use crate::{TestEvent, TestHandler};
use std::io::Write;
/// A wrapper around a writer that sends data down a stream.
#[derive(Debug)]
pub struct WriteHandler<W: Write> {
/// The inner writer.
writer: W,
}
impl<W: Write> WriteHandler<W> {
/// Create a new write handler.
pub fn new(writer: W) -> Self {
Self { writer }
}
}
impl<W: Write> TestHandler for WriteHandler<W> {
fn handle_test(&mut self, event: TestEvent) {
let payload = serde_json::to_vec(&event).unwrap();
let length = u64::to_be_bytes(payload.len() as u64);
// Write the payload to the stream.
self.writer.write_all(&length).unwrap();
self.writer.write_all(&payload).unwrap();
}
}
impl<W: Write> Drop for WriteHandler<W> {
fn drop(&mut self) {
self.writer.flush().ok();
}
}

180
it/gui-test/src/user.rs Normal file
View File

@@ -0,0 +1,180 @@
//! User-facing reporter.
use crate::{TestEvent, TestEventType, TestHandler, TestResult};
use owo_colors::OwoColorize;
use std::collections::BTreeMap;
use std::io::{self, prelude::*};
const TABSIZE: usize = 2;
/// User-facing reporter.
///
/// This reporter dumps events to the console in a user-readable format.
pub struct UserHandler {
/// Current indent.
indent: usize,
/// The test set we're currently displaying.
current_start: Option<String>,
/// Test name we are running, if any.
test_name: Option<String>,
/// Cached events.
cache: BTreeMap<String, Vec<TestEventType>>,
/// Failures we had.
failures: Vec<(String, String)>,
}
impl UserHandler {
/// Create a new handler.
pub fn new() -> Self {
Self {
indent: 0,
current_start: None,
test_name: None,
cache: BTreeMap::new(),
failures: vec![],
}
}
/// Process the provided events.
fn process_events(&mut self, events: impl IntoIterator<Item = TestEvent>) {
for event in events {
// Tell if this is an end event.
let mut ender = matches!(event.ty, TestEventType::Complete { .. });
// If there is no test name set, run the current one.
match self.current_start.as_ref() {
None => {
let TestEvent { runner, ty } = event;
self.current_start = Some(runner);
self.dump_events(Some(ty));
}
Some(test_name) => {
// If there is a test name set and it's ours, post it immediately.
if test_name == &event.runner {
self.dump_events(Some(event.ty));
} else {
// Add it to the back of another one of the events.
self.cache
.entry(test_name.clone())
.or_default()
.push(event.ty);
}
}
}
// If this is the end, dump other events.
while ender {
ender = false;
assert!(self.current_start.take().is_some());
// Pick one set.
if let Some(entry) = self.cache.first_entry() {
let (test_name, entries) = entry.remove_entry();
self.current_start = Some(test_name);
// Dump events and look for a conclusion.
self.dump_events(entries.into_iter().inspect(|ty| {
ender |= matches!(ty, TestEventType::Complete { .. });
}));
}
println!();
}
}
}
/// Dump the provided events to the console.
fn dump_events(&mut self, events: impl IntoIterator<Item = TestEventType>) {
let mut stdout = io::stdout().lock();
for event in events {
// Write the indent.
for _ in 0..(self.indent * TABSIZE) {
stdout.write_all(b" ").unwrap();
}
match event {
TestEventType::GroupStarted { name } => {
assert!(self.test_name.is_none());
// Write the group name and bump the indent.
writeln!(stdout, "{}", name.yellow().italic()).unwrap();
// Add to the indent.
self.indent += 1;
}
TestEventType::GroupEnded => {
assert!(self.test_name.is_none());
// Drop the indent.
self.indent = self.indent.checked_sub(1).unwrap();
}
TestEventType::TestStarted { name } => {
assert!(self.test_name.is_none());
// Write the line.
write!(stdout, "{} ", name.white().italic()).unwrap();
self.test_name = Some(name);
}
TestEventType::TestEnded { result } => {
let test_name = self.test_name.take().unwrap();
// Write the result.
match result {
TestResult::Passed => {
writeln!(stdout, "{}", "ok".green().bold()).unwrap();
}
TestResult::Failed(failure) => {
self.failures.push((test_name, failure));
writeln!(stdout, "{}", "FAIL".red().bold()).unwrap();
}
TestResult::Skipped => {
writeln!(stdout, "{}", "skipped".yellow().bold()).unwrap();
}
}
}
_ => {
// Completion.
}
}
}
}
}
impl TestHandler for UserHandler {
fn handle_test(&mut self, event: TestEvent) {
self.process_events(Some(event));
}
}
impl Drop for UserHandler {
fn drop(&mut self) {
assert!(self.cache.is_empty());
// Write the final bit to the stdout.
let mut stdout = io::stdout().lock();
if !self.failures.is_empty() {
writeln!(stdout, "Test Failures:").ok();
for (test_name, panic) in &self.failures {
writeln!(stdout, " {}", test_name).ok();
writeln!(stdout, "-------------").ok();
writeln!(stdout, "{}", panic).ok();
writeln!(stdout, "-------------").ok();
}
}
}
}

View File

@@ -1,707 +0,0 @@
ABNT
ACCEPTFILES
ALTERASE
APPCOMMAND
APPSTARTING
APPWINDOW
ASYNCWINDOWPOS
ATOK
AZERTY
Abortable
Artur
Autorotate
BACKTAB
BADFLAGS
BADMODE
BADPARAM
BASSBOOST
BITSPERPEL
BKSP
BLURBEHIND
BOTTOMLEFT
BOTTOMRIGHT
BRIGHTNESSDOW
BRIGHTNESSU
BYCOMMAND
Backquote
Bangou
Blackbox
CAEAGL
CANDIDATEFORM
CAPTURECHANGED
CFUUID
CLIPSIBLINGS
CLOEXEC
CLOSECD
CODEOWNERS
COLORSYNC
COMPATTR
COMPOSITIONFORM
COMPSTR
CREATESTRUCTW
CRSEL
CRTC
CTYPE
CURSORPOS
CUSEL
CXVIRTUALSCREEN
CYCLEWINDOWS
CYVIRTUALSCREEN
Calculater
Codeinput
Colormap
Compiz
Condvar
Crtc
DEADCHAR
DEFAULTSIZE
DEFAULTTONEAREST
DEFAULTTONULL
DEFAULTTOPRIMARY
DELETEFILE
DEVICEINFO
DEVICELALTKEYMASK
DEVICELCMDKEYMASK
DEVICELCTLKEYMASK
DEVICELSHIFTKEYMASK
DEVICENAME
DEVICERALTKEYMASK
DEVICERCMDKEYMASK
DEVICERCTLKEYMASK
DEVICERSHIFTKEYMASK
DEVMODEW
DEVNOTIFY
DISP
DISPLAYFREQUENCY
DPAD
DPICHANGED
DROPEFFECT
DVASPECT
DWMCOMPOSITIONCHANGED
DWMWA
Deque
Dflt
EINTR
EINVAL
EJECTCD
EJECTCLOSECD
ENDCALL
ENDCOMPOSITION
ENLW
ENTERSIZEMOVE
ERCIM
EREOF
EWMH
EXITSIZEMOVE
EXSEL
EXSTYLE
EXTENDEDKEY
Eisu
Eisuu
Endcall
Endianness
FASTFORWARD
FLASHW
FLASHWINFO
FORMATETC
FORWARDMAIL
FRAMECHANGED
FVWM
GAMEPAD
GETHIGHCONTRAST
GETMINMAXINFO
GIDC
GLES
GWLP
HANGEUL
HANJA
HCURSOR
HDROP
HEADSETHOOK
HGLOBAL
HICON
HIGHCONTRASTA
HIGHCONTRASTON
HIMC
HINSTANCE
HMODULE
HORZ
HRAWINPUT
HTBOTTOM
HTBOTTOMLEFT
HTBOTTOMRIGHT
HTCAPTION
HTCLIENT
HTLEFT
HTRIGHT
HTTOP
HTTOPLEFT
HTTOPRIGHT
HWHEEL
Hanja
Hankaku
Headsethook
Henkan
Himetric
Hotspot
IACE
IBEAM
ICCCM
ICONINFO
IMEs
IMMENABLED
INPUTSINK
INTERNALPAINT
IOYUV
Impls
Ivars
JISHO
JUNJA
Junja
KBDILLUMDOWN
KBDILLUMTOGGLE
KBDILLUMUP
KEYDOWN
KEYFIRST
KEYLAST
KEYMAP
KEYUP
KILLFOCUS
KPJPCOMMA
KPLEFTPAREN
KPPLUSMINUS
KPRIGHTPAREN
Kanna
Keymap
Koho
LALT
LBUTTON
LBUTTONDOWN
LBUTTONUP
LCONTROL
LCTRL
LEFTALIGN
LOADFROMFILE
LOGPIXELSX
LOYA
LRESULT
LSHIFT
LSUPER
LWIN
Lcdfilter
Libera
MASSHOU
MAXIMIZABLE
MAXIMIZEBOX
MBUTTON
MBUTTONDOWN
MBUTTONUP
MENUCHAR
MICMUTE
MINIMIZABLE
MINIMIZEBOX
MINMAXINFO
MODECHANGE
MONITORINFO
MONITORINFOEXW
MOUSEHWHEEL
MOUSELEAVE
MOUSEMOVE
MOUSEWHEEL
MOVERESIZE
MSDOS
MSRV
Massyo
Miniaturizable
Mmap
Modifiermap
Muhenkan
Multitouch
NCACTIVATE
NCCALCSIZE
NCDESTROY
NCHITTEST
NCLBUTTONDOWN
NEXTTRACK
NOACTIVATE
NOMOVE
NONAME
NONCONVERT
NOREMOVE
NOREPOSITION
NOSIZE
NOTCONVERTED
NOTOPMOST
NOZORDER
NTSTATUS
NUMLOCK
Nesw
Nonnull
Nunley
OSVERSIONINFOW
OVERLAPPEDWINDOW
Overscan
PAUSECD
PCSTR
PCWSTR
PELSHEIGHT
PELSWIDTH
PGDN
PGUP
PINP
PLAYCD
POINTERDOWN
POINTERUP
POINTERUPDATE
POINTL
PQRS
PREVIOUSTRACK
PROCESSKEY
Pboard
Peekable
Pels
Pictsymbols
Pixmap
Premultiply
QERTZ
QWERTZ
RALT
RAWINPUT
RAWINPUTDEVICE
RAWINPUTDEVICELIST
RAWINPUTHEADER
RAWKEYBOARD
RBUTTON
RBUTTONDOWN
RBUTTONUP
RCONTROL
RCTRL
RDWR
RESULTSTR
RETURNCMD
RFKILL
RGBA
RIDEV
RIDI
RMENU
ROYA
RRRRRGGGGGBBBBB
RRRRRRRRGGGGGGGGBBBBBBBB
RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB
RSHIFT
RSUPER
RUSTDOCFLAGS
RWIN
Raii
Reentrancy
Reparent
Romaji
SCREENSAVE
SCROLLDOWN
SCROLLUP
SENDFILE
SETCONTEXT
SETCURSOR
SETFOCUS
SETICON
SETTINGCHANGE
SHOULDAPPSUSEDARKMODE
SHOWNOACTIVATE
SHOWUICOMPOSITIONWINDOW
SIGILL
SIGSEGV
SIZEALL
SIZEBOX
SIZENESW
SIZENS
SIZENWSE
SIZEWE
STARTCOMPOSITION
STATDATA
STGMEDIUM
SWITCHVIDEOMODE
SYSCHAR
SYSCOMMAND
SYSDEADCHAR
SYSKEYDOWN
SYSKEYUP
SYSMENU
Smol
Sonoma
Subcompositor
Sublayer
Subviews
Sysrq
THUMBSTICK
TIMERNOFG
TOOLWINDOW
TOPLEFT
TOPRIGHT
TOUCHEVENTF
TOUCHINPUT
TOUROKU
TRACKMOUSEEVENT
TYMED
TYPEHID
TYPEKEYBOARD
TYPEMOUSE
Thumbl
Thumbr
Tomiĉo
UNICHAR
USEDARKMODECOLORS
UXTHEME
Unadjust
Unadvise
Ungrab
Unminimizing
VKEY
VKEYS
Viewporter
Visualid
Vulkan
WINDOWCOMPOSITIONATTRIB
WINDOWCOMPOSITIONATTRIBDATA
WINDOWEDGE
WINDOWPLACEMENT
WINDOWPOS
WINDOWPOSCHANGED
WINDOWPOSCHANGING
WLAN
WNDPROC
WSCTRL
WXYZ
XBUTTONDOWN
XBUTTONUP
XEMBED
XFER
XKBCH
XKBH
XKBXH
XMODIFIERS
XSETTINGS
XVIRTUALSCREEN
Xcursor
Xdnd
Xfer
Xids
Xorg
Xutf
YVIRTUALSCREEN
Zenkaku
aarch
abortable
adwaita
ahash
altgr
apartmentthreaded
argb
armv
atleast
attribs
autoreleasepool
autoreleases
autorotate
beachball
beforeunload
behaviour
bfcache
bgra
bindgen
bitflags
bitmaprenderer
blackbox
blurregion
borderless
busybutclickable
bytemuck
bytewise
calloop
callstack
cdylib
cgfloat
clicky
clipchildren
clonable
clsctx
clsid
codepaths
coinit
colormap
contextmenu
crossfont
crtc
crtcs
curr
darkmode
dbus
deminiaturize
deviceid
dlopen
docsrs
donotround
doubletap
downscaling
dppx
dwmsbt
dwmwcp
elwt
emscripten
endianness
entrancy
entrantly
evdev
eventloop
evlp
evtype
exclam
excludefromcapture
fcitx
forcetouch
fpath
fract
fsecs
fullscreen
fullscreened
fullsize
gamepads
getpid
gettid
glutin
gotchyas
hdrop
henkan
hidpi
himc
himetric
hinstance
hittest
hiword
hmenu
hmonitor
horz
hotplug
hotspot
hotx
hredraw
hresult
htotal
hwnd
ibus
icrate
impls
initer
inputmethod
isize
ivars
kcav
kchibisov
keybdinput
keybinds
keyevent
keyeventf
keymap
keypermod
keypresses
keysym
keysyms
kunddaliya
kwin
lcddefault
lgid
libc
libwayland
libxkbcommon
lindex
lmenu
longjmp
longsolidusoverlay
lowline
loword
lparam
lpfn
lpsz
lshift
macbooks
madsmtm
mainloop
maintainership
mainwindow
mapvk
memmap
millihertz
minwindef
mkdid
mkwid
modifierless
modifiermap
msiglreith
muhenkan
multitouch
nanos
nccreate
netwm
newtype
nodename
nonminimal
noredirectionbitmap
notgull
notitle
nsec
nsscreen
nsstring
nsview
ntdll
numpad
numpads
objc
offcenter
onpointerrawupdate
opengl
oppsite
orbclient
ossi
overscan
overtyping
pagehide
pageshow
physicalkey
pixmap
pointercancel
pointermove
pointerout
pointerover
pointerrawupdate
polonius
ppmm
preedit
premultiply
primarylangid
pthread
qhandle
randr
reallocs
rects
reentrancy
reparent
reparenting
replugs
repr
resizeable
retval
rgba
rgrc
rightclick
riid
roundsmall
rshift
runloop
rustc
rustdoc
rustix
scancode
scancodes
sctk
serde
setjmp
setlocale
shcore
smithay
smol
softbuffer
sourceid
splitn
standardised
stdweb
struct
structfield
subcompositor
subframework
subsec
subviews
syms
syscall
systembackdrop
sythesize
sythesized
sythetic
tabbedwindow
tabindex
throghout
timespec
titlebar
touchpad
touchstart
trackpad
transientwindow
tymed
uapi
uiscreen
uiscreens
ulong
unaccel
unaccelerated
uncoalesced
unconfine
undropped
unfocusing
ungrab
ungrabs
uninit
uninitialize
unmark
unmaximized
unminimize
unobserve
unparameterised
unref
unresizable
usedefault
userdata
uxtheme
viewporter
visibilitychange
visualid
visualtype
vkey
vredraw
vsync
vtbl
vtotal
vulkano
wakeup
wakeups
wantpalm
wgpu
winapi
windef
winit
winuser
wndclassexw
wparam
wrongcompobj
xbutton
xconn
xconnection
xcursor
xdisplay
xevent
xext
xfiltered
xfixes
xhot
ximage
xkbcommon
xkbext
xlib
xmodifiers
xmonad
xpresent
xrandr
xrender
xresources
xscrnsaver
xsettings
xwindow
yeong
yhot

221
src/application.rs Normal file
View File

@@ -0,0 +1,221 @@
//! End user application handling.
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
use crate::event_loop::ActiveEventLoop;
use crate::window::WindowId;
/// The handler of the application events.
pub trait ApplicationHandler<T: 'static = ()> {
/// Emitted when new events arrive from the OS to be processed.
///
/// This is a useful place to put code that should be done before you start processing
/// events, such as updating frame timing information for benchmarking or checking the
/// [`StartCause`] to see if a timer set by
/// [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil] has elapsed.
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
let _ = (event_loop, cause);
}
/// Emitted when the application has been resumed.
///
/// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a
/// formal suspend/resume lifecycle. For systems without a formal suspend/resume lifecycle
/// the `Resumed` event is always emitted after the [`NewEvents(StartCause::Init)`][StartCause::Init]
/// event.
///
/// # Portability
///
/// It's recommended that applications should only initialize their graphics context and create
/// a window after they have received their first `Resumed` event. Some systems
/// (specifically Android) won't allow applications to create a render surface until they are
/// resumed.
///
/// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events.
///
/// Also see [`Suspended`] notes.
///
/// ## Android
///
/// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is
/// expected to closely correlate with the [`onResume`] lifecycle event but there may technically
/// be a discrepancy.
///
/// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
///
/// Applications that need to run on Android must wait until they have been `Resumed`
/// before they will be able to create a render surface (such as an `EGLSurface`,
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a
/// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their
/// render surfaces are invalid and should be dropped.
///
/// Also see [`Suspended`] notes.
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`]
/// callback which means the application is "active" (according to the
/// [iOS application lifecycle]).
///
/// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event
/// with the property [`persisted`] being true, which means that the page is being
/// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
/// stores a complete snapshot of a page (including the JavaScript heap) as the
/// user is navigating away.
///
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
/// [`Suspended`]: Self::suspended
fn resumed(&mut self, event_loop: &ActiveEventLoop);
/// Emitted when an event is sent from [`EventLoopProxy::send_event`].
///
/// [`EventLoopProxy::send_event`]: crate::event_loop::EventLoopProxy::send_event
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
let _ = (event_loop, event);
}
/// Emitted when the OS sends an event to a winit window.
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
);
/// Emitted when the OS sends an event to a device.
fn device_event(
&mut self,
event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
let _ = (event_loop, device_id, event);
}
/// 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
/// between how often the event loop needs to wake up and the dispatching of any specific events.
///
/// High frequency event sources, such as input devices could potentially lead to lots of wake
/// ups and also lots of corresponding `AboutToWait` events.
///
/// This is not an ideal event to drive application rendering from and instead applications
/// should render in response to [`WindowEvent::RedrawRequested`] events.
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the application has been suspended.
///
/// # Portability
///
/// Not all platforms support the notion of suspending applications, and there may be no
/// technical way to guarantee being able to emit a `Suspended` event if the OS has
/// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason,
/// Winit does not currently try to emit pseudo `Suspended` events before the application
/// quits on platforms without an application lifecycle.
///
/// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] events.
///
/// Also see [`Resumed`] notes.
///
/// ## Android
///
/// On Android, the `Suspended` event is only sent when the application's associated
/// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`]
/// lifecycle event but there may technically be a discrepancy.
///
/// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause()
///
/// Applications that need to run on Android should assume their [`SurfaceView`] has been
/// destroyed, which indirectly invalidates any existing render surfaces that may have been
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
///
/// After being `Suspended` on Android applications must drop all render surfaces before
/// the event callback completes, which may be re-created when the application is next [`Resumed`].
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Suspended` event is currently emitted in response to an
/// [`applicationWillResignActive`] callback which means that the application is
/// about to transition from the active to inactive state (according to the
/// [iOS application lifecycle]).
///
/// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event
/// with the property [`persisted`] being true, which means that the page is being
/// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a
/// complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
/// [`Resumed`]: Self::resumed
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the event loop is being shut down.
///
/// This is irreversible - if this method is called, it is guaranteed that the event loop
/// will exist right after.
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the application has received a memory warning.
///
/// ## Platform-specific
///
/// ### Android
///
/// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application
/// must [release memory] or risk being killed.
///
/// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory()
/// [release memory]: https://developer.android.com/topic/performance/memory#release
///
/// ### iOS
///
/// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`]
/// callback. The application must free as much memory as possible or risk being terminated, see
/// [how to respond to memory warnings].
///
/// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni
/// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings
///
/// ### Others
///
/// - **macOS / Orbital / Wayland / Web / Windows:** Unsupported.
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
}

75
src/changelog/mod.rs Normal file
View File

@@ -0,0 +1,75 @@
//! # 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.29.md"))]
#[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

@@ -0,0 +1,42 @@
## Unreleased
- Deprecate `EventLoop::run` in favor of `EventLoop::run_app`.
- Deprecate `EventLoopExtRunOnDemand::run_on_demand` in favor of `EventLoop::run_app_on_demand`.
- Deprecate `EventLoopExtPumpEvents::pump_events` in favor of `EventLoopExtPumpEvents::pump_app_events`.
- Add `ApplicationHandler<T>` trait which mimics `Event<T>`.
- Move `dpi` types to its own crate, and re-export it from the root crate.
- Implement `Sync` for `EventLoopProxy<T: Send>`.
- **Breaking:** Move `Window::new` to `ActiveEventLoop::create_window` and `EventLoop::create_window` (with the latter being deprecated).
- **Breaking:** Rename `EventLoopWindowTarget` to `ActiveEventLoop`.
- **Breaking:** Remove `Deref` implementation for `EventLoop` that gave `EventLoopWindowTarget`.
- **Breaking**: Remove `WindowBuilder` in favor of `WindowAttributes`.
- **Breaking:** Removed unnecessary generic parameter `T` from `EventLoopWindowTarget`.
- On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example.
- **Breaking:** Remove `Window::set_cursor_icon`
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a `CursorIcon` or `CustomCursor`
- Add `CustomCursor::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.
- On macOS, add services menu.
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
- On Web, fix setting cursor icon overriding cursor visibility.
- **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
- **Breaking:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available.
- **Breaking:** Bump MSRV from `1.65` to `1.70`.
- On Web, add the ability to toggle calling `Event.preventDefault()` on `Window`.
- **Breaking:** Remove `WindowAttributes::fullscreen()` and expose as field directly.
- **Breaking:** Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold static data.
- **Breaking:** No longer export `platform::x11::XNotSupported`.
- **Breaking:** Renamed `platform::x11::XWindowType` to `platform::x11::WindowType`.
- Add the `OwnedDisplayHandle` type for allowing safe display handle usage outside of trivial cases.
- **Breaking:** Rename `TouchpadMagnify` to `PinchGesture`, `SmartMagnify` to `DoubleTapGesture` and `TouchpadRotate` to `RotationGesture` to represent the action rather than the intent.
- on iOS, add detection support for `PinchGesture`, `DoubleTapGesture` and `RotationGesture`.
- on Windows: add `with_system_backdrop`, `with_border_color`, `with_title_background_color`, `with_title_text_color` and `with_corner_preference`
- On Windows, Remove `WS_CAPTION`, `WS_BORDER` and `WS_EX_WINDOWEDGE` styles for child windows without decorations.
- **Breaking:** Removed `EventLoopError::AlreadyRunning`, which can't happen as it is already prevented by the type system.
- Added `EventLoop::builder`, which is intended to replace the (now deprecated) `EventLoopBuilder::new`.
- **Breaking:** Changed the signature of `EventLoop::with_user_event` to return a builder.
- **Breaking:** Removed `EventLoopBuilder::with_user_event`, the functionality is now available in `EventLoop::with_user_event`.
- Add `Window::default_attributes` to get default `WindowAttributes`.
- `log` has been replaced with `tracing`. The old behavior can be emulated by setting the `log` feature on the `tracing` crate.

13
src/changelog/v0.10.md Normal file
View File

@@ -0,0 +1,13 @@
## 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.

27
src/changelog/v0.11.md Normal file
View File

@@ -0,0 +1,27 @@
## 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.

8
src/changelog/v0.12.md Normal file
View File

@@ -0,0 +1,8 @@
## 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.

20
src/changelog/v0.13.md Normal file
View File

@@ -0,0 +1,20 @@
## 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`.

21
src/changelog/v0.14.md Normal file
View File

@@ -0,0 +1,21 @@
## 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.

42
src/changelog/v0.15.md Normal file
View File

@@ -0,0 +1,42 @@
## 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`.

32
src/changelog/v0.16.md Normal file
View File

@@ -0,0 +1,32 @@
## 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 `?`.

23
src/changelog/v0.17.md Normal file
View File

@@ -0,0 +1,23 @@
## 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.

52
src/changelog/v0.18.md Normal file
View File

@@ -0,0 +1,52 @@
## 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`.

27
src/changelog/v0.19.md Normal file
View File

@@ -0,0 +1,27 @@
## 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.

200
src/changelog/v0.20.md Normal file
View File

@@ -0,0 +1,200 @@
## 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.

16
src/changelog/v0.21.md Normal file
View File

@@ -0,0 +1,16 @@
## 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

37
src/changelog/v0.22.md Normal file
View File

@@ -0,0 +1,37 @@
## 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.

65
src/changelog/v0.23.md Normal file
View File

@@ -0,0 +1,65 @@
## 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.

28
src/changelog/v0.24.md Normal file
View File

@@ -0,0 +1,28 @@
## 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`.

31
src/changelog/v0.25.md Normal file
View File

@@ -0,0 +1,31 @@
## 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`).

36
src/changelog/v0.26.md Normal file
View File

@@ -0,0 +1,36 @@
## 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.

107
src/changelog/v0.27.md Normal file
View File

@@ -0,0 +1,107 @@
## 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.

100
src/changelog/v0.28.md Normal file
View File

@@ -0,0 +1,100 @@
## 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.

289
src/changelog/v0.29.md Normal file
View File

@@ -0,0 +1,289 @@
## 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 loosing 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`.

33
src/changelog/v0.8.md Normal file
View File

@@ -0,0 +1,33 @@
## 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).

22
src/changelog/v0.9.md Normal file
View File

@@ -0,0 +1,22 @@
## 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

@@ -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),
@@ -195,6 +195,7 @@ impl OnlyCursorImageSource {
} }
/// Platforms export this directly as `PlatformCustomCursor` if they don't implement caching. /// Platforms export this directly as `PlatformCustomCursor` if they don't implement caching.
#[allow(dead_code)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct OnlyCursorImage(pub(crate) Arc<CursorImage>); pub(crate) struct OnlyCursorImage(pub(crate) Arc<CursorImage>);

View File

@@ -1,36 +1,37 @@
//! The [`Event`] enum and assorted supporting types. //! The [`Event`] enum and assorted supporting types.
//! //!
//! These are sent to the closure given to [`EventLoop::run(...)`], 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 documentation. //! processed and used to modify the program state. For more details, see the root-level documentation.
//! //!
//! Some of these events represent different "parts" of a traditional event-handling loop. You could //! Some of these events represent different "parts" of a traditional event-handling loop. You could
//! approximate the basic ordering loop of [`EventLoop::run(...)`] like this: //! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this:
//! //!
//! ```rust,ignore //! ```rust,ignore
//! let mut start_cause = StartCause::Init; //! let mut start_cause = StartCause::Init;
//! //!
//! while !elwt.exiting() { //! while !elwt.exiting() {
//! event_handler(NewEvents(start_cause), elwt); //! app.new_events(event_loop, start_cause);
//! //!
//! for e in (window events, user events, device events) { //! for event in (window events, user events, device events) {
//! event_handler(e, elwt); //! // This will pick the right method on the application based on the event.
//! app.handle_event(event_loop, event);
//! } //! }
//! //!
//! for w in (redraw windows) { //! for window_id in (redraw windows) {
//! event_handler(RedrawRequested(w), elwt); //! app.window_event(event_loop, window_id, RedrawRequested);
//! } //! }
//! //!
//! event_handler(AboutToWait, elwt); //! app.about_to_wait(event_loop);
//! start_cause = wait_if_necessary(); //! start_cause = wait_if_necessary();
//! } //! }
//! //!
//! event_handler(LoopExiting, elwt); //! app.exiting(event_loop);
//! ``` //! ```
//! //!
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully //! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
//! describes what happens in what order. //! describes what happens in what order.
//! //!
//! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run //! [`EventLoop::run_app(...)`]: crate::event_loop::EventLoop::run_app
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil //! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Mutex, Weak}; use std::sync::{Mutex, Weak};
@@ -59,199 +60,55 @@ use crate::{
/// See the module-level docs for more information on the event loop manages each event. /// See the module-level docs for more information on the event loop manages each event.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Event<T: 'static> { pub enum Event<T: 'static> {
/// Emitted when new events arrive from the OS to be processed. /// See [`ApplicationHandler::new_events`] for details.
/// ///
/// This event type is useful as a place to put code that should be done before you start /// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events
/// processing events, such as updating frame timing information for benchmarking or checking
/// the [`StartCause`] to see if a timer set by
/// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed.
NewEvents(StartCause), NewEvents(StartCause),
/// Emitted when the OS sends an event to a winit window. /// See [`ApplicationHandler::window_event`] for details.
///
/// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event
WindowEvent { WindowEvent {
window_id: WindowId, window_id: WindowId,
event: WindowEvent, event: WindowEvent,
}, },
/// Emitted when the OS sends an event to a device. /// See [`ApplicationHandler::device_event`] for details.
///
/// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event
DeviceEvent { DeviceEvent {
device_id: DeviceId, device_id: DeviceId,
event: DeviceEvent, event: DeviceEvent,
}, },
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event) /// See [`ApplicationHandler::user_event`] for details.
///
/// [`ApplicationHandler::user_event`]: crate::application::ApplicationHandler::user_event
UserEvent(T), UserEvent(T),
/// Emitted when the application has been suspended. /// See [`ApplicationHandler::suspended`] for details.
/// ///
/// # Portability /// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended
///
/// Not all platforms support the notion of suspending applications, and there may be no
/// technical way to guarantee being able to emit a `Suspended` event if the OS has
/// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason,
/// Winit does not currently try to emit pseudo `Suspended` events before the application
/// quits on platforms without an application lifecycle.
///
/// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] events.
///
/// Also see [`Resumed`] notes.
///
/// ## Android
///
/// On Android, the `Suspended` event is only sent when the application's associated
/// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`]
/// lifecycle event but there may technically be a discrepancy.
///
/// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause()
///
/// Applications that need to run on Android should assume their [`SurfaceView`] has been
/// destroyed, which indirectly invalidates any existing render surfaces that may have been
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
///
/// After being `Suspended` on Android applications must drop all render surfaces before
/// the event callback completes, which may be re-created when the application is next [`Resumed`].
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Suspended` event is currently emitted in response to an
/// [`applicationWillResignActive`] callback which means that the application is
/// about to transition from the active to inactive state (according to the
/// [iOS application lifecycle]).
///
/// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event
/// with the property [`persisted`] being true, which means that the page is being
/// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a
/// complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
///
/// [`Resumed`]: Self::Resumed
Suspended, Suspended,
/// Emitted when the application has been resumed. /// See [`ApplicationHandler::resumed`] for details.
/// ///
/// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a /// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
/// formal suspend/resume lifecycle. For systems without a standard suspend/resume lifecycle
/// the `Resumed` event is always emitted after the [`NewEvents(StartCause::Init)`][StartCause::Init]
/// event.
///
/// # Portability
///
/// It's recommended that applications should only initialize their graphics context and create
/// a window after they have received their first `Resumed` event. Some systems
/// (specifically Android) won't allow applications to create a render surface until they are
/// resumed.
///
/// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events.
///
/// Also see [`Suspended`] notes.
///
/// ## Android
///
/// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is
/// expected to closely correlate with the [`onResume`] lifecycle event but there may technically
/// be a discrepancy.
///
/// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
///
/// Applications that need to run on Android must wait until they have been `Resumed`
/// before they will be able to create a render surface (such as an `EGLSurface`,
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a
/// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their
/// render surfaces are invalid and should be dropped.
///
/// Also see [`Suspended`] notes.
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`]
/// callback which means the application is "active" (according to the
/// [iOS application lifecycle]).
///
/// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event
/// with the property [`persisted`] being true, which means that the page is being
/// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
/// stores a complete snapshot of a page (including the JavaScript heap) as the
/// user is navigating away.
///
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
///
/// [`Suspended`]: Self::Suspended
Resumed, Resumed,
/// Emitted when the event loop is about to block and wait for new events. /// See [`ApplicationHandler::about_to_wait`] for details.
/// ///
/// Most applications shouldn't need to hook into this event since there is no real relationship /// [`ApplicationHandler::about_to_wait`]: crate::application::ApplicationHandler::about_to_wait
/// between how often the event loop needs to wake up and the dispatching of any specific events.
///
/// High frequency event sources, such as input devices could potentially lead to lots of wake
/// ups and also lots of corresponding `AboutToWait` events.
///
/// This is not an ideal event to drive application rendering from and instead applications
/// should render in response to [`WindowEvent::RedrawRequested`] events.
AboutToWait, AboutToWait,
/// Emitted when the event loop is being shut down. /// See [`ApplicationHandler::exiting`] for details.
/// ///
/// This is irreversible - if this event is emitted, it is guaranteed to be the last event that /// [`ApplicationHandler::exiting`]: crate::application::ApplicationHandler::exiting
/// gets emitted. You generally want to treat this as a "do on quit" event.
LoopExiting, LoopExiting,
/// Emitted when the application has received a memory warning. /// See [`ApplicationHandler::memory_warning`] for details.
/// ///
/// ## Platform-specific /// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning
///
/// ### Android
///
/// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application
/// must [release memory] or risk being killed.
///
/// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory()
/// [release memory]: https://developer.android.com/topic/performance/memory#release
///
/// ### iOS
///
/// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`]
/// callback. The application must free as much memory as possible or risk being terminated, see
/// [how to respond to memory warnings].
///
/// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni
/// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings
///
/// ### Others
///
/// - **macOS / Wayland / Windows / Orbital:** Unsupported.
MemoryWarning, MemoryWarning,
} }
@@ -561,7 +418,7 @@ pub enum WindowEvent {
/// The window has been occluded (completely hidden from view). /// The window has been occluded (completely hidden from view).
/// ///
/// This is different to window visibility as it depends on whether the window is closed, /// This is different to window visibility as it depends on whether the window is closed,
/// minimized, set invisible, or fully occluded by another window. /// minimised, set invisible, or fully occluded by another window.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
@@ -784,7 +641,7 @@ pub struct KeyEvent {
/// ///
/// # 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.
/// ///
/// ``` /// ```
@@ -933,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,
@@ -954,8 +811,8 @@ 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 pending /// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear pending
/// preedit text. /// preedit text.
Disabled, Disabled,

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.
@@ -18,6 +18,7 @@ use std::time::{Duration, Instant};
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time::{Duration, Instant}; use web_time::{Duration, Instant};
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, OsError}; use crate::error::{EventLoopError, OsError};
use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes}; use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
use crate::{event::Event, monitor::MonitorHandle, platform_impl}; use crate::{event::Event, monitor::MonitorHandle, platform_impl};
@@ -80,7 +81,7 @@ impl<T> EventLoopBuilder<T> {
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread, /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
/// and only once per application.*** /// and only once per application.***
/// ///
/// Calling this function will result in display backend initialization. /// Calling this function will result in display backend initialisation.
/// ///
/// ## Panics /// ## Panics
/// ///
@@ -215,8 +216,22 @@ impl<T> EventLoop<T> {
} }
} }
/// Runs the event loop in the calling thread and calls the given `event_handler` closure /// See [`run_app`].
/// to dispatch any pending events. ///
/// [`run_app`]: Self::run_app
#[inline]
#[deprecated = "use `EventLoop::run_app` instead"]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &ActiveEventLoop),
{
let _span = tracing::debug_span!("winit::EventLoop::run").entered();
self.event_loop.run(event_handler)
}
/// Run the application with the event loop on the calling thread.
/// ///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
@@ -231,10 +246,10 @@ impl<T> EventLoop<T> {
/// Web applications are recommended to use /// Web applications are recommended to use
#[cfg_attr( #[cfg_attr(
web_platform, web_platform,
doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]" doc = "[`EventLoopExtWebSys::spawn_app()`][crate::platform::web::EventLoopExtWebSys::spawn_app()]"
)] )]
#[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")] #[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")]
/// [^1] instead of [`run()`] to avoid the need /// [^1] instead of [`run_app()`] to avoid the need
/// for the Javascript exception trick, and to make it clearer that the event loop runs /// for the Javascript exception trick, and to make it clearer that the event loop runs
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the /// asynchronously (via the browser's own, internal, event loop) and doesn't block the
/// current thread of execution like it does on other platforms. /// current thread of execution like it does on other platforms.
@@ -242,17 +257,13 @@ impl<T> EventLoop<T> {
/// This function won't be available with `target_feature = "exception-handling"`. /// This function won't be available with `target_feature = "exception-handling"`.
/// ///
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`run()`]: Self::run() /// [`run_app()`]: Self::run_app()
/// [^1]: `EventLoopExtWebSys::spawn()` is only available on Web. /// [^1]: `EventLoopExtWebSys::spawn_app()` is only available on Web.
#[inline] #[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))] #[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError> pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> {
where self.event_loop
F: FnMut(Event<T>, &ActiveEventLoop), .run(|event, event_loop| dispatch_event_for_app(app, event_loop, event))
{
let _span = tracing::debug_span!("winit::EventLoop::run").entered();
self.event_loop.run(event_handler)
} }
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
@@ -344,11 +355,11 @@ unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
impl<T> AsFd for EventLoop<T> { impl<T> AsFd for EventLoop<T> {
/// Get the underlying [EventLoop]'s `fd` which you can register /// Get the underlying [EventLoop]'s `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_events`] API. /// loop must be polled with the [`pump_app_events`] API.
/// ///
/// [`calloop`]: https://crates.io/crates/calloop /// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio /// [`mio`]: https://crates.io/crates/mio
/// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events /// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events
fn as_fd(&self) -> BorrowedFd<'_> { fn as_fd(&self) -> BorrowedFd<'_> {
self.event_loop.as_fd() self.event_loop.as_fd()
} }
@@ -358,11 +369,11 @@ impl<T> AsFd for EventLoop<T> {
impl<T> AsRawFd for EventLoop<T> { impl<T> AsRawFd for EventLoop<T> {
/// Get the underlying [EventLoop]'s raw `fd` which you can register /// Get the underlying [EventLoop]'s raw `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_events`] API. /// loop must be polled with the [`pump_app_events`] API.
/// ///
/// [`calloop`]: https://crates.io/crates/calloop /// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio /// [`mio`]: https://crates.io/crates/mio
/// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events /// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.event_loop.as_raw_fd() self.event_loop.as_raw_fd()
} }
@@ -457,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();
@@ -466,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()
} }
@@ -630,3 +641,23 @@ impl AsyncRequestSerial {
Self { serial } Self { serial }
} }
} }
/// Shim for various run APIs.
#[inline(always)]
pub(crate) fn dispatch_event_for_app<T: 'static, A: ApplicationHandler<T>>(
app: &mut A,
event_loop: &ActiveEventLoop,
event: Event<T>,
) {
match event {
Event::NewEvents(cause) => app.new_events(event_loop, cause),
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
Event::UserEvent(event) => app.user_event(event_loop, event),
Event::Suspended => app.suspended(event_loop),
Event::Resumed => app.resumed(event_loop),
Event::AboutToWait => app.about_to_wait(event_loop),
Event::LoopExiting => app.exiting(event_loop),
Event::MemoryWarning => app.memory_warning(event_loop),
}
}

View File

@@ -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))]
@@ -1229,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,

View File

@@ -21,7 +21,7 @@
//! 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 desired. //! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if desired.
//! //!
//! You can retrieve events by calling [`EventLoop::run()`]. 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`].
//! //!
@@ -36,7 +36,7 @@
x11_platform, x11_platform,
wayland_platform wayland_platform
), ),
doc = "[`EventLoopExtPumpEvents::pump_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_events()]" doc = "[`EventLoopExtPumpEvents::pump_app_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_app_events()]"
)] )]
#![cfg_attr( #![cfg_attr(
not(any( not(any(
@@ -46,18 +46,54 @@
x11_platform, x11_platform,
wayland_platform wayland_platform
)), )),
doc = "`EventLoopExtPumpEvents::pump_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
//! it's discouraged beyond compatibility reasons. //! it's discouraged beyond compatibility reasons.
//! //!
//! //!
//! ```no_run //! ```no_run
//! use winit::{ //! use winit::application::ApplicationHandler;
//! event::{Event, WindowEvent}, //! use winit::event::WindowEvent;
//! event_loop::{ControlFlow, EventLoop}, //! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
//! window::Window, //! use winit::window::{Window, WindowId};
//! }; //!
//! #[derive(Default)]
//! struct App {
//! window: Option<Window>,
//! }
//!
//! impl ApplicationHandler for App {
//! 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, id: WindowId, event: WindowEvent) {
//! match event {
//! WindowEvent::CloseRequested => {
//! println!("The close button was pressed; stopping");
//! event_loop.exit();
//! },
//! WindowEvent::RedrawRequested => {
//! // Redraw the application.
//! //
//! // It's preferable for applications that do not render continuously to render in
//! // this event rather than in AboutToWait, since rendering in here allows
//! // the program to gracefully handle redraws requested by the OS.
//!
//! // Draw.
//!
//! // Queue a RedrawRequested event.
//! //
//! // You only need to call this if you've determined that you need to redraw in
//! // applications which do not always need to. Applications that redraw continuously
//! // can render here instead.
//! self.window.as_ref().unwrap().request_redraw();
//! }
//! _ => (),
//! }
//! }
//! }
//! //!
//! let event_loop = EventLoop::new().unwrap(); //! let event_loop = EventLoop::new().unwrap();
//! //!
@@ -70,43 +106,8 @@
//! // 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 window = None; //! let mut app = App::default();
//! //! event_loop.run_app(&mut app);
//! event_loop.run(move |event, event_loop| {
//! match event {
//! Event::Resumed => {
//! window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
//! }
//! Event::WindowEvent {
//! event: WindowEvent::CloseRequested,
//! ..
//! } => {
//! println!("The close button was pressed; stopping");
//! event_loop.exit();
//! },
//! Event::AboutToWait => {
//! // Application update code.
//!
//! // Queue a RedrawRequested event.
//! //
//! // You only need to call this if you've determined that you need to redraw in
//! // applications which do not always need to. Applications that redraw continuously
//! // can render here instead.
//! window.as_ref().unwrap().request_redraw();
//! },
//! Event::WindowEvent {
//! event: WindowEvent::RedrawRequested,
//! ..
//! } => {
//! // Redraw the application.
//! //
//! // It's preferable for applications that do not render continuously to render in
//! // this event rather than in AboutToWait, since rendering in here allows
//! // the program to gracefully handle redraws requested by the OS.
//! },
//! _ => ()
//! }
//! });
//! ``` //! ```
//! //!
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be //! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
@@ -123,7 +124,7 @@
//! 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 the //! [`visible` set to `false`][crate::window::WindowAttributes::with_visible] and explicitly make 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
@@ -164,7 +165,7 @@
//! //!
//! [`EventLoop`]: event_loop::EventLoop //! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoop::new()`]: event_loop::EventLoop::new //! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [`EventLoop::run()`]: event_loop::EventLoop::run //! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app
//! [`exit()`]: event_loop::ActiveEventLoop::exit //! [`exit()`]: event_loop::ActiveEventLoop::exit
//! [`Window`]: window::Window //! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId //! [`WindowId`]: window::WindowId
@@ -178,7 +179,7 @@
//! [`Event::LoopExiting`]: event::Event::LoopExiting //! [`Event::LoopExiting`]: event::Event::LoopExiting
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle //! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle //! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
//! [^1]: `EventLoopExtPumpEvents::pump_events()` is only available on Windows, macOS, Android, X11 and Wayland. //! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland.
#![deny(rust_2018_idioms)] #![deny(rust_2018_idioms)]
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
@@ -200,6 +201,9 @@ pub use rwh_06 as raw_window_handle;
#[doc(inline)] #[doc(inline)]
pub use dpi; pub use dpi;
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,8 +3,8 @@
//! 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::{ use crate::{
dpi::{PhysicalPosition, PhysicalSize}, dpi::{PhysicalPosition, PhysicalSize},
platform_impl, platform_impl,

View File

@@ -45,13 +45,13 @@
//! | Base Class | Feature Flag | Notes | //! | Base Class | Feature Flag | Notes |
//! | :--------------: | :---------------: | :-----: | //! | :--------------: | :---------------: | :-----: |
//! | `NativeActivity` | `android-native-activity` | Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass `NativeActivity` to access some platform features. It does not derive from the [`AndroidAppCompat`] base class.| //! | `NativeActivity` | `android-native-activity` | Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass `NativeActivity` to access some platform features. It does not derive from the [`AndroidAppCompat`] base class.|
//! | [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`], a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][android_jet] (or link with an embedded [release][android_releases] of [`GameActivity`]) | //! | [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`], a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
//! //!
//! [`GameActivity`]: https://developer.android.com/games/agdk/game-activity //! [`GameActivity`]: https://developer.android.com/games/agdk/game-activity
//! [`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input //! [`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input
//! [`AndroidAppCompat`]: https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity //! [`AndroidAppCompat`]: https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity
//! [android_jet]: https://developer.android.com/jetpack/androidx/releases/games //! [agdk_jetpack]: https://developer.android.com/jetpack/androidx/releases/games
//! [android_releases]: https://developer.android.com/games/agdk/download#agdk-libraries //! [agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
//! [Gradle]: https://developer.android.com/studio/build //! [Gradle]: https://developer.android.com/studio/build
//! //!
//! For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples). //! For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples).
@@ -60,7 +60,7 @@
//! //!
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be: //! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
//! 1. Remove `ndk-glue` from your `Cargo.toml` //! 1. Remove `ndk-glue` from your `Cargo.toml`
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.13", features = [ "android-native-activity" ] }` //! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.15", 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 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). //! 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).

View File

@@ -1,22 +1,13 @@
use std::time::Duration; use std::time::Duration;
use crate::{ use crate::application::ApplicationHandler;
event::Event, use crate::event::Event;
event_loop::{ActiveEventLoop, EventLoop}, use crate::event_loop::{self, ActiveEventLoop, EventLoop};
};
/// The return status for `pump_events`
pub enum PumpStatus {
/// Continue running external loop.
Continue,
/// Exit external loop.
Exit(i32),
}
/// Additional methods on [`EventLoop`] for pumping events within an external event loop /// Additional methods on [`EventLoop`] for pumping events within an external event loop
pub trait EventLoopExtPumpEvents { pub trait EventLoopExtPumpEvents {
/// A type provided by the user that can be passed through [`Event::UserEvent`]. /// A type provided by the user that can be passed through [`Event::UserEvent`].
type UserEvent; type UserEvent: 'static;
/// Pump the `EventLoop` to check for and dispatch pending events. /// Pump the `EventLoop` to check for and dispatch pending events.
/// ///
@@ -113,6 +104,21 @@ pub trait EventLoopExtPumpEvents {
/// If you render outside of Winit you are likely to see window resizing artifacts /// If you render outside of Winit you are likely to see window resizing artifacts
/// since MacOS expects applications to render synchronously during any `drawRect` /// since MacOS expects applications to render synchronously during any `drawRect`
/// callback. /// callback.
fn pump_app_events<A: ApplicationHandler<Self::UserEvent>>(
&mut self,
timeout: Option<Duration>,
app: &mut A,
) -> PumpStatus {
#[allow(deprecated)]
self.pump_events(timeout, |event, event_loop| {
event_loop::dispatch_event_for_app(app, event_loop, event)
})
}
/// See [`pump_app_events`].
///
/// [`pump_app_events`]: Self::pump_app_events
#[deprecated = "use EventLoopExtPumpEvents::pump_app_events"]
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop); F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
@@ -128,3 +134,11 @@ impl<T> EventLoopExtPumpEvents for EventLoop<T> {
self.event_loop.pump_events(timeout, event_handler) self.event_loop.pump_events(timeout, event_handler)
} }
} }
/// The return status for `pump_events`
pub enum PumpStatus {
/// Continue running external loop.
Continue,
/// Exit external loop.
Exit(i32),
}

View File

@@ -1,8 +1,7 @@
use crate::{ use crate::application::ApplicationHandler;
error::EventLoopError, use crate::error::EventLoopError;
event::Event, use crate::event::Event;
event_loop::{ActiveEventLoop, EventLoop}, use crate::event_loop::{self, ActiveEventLoop, EventLoop};
};
#[cfg(doc)] #[cfg(doc)]
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window}; use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
@@ -10,12 +9,19 @@ use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
/// Additional methods on [`EventLoop`] to return control flow to the caller. /// Additional methods on [`EventLoop`] to return control flow to the caller.
pub trait EventLoopExtRunOnDemand { pub trait EventLoopExtRunOnDemand {
/// A type provided by the user that can be passed through [`Event::UserEvent`]. /// A type provided by the user that can be passed through [`Event::UserEvent`].
type UserEvent; type UserEvent: 'static;
/// Runs the event loop in the calling thread and calls the given `event_handler` closure /// See [`run_app_on_demand`].
/// to dispatch any window system events.
/// ///
/// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures /// [`run_app_on_demand`]: Self::run_app_on_demand
#[deprecated = "use EventLoopExtRunOnDemand::run_app_on_demand"]
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
/// Run the application with the event loop on the calling thread.
///
/// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`) 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.
@@ -26,11 +32,10 @@ pub trait EventLoopExtRunOnDemand {
/// ///
/// This API is not designed to run an event loop in bursts that you can exit from and return /// This API is not designed to run an event loop in bursts that you can exit from and return
/// to while maintaining the full state of your application. (If you need something like this /// to while maintaining the full state of your application. (If you need something like this
/// you can look at the [`EventLoopExtPumpEvents::pump_events()`] API) /// you can look at the [`EventLoopExtPumpEvents::pump_app_events()`] API)
/// ///
/// Each time `run_on_demand` is called the `event_handler` can expect to receive a /// Each time `run_app_on_demand` is called the startup sequence of `init`, followed by
/// `NewEvents(Init)` and `Resumed` event (even on platforms that have no suspend/resume /// `resume` is being preserved.
/// lifecycle) - which can be used to consistently initialize application state.
/// ///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
@@ -40,8 +45,8 @@ pub trait EventLoopExtRunOnDemand {
/// backend it is possible to use `EventLoopExtWebSys::spawn()`[^1] more than once instead). /// backend it is possible to use `EventLoopExtWebSys::spawn()`[^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()`] for portability, unless you specifically need /// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you
/// the ability to re-run a single event loop more than once /// specifically need the ability to re-run a single event loop more than once
/// ///
/// # Supported Platforms /// # Supported Platforms
/// - Windows /// - Windows
@@ -64,9 +69,15 @@ pub trait EventLoopExtRunOnDemand {
/// ///
/// [`exit()`]: ActiveEventLoop::exit() /// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError> fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>(
where &mut self,
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop); app: &mut A,
) -> Result<(), EventLoopError> {
#[allow(deprecated)]
self.run_on_demand(|event, event_loop| {
event_loop::dispatch_event_for_app(app, event_loop, event)
})
}
} }
impl<T> EventLoopExtRunOnDemand for EventLoop<T> { impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
@@ -94,7 +105,7 @@ impl ActiveEventLoop {
/// ///
/// let mut event_loop = EventLoop::new().unwrap(); /// let mut event_loop = EventLoop::new().unwrap();
/// event_loop.run_on_demand(|_, _| { /// event_loop.run_on_demand(|_, _| {
/// // Attempt to run the event loop in a re-entrant manner; this must fail. /// // Attempt to run the event loop re-entrantly; this must fail.
/// event_loop.run_on_demand(|_, _| {}); /// event_loop.run_on_demand(|_, _| {});
/// }); /// });
/// ``` /// ```

View File

@@ -29,11 +29,11 @@ pub trait PhysicalKeyExtScancode {
impl PhysicalKeyExtScancode for PhysicalKey { impl PhysicalKeyExtScancode for PhysicalKey {
fn to_scancode(self) -> Option<u32> { fn to_scancode(self) -> Option<u32> {
crate::platform_impl::physical_key_to_scancode(self) crate::platform_impl::physicalkey_to_scancode(self)
} }
fn from_scancode(scancode: u32) -> PhysicalKey { fn from_scancode(scancode: u32) -> PhysicalKey {
crate::platform_impl::scancode_to_physical_key(scancode) crate::platform_impl::scancode_to_physicalkey(scancode)
} }
} }

View File

@@ -2,7 +2,7 @@
//! //!
//! The [`ActivationToken`] is essential to ensure that your newly //! The [`ActivationToken`] is essential to ensure that your newly
//! created window will obtain the focus, otherwise the user could //! created window will obtain the focus, otherwise the user could
//! be required to click on the window. //! be requered to click on the window.
//! //!
//! Such token is usually delivered via the environment variable and //! Such token is usually delivered via the environment variable and
//! could be read from it with the [`EventLoopExtStartupNotify::read_token_from_env`]. //! could be read from it with the [`EventLoopExtStartupNotify::read_token_from_env`].

View File

@@ -53,9 +53,10 @@ use std::time::Duration;
#[cfg(web_platform)] #[cfg(web_platform)]
use web_sys::HtmlCanvasElement; use web_sys::HtmlCanvasElement;
use crate::application::ApplicationHandler;
use crate::cursor::CustomCursorSource; use crate::cursor::CustomCursorSource;
use crate::event::Event; use crate::event::Event;
use crate::event_loop::{ActiveEventLoop, EventLoop}; use crate::event_loop::{self, ActiveEventLoop, EventLoop};
#[cfg(web_platform)] #[cfg(web_platform)]
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture; use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
use crate::platform_impl::PlatformCustomCursorSource; use crate::platform_impl::PlatformCustomCursorSource;
@@ -160,18 +161,18 @@ impl WindowAttributesExtWebSys for WindowAttributes {
/// Additional methods on `EventLoop` that are specific to the web. /// Additional methods on `EventLoop` that are specific to the web.
pub trait EventLoopExtWebSys { pub trait EventLoopExtWebSys {
/// A type provided by the user that can be passed through `Event::UserEvent`. /// A type provided by the user that can be passed through `Event::UserEvent`.
type UserEvent; type UserEvent: 'static;
/// Initializes the winit event loop. /// Initializes the winit event loop.
/// ///
/// Unlike /// Unlike
#[cfg_attr( #[cfg_attr(
all(web_platform, target_feature = "exception-handling"), all(web_platform, target_feature = "exception-handling"),
doc = "`run()`" 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()`]" doc = "[`run_app()`]"
)] )]
/// [^1], this returns immediately, and doesn't throw an exception in order to /// [^1], this returns immediately, and doesn't throw an exception in order to
/// satisfy its [`!`] return type. /// satisfy its [`!`] return type.
@@ -183,9 +184,15 @@ pub trait EventLoopExtWebSys {
/// ///
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run()`]: EventLoop::run()" doc = "[`run_app()`]: EventLoop::run_app()"
)] )]
/// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`. /// [^1]: `run_app()` is _not_ available on WASM when the target supports `exception-handling`.
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, app: A);
/// See [`spawn_app`].
///
/// [`spawn_app`]: Self::spawn_app
#[deprecated = "use EventLoopExtWebSys::spawn_app"]
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);
@@ -194,6 +201,12 @@ pub trait EventLoopExtWebSys {
impl<T> EventLoopExtWebSys for EventLoop<T> { impl<T> EventLoopExtWebSys for EventLoop<T> {
type UserEvent = T; type UserEvent = T;
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, mut app: A) {
self.event_loop.spawn(move |event, event_loop| {
event_loop::dispatch_event_for_app(&mut app, event_loop, event)
});
}
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop), F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
@@ -239,7 +252,7 @@ impl ActiveEventLoopExtWebSys for ActiveEventLoop {
} }
} }
/// 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
@@ -330,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"),
} }
} }
} }

View File

@@ -157,11 +157,11 @@ pub trait EventLoopBuilderExtWindows {
/// #[cfg(target_os = "windows")] /// #[cfg(target_os = "windows")]
/// builder.with_msg_hook(|msg|{ /// builder.with_msg_hook(|msg|{
/// let msg = msg as *const MSG; /// let msg = msg as *const MSG;
/// # let accelerators: Vec<ACCEL> = Vec::new(); /// # let accels: Vec<ACCEL> = Vec::new();
/// let translated = unsafe { /// let translated = unsafe {
/// TranslateAcceleratorW( /// TranslateAcceleratorW(
/// (*msg).hwnd, /// (*msg).hwnd,
/// CreateAcceleratorTableW(accelerators.as_ptr() as _, 1), /// CreateAcceleratorTableW(accels.as_ptr() as _, 1),
/// msg, /// msg,
/// ) == 1 /// ) == 1
/// }; /// };
@@ -305,7 +305,7 @@ impl WindowExtWindows for Window {
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 [`WindowExtWindows::set_enable(false)`](WindowExtWindows::set_enable) /// Can be used in combination with [`WindowExtWindows::set_enable(false)`][WindowExtWindows::set_enable]
/// on the owner window to create a modal dialog box. /// on the owner window to create a modal dialog box.
/// ///
/// From MSDN: /// From MSDN:

View File

@@ -16,14 +16,14 @@ use crate::dpi::Size;
#[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 dimensions as the /// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
/// screen, allowing the desktop environment to have full control of the desktop, without the need for proxy-ing /// screen, allowing the desktop environment to have full control of the 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 other windows. /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
Dock, Dock,
/// Toolbar windows. "Torn off" from the main application. /// Toolbar windows. "Torn off" from the main application.
Toolbar, Toolbar,
/// Pin-able menu windows. "Torn off" from the main application. /// Pinnable menu windows. "Torn off" from the main application.
Menu, Menu,
/// A small persistent utility window, such as a palette or toolbox. /// A small persistent utility window, such as a palette or toolbox.
Utility, Utility,

View File

@@ -211,7 +211,7 @@ impl<T: 'static> EventLoop<T> {
where where
F: FnMut(event::Event<T>, &RootAEL), F: FnMut(event::Event<T>, &RootAEL),
{ {
trace!("Main loop iteration"); trace!("Mainloop iteration");
let cause = self.cause; let cause = self.cause;
let mut pending_redraw = self.pending_redraw; let mut pending_redraw = self.pending_redraw;

View File

@@ -182,7 +182,7 @@ impl<T: 'static> EventLoop<T> {
application.is_none(), application.is_none(),
"\ "\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\ `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS", Note: `EventLoop::run_app` calls `UIApplicationMain` on iOS",
); );
let handler = map_user_event(handler, self.receiver); let handler = map_user_event(handler, self.receiver);

View File

@@ -689,7 +689,7 @@ impl Inner {
let screen_frame = self.rect_to_screen_space(bounds); let screen_frame = self.rect_to_screen_space(bounds);
let status_bar_frame = { let status_bar_frame = {
let app = UIApplication::shared(MainThreadMarker::new().unwrap()).expect( let app = UIApplication::shared(MainThreadMarker::new().unwrap()).expect(
"`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS", "`Window::get_inner_position` cannot be called before `EventLoop::run_app` on iOS",
); );
app.statusBarFrame() app.statusBarFrame()
}; };

View File

@@ -24,13 +24,13 @@ use crate::platform_impl::common::xkb::{XkbContext, XKBH};
/// ///
/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses. /// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses.
pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey { pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey {
scancode_to_physical_key(keycode.saturating_sub(8)) scancode_to_physicalkey(keycode.saturating_sub(8))
} }
/// Map the linux scancode to Keycode. /// Map the linux scancode to Keycode.
/// ///
/// Both X11 and Wayland use keys with `+ 8` offset to linux scancode. /// Both X11 and Wayland use keys with `+ 8` offset to linux scancode.
pub fn scancode_to_physical_key(scancode: u32) -> PhysicalKey { pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as // The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as
// libxkbcommon's documentation seems to suggest that the keycode values we're interested in // libxkbcommon's documentation seems to suggest that the keycode values we're interested in
// are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes, // are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes,
@@ -287,7 +287,7 @@ pub fn scancode_to_physical_key(scancode: u32) -> PhysicalKey {
}) })
} }
pub fn physical_key_to_scancode(key: PhysicalKey) -> Option<u32> { pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option<u32> {
let code = match key { let code = match key {
PhysicalKey::Code(code) => code, PhysicalKey::Code(code) => code,
PhysicalKey::Unidentified(code) => { PhysicalKey::Unidentified(code) => {

View File

@@ -29,7 +29,7 @@ use keymap::XkbKeymap;
#[cfg(x11_platform)] #[cfg(x11_platform)]
pub use keymap::raw_keycode_to_physicalkey; pub use keymap::raw_keycode_to_physicalkey;
pub use keymap::{physical_key_to_scancode, scancode_to_physical_key}; pub use keymap::{physicalkey_to_scancode, scancode_to_physicalkey};
pub use state::XkbState; pub use state::XkbState;
// TODO: Wire this up without using a static `AtomicBool`. // TODO: Wire this up without using a static `AtomicBool`.
@@ -373,10 +373,15 @@ impl<'a, 'b> KeyEventResults<'a, 'b> {
fn composed_text(&mut self) -> Result<Option<SmolStr>, ()> { fn composed_text(&mut self) -> Result<Option<SmolStr>, ()> {
match self.compose { match self.compose {
ComposeStatus::Accepted(xkb_compose_status::XKB_COMPOSE_COMPOSED) => { ComposeStatus::Accepted(status) => match status {
let state = self.context.compose_state1.as_mut().unwrap(); xkb_compose_status::XKB_COMPOSE_COMPOSED => {
Ok(state.get_string(self.context.scratch_buffer)) let state = self.context.compose_state1.as_mut().unwrap();
} Ok(state.get_string(self.context.scratch_buffer))
}
xkb_compose_status::XKB_COMPOSE_COMPOSING
| xkb_compose_status::XKB_COMPOSE_CANCELLED => Ok(None),
xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()),
},
_ => Err(()), _ => Err(()),
} }
} }

View File

@@ -34,7 +34,7 @@ use crate::{
}, },
}; };
pub(crate) use self::common::xkb::{physical_key_to_scancode, scancode_to_physical_key}; pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
pub(crate) use crate::icon::RgbaIcon as PlatformIcon; pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; pub(crate) use crate::platform_impl::Fullscreen;

View File

@@ -94,7 +94,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
WlKeyboardEvent::Leave { surface, .. } => { WlKeyboardEvent::Leave { surface, .. } => {
let window_id = wayland::make_wid(&surface); let window_id = wayland::make_wid(&surface);
// NOTE: we should drop the repeat regardless whether it was for the present // NOTE: we should drop the repeat regardless whethere it was for the present
// window of for the window which just went gone. // window of for the window which just went gone.
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap(); let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
keyboard_state.current_repeat = None; keyboard_state.current_repeat = None;

View File

@@ -207,7 +207,7 @@ impl PointerHandler for WinitState {
pointer_data.phase = phase; pointer_data.phase = phase;
// Mice events have both pixel and discrete delta's at the same time. So prefer // Mice events have both pixel and discrete delta's at the same time. So prefer
// the discrete values if they are present. // the descrite values if they are present.
let delta = if has_discrete_scroll { let delta = if has_discrete_scroll {
// XXX Wayland sign convention is the inverse of winit. // XXX Wayland sign convention is the inverse of winit.
MouseScrollDelta::LineDelta( MouseScrollDelta::LineDelta(

View File

@@ -826,9 +826,14 @@ impl WindowState {
/// Set the cursor grabbing state on the top-level. /// Set the cursor grabbing state on the top-level.
pub fn set_cursor_grab(&mut self, mode: CursorGrabMode) -> Result<(), ExternalError> { pub fn set_cursor_grab(&mut self, mode: CursorGrabMode) -> Result<(), ExternalError> {
// Replace the user grabbing mode. if self.cursor_grab_mode.user_grab_mode == mode {
return Ok(());
}
self.set_cursor_grab_inner(mode)?;
// Update user grab on success.
self.cursor_grab_mode.user_grab_mode = mode; self.cursor_grab_mode.user_grab_mode = mode;
self.set_cursor_grab_inner(mode) Ok(())
} }
/// Reload the hints for minimum and maximum sizes. /// Reload the hints for minimum and maximum sizes.

View File

@@ -987,7 +987,7 @@ impl EventProcessor {
if keycode != 0 && !self.is_composing { if keycode != 0 && !self.is_composing {
// Don't alter the modifiers state from replaying. // Don't alter the modifiers state from replaying.
if replay { if replay {
self.send_synthetic_modifier_from_core(window_id, xev.state as u16, &mut callback); self.send_synthic_modifier_from_core(window_id, xev.state as u16, &mut callback);
} }
if let Some(mut key_processor) = self.xkb_context.key_context() { if let Some(mut key_processor) = self.xkb_context.key_context() {
@@ -1037,7 +1037,7 @@ impl EventProcessor {
} }
} }
fn send_synthetic_modifier_from_core<T: 'static, F>( fn send_synthic_modifier_from_core<T: 'static, F>(
&mut self, &mut self,
window_id: crate::window::WindowId, window_id: crate::window::WindowId,
state: u16, state: u16,

View File

@@ -166,7 +166,7 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
pub unsafe extern "C" fn xim_instantiate_callback( pub unsafe extern "C" fn xim_instantiate_callback(
_display: *mut ffi::Display, _display: *mut ffi::Display,
client_data: ffi::XPointer, client_data: ffi::XPointer,
// This field is un-supplied. // This field is unsupplied.
_call_data: ffi::XPointer, _call_data: ffi::XPointer,
) { ) {
let inner: *mut ImeInner = client_data as _; let inner: *mut ImeInner = client_data as _;
@@ -193,7 +193,7 @@ pub unsafe extern "C" fn xim_instantiate_callback(
pub unsafe extern "C" fn xim_destroy_callback( pub unsafe extern "C" fn xim_destroy_callback(
_xim: ffi::XIM, _xim: ffi::XIM,
client_data: ffi::XPointer, client_data: ffi::XPointer,
// This field is un-supplied. // This field is unsupplied.
_call_data: ffi::XPointer, _call_data: ffi::XPointer,
) { ) {
let inner: *mut ImeInner = client_data as _; let inner: *mut ImeInner = client_data as _;

View File

@@ -87,7 +87,7 @@ extern "C" fn preedit_draw_callback(
call_data.chg_first as usize..(call_data.chg_first + call_data.chg_length) as usize; call_data.chg_first as usize..(call_data.chg_first + call_data.chg_length) as usize;
if chg_range.start > client_data.text.len() || chg_range.end > client_data.text.len() { if chg_range.start > client_data.text.len() || chg_range.end > client_data.text.len() {
tracing::warn!( tracing::warn!(
"invalid chg range: buffer length={}, but chg_first={} chg_length={}", "invalid chg range: buffer length={}, but chg_first={} chg_lengthg={}",
client_data.text.len(), client_data.text.len(),
call_data.chg_first, call_data.chg_first,
call_data.chg_length call_data.chg_length

View File

@@ -170,7 +170,7 @@ impl From<util::GetPropertyError> for GetXimServersError {
} }
// The root window has a property named XIM_SERVERS, which contains a list of atoms representing // The root window has a property named XIM_SERVERS, which contains a list of atoms representing
// the available XIM servers. For instance, if you're using ibus, it would contain an atom named // the availabile XIM servers. For instance, if you're using ibus, it would contain an atom named
// "@server=ibus". It's possible for this property to contain multiple atoms, though presumably // "@server=ibus". It's possible for this property to contain multiple atoms, though presumably
// rare. Note that we replace "@server=" with "@im=" in order to match the format of locale // rare. Note that we replace "@server=" with "@im=" in order to match the format of locale
// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set // modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set

View File

@@ -887,6 +887,9 @@ pub enum X11Error {
/// Unable to parse xsettings. /// Unable to parse xsettings.
XsettingsParse(xsettings::ParserError), XsettingsParse(xsettings::ParserError),
/// Failed to get property.
GetProperty(util::GetPropertyError),
} }
impl fmt::Display for X11Error { impl fmt::Display for X11Error {
@@ -896,6 +899,7 @@ impl fmt::Display for X11Error {
X11Error::Connect(e) => write!(f, "X11 connection error: {}", e), X11Error::Connect(e) => write!(f, "X11 connection error: {}", e),
X11Error::Connection(e) => write!(f, "X11 connection error: {}", e), X11Error::Connection(e) => write!(f, "X11 connection error: {}", e),
X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {}", e), X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {}", e),
X11Error::GetProperty(e) => write!(f, "Failed to get X property {}", e),
X11Error::X11(e) => write!(f, "X11 error: {:?}", e), X11Error::X11(e) => write!(f, "X11 error: {:?}", e),
X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {}", s), X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {}", s),
X11Error::InvalidActivationToken(s) => write!( X11Error::InvalidActivationToken(s) => write!(
@@ -988,6 +992,12 @@ impl From<xsettings::ParserError> for X11Error {
} }
} }
impl From<util::GetPropertyError> for X11Error {
fn from(value: util::GetPropertyError) -> Self {
Self::GetProperty(value)
}
}
/// Type alias for a void cookie. /// Type alias for a void cookie.
type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>; type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>;

View File

@@ -44,7 +44,7 @@ impl AaRect {
pub struct Geometry { pub struct Geometry {
pub root: xproto::Window, pub root: xproto::Window,
// If you want positions relative to the root window, use translate_coords. // If you want positions relative to the root window, use translate_coords.
// Note that the overwhelming majority of window managers are re-parenting WMs, thus the window // Note that the overwhelming majority of window managers are reparenting WMs, thus the window
// ID we get from window creation is for a nested window used as the window's client area. If // ID we get from window creation is for a nested window used as the window's client area. If
// you call get_geometry with that window ID, then you'll get the position of that client area // you call get_geometry with that window ID, then you'll get the position of that client area
// window relative to the parent it's nested in (the frame), which isn't helpful if you want // window relative to the parent it's nested in (the frame), which isn't helpful if you want

View File

@@ -48,7 +48,7 @@ where
} }
impl XConnection { impl XConnection {
// This is important, so pay attention! // This is impoartant, so pay attention!
// Xlib has an output buffer, and tries to hide the async nature of X from you. // Xlib has an output buffer, and tries to hide the async nature of X from you.
// This buffer contains the requests you make, and is flushed under various circumstances: // This buffer contains the requests you make, and is flushed under various circumstances:
// 1. `XPending`, `XNextEvent`, and `XWindowEvent` flush "as needed" // 1. `XPending`, `XNextEvent`, and `XWindowEvent` flush "as needed"

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