mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
1 Commits
it
...
request-ig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac0f918500 |
@@ -1,9 +1,2 @@
|
|||||||
[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"]
|
|
||||||
|
|||||||
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
@@ -32,6 +32,3 @@
|
|||||||
# 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
4
.github/ISSUE_TEMPLATE/blank_issue.md
vendored
@@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
name: Blank Issue
|
|
||||||
about: Create a blank issue.
|
|
||||||
---
|
|
||||||
36
.github/ISSUE_TEMPLATE/bug_android.yml
vendored
36
.github/ISSUE_TEMPLATE/bug_android.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: Android bug
|
|
||||||
description: Create an Android-specific bug report
|
|
||||||
labels:
|
|
||||||
- B - bug
|
|
||||||
- DS - android
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for taking the time to fill out this bug report!
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Description of the problem you're having
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: device
|
|
||||||
attributes:
|
|
||||||
label: Device and Android version
|
|
||||||
description: Which devices and Android versions are you seeing the problem on?
|
|
||||||
placeholder: |
|
|
||||||
Samsung Galaxy Z running Android Pie (API level 28),
|
|
||||||
Samsung Galaxy Z running Android 14 (API level 34),
|
|
||||||
Pixel 8 running Android 14 (API level 34)
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: winit-version
|
|
||||||
attributes:
|
|
||||||
label: Winit version
|
|
||||||
description: What version of Winit are you using?
|
|
||||||
placeholder: 0.29.11
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
37
.github/ISSUE_TEMPLATE/bug_ios.yml
vendored
37
.github/ISSUE_TEMPLATE/bug_ios.yml
vendored
@@ -1,37 +0,0 @@
|
|||||||
name: iOS bug
|
|
||||||
description: Create an iOS-specific bug report
|
|
||||||
labels:
|
|
||||||
- B - bug
|
|
||||||
- DS - ios
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for taking the time to fill out this bug report!
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Description of the problem you're having
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: device
|
|
||||||
attributes:
|
|
||||||
label: Device and iOS version
|
|
||||||
description: Which devices and iOS versions are you seeing the problem on?
|
|
||||||
placeholder: |
|
|
||||||
iPhone 15 running iOS 14.0,
|
|
||||||
iPhone 15 running iOS 17.0,
|
|
||||||
MacBook Pro M2 Mac Catalyst running macOS 14.2,
|
|
||||||
iPhone simulator running iOS 17.0
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: winit-version
|
|
||||||
attributes:
|
|
||||||
label: Winit version
|
|
||||||
description: What version of Winit are you using?
|
|
||||||
placeholder: 0.29.11
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
37
.github/ISSUE_TEMPLATE/bug_macos.yml
vendored
37
.github/ISSUE_TEMPLATE/bug_macos.yml
vendored
@@ -1,37 +0,0 @@
|
|||||||
name: MacOS bug
|
|
||||||
description: Create a macOS-specific bug report
|
|
||||||
labels:
|
|
||||||
- B - bug
|
|
||||||
- DS - macos
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for taking the time to fill out this bug report!
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Description of the problem you're having
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: os-version
|
|
||||||
attributes:
|
|
||||||
label: macOS version
|
|
||||||
description: What version of macOS are you using? Please paste in the output of `sw_vers`.
|
|
||||||
placeholder: |
|
|
||||||
ProductName: macOS
|
|
||||||
ProductVersion: 14.2.1
|
|
||||||
BuildVersion: 23C71
|
|
||||||
render: shell
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: winit-version
|
|
||||||
attributes:
|
|
||||||
label: Winit version
|
|
||||||
description: What version of Winit are you using?
|
|
||||||
placeholder: 0.29.11
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
41
.github/ISSUE_TEMPLATE/bug_wayland.yml
vendored
41
.github/ISSUE_TEMPLATE/bug_wayland.yml
vendored
@@ -1,41 +0,0 @@
|
|||||||
name: Wayland bug
|
|
||||||
description: Create a Wayland-specific bug report
|
|
||||||
labels:
|
|
||||||
- B - bug
|
|
||||||
- DS - wayland
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for taking the time to fill out this bug report!
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Description of the problem you're having
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: debug
|
|
||||||
attributes:
|
|
||||||
label: Debugging output
|
|
||||||
description: Output of a binary run with `WAYLAND_DEBUG=1`
|
|
||||||
placeholder: |
|
|
||||||
[1234.5678] -> wl_display@1.get_registry(new id wl_registry@2)
|
|
||||||
[1234.5678] -> wl_display@1.sync(new id wl_callback@3)
|
|
||||||
...
|
|
||||||
render: shell
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Window isn't shown unless you draw
|
|
||||||
options:
|
|
||||||
- label: I understand that windows aren't shown on Wayland unless I draw and present to them.
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: winit-version
|
|
||||||
attributes:
|
|
||||||
label: Winit version
|
|
||||||
description: What version of Winit are you using?
|
|
||||||
placeholder: 0.29.11
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
51
.github/ISSUE_TEMPLATE/bug_web.yml
vendored
51
.github/ISSUE_TEMPLATE/bug_web.yml
vendored
@@ -1,51 +0,0 @@
|
|||||||
name: Web bug
|
|
||||||
description: Create a web-specific bug report
|
|
||||||
labels:
|
|
||||||
- B - bug
|
|
||||||
- DS - web
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for taking the time to fill out this bug report!
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Description of the problem you're having
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
id: browsers
|
|
||||||
attributes:
|
|
||||||
label: Tested browsers
|
|
||||||
description: What browsers are you seeing the problem on?
|
|
||||||
options:
|
|
||||||
- Firefox
|
|
||||||
- Chrome
|
|
||||||
- Microsoft Edge
|
|
||||||
- Safari 13
|
|
||||||
- Safari 14
|
|
||||||
- Safari 15
|
|
||||||
- Safari 16
|
|
||||||
- Safari 17
|
|
||||||
- Safari (newer than listed)
|
|
||||||
multiple: true
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: device
|
|
||||||
attributes:
|
|
||||||
label: Tested devices
|
|
||||||
description: Which device(s) are you using?
|
|
||||||
placeholder: 'iPhone 15, Lenovo ThinkPad X1, MacBook Pro M2, Samsung Galaxy Z, ...'
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: winit-version
|
|
||||||
attributes:
|
|
||||||
label: Winit version
|
|
||||||
description: What version of Winit are you using?
|
|
||||||
placeholder: 0.29.11
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
35
.github/ISSUE_TEMPLATE/bug_windows.yml
vendored
35
.github/ISSUE_TEMPLATE/bug_windows.yml
vendored
@@ -1,35 +0,0 @@
|
|||||||
name: Windows bug
|
|
||||||
description: Create a Windows-specific bug report
|
|
||||||
labels:
|
|
||||||
- B - bug
|
|
||||||
- DS - windows
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for taking the time to fill out this bug report!
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Description of the problem you're having
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: os-version
|
|
||||||
attributes:
|
|
||||||
label: Windows version
|
|
||||||
description: What version of Windows are you using? Please paste in the output of the `ver` command.
|
|
||||||
placeholder: |
|
|
||||||
Microsoft Windows [Version 10.0.19042.2251]
|
|
||||||
render: shell
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: winit-version
|
|
||||||
attributes:
|
|
||||||
label: Winit version
|
|
||||||
description: What version of Winit are you using?
|
|
||||||
placeholder: 0.29.11
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
32
.github/ISSUE_TEMPLATE/bug_x11.yml
vendored
32
.github/ISSUE_TEMPLATE/bug_x11.yml
vendored
@@ -1,32 +0,0 @@
|
|||||||
name: X11 bug
|
|
||||||
description: Create a X11-specific bug report
|
|
||||||
labels:
|
|
||||||
- B - bug
|
|
||||||
- DS - x11
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for taking the time to fill out this bug report!
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Description of the problem you're having
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: os-info
|
|
||||||
attributes:
|
|
||||||
label: OS and window mananger
|
|
||||||
description: Which operating system and window manager are you using?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: winit-version
|
|
||||||
attributes:
|
|
||||||
label: Winit version
|
|
||||||
description: What version of Winit are you using?
|
|
||||||
placeholder: 0.29.11
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
|||||||
blank_issues_enabled: true
|
|
||||||
contact_links:
|
|
||||||
- name: Question
|
|
||||||
url: https://matrix.to/#/#rust-windowing:matrix.org
|
|
||||||
about: Please ask questions on the Matrix channel.
|
|
||||||
26
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
26
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,26 +0,0 @@
|
|||||||
name: Feature request
|
|
||||||
description: Propose a new feature
|
|
||||||
labels:
|
|
||||||
- S - enhancement
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Description of the problem does this solve or what need does it fill? Please be mindful of the [scope](https://github.com/rust-windowing/winit/blob/master/FEATURES.md) of Winit.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
id: platforms
|
|
||||||
attributes:
|
|
||||||
label: Relevant platforms
|
|
||||||
description: On which platforms is this feature relevant?
|
|
||||||
options:
|
|
||||||
- Windows
|
|
||||||
- macOS
|
|
||||||
- Wayland
|
|
||||||
- X11
|
|
||||||
- Web
|
|
||||||
- iOS
|
|
||||||
- Android
|
|
||||||
multiple: true
|
|
||||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,5 +1,5 @@
|
|||||||
- [ ] Tested on all platforms changed
|
- [ ] Tested on all platforms changed
|
||||||
- [ ] Added an entry to the `changelog` module if knowledge of this change could be valuable to users
|
- [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users
|
||||||
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
|
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
|
||||||
- [ ] Created or updated an example program if it would help users understand this functionality
|
- [ ] Created or updated an example program if it would help users understand this functionality
|
||||||
- [ ] Updated [feature matrix](https://github.com/rust-windowing/winit/blob/master/FEATURES.md), if new features were added or implemented
|
- [ ] Updated [feature matrix](https://github.com/rust-windowing/winit/blob/master/FEATURES.md), if new features were added or implemented
|
||||||
|
|||||||
64
.github/workflows/ci.yml
vendored
64
.github/workflows/ci.yml
vendored
@@ -17,24 +17,6 @@ jobs:
|
|||||||
- name: Check Formatting
|
- name: Check Formatting
|
||||||
run: cargo fmt -- --check
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
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 }}
|
||||||
@@ -107,7 +89,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate lockfile
|
- name: Generate lockfile
|
||||||
# Also updates the crates.io index
|
# Also updates the crates.io index
|
||||||
run: cargo generate-lockfile && cargo update -p ahash --precise 0.8.7 && cargo update -p bumpalo --precise 3.14.0
|
run: cargo generate-lockfile
|
||||||
|
|
||||||
- name: Install GCC Multilib
|
- name: Install GCC Multilib
|
||||||
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
||||||
@@ -145,13 +127,6 @@ 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') &&
|
||||||
@@ -190,7 +165,7 @@ jobs:
|
|||||||
if: matrix.toolchain == 'nightly'
|
if: matrix.toolchain == 'nightly'
|
||||||
run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
|
run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
|
||||||
env:
|
env:
|
||||||
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs --cfg=unreleased_changelogs'
|
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs'
|
||||||
|
|
||||||
# See restore step above
|
# See restore step above
|
||||||
- name: Save cache of cargo folder
|
- name: Save cache of cargo folder
|
||||||
@@ -202,41 +177,6 @@ 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
|
||||||
|
|||||||
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@@ -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 --cfg=unreleased_changelogs
|
RUSTDOCFLAGS: --crate-version master --cfg=docsrs
|
||||||
run: |
|
run: |
|
||||||
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
|
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
|
||||||
|
|
||||||
|
|||||||
1256
CHANGELOG.md
1256
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -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 current changelog if the introduced change is relevant to winit users.
|
- your PR adds an entry to the changelog file 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,35 +38,34 @@ 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 the
|
Once your PR is deemed ready, the merging maintainer will take care of resolving conflicts in
|
||||||
`changelog` module (but you must resolve other conflicts yourself). Doing this requires that you check the
|
`CHANGELOG.md` (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 & Testers
|
||||||
|
|
||||||
The current maintainers for each platform are listed in the [CODEOWNERS](.github/CODEOWNERS) file.
|
The current maintainers are listed in the [CODEOWNERS](.github/CODEOWNERS) file.
|
||||||
|
|
||||||
|
If you are interested in being pinged when testing is needed for a specific platform, please add yourself to the [Testers and Contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors) table!
|
||||||
|
|
||||||
## Release process
|
## Release process
|
||||||
|
|
||||||
Given that winit is a widely used library, we should be able to make a patch
|
Given that winit is a widely used library, we should be able to make a patch
|
||||||
releases at any time we want without blocking the development of new features.
|
releases at any time we want without blocking the development of new features.
|
||||||
|
|
||||||
To achieve these goals, a new branch is created for every new release. Releases
|
To achieve these goals, a new branch is created for every new release. Releases and later patch releases are committed and tagged in this branch.
|
||||||
and later patch releases are committed and tagged in this branch.
|
|
||||||
|
|
||||||
The exact steps for an exemplary `0.2.0` release might look like this:
|
The exact steps for an exemplary `0.2.0` release might look like this:
|
||||||
1. Initially, the version on the latest master is `0.1.0`
|
1. Initially, the version on the latest master is `0.1.0`
|
||||||
2. A new `v0.2.x` branch is created for the release
|
2. A new `v0.2.x` branch is created for the release
|
||||||
3. Update released `cfg_attr` in `src/changelog/mod.rs` to `v0.2.md`
|
3. In the branch, the version is bumped to `v0.2.0`
|
||||||
4. Move entries from `src/changelog/unreleased.md` into `src/changelog/v0.2.md`
|
4. The new commit in the branch is tagged `v0.2.0`
|
||||||
5. In the branch, the version is bumped to `v0.2.0`
|
5. The version is pushed to crates.io
|
||||||
6. The new commit in the branch is tagged `v0.2.0`
|
6. A GitHub release is created for the `v0.2.0` tag
|
||||||
7. The version is pushed to crates.io
|
7. On master, the version is bumped to `0.2.0`, and the CHANGELOG is updated
|
||||||
8. A GitHub release is created for the `v0.2.0` tag
|
|
||||||
9. On master, the version is bumped to `0.2.0`, and the changelog is updated
|
|
||||||
|
|
||||||
When doing a patch release, the process is similar:
|
When doing a patch release, the process is similar:
|
||||||
1. Initially, the version of the latest release is `0.2.0`
|
1. Initially, the version of the latest release is `0.2.0`
|
||||||
2. Checkout the `v0.2.x` branch
|
2. Checkout the `v0.2.x` branch
|
||||||
3. Cherry-pick the required non-breaking changes into the `v0.2.x`
|
3. Cherry-pick the required non-breaking changes into the `v0.2.x`
|
||||||
4. Follow steps 4-9 of the regular release example
|
4. Follow steps 3-7 of the regular release example
|
||||||
|
|||||||
54
Cargo.toml
54
Cargo.toml
@@ -1,16 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "winit"
|
name = "winit"
|
||||||
version = "0.29.15"
|
version = "0.29.10"
|
||||||
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."
|
||||||
|
edition = "2021"
|
||||||
keywords = ["windowing"]
|
keywords = ["windowing"]
|
||||||
|
license = "Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/rust-windowing/winit"
|
||||||
documentation = "https://docs.rs/winit"
|
documentation = "https://docs.rs/winit"
|
||||||
categories = ["gui"]
|
categories = ["gui"]
|
||||||
rust-version.workspace = true
|
rust-version = "1.70.0"
|
||||||
repository.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = [
|
features = [
|
||||||
@@ -22,6 +22,7 @@ features = [
|
|||||||
# Enabled to get docs to compile
|
# Enabled to get docs to compile
|
||||||
"android-native-activity",
|
"android-native-activity",
|
||||||
]
|
]
|
||||||
|
default-target = "x86_64-unknown-linux-gnu"
|
||||||
# These are all tested in CI
|
# These are all tested in CI
|
||||||
targets = [
|
targets = [
|
||||||
# Windows
|
# Windows
|
||||||
@@ -41,7 +42,6 @@ targets = [
|
|||||||
]
|
]
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
# Features are documented in either `lib.rs` or under `winit::platform`.
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||||
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
|
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
|
||||||
@@ -52,8 +52,7 @@ wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
|
|||||||
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
|
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
|
||||||
android-native-activity = ["android-activity/native-activity"]
|
android-native-activity = ["android-activity/native-activity"]
|
||||||
android-game-activity = ["android-activity/game-activity"]
|
android-game-activity = ["android-activity/game-activity"]
|
||||||
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde"]
|
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"]
|
||||||
mint = ["dpi/mint"]
|
|
||||||
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
|
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
|
||||||
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
|
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
|
||||||
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
||||||
@@ -64,18 +63,18 @@ 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" }
|
log = "0.4"
|
||||||
|
mint = { version = "0.5.6", optional = true }
|
||||||
|
once_cell = "1.12"
|
||||||
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
|
rwh_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 = { version = "1", optional = true, features = ["serde_derive"] }
|
||||||
smol_str = "0.2.0"
|
smol_str = "0.2.0"
|
||||||
tracing = { version = "0.1.40", default_features = false }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
image = { version = "0.24.0", default-features = false, features = ["png"] }
|
image = { version = "0.24.0", default-features = false, features = ["png"] }
|
||||||
tracing = { version = "0.1.40", default_features = false, features = ["log"] }
|
simple_logger = { version = "4.2.0", default_features = false }
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
|
||||||
winit = { path = ".", features = ["rwh_05"] }
|
winit = { path = ".", features = ["rwh_05"] }
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
|
||||||
@@ -188,9 +187,9 @@ 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.19.1", optional = true }
|
x11-dl = { version = "2.18.5", optional = true }
|
||||||
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
|
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.0"
|
||||||
|
|
||||||
[target.'cfg(target_os = "redox")'.dependencies]
|
[target.'cfg(target_os = "redox")'.dependencies]
|
||||||
orbclient = { version = "0.3.47", default-features = false }
|
orbclient = { version = "0.3.47", default-features = false }
|
||||||
@@ -256,30 +255,11 @@ concurrent-queue = { version = "2", default-features = false }
|
|||||||
console_log = "1"
|
console_log = "1"
|
||||||
web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
|
web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
|
||||||
|
|
||||||
[[example]]
|
|
||||||
doc-scrape-examples = true
|
|
||||||
name = "window"
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
|
||||||
members = [
|
members = [
|
||||||
"dpi",
|
|
||||||
"it/common-tests",
|
|
||||||
"it/gui-test",
|
|
||||||
"it/gui-test-runner",
|
|
||||||
"run-wasm",
|
"run-wasm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[[example]]
|
||||||
rust-version = "1.70.0"
|
doc-scrape-examples = true
|
||||||
repository = "https://github.com/rust-windowing/winit"
|
name = "window"
|
||||||
license = "Apache-2.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
|
||||||
async-io = "2.3.1"
|
|
||||||
gui-test = { path = "it/gui-test" }
|
|
||||||
mint = "0.5.6"
|
|
||||||
serde = { version = "1", features = ["serde_derive"] }
|
|
||||||
serde_json = "1.0.114"
|
|
||||||
winit = { path = "." }
|
|
||||||
|
|||||||
11
FEATURES.md
11
FEATURES.md
@@ -3,8 +3,8 @@
|
|||||||
Winit aims to expose an interface that abstracts over window creation and input handling and can
|
Winit aims to expose an interface that abstracts over window creation and input handling and can
|
||||||
be used to create both games and applications. It supports the following main graphical platforms:
|
be used to create both games and applications. It supports the following main graphical platforms:
|
||||||
- Desktop
|
- Desktop
|
||||||
- Windows
|
- Windows 7+ (10+ is tested regularly)
|
||||||
- macOS
|
- macOS 10.7+ (10.14+ is tested regularly)
|
||||||
- Unix
|
- Unix
|
||||||
- via X11
|
- via X11
|
||||||
- via Wayland
|
- via Wayland
|
||||||
@@ -13,6 +13,9 @@ be used to create both games and applications. It supports the following main gr
|
|||||||
- iOS
|
- iOS
|
||||||
- Android
|
- Android
|
||||||
- Web
|
- Web
|
||||||
|
- Chrome
|
||||||
|
- Firefox
|
||||||
|
- Safari 13.1+
|
||||||
|
|
||||||
Most platforms expose capabilities that cannot be meaningfully transposed onto others. Winit does not
|
Most platforms expose capabilities that cannot be meaningfully transposed onto others. Winit does not
|
||||||
aim to support every single feature of every platform, but rather to abstract over the common features
|
aim to support every single feature of every platform, but rather to abstract over the common features
|
||||||
@@ -148,6 +151,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
|||||||
* Setting the X11 parent window
|
* Setting the X11 parent window
|
||||||
|
|
||||||
### iOS
|
### iOS
|
||||||
|
* `winit` has a minimum OS requirement of iOS 8
|
||||||
* Get the `UIScreen` object pointer
|
* Get the `UIScreen` object pointer
|
||||||
* Setting the `UIView` hidpi factor
|
* Setting the `UIView` hidpi factor
|
||||||
* Valid orientations
|
* Valid orientations
|
||||||
@@ -160,6 +164,9 @@ If your PR makes notable changes to Winit's features, please update this section
|
|||||||
### Web
|
### Web
|
||||||
* Get if the systems preferred color scheme is "dark"
|
* Get if the systems preferred color scheme is "dark"
|
||||||
|
|
||||||
|
## Usability
|
||||||
|
* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial)
|
||||||
|
|
||||||
## Compatibility Matrix
|
## Compatibility Matrix
|
||||||
|
|
||||||
Legend:
|
Legend:
|
||||||
|
|||||||
126
README.md
126
README.md
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
winit = "0.29.15"
|
winit = "0.29.10"
|
||||||
```
|
```
|
||||||
|
|
||||||
## [Documentation](https://docs.rs/winit)
|
## [Documentation](https://docs.rs/winit)
|
||||||
@@ -33,6 +33,14 @@ Winit is designed to be a low-level brick in a hierarchy of libraries. Consequen
|
|||||||
show something on the window you need to use the platform-specific getters provided by winit, or
|
show something on the window you need to use the platform-specific getters provided by winit, or
|
||||||
another library.
|
another library.
|
||||||
|
|
||||||
|
### Cargo Features
|
||||||
|
|
||||||
|
Winit provides the following features, which can be enabled in your `Cargo.toml` file:
|
||||||
|
* `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde).
|
||||||
|
* `x11` (enabled by default): On Unix platform, compiles with the X11 backend
|
||||||
|
* `wayland` (enabled by default): On Unix platform, compiles with the Wayland backend
|
||||||
|
* `mint`: Enables mint (math interoperability standard types) conversions.
|
||||||
|
|
||||||
## MSRV Policy
|
## MSRV Policy
|
||||||
|
|
||||||
This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to
|
This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to
|
||||||
@@ -63,4 +71,118 @@ same MSRV policy.
|
|||||||
|
|
||||||
### Platform-specific usage
|
### Platform-specific usage
|
||||||
|
|
||||||
Check out the [`winit::platform`](https://rust-windowing.github.io/winit/winit/platform/index.html) module for platform-specific usage.
|
#### Wayland
|
||||||
|
|
||||||
|
Note that windows don't appear on Wayland until you draw/present to them.
|
||||||
|
|
||||||
|
#### Web
|
||||||
|
|
||||||
|
To run the web example: `cargo run-wasm --example web`
|
||||||
|
|
||||||
|
Winit supports compiling to the `wasm32-unknown-unknown` target with `web-sys`.
|
||||||
|
|
||||||
|
On the web platform, a Winit window is backed by a `<canvas>` element. You can
|
||||||
|
either [provide Winit with a `<canvas>` element][web with_canvas], or [let Winit
|
||||||
|
create a `<canvas>` element which you can then retrieve][web canvas getter] and
|
||||||
|
insert it into the DOM yourself.
|
||||||
|
|
||||||
|
For the example code using Winit on Web, check out the [web example]. For
|
||||||
|
information on using Rust on WebAssembly, check out the [Rust and WebAssembly
|
||||||
|
book].
|
||||||
|
|
||||||
|
[web with_canvas]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowBuilderExtWebSys.html#tymethod.with_canvas
|
||||||
|
[web canvas getter]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowExtWebSys.html#tymethod.canvas
|
||||||
|
[web example]: ./examples/web.rs
|
||||||
|
[Rust and WebAssembly book]: https://rustwasm.github.io/book/
|
||||||
|
|
||||||
|
#### Android
|
||||||
|
|
||||||
|
The Android backend builds on (and exposes types from) the [`ndk`](https://docs.rs/ndk/latest/ndk/) crate.
|
||||||
|
|
||||||
|
Native Android applications need some form of "glue" crate that is responsible
|
||||||
|
for defining the main entry point for your Rust application as well as tracking
|
||||||
|
various life-cycle events and synchronizing with the main JVM thread.
|
||||||
|
|
||||||
|
Winit uses the [android-activity](https://github.com/rib/android-activity) as a
|
||||||
|
glue crate (prior to `0.28` it used
|
||||||
|
[ndk-glue](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue)).
|
||||||
|
|
||||||
|
The version of the glue crate that your application depends on _must_ match the
|
||||||
|
version that Winit depends on because the glue crate is responsible for your
|
||||||
|
application's main entry point. If Cargo resolves multiple versions, they will
|
||||||
|
clash.
|
||||||
|
|
||||||
|
`winit` glue compatibility table:
|
||||||
|
|
||||||
|
| winit | ndk-glue |
|
||||||
|
| :---: | :--------------------------: |
|
||||||
|
| 0.29 | `android-activity = "0.5"` |
|
||||||
|
| 0.28 | `android-activity = "0.4"` |
|
||||||
|
| 0.27 | `ndk-glue = "0.7"` |
|
||||||
|
| 0.26 | `ndk-glue = "0.5"` |
|
||||||
|
| 0.25 | `ndk-glue = "0.3"` |
|
||||||
|
| 0.24 | `ndk-glue = "0.2"` |
|
||||||
|
|
||||||
|
The recommended way to avoid a conflict with the glue version is to avoid explicitly
|
||||||
|
depending on the `android-activity` crate, and instead consume the API that
|
||||||
|
is re-exported by Winit under `winit::platform::android::activity::*`
|
||||||
|
|
||||||
|
Running on an Android device needs a dynamic system library. Add this to Cargo.toml:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[lib]
|
||||||
|
name = "main"
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
```
|
||||||
|
|
||||||
|
All Android applications are based on an `Activity` subclass, and the
|
||||||
|
`android-activity` crate is designed to support different choices for this base
|
||||||
|
class. Your application _must_ specify the base class it needs via a feature flag:
|
||||||
|
|
||||||
|
| 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.|
|
||||||
|
| [`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
|
||||||
|
[`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input
|
||||||
|
[`AndroidAppCompat`]: https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity
|
||||||
|
[agdk_jetpack]: https://developer.android.com/jetpack/androidx/releases/games
|
||||||
|
[agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
|
||||||
|
[Gradle]: https://developer.android.com/studio/build
|
||||||
|
|
||||||
|
For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples).
|
||||||
|
|
||||||
|
##### Converting from `ndk-glue` to `android-activity`
|
||||||
|
|
||||||
|
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
|
||||||
|
1. Remove `ndk-glue` from your `Cargo.toml`
|
||||||
|
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.10", features = [ "android-native-activity" ] }`
|
||||||
|
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
|
||||||
|
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
|
||||||
|
|
||||||
|
#### MacOS
|
||||||
|
|
||||||
|
A lot of functionality expects the application to be ready before you start
|
||||||
|
doing anything; this includes creating windows, fetching monitors, drawing,
|
||||||
|
and so on, see issues [#2238], [#2051] and [#2087].
|
||||||
|
|
||||||
|
If you encounter problems, you should try doing your initialization inside
|
||||||
|
`Event::Resumed`.
|
||||||
|
|
||||||
|
#### iOS
|
||||||
|
|
||||||
|
Similar to macOS, iOS's main `UIApplicationMain` does some init work that's required
|
||||||
|
by all UI-related code (see issue [#1705]). It would be best to consider creating your windows
|
||||||
|
inside `Event::Resumed`.
|
||||||
|
|
||||||
|
|
||||||
|
[#2238]: https://github.com/rust-windowing/winit/issues/2238
|
||||||
|
[#2051]: https://github.com/rust-windowing/winit/issues/2051
|
||||||
|
[#2087]: https://github.com/rust-windowing/winit/issues/2087
|
||||||
|
[#1705]: https://github.com/rust-windowing/winit/issues/1705
|
||||||
|
|
||||||
|
#### Redox OS
|
||||||
|
|
||||||
|
Redox OS has some functionality not yet present that will be implemented when
|
||||||
|
its orbital display server provides it.
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
# 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}
|
|
||||||
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "dpi"
|
|
||||||
version = "0.1.1"
|
|
||||||
description = "Types for handling UI scaling"
|
|
||||||
keywords = ["DPI", "HiDPI", "scale-factor"]
|
|
||||||
categories = ["gui"]
|
|
||||||
rust-version.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
serde = ["dep:serde"]
|
|
||||||
mint = ["dep:mint"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
serde = { workspace = true, optional = true }
|
|
||||||
mint = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
features = ["serde", "mint"]
|
|
||||||
# These are all tested in CI
|
|
||||||
targets = [
|
|
||||||
# Windows
|
|
||||||
"i686-pc-windows-msvc",
|
|
||||||
"x86_64-pc-windows-msvc",
|
|
||||||
# macOS
|
|
||||||
"x86_64-apple-darwin",
|
|
||||||
# Unix (X11 & Wayland)
|
|
||||||
"i686-unknown-linux-gnu",
|
|
||||||
"x86_64-unknown-linux-gnu",
|
|
||||||
# iOS
|
|
||||||
"x86_64-apple-ios",
|
|
||||||
# Android
|
|
||||||
"aarch64-linux-android",
|
|
||||||
# Web
|
|
||||||
"wasm32-unknown-unknown",
|
|
||||||
]
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../LICENSE
|
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
#[cfg(all(
|
||||||
|
feature = "rwh_06",
|
||||||
|
any(x11_platform, macos_platform, windows_platform)
|
||||||
|
))]
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
feature = "rwh_06",
|
feature = "rwh_06",
|
||||||
any(x11_platform, macos_platform, windows_platform)
|
any(x11_platform, macos_platform, windows_platform)
|
||||||
@@ -6,57 +13,58 @@
|
|||||||
fn main() -> Result<(), impl std::error::Error> {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use winit::dpi::{LogicalPosition, LogicalSize, Position};
|
use winit::{
|
||||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
dpi::{LogicalPosition, LogicalSize, Position},
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
use winit::raw_window_handle::HasRawWindowHandle;
|
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||||
use winit::window::Window;
|
raw_window_handle::HasRawWindowHandle,
|
||||||
|
window::{Window, WindowId},
|
||||||
|
};
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
fn spawn_child_window(
|
||||||
mod fill;
|
parent: &Window,
|
||||||
|
event_loop: &EventLoopWindowTarget,
|
||||||
fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window {
|
windows: &mut HashMap<WindowId, Window>,
|
||||||
|
) {
|
||||||
let parent = parent.raw_window_handle().unwrap();
|
let parent = parent.raw_window_handle().unwrap();
|
||||||
let mut window_attributes = Window::default_attributes()
|
let mut builder = Window::builder()
|
||||||
.with_title("child window")
|
.with_title("child window")
|
||||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||||
.with_visible(true);
|
.with_visible(true);
|
||||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||||
window_attributes = unsafe { window_attributes.with_parent_window(Some(parent)) };
|
builder = unsafe { builder.with_parent_window(Some(parent)) };
|
||||||
|
let child_window = builder.build(event_loop).unwrap();
|
||||||
|
|
||||||
event_loop.create_window(window_attributes).unwrap()
|
let id = child_window.id();
|
||||||
|
windows.insert(id, child_window);
|
||||||
|
println!("child window created with id: {id:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut windows = HashMap::new();
|
let mut windows = HashMap::new();
|
||||||
|
|
||||||
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
|
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
|
||||||
let mut parent_window_id = None;
|
let parent_window = Window::builder()
|
||||||
|
.with_title("parent window")
|
||||||
|
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||||
|
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
event_loop.run(move |event: Event<()>, event_loop| {
|
println!("parent window: {parent_window:?})");
|
||||||
match event {
|
|
||||||
Event::Resumed => {
|
|
||||||
let attributes = Window::default_attributes()
|
|
||||||
.with_title("parent window")
|
|
||||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
|
||||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
|
|
||||||
let window = event_loop.create_window(attributes).unwrap();
|
|
||||||
|
|
||||||
parent_window_id = Some(window.id());
|
event_loop.run(move |event: Event<()>, elwt| {
|
||||||
|
if let Event::WindowEvent { event, window_id } = event {
|
||||||
println!("Parent window id: {parent_window_id:?})");
|
match event {
|
||||||
windows.insert(window.id(), window);
|
|
||||||
}
|
|
||||||
Event::WindowEvent { window_id, event } => match event {
|
|
||||||
WindowEvent::CloseRequested => {
|
WindowEvent::CloseRequested => {
|
||||||
windows.clear();
|
windows.clear();
|
||||||
event_loop.exit();
|
elwt.exit();
|
||||||
}
|
}
|
||||||
WindowEvent::CursorEntered { device_id: _ } => {
|
WindowEvent::CursorEntered { device_id: _ } => {
|
||||||
// On x11, println when the cursor entered in a window even if the child window is created
|
// On x11, println when the cursor entered in a window even if the child window is created
|
||||||
// by some key inputs.
|
// by some key inputs.
|
||||||
// the child windows are always placed at (0, 0) with size (200, 200) in the parent window,
|
// the child windows are always placed at (0, 0) with size (200, 200) in the parent window,
|
||||||
// so we also can see this log when we move the cursor around (200, 200) in parent window.
|
// so we also can see this log when we move the cursor arround (200, 200) in parent window.
|
||||||
println!("cursor entered in the window {window_id:?}");
|
println!("cursor entered in the window {window_id:?}");
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput {
|
WindowEvent::KeyboardInput {
|
||||||
@@ -67,11 +75,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
|||||||
},
|
},
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let parent_window = windows.get(&parent_window_id.unwrap()).unwrap();
|
spawn_child_window(&parent_window, elwt, &mut windows);
|
||||||
let child_window = spawn_child_window(parent_window, event_loop);
|
|
||||||
let child_id = child_window.id();
|
|
||||||
println!("Child window created with id: {child_id:?}");
|
|
||||||
windows.insert(child_id, child_window);
|
|
||||||
}
|
}
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
if let Some(window) = windows.get(&window_id) {
|
if let Some(window) = windows.get(&window_id) {
|
||||||
@@ -79,16 +83,15 @@ fn main() -> Result<(), impl std::error::Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(not(all(
|
||||||
feature = "rwh_06",
|
feature = "rwh_06",
|
||||||
not(any(x11_platform, macos_platform, windows_platform))
|
any(x11_platform, macos_platform, windows_platform)
|
||||||
))]
|
)))]
|
||||||
fn main() {
|
fn main() {
|
||||||
panic!("This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature enabled.");
|
panic!("This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature enabled.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,32 +3,32 @@
|
|||||||
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::application::ApplicationHandler;
|
use simple_logger::SimpleLogger;
|
||||||
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
|
use winit::{
|
||||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
use winit::keyboard::{Key, NamedKey};
|
event_loop::{ControlFlow, EventLoop},
|
||||||
use winit::window::{Window, WindowId};
|
keyboard::{Key, NamedKey},
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
|
||||||
const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
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();
|
SimpleLogger::new().init().unwrap();
|
||||||
|
|
||||||
println!("Press '1' to switch to Wait mode.");
|
println!("Press '1' to switch to Wait mode.");
|
||||||
println!("Press '2' to switch to WaitUntil mode.");
|
println!("Press '2' to switch to WaitUntil mode.");
|
||||||
@@ -37,111 +37,92 @@ fn main() -> Result<(), impl std::error::Error> {
|
|||||||
println!("Press 'Esc' to close the window.");
|
println!("Press 'Esc' to close the window.");
|
||||||
|
|
||||||
let event_loop = EventLoop::new().unwrap();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut app = ControlFlowDemo::default();
|
let mut mode = Mode::Wait;
|
||||||
event_loop.run_app(&mut app)
|
let mut request_redraw = false;
|
||||||
}
|
let mut wait_cancelled = false;
|
||||||
|
let mut close_requested = false;
|
||||||
|
|
||||||
#[derive(Default)]
|
event_loop.run(move |event, elwt| {
|
||||||
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 {
|
||||||
WindowEvent::CloseRequested => {
|
Event::NewEvents(start_cause) => {
|
||||||
self.close_requested = true;
|
wait_cancelled = match start_cause {
|
||||||
|
StartCause::WaitCancelled { .. } => mode == Mode::WaitUntil,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput {
|
Event::WindowEvent { event, .. } => match event {
|
||||||
event:
|
WindowEvent::CloseRequested => {
|
||||||
KeyEvent {
|
close_requested = true;
|
||||||
logical_key: key,
|
|
||||||
state: ElementState::Pressed,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} => 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);
|
|
||||||
}
|
}
|
||||||
Key::Character("2") => {
|
WindowEvent::KeyboardInput {
|
||||||
self.mode = Mode::WaitUntil;
|
event:
|
||||||
println!("\nmode: {:?}\n", self.mode);
|
KeyEvent {
|
||||||
}
|
logical_key: key,
|
||||||
Key::Character("3") => {
|
state: ElementState::Pressed,
|
||||||
self.mode = Mode::Poll;
|
..
|
||||||
println!("\nmode: {:?}\n", self.mode);
|
},
|
||||||
}
|
..
|
||||||
Key::Character("r") => {
|
} => match key.as_ref() {
|
||||||
self.request_redraw = !self.request_redraw;
|
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||||
println!("\nrequest_redraw: {}\n", self.request_redraw);
|
// See the `key_binding` example
|
||||||
}
|
Key::Character("1") => {
|
||||||
Key::Named(NamedKey::Escape) => {
|
mode = Mode::Wait;
|
||||||
self.close_requested = true;
|
println!("\nmode: {mode:?}\n");
|
||||||
|
}
|
||||||
|
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 => {
|
||||||
|
fill::fill_window(&window);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
WindowEvent::RedrawRequested => {
|
Event::AboutToWait => {
|
||||||
let window = self.window.as_ref().unwrap();
|
if request_redraw && !wait_cancelled && !close_requested {
|
||||||
window.pre_present_notify();
|
window.request_redraw();
|
||||||
fill::fill_window(window);
|
}
|
||||||
|
|
||||||
|
match mode {
|
||||||
|
Mode::Wait => elwt.set_control_flow(ControlFlow::Wait),
|
||||||
|
Mode::WaitUntil => {
|
||||||
|
if !wait_cancelled {
|
||||||
|
elwt.set_control_flow(ControlFlow::WaitUntil(
|
||||||
|
time::Instant::now() + WAIT_TIME,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mode::Poll => {
|
||||||
|
thread::sleep(POLL_SLEEP_TIME);
|
||||||
|
elwt.set_control_flow(ControlFlow::Poll);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if close_requested {
|
||||||
|
elwt.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
88
examples/cursor.rs
Normal file
88
examples/cursor.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::{CursorIcon, Window},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder().build(&event_loop).unwrap();
|
||||||
|
window.set_title("A fantastic window!");
|
||||||
|
|
||||||
|
let mut cursor_idx = 0;
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
|
||||||
|
window.set_cursor(CURSORS[cursor_idx]);
|
||||||
|
if cursor_idx < CURSORS.len() - 1 {
|
||||||
|
cursor_idx += 1;
|
||||||
|
} else {
|
||||||
|
cursor_idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
elwt.exit();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const CURSORS: &[CursorIcon] = &[
|
||||||
|
CursorIcon::Default,
|
||||||
|
CursorIcon::Crosshair,
|
||||||
|
CursorIcon::Pointer,
|
||||||
|
CursorIcon::Move,
|
||||||
|
CursorIcon::Text,
|
||||||
|
CursorIcon::Wait,
|
||||||
|
CursorIcon::Help,
|
||||||
|
CursorIcon::Progress,
|
||||||
|
CursorIcon::NotAllowed,
|
||||||
|
CursorIcon::ContextMenu,
|
||||||
|
CursorIcon::Cell,
|
||||||
|
CursorIcon::VerticalText,
|
||||||
|
CursorIcon::Alias,
|
||||||
|
CursorIcon::Copy,
|
||||||
|
CursorIcon::NoDrop,
|
||||||
|
CursorIcon::Grab,
|
||||||
|
CursorIcon::Grabbing,
|
||||||
|
CursorIcon::AllScroll,
|
||||||
|
CursorIcon::ZoomIn,
|
||||||
|
CursorIcon::ZoomOut,
|
||||||
|
CursorIcon::EResize,
|
||||||
|
CursorIcon::NResize,
|
||||||
|
CursorIcon::NeResize,
|
||||||
|
CursorIcon::NwResize,
|
||||||
|
CursorIcon::SResize,
|
||||||
|
CursorIcon::SeResize,
|
||||||
|
CursorIcon::SwResize,
|
||||||
|
CursorIcon::WResize,
|
||||||
|
CursorIcon::EwResize,
|
||||||
|
CursorIcon::NsResize,
|
||||||
|
CursorIcon::NeswResize,
|
||||||
|
CursorIcon::NwseResize,
|
||||||
|
CursorIcon::ColResize,
|
||||||
|
CursorIcon::RowResize,
|
||||||
|
];
|
||||||
73
examples/cursor_grab.rs
Normal file
73
examples/cursor_grab.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::{Key, ModifiersState, NamedKey},
|
||||||
|
window::{CursorGrabMode, Window},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut modifiers = ModifiersState::default();
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| match event {
|
||||||
|
Event::WindowEvent { event, .. } => match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key: key,
|
||||||
|
state: ElementState::Released,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let result = match key {
|
||||||
|
Key::Named(NamedKey::Escape) => {
|
||||||
|
elwt.exit();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||||
|
"g" => window.set_cursor_grab(CursorGrabMode::Confined),
|
||||||
|
"l" => window.set_cursor_grab(CursorGrabMode::Locked),
|
||||||
|
"a" => window.set_cursor_grab(CursorGrabMode::None),
|
||||||
|
"h" => {
|
||||||
|
window.set_cursor_visible(modifiers.shift_key());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
},
|
||||||
|
_ => Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
println!("error: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::ModifiersChanged(new) => modifiers = new.state(),
|
||||||
|
WindowEvent::RedrawRequested => fill::fill_window(&window),
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::DeviceEvent { event, .. } => match event {
|
||||||
|
DeviceEvent::MouseMotion { delta } => println!("mouse moved: {delta:?}"),
|
||||||
|
DeviceEvent::Button { button, state } => match state {
|
||||||
|
ElementState::Pressed => println!("mouse button {button} pressed"),
|
||||||
|
ElementState::Released => println!("mouse button {button} released"),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
})
|
||||||
|
}
|
||||||
143
examples/custom_cursors.rs
Normal file
143
examples/custom_cursors.rs
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#![allow(clippy::single_match, clippy::disallowed_methods)]
|
||||||
|
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||||
|
keyboard::Key,
|
||||||
|
window::{CursorIcon, CustomCursor, Window},
|
||||||
|
};
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
use {
|
||||||
|
std::sync::atomic::{AtomicU64, Ordering},
|
||||||
|
std::time::Duration,
|
||||||
|
winit::platform::web::CustomCursorExtWebSys,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
fn decode_cursor(bytes: &[u8], window_target: &EventLoopWindowTarget) -> CustomCursor {
|
||||||
|
let img = image::load_from_memory(bytes).unwrap().to_rgba8();
|
||||||
|
let samples = img.into_flat_samples();
|
||||||
|
let (_, w, h) = samples.extents();
|
||||||
|
let (w, h) = (w as u16, h as u16);
|
||||||
|
let builder = CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap();
|
||||||
|
|
||||||
|
builder.build(window_target)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
SimpleLogger::new()
|
||||||
|
.with_level(log::LevelFilter::Info)
|
||||||
|
.init()
|
||||||
|
.unwrap();
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
console_log::init_with_level(log::Level::Debug).unwrap();
|
||||||
|
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
let builder = Window::builder().with_title("A fantastic window!");
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
let builder = {
|
||||||
|
use winit::platform::web::WindowBuilderExtWebSys;
|
||||||
|
builder.with_append(true)
|
||||||
|
};
|
||||||
|
let window = builder.build(&event_loop).unwrap();
|
||||||
|
|
||||||
|
let mut cursor_idx = 0;
|
||||||
|
let mut cursor_visible = true;
|
||||||
|
|
||||||
|
let custom_cursors = [
|
||||||
|
decode_cursor(include_bytes!("data/cross.png"), &event_loop),
|
||||||
|
decode_cursor(include_bytes!("data/cross2.png"), &event_loop),
|
||||||
|
decode_cursor(include_bytes!("data/gradient.png"), &event_loop),
|
||||||
|
];
|
||||||
|
|
||||||
|
event_loop.run(move |event, _elwt| match event {
|
||||||
|
Event::WindowEvent { event, .. } => match event {
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
logical_key: key,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => match key.as_ref() {
|
||||||
|
Key::Character("1") => {
|
||||||
|
log::debug!("Setting cursor to {:?}", cursor_idx);
|
||||||
|
window.set_cursor(custom_cursors[cursor_idx].clone());
|
||||||
|
cursor_idx = (cursor_idx + 1) % 3;
|
||||||
|
}
|
||||||
|
Key::Character("2") => {
|
||||||
|
log::debug!("Setting cursor icon to default");
|
||||||
|
window.set_cursor(CursorIcon::default());
|
||||||
|
}
|
||||||
|
Key::Character("3") => {
|
||||||
|
cursor_visible = !cursor_visible;
|
||||||
|
log::debug!("Setting cursor visibility to {:?}", cursor_visible);
|
||||||
|
window.set_cursor_visible(cursor_visible);
|
||||||
|
}
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
Key::Character("4") => {
|
||||||
|
log::debug!("Setting cursor to a random image from an URL");
|
||||||
|
window.set_cursor(
|
||||||
|
CustomCursor::from_url(
|
||||||
|
format!(
|
||||||
|
"https://picsum.photos/128?random={}",
|
||||||
|
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||||
|
),
|
||||||
|
64,
|
||||||
|
64,
|
||||||
|
)
|
||||||
|
.build(_elwt),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
Key::Character("5") => {
|
||||||
|
log::debug!("Setting cursor to an animation");
|
||||||
|
window.set_cursor(
|
||||||
|
CustomCursor::from_animation(
|
||||||
|
Duration::from_secs(3),
|
||||||
|
vec![
|
||||||
|
custom_cursors[0].clone(),
|
||||||
|
custom_cursors[1].clone(),
|
||||||
|
CustomCursor::from_url(
|
||||||
|
format!(
|
||||||
|
"https://picsum.photos/128?random={}",
|
||||||
|
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||||
|
),
|
||||||
|
64,
|
||||||
|
64,
|
||||||
|
)
|
||||||
|
.build(_elwt),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.build(_elwt),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
_elwt.exit();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
})
|
||||||
|
}
|
||||||
60
examples/custom_events.rs
Normal file
60
examples/custom_events.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum CustomEvent {
|
||||||
|
Timer,
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::<CustomEvent>::with_user_event().build().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// `EventLoopProxy` allows you to dispatch custom events to the main Winit event
|
||||||
|
// loop from any thread.
|
||||||
|
let event_loop_proxy = event_loop.create_proxy();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
// Wake up the `event_loop` once every second and dispatch a custom event
|
||||||
|
// from a different thread.
|
||||||
|
loop {
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
event_loop_proxy.send_event(CustomEvent::Timer).ok();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| match event {
|
||||||
|
Event::UserEvent(event) => println!("user event: {event:?}"),
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => elwt.exit(),
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
fn main() {
|
||||||
|
panic!("This example is not supported on web.");
|
||||||
|
}
|
||||||
105
examples/drag_window.rs
Normal file
105
examples/drag_window.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::Key,
|
||||||
|
window::{Window, WindowId},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window_1 = Window::builder().build(&event_loop).unwrap();
|
||||||
|
let window_2 = Window::builder().build(&event_loop).unwrap();
|
||||||
|
|
||||||
|
let mut switched = false;
|
||||||
|
let mut entered_id = window_2.id();
|
||||||
|
let mut cursor_location = None;
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| match event {
|
||||||
|
Event::NewEvents(StartCause::Init) => {
|
||||||
|
eprintln!("Switch which window is to be dragged by pressing \"x\".")
|
||||||
|
}
|
||||||
|
Event::WindowEvent { event, window_id } => match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::CursorMoved { position, .. } => cursor_location = Some(position),
|
||||||
|
WindowEvent::MouseInput { state, button, .. } => {
|
||||||
|
let window = if (window_id == window_1.id() && switched)
|
||||||
|
|| (window_id == window_2.id() && !switched)
|
||||||
|
{
|
||||||
|
&window_2
|
||||||
|
} else {
|
||||||
|
&window_1
|
||||||
|
};
|
||||||
|
|
||||||
|
match (button, state) {
|
||||||
|
(MouseButton::Left, ElementState::Pressed) => window.drag_window().unwrap(),
|
||||||
|
(MouseButton::Right, ElementState::Released) => {
|
||||||
|
if let Some(position) = cursor_location {
|
||||||
|
window.show_window_menu(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::CursorEntered { .. } => {
|
||||||
|
entered_id = window_id;
|
||||||
|
name_windows(entered_id, switched, &window_1, &window_2)
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
state: ElementState::Released,
|
||||||
|
logical_key: Key::Character(c),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => match c.as_str() {
|
||||||
|
"x" => {
|
||||||
|
switched = !switched;
|
||||||
|
name_windows(entered_id, switched, &window_1, &window_2);
|
||||||
|
println!("Switched!")
|
||||||
|
}
|
||||||
|
"d" => {
|
||||||
|
let window = if (window_id == window_1.id() && switched)
|
||||||
|
|| (window_id == window_2.id() && !switched)
|
||||||
|
{
|
||||||
|
&window_2
|
||||||
|
} else {
|
||||||
|
&window_1
|
||||||
|
};
|
||||||
|
|
||||||
|
window.set_decorations(!window.is_decorated());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
if window_id == window_1.id() {
|
||||||
|
fill::fill_window(&window_1);
|
||||||
|
} else if window_id == window_2.id() {
|
||||||
|
fill::fill_window(&window_2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name_windows(window_id: WindowId, switched: bool, window_1: &Window, window_2: &Window) {
|
||||||
|
let (drag_target, other) =
|
||||||
|
if (window_id == window_1.id() && switched) || (window_id == window_2.id() && !switched) {
|
||||||
|
(&window_2, &window_1)
|
||||||
|
} else {
|
||||||
|
(&window_1, &window_2)
|
||||||
|
};
|
||||||
|
drag_target.set_title("drag target");
|
||||||
|
other.set_title("winit window");
|
||||||
|
}
|
||||||
56
examples/focus.rs
Normal file
56
examples/focus.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
//! Example for focusing a window.
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
use std::time;
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
use web_time as time;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, StartCause, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut deadline = time::Instant::now() + time::Duration::from_secs(3);
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
match event {
|
||||||
|
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
|
||||||
|
// Timeout reached; focus the window.
|
||||||
|
println!("Re-focusing the window.");
|
||||||
|
deadline += time::Duration::from_secs(3);
|
||||||
|
window.focus_window();
|
||||||
|
}
|
||||||
|
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
// Notify the windowing system that we'll be presenting to the window.
|
||||||
|
window.pre_present_notify();
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
elwt.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(deadline));
|
||||||
|
})
|
||||||
|
}
|
||||||
163
examples/fullscreen.rs
Normal file
163
examples/fullscreen.rs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::dpi::LogicalSize;
|
||||||
|
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||||
|
use winit::event_loop::EventLoop;
|
||||||
|
use winit::keyboard::{Key, NamedKey};
|
||||||
|
use winit::window::{Fullscreen, Window};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use winit::platform::macos::WindowExtMacOS;
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let mut decorations = true;
|
||||||
|
let mut minimized = false;
|
||||||
|
let mut with_min_size = false;
|
||||||
|
let mut with_max_size = false;
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("Hello world!")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut monitor_index = 0;
|
||||||
|
let mut monitor = event_loop
|
||||||
|
.available_monitors()
|
||||||
|
.next()
|
||||||
|
.expect("no monitor found!");
|
||||||
|
println!("Monitor: {:?}", monitor.name());
|
||||||
|
|
||||||
|
let mut mode_index = 0;
|
||||||
|
let mut mode = monitor.video_modes().next().expect("no mode found");
|
||||||
|
println!("Mode: {mode}");
|
||||||
|
|
||||||
|
println!("Keys:");
|
||||||
|
println!("- Esc\tExit");
|
||||||
|
println!("- F\tToggle exclusive fullscreen mode");
|
||||||
|
println!("- B\tToggle borderless mode");
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
println!("- C\tToggle simple fullscreen mode");
|
||||||
|
println!("- S\tNext screen");
|
||||||
|
println!("- M\tNext mode for this screen");
|
||||||
|
println!("- D\tToggle window decorations");
|
||||||
|
println!("- X\tMaximize window");
|
||||||
|
println!("- Z\tMinimize window");
|
||||||
|
println!("- I\tToggle mIn size limit");
|
||||||
|
println!("- A\tToggle mAx size limit");
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key: key,
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => match key {
|
||||||
|
Key::Named(NamedKey::Escape) => elwt.exit(),
|
||||||
|
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||||
|
// See the `key_binding` example
|
||||||
|
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||||
|
"f" | "b" if window.fullscreen().is_some() => {
|
||||||
|
window.set_fullscreen(None);
|
||||||
|
}
|
||||||
|
"f" => {
|
||||||
|
let fullscreen = Some(Fullscreen::Exclusive(mode.clone()));
|
||||||
|
println!("Setting mode: {fullscreen:?}");
|
||||||
|
window.set_fullscreen(fullscreen);
|
||||||
|
}
|
||||||
|
"b" => {
|
||||||
|
let fullscreen = Some(Fullscreen::Borderless(Some(monitor.clone())));
|
||||||
|
println!("Setting mode: {fullscreen:?}");
|
||||||
|
window.set_fullscreen(fullscreen);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
"c" => {
|
||||||
|
window.set_simple_fullscreen(!window.simple_fullscreen());
|
||||||
|
}
|
||||||
|
"s" => {
|
||||||
|
monitor_index += 1;
|
||||||
|
if let Some(mon) = elwt.available_monitors().nth(monitor_index) {
|
||||||
|
monitor = mon;
|
||||||
|
} else {
|
||||||
|
monitor_index = 0;
|
||||||
|
monitor =
|
||||||
|
elwt.available_monitors().next().expect("no monitor found!");
|
||||||
|
}
|
||||||
|
println!("Monitor: {:?}", monitor.name());
|
||||||
|
|
||||||
|
mode_index = 0;
|
||||||
|
mode = monitor.video_modes().next().expect("no mode found");
|
||||||
|
println!("Mode: {mode}");
|
||||||
|
}
|
||||||
|
"m" => {
|
||||||
|
mode_index += 1;
|
||||||
|
if let Some(m) = monitor.video_modes().nth(mode_index) {
|
||||||
|
mode = m;
|
||||||
|
} else {
|
||||||
|
mode_index = 0;
|
||||||
|
mode = monitor.video_modes().next().expect("no mode found");
|
||||||
|
}
|
||||||
|
println!("Mode: {mode}");
|
||||||
|
}
|
||||||
|
"d" => {
|
||||||
|
decorations = !decorations;
|
||||||
|
window.set_decorations(decorations);
|
||||||
|
}
|
||||||
|
"x" => {
|
||||||
|
let is_maximized = window.is_maximized();
|
||||||
|
window.set_maximized(!is_maximized);
|
||||||
|
}
|
||||||
|
"z" => {
|
||||||
|
minimized = !minimized;
|
||||||
|
window.set_minimized(minimized);
|
||||||
|
}
|
||||||
|
"i" => {
|
||||||
|
with_min_size = !with_min_size;
|
||||||
|
let min_size = if with_min_size {
|
||||||
|
Some(LogicalSize::new(100, 100))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
window.set_min_inner_size(min_size);
|
||||||
|
eprintln!(
|
||||||
|
"Min: {with_min_size}: {min_size:?} => {:?}",
|
||||||
|
window.inner_size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"a" => {
|
||||||
|
with_max_size = !with_max_size;
|
||||||
|
let max_size = if with_max_size {
|
||||||
|
Some(LogicalSize::new(200, 200))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
window.set_max_inner_size(max_size);
|
||||||
|
eprintln!(
|
||||||
|
"Max: {with_max_size}: {max_size:?} => {:?}",
|
||||||
|
window.inner_size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
86
examples/handling_close.rs
Normal file
86
examples/handling_close.rs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::Key,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("Your faithful window")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut close_requested = false;
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
// `CloseRequested` is sent when the close button on the window is pressed (or
|
||||||
|
// through whatever other mechanisms the window manager provides for closing a
|
||||||
|
// window). If you don't handle this event, the close button won't actually do
|
||||||
|
// anything.
|
||||||
|
|
||||||
|
// A common thing to do here is prompt the user if they have unsaved work.
|
||||||
|
// Creating a proper dialog box for that is far beyond the scope of this
|
||||||
|
// example, so here we'll just respond to the Y and N keys.
|
||||||
|
println!("Are you ready to bid your window farewell? [Y/N]");
|
||||||
|
close_requested = true;
|
||||||
|
|
||||||
|
// In applications where you can safely close the window without further
|
||||||
|
// action from the user, this is generally where you'd handle cleanup before
|
||||||
|
// closing the window. How to close the window is detailed in the handler for
|
||||||
|
// the Y key.
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key: key,
|
||||||
|
state: ElementState::Released,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||||
|
// See the `key_binding` example
|
||||||
|
match key.as_ref() {
|
||||||
|
Key::Character("y") => {
|
||||||
|
if close_requested {
|
||||||
|
// This is where you'll want to do any cleanup you need.
|
||||||
|
println!("Buh-bye!");
|
||||||
|
|
||||||
|
// For a single-window application like this, you'd normally just
|
||||||
|
// break out of the event loop here. If you wanted to keep running the
|
||||||
|
// event loop (i.e. if it's a multi-window application), you need to
|
||||||
|
// drop the window. That closes it, and results in `Destroyed` being
|
||||||
|
// sent.
|
||||||
|
elwt.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::Character("n") => {
|
||||||
|
if close_requested {
|
||||||
|
println!("Your window will continue to stay by your side.");
|
||||||
|
close_requested = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
94
examples/ime.rs
Normal file
94
examples/ime.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use log::LevelFilter;
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
dpi::{PhysicalPosition, PhysicalSize},
|
||||||
|
event::{ElementState, Event, Ime, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::NamedKey,
|
||||||
|
window::{ImePurpose, Window},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new()
|
||||||
|
.with_level(LevelFilter::Trace)
|
||||||
|
.init()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("IME position will system default");
|
||||||
|
println!("Click to set IME position to cursor's");
|
||||||
|
println!("Press F2 to toggle IME. See the documentation of `set_ime_allowed` for more info");
|
||||||
|
println!("Press F3 to cycle through IME purposes.");
|
||||||
|
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut ime_purpose = ImePurpose::Normal;
|
||||||
|
let mut ime_allowed = true;
|
||||||
|
window.set_ime_allowed(ime_allowed);
|
||||||
|
|
||||||
|
let mut may_show_ime = false;
|
||||||
|
let mut cursor_position = PhysicalPosition::new(0.0, 0.0);
|
||||||
|
let mut ime_pos = PhysicalPosition::new(0.0, 0.0);
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
|
cursor_position = position;
|
||||||
|
}
|
||||||
|
WindowEvent::MouseInput {
|
||||||
|
state: ElementState::Released,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
println!(
|
||||||
|
"Setting ime position to {}, {}",
|
||||||
|
cursor_position.x, cursor_position.y
|
||||||
|
);
|
||||||
|
ime_pos = cursor_position;
|
||||||
|
if may_show_ime {
|
||||||
|
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::Ime(event) => {
|
||||||
|
println!("{event:?}");
|
||||||
|
may_show_ime = event != Ime::Disabled;
|
||||||
|
if may_show_ime {
|
||||||
|
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
|
println!("key: {event:?}");
|
||||||
|
|
||||||
|
if event.state == ElementState::Pressed && event.logical_key == NamedKey::F2 {
|
||||||
|
ime_allowed = !ime_allowed;
|
||||||
|
window.set_ime_allowed(ime_allowed);
|
||||||
|
println!("\nIME allowed: {ime_allowed}\n");
|
||||||
|
}
|
||||||
|
if event.state == ElementState::Pressed && event.logical_key == NamedKey::F3 {
|
||||||
|
ime_purpose = match ime_purpose {
|
||||||
|
ImePurpose::Normal => ImePurpose::Password,
|
||||||
|
ImePurpose::Password => ImePurpose::Terminal,
|
||||||
|
_ => ImePurpose::Normal,
|
||||||
|
};
|
||||||
|
window.set_ime_purpose(ime_purpose);
|
||||||
|
println!("\nIME purpose: {ime_purpose:?}\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
62
examples/key_binding.rs
Normal file
62
examples/key_binding.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
|
||||||
|
use winit::{
|
||||||
|
dpi::LogicalSize,
|
||||||
|
event::{ElementState, Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::{Key, ModifiersState},
|
||||||
|
// WARNING: This is not available on all platforms (for example on the web).
|
||||||
|
platform::modifier_supplement::KeyEventExtModifierSupplement,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||||
|
fn main() {
|
||||||
|
println!("This example is not supported on this platform");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
simple_logger::SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_inner_size(LogicalSize::new(400.0, 200.0))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut modifiers = ModifiersState::default();
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::ModifiersChanged(new) => {
|
||||||
|
modifiers = new.state();
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
|
if event.state == ElementState::Pressed && !event.repeat {
|
||||||
|
match event.key_without_modifiers().as_ref() {
|
||||||
|
Key::Character("1") => {
|
||||||
|
if modifiers.shift_key() {
|
||||||
|
println!("Shift + 1 | logical_key: {:?}", event.logical_key);
|
||||||
|
} else {
|
||||||
|
println!("1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
58
examples/monitor_list.rs
Normal file
58
examples/monitor_list.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::dpi::{PhysicalPosition, PhysicalSize};
|
||||||
|
use winit::monitor::MonitorHandle;
|
||||||
|
use winit::{event_loop::EventLoop, window::Window};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
let window = Window::builder().build(&event_loop).unwrap();
|
||||||
|
|
||||||
|
if let Some(mon) = window.primary_monitor() {
|
||||||
|
print_info("Primary output", mon);
|
||||||
|
}
|
||||||
|
|
||||||
|
for mon in window.available_monitors() {
|
||||||
|
if Some(&mon) == window.primary_monitor().as_ref() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!();
|
||||||
|
print_info("Output", mon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_info(intro: &str, monitor: MonitorHandle) {
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
65
examples/mouse_wheel.rs
Normal file
65
examples/mouse_wheel.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("Mouse Wheel events")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
r"
|
||||||
|
When using so called 'natural scrolling' (scrolling that acts like on a touch screen), this is what to expect:
|
||||||
|
|
||||||
|
Moving your finger downwards on a scroll wheel should make the window move down, and you should see a positive Y scroll value.
|
||||||
|
|
||||||
|
When moving fingers on a trackpad down and to the right, you should see positive X and Y deltas, and the window should move down and to the right.
|
||||||
|
|
||||||
|
With reverse scrolling, you should see the inverse behavior.
|
||||||
|
|
||||||
|
In both cases the example window should move like the content of a scroll area in any other application.
|
||||||
|
|
||||||
|
In other words, the deltas indicate the direction in which to move the content (in this case the window)."
|
||||||
|
);
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||||
|
winit::event::MouseScrollDelta::LineDelta(x, y) => {
|
||||||
|
println!("mouse wheel Line Delta: ({x},{y})");
|
||||||
|
let pixels_per_line = 120.0;
|
||||||
|
let mut pos = window.outer_position().unwrap();
|
||||||
|
pos.x += (x * pixels_per_line) as i32;
|
||||||
|
pos.y += (y * pixels_per_line) as i32;
|
||||||
|
window.set_outer_position(pos)
|
||||||
|
}
|
||||||
|
winit::event::MouseScrollDelta::PixelDelta(p) => {
|
||||||
|
println!("mouse wheel Pixel Delta: ({},{})", p.x, p.y);
|
||||||
|
let mut pos = window.outer_position().unwrap();
|
||||||
|
pos.x += p.x as i32;
|
||||||
|
pos.y += p.y as i32;
|
||||||
|
window.set_outer_position(pos)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
211
examples/multithreaded.rs
Normal file
211
examples/multithreaded.rs
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::{Key, ModifiersState, NamedKey},
|
||||||
|
window::{CursorGrabMode, CursorIcon, Fullscreen, Window, WindowLevel},
|
||||||
|
};
|
||||||
|
|
||||||
|
const WINDOW_COUNT: usize = 3;
|
||||||
|
const WINDOW_SIZE: PhysicalSize<u32> = PhysicalSize::new(600, 400);
|
||||||
|
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
|
||||||
|
for _ in 0..WINDOW_COUNT {
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_inner_size(WINDOW_SIZE)
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut video_modes: Vec<_> = window.current_monitor().unwrap().video_modes().collect();
|
||||||
|
let mut video_mode_id = 0usize;
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
window_senders.insert(window.id(), tx);
|
||||||
|
let mut modifiers = ModifiersState::default();
|
||||||
|
thread::spawn(move || {
|
||||||
|
while let Ok(event) = rx.recv() {
|
||||||
|
match event {
|
||||||
|
WindowEvent::Moved { .. } => {
|
||||||
|
// We need to update our chosen video mode if the window
|
||||||
|
// was moved to an another monitor, so that the window
|
||||||
|
// appears on this monitor instead when we go fullscreen
|
||||||
|
let previous_video_mode = video_modes.get(video_mode_id).cloned();
|
||||||
|
video_modes = window.current_monitor().unwrap().video_modes().collect();
|
||||||
|
video_mode_id = video_mode_id.min(video_modes.len());
|
||||||
|
let video_mode = video_modes.get(video_mode_id);
|
||||||
|
|
||||||
|
// Different monitors may support different video modes,
|
||||||
|
// and the index we chose previously may now point to a
|
||||||
|
// completely different video mode, so notify the user
|
||||||
|
if video_mode != previous_video_mode.as_ref() {
|
||||||
|
println!(
|
||||||
|
"Window moved to another monitor, picked video mode: {}",
|
||||||
|
video_modes.get(video_mode_id).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::ModifiersChanged(new) => {
|
||||||
|
modifiers = new.state();
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
state: ElementState::Released,
|
||||||
|
logical_key: key,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
use NamedKey::{ArrowLeft, ArrowRight};
|
||||||
|
window.set_title(&format!("{key:?}"));
|
||||||
|
let state = !modifiers.shift_key();
|
||||||
|
match key {
|
||||||
|
// Cycle through video modes
|
||||||
|
Key::Named(ArrowRight) | Key::Named(ArrowLeft) => {
|
||||||
|
if key == ArrowLeft {
|
||||||
|
video_mode_id = video_mode_id.saturating_sub(1);
|
||||||
|
} else if key == ArrowRight {
|
||||||
|
video_mode_id = (video_modes.len() - 1).min(video_mode_id + 1);
|
||||||
|
}
|
||||||
|
println!("Picking video mode: {}", video_modes[video_mode_id]);
|
||||||
|
}
|
||||||
|
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||||
|
// See the `key_binding` example
|
||||||
|
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||||
|
"1" => window.set_window_level(WindowLevel::AlwaysOnTop),
|
||||||
|
"2" => window.set_window_level(WindowLevel::AlwaysOnBottom),
|
||||||
|
"3" => window.set_window_level(WindowLevel::Normal),
|
||||||
|
"c" => window.set_cursor(match state {
|
||||||
|
true => CursorIcon::Progress,
|
||||||
|
false => CursorIcon::Default,
|
||||||
|
}),
|
||||||
|
"d" => window.set_decorations(!state),
|
||||||
|
"f" => window.set_fullscreen(match (state, modifiers.alt_key()) {
|
||||||
|
(true, false) => Some(Fullscreen::Borderless(None)),
|
||||||
|
(true, true) => Some(Fullscreen::Exclusive(
|
||||||
|
video_modes[video_mode_id].clone(),
|
||||||
|
)),
|
||||||
|
(false, _) => None,
|
||||||
|
}),
|
||||||
|
ch @ ("g" | "l") => {
|
||||||
|
let mode = match (ch, state) {
|
||||||
|
("l", true) => CursorGrabMode::Locked,
|
||||||
|
("g", true) => CursorGrabMode::Confined,
|
||||||
|
(_, _) => CursorGrabMode::None,
|
||||||
|
};
|
||||||
|
if let Err(err) = window.set_cursor_grab(mode) {
|
||||||
|
println!("error: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"h" => window.set_cursor_visible(!state),
|
||||||
|
"i" => {
|
||||||
|
println!("Info:");
|
||||||
|
println!("-> outer_position : {:?}", window.outer_position());
|
||||||
|
println!("-> inner_position : {:?}", window.inner_position());
|
||||||
|
println!("-> outer_size : {:?}", window.outer_size());
|
||||||
|
println!("-> inner_size : {:?}", window.inner_size());
|
||||||
|
println!("-> fullscreen : {:?}", window.fullscreen());
|
||||||
|
}
|
||||||
|
"m" => window.set_maximized(state),
|
||||||
|
"p" => window.set_outer_position({
|
||||||
|
let mut position = window.outer_position().unwrap();
|
||||||
|
let sign = if state { 1 } else { -1 };
|
||||||
|
position.x += 10 * sign;
|
||||||
|
position.y += 10 * sign;
|
||||||
|
position
|
||||||
|
}),
|
||||||
|
"q" => window.request_redraw(),
|
||||||
|
"r" => window.set_resizable(state),
|
||||||
|
"s" => {
|
||||||
|
let _ = window.request_inner_size(match state {
|
||||||
|
true => PhysicalSize::new(
|
||||||
|
WINDOW_SIZE.width + 50,
|
||||||
|
WINDOW_SIZE.height + 50,
|
||||||
|
),
|
||||||
|
false => WINDOW_SIZE,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
"k" => window.set_min_inner_size(match state {
|
||||||
|
true => Some(PhysicalSize::new(
|
||||||
|
WINDOW_SIZE.width - 100,
|
||||||
|
WINDOW_SIZE.height - 100,
|
||||||
|
)),
|
||||||
|
false => None,
|
||||||
|
}),
|
||||||
|
"o" => window.set_max_inner_size(match state {
|
||||||
|
true => Some(PhysicalSize::new(
|
||||||
|
WINDOW_SIZE.width + 100,
|
||||||
|
WINDOW_SIZE.height + 100,
|
||||||
|
)),
|
||||||
|
false => None,
|
||||||
|
}),
|
||||||
|
"w" => {
|
||||||
|
if let Size::Physical(size) = WINDOW_SIZE.into() {
|
||||||
|
window
|
||||||
|
.set_cursor_position(Position::Physical(
|
||||||
|
PhysicalPosition::new(
|
||||||
|
size.width as i32 / 2,
|
||||||
|
size.height as i32 / 2,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"z" => {
|
||||||
|
window.set_visible(false);
|
||||||
|
thread::sleep(Duration::from_secs(1));
|
||||||
|
window.set_visible(true);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if window_senders.is_empty() {
|
||||||
|
elwt.exit()
|
||||||
|
}
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent { event, window_id } => match event {
|
||||||
|
WindowEvent::CloseRequested
|
||||||
|
| WindowEvent::Destroyed
|
||||||
|
| WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
state: ElementState::Released,
|
||||||
|
logical_key: Key::Named(NamedKey::Escape),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
window_senders.remove(&window_id);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let Some(tx) = window_senders.get(&window_id) {
|
||||||
|
tx.send(event).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
fn main() {
|
||||||
|
panic!("Example not supported on Web");
|
||||||
|
}
|
||||||
64
examples/multiwindow.rs
Normal file
64
examples/multiwindow.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::{Key, NamedKey},
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let mut windows = HashMap::new();
|
||||||
|
for _ in 0..3 {
|
||||||
|
let window = Window::new(&event_loop).unwrap();
|
||||||
|
println!("Opened a new window: {:?}", window.id());
|
||||||
|
windows.insert(window.id(), window);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Press N to open a new window.");
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, window_id } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
println!("Window {window_id:?} has received the signal to close");
|
||||||
|
|
||||||
|
// This drops the window, causing it to close.
|
||||||
|
windows.remove(&window_id);
|
||||||
|
|
||||||
|
if windows.is_empty() {
|
||||||
|
elwt.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event,
|
||||||
|
is_synthetic: false,
|
||||||
|
..
|
||||||
|
} if event.state == ElementState::Pressed => match event.logical_key {
|
||||||
|
Key::Named(NamedKey::Escape) => elwt.exit(),
|
||||||
|
Key::Character(c) if c == "n" || c == "N" => {
|
||||||
|
let window = Window::new(elwt).unwrap();
|
||||||
|
println!("Opened a new window: {:?}", window.id());
|
||||||
|
windows.insert(window.id(), window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
if let Some(window) = windows.get(&window_id) {
|
||||||
|
fill::fill_window(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
#![allow(clippy::single_match)]
|
|
||||||
|
|
||||||
// Limit this example to only compatible platforms.
|
|
||||||
#[cfg(any(
|
|
||||||
windows_platform,
|
|
||||||
macos_platform,
|
|
||||||
x11_platform,
|
|
||||||
wayland_platform,
|
|
||||||
android_platform,
|
|
||||||
))]
|
|
||||||
fn main() -> std::process::ExitCode {
|
|
||||||
use std::{process::ExitCode, thread::sleep, time::Duration};
|
|
||||||
|
|
||||||
use winit::application::ApplicationHandler;
|
|
||||||
use winit::event::WindowEvent;
|
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
|
||||||
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus};
|
|
||||||
use winit::window::{Window, WindowId};
|
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
|
||||||
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();
|
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
|
||||||
|
|
||||||
let mut app = PumpDemo::default();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let timeout = Some(Duration::ZERO);
|
|
||||||
let status = event_loop.pump_app_events(timeout, &mut app);
|
|
||||||
|
|
||||||
if let PumpStatus::Exit(exit_code) = status {
|
|
||||||
break ExitCode::from(exit_code as u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sleep for 1/60 second to simulate application work
|
|
||||||
//
|
|
||||||
// Since `pump_events` doesn't block it will be important to
|
|
||||||
// throttle the loop in the app somehow.
|
|
||||||
println!("Update()");
|
|
||||||
sleep(Duration::from_millis(16));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(ios_platform, web_platform, orbital_platform))]
|
|
||||||
fn main() {
|
|
||||||
println!("This platform doesn't support pump_events.");
|
|
||||||
}
|
|
||||||
42
examples/request_redraw.rs
Normal file
42
examples/request_redraw.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
println!("{event:?}");
|
||||||
|
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::MouseInput {
|
||||||
|
state: ElementState::Released,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
println!("\nredrawing!\n");
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
59
examples/request_redraw_threaded.rs
Normal file
59
examples/request_redraw_threaded.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
use std::{sync::Arc, thread, time};
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = {
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
Arc::new(window)
|
||||||
|
};
|
||||||
|
|
||||||
|
thread::spawn({
|
||||||
|
let window = window.clone();
|
||||||
|
move || loop {
|
||||||
|
thread::sleep(time::Duration::from_secs(1));
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
println!("{event:?}");
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => elwt.exit(),
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
println!("\nredrawing!\n");
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
fn main() {
|
||||||
|
unimplemented!() // `Window` can't be sent between threads
|
||||||
|
}
|
||||||
54
examples/resizable.rs
Normal file
54
examples/resizable.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
dpi::LogicalSize,
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::{KeyCode, PhysicalKey},
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let mut resizable = false;
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("Hit space to toggle resizability.")
|
||||||
|
.with_inner_size(LogicalSize::new(600.0, 300.0))
|
||||||
|
.with_min_inner_size(LogicalSize::new(400.0, 200.0))
|
||||||
|
.with_max_inner_size(LogicalSize::new(800.0, 400.0))
|
||||||
|
.with_resizable(resizable)
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
physical_key: PhysicalKey::Code(KeyCode::Space),
|
||||||
|
state: ElementState::Released,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
resizable = !resizable;
|
||||||
|
println!("Resizable: {resizable}");
|
||||||
|
window.set_resizable(resizable);
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
#![allow(clippy::single_match)]
|
|
||||||
|
|
||||||
// Limit this example to only compatible platforms.
|
|
||||||
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))]
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use winit::application::ApplicationHandler;
|
|
||||||
use winit::event::WindowEvent;
|
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
|
||||||
use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
|
|
||||||
use winit::window::{Window, WindowId};
|
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
|
||||||
mod fill;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct App {
|
|
||||||
idx: usize,
|
|
||||||
window_id: Option<WindowId>,
|
|
||||||
window: Option<Window>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApplicationHandler for App {
|
|
||||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
|
||||||
if let Some(window) = self.window.as_ref() {
|
|
||||||
window.request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
let window_attributes = Window::default_attributes()
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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!("--------------------------------------------------------- Waiting 5 seconds");
|
|
||||||
std::thread::sleep(Duration::from_secs(5));
|
|
||||||
|
|
||||||
app.idx += 1;
|
|
||||||
event_loop.run_app_on_demand(&mut app)?;
|
|
||||||
println!("--------------------------------------------------------- Finished second loop");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))]
|
|
||||||
fn main() {
|
|
||||||
println!("This example is not supported on this platform");
|
|
||||||
}
|
|
||||||
115
examples/startup_notification.rs
Normal file
115
examples/startup_notification.rs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
//! Demonstrates the use of startup notifications on Linux.
|
||||||
|
|
||||||
|
#[cfg(any(x11_platform, wayland_platform))]
|
||||||
|
#[path = "./util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
#[cfg(any(x11_platform, wayland_platform))]
|
||||||
|
mod example {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||||
|
use winit::event_loop::EventLoop;
|
||||||
|
use winit::platform::startup_notify::{
|
||||||
|
EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify,
|
||||||
|
};
|
||||||
|
use winit::window::{Window, WindowId};
|
||||||
|
|
||||||
|
pub(super) fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
// Create the event loop and get the activation token.
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
let mut current_token = match event_loop.read_token_from_env() {
|
||||||
|
Some(token) => Some(token),
|
||||||
|
None => {
|
||||||
|
println!("No startup notification token found in environment.");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut windows: HashMap<WindowId, Rc<Window>> = HashMap::new();
|
||||||
|
let mut counter = 0;
|
||||||
|
let mut create_first_window = false;
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
match event {
|
||||||
|
Event::Resumed => create_first_window = true,
|
||||||
|
|
||||||
|
Event::WindowEvent { window_id, event } => match event {
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key,
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if logical_key == "n" {
|
||||||
|
if let Some(window) = windows.get(&window_id) {
|
||||||
|
// Request a new activation token on this window.
|
||||||
|
// Once we get it we will use it to create a window.
|
||||||
|
window
|
||||||
|
.request_activation_token()
|
||||||
|
.expect("Failed to request activation token.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
// Remove the window from the map.
|
||||||
|
windows.remove(&window_id);
|
||||||
|
if windows.is_empty() {
|
||||||
|
elwt.exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowEvent::ActivationTokenDone { token, .. } => {
|
||||||
|
current_token = Some(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
if let Some(window) = windows.get(&window_id) {
|
||||||
|
super::fill::fill_window(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we've passed the deadline.
|
||||||
|
if current_token.is_some() || create_first_window {
|
||||||
|
// Create the initial window.
|
||||||
|
let window = {
|
||||||
|
let mut builder = Window::builder().with_title(format!("Window {}", counter));
|
||||||
|
|
||||||
|
if let Some(token) = current_token.take() {
|
||||||
|
println!("Creating a window with token {token:?}");
|
||||||
|
builder = builder.with_activation_token(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rc::new(builder.build(elwt).unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the window to the map.
|
||||||
|
windows.insert(window.id(), window.clone());
|
||||||
|
|
||||||
|
counter += 1;
|
||||||
|
create_first_window = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(x11_platform, wayland_platform))]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
example::main()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(x11_platform, wayland_platform)))]
|
||||||
|
fn main() {
|
||||||
|
println!("This example is only supported on X11 and Wayland platforms.");
|
||||||
|
}
|
||||||
68
examples/theme.rs
Normal file
68
examples/theme.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::Key,
|
||||||
|
window::{Theme, Window},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.with_theme(Some(Theme::Dark))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("Initial theme: {:?}", window.theme());
|
||||||
|
println!("debugging keys:");
|
||||||
|
println!(" (A) Automatic theme");
|
||||||
|
println!(" (L) Light theme");
|
||||||
|
println!(" (D) Dark theme");
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { window_id, event } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::ThemeChanged(theme) if window_id == window.id() => {
|
||||||
|
println!("Theme is changed: {theme:?}")
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key: key,
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => match key.as_ref() {
|
||||||
|
Key::Character("A" | "a") => {
|
||||||
|
println!("Theme was: {:?}", window.theme());
|
||||||
|
window.set_theme(None);
|
||||||
|
}
|
||||||
|
Key::Character("L" | "l") => {
|
||||||
|
println!("Theme was: {:?}", window.theme());
|
||||||
|
window.set_theme(Some(Theme::Light));
|
||||||
|
}
|
||||||
|
Key::Character("D" | "d") => {
|
||||||
|
println!("Theme was: {:?}", window.theme());
|
||||||
|
window.set_theme(Some(Theme::Dark));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
println!("\nredrawing!\n");
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
54
examples/timer.rs
Normal file
54
examples/timer.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
#[cfg(not(web_platform))]
|
||||||
|
use std::time::Instant;
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
use web_time::Instant;
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, StartCause, WindowEvent},
|
||||||
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let timer_length = Duration::new(1, 0);
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
println!("{event:?}");
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::NewEvents(StartCause::Init) => {
|
||||||
|
elwt.set_control_flow(ControlFlow::WaitUntil(Instant::now() + timer_length));
|
||||||
|
}
|
||||||
|
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
|
||||||
|
elwt.set_control_flow(ControlFlow::WaitUntil(Instant::now() + timer_length));
|
||||||
|
println!("\nTimer\n");
|
||||||
|
}
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => elwt.exit(),
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
62
examples/touch_gestures.rs
Normal file
62
examples/touch_gestures.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("Touchpad gestures")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
#[cfg(target_os = "ios")]
|
||||||
|
{
|
||||||
|
use winit::platform::ios::WindowExtIOS;
|
||||||
|
window.recognize_doubletap_gesture(true);
|
||||||
|
window.recognize_pinch_gesture(true);
|
||||||
|
window.recognize_rotation_gesture(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Only supported on macOS/iOS at the moment.");
|
||||||
|
|
||||||
|
let mut zoom = 0.0;
|
||||||
|
let mut rotated = 0.0;
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::PinchGesture { delta, .. } => {
|
||||||
|
zoom += delta;
|
||||||
|
if delta > 0.0 {
|
||||||
|
println!("Zoomed in {delta:.5} (now: {zoom:.5})");
|
||||||
|
} else {
|
||||||
|
println!("Zoomed out {delta:.5} (now: {zoom:.5})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::DoubleTapGesture { .. } => {
|
||||||
|
println!("Smart zoom");
|
||||||
|
}
|
||||||
|
WindowEvent::RotationGesture { delta, .. } => {
|
||||||
|
rotated += delta;
|
||||||
|
if delta > 0.0 {
|
||||||
|
println!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
|
||||||
|
} else {
|
||||||
|
println!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
38
examples/transparent.rs
Normal file
38
examples/transparent.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_decorations(false)
|
||||||
|
.with_transparent(true)
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
window.set_title("A fantastic window!");
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
println!("{event:?}");
|
||||||
|
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ mod platform {
|
|||||||
// ManuallyDrop to prevent destructors from running.
|
// ManuallyDrop to prevent destructors from running.
|
||||||
//
|
//
|
||||||
// A static, thread-local map of graphics contexts to open windows.
|
// A static, thread-local map of graphics contexts to open windows.
|
||||||
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) };
|
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The graphics context used to draw to a window.
|
/// The graphics context used to draw to a window.
|
||||||
|
|||||||
22
examples/video_modes.rs
Normal file
22
examples/video_modes.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::event_loop::EventLoop;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
let monitor = match event_loop.primary_monitor() {
|
||||||
|
Some(monitor) => monitor,
|
||||||
|
None => {
|
||||||
|
println!("No primary monitor detected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Listing available video modes:");
|
||||||
|
|
||||||
|
for mode in monitor.video_modes() {
|
||||||
|
println!("{mode}");
|
||||||
|
}
|
||||||
|
}
|
||||||
147
examples/web.rs
Normal file
147
examples/web.rs
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
#![allow(clippy::disallowed_methods, clippy::single_match)]
|
||||||
|
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::Key,
|
||||||
|
window::{Fullscreen, Window},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let builder = Window::builder().with_title("A fantastic window!");
|
||||||
|
#[cfg(wasm_platform)]
|
||||||
|
let builder = {
|
||||||
|
use winit::platform::web::WindowBuilderExtWebSys;
|
||||||
|
builder.with_append(true)
|
||||||
|
};
|
||||||
|
let window = builder.build(&event_loop).unwrap();
|
||||||
|
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
let log_list = wasm::insert_canvas_and_create_log_list(&window);
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
wasm::log_event(&log_list, &event);
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => elwt.exit(),
|
||||||
|
Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id,
|
||||||
|
event:
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key: Key::Character(c),
|
||||||
|
state: ElementState::Released,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
},
|
||||||
|
} if window_id == window.id() && c == "f" => {
|
||||||
|
if window.fullscreen().is_some() {
|
||||||
|
window.set_fullscreen(None);
|
||||||
|
} else {
|
||||||
|
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
mod wasm {
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
|
use softbuffer::{Surface, SurfaceExtWeb};
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[wasm_bindgen(start)]
|
||||||
|
pub fn run() {
|
||||||
|
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||||
|
|
||||||
|
#[allow(clippy::main_recursion)]
|
||||||
|
let _ = super::main();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_canvas_and_create_log_list(window: &Window) -> web_sys::Element {
|
||||||
|
use winit::platform::web::WindowExtWebSys;
|
||||||
|
|
||||||
|
let canvas = window.canvas().unwrap();
|
||||||
|
let mut surface = Surface::from_canvas(canvas.clone()).unwrap();
|
||||||
|
surface
|
||||||
|
.resize(
|
||||||
|
NonZeroU32::new(canvas.width()).unwrap(),
|
||||||
|
NonZeroU32::new(canvas.height()).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut buffer = surface.buffer_mut().unwrap();
|
||||||
|
buffer.fill(0xFFF0000);
|
||||||
|
buffer.present().unwrap();
|
||||||
|
|
||||||
|
let window = web_sys::window().unwrap();
|
||||||
|
let document = window.document().unwrap();
|
||||||
|
let body = document.body().unwrap();
|
||||||
|
|
||||||
|
let style = &canvas.style();
|
||||||
|
style.set_property("margin", "50px").unwrap();
|
||||||
|
// Use to test interactions with border and padding.
|
||||||
|
//style.set_property("border", "50px solid black").unwrap();
|
||||||
|
//style.set_property("padding", "50px").unwrap();
|
||||||
|
|
||||||
|
let log_header = document.create_element("h2").unwrap();
|
||||||
|
log_header.set_text_content(Some("Event Log"));
|
||||||
|
body.append_child(&log_header).unwrap();
|
||||||
|
|
||||||
|
let log_list = document.create_element("ul").unwrap();
|
||||||
|
body.append_child(&log_list).unwrap();
|
||||||
|
log_list
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_event(log_list: &web_sys::Element, event: &Event<()>) {
|
||||||
|
log::debug!("{:?}", event);
|
||||||
|
|
||||||
|
// Getting access to browser logs requires a lot of setup on mobile devices.
|
||||||
|
// So we implement this basic logging system into the page to give developers an easy alternative.
|
||||||
|
// As a bonus its also kind of handy on desktop.
|
||||||
|
let event = match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
..
|
||||||
|
} => None,
|
||||||
|
Event::WindowEvent { event, .. } => Some(format!("{event:?}")),
|
||||||
|
Event::Resumed | Event::Suspended => Some(format!("{event:?}")),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(event) = event {
|
||||||
|
let window = web_sys::window().unwrap();
|
||||||
|
let document = window.document().unwrap();
|
||||||
|
let log = document.create_element("li").unwrap();
|
||||||
|
|
||||||
|
let date = js_sys::Date::new_0();
|
||||||
|
log.set_text_content(Some(&format!(
|
||||||
|
"{:02}:{:02}:{:02}.{:03}: {event}",
|
||||||
|
date.get_hours(),
|
||||||
|
date.get_minutes(),
|
||||||
|
date.get_seconds(),
|
||||||
|
date.get_milliseconds(),
|
||||||
|
)));
|
||||||
|
|
||||||
|
log_list
|
||||||
|
.insert_before(&log, log_list.first_child().as_ref())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
102
examples/web_aspect_ratio.rs
Normal file
102
examples/web_aspect_ratio.rs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#![allow(clippy::disallowed_methods)]
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
println!("This example must be run with cargo run-wasm --example web_aspect_ratio")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(web_platform)]
|
||||||
|
mod wasm {
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
use web_sys::HtmlCanvasElement;
|
||||||
|
use winit::{
|
||||||
|
dpi::PhysicalSize,
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
platform::web::WindowBuilderExtWebSys,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
const EXPLANATION: &str = "
|
||||||
|
This example draws a circle in the middle of a 4/1 aspect ratio canvas which acts as a useful demonstration of winit's resize handling on web.
|
||||||
|
Even when the browser window is resized or aspect-ratio of the canvas changed the circle should always:
|
||||||
|
* Fill the entire width or height of the canvas (whichever is smaller) without exceeding it.
|
||||||
|
* Be perfectly round
|
||||||
|
* Not be blurry or pixelated (there is no antialiasing so you may still see jagged edges depending on the DPI of your monitor)
|
||||||
|
|
||||||
|
Currently winit does not handle resizes on web so the circle is rendered incorrectly.
|
||||||
|
This example demonstrates the desired future functionality which will possibly be provided by https://github.com/rust-windowing/winit/pull/2074
|
||||||
|
";
|
||||||
|
|
||||||
|
#[wasm_bindgen(start)]
|
||||||
|
pub fn run() {
|
||||||
|
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
// When running in a non-wasm environment this would set the window size to 100x100.
|
||||||
|
// However in this example it just sets a default initial size of 100x100 that is immediately overwritten due to the layout + styling of the page.
|
||||||
|
.with_inner_size(PhysicalSize::new(100, 100))
|
||||||
|
.with_append(true)
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let canvas = create_canvas(&window);
|
||||||
|
|
||||||
|
// Render once with the size info we currently have
|
||||||
|
render_circle(&canvas, window.inner_size());
|
||||||
|
|
||||||
|
let _ = event_loop.run(move |event, _| match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::Resized(resize),
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => {
|
||||||
|
render_circle(&canvas, resize);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_canvas(window: &Window) -> HtmlCanvasElement {
|
||||||
|
use winit::platform::web::WindowExtWebSys;
|
||||||
|
|
||||||
|
let web_window = web_sys::window().unwrap();
|
||||||
|
let document = web_window.document().unwrap();
|
||||||
|
let body = document.body().unwrap();
|
||||||
|
|
||||||
|
// Set a background color for the canvas to make it easier to tell the where the canvas is for debugging purposes.
|
||||||
|
let canvas = window.canvas().unwrap();
|
||||||
|
canvas
|
||||||
|
.style()
|
||||||
|
.set_css_text("display: block; background-color: crimson; margin: auto; width: 50%; aspect-ratio: 4 / 1;");
|
||||||
|
|
||||||
|
let explanation = document.create_element("pre").unwrap();
|
||||||
|
explanation.set_text_content(Some(EXPLANATION));
|
||||||
|
body.append_child(&explanation).unwrap();
|
||||||
|
|
||||||
|
canvas
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_circle(canvas: &HtmlCanvasElement, size: PhysicalSize<u32>) {
|
||||||
|
log::info!("rendering circle with canvas size: {:?}", size);
|
||||||
|
let context = canvas
|
||||||
|
.get_context("2d")
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into::<web_sys::CanvasRenderingContext2d>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
context.begin_path();
|
||||||
|
context
|
||||||
|
.arc(
|
||||||
|
size.width as f64 / 2.0,
|
||||||
|
size.height as f64 / 2.0,
|
||||||
|
size.width.min(size.height) as f64 / 2.0,
|
||||||
|
0.0,
|
||||||
|
std::f64::consts::PI * 2.0,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
context.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
1001
examples/window.rs
1001
examples/window.rs
File diff suppressed because it is too large
Load Diff
68
examples/window_buttons.rs
Normal file
68
examples/window_buttons.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
// This example is used by developers to test various window functions.
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
dpi::LogicalSize,
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::{DeviceEvents, EventLoop},
|
||||||
|
keyboard::Key,
|
||||||
|
window::{Window, WindowButtons},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.with_inner_size(LogicalSize::new(300.0, 300.0))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
eprintln!("Window Button keys:");
|
||||||
|
eprintln!(" (F) Toggle close button");
|
||||||
|
eprintln!(" (G) Toggle maximize button");
|
||||||
|
eprintln!(" (H) Toggle minimize button");
|
||||||
|
|
||||||
|
event_loop.listen_device_events(DeviceEvents::Always);
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { window_id, event } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key: key,
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => match key.as_ref() {
|
||||||
|
Key::Character("F" | "f") => {
|
||||||
|
let buttons = window.enabled_buttons();
|
||||||
|
window.set_enabled_buttons(buttons ^ WindowButtons::CLOSE);
|
||||||
|
}
|
||||||
|
Key::Character("G" | "g") => {
|
||||||
|
let buttons = window.enabled_buttons();
|
||||||
|
window.set_enabled_buttons(buttons ^ WindowButtons::MAXIMIZE);
|
||||||
|
}
|
||||||
|
Key::Character("H" | "h") => {
|
||||||
|
let buttons = window.enabled_buttons();
|
||||||
|
window.set_enabled_buttons(buttons ^ WindowButtons::MINIMIZE);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
WindowEvent::CloseRequested if window_id == window.id() => elwt.exit(),
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
137
examples/window_debug.rs
Normal file
137
examples/window_debug.rs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
// This example is used by developers to test various window functions.
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
dpi::{LogicalSize, PhysicalSize},
|
||||||
|
event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent},
|
||||||
|
event_loop::{DeviceEvents, EventLoop},
|
||||||
|
keyboard::{Key, KeyCode, PhysicalKey},
|
||||||
|
window::{Fullscreen, Window},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.with_inner_size(LogicalSize::new(100.0, 100.0))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
eprintln!("debugging keys:");
|
||||||
|
eprintln!(" (E) Enter exclusive fullscreen");
|
||||||
|
eprintln!(" (F) Toggle borderless fullscreen");
|
||||||
|
eprintln!(" (P) Toggle borderless fullscreen on system's preffered monitor");
|
||||||
|
eprintln!(" (M) Toggle minimized");
|
||||||
|
eprintln!(" (Q) Quit event loop");
|
||||||
|
eprintln!(" (V) Toggle visibility");
|
||||||
|
eprintln!(" (X) Toggle maximized");
|
||||||
|
|
||||||
|
let mut minimized = false;
|
||||||
|
let mut visible = true;
|
||||||
|
|
||||||
|
event_loop.listen_device_events(DeviceEvents::Always);
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
match event {
|
||||||
|
// This used to use the virtual key, but the new API
|
||||||
|
// only provides the `physical_key` (`Code`).
|
||||||
|
Event::DeviceEvent {
|
||||||
|
event:
|
||||||
|
DeviceEvent::Key(RawKeyEvent {
|
||||||
|
physical_key,
|
||||||
|
state: ElementState::Released,
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
..
|
||||||
|
} => match physical_key {
|
||||||
|
PhysicalKey::Code(KeyCode::KeyM) => {
|
||||||
|
if minimized {
|
||||||
|
minimized = !minimized;
|
||||||
|
window.set_minimized(minimized);
|
||||||
|
window.focus_window();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PhysicalKey::Code(KeyCode::KeyV) => {
|
||||||
|
if !visible {
|
||||||
|
visible = !visible;
|
||||||
|
window.set_visible(visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::WindowEvent { window_id, event } => match event {
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key: Key::Character(key_str),
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => match key_str.as_ref() {
|
||||||
|
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||||
|
// See the `key_binding` example
|
||||||
|
"e" => {
|
||||||
|
fn area(size: PhysicalSize<u32>) -> u32 {
|
||||||
|
size.width * size.height
|
||||||
|
}
|
||||||
|
|
||||||
|
let monitor = window.current_monitor().unwrap();
|
||||||
|
if let Some(mode) = monitor
|
||||||
|
.video_modes()
|
||||||
|
.max_by(|a, b| area(a.size()).cmp(&area(b.size())))
|
||||||
|
{
|
||||||
|
window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
|
||||||
|
} else {
|
||||||
|
eprintln!("no video modes available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"f" => {
|
||||||
|
if window.fullscreen().is_some() {
|
||||||
|
window.set_fullscreen(None);
|
||||||
|
} else {
|
||||||
|
let monitor = window.current_monitor();
|
||||||
|
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"p" => {
|
||||||
|
if window.fullscreen().is_some() {
|
||||||
|
window.set_fullscreen(None);
|
||||||
|
} else {
|
||||||
|
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"m" => {
|
||||||
|
minimized = !minimized;
|
||||||
|
window.set_minimized(minimized);
|
||||||
|
}
|
||||||
|
"q" => {
|
||||||
|
elwt.exit();
|
||||||
|
}
|
||||||
|
"v" => {
|
||||||
|
visible = !visible;
|
||||||
|
window.set_visible(visible);
|
||||||
|
}
|
||||||
|
"x" => {
|
||||||
|
let is_maximized = window.is_maximized();
|
||||||
|
window.set_maximized(!is_maximized);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
WindowEvent::CloseRequested if window_id == window.id() => elwt.exit(),
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
149
examples/window_drag_resize.rs
Normal file
149
examples/window_drag_resize.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
//! Demonstrates capability to create in-app draggable regions for client-side decoration support.
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::Key,
|
||||||
|
window::{CursorIcon, ResizeDirection, Window},
|
||||||
|
};
|
||||||
|
|
||||||
|
const BORDER: f64 = 8.0;
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0))
|
||||||
|
.with_min_inner_size(winit::dpi::LogicalSize::new(400.0, 200.0))
|
||||||
|
.with_decorations(false)
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut border = false;
|
||||||
|
let mut cursor_location = None;
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| match event {
|
||||||
|
Event::NewEvents(StartCause::Init) => {
|
||||||
|
eprintln!("Press 'B' to toggle borderless")
|
||||||
|
}
|
||||||
|
Event::WindowEvent { event, .. } => match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
|
if !window.is_decorated() {
|
||||||
|
let new_location =
|
||||||
|
cursor_resize_direction(window.inner_size(), position, BORDER);
|
||||||
|
|
||||||
|
if new_location != cursor_location {
|
||||||
|
cursor_location = new_location;
|
||||||
|
window.set_cursor(cursor_direction_icon(cursor_location))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowEvent::MouseInput {
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
button: MouseButton::Left,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if let Some(dir) = cursor_location {
|
||||||
|
let _res = window.drag_resize_window(dir);
|
||||||
|
} else if !window.is_decorated() {
|
||||||
|
let _res = window.drag_window();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
state: ElementState::Released,
|
||||||
|
logical_key: Key::Character(c),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} if matches!(c.as_ref(), "B" | "b") => {
|
||||||
|
border = !border;
|
||||||
|
window.set_decorations(border);
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cursor_direction_icon(resize_direction: Option<ResizeDirection>) -> CursorIcon {
|
||||||
|
match resize_direction {
|
||||||
|
Some(resize_direction) => match resize_direction {
|
||||||
|
ResizeDirection::East => CursorIcon::EResize,
|
||||||
|
ResizeDirection::North => CursorIcon::NResize,
|
||||||
|
ResizeDirection::NorthEast => CursorIcon::NeResize,
|
||||||
|
ResizeDirection::NorthWest => CursorIcon::NwResize,
|
||||||
|
ResizeDirection::South => CursorIcon::SResize,
|
||||||
|
ResizeDirection::SouthEast => CursorIcon::SeResize,
|
||||||
|
ResizeDirection::SouthWest => CursorIcon::SwResize,
|
||||||
|
ResizeDirection::West => CursorIcon::WResize,
|
||||||
|
},
|
||||||
|
None => CursorIcon::Default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cursor_resize_direction(
|
||||||
|
win_size: winit::dpi::PhysicalSize<u32>,
|
||||||
|
position: winit::dpi::PhysicalPosition<f64>,
|
||||||
|
border_size: f64,
|
||||||
|
) -> Option<ResizeDirection> {
|
||||||
|
enum XDirection {
|
||||||
|
West,
|
||||||
|
East,
|
||||||
|
Default,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum YDirection {
|
||||||
|
North,
|
||||||
|
South,
|
||||||
|
Default,
|
||||||
|
}
|
||||||
|
|
||||||
|
let xdir = if position.x < border_size {
|
||||||
|
XDirection::West
|
||||||
|
} else if position.x > (win_size.width as f64 - border_size) {
|
||||||
|
XDirection::East
|
||||||
|
} else {
|
||||||
|
XDirection::Default
|
||||||
|
};
|
||||||
|
|
||||||
|
let ydir = if position.y < border_size {
|
||||||
|
YDirection::North
|
||||||
|
} else if position.y > (win_size.height as f64 - border_size) {
|
||||||
|
YDirection::South
|
||||||
|
} else {
|
||||||
|
YDirection::Default
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(match xdir {
|
||||||
|
XDirection::West => match ydir {
|
||||||
|
YDirection::North => ResizeDirection::NorthWest,
|
||||||
|
YDirection::South => ResizeDirection::SouthWest,
|
||||||
|
YDirection::Default => ResizeDirection::West,
|
||||||
|
},
|
||||||
|
|
||||||
|
XDirection::East => match ydir {
|
||||||
|
YDirection::North => ResizeDirection::NorthEast,
|
||||||
|
YDirection::South => ResizeDirection::SouthEast,
|
||||||
|
YDirection::Default => ResizeDirection::East,
|
||||||
|
},
|
||||||
|
|
||||||
|
XDirection::Default => match ydir {
|
||||||
|
YDirection::North => ResizeDirection::North,
|
||||||
|
YDirection::South => ResizeDirection::South,
|
||||||
|
YDirection::Default => return None,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
60
examples/window_icon.rs
Normal file
60
examples/window_icon.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::{Icon, Window},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
// since it seems to work well enough in most cases. Be careful about going too high, or
|
||||||
|
// you'll be bitten by the low-quality downscaling built into the WM.
|
||||||
|
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/icon.png");
|
||||||
|
|
||||||
|
let icon = load_icon(Path::new(path));
|
||||||
|
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("An iconic window!")
|
||||||
|
// At present, this only does anything on Windows and X11, so if you want to save load
|
||||||
|
// time, you can put icon loading behind a function that returns `None` on other platforms.
|
||||||
|
.with_window_icon(Some(icon))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::DroppedFile(path) => {
|
||||||
|
window.set_window_icon(Some(load_icon(&path)));
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => fill::fill_window(&window),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_icon(path: &Path) -> Icon {
|
||||||
|
let (icon_rgba, icon_width, icon_height) = {
|
||||||
|
let image = image::open(path)
|
||||||
|
.expect("Failed to open icon path")
|
||||||
|
.into_rgba8();
|
||||||
|
let (width, height) = image.dimensions();
|
||||||
|
let rgba = image.into_raw();
|
||||||
|
(rgba, width, height)
|
||||||
|
};
|
||||||
|
Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
|
||||||
|
}
|
||||||
93
examples/window_on_demand.rs
Normal file
93
examples/window_on_demand.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
// Limit this example to only compatible platforms.
|
||||||
|
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
|
||||||
|
use winit::{
|
||||||
|
error::EventLoopError,
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
platform::run_on_demand::EventLoopExtRunOnDemand,
|
||||||
|
window::{Window, WindowId},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct App {
|
||||||
|
window_id: Option<WindowId>,
|
||||||
|
window: Option<Window>,
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let mut event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
fn run_app(event_loop: &mut EventLoop<()>, idx: usize) -> Result<(), EventLoopError> {
|
||||||
|
let mut app = App::default();
|
||||||
|
|
||||||
|
event_loop.run_on_demand(move |event, elwt| {
|
||||||
|
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;
|
||||||
|
elwt.exit();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
} else if let Event::Resumed = event {
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("Fantastic window number one!")
|
||||||
|
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||||
|
.build(elwt)
|
||||||
|
.unwrap();
|
||||||
|
app.window_id = Some(window.id());
|
||||||
|
app.window = Some(window);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
run_app(&mut event_loop, 1)?;
|
||||||
|
|
||||||
|
println!("--------------------------------------------------------- Finished first loop");
|
||||||
|
println!("--------------------------------------------------------- Waiting 5 seconds");
|
||||||
|
std::thread::sleep(Duration::from_secs(5));
|
||||||
|
|
||||||
|
let ret = run_app(&mut event_loop, 2);
|
||||||
|
println!("--------------------------------------------------------- Finished second loop");
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))]
|
||||||
|
fn main() {
|
||||||
|
println!("This example is not supported on this platform");
|
||||||
|
}
|
||||||
72
examples/window_option_as_alt.rs
Normal file
72
examples/window_option_as_alt.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use winit::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use winit::{
|
||||||
|
event::ElementState,
|
||||||
|
event::{Event, MouseButton, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
/// Prints the keyboard events characters received when option_is_alt is true versus false.
|
||||||
|
/// A left mouse click will toggle option_is_alt.
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
window.set_ime_allowed(true);
|
||||||
|
|
||||||
|
let mut option_as_alt = window.option_as_alt();
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => elwt.exit(),
|
||||||
|
Event::WindowEvent { event, .. } => match event {
|
||||||
|
WindowEvent::MouseInput {
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
button: MouseButton::Left,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
option_as_alt = match option_as_alt {
|
||||||
|
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
|
||||||
|
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
|
||||||
|
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
|
||||||
|
OptionAsAlt::Both => OptionAsAlt::None,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Received Mouse click, toggling option_as_alt to: {option_as_alt:?}");
|
||||||
|
window.set_option_as_alt(option_as_alt);
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput { .. } => println!("KeyboardInput: {event:?}"),
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
fn main() {
|
||||||
|
println!("This example is only supported on MacOS");
|
||||||
|
}
|
||||||
74
examples/window_pump_events.rs
Normal file
74
examples/window_pump_events.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
// Limit this example to only compatible platforms.
|
||||||
|
#[cfg(any(
|
||||||
|
windows_platform,
|
||||||
|
macos_platform,
|
||||||
|
x11_platform,
|
||||||
|
wayland_platform,
|
||||||
|
android_platform,
|
||||||
|
))]
|
||||||
|
fn main() -> std::process::ExitCode {
|
||||||
|
use std::{process::ExitCode, thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
let mut event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
'main: loop {
|
||||||
|
let timeout = Some(Duration::ZERO);
|
||||||
|
let status = event_loop.pump_events(timeout, |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, .. } = &event {
|
||||||
|
// Print only Window events to reduce noise
|
||||||
|
println!("{event:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => elwt.exit(),
|
||||||
|
Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let PumpStatus::Exit(exit_code) = status {
|
||||||
|
break 'main ExitCode::from(exit_code as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep for 1/60 second to simulate application work
|
||||||
|
//
|
||||||
|
// Since `pump_events` doesn't block it will be important to
|
||||||
|
// throttle the loop in the app somehow.
|
||||||
|
println!("Update()");
|
||||||
|
sleep(Duration::from_millis(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(ios_platform, web_platform, orbital_platform))]
|
||||||
|
fn main() {
|
||||||
|
println!("This platform doesn't support pump_events.");
|
||||||
|
}
|
||||||
52
examples/window_resize_increments.rs
Normal file
52
examples/window_resize_increments.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use log::debug;
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
dpi::LogicalSize,
|
||||||
|
event::{ElementState, Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::NamedKey,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_title("A fantastic window!")
|
||||||
|
.with_inner_size(LogicalSize::new(128.0, 128.0))
|
||||||
|
.with_resize_increments(LogicalSize::new(25.0, 25.0))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut has_increments = true;
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| match event {
|
||||||
|
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
|
||||||
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
|
WindowEvent::KeyboardInput { event, .. }
|
||||||
|
if event.logical_key == NamedKey::Space
|
||||||
|
&& event.state == ElementState::Released =>
|
||||||
|
{
|
||||||
|
has_increments = !has_increments;
|
||||||
|
|
||||||
|
let new_increments = match window.resize_increments() {
|
||||||
|
Some(_) => None,
|
||||||
|
None => Some(LogicalSize::new(25.0, 25.0)),
|
||||||
|
};
|
||||||
|
debug!("Had increments: {}", new_increments.is_none());
|
||||||
|
window.set_resize_increments(new_increments);
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
fill::fill_window(&window);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::AboutToWait => window.request_redraw(),
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
})
|
||||||
|
}
|
||||||
107
examples/window_tabbing.rs
Normal file
107
examples/window_tabbing.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use std::{collections::HashMap, num::NonZeroUsize};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
keyboard::{Key, NamedKey},
|
||||||
|
platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS},
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[path = "util/fill.rs"]
|
||||||
|
mod fill;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
SimpleLogger::new().init().unwrap();
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let mut windows = HashMap::new();
|
||||||
|
let window = Window::new(&event_loop).unwrap();
|
||||||
|
println!("Opened a new window: {:?}", window.id());
|
||||||
|
windows.insert(window.id(), window);
|
||||||
|
|
||||||
|
println!("Press N to open a new window.");
|
||||||
|
|
||||||
|
event_loop.run(move |event, elwt| {
|
||||||
|
if let Event::WindowEvent { event, window_id } = event {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
println!("Window {window_id:?} has received the signal to close");
|
||||||
|
|
||||||
|
// This drops the window, causing it to close.
|
||||||
|
windows.remove(&window_id);
|
||||||
|
|
||||||
|
if windows.is_empty() {
|
||||||
|
elwt.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::Resized(_) => {
|
||||||
|
if let Some(window) = windows.get(&window_id) {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
logical_key,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
is_synthetic: false,
|
||||||
|
..
|
||||||
|
} => match logical_key.as_ref() {
|
||||||
|
Key::Character("t") => {
|
||||||
|
let tabbing_id = windows.get(&window_id).unwrap().tabbing_identifier();
|
||||||
|
let window = Window::builder()
|
||||||
|
.with_tabbing_identifier(&tabbing_id)
|
||||||
|
.build(elwt)
|
||||||
|
.unwrap();
|
||||||
|
println!("Added a new tab: {:?}", window.id());
|
||||||
|
windows.insert(window.id(), window);
|
||||||
|
}
|
||||||
|
Key::Character("w") => {
|
||||||
|
let _ = windows.remove(&window_id);
|
||||||
|
}
|
||||||
|
Key::Named(NamedKey::ArrowRight) => {
|
||||||
|
windows.get(&window_id).unwrap().select_next_tab();
|
||||||
|
}
|
||||||
|
Key::Named(NamedKey::ArrowLeft) => {
|
||||||
|
windows.get(&window_id).unwrap().select_previous_tab();
|
||||||
|
}
|
||||||
|
Key::Character(ch) => {
|
||||||
|
if let Ok(index) = ch.parse::<NonZeroUsize>() {
|
||||||
|
let index = index.get();
|
||||||
|
// Select the last tab when pressing `9`.
|
||||||
|
let window = windows.get(&window_id).unwrap();
|
||||||
|
if index == 9 {
|
||||||
|
window.select_tab_at_index(window.num_tabs() - 1)
|
||||||
|
} else {
|
||||||
|
window.select_tab_at_index(index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
if let Some(window) = windows.get(&window_id) {
|
||||||
|
fill::fill_window(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
fn main() {
|
||||||
|
println!("This example is only supported on MacOS");
|
||||||
|
}
|
||||||
@@ -1,72 +1,70 @@
|
|||||||
//! A demonstration of embedding a winit window in an existing X11 application.
|
//! A demonstration of embedding a winit window in an existing X11 application.
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
#[path = "util/fill.rs"]
|
||||||
use winit::application::ApplicationHandler;
|
mod fill;
|
||||||
use winit::event::WindowEvent;
|
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
|
||||||
use winit::platform::x11::WindowAttributesExtX11;
|
|
||||||
use winit::window::{Window, WindowId};
|
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
#[cfg(x11_platform)]
|
||||||
mod fill;
|
mod imple {
|
||||||
|
use super::fill;
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
platform::x11::WindowBuilderExtX11,
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct XEmbedDemo {
|
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
parent_window_id: u32,
|
// First argument should be a 32-bit X11 window ID.
|
||||||
window: Option<Window>,
|
let parent_window_id = std::env::args()
|
||||||
}
|
.nth(1)
|
||||||
|
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
|
||||||
|
.parse::<u32>()?;
|
||||||
|
|
||||||
impl ApplicationHandler for XEmbedDemo {
|
SimpleLogger::new().init().unwrap();
|
||||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
let event_loop = EventLoop::new()?;
|
||||||
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());
|
let window = Window::builder()
|
||||||
}
|
.with_title("An embedded window!")
|
||||||
|
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||||
|
.with_embed_parent_window(parent_window_id)
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
fn window_event(
|
event_loop.run(move |event, elwt| {
|
||||||
&mut self,
|
|
||||||
event_loop: &ActiveEventLoop,
|
|
||||||
_window_id: WindowId,
|
|
||||||
event: WindowEvent,
|
|
||||||
) {
|
|
||||||
let window = self.window.as_ref().unwrap();
|
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CloseRequested => event_loop.exit(),
|
Event::WindowEvent {
|
||||||
WindowEvent::RedrawRequested => {
|
event: WindowEvent::CloseRequested,
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => elwt.exit(),
|
||||||
|
Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// Notify the windowing system that we'll be presenting to the window.
|
||||||
window.pre_present_notify();
|
window.pre_present_notify();
|
||||||
fill::fill_window(window);
|
fill::fill_window(&window);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
})?;
|
||||||
|
|
||||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
Ok(())
|
||||||
self.window.as_ref().unwrap().request_redraw();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// First argument should be a 32-bit X11 window ID.
|
|
||||||
let parent_window_id = std::env::args()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
|
|
||||||
.parse::<u32>()?;
|
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
|
||||||
let event_loop = EventLoop::new()?;
|
|
||||||
|
|
||||||
let mut app = XEmbedDemo {
|
|
||||||
parent_window_id,
|
|
||||||
window: None,
|
|
||||||
};
|
|
||||||
event_loop.run_app(&mut app).map_err(Into::into)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(x11_platform))]
|
#[cfg(not(x11_platform))]
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
mod imple {
|
||||||
println!("This example is only supported on X11 platforms.");
|
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Ok(())
|
println!("This example is only supported on X11 platforms.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
imple::entry()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
[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
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
//! 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()
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
[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
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
// 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();
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
// 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(())
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
// 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;
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
//! 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")
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
// 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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
[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
|
|
||||||
@@ -1,443 +0,0 @@
|
|||||||
//! 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()
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
//! 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())
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
//! 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
//! 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "run-wasm"
|
name = "run-wasm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version.workspace = true
|
edition = "2021"
|
||||||
repository.workspace = true
|
|
||||||
license.workspace = true
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
edition.workspace = true
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cargo-run-wasm = "0.2.0"
|
cargo-run-wasm = "0.2.0"
|
||||||
|
|||||||
@@ -1,221 +0,0 @@
|
|||||||
//! 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
//! # Changelog and migrations
|
|
||||||
//!
|
|
||||||
//! All notable changes to this project will be documented in this module,
|
|
||||||
//! along with migration instructions for larger changes.
|
|
||||||
//!
|
|
||||||
// Put the current entry at the top of this page, for discoverability.
|
|
||||||
// See `.cargo/config.toml` for details about `unreleased_changelogs`.
|
|
||||||
#![cfg_attr(unreleased_changelogs, doc = include_str!("unreleased.md"))]
|
|
||||||
#![cfg_attr(not(unreleased_changelogs), doc = include_str!("v0.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 {}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
## 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.
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
## 0.10.1
|
|
||||||
|
|
||||||
_Yanked_
|
|
||||||
|
|
||||||
## 0.10.0
|
|
||||||
|
|
||||||
- Add support for `Touch` for emscripten backend.
|
|
||||||
- Added support for `DroppedFile`, `HoveredFile`, and `HoveredFileCancelled` to X11 backend.
|
|
||||||
- **Breaking:** `unix::WindowExt` no longer returns pointers for things that aren't actually pointers; `get_xlib_window` now returns `Option<std::os::raw::c_ulong>` and `get_xlib_screen_id` returns `Option<std::os::raw::c_int>`. Additionally, methods that previously returned `libc::c_void` have been changed to return `std::os::raw::c_void`, which are not interchangeable types, so users wanting the former will need to explicitly cast.
|
|
||||||
- Added `set_decorations` method to `Window` to allow decorations to be toggled after the window is built. Presently only implemented on X11.
|
|
||||||
- Raised the minimum supported version of Rust to 1.20 on MacOS due to usage of associated constants in new versions of cocoa and core-graphics.
|
|
||||||
- Added `modifiers` field to `MouseInput`, `MouseWheel`, and `CursorMoved` events to track the modifiers state (`ModifiersState`).
|
|
||||||
- Fixed the emscripten backend to return the size of the canvas instead of the size of the window.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
## 0.11.3
|
|
||||||
|
|
||||||
- Added `set_min_dimensions` and `set_max_dimensions` methods to `Window`, and implemented on Windows, X11, Wayland, and OSX.
|
|
||||||
- On X11, dropping a `Window` actually closes it now, and clicking the window's × button (or otherwise having the WM signal to close it) will result in the window closing.
|
|
||||||
- Added `WindowBuilderExt` methods for macos: `with_titlebar_transparent`,
|
|
||||||
`with_title_hidden`, `with_titlebar_buttons_hidden`,
|
|
||||||
`with_fullsize_content_view`.
|
|
||||||
- Mapped X11 numpad keycodes (arrows, Home, End, PageUp, PageDown, Insert and Delete) to corresponding virtual keycodes
|
|
||||||
|
|
||||||
## 0.11.2
|
|
||||||
|
|
||||||
- Impl `Hash`, `PartialEq`, and `Eq` for `events::ModifiersState`.
|
|
||||||
- Implement `MonitorId::get_hidpi_factor` for MacOS.
|
|
||||||
- Added method `os::macos::MonitorIdExt::get_nsscreen() -> *mut c_void` that gets a `NSScreen` object matching the monitor ID.
|
|
||||||
- Send `Awakened` event on Android when event loop is woken up.
|
|
||||||
|
|
||||||
## 0.11.1
|
|
||||||
|
|
||||||
- Fixed windows not receiving mouse events when click-dragging the mouse outside the client area of a window, on Windows platforms.
|
|
||||||
- Added method `os::android::EventsLoopExt:set_suspend_callback(Option<Box<Fn(bool) -> ()>>)` that allows glutin to register a callback when a suspend event happens
|
|
||||||
|
|
||||||
## 0.11.0
|
|
||||||
|
|
||||||
- Implement `MonitorId::get_dimensions` for Android.
|
|
||||||
- Added method `os::macos::WindowBuilderExt::with_movable_by_window_background(bool)` that allows to move a window without a titlebar - `with_decorations(false)`
|
|
||||||
- Implement `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Wayland.
|
|
||||||
- Added `Caret` as VirtualKeyCode and support OSX ^-Key with german input.
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
## 0.12.0
|
|
||||||
|
|
||||||
- Added subclass to macos windows so they can be made resizable even with no decorations.
|
|
||||||
- Dead keys now work properly on X11, no longer resulting in a panic.
|
|
||||||
- On X11, input method creation first tries to use the value from the user's `XMODIFIERS` environment variable, so application developers should no longer need to manually call `XSetLocaleModifiers`. If that fails, fallbacks are tried, which should prevent input method initialization from ever outright failing.
|
|
||||||
- Fixed thread safety issues with input methods on X11.
|
|
||||||
- Add support for `Touch` for win32 backend.
|
|
||||||
- Fixed `Window::get_inner_size` and friends to return the size in pixels instead of points when using HIDPI displays on OSX.
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
## 0.13.1
|
|
||||||
|
|
||||||
- Ensure necessary `x11-dl` version is used.
|
|
||||||
|
|
||||||
## 0.13.0
|
|
||||||
|
|
||||||
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for MacOS.
|
|
||||||
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Windows.
|
|
||||||
- On Windows, `WindowBuilder::with_fullscreen` no longer changing monitor display resolution.
|
|
||||||
- Overhauled X11 window geometry calculations. `get_position` and `set_position` are more universally accurate across different window managers, and `get_outer_size` actually works now.
|
|
||||||
- Fixed SIGSEGV/SIGILL crashes on macOS caused by stabilization of the `!` (never) type.
|
|
||||||
- Implement `WindowEvent::HiDPIFactorChanged` for macOS
|
|
||||||
- On X11, input methods now work completely out of the box, no longer requiring application developers to manually call `setlocale`. Additionally, when input methods are started, stopped, or restarted on the server end, it's correctly handled.
|
|
||||||
- Implemented `Refresh` event on Windows.
|
|
||||||
- Properly calculate the minimum and maximum window size on Windows, including window decorations.
|
|
||||||
- Map more `MouseCursor` variants to cursor icons on Windows.
|
|
||||||
- Corrected `get_position` on macOS to return outer frame position, not content area position.
|
|
||||||
- Corrected `set_position` on macOS to set outer frame position, not content area position.
|
|
||||||
- Added `get_inner_position` method to `Window`, which gets the position of the window's client area. This is implemented on all applicable platforms (all desktop platforms other than Wayland, where this isn't possible).
|
|
||||||
- **Breaking:** the `Closed` event has been replaced by `CloseRequested` and `Destroyed`. To migrate, you typically just need to replace all usages of `Closed` with `CloseRequested`; see example programs for more info. The exception is iOS, where `Closed` must be replaced by `Destroyed`.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
## 0.14.0
|
|
||||||
|
|
||||||
- Created the `Copy`, `Paste` and `Cut` `VirtualKeyCode`s and added support for them on X11 and Wayland
|
|
||||||
- Fix `.with_decorations(false)` in macOS
|
|
||||||
- On Mac, `NSWindow` and supporting objects might be alive long after they were `closed` which resulted in apps consuming more heap then needed. Mainly it was affecting multi window applications. Not expecting any user visible change of behaviour after the fix.
|
|
||||||
- Fix regression of Window platform extensions for macOS where `NSFullSizeContentViewWindowMask` was not being correctly applied to `.fullsize_content_view`.
|
|
||||||
- Corrected `get_position` on Windows to be relative to the screen rather than to the taskbar.
|
|
||||||
- Corrected `Moved` event on Windows to use position values equivalent to those returned by `get_position`. It previously supplied client area positions instead of window positions, and would additionally interpret negative values as being very large (around `u16::MAX`).
|
|
||||||
- Implemented `Moved` event on macOS.
|
|
||||||
- On X11, the `Moved` event correctly use window positions rather than client area positions. Additionally, a stray `Moved` that unconditionally accompanied `Resized` with the client area position relative to the parent has been eliminated; `Moved` is still received alongside `Resized`, but now only once and always correctly.
|
|
||||||
- On Windows, implemented all variants of `DeviceEvent` other than `Text`. Mouse `DeviceEvent`s are now received even if the window isn't in the foreground.
|
|
||||||
- `DeviceId` on Windows is no longer a unit struct, and now contains a `u32`. For `WindowEvent`s, this will always be 0, but on `DeviceEvent`s it will be the handle to that device. `DeviceIdExt::get_persistent_identifier` can be used to acquire a unique identifier for that device that persists across replugs/reboots/etc.
|
|
||||||
- Corrected `run_forever` on X11 to stop discarding `Awakened` events.
|
|
||||||
- Various safety and correctness improvements to the X11 backend internals.
|
|
||||||
- Fixed memory leak on X11 every time the mouse entered the window.
|
|
||||||
- On X11, drag and drop now works reliably in release mode.
|
|
||||||
- Added `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` to X11, allowing for more optional hints to be set.
|
|
||||||
- Rework of the wayland backend, migrating it to use [Smithay's Client Toolkit](https://github.com/Smithay/client-toolkit).
|
|
||||||
- Added `WindowBuilder::with_window_icon` and `Window::set_window_icon`, finally making it possible to set the window icon on Windows and X11. The `icon_loading` feature can be enabled to allow for icons to be easily loaded; see example program `window_icon.rs` for usage.
|
|
||||||
- Windows additionally has `WindowBuilderExt::with_taskbar_icon` and `WindowExt::set_taskbar_icon`.
|
|
||||||
- On Windows, fix panic when trying to call `set_fullscreen(None)` on a window that has not been fullscreened prior.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
## 0.15.1
|
|
||||||
|
|
||||||
- On X11, the `Moved` event is no longer sent when the window is resized without changing position.
|
|
||||||
- `MouseCursor` and `CursorState` now implement `Default`.
|
|
||||||
- `WindowBuilder::with_resizable` implemented for Windows, X11, Wayland, and macOS.
|
|
||||||
- `Window::set_resizable` implemented for Windows, X11, Wayland, and macOS.
|
|
||||||
- On X11, if the monitor's width or height in millimeters is reported as 0, the DPI is now 1.0 instead of +inf.
|
|
||||||
- On X11, the environment variable `WINIT_HIDPI_FACTOR` has been added for overriding DPI factor.
|
|
||||||
- On X11, enabling transparency no longer causes the window contents to flicker when resizing.
|
|
||||||
- On X11, `with_override_redirect` now actually enables override redirect.
|
|
||||||
- macOS now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead of `None` for both.
|
|
||||||
- On macOS, `VirtualKeyCode::RWin` and `VirtualKeyCode::LWin` are no longer switched.
|
|
||||||
- On macOS, windows without decorations can once again be resized.
|
|
||||||
- Fixed race conditions when creating an `EventsLoop` on X11, most commonly manifesting as `"[xcb] Unknown sequence number while processing queue"`.
|
|
||||||
- On macOS, `CursorMoved` and `MouseInput` events are only generated if they occurs within the window's client area.
|
|
||||||
- On macOS, resizing the window no longer generates a spurious `MouseInput` event.
|
|
||||||
|
|
||||||
## 0.15.0
|
|
||||||
|
|
||||||
- `Icon::to_cardinals` is no longer public, since it was never supposed to be.
|
|
||||||
- Wayland: improve diagnostics if initialization fails
|
|
||||||
- Fix some system event key doesn't work when focused, do not block keyevent forward to system on macOS
|
|
||||||
- On X11, the scroll wheel position is now correctly reset on i3 and other WMs that have the same quirk.
|
|
||||||
- On X11, `Window::get_current_monitor` now reliably returns the correct monitor.
|
|
||||||
- On X11, `Window::hidpi_factor` returns values from XRandR rather than the inaccurate values previously queried from the core protocol.
|
|
||||||
- On X11, the primary monitor is detected correctly even when using versions of XRandR less than 1.5.
|
|
||||||
- `MonitorId` now implements `Debug`.
|
|
||||||
- Fixed bug on macOS where using `with_decorations(false)` would cause `set_decorations(true)` to produce a transparent titlebar with no title.
|
|
||||||
- Implemented `MonitorId::get_position` on macOS.
|
|
||||||
- On macOS, `Window::get_current_monitor` now returns accurate values.
|
|
||||||
- Added `WindowBuilderExt::with_resize_increments` to macOS.
|
|
||||||
- **Breaking:** On X11, `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` now take `u32` values rather than `i32`.
|
|
||||||
- macOS keyboard handling has been overhauled, allowing for the use of dead keys, IME, etc. Right modifier keys are also no longer reported as being left.
|
|
||||||
- Added the `Window::set_ime_spot(x: i32, y: i32)` method, which is implemented on X11 and macOS.
|
|
||||||
- **Breaking**: `os::unix::WindowExt::send_xim_spot(x: i16, y: i16)` no longer exists. Switch to the new `Window::set_ime_spot(x: i32, y: i32)`, which has equivalent functionality.
|
|
||||||
- Fixed detection of `Pause` and `Scroll` keys on Windows.
|
|
||||||
- On Windows, alt-tabbing while the cursor is grabbed no longer makes it impossible to re-grab the cursor.
|
|
||||||
- On Windows, using `CursorState::Hide` when the cursor is grabbed now ungrabs the cursor first.
|
|
||||||
- Implemented `MouseCursor::NoneCursor` on Windows.
|
|
||||||
- Added `WindowBuilder::with_always_on_top` and `Window::set_always_on_top`. Implemented on Windows, macOS, and X11.
|
|
||||||
- On X11, `WindowBuilderExt` now has `with_class`, `with_override_redirect`, and `with_x11_window_type` to allow for more control over window creation. `WindowExt` additionally has `set_urgent`.
|
|
||||||
- More hints are set by default on X11, including `_NET_WM_PID` and `WM_CLIENT_MACHINE`. Note that prior to this, the `WM_CLASS` hint was automatically set to whatever value was passed to `with_title`. It's now set to the executable name to better conform to expectations and the specification; if this is undesirable, you must explicitly use `WindowBuilderExt::with_class`.
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
## 0.16.2
|
|
||||||
|
|
||||||
- On Windows, non-resizable windows now have the maximization button disabled. This is consistent with behavior on macOS and popular X11 WMs.
|
|
||||||
- Corrected incorrect `unreachable!` usage when guessing the DPI factor with no detected monitors.
|
|
||||||
|
|
||||||
## 0.16.1
|
|
||||||
|
|
||||||
- Added logging through `log`. Logging will become more extensive over time.
|
|
||||||
- On X11 and Windows, the window's DPI factor is guessed before creating the window. This _greatly_ cuts back on unsightly auto-resizing that would occur immediately after window creation.
|
|
||||||
- Fixed X11 backend compilation for environments where `c_char` is unsigned.
|
|
||||||
|
|
||||||
## 0.16.0
|
|
||||||
|
|
||||||
- Windows additionally has `WindowBuilderExt::with_no_redirection_bitmap`.
|
|
||||||
- **Breaking:** Removed `VirtualKeyCode::LMenu` and `VirtualKeyCode::RMenu`; Windows now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead.
|
|
||||||
- On X11, exiting fullscreen no longer leaves the window in the monitor's top left corner.
|
|
||||||
- **Breaking:** `Window::hidpi_factor` has been renamed to `Window::get_hidpi_factor` for better consistency. `WindowEvent::HiDPIFactorChanged` has been renamed to `WindowEvent::HiDpiFactorChanged`. DPI factors are always represented as `f64` instead of `f32` now.
|
|
||||||
- The Windows backend is now DPI aware. `WindowEvent::HiDpiFactorChanged` is implemented, and `MonitorId::get_hidpi_factor` and `Window::hidpi_factor` return accurate values.
|
|
||||||
- Implemented `WindowEvent::HiDpiFactorChanged` on X11.
|
|
||||||
- On macOS, `Window::set_cursor_position` is now relative to the client area.
|
|
||||||
- On macOS, setting the maximum and minimum dimensions now applies to the client area dimensions rather than to the window dimensions.
|
|
||||||
- On iOS, `MonitorId::get_dimensions` has been implemented and both `MonitorId::get_hidpi_factor` and `Window::get_hidpi_factor` return accurate values.
|
|
||||||
- On Emscripten, `MonitorId::get_hidpi_factor` now returns the same value as `Window::get_hidpi_factor` (it previously would always return 1.0).
|
|
||||||
- **Breaking:** The entire API for sizes, positions, etc. has changed. In the majority of cases, winit produces and consumes positions and sizes as `LogicalPosition` and `LogicalSize`, respectively. The notable exception is `MonitorId` methods, which deal in `PhysicalPosition` and `PhysicalSize`. See the documentation for specifics and explanations of the types. Additionally, winit automatically conserves logical size when the DPI factor changes.
|
|
||||||
- **Breaking:** All deprecated methods have been removed. For `Window::platform_display` and `Window::platform_window`, switch to the appropriate platform-specific `WindowExt` methods. For `Window::get_inner_size_points` and `Window::get_inner_size_pixels`, use the `LogicalSize` returned by `Window::get_inner_size` and convert as needed.
|
|
||||||
- HiDPI support for Wayland.
|
|
||||||
- `EventsLoop::get_available_monitors` and `EventsLoop::get_primary_monitor` now have identical counterparts on `Window`, so this information can be acquired without an `EventsLoop` borrow.
|
|
||||||
- `AvailableMonitorsIter` now implements `Debug`.
|
|
||||||
- Fixed quirk on macOS where certain keys would generate characters at twice the normal rate when held down.
|
|
||||||
- On X11, all event loops now share the same `XConnection`.
|
|
||||||
- **Breaking:** `Window::set_cursor_state` and `CursorState` enum removed in favor of the more composable `Window::grab_cursor` and `Window::hide_cursor`. As a result, grabbing the cursor no longer automatically hides it; you must call both methods to retain the old behavior on Windows and macOS. `Cursor::NoneCursor` has been removed, as it's no longer useful.
|
|
||||||
- **Breaking:** `Window::set_cursor_position` now returns `Result<(), String>`, thus allowing for `Box<Error>` conversion via `?`.
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
## 0.17.2
|
|
||||||
|
|
||||||
- On macOS, fix `<C-Tab>` so applications receive the event.
|
|
||||||
- On macOS, fix `<Cmd-{key}>` so applications receive the event.
|
|
||||||
- On Wayland, key press events will now be repeated.
|
|
||||||
|
|
||||||
## 0.17.1
|
|
||||||
|
|
||||||
- On X11, prevent a compilation failure in release mode for versions of Rust greater than or equal to 1.30.
|
|
||||||
- Fixed deadlock that broke fullscreen mode on Windows.
|
|
||||||
|
|
||||||
## 0.17.0
|
|
||||||
|
|
||||||
- Cocoa and core-graphics updates.
|
|
||||||
- Fixed thread-safety issues in several `Window` functions on Windows.
|
|
||||||
- On MacOS, the key state for modifiers key events is now properly set.
|
|
||||||
- On iOS, the view is now set correctly. This makes it possible to render things (instead of being stuck on a black screen), and touch events work again.
|
|
||||||
- Added NetBSD support.
|
|
||||||
- **Breaking:** On iOS, `UIView` is now the default root view. `WindowBuilderExt::with_root_view_class` can be used to set the root view objective-c class to `GLKView` (OpenGLES) or `MTKView` (Metal/MoltenVK).
|
|
||||||
- On iOS, the `UIApplication` is not started until `Window::new` is called.
|
|
||||||
- Fixed thread unsafety with cursor hiding on macOS.
|
|
||||||
- On iOS, fixed the size of the `JmpBuf` type used for `setjmp`/`longjmp` calls. Previously this was a buffer overflow on most architectures.
|
|
||||||
- On Windows, use cached window DPI instead of repeatedly querying the system. This fixes sporadic crashes on Windows 7.
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
## 0.18.1
|
|
||||||
|
|
||||||
- On macOS, fix `Yen` (JIS) so applications receive the event.
|
|
||||||
- On X11 with a tiling WM, fixed high CPU usage when moving windows across monitors.
|
|
||||||
- On X11, fixed panic caused by dropping the window before running the event loop.
|
|
||||||
- on macOS, added `WindowExt::set_simple_fullscreen` which does not require a separate space
|
|
||||||
- Introduce `WindowBuilderExt::with_app_id` to allow setting the application ID on Wayland.
|
|
||||||
- On Windows, catch panics in event loop child thread and forward them to the parent thread. This prevents an invocation of undefined behavior due to unwinding into foreign code.
|
|
||||||
- On Windows, fix issue where resizing or moving window combined with grabbing the cursor would freeze program.
|
|
||||||
- On Windows, fix issue where resizing or moving window would eat `Awakened` events.
|
|
||||||
- On Windows, exiting fullscreen after entering fullscreen with disabled decorations no longer shrinks window.
|
|
||||||
- On X11, fixed a segfault when using virtual monitors with XRandR.
|
|
||||||
- Derive `Ord` and `PartialOrd` for `VirtualKeyCode` enum.
|
|
||||||
- On Windows, fix issue where hovering or dropping a non file item would create a panic.
|
|
||||||
- On Wayland, fix resizing and DPI calculation when a `wl_output` is removed without sending a `leave` event to the `wl_surface`, such as disconnecting a monitor from a laptop.
|
|
||||||
- On Wayland, DPI calculation is handled by smithay-client-toolkit.
|
|
||||||
- On X11, `WindowBuilder::with_min_dimensions` and `WindowBuilder::with_max_dimensions` now correctly account for DPI.
|
|
||||||
- Added support for generating dummy `DeviceId`s and `WindowId`s to better support unit testing.
|
|
||||||
- On macOS, fixed unsoundness in drag-and-drop that could result in drops being rejected.
|
|
||||||
- On macOS, implemented `WindowEvent::Refresh`.
|
|
||||||
- On macOS, all `MouseCursor` variants are now implemented and the cursor will no longer reset after unfocusing.
|
|
||||||
- Removed minimum supported Rust version guarantee.
|
|
||||||
|
|
||||||
## 0.18.0
|
|
||||||
|
|
||||||
- **Breaking:** `image` crate upgraded to 0.20. This is exposed as part of the `icon_loading` API.
|
|
||||||
- On Wayland, pointer events will now provide the current modifiers state.
|
|
||||||
- On Wayland, titles will now be displayed in the window header decoration.
|
|
||||||
- On Wayland, key repetition is now ended when keyboard loses focus.
|
|
||||||
- On Wayland, windows will now use more stylish and modern client side decorations.
|
|
||||||
- On Wayland, windows will use server-side decorations when available.
|
|
||||||
- **Breaking:** Added support for F16-F24 keys (variants were added to the `VirtualKeyCode` enum).
|
|
||||||
- Fixed graphical glitches when resizing on Wayland.
|
|
||||||
- On Windows, fix freezes when performing certain actions after a window resize has been triggered. Reintroduces some visual artifacts when resizing.
|
|
||||||
- Updated window manager hints under X11 to v1.5 of [Extended Window Manager Hints](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html#idm140200472629520).
|
|
||||||
- Added `WindowBuilderExt::with_gtk_theme_variant` to X11-specific `WindowBuilder` functions.
|
|
||||||
- Fixed UTF8 handling bug in X11 `set_title` function.
|
|
||||||
- On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first.
|
|
||||||
- On Windows, the `HoveredFile` and `HoveredFileCancelled` events are now implemented.
|
|
||||||
- On Windows, fix `Window::set_maximized`.
|
|
||||||
- On Windows 10, fix transparency (#260).
|
|
||||||
- On macOS, fix modifiers during key repeat.
|
|
||||||
- Implemented the `Debug` trait for `Window`, `EventsLoop`, `EventsLoopProxy` and `WindowBuilder`.
|
|
||||||
- On X11, now a `Resized` event will always be generated after a DPI change to ensure the window's logical size is consistent with the new DPI.
|
|
||||||
- Added further clarifications to the DPI docs.
|
|
||||||
- On Linux, if neither X11 nor Wayland manage to initialize, the corresponding panic now consists of a single line only.
|
|
||||||
- Add optional `serde` feature with implementations of `Serialize`/`Deserialize` for DPI types and various event types.
|
|
||||||
- Add `PartialEq`, `Eq`, and `Hash` implementations on public types that could have them but were missing them.
|
|
||||||
- On X11, drag-and-drop receiving an unsupported drop type can no longer cause the WM to freeze.
|
|
||||||
- Fix issue whereby the OpenGL context would not appear at startup on macOS Mojave (#1069).
|
|
||||||
- **Breaking:** Removed `From<NSApplicationActivationPolicy>` impl from `ActivationPolicy` on macOS.
|
|
||||||
- On macOS, the application can request the user's attention with `WindowExt::request_user_attention`.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
## 0.19.1
|
|
||||||
|
|
||||||
- On Wayland, added a `get_wayland_display` function to `EventsLoopExt`.
|
|
||||||
- On Windows, fix `CursorMoved(0, 0)` getting dispatched on window focus.
|
|
||||||
- On macOS, fix command key event left and right reverse.
|
|
||||||
- On FreeBSD, NetBSD, and OpenBSD, fix build of X11 backend.
|
|
||||||
- On Linux, the numpad's add, subtract and divide keys are now mapped to the `Add`, `Subtract` and `Divide` virtual key codes
|
|
||||||
- On macOS, the numpad's subtract key has been added to the `Subtract` mapping
|
|
||||||
- On Wayland, the numpad's home, end, page up and page down keys are now mapped to the `Home`, `End`, `PageUp` and `PageDown` virtual key codes
|
|
||||||
- On Windows, fix icon not showing up in corner of window.
|
|
||||||
- On X11, change DPI scaling factor behavior. First, winit tries to read it from "Xft.dpi" XResource, and uses DPI calculation from xrandr dimensions as fallback behavior.
|
|
||||||
|
|
||||||
## 0.19.0
|
|
||||||
|
|
||||||
- On X11, we will use the faster `XRRGetScreenResourcesCurrent` function instead of `XRRGetScreenResources` when available.
|
|
||||||
- On macOS, fix keycodes being incorrect when using a non-US keyboard layout.
|
|
||||||
- On Wayland, fix `with_title()` not setting the windows title
|
|
||||||
- On Wayland, add `set_wayland_theme()` to control client decoration color theme
|
|
||||||
- Added serde serialization to `os::unix::XWindowType`.
|
|
||||||
- **Breaking:** Remove the `icon_loading` feature and the associated `image` dependency.
|
|
||||||
- On X11, make event loop thread safe by replacing XNextEvent with select(2) and XCheckIfEvent
|
|
||||||
- On Windows, fix malformed function pointer typecast that could invoke undefined behavior.
|
|
||||||
- Refactored Windows state/flag-setting code.
|
|
||||||
- On Windows, hiding the cursor no longer hides the cursor for all Winit windows - just the one `hide_cursor` was called on.
|
|
||||||
- On Windows, cursor grabs used to get perpetually canceled when the grabbing window lost focus. Now, cursor grabs automatically get re-initialized when the window regains focus and the mouse moves over the client area.
|
|
||||||
- On Windows, only vertical mouse wheel events were handled. Now, horizontal mouse wheel events are also handled.
|
|
||||||
- On Windows, ignore the AltGr key when populating the `ModifiersState` type.
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
## 0.20.0
|
|
||||||
|
|
||||||
- On X11, fix `ModifiersChanged` emitting incorrect modifier change events
|
|
||||||
- **Breaking**: Overhaul how Winit handles DPI:
|
|
||||||
- Window functions and events now return `PhysicalSize` instead of `LogicalSize`.
|
|
||||||
- Functions that take `Size` or `Position` types can now take either `Logical` or `Physical` types.
|
|
||||||
- `hidpi_factor` has been renamed to `scale_factor`.
|
|
||||||
- `HiDpiFactorChanged` has been renamed to `ScaleFactorChanged`, and lets you control how the OS
|
|
||||||
resizes the window in response to the change.
|
|
||||||
- On X11, deprecate `WINIT_HIDPI_FACTOR` environment variable in favor of `WINIT_X11_SCALE_FACTOR`.
|
|
||||||
- `Size` and `Position` types are now generic over their exact pixel type.
|
|
||||||
|
|
||||||
## 0.20.0-alpha6
|
|
||||||
|
|
||||||
- On macOS, fix `set_cursor_visible` hides cursor outside of window.
|
|
||||||
- On macOS, fix `CursorEntered` and `CursorLeft` events fired at old window size.
|
|
||||||
- On macOS, fix error when `set_fullscreen` is called during fullscreen transition.
|
|
||||||
- On all platforms except mobile and WASM, implement `Window::set_minimized`.
|
|
||||||
- On X11, fix `CursorEntered` event being generated for non-winit windows.
|
|
||||||
- On macOS, fix crash when starting maximized without decorations.
|
|
||||||
- On macOS, fix application not terminating on `run_return`.
|
|
||||||
- On Wayland, fix cursor icon updates on window borders when using CSD.
|
|
||||||
- On Wayland, under mutter(GNOME Wayland), fix CSD being behind the status bar, when starting window in maximized mode.
|
|
||||||
- On Windows, theme the title bar according to whether the system theme is "Light" or "Dark".
|
|
||||||
- Added `WindowEvent::ThemeChanged` variant to handle changes to the system theme. Currently only implemented on Windows.
|
|
||||||
- **Breaking**: Changes to the `RedrawRequested` event (#1041):
|
|
||||||
- `RedrawRequested` has been moved from `WindowEvent` to `Event`.
|
|
||||||
- `EventsCleared` has been renamed to `MainEventsCleared`.
|
|
||||||
- `RedrawRequested` is now issued only after `MainEventsCleared`.
|
|
||||||
- `RedrawEventsCleared` is issued after each set of `RedrawRequested` events.
|
|
||||||
- Implement synthetic window focus key events on Windows.
|
|
||||||
- **Breaking**: Change `ModifiersState` to a `bitflags` struct.
|
|
||||||
- On Windows, implement `VirtualKeyCode` translation for `LWin` and `RWin`.
|
|
||||||
- On Windows, fix closing the last opened window causing `DeviceEvent`s to stop getting emitted.
|
|
||||||
- On Windows, fix `Window::set_visible` not setting internal flags correctly. This resulted in some weird behavior.
|
|
||||||
- Add `DeviceEvent::ModifiersChanged`.
|
|
||||||
- Deprecate `modifiers` fields in other events in favor of `ModifiersChanged`.
|
|
||||||
- On X11, `WINIT_HIDPI_FACTOR` now dominates `Xft.dpi` when picking DPI factor for output.
|
|
||||||
- On X11, add special value `randr` for `WINIT_HIDPI_FACTOR` to make winit use self computed DPI factor instead of the one from `Xft.dpi`.
|
|
||||||
|
|
||||||
## 0.20.0-alpha5
|
|
||||||
|
|
||||||
- On macOS, fix application termination on `ControlFlow::Exit`
|
|
||||||
- On Windows, fix missing `ReceivedCharacter` events when Alt is held.
|
|
||||||
- On macOS, stop emitting private corporate characters in `ReceivedCharacter` events.
|
|
||||||
- On X11, fix misreporting DPI factor at startup.
|
|
||||||
- On X11, fix events not being reported when using `run_return`.
|
|
||||||
- On X11, fix key modifiers being incorrectly reported.
|
|
||||||
- On X11, fix window creation hanging when another window is fullscreen.
|
|
||||||
- On Windows, fix focusing unfocused windows when switching from fullscreen to windowed.
|
|
||||||
- On X11, fix reporting incorrect DPI factor when waking from suspend.
|
|
||||||
- Change `EventLoopClosed` to contain the original event.
|
|
||||||
- **Breaking**: Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`,
|
|
||||||
indicating that the event is generated by winit.
|
|
||||||
- On X11, generate synthetic key events for keys held when a window gains or loses focus.
|
|
||||||
- On X11, issue a `CursorMoved` event when a `Touch` event occurs,
|
|
||||||
as X11 implicitly moves the cursor for such events.
|
|
||||||
|
|
||||||
## 0.20.0-alpha4
|
|
||||||
|
|
||||||
- Add web support via the 'stdweb' or 'web-sys' features
|
|
||||||
- On Windows, implemented function to get HINSTANCE
|
|
||||||
- On macOS, implement `run_return`.
|
|
||||||
- On iOS, fix inverted parameter in `set_prefers_home_indicator_hidden`.
|
|
||||||
- On X11, performance is improved when rapidly calling `Window::set_cursor_icon`.
|
|
||||||
- On iOS, fix improper `msg_send` usage that was UB and/or would break if `!` is stabilized.
|
|
||||||
- On Windows, unset `maximized` when manually changing the window's position or size.
|
|
||||||
- On Windows, add touch pressure information for touch events.
|
|
||||||
- On macOS, differentiate between `CursorIcon::Grab` and `CursorIcon::Grabbing`.
|
|
||||||
- On Wayland, fix event processing sometimes stalling when using OpenGL with vsync.
|
|
||||||
- Officially remove the Emscripten backend.
|
|
||||||
- On Windows, fix handling of surrogate pairs when dispatching `ReceivedCharacter`.
|
|
||||||
- On macOS 10.15, fix freeze upon exiting exclusive fullscreen mode.
|
|
||||||
- On iOS, fix panic upon closing the app.
|
|
||||||
- On X11, allow setting multiple `XWindowType`s.
|
|
||||||
- On iOS, fix null window on initial `HiDpiFactorChanged` event.
|
|
||||||
- On Windows, fix fullscreen window shrinking upon getting restored to a normal window.
|
|
||||||
- On macOS, fix events not being emitted during modal loops, such as when windows are being resized
|
|
||||||
by the user.
|
|
||||||
- On Windows, fix hovering the mouse over the active window creating an endless stream of CursorMoved events.
|
|
||||||
- Always dispatch a `RedrawRequested` event after creating a new window.
|
|
||||||
- On X11, return dummy monitor data to avoid panicking when no monitors exist.
|
|
||||||
- On X11, prevent stealing input focus when creating a new window.
|
|
||||||
Only steal input focus when entering fullscreen mode.
|
|
||||||
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced
|
|
||||||
- On Wayland, add support for set_cursor_visible and set_cursor_grab.
|
|
||||||
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced.
|
|
||||||
- Removed `derivative` crate dependency.
|
|
||||||
- On Wayland, add support for set_cursor_icon.
|
|
||||||
- Use `impl Iterator<Item = MonitorHandle>` instead of `AvailableMonitorsIter` consistently.
|
|
||||||
- On macOS, fix fullscreen state being updated after entering fullscreen instead of before,
|
|
||||||
resulting in `Window::fullscreen` returning the old state in `Resized` events instead of
|
|
||||||
reflecting the new fullscreen state
|
|
||||||
- On X11, fix use-after-free during window creation
|
|
||||||
- On Windows, disable monitor change keyboard shortcut while in exclusive fullscreen.
|
|
||||||
- On Windows, ensure that changing a borderless fullscreen window's monitor via keyboard shortcuts keeps the window fullscreen on the new monitor.
|
|
||||||
- Prevent `EventLoop::new` and `EventLoop::with_user_event` from getting called outside the main thread.
|
|
||||||
- This is because some platforms cannot run the event loop outside the main thread. Preventing this
|
|
||||||
reduces the potential for cross-platform compatibility gotchyas.
|
|
||||||
- On Windows and Linux X11/Wayland, add platform-specific functions for creating an `EventLoop` outside the main thread.
|
|
||||||
- On Wayland, drop resize events identical to the current window size.
|
|
||||||
- On Windows, fix window rectangle not getting set correctly on high-DPI systems.
|
|
||||||
|
|
||||||
## 0.20.0-alpha3
|
|
||||||
|
|
||||||
- On macOS, drop the run closure on exit.
|
|
||||||
- On Windows, location of `WindowEvent::Touch` are window client coordinates instead of screen coordinates.
|
|
||||||
- On X11, fix delayed events after window redraw.
|
|
||||||
- On macOS, add `WindowBuilderExt::with_disallow_hidpi` to have the option to turn off best resolution openGL surface.
|
|
||||||
- On Windows, screen saver won't start if the window is in fullscreen mode.
|
|
||||||
- Change all occurrences of the `new_user_event` method to `with_user_event`.
|
|
||||||
- On macOS, the dock and the menu bar are now hidden in fullscreen mode.
|
|
||||||
- `Window::set_fullscreen` now takes `Option<Fullscreen>` where `Fullscreen`
|
|
||||||
consists of `Fullscreen::Exclusive(VideoMode)` and
|
|
||||||
`Fullscreen::Borderless(MonitorHandle)` variants.
|
|
||||||
- Adds support for exclusive fullscreen mode.
|
|
||||||
- On iOS, add support for hiding the home indicator.
|
|
||||||
- On iOS, add support for deferring system gestures.
|
|
||||||
- On iOS, fix a crash that occurred while acquiring a monitor's name.
|
|
||||||
- On iOS, fix armv7-apple-ios compile target.
|
|
||||||
- Removed the `T: Clone` requirement from the `Clone` impl of `EventLoopProxy<T>`.
|
|
||||||
- On iOS, disable overscan compensation for external displays (removes black
|
|
||||||
bars surrounding the image).
|
|
||||||
- On Linux, the functions `is_wayland`, `is_x11`, `xlib_xconnection` and `wayland_display` have been moved to a new `EventLoopWindowTargetExtUnix` trait.
|
|
||||||
- On iOS, add `set_prefers_status_bar_hidden` extension function instead of
|
|
||||||
hijacking `set_decorations` for this purpose.
|
|
||||||
- On macOS and iOS, corrected the auto trait impls of `EventLoopProxy`.
|
|
||||||
- On iOS, add touch pressure information for touch events.
|
|
||||||
- Implement `raw_window_handle::HasRawWindowHandle` for `Window` type on all supported platforms.
|
|
||||||
- On macOS, fix the signature of `-[NSView drawRect:]`.
|
|
||||||
- On iOS, fix the behavior of `ControlFlow::Poll`. It wasn't polling if that was the only mode ever used by the application.
|
|
||||||
- On iOS, fix DPI sent out by views on creation was `0.0` - now it gives a reasonable number.
|
|
||||||
- On iOS, RedrawRequested now works for gl/metal backed views.
|
|
||||||
- On iOS, RedrawRequested is generally ordered after EventsCleared.
|
|
||||||
|
|
||||||
## 0.20.0-alpha2
|
|
||||||
|
|
||||||
- On X11, non-resizable windows now have maximize explicitly disabled.
|
|
||||||
- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile`
|
|
||||||
and `WindowEvent::HoveredFile`.
|
|
||||||
- On Mac, implement `DeviceEvent::Button`.
|
|
||||||
- Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`.
|
|
||||||
- On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor.
|
|
||||||
- Revert the use of invisible surfaces in Wayland, which introduced graphical glitches with OpenGL (#835)
|
|
||||||
- On X11, implement `_NET_WM_PING` to allow desktop environment to kill unresponsive programs.
|
|
||||||
- On Windows, when a window is initially invisible, it won't take focus from the existing visible windows.
|
|
||||||
- On Windows, fix multiple calls to `request_redraw` during `EventsCleared` sending multiple `RedrawRequested events.`
|
|
||||||
- On Windows, fix edge case where `RedrawRequested` could be dispatched before input events in event loop iteration.
|
|
||||||
- On Windows, fix timing issue that could cause events to be improperly dispatched after `RedrawRequested` but before `EventsCleared`.
|
|
||||||
- On macOS, drop unused Metal dependency.
|
|
||||||
- On Windows, fix the trail effect happening on transparent decorated windows. Borderless (or un-decorated) windows were not affected.
|
|
||||||
- On Windows, fix `with_maximized` not properly setting window size to entire window.
|
|
||||||
- On macOS, change `WindowExtMacOS::request_user_attention()` to take an `enum` instead of a `bool`.
|
|
||||||
|
|
||||||
## 0.20.0-alpha1
|
|
||||||
|
|
||||||
- Changes below are considered **breaking**.
|
|
||||||
- Change all occurrences of `EventsLoop` to `EventLoop`.
|
|
||||||
- Previously flat API is now exposed through `event`, `event_loop`, `monitor`, and `window` modules.
|
|
||||||
- `os` module changes:
|
|
||||||
- Renamed to `platform`.
|
|
||||||
- All traits now have platform-specific suffixes.
|
|
||||||
- Exposes new `desktop` module on Windows, Mac, and Linux.
|
|
||||||
- Changes to event loop types:
|
|
||||||
- `EventLoopProxy::wakeup` has been removed in favor of `send_event`.
|
|
||||||
- **Major:** New `run` method drives winit event loop.
|
|
||||||
- Returns `!` to ensure API behaves identically across all supported platforms.
|
|
||||||
- This allows `emscripten` implementation to work without lying about the API.
|
|
||||||
- `ControlFlow`'s variants have been replaced with `Wait`, `WaitUntil(Instant)`, `Poll`, and `Exit`.
|
|
||||||
- Is read after `EventsCleared` is processed.
|
|
||||||
- `Wait` waits until new events are available.
|
|
||||||
- `WaitUntil` waits until either new events are available or the provided time has been reached.
|
|
||||||
- `Poll` instantly resumes the event loop.
|
|
||||||
- `Exit` aborts the event loop.
|
|
||||||
- Takes a closure that implements `'static + FnMut(Event<T>, &EventLoop<T>, &mut ControlFlow)`.
|
|
||||||
- `&EventLoop<T>` is provided to allow new `Window`s to be created.
|
|
||||||
- **Major:** `platform::desktop` module exposes `EventLoopExtDesktop` trait with `run_return` method.
|
|
||||||
- Behaves identically to `run`, but returns control flow to the calling context and can take non-`'static` closures.
|
|
||||||
- `EventLoop`'s `poll_events` and `run_forever` methods have been removed in favor of `run` and `run_return`.
|
|
||||||
- Changes to events:
|
|
||||||
- Remove `Event::Awakened` in favor of `Event::UserEvent(T)`.
|
|
||||||
- Can be sent with `EventLoopProxy::send_event`.
|
|
||||||
- Rename `WindowEvent::Refresh` to `WindowEvent::RedrawRequested`.
|
|
||||||
- `RedrawRequested` can be sent by the user with the `Window::request_redraw` method.
|
|
||||||
- `EventLoop`, `EventLoopProxy`, and `Event` are now generic over `T`, for use in `UserEvent`.
|
|
||||||
- **Major:** Add `NewEvents(StartCause)`, `EventsCleared`, and `LoopDestroyed` variants to `Event`.
|
|
||||||
- `NewEvents` is emitted when new events are ready to be processed by event loop.
|
|
||||||
- `StartCause` describes why new events are available, with `ResumeTimeReached`, `Poll`, `WaitCancelled`, and `Init` (sent once at start of loop).
|
|
||||||
- `EventsCleared` is emitted when all available events have been processed.
|
|
||||||
- Can be used to perform logic that depends on all events being processed (e.g. an iteration of a game loop).
|
|
||||||
- `LoopDestroyed` is emitted when the `run` or `run_return` method is about to exit.
|
|
||||||
- Rename `MonitorId` to `MonitorHandle`.
|
|
||||||
- Removed `serde` implementations from `ControlFlow`.
|
|
||||||
- Rename several functions to improve both internal consistency and compliance with Rust API guidelines.
|
|
||||||
- Remove `WindowBuilder::multitouch` field, since it was only implemented on a few platforms. Multitouch is always enabled now.
|
|
||||||
- **Breaking:** On macOS, change `ns` identifiers to use snake_case for consistency with iOS's `ui` identifiers.
|
|
||||||
- Add `MonitorHandle::video_modes` method for retrieving supported video modes for the given monitor.
|
|
||||||
- On Wayland, the window now exists even if nothing has been drawn.
|
|
||||||
- On Windows, fix initial dimensions of a fullscreen window.
|
|
||||||
- On Windows, Fix transparent borderless windows rendering wrong.
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
## 0.21.0
|
|
||||||
|
|
||||||
- On Windows, fixed "error: linking with `link.exe` failed: exit code: 1120" error on older versions of windows.
|
|
||||||
- On macOS, fix set_minimized(true) works only with decorations.
|
|
||||||
- On macOS, add `hide_application` to `EventLoopWindowTarget` via a new `EventLoopWindowTargetExtMacOS` trait. `hide_application` will hide the entire application by calling `-[NSApplication hide: nil]`.
|
|
||||||
- On macOS, fix not sending ReceivedCharacter event for specific keys combinations.
|
|
||||||
- On macOS, fix `CursorMoved` event reporting the cursor position using logical coordinates.
|
|
||||||
- On macOS, fix issue where unbundled applications would sometimes open without being focused.
|
|
||||||
- On macOS, fix `run_return` does not return unless it receives a message.
|
|
||||||
- On Windows, fix bug where `RedrawRequested` would only get emitted every other iteration of the event loop.
|
|
||||||
- On X11, fix deadlock on window state when handling certain window events.
|
|
||||||
- `WindowBuilder` now implements `Default`.
|
|
||||||
- **Breaking:** `WindowEvent::CursorMoved` changed to `f64` units, preserving high-precision data supplied by most backends
|
|
||||||
- On Wayland, fix coordinates in mouse events when scale factor isn't 1
|
|
||||||
- On Web, add the ability to provide a custom canvas
|
|
||||||
- **Breaking:** On Wayland, the `WaylandTheme` struct has been replaced with a `Theme` trait, allowing for extra configuration
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
## 0.22.2
|
|
||||||
|
|
||||||
- Added Clone implementation for 'static events.
|
|
||||||
- On Windows, fix window intermittently hanging when `ControlFlow` was set to `Poll`.
|
|
||||||
- On Windows, fix `WindowBuilder::with_maximized` being ignored.
|
|
||||||
- On Android, minimal platform support.
|
|
||||||
- On iOS, touch positions are now properly converted to physical pixels.
|
|
||||||
- On macOS, updated core-* dependencies and cocoa
|
|
||||||
|
|
||||||
## 0.22.1
|
|
||||||
|
|
||||||
- On X11, fix `ResumeTimeReached` being fired too early.
|
|
||||||
- On Web, replaced zero timeout for `ControlFlow::Poll` with `requestAnimationFrame`
|
|
||||||
- On Web, fix a possible panic during event handling
|
|
||||||
- On macOS, fix `EventLoopProxy` leaking memory for every instance.
|
|
||||||
|
|
||||||
## 0.22.0
|
|
||||||
|
|
||||||
- On Windows, fix minor timing issue in wait_until_time_or_msg
|
|
||||||
- On Windows, rework handling of request_redraw() to address panics.
|
|
||||||
- On macOS, fix `set_simple_screen` to remember frame excluding title bar.
|
|
||||||
- On Wayland, fix coordinates in touch events when scale factor isn't 1.
|
|
||||||
- On Wayland, fix color from `close_button_icon_color` not applying.
|
|
||||||
- Ignore locale if unsupported by X11 backend
|
|
||||||
- On Wayland, Add HiDPI cursor support
|
|
||||||
- On Web, add the ability to query "Light" or "Dark" system theme send `ThemeChanged` on change.
|
|
||||||
- Fix `Event::to_static` returning `None` for user events.
|
|
||||||
- On Wayland, Hide CSD for fullscreen windows.
|
|
||||||
- On Windows, ignore spurious mouse move messages.
|
|
||||||
- **Breaking:** Move `ModifiersChanged` variant from `DeviceEvent` to `WindowEvent`.
|
|
||||||
- On Windows, add `IconExtWindows` trait which exposes creating an `Icon` from an external file or embedded resource
|
|
||||||
- Add `BadIcon::OsError` variant for when OS icon functionality fails
|
|
||||||
- On Windows, fix crash at startup on systems that do not properly support Windows' Dark Mode
|
|
||||||
- Revert On macOS, fix not sending ReceivedCharacter event for specific keys combinations.
|
|
||||||
- on macOS, fix incorrect ReceivedCharacter events for some key combinations.
|
|
||||||
- **Breaking:** Use `i32` instead of `u32` for position type in `WindowEvent::Moved`.
|
|
||||||
- On macOS, a mouse motion event is now generated before every mouse click.
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
## 0.23.0
|
|
||||||
|
|
||||||
- On iOS, fixed support for the "Debug View Hierarchy" feature in Xcode.
|
|
||||||
- On all platforms, `available_monitors` and `primary_monitor` are now on `EventLoopWindowTarget` rather than `EventLoop` to list monitors event in the event loop.
|
|
||||||
- On Unix, X11 and Wayland are now optional features (enabled by default)
|
|
||||||
- On X11, fix deadlock when calling `set_fullscreen_inner`.
|
|
||||||
- On Web, prevent the webpage from scrolling when the user is focused on a winit canvas
|
|
||||||
- On Web, calling `window.set_cursor_icon` no longer breaks HiDPI scaling
|
|
||||||
- On Windows, drag and drop is now optional (enabled by default) and can be disabled with `WindowBuilderExtWindows::with_drag_and_drop(false)`.
|
|
||||||
- On Wayland, fix deadlock when calling to `set_inner_size` from a callback.
|
|
||||||
- On macOS, add `hide__other_applications` to `EventLoopWindowTarget` via existing `EventLoopWindowTargetExtMacOS` trait. `hide_other_applications` will hide other applications by calling `-[NSApplication hideOtherApplications: nil]`.
|
|
||||||
- On android added support for `run_return`.
|
|
||||||
- On MacOS, Fixed fullscreen and dialog support for `run_return`.
|
|
||||||
- On Windows, fix bug where we'd try to emit `MainEventsCleared` events during nested win32 event loops.
|
|
||||||
- On Web, use mouse events if pointer events aren't supported. This affects Safari.
|
|
||||||
- On Windows, `set_ime_position` is now a no-op instead of a runtime crash.
|
|
||||||
- On Android, `set_fullscreen` is now a no-op instead of a runtime crash.
|
|
||||||
- On iOS and Android, `set_inner_size` is now a no-op instead of a runtime crash.
|
|
||||||
- On Android, fix `ControlFlow::Poll` not polling the Android event queue.
|
|
||||||
- On macOS, add `NSWindow.hasShadow` support.
|
|
||||||
- On Web, fix vertical mouse wheel scrolling being inverted.
|
|
||||||
- On Web, implement mouse capturing for click-dragging out of the canvas.
|
|
||||||
- On Web, fix `ControlFlow::Exit` not properly handled.
|
|
||||||
- On Web (web-sys only), send `WindowEvent::ScaleFactorChanged` event when `window.devicePixelRatio` is changed.
|
|
||||||
- **Breaking:** On Web, `set_cursor_position` and `set_cursor_grab` will now always return an error.
|
|
||||||
- **Breaking:** `PixelDelta` scroll events now return a `PhysicalPosition`.
|
|
||||||
- On NetBSD, fixed crash due to incorrect detection of the main thread.
|
|
||||||
- **Breaking:** On X11, `-` key is mapped to the `Minus` virtual key code, instead of `Subtract`.
|
|
||||||
- On macOS, fix inverted horizontal scroll.
|
|
||||||
- **Breaking:** `current_monitor` now returns `Option<MonitorHandle>`.
|
|
||||||
- **Breaking:** `primary_monitor` now returns `Option<MonitorHandle>`.
|
|
||||||
- On macOS, updated core-* dependencies and cocoa.
|
|
||||||
- Bump `parking_lot` to 0.11
|
|
||||||
- On Android, bump `ndk`, `ndk-sys` and `ndk-glue` to 0.2. Checkout the new ndk-glue main proc attribute.
|
|
||||||
- On iOS, fixed starting the app in landscape where the view still had portrait dimensions.
|
|
||||||
- Deprecate the stdweb backend, to be removed in a future release
|
|
||||||
- **Breaking:** Prefixed virtual key codes `Add`, `Multiply`, `Divide`, `Decimal`, and `Subtract` with `Numpad`.
|
|
||||||
- Added `Asterisk` and `Plus` virtual key codes.
|
|
||||||
- On Web (web-sys only), the `Event::LoopDestroyed` event is correctly emitted when leaving the page.
|
|
||||||
- On Web, the `WindowEvent::Destroyed` event now gets emitted when a `Window` is dropped.
|
|
||||||
- On Web (web-sys only), the event listeners are now removed when a `Window` is dropped or when the event loop is destroyed.
|
|
||||||
- On Web, the event handler closure passed to `EventLoop::run` now gets dropped after the event loop is destroyed.
|
|
||||||
- **Breaking:** On Web, the canvas element associated to a `Window` is no longer removed from the DOM when the `Window` is dropped.
|
|
||||||
- On Web, `WindowEvent::Resized` is now emitted when `Window::set_inner_size` is called.
|
|
||||||
- **Breaking:** `Fullscreen` enum now uses `Borderless(Option<MonitorHandle>)` instead of `Borderless(MonitorHandle)` to allow picking the current monitor.
|
|
||||||
- On MacOS, fix `WindowEvent::Moved` ignoring the scale factor.
|
|
||||||
- On Wayland, add missing virtual keycodes.
|
|
||||||
- On Wayland, implement proper `set_cursor_grab`.
|
|
||||||
- On Wayland, the cursor will use similar icons if the requested one isn't available.
|
|
||||||
- On Wayland, right clicking on client side decorations will request application menu.
|
|
||||||
- On Wayland, fix tracking of window size after state changes.
|
|
||||||
- On Wayland, fix client side decorations not being hidden properly in fullscreen.
|
|
||||||
- On Wayland, fix incorrect size event when entering fullscreen with client side decorations.
|
|
||||||
- On Wayland, fix `resizable` attribute not being applied properly on startup.
|
|
||||||
- On Wayland, fix disabled repeat rate not being handled.
|
|
||||||
- On Wayland, fix decoration buttons not working after tty switch.
|
|
||||||
- On Wayland, fix scaling not being applied on output re-enable.
|
|
||||||
- On Wayland, fix crash when `XCURSOR_SIZE` is `0`.
|
|
||||||
- On Wayland, fix pointer getting created in some cases without pointer capability.
|
|
||||||
- On Wayland, on kwin, fix space between window and decorations on startup.
|
|
||||||
- **Breaking:** On Wayland, `Theme` trait was reworked.
|
|
||||||
- On Wayland, disable maximize button for non-resizable window.
|
|
||||||
- On Wayland, added support for `set_ime_position`.
|
|
||||||
- On Wayland, fix crash on startup since GNOME 3.37.90.
|
|
||||||
- On X11, fix incorrect modifiers state on startup.
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
## 0.24.0
|
|
||||||
|
|
||||||
- On Windows, fix applications not exiting gracefully due to thread_event_target_callback accessing corrupted memory.
|
|
||||||
- On Windows, implement `Window::set_ime_position`.
|
|
||||||
- **Breaking:** On Windows, Renamed `WindowBuilderExtWindows`'s `is_dark_mode` to `theme`.
|
|
||||||
- **Breaking:** On Windows, renamed `WindowBuilderExtWindows::is_dark_mode` to `theme`.
|
|
||||||
- On Windows, add `WindowBuilderExtWindows::with_theme` to set a preferred theme.
|
|
||||||
- On Windows, fix bug causing message boxes to appear delayed.
|
|
||||||
- On Android, calling `WindowEvent::Focused` now works properly instead of always returning false.
|
|
||||||
- On Windows, fix Alt-Tab behaviour by removing borderless fullscreen "always on top" flag.
|
|
||||||
- On Windows, fix bug preventing windows with transparency enabled from having fully-opaque regions.
|
|
||||||
- **Breaking:** On Windows, include prefix byte in scancodes.
|
|
||||||
- On Wayland, fix window not being resizeable when using `WindowBuilder::with_min_inner_size`.
|
|
||||||
- On Unix, fix cross-compiling to wasm32 without enabling X11 or Wayland.
|
|
||||||
- On Windows, fix use-after-free crash during window destruction.
|
|
||||||
- On Web, fix `WindowEvent::ReceivedCharacter` never being sent on key input.
|
|
||||||
- On macOS, fix compilation when targeting aarch64.
|
|
||||||
- On X11, fix `Window::request_redraw` not waking the event loop.
|
|
||||||
- On Wayland, the keypad arrow keys are now recognized.
|
|
||||||
- **Breaking** Rename `desktop::EventLoopExtDesktop` to `run_return::EventLoopExtRunReturn`.
|
|
||||||
- Added `request_user_attention` method to `Window`.
|
|
||||||
- **Breaking:** On macOS, removed `WindowExt::request_user_attention`, use `Window::request_user_attention`.
|
|
||||||
- **Breaking:** On X11, removed `WindowExt::set_urgent`, use `Window::request_user_attention`.
|
|
||||||
- On Wayland, default font size in CSD increased from 11 to 17.
|
|
||||||
- On Windows, fix bug causing message boxes to appear delayed.
|
|
||||||
- On Android, support multi-touch.
|
|
||||||
- On Wayland, extra mouse buttons are not dropped anymore.
|
|
||||||
- **Breaking**: `MouseButton::Other` now uses `u16`.
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user