Compare commits

..

6 Commits

Author SHA1 Message Date
Mads Marquart
926fb051de Rename window example to full, to signify that it's a large example 2024-03-01 09:45:56 +01:00
Mads Marquart
21fecf2cb2 Inline example helper functionality
It is more clear what's happening when you don't have to jump to the bottom of the file to look at the implementation.
2024-03-01 09:45:54 +01:00
Mads Marquart
c6dfe620ff Remove Display impl for Action 2024-03-01 09:23:52 +01:00
Mads Marquart
1bef238c2a Remove option_as_alt example state 2024-03-01 09:22:25 +01:00
Mads Marquart
1edf4f1238 Remove a level of indirection in main window example
We already have the `Action` enum that abstracts key and mouse event bindings; on top of that, putting each action handler in its own functions seems excessive.
2024-03-01 09:22:24 +01:00
Mads Marquart
72482048a0 Move monitor functionality out of the main window example 2024-03-01 08:37:25 +01:00
265 changed files with 20940 additions and 23660 deletions

View File

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

View File

@@ -1,8 +0,0 @@
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
# chore(rustfmt): use nightly
7b0c7b6cb2c62767ca0c73c857b299883f55a883
# Rustfmt: use `group_imports`
2665c120981af548433645c6383b3580dd8f8fc4
# Use Taplo for TOML formatting
3398ebe467c43ccfd91916c5b81ff3c68f598556

17
.github/CODEOWNERS vendored
View File

@@ -1,11 +1,10 @@
# Android # Android
/src/platform/android.rs @MarijnS95 /src/platform/android.rs @msiglreith @MarijnS95
/src/platform_impl/android @MarijnS95 /src/platform_impl/android @msiglreith @MarijnS95
# Apple (AppKit + UIKit) # iOS
/src/platform/ios.rs @madsmtm /src/platform/ios.rs @madsmtm
/src/platform/macos.rs @madsmtm /src/platform_impl/ios @madsmtm
/src/platform_impl/apple @madsmtm
# Unix # Unix
/src/platform_impl/linux/mod.rs @kchibisov /src/platform_impl/linux/mod.rs @kchibisov
@@ -18,13 +17,17 @@
/src/platform/x11.rs @kchibisov @notgull /src/platform/x11.rs @kchibisov @notgull
/src/platform_impl/linux/x11 @kchibisov @notgull /src/platform_impl/linux/x11 @kchibisov @notgull
# macOS
/src/platform/macos.rs @madsmtm
/src/platform_impl/macos @madsmtm
# Web # Web
/src/platform/web.rs @daxpedda /src/platform/web.rs @daxpedda
/src/platform_impl/web @daxpedda /src/platform_impl/web @daxpedda
# Windows # Windows
/src/platform/windows.rs @notgull /src/platform/windows.rs @msiglreith
/src/platform_impl/windows @notgull /src/platform_impl/windows @msiglreith
# Orbital (Redox OS) # Orbital (Redox OS)
/src/platform/orbital.rs @jackpot51 /src/platform/orbital.rs @jackpot51

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,22 +0,0 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
groups:
github-actions:
patterns:
- "*"
- package-ecosystem: npm
directory: src/platform_impl/web/script
schedule:
interval: daily
groups:
github-actions:
patterns:
- '*'
labels:
- "DS - web"

View File

@@ -10,44 +10,13 @@ jobs:
name: Check formatting name: Check formatting
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: taiki-e/checkout-action@v1 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/rust-toolchain@stable
with: with:
components: rustfmt components: rustfmt
- name: Check Formatting - name: Check Formatting
run: cargo fmt -- --check run: cargo fmt -- --check
taplo:
name: Taplo
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: taiki-e/checkout-action@v1
- name: Install Taplo
uses: taiki-e/install-action@v2
with:
tool: taplo-cli
- name: Run Taplo
run: taplo fmt --check
typos:
name: Check for typos
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: taiki-e/checkout-action@v1
- 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 }}
@@ -55,7 +24,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
toolchain: [stable, nightly, '1.73'] toolchain: [stable, nightly, '1.70.0']
platform: platform:
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml! # Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
- { name: 'Windows 64bit MSVC', target: x86_64-pc-windows-msvc, os: windows-latest, } - { name: 'Windows 64bit MSVC', target: x86_64-pc-windows-msvc, os: windows-latest, }
@@ -68,33 +37,23 @@ jobs:
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' } - { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' } - { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
- { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, } - { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, }
- { name: 'macOS x86_64', target: x86_64-apple-darwin, os: macos-latest, } - { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, }
- { name: 'macOS Aarch64', target: aarch64-apple-darwin, os: macos-latest, }
- { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, } - { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, }
- { name: 'iOS Aarch64', target: aarch64-apple-ios, os: macos-latest, } - { name: 'iOS Aarch64', target: aarch64-apple-ios, os: macos-latest, }
- { name: 'Web', target: wasm32-unknown-unknown, os: ubuntu-latest, } - { name: 'web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
exclude: exclude:
# Web on nightly needs extra arguments
- toolchain: nightly
platform: { name: 'Web', target: wasm32-unknown-unknown, os: ubuntu-latest }
# Android is tested on stable-3 # Android is tested on stable-3
- toolchain: '1.73' - toolchain: '1.70.0'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' } platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
# Redox OS doesn't follow MSRV
- toolchain: '1.73'
platform: { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest }
include: include:
- toolchain: '1.73' - toolchain: '1.70.0'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' } platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
- toolchain: 'nightly'
platform: { name: 'Web', target: wasm32-unknown-unknown, os: ubuntu-latest, test-options: -Zdoctest-xcompile }
- toolchain: 'nightly' - toolchain: 'nightly'
platform: { platform: {
name: 'Web Atomic', name: 'web Atomic',
target: wasm32-unknown-unknown, target: wasm32-unknown-unknown,
os: ubuntu-latest, os: ubuntu-latest,
options: '-Zbuild-std=panic_abort,std', options: '-Zbuild-std=panic_abort,std',
test-options: -Zdoctest-xcompile,
rustflags: '-Ctarget-feature=+atomics,+bulk-memory', rustflags: '-Ctarget-feature=+atomics,+bulk-memory',
components: rust-src, components: rust-src,
} }
@@ -106,21 +65,19 @@ jobs:
# Faster compilation and error on warnings # Faster compilation and error on warnings
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}' RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}'
RUSTDOCFLAGS: ${{ matrix.platform.rustflags }}
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }} OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
TEST_OPTIONS: ${{ matrix.platform.test-options }}
CMD: ${{ matrix.platform.cmd }} CMD: ${{ matrix.platform.cmd }}
steps: steps:
- uses: taiki-e/checkout-action@v1 - uses: actions/checkout@v3
- name: Restore cache of cargo folder - name: Restore cache of cargo folder
# We use `restore` and later `save`, so that we can create the key after # We use `restore` and later `save`, so that we can create the key after
# the cache has been downloaded. # the cache has been downloaded.
# #
# This could be avoided if we added Cargo.lock to the repository. # This could be avoided if we added Cargo.lock to the repository.
uses: actions/cache/restore@v4 uses: actions/cache/restore@v3
with: with:
# https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci # https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
path: | path: |
@@ -141,7 +98,7 @@ jobs:
- name: Cache cargo-apk - name: Cache cargo-apk
if: contains(matrix.platform.target, 'android') if: contains(matrix.platform.target, 'android')
id: cargo-apk-cache id: cargo-apk-cache
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: ~/.cargo/bin/cargo-apk path: ~/.cargo/bin/cargo-apk
# Change this key if we update the required cargo-apk version # Change this key if we update the required cargo-apk version
@@ -156,11 +113,6 @@ jobs:
if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true') if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
run: cargo install cargo-apk --version=^0.9.7 --locked run: cargo install cargo-apk --version=^0.9.7 --locked
- uses: taiki-e/cache-cargo-install-action@v2
if: contains(matrix.platform.target, 'wasm32') && matrix.toolchain == 'nightly'
with:
tool: wasm-bindgen-cli
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:
toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }} toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
@@ -175,56 +127,49 @@ 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.73'
run: cargo test -p dpi
- name: Build tests - name: Build tests
if: > if: >
!contains(matrix.platform.target, 'redox') && !contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.73' matrix.toolchain != '1.70.0'
run: cargo $CMD test --no-run $OPTIONS run: cargo $CMD test --no-run $OPTIONS
- name: Run tests - name: Run tests
if: > if: >
!contains(matrix.platform.target, 'android') && !contains(matrix.platform.target, 'android') &&
!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'ios') &&
(!contains(matrix.platform.target, 'wasm32') || matrix.toolchain == 'nightly') && !contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') && !contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.73' matrix.toolchain != '1.70.0'
run: cargo $CMD test $OPTIONS run: cargo $CMD test $OPTIONS
- name: Lint with clippy - name: Lint with clippy
if: (matrix.toolchain == 'stable') && !contains(matrix.platform.options, '--no-default-features') if: (matrix.toolchain == 'stable') && !contains(matrix.platform.options, '--no-default-features')
run: cargo clippy --all-targets $OPTIONS $TEST_OPTIONS -- -Dwarnings run: cargo clippy --all-targets $OPTIONS -- -Dwarnings
- name: Build tests with serde enabled - name: Build tests with serde enabled
if: > if: >
!contains(matrix.platform.target, 'redox') && !contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.73' matrix.toolchain != '1.70.0'
run: cargo $CMD test --no-run $OPTIONS $TEST_OPTIONS --features serde run: cargo $CMD test --no-run $OPTIONS --features serde
- name: Run tests with serde enabled - name: Run tests with serde enabled
if: > if: >
!contains(matrix.platform.target, 'android') && !contains(matrix.platform.target, 'android') &&
!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'ios') &&
(!contains(matrix.platform.target, 'wasm32') || matrix.toolchain == 'nightly') && !contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') && !contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.73' matrix.toolchain != '1.70.0'
run: cargo $CMD test $OPTIONS $TEST_OPTIONS --features serde run: cargo $CMD test $OPTIONS --features serde
- name: Check docs.rs documentation - name: Check docs.rs documentation
if: matrix.toolchain == 'nightly' if: matrix.toolchain == 'nightly'
run: cargo doc --no-deps $OPTIONS --features=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
uses: actions/cache/save@v4 uses: actions/cache/save@v3
with: with:
path: | path: |
~/.cargo/registry/index/ ~/.cargo/registry/index/
@@ -244,50 +189,15 @@ jobs:
- { name: 'Android', target: aarch64-linux-android } - { name: 'Android', target: aarch64-linux-android }
- { name: 'iOS', target: aarch64-apple-ios } - { name: 'iOS', target: aarch64-apple-ios }
- { name: 'Linux', target: x86_64-unknown-linux-gnu } - { name: 'Linux', target: x86_64-unknown-linux-gnu }
- { name: 'macOS', target: aarch64-apple-darwin } - { name: 'macOS', target: x86_64-apple-darwin }
- { name: 'Redox OS', target: x86_64-unknown-redox } - { name: 'Redox OS', target: x86_64-unknown-redox }
- { name: 'Web', target: wasm32-unknown-unknown } - { name: 'web', target: wasm32-unknown-unknown }
- { name: 'Windows GNU', target: x86_64-pc-windows-gnu } - { name: 'Windows', target: x86_64-pc-windows-gnu }
- { name: 'Windows MSVC', target: x86_64-pc-windows-msvc }
steps: steps:
- uses: taiki-e/checkout-action@v1 - uses: actions/checkout@v3
- uses: EmbarkStudios/cargo-deny-action@v2 - uses: EmbarkStudios/cargo-deny-action@v1
with: with:
command: check command: check
log-level: error log-level: error
arguments: --all-features --target ${{ matrix.platform.target }} arguments: --all-features --target ${{ matrix.platform.target }}
eslint:
name: ESLint
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./src/platform_impl/web/script
steps:
- uses: taiki-e/checkout-action@v1
- name: Setup NPM
run: npm install
- name: Run ESLint
run: npx eslint
swc:
name: Minimize JavaScript
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./src/platform_impl/web/script
steps:
- uses: taiki-e/checkout-action@v1
- name: Install SWC
run: sudo npm i -g @swc/cli
- name: Run SWC
run: |
swc . --ignore node_modules,**/*.d.ts --only **/*.ts -d . --out-file-extension min.js
- name: Check for diff
run: |
[[ -z $(git status -s) ]]

View File

@@ -19,7 +19,7 @@ jobs:
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:
@@ -27,12 +27,12 @@ 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=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
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v5 uses: actions/configure-pages@v4
- name: Fix permissions - name: Fix permissions
run: | run: |

6
.gitignore vendored
View File

@@ -3,8 +3,8 @@ target/
rls/ rls/
.vscode/ .vscode/
*~ *~
*.wasm
*.ts
*.js
#*# #*#
.DS_Store .DS_Store
# NPM package used to run ESLint.
/src/platform_impl/web/script/node_modules
/src/platform_impl/web/script/package-lock.json

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

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

View File

@@ -1,209 +1,152 @@
[package] [package]
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
categories = ["gui"]
description = "Cross-platform window creation library."
documentation = "https://docs.rs/winit"
edition.workspace = true
include = [
"/build.rs",
"/docs",
"/examples",
"/FEATURES.md",
"/LICENSE",
"/src",
"!/src/platform_impl/web/script",
"/src/platform_impl/web/script/**/*.min.js",
"/tests",
]
keywords = ["windowing"]
license.workspace = true
name = "winit" name = "winit"
version = "0.29.11"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
keywords = ["windowing"]
readme = "README.md" readme = "README.md"
repository.workspace = true documentation = "https://docs.rs/winit"
categories = ["gui"]
rust-version.workspace = true rust-version.workspace = true
version = "0.30.5" repository.workspace = true
license.workspace = true
edition.workspace = true
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = [ features = [
"rwh_04",
"rwh_05",
"rwh_06",
"serde", "serde",
"mint", "mint",
# Enabled to get docs to compile # Enabled to get docs to compile
"android-native-activity", "android-native-activity",
] ]
# These are all tested in CI # These are all tested in CI
rustdoc-args = ["--cfg", "docsrs"]
targets = [ targets = [
# Windows # Windows
"i686-pc-windows-msvc", "i686-pc-windows-msvc",
"x86_64-pc-windows-msvc", "x86_64-pc-windows-msvc",
# macOS # macOS
"aarch64-apple-darwin",
"x86_64-apple-darwin", "x86_64-apple-darwin",
# Unix (X11 & Wayland) # Unix (X11 & Wayland)
"i686-unknown-linux-gnu", "i686-unknown-linux-gnu",
"x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu",
# iOS # iOS
"aarch64-apple-ios", "x86_64-apple-ios",
# Android # Android
"aarch64-linux-android", "aarch64-linux-android",
# Web # Web
"wasm32-unknown-unknown", "wasm32-unknown-unknown",
] ]
rustdoc-args = ["--cfg", "docsrs"]
# Features are documented in either `lib.rs` or under `winit::platform`. # Features are documented in either `lib.rs` or under `winit::platform`.
[features] [features]
android-game-activity = ["android-activity/game-activity"] default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
android-native-activity = ["android-activity/native-activity"] x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"]
mint = ["dpi/mint"] wayland-dlopen = ["wayland-backend/dlopen"]
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde", "bitflags/serde"]
wayland = [
"wayland-client",
"wayland-backend",
"wayland-protocols",
"wayland-protocols-plasma",
"sctk",
"ahash",
"memmap2",
]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"] wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"] wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
wayland-csd-adwaita-notitle = ["sctk-adwaita"] wayland-csd-adwaita-notitle = ["sctk-adwaita"]
wayland-dlopen = ["wayland-backend/dlopen"] android-native-activity = ["android-activity/native-activity"]
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"] android-game-activity = ["android-activity/game-activity"]
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde"]
mint = ["dpi/mint"]
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
[build-dependencies] [build-dependencies]
cfg_aliases = "0.2.1" cfg_aliases = "0.2.0"
[dependencies] [dependencies]
bitflags = "2" bitflags = "2"
cursor-icon = "1.1.0" cursor-icon = "1.1.0"
dpi = { version = "0.1.1", path = "dpi" } log = "0.4"
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"] } rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
serde = { workspace = true, optional = true } serde = { workspace = true, optional = true }
smol_str = "0.2.0" smol_str = "0.2.0"
tracing = { version = "0.1.40", default-features = false } dpi = { path = "dpi" }
[dev-dependencies] [dev-dependencies]
image = { version = "0.25.0", default-features = false, features = ["png"] } image = { version = "0.24.0", default-features = false, features = ["png"] }
tracing = { version = "0.1.40", default-features = false, features = ["log"] } simple_logger = { version = "4.2.0", default_features = false }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } winit = { path = ".", features = ["rwh_05"] }
[target.'cfg(not(target_os = "android"))'.dev-dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
softbuffer = { version = "0.4.6", default-features = false, features = [ softbuffer = { version = "0.3.0", default-features = false, features = ["x11", "x11-dlopen", "wayland", "wayland-dlopen"] }
"x11",
"x11-dlopen",
"wayland",
"wayland-dlopen",
] }
# Android
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.6.0" android-activity = "0.5.0"
ndk = { version = "0.9.0", features = ["rwh_06"], default-features = false } ndk = { version = "0.8.0", default-features = false }
ndk-sys = "0.5.0"
# AppKit or UIKit [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
[target.'cfg(target_vendor = "apple")'.dependencies]
block2 = "0.5.1"
core-foundation = "0.9.3" core-foundation = "0.9.3"
objc2 = "0.5.2" objc2 = "0.5.0"
# AppKit
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.23.1" core-graphics = "0.23.1"
objc2-app-kit = { version = "0.2.2", features = [
"NSAppearance",
"NSApplication",
"NSBitmapImageRep",
"NSButton",
"NSColor",
"NSControl",
"NSCursor",
"NSDragging",
"NSEvent",
"NSGraphics",
"NSGraphicsContext",
"NSImage",
"NSImageRep",
"NSMenu",
"NSMenuItem",
"NSOpenGLView",
"NSPasteboard",
"NSResponder",
"NSRunningApplication",
"NSScreen",
"NSTextInputClient",
"NSTextInputContext",
"NSToolbar",
"NSView",
"NSWindow",
"NSWindowScripting",
"NSWindowTabGroup",
] }
objc2-foundation = { version = "0.2.2", features = [
"block2",
"dispatch",
"NSArray",
"NSAttributedString",
"NSData",
"NSDictionary",
"NSDistributedNotificationCenter",
"NSEnumerator",
"NSGeometry",
"NSKeyValueObserving",
"NSNotification",
"NSObjCRuntime",
"NSOperation",
"NSPathUtilities",
"NSProcessInfo",
"NSRunLoop",
"NSString",
"NSThread",
"NSValue",
] }
# UIKit [target.'cfg(target_os = "macos")'.dependencies.icrate]
[target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dependencies] version = "0.1.0"
objc2-foundation = { version = "0.2.2", features = [ features = [
"block2",
"dispatch", "dispatch",
"NSArray", "Foundation",
"NSEnumerator", "Foundation_NSArray",
"NSGeometry", "Foundation_NSAttributedString",
"NSObjCRuntime", "Foundation_NSMutableAttributedString",
"NSOperation", "Foundation_NSData",
"NSString", "Foundation_NSDictionary",
"NSProcessInfo", "Foundation_NSString",
"NSThread", "Foundation_NSProcessInfo",
"NSSet", "Foundation_NSThread",
] } "Foundation_NSNumber",
objc2-ui-kit = { version = "0.2.2", features = [ "AppKit",
"UIApplication", "AppKit_NSAppearance",
"UIDevice", "AppKit_NSApplication",
"UIEvent", "AppKit_NSBitmapImageRep",
"UIGeometry", "AppKit_NSButton",
"UIGestureRecognizer", "AppKit_NSColor",
"UITextInput", "AppKit_NSControl",
"UITextInputTraits", "AppKit_NSCursor",
"UIOrientation", "AppKit_NSEvent",
"UIPanGestureRecognizer", "AppKit_NSGraphicsContext",
"UIPinchGestureRecognizer", "AppKit_NSImage",
"UIResponder", "AppKit_NSImageRep",
"UIRotationGestureRecognizer", "AppKit_NSMenu",
"UIScreen", "AppKit_NSMenuItem",
"UIScreenMode", "AppKit_NSPasteboard",
"UITapGestureRecognizer", "AppKit_NSResponder",
"UITouch", "AppKit_NSScreen",
"UITraitCollection", "AppKit_NSTextInputContext",
"UIView", "AppKit_NSView",
"UIViewController", "AppKit_NSWindow",
"UIWindow", "AppKit_NSWindowTabGroup",
] } ]
[target.'cfg(target_os = "ios")'.dependencies.icrate]
version = "0.1.0"
features = [
"dispatch",
"Foundation",
"Foundation_NSArray",
"Foundation_NSString",
"Foundation_NSProcessInfo",
"Foundation_NSThread",
"Foundation_NSSet",
]
# Windows
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
unicode-segmentation = "1.7.1" unicode-segmentation = "1.7.1"
windows-sys = { version = "0.52.0", features = [
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.48"
features = [
"Win32_Devices_HumanInterfaceDevice", "Win32_Devices_HumanInterfaceDevice",
"Win32_Foundation", "Win32_Foundation",
"Win32_Globalization", "Win32_Globalization",
@@ -214,7 +157,6 @@ windows-sys = { version = "0.52.0", features = [
"Win32_System_Com", "Win32_System_Com",
"Win32_System_LibraryLoader", "Win32_System_LibraryLoader",
"Win32_System_Ole", "Win32_System_Ole",
"Win32_Security",
"Win32_System_SystemInformation", "Win32_System_SystemInformation",
"Win32_System_SystemServices", "Win32_System_SystemServices",
"Win32_System_Threading", "Win32_System_Threading",
@@ -229,136 +171,107 @@ windows-sys = { version = "0.52.0", features = [
"Win32_UI_Shell", "Win32_UI_Shell",
"Win32_UI_TextServices", "Win32_UI_TextServices",
"Win32_UI_WindowsAndMessaging", "Win32_UI_WindowsAndMessaging",
] } ]
# Linux [target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
ahash = { version = "0.8.7", features = ["no-rng"], optional = true } ahash = { version = "0.8.7", features = ["no-rng"], optional = true }
bytemuck = { version = "1.13.1", default-features = false, optional = true } bytemuck = { version = "1.13.1", default-features = false, optional = true }
calloop = "0.13.0" calloop = "0.12.3"
libc = "0.2.64" libc = "0.2.64"
memmap2 = { version = "0.9.0", optional = true } memmap2 = { version = "0.9.0", optional = true }
percent-encoding = { version = "2.0", optional = true } percent-encoding = { version = "2.0", optional = true }
rustix = { version = "0.38.4", default-features = false, features = [ rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
"std", sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
"system", sctk-adwaita = { version = "0.8.0", default_features = false, optional = true }
"thread", wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
"process", wayland-client = { version = "0.31.1", optional = true }
] } wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
sctk = { package = "smithay-client-toolkit", version = "0.19.2", default-features = false, features = [ wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
"calloop", x11-dl = { version = "2.18.5", optional = true }
], optional = true } x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
sctk-adwaita = { version = "0.10.1", default-features = false, optional = true }
wayland-backend = { version = "0.3.5", default-features = false, features = [
"client_system",
], optional = true }
wayland-client = { version = "0.31.4", optional = true }
wayland-protocols = { version = "0.32.2", features = ["staging"], optional = true }
wayland-protocols-plasma = { version = "0.3.2", features = ["client"], optional = true }
x11-dl = { version = "2.19.1", optional = true }
x11rb = { version = "0.13.0", default-features = false, features = [
"allow-unsafe-code",
"cursor",
"dl-libxcb",
"randr",
"resource_manager",
"sync",
"xinput",
"xkb",
], optional = true }
xkbcommon-dl = "0.4.2" xkbcommon-dl = "0.4.2"
# Orbital
[target.'cfg(target_os = "redox")'.dependencies] [target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.47", default-features = false } orbclient = { version = "0.3.47", default-features = false }
redox_syscall = "0.5.7" redox_syscall = "0.4.1"
[target.'cfg(target_family = "wasm")'.dependencies.web_sys]
package = "web-sys"
version = "0.3.64"
features = [
'AbortController',
'AbortSignal',
'Blob',
'console',
'CssStyleDeclaration',
'Document',
'DomException',
'DomRect',
'DomRectReadOnly',
'Element',
'Event',
'EventTarget',
'FocusEvent',
'HtmlCanvasElement',
'HtmlElement',
'HtmlImageElement',
'ImageBitmap',
'ImageBitmapOptions',
'ImageBitmapRenderingContext',
'ImageData',
'IntersectionObserver',
'IntersectionObserverEntry',
'KeyboardEvent',
'MediaQueryList',
'MessageChannel',
'MessagePort',
'Node',
'PageTransitionEvent',
'PointerEvent',
'PremultiplyAlpha',
'ResizeObserver',
'ResizeObserverBoxOptions',
'ResizeObserverEntry',
'ResizeObserverOptions',
'ResizeObserverSize',
'VisibilityState',
'Window',
'WheelEvent',
'Url',
]
# Web
[target.'cfg(target_family = "wasm")'.dependencies] [target.'cfg(target_family = "wasm")'.dependencies]
js-sys = "0.3.70" js-sys = "0.3.64"
pin-project = "1" pin-project = "1"
wasm-bindgen = "0.2.93" wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4.43" wasm-bindgen-futures = "0.4"
web-time = "1" web-time = "1"
web_sys = { package = "web-sys", version = "0.3.70", features = [
"AbortController",
"AbortSignal",
"Blob",
"BlobPropertyBag",
"console",
"CssStyleDeclaration",
"Document",
"DomException",
"DomRect",
"DomRectReadOnly",
"Element",
"Event",
"EventTarget",
"FocusEvent",
"HtmlCanvasElement",
"HtmlElement",
"HtmlHtmlElement",
"HtmlImageElement",
"ImageBitmap",
"ImageBitmapOptions",
"ImageBitmapRenderingContext",
"ImageData",
"IntersectionObserver",
"IntersectionObserverEntry",
"KeyboardEvent",
"MediaQueryList",
"MessageChannel",
"MessagePort",
"Navigator",
"Node",
"OrientationLockType",
"OrientationType",
"PageTransitionEvent",
"Permissions",
"PermissionState",
"PermissionStatus",
"PointerEvent",
"PremultiplyAlpha",
"ResizeObserver",
"ResizeObserverBoxOptions",
"ResizeObserverEntry",
"ResizeObserverOptions",
"ResizeObserverSize",
"Screen",
"ScreenOrientation",
"Url",
"VisibilityState",
"WheelEvent",
"Window",
"Worker",
] }
[target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies] [target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies]
atomic-waker = "1" atomic-waker = "1"
concurrent-queue = { version = "2", default-features = false } concurrent-queue = { version = "2", default-features = false }
[target.'cfg(target_family = "wasm")'.dev-dependencies] [target.'cfg(target_family = "wasm")'.dev-dependencies]
console_error_panic_hook = "0.1" console_log = "1"
tracing-web = "0.1" web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
wasm-bindgen-test = "0.3"
[[example]] [[example]]
doc-scrape-examples = true doc-scrape-examples = true
name = "window" name = "full"
[[example]]
name = "child_window"
[workspace] [workspace]
members = ["dpi"]
resolver = "2" resolver = "2"
members = [
"dpi",
"run-wasm",
]
[workspace.package] [workspace.package]
edition = "2021" rust-version = "1.70.0"
license = "Apache-2.0"
repository = "https://github.com/rust-windowing/winit" repository = "https://github.com/rust-windowing/winit"
rust-version = "1.73" license = "Apache-2.0"
edition = "2021"
[workspace.dependencies] [workspace.dependencies]
mint = "0.5.6"
serde = { version = "1", features = ["serde_derive"] } serde = { version = "1", features = ["serde_derive"] }
mint = "0.5.6"

View File

@@ -154,6 +154,7 @@ If your PR makes notable changes to Winit's features, please update this section
* Home indicator visibility * Home indicator visibility
* Status bar visibility and style * Status bar visibility and style
* Deferring system gestures * Deferring system gestures
* Getting the device idiom
* Getting the preferred video mode * Getting the preferred video mode
### Web ### Web
@@ -178,7 +179,7 @@ Legend:
|Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ | |Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ |
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** | |Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ | |Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|Window resize increments |✔️ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** | |Window resize increments | |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ | |Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ | |Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ |
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** | |Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |

View File

@@ -8,7 +8,7 @@
```toml ```toml
[dependencies] [dependencies]
winit = "0.30.5" winit = "0.29.11"
``` ```
## [Documentation](https://docs.rs/winit) ## [Documentation](https://docs.rs/winit)
@@ -19,7 +19,7 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
## Contact Us ## Contact Us
Join us in our [![Matrix](https://img.shields.io/badge/Matrix-%23rust--windowing%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#rust-windowing:matrix.org) room. Join us in our [![Matrix](https://img.shields.io/badge/Matrix-%23rust--windowing%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#rust-windowing:matrix.org) room. If you don't get an answer there, try [![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit).
The maintainers have a meeting every friday at UTC 15. The meeting notes can be found [here](https://hackmd.io/@winit-meetings). The maintainers have a meeting every friday at UTC 15. The meeting notes can be found [here](https://hackmd.io/@winit-meetings).
@@ -35,7 +35,7 @@ another library.
## MSRV Policy ## MSRV Policy
This crate's Minimum Supported Rust Version (MSRV) is **1.73**. Changes to This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to
the MSRV will be accompanied by a minor version bump. the MSRV will be accompanied by a minor version bump.
As a **tentative** policy, the upper bound of the MSRV is given by the following As a **tentative** policy, the upper bound of the MSRV is given by the following
@@ -50,15 +50,12 @@ Where `sid` is the current version of `rustc` provided by [Debian Sid], and
[Debian Sid]: https://packages.debian.org/sid/rustc [Debian Sid]: https://packages.debian.org/sid/rustc
An exception is made for the Android platform, where a higher Rust version The exception is for the Android platform, where a higher Rust version
must be used for certain Android features. In this case, the MSRV will be must be used for certain Android features. In this case, the MSRV will be
capped at the latest stable version of Rust minus three. This inconsistency is capped at the latest stable version of Rust minus three. This inconsistency is
not reflected in Cargo metadata, as it is not powerful enough to expose this not reflected in Cargo metadata, as it is not powerful enough to expose this
restriction. restriction.
Redox OS is also not covered by this MSRV policy, as it requires a Rust nightly
toolchain to compile.
All crates in the [`rust-windowing`] organizations have the All crates in the [`rust-windowing`] organizations have the
same MSRV policy. same MSRV policy.

View File

@@ -1,18 +1,19 @@
use cfg_aliases::cfg_aliases; use cfg_aliases::cfg_aliases;
fn main() { fn main() {
// The script doesn't depend on our code. // The script doesn't depend on our code
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=build.rs");
// Setup cfg aliases. // Setup cfg aliases
cfg_aliases! { cfg_aliases! {
// Systems. // Systems.
android_platform: { target_os = "android" }, android_platform: { target_os = "android" },
web_platform: { all(target_family = "wasm", target_os = "unknown") }, web_platform: { all(target_family = "wasm", target_os = "unknown") },
macos_platform: { target_os = "macos" }, macos_platform: { target_os = "macos" },
ios_platform: { all(target_vendor = "apple", not(target_os = "macos")) }, ios_platform: { target_os = "ios" },
windows_platform: { target_os = "windows" }, windows_platform: { target_os = "windows" },
free_unix: { all(unix, not(target_vendor = "apple"), not(android_platform), not(target_os = "emscripten")) }, apple: { any(target_os = "ios", target_os = "macos") },
free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) },
redox: { target_os = "redox" }, redox: { target_os = "redox" },
// Native displays. // Native displays.
@@ -20,7 +21,4 @@ fn main() {
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) }, wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
orbital_platform: { redox }, orbital_platform: { redox },
} }
// Winit defined cfgs.
println!("cargo:rustc-check-cfg=cfg(unreleased_changelogs)");
} }

View File

@@ -1,16 +1,15 @@
disallowed-methods = [ disallowed-methods = [
{ path = "objc2_app_kit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." }, { path = "web_sys::window", reason = "is not available in every context" },
{ path = "objc2_app_kit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlCanvasElement::width", reason = "Winit shouldn't touch the internal canvas size" }, { path = "web_sys::HtmlCanvasElement::width", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlElement::style", reason = "cache this to reduce calls to JS" }, { path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" }, { path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" }, { path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::Window::navigator", reason = "cache this to reduce calls to JS" }, { path = "web_sys::HtmlElement::style", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::window", reason = "is not available in every context" }, { path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
{ path = "icrate::AppKit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
{ path = "icrate::AppKit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
] ]

View File

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

View File

@@ -1,130 +0,0 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0" version="24.8.6" pages="2">
<diagram name="desktop" id="3DDum1nDijUk3y7wIDRm">
<mxGraphModel dx="1080" dy="707" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1000" pageHeight="500" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="cRYnzpdCW-J0f_YpP3mc-1" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#E8E8E8;fontColor=#333333;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="200" y="80" width="480" height="360" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-4" value="" style="rounded=1;whiteSpace=wrap;html=1;shadow=0;fillColor=#d5e8d4;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="260" y="340" width="360" height="40" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-2" value="" style="rounded=1;whiteSpace=wrap;html=1;shadow=0;fillColor=#dae8fc;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="260" y="140" width="360" height="80" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-3" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#DBDBDB;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
<mxGeometry x="200" y="60" width="480" height="20" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-5" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=none;fillColor=#d5e8d4;" parent="1" vertex="1">
<mxGeometry x="260" y="180" width="360" height="180" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-6" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#666666;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-4" target="cRYnzpdCW-J0f_YpP3mc-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="10" y="310" as="sourcePoint" />
<mxPoint x="60" y="260" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-7" value="" style="endArrow=none;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#666666;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-4" target="cRYnzpdCW-J0f_YpP3mc-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="770" y="570" as="sourcePoint" />
<mxPoint x="770" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-8" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;strokeColor=#666666;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-2" target="cRYnzpdCW-J0f_YpP3mc-5" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="220.00000000000023" y="179.69" as="sourcePoint" />
<mxPoint x="740.0000000000002" y="179.69" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-9" value="&lt;font&gt;outer_position&lt;/font&gt;" style="endArrow=blockThin;html=1;strokeWidth=3;rounded=0;exitX=0;exitY=0;exitDx=0;exitDy=0;dashed=1;align=right;fontSize=20;fontFamily=monospace;fontColor=#6C8EBF;labelBackgroundColor=none;spacingLeft=0;spacingRight=15;spacing=0;fillColor=#dae8fc;strokeColor=#6C8EBF;endFill=1;startArrow=oval;startFill=1;endSize=6;targetPerimeterSpacing=0;entryX=0;entryY=0;entryDx=0;entryDy=0;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-3" edge="1" target="cRYnzpdCW-J0f_YpP3mc-2">
<mxGeometry x="-0.36" y="-24" width="50" height="50" relative="1" as="geometry">
<mxPoint x="80" y="160" as="sourcePoint" />
<mxPoint x="240" y="160" as="targetPoint" />
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-10" value="&lt;font&gt;outer_size&lt;/font&gt;" style="endArrow=none;html=1;strokeWidth=3;rounded=0;dashed=1;align=left;fontSize=20;fontFamily=monospace;fontColor=#6C8EBF;labelBackgroundColor=none;spacingLeft=15;spacingRight=0;spacing=0;exitX=1;exitY=0;exitDx=0;exitDy=0;fillColor=#dae8fc;strokeColor=#6c8ebf;entryX=1;entryY=1;entryDx=0;entryDy=0;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-2" edge="1" target="cRYnzpdCW-J0f_YpP3mc-4">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="850" y="170" as="sourcePoint" />
<mxPoint x="760" y="420" as="targetPoint" />
<Array as="points">
<mxPoint x="860" y="140" />
<mxPoint x="860" y="380" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-11" value="&lt;font&gt;surface_size&lt;/font&gt;" style="endArrow=none;html=1;strokeWidth=3;rounded=0;dashed=1;align=left;fontSize=20;fontFamily=monospace;fontColor=#82B366;labelBackgroundColor=none;spacingLeft=15;spacingRight=0;spacing=0;entryX=1;entryY=1;entryDx=0;entryDy=0;fillColor=#d5e8d4;strokeColor=#82B366;" parent="1" target="cRYnzpdCW-J0f_YpP3mc-4" edge="1">
<mxGeometry x="0.0526" width="50" height="50" relative="1" as="geometry">
<mxPoint x="600" y="180" as="sourcePoint" />
<mxPoint x="760" y="420" as="targetPoint" />
<Array as="points">
<mxPoint x="700" y="180" />
<mxPoint x="700" y="380" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-12" value="&lt;font&gt;surface_position&lt;/font&gt;" style="endArrow=blockThin;html=1;strokeWidth=3;rounded=0;dashed=1;align=right;fontSize=20;fontFamily=monospace;fontColor=#82B366;labelBackgroundColor=none;spacingLeft=0;spacingRight=15;spacing=0;fillColor=#d5e8d4;strokeColor=#82b366;exitX=0;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;curved=1;startArrow=oval;startFill=1;endFill=1;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-2" target="cRYnzpdCW-J0f_YpP3mc-5" edge="1">
<mxGeometry y="-50" width="50" height="50" relative="1" as="geometry">
<mxPoint x="140" y="140" as="sourcePoint" />
<mxPoint x="160" y="200" as="targetPoint" />
<Array as="points">
<mxPoint x="250" y="160" />
</Array>
<mxPoint x="-5" y="-22" as="offset" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram name="mobile" id="D5mAeJSS4Z33KEKjPCBt">
<mxGraphModel dx="1710" dy="1120" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="720" pageHeight="720" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-1" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#E8E8E8;fontColor=#333333;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="200" y="40" width="320" height="640" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-2" value="" style="rounded=1;whiteSpace=wrap;html=1;shadow=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="210" y="50" width="300" height="620" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-4" value="" style="rounded=0;whiteSpace=wrap;html=1;shadow=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="1" vertex="1">
<mxGeometry x="210" y="90" width="300" height="540" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-20" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#DBDBDB;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
<mxGeometry x="290" y="640" width="140" height="10" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-3" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#DBDBDB;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
<mxGeometry x="300" y="50" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-12" value="&lt;font&gt;surface_size&lt;/font&gt;" style="endArrow=none;html=1;strokeWidth=3;rounded=0;dashed=1;align=left;fontSize=20;fontFamily=monospace;fontColor=#82B366;labelBackgroundColor=none;spacingLeft=15;spacingRight=15;spacing=0;fillColor=#d5e8d4;strokeColor=#82b366;exitX=1;exitY=0;exitDx=0;exitDy=0;" parent="1" source="RxwCrVmIsQwV7z5iJ9nY-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="210" y="50" as="sourcePoint" />
<mxPoint x="510" y="670" as="targetPoint" />
<Array as="points">
<mxPoint x="560" y="50" />
<mxPoint x="560" y="670" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="NrHAzeOh65jb3hkBOxW9-1" value="&lt;div&gt;safe_area.top&lt;/div&gt;" style="endArrow=blockThin;html=1;strokeWidth=3;rounded=0;align=right;fontSize=20;fontFamily=monospace;fontColor=#D79B00;labelBackgroundColor=none;spacingLeft=0;spacingRight=15;spacing=0;fillColor=#ffe6cc;strokeColor=#d79b00;startArrow=blockThin;startFill=1;endFill=1;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="180" y="50" as="sourcePoint" />
<mxPoint x="180" y="90" as="targetPoint" />
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="NrHAzeOh65jb3hkBOxW9-5" value="&lt;div&gt;safe_area.bottom&lt;/div&gt;" style="endArrow=blockThin;html=1;strokeWidth=3;rounded=0;align=right;fontSize=20;fontFamily=monospace;fontColor=#D79B00;labelBackgroundColor=none;spacingLeft=0;spacingRight=15;spacing=0;fillColor=#ffe6cc;strokeColor=#d79b00;startArrow=blockThin;startFill=1;endFill=1;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="180" y="670" as="sourcePoint" />
<mxPoint x="180" y="630" as="targetPoint" />
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -9,10 +9,3 @@ by [Tomiĉo] (https://commons.wikimedia.org/wiki/User:Tomi%C4%89o). It was
originally released under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en) originally released under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)
License. Minor modifications have been made by [John Nunley](https://github.com/notgull), License. Minor modifications have been made by [John Nunley](https://github.com/notgull),
which have been released under the same license as a derivative work. which have been released under the same license as a derivative work.
## `coordinate-systems*`
These files are created by [Mads Marquart](https://github.com/madsmtm) using
[draw.io](https://draw.io/), and compressed using [svgomg.net](https://svgomg.net/).
They are licensed under the [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/) license.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="720" viewBox="-0.5 -0.5 720 720" class="ge-export-svg-auto"><defs><style><![CDATA[@media (prefers-color-scheme:dark){svg.ge-export-svg-auto svg:not(mjx-container>svg),svg.ge-export-svg-auto:not(mjx-container>svg){filter:invert(100%) hue-rotate(180deg)}}]]></style></defs><g data-cell-id="0"><g data-cell-id="1"><rect x="200" y="40" width="320" height="640" rx="48" ry="48" fill="#e8e8e8" stroke="#666" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-1"/><rect x="210" y="50" width="300" height="620" rx="45" ry="45" fill="#d5e8d4" stroke="#82b366" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-2"/><path fill="#ffe6cc" stroke="#d79b00" pointer-events="all" d="M210 90h300v540H210z" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-4"/><rect x="290" y="640" width="140" height="10" rx="1.5" ry="1.5" fill="#dbdbdb" stroke="#666" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-20"/><rect x="300" y="50" width="120" height="30" rx="4.5" ry="4.5" fill="#dbdbdb" stroke="#666" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-3"/><g data-cell-id="RxwCrVmIsQwV7z5iJ9nY-12"><path d="M510 50h50v620h-50" fill="none" stroke="#82b366" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><switch transform="translate(-.5 -.5)"><foreignObject style="overflow:visible;text-align:left" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe flex-start;width:1px;height:1px;padding-top:360px;margin-left:575px"><div style="box-sizing:border-box;font-size:0;text-align:left" data-drawio-colors="color: #82B366;"><div style="display:inline-block;font-size:20px;font-family:&quot;monospace&quot;;color:#82b366;line-height:1.2;pointer-events:all;white-space:nowrap"><font>surface_size</font></div></div></div></foreignObject><text x="575" y="366" fill="#82B366" font-family="&quot;monospace&quot;" font-size="20">surfa...</text></switch></g><g data-cell-id="NrHAzeOh65jb3hkBOxW9-1"><path d="M180 62.35v15.3" fill="none" stroke="#d79b00" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/><path d="m180 53.35 3 9h-6ZM180 86.65l-3-9h6Z" fill="#d79b00" stroke="#d79b00" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><switch transform="translate(-.5 -.5)"><foreignObject style="overflow:visible;text-align:left" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe flex-end;width:1px;height:1px;padding-top:70px;margin-left:165px"><div style="box-sizing:border-box;font-size:0;text-align:right" data-drawio-colors="color: #D79B00;"><div style="display:inline-block;font-size:20px;font-family:&quot;monospace&quot;;color:#d79b00;line-height:1.2;pointer-events:all;white-space:nowrap"><div>safe_area.top</div></div></div></div></foreignObject><text x="165" y="76" fill="#D79B00" font-family="&quot;monospace&quot;" font-size="20" text-anchor="end">safe_...</text></switch></g><g data-cell-id="NrHAzeOh65jb3hkBOxW9-5"><path d="M180 657.65v-15.3" fill="none" stroke="#d79b00" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/><path d="m180 666.65-3-9h6ZM180 633.35l3 9h-6Z" fill="#d79b00" stroke="#d79b00" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><switch transform="translate(-.5 -.5)"><foreignObject style="overflow:visible;text-align:left" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe flex-end;width:1px;height:1px;padding-top:650px;margin-left:165px"><div style="box-sizing:border-box;font-size:0;text-align:right" data-drawio-colors="color: #D79B00;"><div style="display:inline-block;font-size:20px;font-family:&quot;monospace&quot;;color:#d79b00;line-height:1.2;pointer-events:all;white-space:nowrap"><div>safe_area.bottom</div></div></div></div></foreignObject><text x="165" y="656" fill="#D79B00" font-family="&quot;monospace&quot;" font-size="20" text-anchor="end">safe_...</text></switch></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -1,22 +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
- Added `Insets`, `LogicalInsets` and `PhysicalInsets` types.
## 0.1.1
- Derive `Debug`, `Copy`, `Clone`, `PartialEq`, `Serialize`, `Deserialize` traits for `PixelUnit`.
## 0.1.0
- Add `LogicalUnit`, `PhysicalUnit` and `PixelUnit` types and related functions.

View File

@@ -1,40 +1,39 @@
[package] [package]
categories = ["gui"]
description = "Types for handling UI scaling"
edition.workspace = true
keywords = ["DPI", "HiDPI", "scale-factor"]
license.workspace = true
name = "dpi" name = "dpi"
repository.workspace = true version = "0.0.0"
description = "Types for handling UI scaling"
keywords = ["DPI", "HiDPI", "scale-factor"]
categories = ["gui"]
rust-version.workspace = true rust-version.workspace = true
version = "0.1.1" repository.workspace = true
license.workspace = true
edition.workspace = true
[features] [features]
mint = ["dep:mint"]
serde = ["dep:serde"] serde = ["dep:serde"]
mint = ["dep:mint"]
[dependencies] [dependencies]
mint = { workspace = true, optional = true }
serde = { workspace = true, optional = true } serde = { workspace = true, optional = true }
mint = { workspace = true, optional = true }
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["mint", "serde"] features = ["serde", "mint"]
# These are all tested in CI # These are all tested in CI
rustdoc-args = ["--cfg", "docsrs"]
targets = [ targets = [
# Windows # Windows
"i686-pc-windows-msvc", "i686-pc-windows-msvc",
"x86_64-pc-windows-msvc", "x86_64-pc-windows-msvc",
# macOS # macOS
"aarch64-apple-darwin",
"x86_64-apple-darwin", "x86_64-apple-darwin",
# Unix (X11 & Wayland) # Unix (X11 & Wayland)
"i686-unknown-linux-gnu", "i686-unknown-linux-gnu",
"x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu",
# iOS # iOS
"aarch64-apple-ios", "x86_64-apple-ios",
# Android # Android
"aarch64-linux-android", "aarch64-linux-android",
# Web # Web
"wasm32-unknown-unknown", "wasm32-unknown-unknown",
] ]
rustdoc-args = ["--cfg", "docsrs"]

View File

@@ -35,9 +35,9 @@
//! //!
//! ### Position and Size types //! ### Position and Size types
//! //!
//! The [`PhysicalPosition`] / [`PhysicalSize`] / [`PhysicalUnit`] types correspond with the actual //! The [`PhysicalPosition`] / [`PhysicalSize`] types correspond with the actual pixels on the
//! pixels on the device, and the [`LogicalPosition`] / [`LogicalSize`] / [`LogicalUnit`] types //! device, and the [`LogicalPosition`] / [`LogicalSize`] types correspond to the physical pixels
//! correspond to the physical pixels divided by the scale factor. //! divided by the scale factor.
//! //!
//! The position and size types are generic over their exact pixel type, `P`, to allow the //! The position and size types are generic over their exact pixel type, `P`, to allow the
//! API to have integer precision where appropriate (e.g. most window manipulation functions) and //! API to have integer precision where appropriate (e.g. most window manipulation functions) and
@@ -52,14 +52,19 @@
//! //!
//! This crate provides the following Cargo features: //! This crate provides the following Cargo features:
//! //!
//! * `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde). //! * `serde`: Enables serialization/deserialization of certain types with
//! [Serde](https://crates.io/crates/serde).
//! * `mint`: Enables mint (math interoperability standard types) conversions. //! * `mint`: Enables mint (math interoperability standard types) conversions.
//! //!
//! //!
//! [points]: https://en.wikipedia.org/wiki/Point_(typography) //! [points]: https://en.wikipedia.org/wiki/Point_(typography)
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography) //! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))] #![cfg_attr(
docsrs,
feature(doc_auto_cfg, doc_cfg_hide),
doc(cfg_hide(doc, docsrs))
)]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@@ -115,248 +120,14 @@ impl Pixel for f64 {
/// Checks that the scale factor is a normal positive `f64`. /// Checks that the scale factor is a normal positive `f64`.
/// ///
/// All functions that take a scale factor assert that this will return `true`. If you're sourcing /// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from
/// scale factors from anywhere other than winit, it's recommended to validate them using this /// anywhere other than winit, it's recommended to validate them using this function before passing them to winit;
/// function before passing them to winit; otherwise, you risk panics. /// otherwise, you risk panics.
#[inline] #[inline]
pub fn validate_scale_factor(scale_factor: f64) -> bool { pub fn validate_scale_factor(scale_factor: f64) -> bool {
scale_factor.is_sign_positive() && scale_factor.is_normal() scale_factor.is_sign_positive() && scale_factor.is_normal()
} }
/// A logical pixel unit.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalUnit<P>(pub P);
impl<P> LogicalUnit<P> {
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
/// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a logical unit of `0_f64`.
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
#[inline]
pub const fn new(v: P) -> Self {
LogicalUnit(v)
}
}
impl<P: Pixel> LogicalUnit<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalUnit<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<X> {
assert!(validate_scale_factor(scale_factor));
PhysicalUnit::new(self.0.into() * scale_factor).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalUnit<X> {
LogicalUnit(self.0.cast())
}
}
impl<P: Pixel, X: Pixel> From<X> for LogicalUnit<P> {
fn from(v: X) -> LogicalUnit<P> {
LogicalUnit::new(v.cast())
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u8 {
fn from(v: LogicalUnit<P>) -> u8 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u16 {
fn from(v: LogicalUnit<P>) -> u16 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u32 {
fn from(v: LogicalUnit<P>) -> u32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i8 {
fn from(v: LogicalUnit<P>) -> i8 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i16 {
fn from(v: LogicalUnit<P>) -> i16 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i32 {
fn from(v: LogicalUnit<P>) -> i32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for f32 {
fn from(v: LogicalUnit<P>) -> f32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for f64 {
fn from(v: LogicalUnit<P>) -> f64 {
v.0.cast()
}
}
/// A physical pixel unit.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalUnit<P>(pub P);
impl<P> PhysicalUnit<P> {
/// Represents a maximum physical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
/// Represents a minimum physical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a physical unit of `0_f64`.
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
#[inline]
pub const fn new(v: P) -> Self {
PhysicalUnit(v)
}
}
impl<P: Pixel> PhysicalUnit<P> {
#[inline]
pub fn from_logical<T: Into<LogicalUnit<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalUnit<X> {
assert!(validate_scale_factor(scale_factor));
LogicalUnit::new(self.0.into() / scale_factor).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalUnit<X> {
PhysicalUnit(self.0.cast())
}
}
impl<P: Pixel, X: Pixel> From<X> for PhysicalUnit<P> {
fn from(v: X) -> PhysicalUnit<P> {
PhysicalUnit::new(v.cast())
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u8 {
fn from(v: PhysicalUnit<P>) -> u8 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u16 {
fn from(v: PhysicalUnit<P>) -> u16 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u32 {
fn from(v: PhysicalUnit<P>) -> u32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i8 {
fn from(v: PhysicalUnit<P>) -> i8 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i16 {
fn from(v: PhysicalUnit<P>) -> i16 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i32 {
fn from(v: PhysicalUnit<P>) -> i32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for f32 {
fn from(v: PhysicalUnit<P>) -> f32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for f64 {
fn from(v: PhysicalUnit<P>) -> f64 {
v.0.cast()
}
}
/// A pixel unit that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PixelUnit {
Physical(PhysicalUnit<i32>),
Logical(LogicalUnit<f64>),
}
impl PixelUnit {
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MAX));
/// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MIN));
/// Represents a logical unit of `0_f64`.
pub const ZERO: PixelUnit = PixelUnit::Logical(LogicalUnit::new(0.0));
pub fn new<S: Into<PixelUnit>>(unit: S) -> PixelUnit {
unit.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalUnit<P> {
match *self {
PixelUnit::Physical(unit) => unit.to_logical(scale_factor),
PixelUnit::Logical(unit) => unit.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<P> {
match *self {
PixelUnit::Physical(unit) => unit.cast(),
PixelUnit::Logical(unit) => unit.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for PixelUnit {
#[inline]
fn from(unit: PhysicalUnit<P>) -> PixelUnit {
PixelUnit::Physical(unit.cast())
}
}
impl<P: Pixel> From<LogicalUnit<P>> for PixelUnit {
#[inline]
fn from(unit: LogicalUnit<P>) -> PixelUnit {
PixelUnit::Logical(unit.cast())
}
}
/// A position represented in logical pixels. /// A position represented in logical pixels.
/// ///
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the /// The position is stored as floats, so please be careful. Casting floats to integers truncates the
@@ -395,7 +166,10 @@ impl<P: Pixel> LogicalPosition<P> {
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> { pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> {
LogicalPosition { x: self.x.cast(), y: self.y.cast() } LogicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
} }
} }
@@ -471,7 +245,10 @@ impl<P: Pixel> PhysicalPosition<P> {
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> { pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> {
PhysicalPosition { x: self.x.cast(), y: self.y.cast() } PhysicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
} }
} }
@@ -547,7 +324,10 @@ impl<P: Pixel> LogicalSize<P> {
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> LogicalSize<X> { pub fn cast<X: Pixel>(&self) -> LogicalSize<X> {
LogicalSize { width: self.width.cast(), height: self.height.cast() } LogicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
} }
} }
@@ -585,7 +365,10 @@ impl<P: Pixel> From<mint::Vector2<P>> for LogicalSize<P> {
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> { impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> {
fn from(s: LogicalSize<P>) -> Self { fn from(s: LogicalSize<P>) -> Self {
mint::Vector2 { x: s.width, y: s.height } mint::Vector2 {
x: s.width,
y: s.height,
}
} }
} }
@@ -620,7 +403,10 @@ impl<P: Pixel> PhysicalSize<P> {
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> { pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> {
PhysicalSize { width: self.width.cast(), height: self.height.cast() } PhysicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
} }
} }
@@ -658,7 +444,10 @@ impl<P: Pixel> From<mint::Vector2<P>> for PhysicalSize<P> {
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> { impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> {
fn from(s: PhysicalSize<P>) -> Self { fn from(s: PhysicalSize<P>) -> Self {
mint::Vector2 { x: s.width, y: s.height } mint::Vector2 {
x: s.width,
y: s.height,
}
} }
} }
@@ -759,155 +548,10 @@ impl<P: Pixel> From<LogicalPosition<P>> for Position {
} }
} }
/// The logical distance between the edges of two rectangles.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalInsets<P> {
/// The distance to the top edge.
pub top: P,
/// The distance to the left edge.
pub left: P,
/// The distance to the bottom edge.
pub bottom: P,
/// The distance to the right edge.
pub right: P,
}
impl<P> LogicalInsets<P> {
#[inline]
pub const fn new(top: P, left: P, bottom: P, right: P) -> Self {
Self { top, left, bottom, right }
}
}
impl<P: Pixel> LogicalInsets<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalInsets<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalInsets<X> {
assert!(validate_scale_factor(scale_factor));
let top = self.top.into() * scale_factor;
let left = self.left.into() * scale_factor;
let bottom = self.bottom.into() * scale_factor;
let right = self.right.into() * scale_factor;
PhysicalInsets::new(top, left, bottom, right).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalInsets<X> {
LogicalInsets {
top: self.top.cast(),
left: self.left.cast(),
bottom: self.bottom.cast(),
right: self.right.cast(),
}
}
}
/// The physical distance between the edges of two rectangles.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalInsets<P> {
/// The distance to the top edge.
pub top: P,
/// The distance to the left edge.
pub left: P,
/// The distance to the bottom edge.
pub bottom: P,
/// The distance to the right edge.
pub right: P,
}
impl<P> PhysicalInsets<P> {
#[inline]
pub const fn new(top: P, left: P, bottom: P, right: P) -> Self {
Self { top, left, bottom, right }
}
}
impl<P: Pixel> PhysicalInsets<P> {
#[inline]
pub fn from_logical<T: Into<LogicalInsets<X>>, X: Pixel>(
logical: T,
scale_factor: f64,
) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalInsets<X> {
assert!(validate_scale_factor(scale_factor));
let top = self.top.into() / scale_factor;
let left = self.left.into() / scale_factor;
let bottom = self.bottom.into() / scale_factor;
let right = self.right.into() / scale_factor;
LogicalInsets::new(top, left, bottom, right).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalInsets<X> {
PhysicalInsets {
top: self.top.cast(),
left: self.left.cast(),
bottom: self.bottom.cast(),
right: self.right.cast(),
}
}
}
/// Insets that are either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Insets {
Physical(PhysicalInsets<u32>),
Logical(LogicalInsets<f64>),
}
impl Insets {
pub fn new<S: Into<Self>>(insets: S) -> Self {
insets.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalInsets<P> {
match *self {
Self::Physical(insets) => insets.to_logical(scale_factor),
Self::Logical(insets) => insets.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalInsets<P> {
match *self {
Self::Physical(insets) => insets.cast(),
Self::Logical(insets) => insets.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalInsets<P>> for Insets {
#[inline]
fn from(insets: PhysicalInsets<P>) -> Self {
Self::Physical(insets.cast())
}
}
impl<P: Pixel> From<LogicalInsets<P>> for Insets {
#[inline]
fn from(insets: LogicalInsets<P>) -> Self {
Self::Logical(insets.cast())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::HashSet;
use super::*; use super::*;
use std::collections::HashSet;
macro_rules! test_pixel_int_impl { macro_rules! test_pixel_int_impl {
($($name:ident => $ty:ty),*) => {$( ($($name:ident => $ty:ty),*) => {$(
@@ -968,7 +612,12 @@ mod tests {
macro_rules! assert_approx_eq { macro_rules! assert_approx_eq {
($a:expr, $b:expr $(,)?) => { ($a:expr, $b:expr $(,)?) => {
assert!(($a - $b).abs() < 0.001, "{} is not approximately equal to {}", $a, $b); assert!(
($a - $b).abs() < 0.001,
"{} is not approximately equal to {}",
$a,
$b
);
}; };
} }
@@ -1081,41 +730,28 @@ mod tests {
assert!(!validate_scale_factor(f64::NEG_INFINITY)); assert!(!validate_scale_factor(f64::NEG_INFINITY));
} }
#[test]
fn test_logical_unity() {
let log_unit = LogicalUnit::new(1.0);
assert_eq!(log_unit.to_physical::<u32>(1.0), PhysicalUnit::new(1));
assert_eq!(log_unit.to_physical::<u32>(2.0), PhysicalUnit::new(2));
assert_eq!(log_unit.cast::<u32>(), LogicalUnit::new(1));
assert_eq!(log_unit, LogicalUnit::from_physical(PhysicalUnit::new(1.0), 1.0));
assert_eq!(log_unit, LogicalUnit::from_physical(PhysicalUnit::new(2.0), 2.0));
assert_eq!(LogicalUnit::from(2.0), LogicalUnit::new(2.0));
let x: f64 = log_unit.into();
assert_eq!(x, 1.0);
}
#[test]
fn test_physical_unit() {
assert_eq!(PhysicalUnit::from_logical(LogicalUnit::new(1.0), 1.0), PhysicalUnit::new(1));
assert_eq!(PhysicalUnit::from_logical(LogicalUnit::new(2.0), 0.5), PhysicalUnit::new(1));
assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0,));
assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0));
let x: f64 = PhysicalUnit::new(1).into();
assert_eq!(x, 1.0);
}
#[test] #[test]
fn test_logical_position() { fn test_logical_position() {
let log_pos = LogicalPosition::new(1.0, 2.0); let log_pos = LogicalPosition::new(1.0, 2.0);
assert_eq!(log_pos.to_physical::<u32>(1.0), PhysicalPosition::new(1, 2)); assert_eq!(log_pos.to_physical::<u32>(1.0), PhysicalPosition::new(1, 2));
assert_eq!(log_pos.to_physical::<u32>(2.0), PhysicalPosition::new(2, 4)); assert_eq!(log_pos.to_physical::<u32>(2.0), PhysicalPosition::new(2, 4));
assert_eq!(log_pos.cast::<u32>(), LogicalPosition::new(1, 2)); assert_eq!(log_pos.cast::<u32>(), LogicalPosition::new(1, 2));
assert_eq!(log_pos, LogicalPosition::from_physical(PhysicalPosition::new(1.0, 2.0), 1.0)); assert_eq!(
assert_eq!(log_pos, LogicalPosition::from_physical(PhysicalPosition::new(2.0, 4.0), 2.0)); log_pos,
assert_eq!(LogicalPosition::from((2.0, 2.0)), LogicalPosition::new(2.0, 2.0)); LogicalPosition::from_physical(PhysicalPosition::new(1.0, 2.0), 1.0)
assert_eq!(LogicalPosition::from([2.0, 3.0]), LogicalPosition::new(2.0, 3.0)); );
assert_eq!(
log_pos,
LogicalPosition::from_physical(PhysicalPosition::new(2.0, 4.0), 2.0)
);
assert_eq!(
LogicalPosition::from((2.0, 2.0)),
LogicalPosition::new(2.0, 2.0)
);
assert_eq!(
LogicalPosition::from([2.0, 3.0]),
LogicalPosition::new(2.0, 3.0)
);
let x: (f64, f64) = log_pos.into(); let x: (f64, f64) = log_pos.into();
assert_eq!(x, (1.0, 2.0)); assert_eq!(x, (1.0, 2.0));
@@ -1133,8 +769,14 @@ mod tests {
PhysicalPosition::from_logical(LogicalPosition::new(2.0, 4.0), 0.5), PhysicalPosition::from_logical(LogicalPosition::new(2.0, 4.0), 0.5),
PhysicalPosition::new(1, 2) PhysicalPosition::new(1, 2)
); );
assert_eq!(PhysicalPosition::from((2.0, 2.0)), PhysicalPosition::new(2.0, 2.0)); assert_eq!(
assert_eq!(PhysicalPosition::from([2.0, 3.0]), PhysicalPosition::new(2.0, 3.0)); PhysicalPosition::from((2.0, 2.0)),
PhysicalPosition::new(2.0, 2.0)
);
assert_eq!(
PhysicalPosition::from([2.0, 3.0]),
PhysicalPosition::new(2.0, 3.0)
);
let x: (f64, f64) = PhysicalPosition::new(1, 2).into(); let x: (f64, f64) = PhysicalPosition::new(1, 2).into();
assert_eq!(x, (1.0, 2.0)); assert_eq!(x, (1.0, 2.0));
@@ -1148,8 +790,14 @@ mod tests {
assert_eq!(log_size.to_physical::<u32>(1.0), PhysicalSize::new(1, 2)); assert_eq!(log_size.to_physical::<u32>(1.0), PhysicalSize::new(1, 2));
assert_eq!(log_size.to_physical::<u32>(2.0), PhysicalSize::new(2, 4)); assert_eq!(log_size.to_physical::<u32>(2.0), PhysicalSize::new(2, 4));
assert_eq!(log_size.cast::<u32>(), LogicalSize::new(1, 2)); assert_eq!(log_size.cast::<u32>(), LogicalSize::new(1, 2));
assert_eq!(log_size, LogicalSize::from_physical(PhysicalSize::new(1.0, 2.0), 1.0)); assert_eq!(
assert_eq!(log_size, LogicalSize::from_physical(PhysicalSize::new(2.0, 4.0), 2.0)); log_size,
LogicalSize::from_physical(PhysicalSize::new(1.0, 2.0), 1.0)
);
assert_eq!(
log_size,
LogicalSize::from_physical(PhysicalSize::new(2.0, 4.0), 2.0)
);
assert_eq!(LogicalSize::from((2.0, 2.0)), LogicalSize::new(2.0, 2.0)); assert_eq!(LogicalSize::from((2.0, 2.0)), LogicalSize::new(2.0, 2.0));
assert_eq!(LogicalSize::from([2.0, 3.0]), LogicalSize::new(2.0, 3.0)); assert_eq!(LogicalSize::from([2.0, 3.0]), LogicalSize::new(2.0, 3.0));
@@ -1180,7 +828,10 @@ mod tests {
#[test] #[test]
fn test_size() { fn test_size() {
assert_eq!(Size::new(PhysicalSize::new(1, 2)), Size::Physical(PhysicalSize::new(1, 2))); assert_eq!(
Size::new(PhysicalSize::new(1, 2)),
Size::Physical(PhysicalSize::new(1, 2))
);
assert_eq!( assert_eq!(
Size::new(LogicalSize::new(1.0, 2.0)), Size::new(LogicalSize::new(1.0, 2.0)),
Size::Logical(LogicalSize::new(1.0, 2.0)) Size::Logical(LogicalSize::new(1.0, 2.0))
@@ -1285,38 +936,4 @@ mod tests {
let _ = format!("{:?}", Size::Physical((1, 2).into()).clone()); let _ = format!("{:?}", Size::Physical((1, 2).into()).clone());
let _ = format!("{:?}", Position::Physical((1, 2).into()).clone()); let _ = format!("{:?}", Position::Physical((1, 2).into()).clone());
} }
#[test]
fn ensure_copy_trait() {
fn is_copy<T: Copy>() {}
is_copy::<LogicalUnit<i32>>();
is_copy::<PhysicalUnit<f64>>();
is_copy::<PixelUnit>();
is_copy::<LogicalSize<i32>>();
is_copy::<PhysicalSize<f64>>();
is_copy::<Size>();
is_copy::<LogicalPosition<i32>>();
is_copy::<PhysicalPosition<f64>>();
is_copy::<Position>();
}
#[test]
fn ensure_partial_eq_trait() {
fn is_partial_eq<T: PartialEq>() {}
is_partial_eq::<LogicalUnit<i32>>();
is_partial_eq::<PhysicalUnit<f64>>();
is_partial_eq::<PixelUnit>();
is_partial_eq::<LogicalSize<i32>>();
is_partial_eq::<PhysicalSize<f64>>();
is_partial_eq::<Size>();
is_partial_eq::<LogicalPosition<i32>>();
is_partial_eq::<PhysicalPosition<f64>>();
is_partial_eq::<Position>();
}
} }

View File

@@ -1,85 +1,25 @@
#[cfg(any(x11_platform, macos_platform, windows_platform))] #[cfg(all(
feature = "rwh_06",
any(x11_platform, macos_platform, windows_platform)
))]
#[allow(deprecated)] #[allow(deprecated)]
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
use std::collections::HashMap; use std::collections::HashMap;
use winit::application::ApplicationHandler;
use winit::dpi::{LogicalPosition, LogicalSize, Position}; use winit::dpi::{LogicalPosition, LogicalSize, Position};
use winit::event::{ElementState, KeyEvent, WindowEvent}; use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::raw_window_handle::HasRawWindowHandle; use winit::raw_window_handle::HasRawWindowHandle;
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::Window;
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
struct Application { fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window {
parent_window_id: WindowId,
windows: HashMap<WindowId, Box<dyn Window>>,
}
impl ApplicationHandler for Application {
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: winit::window::WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::CloseRequested => {
self.windows.clear();
event_loop.exit();
},
WindowEvent::PointerEntered { device_id: _, .. } => {
// On x11, println when the cursor entered in a window even if the child window
// is created by some key inputs.
// 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.
println!("cursor entered in the window {window_id:?}");
},
WindowEvent::KeyboardInput {
event: KeyEvent { state: ElementState::Pressed, .. },
..
} => {
let parent_window = self.windows.get(&self.parent_window_id).unwrap();
let child_window = spawn_child_window(parent_window.as_ref(), event_loop);
let child_id = child_window.id();
println!("Child window created with id: {child_id:?}");
self.windows.insert(child_id, child_window);
},
WindowEvent::RedrawRequested => {
if let Some(window) = self.windows.get(&window_id) {
fill::fill_window(window.as_ref());
}
},
_ => (),
}
}
}
fn init(event_loop: &dyn ActiveEventLoop) -> Application {
let attributes = WindowAttributes::default()
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
let window = event_loop.create_window(attributes).unwrap();
println!("Parent window id: {:?})", window.id());
let parent_window_id = window.id();
let windows = HashMap::from([(window.id(), window)]);
Application { parent_window_id, windows }
}
fn spawn_child_window(
parent: &dyn Window,
event_loop: &dyn ActiveEventLoop,
) -> Box<dyn Window> {
let parent = parent.raw_window_handle().unwrap(); let parent = parent.raw_window_handle().unwrap();
let mut window_attributes = WindowAttributes::default() let mut window_attributes = Window::default_attributes()
.with_title("child window") .with_title("child window")
.with_surface_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.
@@ -88,14 +28,67 @@ fn main() -> Result<(), impl std::error::Error> {
event_loop.create_window(window_attributes).unwrap() event_loop.create_window(window_attributes).unwrap()
} }
let event_loop = EventLoop::new().unwrap(); let mut windows = HashMap::new();
event_loop.run(init)
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
let mut parent_window_id = None;
event_loop.run(move |event: Event<()>, event_loop| {
match event {
Event::Resumed => {
let attributes = Window::default_attributes()
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
let window = event_loop.create_window(attributes).unwrap();
parent_window_id = Some(window.id());
println!("Parent window id: {parent_window_id:?})");
windows.insert(window.id(), window);
}
Event::WindowEvent { window_id, event } => match event {
WindowEvent::CloseRequested => {
windows.clear();
event_loop.exit();
}
WindowEvent::CursorEntered { device_id: _ } => {
// On x11, println when the cursor entered in a window even if the child window is created
// by some key inputs.
// 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.
println!("cursor entered in the window {window_id:?}");
}
WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
..
},
..
} => {
let parent_window = windows.get(&parent_window_id.unwrap()).unwrap();
let child_window = spawn_child_window(parent_window, event_loop);
let child_id = child_window.id();
println!("Child window created with id: {child_id:?}");
windows.insert(child_id, child_window);
}
WindowEvent::RedrawRequested => {
if let Some(window) = windows.get(&window_id) {
fill::fill_window(window);
}
}
_ => (),
},
_ => (),
}
})
} }
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))] #[cfg(all(
feature = "rwh_06",
not(any(x11_platform, macos_platform, windows_platform))
))]
fn main() { fn main() {
panic!( panic!("This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature enabled.");
"This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature \
enabled."
);
} }

View File

@@ -3,151 +3,131 @@
use std::thread; use std::thread;
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
use std::time; use std::time;
use ::tracing::{info, warn};
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time as time; use web_time as time;
use winit::application::ApplicationHandler;
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent}; use simple_logger::SimpleLogger;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::{
use winit::keyboard::{Key, NamedKey}; event::{ElementState, Event, KeyEvent, WindowEvent},
use winit::window::{Window, WindowAttributes, WindowId}; event_loop::{ControlFlow, EventLoop},
keyboard::{Key, NamedKey},
window::Window,
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
#[path = "util/tracing.rs"]
mod tracing;
const WAIT_TIME: time::Duration = time::Duration::from_millis(100); #[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> {
#[cfg(web_platform)] SimpleLogger::new().init().unwrap();
console_error_panic_hook::set_once();
tracing::init(); println!("Press '1' to switch to Wait mode.");
println!("Press '2' to switch to WaitUntil mode.");
info!("Press '1' to switch to Wait mode."); println!("Press '3' to switch to Poll mode.");
info!("Press '2' to switch to WaitUntil mode."); println!("Press 'R' to toggle request_redraw() calls.");
info!("Press '3' to switch to Poll mode."); println!("Press 'Esc' to close the window.");
info!("Press 'R' to toggle request_redraw() calls.");
info!("Press 'Esc' to close the window.");
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
event_loop.run(ControlFlowDemo::new) let mut mode = Mode::Wait;
} let mut request_redraw = false;
let mut wait_cancelled = false;
let mut close_requested = false;
struct ControlFlowDemo { let mut window = None;
mode: Mode, event_loop.run(move |event, event_loop| {
request_redraw: bool, use winit::event::StartCause;
wait_cancelled: bool, println!("{event:?}");
close_requested: bool, match event {
window: Box<dyn Window>, Event::NewEvents(start_cause) => {
} wait_cancelled = match start_cause {
StartCause::WaitCancelled { .. } => mode == Mode::WaitUntil,
impl ControlFlowDemo {
fn new(event_loop: &dyn ActiveEventLoop) -> Self {
let window_attributes = WindowAttributes::default().with_title(
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
);
let window = event_loop.create_window(window_attributes).unwrap();
Self {
mode: Mode::default(),
request_redraw: false,
wait_cancelled: false,
close_requested: false,
window,
}
}
}
impl ApplicationHandler for ControlFlowDemo {
fn new_events(&mut self, _event_loop: &dyn ActiveEventLoop, cause: StartCause) {
info!("new_events: {cause:?}");
self.wait_cancelled = match cause {
StartCause::WaitCancelled { .. } => self.mode == Mode::WaitUntil,
_ => false, _ => false,
} }
} }
Event::Resumed => {
fn window_event( let window_attributes = Window::default_attributes().with_title(
&mut self, "Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
_event_loop: &dyn ActiveEventLoop, );
_window_id: WindowId, window = Some(event_loop.create_window(window_attributes).unwrap());
event: WindowEvent, }
) { Event::WindowEvent { event, .. } => match event {
info!("{event:?}");
match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
self.close_requested = true; close_requested = true;
}, }
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
event: KeyEvent { logical_key: key, state: ElementState::Pressed, .. }, event:
KeyEvent {
logical_key: key,
state: ElementState::Pressed,
..
},
.. ..
} => match key.as_ref() { } => match key.as_ref() {
// WARNING: Consider using `key_without_modifiers()` if available on your platform. // WARNING: Consider using `key_without_modifiers()` if available on your platform.
// See the `key_binding` example // See the `key_binding` example
Key::Character("1") => { Key::Character("1") => {
self.mode = Mode::Wait; mode = Mode::Wait;
warn!("mode: {:?}", self.mode); println!("\nmode: {mode:?}\n");
}, }
Key::Character("2") => { Key::Character("2") => {
self.mode = Mode::WaitUntil; mode = Mode::WaitUntil;
warn!("mode: {:?}", self.mode); println!("\nmode: {mode:?}\n");
}, }
Key::Character("3") => { Key::Character("3") => {
self.mode = Mode::Poll; mode = Mode::Poll;
warn!("mode: {:?}", self.mode); println!("\nmode: {mode:?}\n");
}, }
Key::Character("r") => { Key::Character("r") => {
self.request_redraw = !self.request_redraw; request_redraw = !request_redraw;
warn!("request_redraw: {}", self.request_redraw); println!("\nrequest_redraw: {request_redraw}\n");
}, }
Key::Named(NamedKey::Escape) => { Key::Named(NamedKey::Escape) => {
self.close_requested = true; close_requested = true;
}, }
_ => (), _ => (),
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
self.window.pre_present_notify(); let window = window.as_ref().unwrap();
fill::fill_window(&*self.window); window.pre_present_notify();
}, fill::fill_window(window);
}
_ => (), _ => (),
} },
Event::AboutToWait => {
if request_redraw && !wait_cancelled && !close_requested {
window.as_ref().unwrap().request_redraw();
} }
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { match mode {
if self.request_redraw && !self.wait_cancelled && !self.close_requested {
self.window.request_redraw();
}
match self.mode {
Mode::Wait => event_loop.set_control_flow(ControlFlow::Wait), Mode::Wait => event_loop.set_control_flow(ControlFlow::Wait),
Mode::WaitUntil => { Mode::WaitUntil => {
if !self.wait_cancelled { if !wait_cancelled {
event_loop event_loop.set_control_flow(ControlFlow::WaitUntil(
.set_control_flow(ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME)); time::Instant::now() + WAIT_TIME,
));
}
} }
},
Mode::Poll => { Mode::Poll => {
thread::sleep(POLL_SLEEP_TIME); thread::sleep(POLL_SLEEP_TIME);
event_loop.set_control_flow(ControlFlow::Poll); event_loop.set_control_flow(ControlFlow::Poll);
}, }
}; };
if self.close_requested { if close_requested {
event_loop.exit(); event_loop.exit();
} }
} }
_ => (),
}
})
} }

847
examples/full.rs Normal file
View File

@@ -0,0 +1,847 @@
//! An example showcasing various functionality that Winit has.
//!
//! Often used by Winit developers to test certain functionality, may be a bit
//! verbose.
use std::collections::HashMap;
use std::error::Error;
#[cfg(not(any(android_platform, ios_platform)))]
use std::num::NonZeroU32;
use cursor_icon::CursorIcon;
#[cfg(not(any(android_platform, ios_platform)))]
use rwh_05::HasRawDisplayHandle;
#[cfg(not(any(android_platform, ios_platform)))]
use softbuffer::{Context, Surface};
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event::{DeviceEvent, DeviceId, ElementState, Event, Ime, KeyEvent, WindowEvent};
use winit::event::{MouseButton, MouseScrollDelta};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::keyboard::{Key, ModifiersState};
use winit::window::{
Cursor, CursorGrabMode, CustomCursor, Fullscreen, Icon, ResizeDirection, Theme,
};
use winit::window::{Window, WindowId};
#[cfg(macos_platform)]
use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS};
#[cfg(any(x11_platform, wayland_platform))]
use winit::platform::startup_notify::{
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
};
fn main() -> Result<(), Box<dyn Error>> {
let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
let _event_loop_proxy = event_loop.create_proxy();
// Wire the user event from another thread.
#[cfg(not(web_platform))]
std::thread::spawn(move || {
// Wake up the `event_loop` once every second and dispatch a custom event
// from a different thread.
println!("Starting to send user event every second");
loop {
let _ = _event_loop_proxy.send_event(UserEvent::WakeUp);
std::thread::sleep(std::time::Duration::from_secs(1));
}
});
let mut app = Application::new(&event_loop);
event_loop.run(move |event, event_loop| match event {
Event::NewEvents(_) => (),
Event::Resumed => {
println!("Resumed the event loop");
// Create initial window.
app.create_window(event_loop, None)
.expect("failed to create initial window");
app.print_help();
}
Event::AboutToWait => {
if app.windows.is_empty() {
println!("No windows left, exiting...");
event_loop.exit();
}
}
Event::WindowEvent { window_id, event } => {
app.handle_window_event(event_loop, window_id, event)
}
Event::DeviceEvent { device_id, event } => {
app.handle_device_event(event_loop, device_id, event)
}
Event::UserEvent(event) => {
println!("User event: {event:?}");
}
Event::Suspended | Event::LoopExiting | Event::MemoryWarning => (),
})?;
Ok(())
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
enum UserEvent {
WakeUp,
}
/// Application state and event handling.
struct Application {
/// Custom cursors assets.
custom_cursors: Vec<CustomCursor>,
/// Application icon.
icon: Icon,
windows: HashMap<WindowId, WindowState>,
/// Drawing context.
///
/// With OpenGL it could be EGLDisplay.
#[cfg(not(any(android_platform, ios_platform)))]
context: Context,
}
impl Application {
fn new<T>(event_loop: &EventLoop<T>) -> Self {
// SAFETY: the context is dropped inside the loop, since the state we're using
// is moved inside the closure.
#[cfg(not(any(android_platform, ios_platform)))]
let context = unsafe { Context::from_raw(event_loop.raw_display_handle()).unwrap() };
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
// 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/data/icon.png");
// Load icon
let icon = {
let image = image::open(path)
.expect("Failed to open icon path")
.into_rgba8();
let (width, height) = image.dimensions();
let rgba = image.into_raw();
Icon::from_rgba(rgba, width, height).expect("Failed to open icon")
};
println!("Loading cursor assets");
let decode_cursor = |bytes| {
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);
CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap()
};
let custom_cursors = vec![
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross.png"))),
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross2.png"))),
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/gradient.png"))),
];
Self {
#[cfg(not(any(android_platform, ios_platform)))]
context,
custom_cursors,
icon,
windows: Default::default(),
}
}
fn create_window(
&mut self,
event_loop: &ActiveEventLoop,
_tab_id: Option<String>,
) -> Result<WindowId, Box<dyn Error>> {
// TODO read-out activation token.
#[allow(unused_mut)]
let mut window_attributes = Window::default_attributes()
.with_title("Winit window")
.with_transparent(true)
.with_window_icon(Some(self.icon.clone()));
#[cfg(any(x11_platform, wayland_platform))]
if let Some(token) = event_loop.read_token_from_env() {
startup_notify::reset_activation_token_env();
println!("Using token {:?} to activate a window", token);
window_attributes = window_attributes.with_activation_token(token);
}
#[cfg(macos_platform)]
if let Some(tab_id) = _tab_id {
window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
}
let window = event_loop.create_window(window_attributes)?;
#[cfg(ios_platform)]
{
use winit::platform::ios::WindowExtIOS;
window.recognize_doubletap_gesture(true);
window.recognize_pinch_gesture(true);
window.recognize_rotation_gesture(true);
}
#[cfg(not(any(android_platform, ios_platform)))]
let surface = {
// SAFETY: the surface is dropped before the `window` which
// provided it with handle, thus it doesn't outlive it.
let mut surface = unsafe { Surface::new(&self.context, &window)? };
let size = window.inner_size();
if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
surface
.resize(width, height)
.expect("failed to resize inner buffer");
};
surface
};
let theme = window.theme().unwrap_or(Theme::Dark);
println!("Theme: {theme:?}");
// Allow IME out of the box.
let ime = true;
window.set_ime_allowed(ime);
let state = WindowState {
window,
custom_idx: self.custom_cursors.len() - 1,
cursor_grab: CursorGrabMode::None,
named_idx: 0,
#[cfg(not(any(android_platform, ios_platform)))]
surface,
theme,
ime,
cursor_position: Default::default(),
cursor_hidden: Default::default(),
modifiers: Default::default(),
occluded: Default::default(),
rotated: Default::default(),
zoom: Default::default(),
};
let window_id = state.window.id();
println!("Created new window with id={window_id:?}");
self.windows.insert(window_id, state);
Ok(window_id)
}
fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
let state = self.windows.get_mut(&window_id).unwrap();
let window = &state.window;
println!("Executing action: {action:?}");
match action {
Action::CloseWindow => {
let _ = self.windows.remove(&window_id);
}
Action::CreateNewWindow => {
#[cfg(any(x11_platform, wayland_platform))]
if let Err(err) = window.request_activation_token() {
println!("Failed to get activation token: {err}");
} else {
return;
}
if let Err(err) = self.create_window(event_loop, None) {
eprintln!("Error creating new window: {err}");
}
}
Action::ToggleResizeIncrements => {
let new_increments = match window.resize_increments() {
Some(_) => None,
None => Some(LogicalSize::new(25.0, 25.0)),
};
println!("Had increments: {}", new_increments.is_none());
window.set_resize_increments(new_increments);
}
Action::ToggleCursorVisibility => {
state.cursor_hidden = !state.cursor_hidden;
window.set_cursor_visible(!state.cursor_hidden);
}
Action::ToggleResizable => {
let resizable = window.is_resizable();
window.set_resizable(!resizable);
}
Action::ToggleDecorations => {
let decorated = window.is_decorated();
window.set_decorations(!decorated);
}
Action::ToggleFullscreen => {
let fullscreen = if window.fullscreen().is_some() {
None
} else {
Some(Fullscreen::Borderless(None))
};
window.set_fullscreen(fullscreen);
}
Action::ToggleMaximize => {
let maximized = window.is_maximized();
window.set_maximized(!maximized);
}
Action::ToggleImeInput => {
state.ime = !state.ime;
window.set_ime_allowed(state.ime);
if let Some(position) = state.ime.then_some(state.cursor_position).flatten() {
window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
}
}
Action::Minimize => {
window.set_minimized(true);
}
Action::NextCursor => {
// Cursor list to cycle through.
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,
];
// Pick the next cursor
state.named_idx = (state.named_idx + 1) % CURSORS.len();
println!("Setting cursor to \"{:?}\"", CURSORS[state.named_idx]);
window.set_cursor(Cursor::Icon(CURSORS[state.named_idx]));
}
Action::NextCustomCursor => {
state.custom_idx = (state.custom_idx + 1) % self.custom_cursors.len();
let cursor = Cursor::Custom(self.custom_cursors[state.custom_idx].clone());
window.set_cursor(cursor);
}
Action::CycleCursorGrab => {
state.cursor_grab = match state.cursor_grab {
CursorGrabMode::None => CursorGrabMode::Confined,
CursorGrabMode::Confined => CursorGrabMode::Locked,
CursorGrabMode::Locked => CursorGrabMode::None,
};
println!("Changing cursor grab mode to {:?}", state.cursor_grab);
if let Err(err) = window.set_cursor_grab(state.cursor_grab) {
eprintln!("Error setting cursor grab: {err}");
}
}
Action::DragWindow => {
if let Err(err) = window.drag_window() {
println!("Error starting window drag: {err}");
} else {
println!("Dragging window Window={:?}", window.id());
}
}
Action::DragResizeWindow => {
let position = match state.cursor_position {
Some(position) => position,
None => {
println!("Drag-resize requires cursor to be inside the window");
return;
}
};
// The amount of points around the window.
const BORDER_SIZE: f64 = 20.0;
let win_size = window.inner_size();
let border_size = BORDER_SIZE * window.scale_factor();
let x_direction = if position.x < border_size {
ResizeDirection::West
} else if position.x > (win_size.width as f64 - border_size) {
ResizeDirection::East
} else {
// Use arbitrary direction instead of None for simplicity.
ResizeDirection::SouthEast
};
let y_direction = if position.y < border_size {
ResizeDirection::North
} else if position.y > (win_size.height as f64 - border_size) {
ResizeDirection::South
} else {
// Use arbitrary direction instead of None for simplicity.
ResizeDirection::SouthEast
};
let direction = match (x_direction, y_direction) {
(ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
(ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
(ResizeDirection::West, _) => ResizeDirection::West,
(ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
(ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
(ResizeDirection::East, _) => ResizeDirection::East,
(_, ResizeDirection::South) => ResizeDirection::South,
(_, ResizeDirection::North) => ResizeDirection::North,
_ => return,
};
if let Err(err) = window.drag_resize_window(direction) {
println!("Error starting window drag-resize: {err}");
} else {
println!("Drag-resizing window Window={:?}", window.id());
}
}
Action::ShowWindowMenu => {
if let Some(position) = state.cursor_position {
window.show_window_menu(position);
}
}
Action::PrintHelp => self.print_help(),
#[cfg(macos_platform)]
Action::CycleOptionAsAlt => {
let new = match window.option_as_alt() {
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
OptionAsAlt::Both => OptionAsAlt::None,
};
println!("Setting option as alt {:?}", new);
window.set_option_as_alt(new);
}
#[cfg(macos_platform)]
Action::CreateNewTab => {
let tab_id = window.tabbing_identifier();
if let Err(err) = self.create_window(event_loop, Some(tab_id)) {
eprintln!("Error creating new window: {err}");
}
}
}
}
fn handle_window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
let state = match self.windows.get_mut(&window_id) {
Some(state) => state,
None => return,
};
let window = &state.window;
match event {
// Resize the surface to the new size
WindowEvent::Resized(_size) => {
#[cfg(not(any(android_platform, ios_platform)))]
{
let (width, height) =
match (NonZeroU32::new(_size.width), NonZeroU32::new(_size.height)) {
(Some(width), Some(height)) => (width, height),
_ => return,
};
state
.surface
.resize(width, height)
.expect("failed to resize inner buffer");
}
window.request_redraw();
}
WindowEvent::Focused(focused) => {
if focused {
println!("Window={window_id:?} fosused");
} else {
println!("Window={window_id:?} unfosused");
}
}
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
println!("Window={window_id:?} changed scale to {scale_factor}");
}
WindowEvent::ThemeChanged(theme) => {
println!("Theme changed to {theme:?}");
state.theme = theme;
window.request_redraw();
}
#[cfg(not(any(android_platform, ios_platform)))]
WindowEvent::RedrawRequested => {
// Draw the window contents.
if state.occluded {
println!("Skipping drawing occluded window={:?}", window_id);
}
const WHITE: u32 = 0xFFFFFFFF;
const DARK_GRAY: u32 = 0xFF181818;
let color = match state.theme {
Theme::Light => WHITE,
Theme::Dark => DARK_GRAY,
};
let mut buffer = state
.surface
.buffer_mut()
.expect("could not retrieve buffer");
buffer.fill(color);
window.pre_present_notify();
buffer.present().expect("failed presenting to window");
}
#[cfg(any(android_platform, ios_platform))]
WindowEvent::RedrawRequested => {
println!("Drawing but without rendering...");
}
// Change window occlusion state.
WindowEvent::Occluded(occluded) => {
state.occluded = occluded;
if !occluded {
window.request_redraw();
}
}
WindowEvent::CloseRequested => {
println!("Closing Window={window_id:?}");
self.windows.remove(&window_id);
}
WindowEvent::ModifiersChanged(modifiers) => {
state.modifiers = modifiers.state();
println!("Modifiers changed to {:?}", state.modifiers);
}
WindowEvent::MouseWheel { delta, .. } => match delta {
MouseScrollDelta::LineDelta(x, y) => {
println!("Mouse wheel Line Delta: ({x},{y})");
}
MouseScrollDelta::PixelDelta(px) => {
println!("Mouse wheel Pixel Delta: ({},{})", px.x, px.y);
}
},
WindowEvent::KeyboardInput {
event:
KeyEvent {
// Dispatch actions only on press.
state: ElementState::Pressed,
logical_key,
..
},
is_synthetic: false,
..
} => {
if let Key::Character(ch) = logical_key.as_ref() {
let mods = state.modifiers;
if let Some(action) = Self::process_key_binding(&ch.to_uppercase(), &mods) {
self.handle_action(event_loop, window_id, action);
}
}
}
WindowEvent::KeyboardInput { .. } => {}
WindowEvent::MouseInput {
button,
state: ElementState::Pressed,
..
} => {
if let Some(action) = Self::process_mouse_binding(button, &state.modifiers) {
self.handle_action(event_loop, window_id, action);
}
}
WindowEvent::MouseInput { .. } => {}
WindowEvent::CursorLeft { .. } => {
println!("Cursor left Window={window_id:?}");
state.cursor_position = None;
}
WindowEvent::CursorMoved { position, .. } => {
println!("Moved cursor to {position:?}");
state.cursor_position = Some(position);
if state.ime {
window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
}
}
WindowEvent::ActivationTokenDone { token: _token, .. } => {
#[cfg(any(x11_platform, wayland_platform))]
{
startup_notify::set_activation_token_env(_token);
if let Err(err) = self.create_window(event_loop, None) {
eprintln!("Error creating new window: {err}");
}
}
}
WindowEvent::Ime(event) => match event {
Ime::Enabled => println!("IME enabled for Window={window_id:?}"),
Ime::Preedit(text, caret_pos) => {
println!("Preedit: {}, with caret at {:?}", text, caret_pos);
}
Ime::Commit(text) => {
println!("Commited: {}", text);
}
Ime::Disabled => println!("IME disabled for Window={window_id:?}"),
},
WindowEvent::PinchGesture { delta, .. } => {
state.zoom += delta;
let zoom = state.zoom;
if delta > 0.0 {
println!("Zoomed in {delta:.5} (now: {zoom:.5})");
} else {
println!("Zoomed out {delta:.5} (now: {zoom:.5})");
}
}
WindowEvent::RotationGesture { delta, .. } => {
state.rotated += delta;
let rotated = state.rotated;
if delta > 0.0 {
println!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
} else {
println!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
}
}
WindowEvent::DoubleTapGesture { .. } => {
println!("Smart zoom");
}
WindowEvent::TouchpadPressure { .. }
| WindowEvent::HoveredFileCancelled
| WindowEvent::CursorEntered { .. }
| WindowEvent::AxisMotion { .. }
| WindowEvent::DroppedFile(_)
| WindowEvent::HoveredFile(_)
| WindowEvent::Destroyed
| WindowEvent::Touch(_)
| WindowEvent::Moved(_) => (),
}
}
fn handle_device_event(&mut self, _: &ActiveEventLoop, _: DeviceId, event: DeviceEvent) {
println!("Device event: {event:?}");
}
/// Process the key binding.
fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
KEY_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&key, mods)
.then_some(binding.action)
})
}
/// Process mouse binding.
fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> {
MOUSE_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&button, mods)
.then_some(binding.action)
})
}
fn print_help(&self) {
fn modifiers_to_string(mods: ModifiersState) -> String {
let mut mods_line = String::new();
// Always add + since it's printed as a part of the bindings.
for (modifier, desc) in [
(ModifiersState::SUPER, "Super+"),
(ModifiersState::ALT, "Alt+"),
(ModifiersState::CONTROL, "Ctrl+"),
(ModifiersState::SHIFT, "Shift+"),
] {
if !mods.contains(modifier) {
continue;
}
mods_line.push_str(desc);
}
mods_line
}
println!("Keyboard bindings:");
for binding in KEY_BINDINGS {
println!(
"{}{:<10} - {:?} ({})",
modifiers_to_string(binding.mods),
binding.trigger,
binding.action,
binding.action.help(),
);
}
println!("Mouse bindings:");
for binding in MOUSE_BINDINGS {
let button_name = match binding.trigger {
MouseButton::Left => "LMB",
MouseButton::Right => "RMB",
MouseButton::Middle => "MMB",
MouseButton::Back => "Back",
MouseButton::Forward => "Forward",
MouseButton::Other(_) => "",
};
println!(
"{}{:<10} - {:?} ({})",
modifiers_to_string(binding.mods),
button_name,
binding.action,
binding.action.help(),
);
}
}
}
/// Extra state on a window used in this example.
struct WindowState {
/// The actual Winit window.
window: Window,
/// IME input.
ime: bool,
/// Render surface.
///
/// NOTE: This surface must be dropped before the `Window`.
#[cfg(not(any(android_platform, ios_platform)))]
surface: Surface,
/// The window theme we're drawing with.
theme: Theme,
/// Cursor position over the window.
cursor_position: Option<PhysicalPosition<f64>>,
/// Window modifiers state.
modifiers: ModifiersState,
/// Occlusion state of the window.
occluded: bool,
/// Current cursor grab mode.
cursor_grab: CursorGrabMode,
/// The amount of zoom into window.
zoom: f64,
/// The amount of rotation of the window.
rotated: f32,
// Cursor states.
named_idx: usize,
custom_idx: usize,
cursor_hidden: bool,
}
struct Binding<T: Eq> {
trigger: T,
mods: ModifiersState,
action: Action,
}
impl<T: Eq> Binding<T> {
const fn new(trigger: T, mods: ModifiersState, action: Action) -> Self {
Self {
trigger,
mods,
action,
}
}
fn is_triggered_by(&self, trigger: &T, mods: &ModifiersState) -> bool {
&self.trigger == trigger && &self.mods == mods
}
}
/// Helper enum describing the different kinds of actions this example can do.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Action {
CloseWindow,
ToggleCursorVisibility,
CreateNewWindow,
ToggleResizeIncrements,
ToggleImeInput,
ToggleDecorations,
ToggleResizable,
ToggleFullscreen,
ToggleMaximize,
Minimize,
NextCursor,
NextCustomCursor,
CycleCursorGrab,
PrintHelp,
DragWindow,
DragResizeWindow,
ShowWindowMenu,
#[cfg(macos_platform)]
CycleOptionAsAlt,
#[cfg(macos_platform)]
CreateNewTab,
}
impl Action {
fn help(&self) -> &'static str {
match self {
Action::CloseWindow => "Close window",
Action::ToggleCursorVisibility => "Hide cursor",
Action::CreateNewWindow => "Create new window",
Action::ToggleImeInput => "Toggle IME input",
Action::ToggleDecorations => "Toggle decorations",
Action::ToggleResizable => "Toggle window resizable state",
Action::ToggleFullscreen => "Toggle fullscreen",
Action::ToggleMaximize => "Maximize",
Action::Minimize => "Minimize",
Action::ToggleResizeIncrements => "Use resize increments when resizing window",
Action::NextCursor => "Advance the cursor to the next value",
Action::NextCustomCursor => "Advance custom cursor to the next value",
Action::CycleCursorGrab => "Cycle through cursor grab mode",
Action::PrintHelp => "Print help",
Action::DragWindow => "Start window drag",
Action::DragResizeWindow => "Start window drag-resize",
Action::ShowWindowMenu => "Show window menu",
#[cfg(macos_platform)]
Action::CycleOptionAsAlt => "Cycle option as alt mode",
#[cfg(macos_platform)]
Action::CreateNewTab => "Create new tab",
}
}
}
const KEY_BINDINGS: &[Binding<&'static str>] = &[
Binding::new("Q", ModifiersState::CONTROL, Action::CloseWindow),
Binding::new("H", ModifiersState::CONTROL, Action::PrintHelp),
Binding::new("F", ModifiersState::CONTROL, Action::ToggleFullscreen),
Binding::new("D", ModifiersState::CONTROL, Action::ToggleDecorations),
Binding::new("I", ModifiersState::CONTROL, Action::ToggleImeInput),
Binding::new("L", ModifiersState::CONTROL, Action::CycleCursorGrab),
Binding::new("P", ModifiersState::CONTROL, Action::ToggleResizeIncrements),
Binding::new("R", ModifiersState::CONTROL, Action::ToggleResizable),
// M.
Binding::new("M", ModifiersState::CONTROL, Action::ToggleMaximize),
Binding::new("M", ModifiersState::ALT, Action::Minimize),
// N.
Binding::new("N", ModifiersState::CONTROL, Action::CreateNewWindow),
// C.
Binding::new("C", ModifiersState::CONTROL, Action::NextCursor),
Binding::new("C", ModifiersState::ALT, Action::NextCustomCursor),
Binding::new("Z", ModifiersState::CONTROL, Action::ToggleCursorVisibility),
#[cfg(macos_platform)]
Binding::new("T", ModifiersState::SUPER, Action::CreateNewTab),
#[cfg(macos_platform)]
Binding::new("O", ModifiersState::CONTROL, Action::CycleOptionAsAlt),
];
const MOUSE_BINDINGS: &[Binding<MouseButton>] = &[
Binding::new(
MouseButton::Left,
ModifiersState::ALT,
Action::DragResizeWindow,
),
Binding::new(
MouseButton::Left,
ModifiersState::CONTROL,
Action::DragWindow,
),
Binding::new(
MouseButton::Right,
ModifiersState::CONTROL,
Action::ShowWindowMenu,
),
];

62
examples/monitors.rs Normal file
View File

@@ -0,0 +1,62 @@
use std::error::Error;
use winit::{
event::{Event, StartCause},
event_loop::{ActiveEventLoop, EventLoop},
};
fn main() -> Result<(), Box<dyn Error>> {
let event_loop = EventLoop::new()?;
Ok(event_loop.run(|event, event_loop| match event {
Event::NewEvents(StartCause::Init) => {
dump_monitors(event_loop);
event_loop.exit()
}
_ => {}
})?)
}
fn dump_monitors(event_loop: &ActiveEventLoop) {
println!("Monitors information");
let primary_monitor = event_loop.primary_monitor();
for monitor in event_loop.available_monitors() {
let intro = if primary_monitor.as_ref() == Some(&monitor) {
"Primary monitor"
} else {
"Monitor"
};
if let Some(name) = monitor.name() {
println!("{intro}: {name}");
} else {
println!("{intro}: [no name]");
}
let size = monitor.size();
print!(" Current mode: {}x{}", size.width, size.height);
if let Some(m_hz) = monitor.refresh_rate_millihertz() {
println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
} else {
println!();
}
let position = monitor.position();
println!(" Position: {}, {}", position.x, position.y);
println!(" Scale factor: {}", monitor.scale_factor());
println!(" Available modes (width x height x bit-depth):");
for mode in monitor.video_modes() {
let size = mode.size();
let m_hz = mode.refresh_rate_millihertz();
println!(
" {:04}x{:04}x{:02} @ {:>3}.{} Hz",
size.width,
size.height,
mode.bit_depth(),
m_hz / 1000,
m_hz % 1000
);
}
}
}

View File

@@ -1,68 +1,61 @@
#![allow(clippy::single_match)] #![allow(clippy::single_match)]
// Limit this example to only compatible platforms. // Limit this example to only compatible platforms.
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, android_platform,))] #[cfg(any(
windows_platform,
macos_platform,
x11_platform,
wayland_platform,
android_platform,
))]
fn main() -> std::process::ExitCode { fn main() -> std::process::ExitCode {
use std::process::ExitCode; use std::{process::ExitCode, thread::sleep, time::Duration};
use std::thread::sleep;
use std::time::Duration;
use winit::application::ApplicationHandler; use simple_logger::SimpleLogger;
use winit::event::{StartCause, WindowEvent}; use winit::{
use winit::event_loop::{ActiveEventLoop, EventLoop}; event::{Event, WindowEvent},
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus}; event_loop::EventLoop,
use winit::window::{Window, WindowAttributes, WindowId}; platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
window::Window,
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
#[derive(Default)]
struct PumpDemo {
window: Option<Box<dyn Window>>,
}
impl ApplicationHandler for PumpDemo {
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
if matches!(cause, StartCause::Init) && self.window.is_none() {
let window_attributes =
WindowAttributes::default().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
}
fn window_event(
&mut self,
event_loop: &dyn 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.as_ref());
window.request_redraw();
},
_ => (),
}
}
}
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
tracing_subscriber::fmt::init(); SimpleLogger::new().init().unwrap();
let mut app = PumpDemo::default(); let mut window = None;
loop { loop {
let timeout = Some(Duration::ZERO); let timeout = Some(Duration::ZERO);
let status = event_loop.pump_app_events(timeout, &mut app); let status = event_loop.pump_events(timeout, |event, event_loop| {
if let Event::WindowEvent { event, .. } = &event {
// Print only Window events to reduce noise
println!("{event:?}");
}
match event {
Event::Resumed => {
let window_attributes =
Window::default_attributes().with_title("A fantastic window!");
window = Some(event_loop.create_window(window_attributes).unwrap());
}
Event::WindowEvent { event, .. } => {
let window = window.as_ref().unwrap();
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => fill::fill_window(window),
_ => (),
}
}
Event::AboutToWait => {
window.as_ref().unwrap().request_redraw();
}
_ => (),
}
});
if let PumpStatus::Exit(exit_code) = status { if let PumpStatus::Exit(exit_code) = status {
break ExitCode::from(exit_code as u8); break ExitCode::from(exit_code as u8);

View File

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

View File

@@ -11,16 +11,16 @@
pub use platform::cleanup_window; pub use platform::cleanup_window;
pub use platform::fill_window; pub use platform::fill_window;
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
mod platform { mod platform {
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::mem;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use softbuffer::{Context, Surface}; use softbuffer::{Context, Surface};
use winit::window::{Window, WindowId}; use winit::window::Window;
use winit::window::WindowId;
thread_local! { thread_local! {
// NOTE: You should never do things like that, create context and drop it before // NOTE: You should never do things like that, create context and drop it before
@@ -34,45 +34,35 @@ mod platform {
/// The graphics context used to draw to a window. /// The graphics context used to draw to a window.
struct GraphicsContext { struct GraphicsContext {
/// The global softbuffer context. /// The global softbuffer context.
context: RefCell<Context<&'static dyn Window>>, context: Context,
/// The hash map of window IDs to surfaces. /// The hash map of window IDs to surfaces.
surfaces: HashMap<WindowId, Surface<&'static dyn Window, &'static dyn Window>>, surfaces: HashMap<WindowId, Surface>,
} }
impl GraphicsContext { impl GraphicsContext {
fn new(w: &dyn Window) -> Self { fn new(w: &Window) -> Self {
Self { Self {
context: RefCell::new( context: unsafe { Context::new(w) }.expect("Failed to create a softbuffer context"),
Context::new(unsafe {
mem::transmute::<&'_ dyn Window, &'static dyn Window>(w)
})
.expect("Failed to create a softbuffer context"),
),
surfaces: HashMap::new(), surfaces: HashMap::new(),
} }
} }
fn create_surface( fn create_surface(&mut self, window: &Window) -> &mut Surface {
&mut self,
window: &dyn Window,
) -> &mut Surface<&'static dyn Window, &'static dyn Window> {
self.surfaces.entry(window.id()).or_insert_with(|| { self.surfaces.entry(window.id()).or_insert_with(|| {
Surface::new(&self.context.borrow(), unsafe { unsafe { Surface::new(&self.context, window) }
mem::transmute::<&'_ dyn Window, &'static dyn Window>(window)
})
.expect("Failed to create a softbuffer surface") .expect("Failed to create a softbuffer surface")
}) })
} }
fn destroy_surface(&mut self, window: &dyn Window) { fn destroy_surface(&mut self, window: &Window) {
self.surfaces.remove(&window.id()); self.surfaces.remove(&window.id());
} }
} }
pub fn fill_window(window: &dyn Window) { pub fn fill_window(window: &Window) {
GC.with(|gc| { GC.with(|gc| {
let size = window.surface_size(); let size = window.inner_size();
let (Some(width), Some(height)) = let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height)) (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
else { else {
@@ -81,22 +71,29 @@ mod platform {
// Either get the last context used or create a new one. // Either get the last context used or create a new one.
let mut gc = gc.borrow_mut(); let mut gc = gc.borrow_mut();
let surface = let surface = gc
gc.get_or_insert_with(|| GraphicsContext::new(window)).create_surface(window); .get_or_insert_with(|| GraphicsContext::new(window))
.create_surface(window);
// Fill a buffer with a solid color. // Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xff181818; const DARK_GRAY: u32 = 0xFF181818;
surface.resize(width, height).expect("Failed to resize the softbuffer surface"); surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");
let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer"); let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY); buffer.fill(DARK_GRAY);
buffer.present().expect("Failed to present the softbuffer buffer"); buffer
.present()
.expect("Failed to present the softbuffer buffer");
}) })
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn cleanup_window(window: &dyn Window) { pub fn cleanup_window(window: &Window) {
GC.with(|gc| { GC.with(|gc| {
let mut gc = gc.borrow_mut(); let mut gc = gc.borrow_mut();
if let Some(context) = gc.as_mut() { if let Some(context) = gc.as_mut() {
@@ -106,14 +103,14 @@ mod platform {
} }
} }
#[cfg(any(target_os = "android", target_os = "ios"))] #[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
mod platform { mod platform {
pub fn fill_window(_window: &dyn winit::window::Window) { pub fn fill_window(_window: &winit::window::Window) {
// No-op on mobile platforms. // No-op on mobile platforms.
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn cleanup_window(_window: &dyn winit::window::Window) { pub fn cleanup_window(_window: &winit::window::Window) {
// No-op on mobile platforms. // No-op on mobile platforms.
} }
} }

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -3,59 +3,56 @@ use std::error::Error;
#[cfg(x11_platform)] #[cfg(x11_platform)]
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
use winit::application::ApplicationHandler; use simple_logger::SimpleLogger;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::{
use winit::platform::x11::WindowAttributesExtX11; event::{Event, WindowEvent},
use winit::window::{Window, WindowAttributes, WindowId}; event_loop::EventLoop,
platform::x11::WindowAttributesExtX11,
window::Window,
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
pub struct XEmbedDemo {
window: Box<dyn Window>,
}
impl ApplicationHandler for XEmbedDemo {
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
self.window.pre_present_notify();
fill::fill_window(&*self.window);
},
_ => (),
}
}
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
self.window.request_redraw();
}
}
// First argument should be a 32-bit X11 window ID. // First argument should be a 32-bit X11 window ID.
let parent_window_id = std::env::args() let parent_window_id = std::env::args()
.nth(1) .nth(1)
.ok_or("Expected a 32-bit X11 window ID as the first argument.")? .ok_or("Expected a 32-bit X11 window ID as the first argument.")?
.parse::<u32>()?; .parse::<u32>()?;
tracing_subscriber::fmt::init(); SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new()?; let event_loop = EventLoop::new()?;
Ok(event_loop.run(|event_loop| { let mut window = None;
let window_attributes = WindowAttributes::default() event_loop.run(move |event, event_loop| match event {
Event::Resumed => {
let window_attributes = Window::default_attributes()
.with_title("An embedded window!") .with_title("An embedded window!")
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(parent_window_id); .with_embed_parent_window(parent_window_id);
let window = event_loop.create_window(window_attributes).unwrap(); window = Some(event_loop.create_window(window_attributes).unwrap());
XEmbedDemo { window } }
})?) Event::WindowEvent { event, .. } => {
let window = window.as_ref().unwrap();
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
window.pre_present_notify();
fill::fill_window(window);
}
_ => (),
}
}
Event::AboutToWait => {
window.as_ref().unwrap().request_redraw();
}
_ => (),
})?;
Ok(())
} }
#[cfg(not(x11_platform))] #[cfg(not(x11_platform))]

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

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

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

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

View File

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

View File

@@ -1,486 +0,0 @@
//! End user application handling.
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
use crate::event_loop::ActiveEventLoop;
#[cfg(any(docsrs, macos_platform))]
use crate::platform::macos::ApplicationHandlerExtMacOS;
use crate::window::WindowId;
/// The handler of the application events.
///
/// This is [dropped][std::ops::Drop] when the event loop is being shut down.
pub trait ApplicationHandler {
/// Emitted when new events arrive from the OS to be processed.
///
/// This is a useful place to put code that should be done before you start processing
/// events, such as updating frame timing information for benchmarking or checking the
/// [`StartCause`] to see if a timer set by
/// [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil] has elapsed.
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
let _ = (event_loop, cause);
}
/// Emitted when the application has been resumed.
///
/// See [`suspended()`][Self::suspended].
///
/// ## Platform-specific
///
/// ### iOS
///
/// On iOS, the [`resumed()`] method is called in response to an [`applicationDidBecomeActive`]
/// callback which means the application is about to transition from the inactive to active
/// state (according to the [iOS application lifecycle]).
///
/// [`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()`] method is called in response to a [`pageshow`] event if the
/// page is being restored from the [`bfcache`] (back/forward cache) - an in-memory cache
/// that stores a complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`bfcache`]: https://web.dev/bfcache/
///
/// ### Android
///
/// On Android, the [`resumed()`] method is called when the `Activity` is (again, if after a
/// prior [`suspended()`]) being displayed to the user. This is a good place to begin drawing
/// visual elements, running animations, etc. It is driven by Android's [`onStart()`] method.
///
/// [`onStart()`]: https://developer.android.com/reference/android/app/Activity#onStart()
///
/// ### Others
///
/// **macOS / Orbital / Wayland / Windows / X11:** Unsupported.
///
/// [`resumed()`]: Self::resumed()
/// [`suspended()`]: Self::suspended()
/// [`exiting()`]: Self::exiting()
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted from the point onwards the application should create render surfaces.
///
/// See [`destroy_surfaces()`].
///
/// ## Portability
///
/// It's recommended that applications should only initialize their render surfaces after the
/// [`can_create_surfaces()`] method is called. Some systems (specifically Android) won't allow
/// applications to create a render surface until that point.
///
/// For consistency, all platforms call this method even if they don't themselves have a formal
/// surface destroy/create lifecycle. For systems without a surface destroy/create lifecycle the
/// [`can_create_surfaces()`] event is always emitted after the [`StartCause::Init`] event.
///
/// Applications should be able to gracefully handle back-to-back [`can_create_surfaces()`] and
/// [`destroy_surfaces()`] calls.
///
/// ## Platform-specific
///
/// ### Android
///
/// On Android, the [`can_create_surfaces()`] method is called when a new [`NativeWindow`]
/// (native [`Surface`]) is created which backs the application window. This is expected to
/// closely correlate with the [`onStart`] lifecycle event which typically results in a surface
/// to be created after the app becomes visible.
///
/// Applications that need to run on Android must wait until they have received a surface before
/// they will be able to create a render surface (such as an `EGLSurface`, [`VkSurfaceKHR`]
/// or [`wgpu::Surface`]) which depend on having a [`NativeWindow`]. Applications must handle
/// [`destroy_surfaces()`], where their render surfaces are invalid and should be dropped.
///
/// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window
/// [`Surface`]: https://developer.android.com/reference/android/view/Surface
/// [`onStart`]: https://developer.android.com/reference/android/app/Activity#onStart()
/// [`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
///
/// [`can_create_surfaces()`]: Self::can_create_surfaces()
/// [`destroy_surfaces()`]: Self::destroy_surfaces()
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Called after a wake up is requested using [`EventLoopProxy::wake_up()`].
///
/// Multiple calls to the aforementioned method will be merged, and will only wake the event
/// loop once; however, due to the nature of multi-threading some wake ups may appear
/// spuriously. For these reasons, you should not rely on the number of times that this was
/// called.
///
/// The order in which this is emitted in relation to other events is not guaranteed. The time
/// at which this will be emitted is not guaranteed, only that it will happen "soon". That is,
/// there may be several executions of the event loop, including multiple redraws to windows,
/// between [`EventLoopProxy::wake_up()`] being called and the event being delivered.
///
/// [`EventLoopProxy::wake_up()`]: crate::event_loop::EventLoopProxy::wake_up
///
/// # Example
///
/// Use a [`std::sync::mpsc`] channel to handle events from a different thread.
///
/// ```no_run
/// use std::sync::mpsc;
/// use std::thread;
/// use std::time::Duration;
///
/// use winit::application::ApplicationHandler;
/// use winit::event_loop::{ActiveEventLoop, EventLoop};
///
/// struct MyApp {
/// receiver: mpsc::Receiver<u64>,
/// }
///
/// impl ApplicationHandler for MyApp {
/// # fn window_event(
/// # &mut self,
/// # _event_loop: &dyn ActiveEventLoop,
/// # _window_id: winit::window::WindowId,
/// # _event: winit::event::WindowEvent,
/// # ) {
/// # }
/// #
/// fn proxy_wake_up(&mut self, _event_loop: &dyn ActiveEventLoop) {
/// // Iterate current events, since wake-ups may have been merged.
/// //
/// // Note: We take care not to use `recv` or `iter` here, as those are blocking,
/// // and that would be bad for performance and might lead to a deadlock.
/// for i in self.receiver.try_iter() {
/// println!("received: {i}");
/// }
/// }
///
/// // Rest of `ApplicationHandler`
/// }
///
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let event_loop = EventLoop::new()?;
///
/// let (sender, receiver) = mpsc::channel();
///
/// // Send an event in a loop
/// let proxy = event_loop.create_proxy();
/// let background_thread = thread::spawn(move || {
/// let mut i = 0;
/// loop {
/// println!("sending: {i}");
/// if sender.send(i).is_err() {
/// // Stop sending once `MyApp` is dropped
/// break;
/// }
/// // Trigger the wake-up _after_ we placed the event in the channel.
/// // Otherwise, `proxy_wake_up` might be triggered prematurely.
/// proxy.wake_up();
/// i += 1;
/// thread::sleep(Duration::from_secs(1));
/// }
/// });
///
/// event_loop.run(|_event_loop| MyApp { receiver })?;
///
/// background_thread.join().unwrap();
///
/// Ok(())
/// }
/// ```
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the OS sends an event to a winit window.
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
);
/// Emitted when the OS sends an event to a device.
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<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: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the application has been suspended.
///
/// See [`resumed()`][Self::resumed].
///
/// ## Platform-specific
///
/// ### iOS
///
/// On iOS, the [`suspended()`] method is called in response to an
/// [`applicationWillResignActive`] callback which means that the application is about to
/// transition from the active to inactive state (according to the [iOS application lifecycle]).
///
/// [`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()`] method is called in response to a [`pagehide`] event if the
/// page is being stored 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
/// [`bfcache`]: https://web.dev/bfcache/
///
/// ### Android
///
/// On Android, the [`suspended()`] method is called when the `Activity` is no longer visible
/// to the user. This is a good place to stop refreshing UI, running animations and other visual
/// things. It is driven by Android's [`onStop()`] method.
///
/// After this event the application either receives [`resumed()`] again, or [`exiting()`].
///
/// [`onStop()`]: https://developer.android.com/reference/android/app/Activity#onStop()
///
/// ### Others
///
/// **macOS / Orbital / Wayland / Windows / X11:** Unsupported.
///
/// [`resumed()`]: Self::resumed()
/// [`suspended()`]: Self::suspended()
/// [`exiting()`]: Self::exiting()
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the application must destroy its render surfaces.
///
/// See [`can_create_surfaces()`] for more details.
///
/// ## Platform-specific
///
/// ### Android
///
/// On Android, the [`destroy_surfaces()`] method is called when the application's
/// [`NativeWindow`] (native [`Surface`]) is destroyed. This is expected to closely correlate
/// with the [`onStop`] lifecycle event which typically results in the surface to be destroyed
/// after the app becomes invisible.
///
/// Applications that need to run on Android must be able to handle their underlying
/// [`SurfaceView`] being destroyed, which in turn indirectly invalidates any existing
/// render surfaces that may have been created outside of Winit (such as an `EGLSurface`,
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]).
///
/// This means that in this method, you must drop all render surfaces before the event callback
/// completes, and only re-create them in or after [`can_create_surfaces()`] is next recieved.
///
/// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window
/// [`Surface`]: https://developer.android.com/reference/android/view/Surface
/// [`onStop`]: https://developer.android.com/reference/android/app/Activity#onStop()
/// [`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
///
/// ### Others
///
/// - **iOS / macOS / Orbital / Wayland / Web / Windows / X11:** Unsupported.
///
/// [`can_create_surfaces()`]: Self::can_create_surfaces()
/// [`destroy_surfaces()`]: Self::destroy_surfaces()
fn destroy_surfaces(&mut self, event_loop: &dyn 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: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// The macOS-specific handler.
///
/// The return value from this should not change at runtime.
#[cfg(any(docsrs, macos_platform))]
#[inline(always)]
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
None
}
}
#[deny(clippy::missing_trait_methods)]
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
#[inline]
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
}
#[inline]
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).resumed(event_loop);
}
#[inline]
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).can_create_surfaces(event_loop);
}
#[inline]
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).proxy_wake_up(event_loop);
}
#[inline]
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, event);
}
#[inline]
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
}
#[inline]
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).about_to_wait(event_loop);
}
#[inline]
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).suspended(event_loop);
}
#[inline]
fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).destroy_surfaces(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
#[cfg(any(docsrs, macos_platform))]
#[inline]
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
(**self).macos_handler()
}
}
#[deny(clippy::missing_trait_methods)]
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
#[inline]
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
}
#[inline]
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).resumed(event_loop);
}
#[inline]
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).can_create_surfaces(event_loop);
}
#[inline]
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).proxy_wake_up(event_loop);
}
#[inline]
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, event);
}
#[inline]
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
}
#[inline]
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).about_to_wait(event_loop);
}
#[inline]
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).suspended(event_loop);
}
#[inline]
fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).destroy_surfaces(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
#[cfg(any(docsrs, macos_platform))]
#[inline]
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
(**self).macos_handler()
}
}

View File

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

View File

@@ -1,212 +0,0 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
The sections should follow the order `Added`, `Changed`, `Deprecated`,
`Removed`, and `Fixed`.
Platform specific changed should be added to the end of the section and grouped
by platform name. Common API additions should have `, implemented` at the end
for platforms where the API was initially implemented. See the following example
on how to add them:
```md
### Added
- Add `Window::turbo()`, implemented on X11, Wayland, and Web.
- On X11, add `Window::some_rare_api`.
- On X11, add `Window::even_more_rare_api`.
- On Wayland, add `Window::common_api`.
- On Windows, add `Window::some_rare_api`.
```
When the change requires non-trivial amount of work for users to comply
with it, the migration guide should be added below the entry, like:
```md
- Deprecate `Window` creation outside of `EventLoop::run`
This was done to simply migration in the future. Consider the
following code:
// Code snippet.
To migrate it we should do X, Y, and then Z, for example:
// Code snippet.
```
The migration guide could reference other migration examples in the current
changelog entry.
## Unreleased
### Added
- Add `ActiveEventLoop::create_proxy()`.
- On Web, add `ActiveEventLoopExtWeb::is_cursor_lock_raw()` to determine if
`DeviceEvent::MouseMotion` is returning raw data, not OS accelerated, when using
`CursorGrabMode::Locked`.
- On Web, implement `MonitorHandle` and `VideoModeHandle`.
Without prompting the user for permission, only the current monitor is returned. But when
prompting and being granted permission through
`ActiveEventLoop::request_detailed_monitor_permission()`, access to all monitors and their
details is available. Handles created with "detailed monitor permissions" can be used in
`Window::set_fullscreen()` as well.
Keep in mind that handles do not auto-upgrade after permissions are granted and have to be
re-created to make full use of this feature.
- Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd`
and `Serialize` on many types.
- Add `MonitorHandle::current_video_mode()`.
- On Android, the soft keyboard can now be shown using `Window::set_ime_allowed`.
- Add basic iOS IME support. The soft keyboard can now be shown using `Window::set_ime_allowed`.
- Add `ApplicationHandlerExtMacOS` trait, and a `macos_handler` method to `ApplicationHandler` which returns a `dyn ApplicationHandlerExtMacOS` which allows for macOS specific extensions to winit.
- Add a `standard_key_binding` method to the `ApplicationHandlerExtMacOS` trait. This allows handling of standard keybindings such as "go to end of line" on macOS.
- On macOS, add `WindowExtMacOS::set_borderless_game` and `WindowAttributesExtMacOS::with_borderless_game`
to fully disable the menu bar and dock in Borderless Fullscreen as commonly done in games.
- On macOS, add `WindowExtMacOS::set_unified_titlebar` and `WindowAttributesExtMacOS::with_unified_titlebar`
to use a larger style of titlebar.
- Add `WindowId::into_raw()` and `from_raw()`.
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId`, `primary` and `position` to all
pointer events as part of the pointer event overhaul.
- Add `DeviceId::into_raw()` and `from_raw()`.
- On X11, the `window` example now understands the `X11_VISUAL_ID` and `X11_SCREEN_ID` env
variables to test the respective modifiers of window creation.
- Added `Window::surface_position`, which is the position of the surface inside the window.
- Added `Window::safe_area`, which describes the area of the surface that is unobstructed.
- On X11 and Wayland, improved scancode conversions for more obscure key codes.
- On Windows, improved scancode conversions for more obscure key codes.
### Changed
- Change `ActiveEventLoop` to be a trait.
- Change `Window` to be a trait.
- `ActiveEventLoop::create_window` now returns `Box<dyn Window>`.
- `ApplicationHandler` now uses `dyn ActiveEventLoop`.
- On Web, let events wake up event loop immediately when using `ControlFlow::Poll`.
- Bump MSRV from `1.70` to `1.73`.
- Changed `ApplicationHandler::user_event` to `user_wake_up`, removing the
generic user event.
Winit will now only indicate that wake up happened, you will have to pair
this with an external mechanism like `std::sync::mpsc::channel` if you want
to send specific data to be processed on the main thread.
- Changed `EventLoopProxy::send_event` to `EventLoopProxy::wake_up`, it now
only wakes up the loop.
- On X11, implement smooth resizing through the sync extension API.
- `ApplicationHandler::can_create|destroy_surfaces()` was split off from
`ApplicationHandler::resumed/suspended()`.
`ApplicationHandler::can_create_surfaces()` should, for portability reasons
to Android, be the only place to create render surfaces.
`ApplicationHandler::resumed/suspended()` are now only emitted by iOS, Web
and Android, and now signify actually resuming/suspending the application.
- Rename `platform::web::*ExtWebSys` to `*ExtWeb`.
- Change signature of `EventLoop::run_app`, `EventLoopExtPumpEvents::pump_app_events` and
`EventLoopExtRunOnDemand::run_app_on_demand` to accept a `impl ApplicationHandler` directly,
instead of requiring a `&mut` reference to it.
- On Web, `Window::canvas()` now returns a reference.
- On Web, `CursorGrabMode::Locked` now lets `DeviceEvent::MouseMotion` return raw data, not OS
accelerated, if the browser supports it.
- `(Active)EventLoop::create_custom_cursor()` now returns a `Result<CustomCursor, ExternalError>`.
- Changed how `ModifiersState` is serialized by Serde.
- `VideoModeHandle::refresh_rate_millihertz()` and `bit_depth()` now return a `Option<NonZero*>`.
- `MonitorHandle::position()` now returns an `Option`.
- On iOS and macOS, remove custom application delegates. You are now allowed to override the
application delegate yourself.
- On iOS, no longer act as-if the application successfully open all URLs. Override
`application:didFinishLaunchingWithOptions:` and provide the desired behaviour yourself.
- On X11, remove our dependency on libXcursor. (#3749)
- Renamed the following APIs to make it clearer that the sizes apply to the underlying surface:
- `WindowEvent::Resized` to `SurfaceResized`.
- `InnerSizeWriter` to `SurfaceSizeWriter`.
- `WindowAttributes.inner_size` to `surface_size`.
- `WindowAttributes.min_inner_size` to `min_surface_size`.
- `WindowAttributes.max_inner_size` to `max_surface_size`.
- `WindowAttributes.resize_increments` to `surface_resize_increments`.
- `WindowAttributes::with_inner_size` to `with_surface_size`.
- `WindowAttributes::with_min_inner_size` to `with_min_surface_size`.
- `WindowAttributes::with_max_inner_size` to `with_max_surface_size`.
- `WindowAttributes::with_resize_increments` to `with_surface_resize_increments`.
- `Window::inner_size` to `surface_size`.
- `Window::request_inner_size` to `request_surface_size`.
- `Window::set_min_inner_size` to `set_min_surface_size`.
- `Window::set_max_inner_size` to `set_max_surface_size`.
To migrate, you can probably just replace all instances of `inner_size` with `surface_size` in your codebase.
- Every event carrying a `DeviceId` now uses `Option<DeviceId>` instead. A `None` value signifies that the
device can't be uniquely identified.
- Pointer `WindowEvent`s were overhauled. The new events can handle any type of pointer, serving as
a single pointer input source. Now your application can handle any pointer type without having to
explicitly handle e.g. `Touch`:
- Rename `CursorMoved` to `PointerMoved`.
- Rename `CursorEntered` to `PointerEntered`.
- Rename `CursorLeft` to `PointerLeft`.
- Rename `MouseInput` to `PointerButton`.
- Add `primary` to every `PointerEvent` as a way to identify discard non-primary pointers in a
multi-touch interaction.
- Add `position` to every `PointerEvent`.
- `PointerMoved` is **not sent** after `PointerEntered` anymore.
- Remove `Touch`, which is folded into the `Pointer*` events.
- New `PointerKind` added to `PointerEntered` and `PointerLeft`, signifying which pointer type is
the source of this event.
- New `PointerSource` added to `PointerMoved`, similar to `PointerKind` but holding additional
data.
- New `ButtonSource` added to `PointerButton`, similar to `PointerKind` but holding pointer type
specific buttons. Use `ButtonSource::mouse_button()` to easily normalize any pointer button
type to a generic mouse button.
- New `FingerId` added to `PointerKind::Touch` and `PointerSource::Touch` able to uniquely
identify a finger in a multi-touch interaction. Replaces the old `Touch::id`.
- In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`.
- Remove `Force::Calibrated::altitude_angle`.
- On X11, use bottom-right corner for IME hotspot in `Window::set_ime_cursor_area`.
- On macOS and iOS, no longer emit `ScaleFactorChanged` upon window creation.
- On macOS, no longer emit `Focused` upon window creation.
### Removed
- Remove `Event`.
- Remove already deprecated APIs:
- `EventLoop::create_window()`
- `EventLoop::run`.
- `EventLoopBuilder::new()`
- `EventLoopExtPumpEvents::pump_events`.
- `EventLoopExtRunOnDemand::run_on_demand`.
- `VideoMode`
- `WindowAttributes::new()`
- `Window::set_cursor_icon()`
- On iOS, remove `platform::ios::EventLoopExtIOS` and related `platform::ios::Idiom` type.
This feature was incomplete, and the equivalent functionality can be trivially achieved outside
of `winit` using `objc2-ui-kit` and calling `UIDevice::currentDevice().userInterfaceIdiom()`.
- On Web, remove unused `platform::web::CustomCursorError::Animation`.
- Remove the `rwh_04` and `rwh_05` cargo feature and the corresponding `raw-window-handle` v0.4 and
v0.5 support. v0.6 remains in place and is enabled by default.
- Remove `DeviceEvent::Added` and `DeviceEvent::Removed`.
- Remove `DeviceEvent::Motion` and `WindowEvent::AxisMotion`.
- Remove `MonitorHandle::size()` and `refresh_rate_millihertz()` in favor of
`MonitorHandle::current_video_mode()`.
- On Android, remove all `MonitorHandle` support instead of emitting false data.
- Remove `impl From<u64> for WindowId` and `impl From<WindowId> for u64`. Replaced with
`WindowId::into_raw()` and `from_raw()`.
- Remove `dummy()` from `WindowId` and `DeviceId`.
- Remove `WindowEvent::Touch` and `Touch` in favor of the new `PointerKind`, `PointerSource` and
`ButtonSource` as part of the new pointer event overhaul.
- Remove `Force::altitude_angle`.
- Removed `Window::inner_position`, use the new `Window::surface_position` instead.
### Fixed
- On Orbital, `MonitorHandle::name()` now returns `None` instead of a dummy name.
- On macOS, fix `WindowEvent::Moved` sometimes being triggered unnecessarily on resize.
- On macOS, package manifest definitions of `LSUIElement` will no longer be overridden with the
default activation policy, unless explicitly provided during initialization.
- On macOS, fix crash when calling `drag_window()` without a left click present.
- On X11, key events forward to IME anyway, even when it's disabled.
- On Windows, make `ControlFlow::WaitUntil` work more precisely using `CREATE_WAITABLE_TIMER_HIGH_RESOLUTION`.
- On X11, creating windows on screen that is not the first one (e.g. `DISPLAY=:0.1`) works again.
- On X11, creating windows while passing `with_x11_screen(non_default_screen)` works again.
- On X11, fix XInput handling that prevented a new window from getting the focus in some cases.
- On iOS, fixed `SurfaceResized` and `Window::surface_size` not reporting the size of the actual surface.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,300 +0,0 @@
## 0.30.5
### Added
- Add `ActiveEventLoop::system_theme()`, returning the current system theme.
- On Web, implement `Error` for `platform::web::CustomCursorError`.
- On Android, add `{Active,}EventLoopExtAndroid::android_app()` to access the app used to create the loop.
### Fixed
- On MacOS, fix building with `feature = "rwh_04"`.
- On Web, pen events are now routed through to `WindowEvent::Cursor*`.
- On macOS, fix panic when releasing not available monitor.
- On MacOS, return the system theme in `Window::theme()` if no theme override is set.
## 0.30.4
### Changed
- `DeviceId::dummy()` and `WindowId::dummy()` are no longer marked `unsafe`.
### Fixed
- On Wayland, avoid crashing when compositor is misbehaving.
- On Web, fix `WindowEvent::Resized` not using `requestAnimationFrame` when sending
`WindowEvent::RedrawRequested` and also potentially causing `WindowEvent::RedrawRequested`
to not be de-duplicated.
- Account for different browser engine implementations of pointer movement coordinate space.
## 0.30.3
### Added
- On Web, add `EventLoopExtWebSys::(set_)poll_strategy()` to allow setting
control flow strategies before starting the event loop.
- On Web, add `WaitUntilStrategy`, which allows to set different strategies for
`ControlFlow::WaitUntil`. By default the Prioritized Task Scheduling API is
used, with a fallback to `setTimeout()` with a trick to circumvent throttling
to 4ms. But an option to use a Web worker to schedule the timer is available
as well, which commonly prevents any throttling when the window is not focused.
### Changed
- On macOS, set the window theme on the `NSWindow` instead of application-wide.
### Fixed
- On X11, build on arm platforms.
- On macOS, fixed `WindowBuilder::with_theme` not having any effect on the window.
## 0.30.2
### Fixed
- On Web, fix `EventLoopProxy::send_event()` triggering event loop immediately
when not called from inside the event loop. Now queues a microtask instead.
- On Web, stop overwriting default cursor with `CursorIcon::Default`.
- On Web, prevent crash when using `InnerSizeWriter::request_inner_size()`.
- On macOS, fix not working opacity for entire window.
## 0.30.1
### Added
- Reexport `raw-window-handle` versions 0.4 and 0.5 as `raw_window_handle_04` and `raw_window_handle_05`.
- Implement `ApplicationHandler` for `&mut` references and heap allocations to something that implements `ApplicationHandler`.
- Add traits `EventLoopExtWayland` and `EventLoopExtX11`, providing methods `is_wayland` and `is_x11` on `EventLoop`.
### Fixed
- On macOS, fix panic on exit when dropping windows outside the event loop.
- On macOS, fix window dragging glitches when dragging across a monitor boundary with different scale factor.
- On macOS, fix the range in `Ime::Preedit`.
- On macOS, use the system's internal mechanisms for queuing events.
- On macOS, handle events directly instead of queuing when possible.
## 0.30.0
### Added
- Add `OwnedDisplayHandle` type for allowing safe display handle usage outside of
trivial cases.
- Add `ApplicationHandler<T>` trait which mimics `Event<T>`.
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a
`CursorIcon` or `CustomCursor`.
- Add `Sync` implementation for `EventLoopProxy<T: Send>`.
- Add `Window::default_attributes` to get default `WindowAttributes`.
- Add `EventLoop::builder` to get `EventLoopBuilder` without export.
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated
cursors from other `CustomCursor`s.
- Add `{Active,}EventLoop::create_custom_cursor` to load custom cursor image sources.
- Add `ActiveEventLoop::create_window` and `EventLoop::create_window`.
- Add `CustomCursor` which could be set via `Window::set_cursor`, implemented on
Windows, macOS, X11, Wayland, and Web.
- On Web, add to toggle calling `Event.preventDefault()` on `Window`.
- On iOS, add `PinchGesture`, `DoubleTapGesture`, `PanGesture` and `RotationGesture`.
- on iOS, use `UIGestureRecognizerDelegate` for fine grained control of gesture recognizers.
- On macOS, add services menu.
- On Windows, add `with_title_text_color`, and `with_corner_preference` on
`WindowAttributesExtWindows`.
- On Windows, implement resize increments.
- On Windows, add `AnyThread` API to access window handle off the main thread.
### Changed
- Bump MSRV from `1.65` to `1.70`.
- On Wayland, bump `sctk-adwaita` to `0.9.0`, which changed system library
crates. This change is a **cascading breaking change**, you must do breaking
change as well, even if you don't expose winit.
- Rename `TouchpadMagnify` to `PinchGesture`.
- Rename `SmartMagnify` to `DoubleTapGesture`.
- Rename `TouchpadRotate` to `RotationGesture`.
- Rename `EventLoopWindowTarget` to `ActiveEventLoop`.
- Rename `platform::x11::XWindowType` to `platform::x11::WindowType`.
- Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold
static data.
- Make `Debug` formatting of `WindowId` more concise.
- Move `dpi` types to its own crate, and re-export it from the root crate.
- Replace `log` with `tracing`, use `log` feature on `tracing` to restore old
behavior.
- `EventLoop::with_user_event` now returns `EventLoopBuilder`.
- On Web, return `HandleError::Unavailable` when a window handle is not available.
- On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
- On Web, remove queuing fullscreen request in absence of transient activation.
- On iOS, return `HandleError::Unavailable` when a window handle is not available.
- On macOS, return `HandleError::Unavailable` when a window handle is not available.
- On Windows, remove `WS_CAPTION`, `WS_BORDER`, and `WS_EX_WINDOWEDGE` styles
for child windows without decorations.
- On Android, bump `ndk` to `0.9.0` and `android-activity` to `0.6.0`,
and remove unused direct dependency on `ndk-sys`.
### Deprecated
- Deprecate `EventLoop::run`, use `EventLoop::run_app`.
- Deprecate `EventLoopExtRunOnDemand::run_on_demand`, use `EventLoop::run_app_on_demand`.
- Deprecate `EventLoopExtPumpEvents::pump_events`, use `EventLoopExtPumpEvents::pump_app_events`.
The new `app` APIs accept a newly added `ApplicationHandler<T>` instead of
`Fn`. The semantics are mostly the same, given that the capture list of the
closure is your new `State`. Consider the following code:
```rust,no_run,ignore
use winit::event::Event;
use winit::event_loop::EventLoop;
use winit::window::Window;
struct MyUserEvent;
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
let window = event_loop.create_window(Window::default_attributes()).unwrap();
let mut counter = 0;
let _ = event_loop.run(move |event, event_loop| {
match event {
Event::AboutToWait => {
window.request_redraw();
counter += 1;
}
Event::WindowEvent { window_id, event } => {
// Handle window event.
}
Event::UserEvent(event) => {
// Handle user event.
}
Event::DeviceEvent { device_id, event } => {
// Handle device event.
}
_ => (),
}
});
```
To migrate this code, you should move all the captured values into some
newtype `State` and implement `ApplicationHandler` for this type. Finally,
we move particular `match event` arms into methods on `ApplicationHandler`,
for example:
```rust,no_run,ignore
use winit::application::ApplicationHandler;
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
use winit::event_loop::{EventLoop, ActiveEventLoop};
use winit::window::{Window, WindowId};
struct MyUserEvent;
struct State {
window: Window,
counter: i32,
}
impl ApplicationHandler<MyUserEvent> for State {
fn user_event(&mut self, event_loop: &ActiveEventLoop, user_event: MyUserEvent) {
// Handle user event.
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
// Your application got resumed.
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
// Handle window event.
}
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
// Handle device event.
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
self.window.request_redraw();
self.counter += 1;
}
}
let event_loop = EventLoop::<MyUserEvent>::with_user_event().build().unwrap();
#[allow(deprecated)]
let window = event_loop.create_window(Window::default_attributes()).unwrap();
let mut state = State { window, counter: 0 };
let _ = event_loop.run_app(&mut state);
```
Please submit your feedback after migrating in [this issue](https://github.com/rust-windowing/winit/issues/3626).
- Deprecate `Window::set_cursor_icon`, use `Window::set_cursor`.
### Removed
- Remove `Window::new`, use `ActiveEventLoop::create_window` instead.
You now have to create your windows inside the actively running event loop
(usually the `new_events(cause: StartCause::Init)` or `resumed()` events),
and can no longer do it before the application has properly launched.
This change is done to fix many long-standing issues on iOS and macOS, and
will improve things on Wayland once fully implemented.
To ease migration, we provide the deprecated `EventLoop::create_window` that
will allow you to bypass this restriction in this release.
Using the migration example from above, you can change your code as follows:
```rust,no_run,ignore
use winit::application::ApplicationHandler;
use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId};
use winit::event_loop::{EventLoop, ActiveEventLoop};
use winit::window::{Window, WindowId};
#[derive(Default)]
struct State {
// Use an `Option` to allow the window to not be available until the
// application is properly running.
window: Option<Window>,
counter: i32,
}
impl ApplicationHandler for State {
// This is a common indicator that you can create a window.
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
// `unwrap` is fine, the window will always be available when
// receiving a window event.
let window = self.window.as_ref().unwrap();
// Handle window event.
}
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
// Handle window event.
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
self.counter += 1;
}
}
}
let event_loop = EventLoop::new().unwrap();
let mut state = State::default();
let _ = event_loop.run_app(&mut state);
```
- Remove `Deref` implementation for `EventLoop` that gave `EventLoopWindowTarget`.
- Remove `WindowBuilder` in favor of `WindowAttributes`.
- Remove Generic parameter `T` from `ActiveEventLoop`.
- Remove `EventLoopBuilder::with_user_event`, use `EventLoop::with_user_event`.
- Remove Redundant `EventLoopError::AlreadyRunning`.
- Remove `WindowAttributes::fullscreen` and expose as field directly.
- On X11, remove `platform::x11::XNotSupported` export.
### Fixed
- On Web, fix setting cursor icon overriding cursor visibility.
- On Windows, fix cursor not confined to center of window when grabbed and hidden.
- On macOS, fix sequence of mouse events being out of order when dragging on the trackpad.
- On Wayland, fix decoration glitch on close with some compositors.
- On Android, fix a regression introduced in #2748 to allow volume key events to be received again.
- On Windows, don't return a valid window handle outside of the GUI thread.
- On macOS, don't set the background color when initializing a window with transparency.

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
use core::fmt; use core::fmt;
use std::error::Error; use std::hash::Hasher;
use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use std::{error::Error, hash::Hash};
use cursor_icon::CursorIcon; use cursor_icon::CursorIcon;
@@ -12,7 +12,7 @@ pub const MAX_CURSOR_SIZE: u16 = 2048;
const PIXEL_SIZE: usize = 4; const PIXEL_SIZE: usize = 4;
/// See [`Window::set_cursor()`][crate::window::Window::set_cursor] for more details. /// See [`Window::set_cursor()`](crate::window::Window::set_cursor) for more details.
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Cursor { pub enum Cursor {
Icon(CursorIcon), Icon(CursorIcon),
@@ -50,7 +50,7 @@ impl From<CustomCursor> for Cursor {
/// ```no_run /// ```no_run
/// # use winit::event_loop::ActiveEventLoop; /// # use winit::event_loop::ActiveEventLoop;
/// # use winit::window::Window; /// # use winit::window::Window;
/// # fn scope(event_loop: &dyn ActiveEventLoop, window: &dyn Window) { /// # fn scope(event_loop: &ActiveEventLoop, window: &Window) {
/// use winit::window::CustomCursor; /// use winit::window::CustomCursor;
/// ///
/// let w = 10; /// let w = 10;
@@ -62,13 +62,13 @@ impl From<CustomCursor> for Cursor {
/// ///
/// #[cfg(target_family = "wasm")] /// #[cfg(target_family = "wasm")]
/// let source = { /// let source = {
/// use winit::platform::web::CustomCursorExtWeb; /// use winit::platform::web::CustomCursorExtWebSys;
/// CustomCursor::from_url(String::from("http://localhost:3000/cursor.png"), 0, 0) /// CustomCursor::from_url(String::from("http://localhost:3000/cursor.png"), 0, 0)
/// }; /// };
/// ///
/// if let Ok(custom_cursor) = event_loop.create_custom_cursor(source) { /// let custom_cursor = event_loop.create_custom_cursor(source);
/// window.set_cursor(custom_cursor.clone().into()); ///
/// } /// window.set_cursor(custom_cursor.clone());
/// # } /// # }
/// ``` /// ```
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
@@ -88,10 +88,6 @@ impl CustomCursor {
hotspot_x: u16, hotspot_x: u16,
hotspot_y: u16, hotspot_y: u16,
) -> Result<CustomCursorSource, BadImage> { ) -> Result<CustomCursorSource, BadImage> {
let _span =
tracing::debug_span!("winit::Cursor::from_rgba", width, height, hotspot_x, hotspot_y)
.entered();
Ok(CustomCursorSource { Ok(CustomCursorSource {
inner: PlatformCustomCursorSource::from_rgba( inner: PlatformCustomCursorSource::from_rgba(
rgba.into(), rgba.into(),
@@ -107,16 +103,13 @@ impl CustomCursor {
/// Source for [`CustomCursor`]. /// Source for [`CustomCursor`].
/// ///
/// See [`CustomCursor`] for more details. /// See [`CustomCursor`] for more details.
#[derive(Debug, Clone, Eq, Hash, PartialEq)] #[derive(Debug)]
pub struct CustomCursorSource { pub struct CustomCursorSource {
// Some platforms don't support custom cursors.
#[allow(dead_code)]
pub(crate) inner: PlatformCustomCursorSource, pub(crate) inner: PlatformCustomCursorSource,
} }
/// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments. /// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BadImage { pub enum BadImage {
/// Produced when the image dimensions are larger than [`MAX_CURSOR_SIZE`]. This doesn't /// Produced when the image dimensions are larger than [`MAX_CURSOR_SIZE`]. This doesn't
/// guarantee that the cursor will work, but should avoid many platform and device specific /// guarantee that the cursor will work, but should avoid many platform and device specific
@@ -127,36 +120,45 @@ pub enum BadImage {
ByteCountNotDivisibleBy4 { byte_count: usize }, ByteCountNotDivisibleBy4 { byte_count: usize },
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`. /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
/// At least one of your arguments is incorrect. /// At least one of your arguments is incorrect.
DimensionsVsPixelCount { width: u16, height: u16, width_x_height: u64, pixel_count: u64 }, DimensionsVsPixelCount {
width: u16,
height: u16,
width_x_height: u64,
pixel_count: u64,
},
/// Produced when the hotspot is outside the image bounds /// Produced when the hotspot is outside the image bounds
HotspotOutOfBounds { width: u16, height: u16, hotspot_x: u16, hotspot_y: u16 }, HotspotOutOfBounds {
width: u16,
height: u16,
hotspot_x: u16,
hotspot_y: u16,
},
} }
impl fmt::Display for BadImage { impl fmt::Display for BadImage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
BadImage::TooLarge { width, height } => write!( BadImage::TooLarge { width, height } => write!(f,
f, "The specified dimensions ({width:?}x{height:?}) are too large. The maximum is {MAX_CURSOR_SIZE:?}x{MAX_CURSOR_SIZE:?}.",
"The specified dimensions ({width:?}x{height:?}) are too large. The maximum is \
{MAX_CURSOR_SIZE:?}x{MAX_CURSOR_SIZE:?}.",
), ),
BadImage::ByteCountNotDivisibleBy4 { byte_count } => write!( BadImage::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
f, "The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
"The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making \
it impossible to interpret as 32bpp RGBA pixels.",
), ),
BadImage::DimensionsVsPixelCount { width, height, width_x_height, pixel_count } => { BadImage::DimensionsVsPixelCount {
write!( width,
f, height,
"The specified dimensions ({width:?}x{height:?}) don't match the number of \ width_x_height,
pixels supplied by the `rgba` argument ({pixel_count:?}). For those \ pixel_count,
dimensions, the expected pixel count is {width_x_height:?}.", } => write!(f,
) "The specified dimensions ({width:?}x{height:?}) don't match the number of pixels supplied by the `rgba` argument ({pixel_count:?}). For those dimensions, the expected pixel count is {width_x_height:?}.",
}, ),
BadImage::HotspotOutOfBounds { width, height, hotspot_x, hotspot_y } => write!( BadImage::HotspotOutOfBounds {
f, width,
"The specified hotspot ({hotspot_x:?}, {hotspot_y:?}) is outside the image bounds \ height,
({width:?}x{height:?}).", hotspot_x,
hotspot_y,
} => write!(f,
"The specified hotspot ({hotspot_x:?}, {hotspot_y:?}) is outside the image bounds ({width:?}x{height:?}).",
), ),
} }
} }
@@ -167,7 +169,7 @@ impl Error for BadImage {}
/// Platforms export this directly as `PlatformCustomCursorSource` if they need to only work with /// Platforms export this directly as `PlatformCustomCursorSource` if they need to only work with
/// images. /// images.
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, Clone, Eq, Hash, PartialEq)] #[derive(Debug)]
pub(crate) struct OnlyCursorImageSource(pub(crate) CursorImage); pub(crate) struct OnlyCursorImageSource(pub(crate) CursorImage);
#[allow(dead_code)] #[allow(dead_code)]
@@ -184,7 +186,6 @@ impl OnlyCursorImageSource {
} }
/// Platforms export this directly as `PlatformCustomCursor` if they don't implement caching. /// Platforms export this directly as `PlatformCustomCursor` if they don't implement caching.
#[allow(dead_code)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct OnlyCursorImage(pub(crate) Arc<CursorImage>); pub(crate) struct OnlyCursorImage(pub(crate) Arc<CursorImage>);
@@ -202,7 +203,7 @@ impl PartialEq for OnlyCursorImage {
impl Eq for OnlyCursorImage {} impl Eq for OnlyCursorImage {}
#[derive(Debug, Clone, Eq, Hash, PartialEq)] #[derive(Debug)]
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) struct CursorImage { pub(crate) struct CursorImage {
pub(crate) rgba: Vec<u8>, pub(crate) rgba: Vec<u8>,
@@ -225,7 +226,9 @@ impl CursorImage {
} }
if rgba.len() % PIXEL_SIZE != 0 { if rgba.len() % PIXEL_SIZE != 0 {
return Err(BadImage::ByteCountNotDivisibleBy4 { byte_count: rgba.len() }); return Err(BadImage::ByteCountNotDivisibleBy4 {
byte_count: rgba.len(),
});
} }
let pixel_count = (rgba.len() / PIXEL_SIZE) as u64; let pixel_count = (rgba.len() / PIXEL_SIZE) as u64;
@@ -240,10 +243,21 @@ impl CursorImage {
} }
if hotspot_x >= width || hotspot_y >= height { if hotspot_x >= width || hotspot_y >= height {
return Err(BadImage::HotspotOutOfBounds { width, height, hotspot_x, hotspot_y }); return Err(BadImage::HotspotOutOfBounds {
width,
height,
hotspot_x,
hotspot_y,
});
} }
Ok(CursorImage { rgba, width, height, hotspot_x, hotspot_y }) Ok(CursorImage {
rgba,
width,
height,
hotspot_x,
hotspot_y,
})
} }
} }

View File

@@ -1,40 +1,44 @@
use std::error::Error; use std::{error, fmt};
use std::fmt::{self, Display};
/// A general error that may occur while running or creating use crate::platform_impl;
/// the event loop.
// TODO: Rename
/// An error that may be generated when requesting Winit state
#[derive(Debug)]
pub enum ExternalError {
/// The operation is not supported by the backend.
NotSupported(NotSupportedError),
/// The operation was ignored.
Ignored,
/// The OS cannot perform the operation.
Os(OsError),
}
/// The error type for when the requested operation is not supported by the backend.
#[derive(Clone)]
pub struct NotSupportedError {
_marker: (),
}
/// The error type for when the OS cannot perform the requested operation.
#[derive(Debug)]
pub struct OsError {
line: u32,
file: &'static str,
error: platform_impl::OsError,
}
/// A general error that may occur while running the Winit event loop
#[derive(Debug)] #[derive(Debug)]
#[non_exhaustive]
pub enum EventLoopError { pub enum EventLoopError {
/// The operation is not supported by the backend.
NotSupported(NotSupportedError),
/// The OS cannot perform the operation.
Os(OsError),
/// The event loop can't be re-created. /// The event loop can't be re-created.
RecreationAttempt, RecreationAttempt,
/// Application has exit with an error status. /// Application has exit with an error status.
ExitFailure(i32), ExitFailure(i32),
/// Got unspecified OS-specific error during the request.
Os(OsError),
/// Creating the event loop with the requested configuration is not supported.
NotSupported(NotSupportedError),
}
impl fmt::Display for EventLoopError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
Self::Os(err) => err.fmt(f),
Self::ExitFailure(status) => write!(f, "Exit Failure: {status}"),
Self::NotSupported(err) => err.fmt(f),
}
}
}
impl Error for EventLoopError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
if let Self::Os(err) = self {
err.source()
} else {
None
}
}
} }
impl From<OsError> for EventLoopError { impl From<OsError> for EventLoopError {
@@ -43,102 +47,18 @@ impl From<OsError> for EventLoopError {
} }
} }
impl From<NotSupportedError> for EventLoopError {
fn from(value: NotSupportedError) -> Self {
Self::NotSupported(value)
}
}
/// A general error that may occur during a request to the windowing system.
#[derive(Debug)]
#[non_exhaustive]
pub enum RequestError {
/// The request is not supported.
NotSupported(NotSupportedError),
/// The request was ignored by the operating system.
Ignored,
/// Got unspecified OS specific error during the request.
Os(OsError),
}
impl Display for RequestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotSupported(err) => err.fmt(f),
Self::Ignored => write!(f, "The request was ignored"),
Self::Os(err) => err.fmt(f),
}
}
}
impl Error for RequestError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
if let Self::Os(err) = self {
err.source()
} else {
None
}
}
}
impl From<NotSupportedError> for RequestError {
fn from(value: NotSupportedError) -> Self {
Self::NotSupported(value)
}
}
impl From<OsError> for RequestError {
fn from(value: OsError) -> Self {
Self::Os(value)
}
}
/// The requested operation is not supported.
#[derive(Debug)]
pub struct NotSupportedError {
/// The reason why a certain operation is not supported.
reason: &'static str,
}
impl NotSupportedError { impl NotSupportedError {
pub(crate) fn new(reason: &'static str) -> Self { #[inline]
Self { reason } #[allow(dead_code)]
pub(crate) fn new() -> NotSupportedError {
NotSupportedError { _marker: () }
} }
} }
impl fmt::Display for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Operation is not supported: {}", self.reason)
}
}
impl Error for NotSupportedError {}
/// Unclassified error from the OS.
#[derive(Debug)]
pub struct OsError {
line: u32,
file: &'static str,
error: Box<dyn Error + Send + Sync + 'static>,
}
impl OsError { impl OsError {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn new( pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError {
line: u32, OsError { line, file, error }
file: &'static str,
error: impl Into<Box<dyn Error + Send + Sync + 'static>>,
) -> Self {
Self { line, file, error: error.into() }
}
}
impl Display for OsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(&format!("os error at {}:{}: {}", self.file, self.line, self.error))
}
}
impl Error for OsError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(self.error.as_ref())
} }
} }
@@ -148,3 +68,72 @@ macro_rules! os_error {
crate::error::OsError::new(line!(), file!(), $error) crate::error::OsError::new(line!(), file!(), $error)
}}; }};
} }
impl fmt::Display for OsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.pad(&format!(
"os error at {}:{}: {}",
self.file, self.line, self.error
))
}
}
impl fmt::Display for ExternalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
ExternalError::NotSupported(e) => e.fmt(f),
ExternalError::Ignored => write!(f, "Operation was ignored"),
ExternalError::Os(e) => e.fmt(f),
}
}
}
impl fmt::Debug for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("NotSupportedError").finish()
}
}
impl fmt::Display for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.pad("the requested operation is not supported by Winit")
}
}
impl fmt::Display for EventLoopError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
EventLoopError::NotSupported(e) => e.fmt(f),
EventLoopError::Os(e) => e.fmt(f),
EventLoopError::ExitFailure(status) => write!(f, "Exit Failure: {status}"),
}
}
}
impl error::Error for OsError {}
impl error::Error for ExternalError {}
impl error::Error for NotSupportedError {}
impl error::Error for EventLoopError {}
#[cfg(test)]
mod tests {
#![allow(clippy::redundant_clone)]
use super::*;
// Eat attributes for testing
#[test]
fn ensure_fmt_does_not_panic() {
let _ = format!(
"{:?}, {}",
NotSupportedError::new(),
NotSupportedError::new().clone()
);
let _ = format!(
"{:?}, {}",
ExternalError::NotSupported(NotSupportedError::new()),
ExternalError::NotSupported(NotSupportedError::new())
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,30 +3,24 @@
//! //!
//! If you want to send custom events to the event loop, use //! If you want to send custom events to the event loop, use
//! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its //! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its
//! [`wake_up`][EventLoopProxy::wake_up] method. Then during handling the wake up //! [`send_event`](`EventLoopProxy::send_event`) method.
//! you can poll your event sources.
//! //!
//! See the root-level documentation for information on how to create and use an event loop to //! See the root-level documentation for information on how to create and use an event loop to
//! handle events. //! handle events.
use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc; use std::{error, fmt};
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time::{Duration, Instant}; use web_time::{Duration, Instant};
use crate::application::ApplicationHandler; use crate::error::{EventLoopError, OsError};
use crate::error::{EventLoopError, RequestError}; use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
use crate::monitor::MonitorHandle; use crate::{event::Event, monitor::MonitorHandle, platform_impl};
use crate::platform_impl;
use crate::utils::AsAny;
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes};
/// Provides a way to retrieve events from the system and from the windows that were registered to /// Provides a way to retrieve events from the system and from the windows that were registered to
/// the events loop. /// the events loop.
@@ -38,13 +32,22 @@ use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttri
/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs. /// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs.
/// ///
/// Note that this cannot be shared across threads (due to platform-dependant logic /// Note that this cannot be shared across threads (due to platform-dependant logic
/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access, /// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access, the
/// the [`Window`] created from this _can_ be sent to an other thread, and the /// [`Window`] created from this _can_ be sent to an other thread, and the
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread. /// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
/// ///
/// [`Window`]: crate::window::Window /// [`Window`]: crate::window::Window
pub struct EventLoop { pub struct EventLoop<T: 'static> {
pub(crate) event_loop: platform_impl::EventLoop, pub(crate) event_loop: platform_impl::EventLoop<T>,
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
}
/// Target that associates windows with an [`EventLoop`].
///
/// This type exists to allow you to create new windows while Winit executes
/// your callback.
pub struct ActiveEventLoop {
pub(crate) p: platform_impl::ActiveEventLoop,
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
} }
@@ -53,15 +56,25 @@ pub struct EventLoop {
/// This is used to make specifying options that affect the whole application /// This is used to make specifying options that affect the whole application
/// easier. But note that constructing multiple event loops is not supported. /// easier. But note that constructing multiple event loops is not supported.
/// ///
/// This can be created using [`EventLoop::builder`]. /// This can be created using [`EventLoop::new`] or [`EventLoop::with_user_event`].
#[derive(Default, PartialEq, Eq, Hash)] #[derive(Default)]
pub struct EventLoopBuilder { pub struct EventLoopBuilder<T: 'static> {
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes, pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
_p: PhantomData<T>,
} }
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl EventLoopBuilder { impl EventLoopBuilder<()> {
/// Start building a new event loop.
#[inline]
#[deprecated = "use `EventLoop::builder` instead"]
pub fn new() -> Self {
EventLoop::builder()
}
}
impl<T> EventLoopBuilder<T> {
/// Builds a new event loop. /// Builds a new event loop.
/// ///
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread, /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
@@ -74,31 +87,28 @@ impl EventLoopBuilder {
/// Attempting to create the event loop off the main thread will panic. This /// Attempting to create the event loop off the main thread will panic. This
/// restriction isn't strictly necessary on all platforms, but is imposed to /// restriction isn't strictly necessary on all platforms, but is imposed to
/// eliminate any nasty surprises when porting to platforms that require it. /// eliminate any nasty surprises when porting to platforms that require it.
/// `EventLoopBuilderExt::with_any_thread` functions are exposed in the relevant /// `EventLoopBuilderExt::any_thread` functions are exposed in the relevant
/// [`platform`] module if the target platform supports creating an event /// [`platform`] module if the target platform supports creating an event
/// loop on any thread. /// loop on any thread.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY` or /// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY`
/// `DISPLAY` respectively when building the event loop. /// or `DISPLAY` respectively when building the event loop.
/// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling /// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling
/// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic. /// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic.
/// ///
/// [`platform`]: crate::platform /// [`platform`]: crate::platform
#[cfg_attr( #[cfg_attr(
android_platform, android,
doc = "[`.with_android_app(app)`]: \ doc = "[`.with_android_app(app)`]: crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
)] )]
#[cfg_attr( #[cfg_attr(
not(android_platform), not(android),
doc = "[`.with_android_app(app)`]: #only-available-on-android" doc = "[`.with_android_app(app)`]: #only-available-on-android"
)] )]
#[inline] #[inline]
pub fn build(&mut self) -> Result<EventLoop, EventLoopError> { pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> {
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
return Err(EventLoopError::RecreationAttempt); return Err(EventLoopError::RecreationAttempt);
} }
@@ -117,27 +127,26 @@ impl EventLoopBuilder {
} }
} }
impl fmt::Debug for EventLoopBuilder { impl<T> fmt::Debug for EventLoop<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopBuilder").finish_non_exhaustive() f.pad("EventLoop { .. }")
} }
} }
impl fmt::Debug for EventLoop { impl fmt::Debug for ActiveEventLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoop").finish_non_exhaustive() f.pad("ActiveEventLoop { .. }")
} }
} }
/// Set through [`ActiveEventLoop::set_control_flow()`]. /// Set through [`ActiveEventLoop::set_control_flow()`].
/// ///
/// Indicates the desired behavior of the event loop after [`about_to_wait`] is called. /// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
/// ///
/// Defaults to [`Wait`]. /// Defaults to [`Wait`].
/// ///
/// [`Wait`]: Self::Wait /// [`Wait`]: Self::Wait
/// [`about_to_wait`]: crate::application::ApplicationHandler::about_to_wait #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub enum ControlFlow { pub enum ControlFlow {
/// When the current loop iteration finishes, immediately begin a new iteration regardless of /// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process. /// whether or not new events are available to process.
@@ -150,9 +159,9 @@ pub enum ControlFlow {
/// When the current loop iteration finishes, suspend the thread until either another event /// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the given time is reached. /// arrives or the given time is reached.
/// ///
/// Useful for implementing efficient timers. Applications which want to render at the /// Useful for implementing efficient timers. Applications which want to render at the display's
/// display's native refresh rate should instead use [`Poll`] and the VSync functionality /// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
/// of a graphics API to reduce odds of missed frames. /// to reduce odds of missed frames.
/// ///
/// [`Poll`]: Self::Poll /// [`Poll`]: Self::Poll
WaitUntil(Instant), WaitUntil(Instant),
@@ -174,12 +183,12 @@ impl ControlFlow {
} }
} }
impl EventLoop { impl EventLoop<()> {
/// Create the event loop. /// Create the event loop.
/// ///
/// This is an alias of `EventLoop::builder().build()`. /// This is an alias of `EventLoop::builder().build()`.
#[inline] #[inline]
pub fn new() -> Result<EventLoop, EventLoopError> { pub fn new() -> Result<EventLoop<()>, EventLoopError> {
Self::builder().build() Self::builder().build()
} }
@@ -189,105 +198,74 @@ impl EventLoop {
/// ///
/// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that. /// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that.
#[inline] #[inline]
pub fn builder() -> EventLoopBuilder { pub fn builder() -> EventLoopBuilder<()> {
EventLoopBuilder { platform_specific: Default::default() } Self::with_user_event()
} }
} }
impl EventLoop { impl<T> EventLoop<T> {
/// Run the event loop on the current thread. /// Start building a new event loop, with the given type as the user event
/// /// type.
/// You pass in a closure that returns your application state. This closure has access to the pub fn with_user_event() -> EventLoopBuilder<T> {
/// currently running event loop, allowing you to initialize your windows and surfaces in here. EventLoopBuilder {
platform_specific: Default::default(),
_p: PhantomData,
}
}
/// Runs the event loop in the calling thread and calls the given `event_handler` closure
/// to dispatch any pending events.
/// ///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
/// ## Event loop flow
///
/// This function internally handles the different parts of a traditional event-handling loop.
/// You could imagine this method being implemented like this:
///
/// ```rust,ignore
/// // Initialize.
/// let mut app = init_closure(event_loop);
/// let mut start_cause = StartCause::Init;
///
/// // Run loop.
/// while !elwt.exiting() {
/// // Wake up.
/// app.new_events(event_loop, start_cause);
///
/// // Handle events by the user.
/// for (device_id, event) in incoming_device_events {
/// app.device_event(event_loop, device_id, event);
/// }
/// for (window_id, event) in incoming_window_events {
/// app.window_event(event_loop, window_id, event);
/// }
///
/// // Done handling events, wait until we're woken up again.
/// app.about_to_wait(event_loop);
/// start_cause = wait_if_necessary();
/// }
///
/// // Finished running, drop application state.
/// drop(app);
/// ```
///
/// This is of course a very coarse-grained overview, and leaves out timing details like
/// [`ControlFlow::WaitUntil`] and life-cycle methods like [`ApplicationHandler::resumed`].
///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS:** Will never return to the caller and so values not passed to this function will /// - **iOS:** Will never return to the caller and so values not passed to this function will
/// *not* be dropped before the process exits. /// *not* be dropped before the process exits.
/// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript /// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript exception
/// exception (that Rust doesn't see) that will also mean that the rest of the function is /// (that Rust doesn't see) that will also mean that the rest of the function is never executed
/// never executed and any values not passed to this function will *not* be dropped. /// and any values not passed to this function will *not* be dropped.
/// ///
/// Web applications are recommended to use /// Web applications are recommended to use
#[cfg_attr( #[cfg_attr(
any(web_platform, docsrs), web_platform,
doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]" doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]"
)] )]
#[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")] #[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")]
/// [^1] instead of [`run()`] to avoid the need for the Javascript exception trick, and to /// [^1] instead of [`run()`] to avoid the need
/// make it clearer that the event loop runs asynchronously (via the browser's own, /// for the Javascript exception trick, and to make it clearer that the event loop runs
/// internal, event loop) and doesn't block the current thread of execution like it does /// asynchronously (via the browser's own, internal, event loop) and doesn't block the
/// on other platforms. /// current thread of execution like it does on other platforms.
/// ///
/// This function won't be available with `target_feature = "exception-handling"`. /// This function won't be available with `target_feature = "exception-handling"`.
/// ///
/// [^1]: `spawn_app()` is only available on the Web platform.
///
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`run()`]: Self::run() /// [`run()`]: Self::run()
/// [^1]: `EventLoopExtWebSys::spawn()` is only available on Web.
#[inline] #[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))] #[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run<A: ApplicationHandler>( pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
self, where
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A, F: FnMut(Event<T>, &ActiveEventLoop),
) -> Result<(), EventLoopError> { {
self.event_loop.run(init_closure) self.event_loop.run(event_handler)
}
/// Run the event loop with the given application state.
#[deprecated = "less flexible version of `run`"]
pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
self.run(|_event_loop| app)
} }
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread. /// to the main event loop, possibly from another thread.
pub fn create_proxy(&self) -> EventLoopProxy { pub fn create_proxy(&self) -> EventLoopProxy<T> {
self.event_loop.window_target().create_proxy() EventLoopProxy {
event_loop_proxy: self.event_loop.create_proxy(),
}
} }
/// Gets a persistent reference to the underlying platform display. /// Gets a persistent reference to the underlying platform display.
/// ///
/// See the [`OwnedDisplayHandle`] type for more information. /// See the [`OwnedDisplayHandle`] type for more information.
pub fn owned_display_handle(&self) -> OwnedDisplayHandle { pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
self.event_loop.window_target().owned_display_handle() OwnedDisplayHandle {
platform: self.event_loop.window_target().p.owned_display_handle(),
}
} }
/// Change if or when [`DeviceEvent`]s are captured. /// Change if or when [`DeviceEvent`]s are captured.
@@ -296,105 +274,113 @@ impl EventLoop {
/// ///
/// [`DeviceEvent`]: crate::event::DeviceEvent /// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn listen_device_events(&self, allowed: DeviceEvents) { pub fn listen_device_events(&self, allowed: DeviceEvents) {
let _span = tracing::debug_span!( self.event_loop
"winit::EventLoop::listen_device_events", .window_target()
allowed = ?allowed .p
) .listen_device_events(allowed);
.entered();
self.event_loop.window_target().listen_device_events(allowed)
} }
/// Sets the [`ControlFlow`]. /// Sets the [`ControlFlow`].
pub fn set_control_flow(&self, control_flow: ControlFlow) { pub fn set_control_flow(&self, control_flow: ControlFlow) {
self.event_loop.window_target().set_control_flow(control_flow); self.event_loop
.window_target()
.p
.set_control_flow(control_flow)
}
/// Create a window.
///
/// Creating window without event loop running often leads to improper window creation;
/// use [`ActiveEventLoop::create_window`] instead.
#[deprecated = "use `ActiveEventLoop::create_window` instead"]
#[inline]
pub fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError> {
let window =
platform_impl::Window::new(&self.event_loop.window_target().p, window_attributes)?;
Ok(Window { window })
} }
/// Create custom cursor. /// Create custom cursor.
/// pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
/// ## Platform-specific self.event_loop
/// .window_target()
/// **iOS / Android / Orbital:** Unsupported. .p
pub fn create_custom_cursor( .create_custom_cursor(custom_cursor)
&self,
custom_cursor: CustomCursorSource,
) -> Result<CustomCursor, RequestError> {
self.event_loop.window_target().create_custom_cursor(custom_cursor)
} }
} }
impl HasDisplayHandle for EventLoop { #[cfg(feature = "rwh_06")]
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> { impl<T> rwh_06::HasDisplayHandle for EventLoop<T> {
HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle()) fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target())
}
}
#[cfg(feature = "rwh_05")]
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
rwh_05::HasRawDisplayHandle::raw_display_handle(self.event_loop.window_target())
} }
} }
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
impl AsFd for EventLoop { impl<T> AsFd for EventLoop<T> {
/// Get the underlying [EventLoop]'s `fd` which you can register /// Get the underlying [EventLoop]'s `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_app_events`] API. /// loop must be polled with the [`pump_events`] API.
/// ///
/// [`calloop`]: https://crates.io/crates/calloop /// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio /// [`mio`]: https://crates.io/crates/mio
/// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events /// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events
fn as_fd(&self) -> BorrowedFd<'_> { fn as_fd(&self) -> BorrowedFd<'_> {
self.event_loop.as_fd() self.event_loop.as_fd()
} }
} }
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
impl AsRawFd for EventLoop { impl<T> AsRawFd for EventLoop<T> {
/// Get the underlying [EventLoop]'s raw `fd` which you can register /// Get the underlying [EventLoop]'s raw `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_app_events`] API. /// loop must be polled with the [`pump_events`] API.
/// ///
/// [`calloop`]: https://crates.io/crates/calloop /// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio /// [`mio`]: https://crates.io/crates/mio
/// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events /// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.event_loop.as_raw_fd() self.event_loop.as_raw_fd()
} }
} }
pub trait ActiveEventLoop: AsAny { impl ActiveEventLoop {
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread.
fn create_proxy(&self) -> EventLoopProxy;
/// Create the window. /// Create the window.
/// ///
/// Possible causes of error include denied permission, incompatible system, and lack of memory. /// Possible causes of error include denied permission, incompatible system, and lack of memory.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Web:** The window is created but not inserted into the Web page automatically. Please /// - **Web:** The window is created but not inserted into the web page automatically. Please
/// see the Web platform module for more information. /// see the web platform module for more information.
fn create_window( #[inline]
&self, pub fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError> {
window_attributes: WindowAttributes, let window = platform_impl::Window::new(&self.p, window_attributes)?;
) -> Result<Box<dyn Window>, RequestError>; Ok(Window { window })
}
/// Create custom cursor. /// Create custom cursor.
/// pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
/// ## Platform-specific self.p.create_custom_cursor(custom_cursor)
/// }
/// **iOS / Android / Orbital:** Unsupported.
fn create_custom_cursor(
&self,
custom_cursor: CustomCursorSource,
) -> Result<CustomCursor, RequestError>;
/// Returns the list of all the monitors available on the system. /// Returns the list of all the monitors available on the system.
/// #[inline]
/// ## Platform-specific pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
/// #[allow(clippy::useless_conversion)] // false positive on some platforms
/// **Web:** Only returns the current monitor without self.p
#[cfg_attr( .available_monitors()
any(web_platform, docsrs), .into_iter()
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." .map(|inner| MonitorHandle { inner })
)] }
#[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
fn available_monitors(&self) -> Box<dyn Iterator<Item = MonitorHandle>>;
/// Returns the primary monitor of the system. /// Returns the primary monitor of the system.
/// ///
@@ -402,14 +388,13 @@ pub trait ActiveEventLoop: AsAny {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Wayland:** Always returns `None`. /// **Wayland / Web:** Always returns `None`.
/// - **Web:** Always returns `None` without #[inline]
#[cfg_attr( pub fn primary_monitor(&self) -> Option<MonitorHandle> {
any(web_platform, docsrs), self.p
doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." .primary_monitor()
)] .map(|inner| MonitorHandle { inner })
#[cfg_attr(not(any(web_platform, docsrs)), doc = " detailed monitor permissions.")] }
fn primary_monitor(&self) -> Option<MonitorHandle>;
/// Change if or when [`DeviceEvent`]s are captured. /// Change if or when [`DeviceEvent`]s are captured.
/// ///
@@ -422,49 +407,64 @@ pub trait ActiveEventLoop: AsAny {
/// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported. /// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
/// ///
/// [`DeviceEvent`]: crate::event::DeviceEvent /// [`DeviceEvent`]: crate::event::DeviceEvent
fn listen_device_events(&self, allowed: DeviceEvents); pub fn listen_device_events(&self, allowed: DeviceEvents) {
self.p.listen_device_events(allowed);
/// Returns the current system theme. }
///
/// Returns `None` if it cannot be determined on the current platform.
///
/// ## Platform-specific
///
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
fn system_theme(&self) -> Option<Theme>;
/// Sets the [`ControlFlow`]. /// Sets the [`ControlFlow`].
fn set_control_flow(&self, control_flow: ControlFlow); pub fn set_control_flow(&self, control_flow: ControlFlow) {
self.p.set_control_flow(control_flow)
}
/// Gets the current [`ControlFlow`]. /// Gets the current [`ControlFlow`].
fn control_flow(&self) -> ControlFlow; pub fn control_flow(&self) -> ControlFlow {
self.p.control_flow()
}
/// This stops the event loop. /// This exits the event loop.
fn exit(&self); ///
/// See [`LoopExiting`](Event::LoopExiting).
pub fn exit(&self) {
self.p.exit()
}
/// Returns if the [`EventLoop`] is about to stop. /// Returns if the [`EventLoop`] is about to stop.
/// ///
/// See [`exit()`][Self::exit]. /// See [`exit()`](Self::exit).
fn exiting(&self) -> bool; pub fn exiting(&self) -> bool {
self.p.exiting()
}
/// Gets a persistent reference to the underlying platform display. /// Gets a persistent reference to the underlying platform display.
/// ///
/// See the [`OwnedDisplayHandle`] type for more information. /// See the [`OwnedDisplayHandle`] type for more information.
fn owned_display_handle(&self) -> OwnedDisplayHandle; pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle {
/// Get the raw-window-handle handle. platform: self.p.owned_display_handle(),
fn rwh_06_handle(&self) -> &dyn HasDisplayHandle; }
}
} }
impl HasDisplayHandle for dyn ActiveEventLoop + '_ { #[cfg(feature = "rwh_06")]
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> { impl rwh_06::HasDisplayHandle for ActiveEventLoop {
self.rwh_06_handle().display_handle() fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.p.raw_display_handle_rwh_06()?;
// SAFETY: The display will never be deallocated while the event loop is alive.
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) })
}
}
#[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawDisplayHandle for ActiveEventLoop {
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.p.raw_display_handle_rwh_05()
} }
} }
/// A proxy for the underlying display handle. /// A proxy for the underlying display handle.
/// ///
/// The purpose of this type is to provide a cheaply cloneable handle to the underlying /// The purpose of this type is to provide a cheaply clonable handle to the underlying
/// display handle. This is often used by graphics APIs to connect to the underlying APIs. /// display handle. This is often used by graphics APIs to connect to the underlying APIs.
/// It is difficult to keep a handle to the [`EventLoop`] type or the [`ActiveEventLoop`] /// It is difficult to keep a handle to the [`EventLoop`] type or the [`ActiveEventLoop`]
/// type. In contrast, this type involves no lifetimes and can be persisted for as long as /// type. In contrast, this type involves no lifetimes and can be persisted for as long as
@@ -476,83 +476,87 @@ impl HasDisplayHandle for dyn ActiveEventLoop + '_ {
/// - A reference-counted pointer to the underlying type. /// - A reference-counted pointer to the underlying type.
#[derive(Clone)] #[derive(Clone)]
pub struct OwnedDisplayHandle { pub struct OwnedDisplayHandle {
pub(crate) handle: Arc<dyn HasDisplayHandle>, #[cfg_attr(not(any(feature = "rwh_05", feature = "rwh_06")), allow(dead_code))]
} platform: platform_impl::OwnedDisplayHandle,
impl OwnedDisplayHandle {
pub(crate) fn new(handle: Arc<dyn HasDisplayHandle>) -> Self {
Self { handle }
}
}
impl HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.handle.display_handle()
}
} }
impl fmt::Debug for OwnedDisplayHandle { impl fmt::Debug for OwnedDisplayHandle {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive() f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
} }
} }
impl PartialEq for OwnedDisplayHandle { #[cfg(feature = "rwh_06")]
fn eq(&self, other: &Self) -> bool { impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
match (self.display_handle(), other.display_handle()) { #[inline]
(Ok(lhs), Ok(rhs)) => lhs == rhs, fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
_ => false, let raw = self.platform.raw_display_handle_rwh_06()?;
// SAFETY: The underlying display handle should be safe.
let handle = unsafe { rwh_06::DisplayHandle::borrow_raw(raw) };
Ok(handle)
}
}
#[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawDisplayHandle for OwnedDisplayHandle {
#[inline]
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.platform.raw_display_handle_rwh_05()
}
}
/// Used to send custom events to [`EventLoop`].
pub struct EventLoopProxy<T: 'static> {
event_loop_proxy: platform_impl::EventLoopProxy<T>,
}
impl<T: 'static> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
Self {
event_loop_proxy: self.event_loop_proxy.clone(),
} }
} }
} }
impl Eq for OwnedDisplayHandle {} impl<T: 'static> EventLoopProxy<T> {
/// Send an event to the [`EventLoop`] from which this proxy was created. This emits a
pub(crate) trait EventLoopProxyProvider: Send + Sync { /// `UserEvent(event)` event in the event loop, where `event` is the value passed to this
/// See [`EventLoopProxy::wake_up`] for details. /// function.
fn wake_up(&self); ///
/// Returns an `Err` if the associated [`EventLoop`] no longer exists.
///
/// [`UserEvent(event)`]: Event::UserEvent
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.event_loop_proxy.send_event(event)
}
} }
/// Control the [`EventLoop`], possibly from a different thread, without referencing it directly. impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
#[derive(Clone)]
pub struct EventLoopProxy {
pub(crate) proxy: Arc<dyn EventLoopProxyProvider>,
}
impl fmt::Debug for EventLoopProxy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopProxy").finish_non_exhaustive() f.pad("EventLoopProxy { .. }")
} }
} }
impl EventLoopProxy { /// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] that
/// Wake up the [`EventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being /// no longer exists.
/// called. ///
/// /// Contains the original event given to [`EventLoopProxy::send_event`].
/// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
/// documentation on that for details. pub struct EventLoopClosed<T>(pub T);
///
/// If the event loop is no longer running, this is a no-op.
///
/// [`proxy_wake_up`]: ApplicationHandler::proxy_wake_up
///
/// # Platform-specific
///
/// - **Windows**: The wake-up may be ignored under high contention, see [#3687].
///
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
pub fn wake_up(&self) {
self.proxy.wake_up();
}
pub(crate) fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self { impl<T> fmt::Display for EventLoopClosed<T> {
Self { proxy } fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Tried to wake up a closed `EventLoop`")
} }
} }
impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {}
/// Control when device events are captured. /// Control when device events are captured.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DeviceEvents { pub enum DeviceEvents {
/// Report device events regardless of window focus. /// Report device events regardless of window focus.
Always, Always,
@@ -572,7 +576,7 @@ pub enum DeviceEvents {
/// containing [`AsyncRequestSerial`] and some closure associated with it. /// containing [`AsyncRequestSerial`] and some closure associated with it.
/// Then once event is arriving the working list is being traversed and a job /// Then once event is arriving the working list is being traversed and a job
/// executed and removed from the list. /// executed and removed from the list.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AsyncRequestSerial { pub struct AsyncRequestSerial {
serial: usize, serial: usize,
} }

View File

@@ -1,7 +1,5 @@
use std::error::Error;
use std::{fmt, io, mem};
use crate::platform_impl::PlatformIcon; use crate::platform_impl::PlatformIcon;
use std::{error::Error, fmt, io, mem};
#[repr(C)] #[repr(C)]
#[derive(Debug)] #[derive(Debug)]
@@ -22,7 +20,12 @@ pub enum BadIcon {
ByteCountNotDivisibleBy4 { byte_count: usize }, ByteCountNotDivisibleBy4 { byte_count: usize },
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`. /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
/// At least one of your arguments is incorrect. /// At least one of your arguments is incorrect.
DimensionsVsPixelCount { width: u32, height: u32, width_x_height: usize, pixel_count: usize }, DimensionsVsPixelCount {
width: u32,
height: u32,
width_x_height: usize,
pixel_count: usize,
},
/// Produced when underlying OS functionality failed to create the icon /// Produced when underlying OS functionality failed to create the icon
OsError(io::Error), OsError(io::Error),
} }
@@ -30,19 +33,17 @@ pub enum BadIcon {
impl fmt::Display for BadIcon { impl fmt::Display for BadIcon {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!( BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
f, "The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
"The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making \ ),
it impossible to interpret as 32bpp RGBA pixels.", BadIcon::DimensionsVsPixelCount {
width,
height,
width_x_height,
pixel_count,
} => write!(f,
"The specified dimensions ({width:?}x{height:?}) don't match the number of pixels supplied by the `rgba` argument ({pixel_count:?}). For those dimensions, the expected pixel count is {width_x_height:?}.",
), ),
BadIcon::DimensionsVsPixelCount { width, height, width_x_height, pixel_count } => {
write!(
f,
"The specified dimensions ({width:?}x{height:?}) don't match the number of \
pixels supplied by the `rgba` argument ({pixel_count:?}). For those \
dimensions, the expected pixel count is {width_x_height:?}.",
)
},
BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {e:?}"), BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {e:?}"),
} }
} }
@@ -50,15 +51,15 @@ impl fmt::Display for BadIcon {
impl Error for BadIcon {} impl Error for BadIcon {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct RgbaIcon { pub(crate) struct RgbaIcon {
pub(crate) rgba: Vec<u8>, pub(crate) rgba: Vec<u8>,
pub(crate) width: u32, pub(crate) width: u32,
pub(crate) height: u32, pub(crate) height: u32,
} }
/// For platforms which don't have window icons (e.g. Web) /// For platforms which don't have window icons (e.g. web)
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct NoIcon; pub(crate) struct NoIcon;
#[allow(dead_code)] // These are not used on every platform #[allow(dead_code)] // These are not used on every platform
@@ -68,7 +69,9 @@ mod constructors {
impl RgbaIcon { impl RgbaIcon {
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> { pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
if rgba.len() % PIXEL_SIZE != 0 { if rgba.len() % PIXEL_SIZE != 0 {
return Err(BadIcon::ByteCountNotDivisibleBy4 { byte_count: rgba.len() }); return Err(BadIcon::ByteCountNotDivisibleBy4 {
byte_count: rgba.len(),
});
} }
let pixel_count = rgba.len() / PIXEL_SIZE; let pixel_count = rgba.len() / PIXEL_SIZE;
if pixel_count != (width * height) as usize { if pixel_count != (width * height) as usize {
@@ -79,7 +82,11 @@ mod constructors {
pixel_count, pixel_count,
}) })
} else { } else {
Ok(RgbaIcon { rgba, width, height }) Ok(RgbaIcon {
rgba,
width,
height,
})
} }
} }
} }
@@ -94,7 +101,7 @@ mod constructors {
} }
/// An icon used for the window titlebar, taskbar, etc. /// An icon used for the window titlebar, taskbar, etc.
#[derive(Clone, Eq, Hash, PartialEq)] #[derive(Clone)]
pub struct Icon { pub struct Icon {
pub(crate) inner: PlatformIcon, pub(crate) inner: PlatformIcon,
} }
@@ -111,8 +118,8 @@ impl Icon {
/// The length of `rgba` must be divisible by 4, and `width * height` must equal /// The length of `rgba` must be divisible by 4, and `width * height` must equal
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error. /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> { pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
let _span = tracing::debug_span!("winit::Icon::from_rgba", width, height).entered(); Ok(Icon {
inner: PlatformIcon::from_rgba(rgba, width, height)?,
Ok(Icon { inner: PlatformIcon::from_rgba(rgba, width, height)? }) })
} }
} }

View File

@@ -84,7 +84,7 @@ pub use smol_str::SmolStr;
/// haven't mapped for you yet, this lets you use use [`KeyCode`] to: /// haven't mapped for you yet, this lets you use use [`KeyCode`] to:
/// ///
/// - Correctly match key press and release events. /// - Correctly match key press and release events.
/// - On non-Web platforms, support assigning keybinds to virtually any key through a UI. /// - On non-web platforms, support assigning keybinds to virtually any key through a UI.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum NativeKeyCode { pub enum NativeKeyCode {
@@ -106,23 +106,23 @@ impl std::fmt::Debug for NativeKeyCode {
match self { match self {
Unidentified => { Unidentified => {
debug_tuple = f.debug_tuple("Unidentified"); debug_tuple = f.debug_tuple("Unidentified");
}, }
Android(code) => { Android(code) => {
debug_tuple = f.debug_tuple("Android"); debug_tuple = f.debug_tuple("Android");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
MacOS(code) => { MacOS(code) => {
debug_tuple = f.debug_tuple("MacOS"); debug_tuple = f.debug_tuple("MacOS");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Windows(code) => { Windows(code) => {
debug_tuple = f.debug_tuple("Windows"); debug_tuple = f.debug_tuple("Windows");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Xkb(code) => { Xkb(code) => {
debug_tuple = f.debug_tuple("Xkb"); debug_tuple = f.debug_tuple("Xkb");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
} }
debug_tuple.finish() debug_tuple.finish()
} }
@@ -162,27 +162,27 @@ impl std::fmt::Debug for NativeKey {
match self { match self {
Unidentified => { Unidentified => {
debug_tuple = f.debug_tuple("Unidentified"); debug_tuple = f.debug_tuple("Unidentified");
}, }
Android(code) => { Android(code) => {
debug_tuple = f.debug_tuple("Android"); debug_tuple = f.debug_tuple("Android");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
MacOS(code) => { MacOS(code) => {
debug_tuple = f.debug_tuple("MacOS"); debug_tuple = f.debug_tuple("MacOS");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Windows(code) => { Windows(code) => {
debug_tuple = f.debug_tuple("Windows"); debug_tuple = f.debug_tuple("Windows");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Xkb(code) => { Xkb(code) => {
debug_tuple = f.debug_tuple("Xkb"); debug_tuple = f.debug_tuple("Xkb");
debug_tuple.field(&format_args!("0x{code:04X}")); debug_tuple.field(&format_args!("0x{code:04X}"));
}, }
Web(code) => { Web(code) => {
debug_tuple = f.debug_tuple("Web"); debug_tuple = f.debug_tuple("Web");
debug_tuple.field(code); debug_tuple.field(code);
}, }
} }
debug_tuple.finish() debug_tuple.finish()
} }
@@ -218,7 +218,7 @@ impl PartialEq<NativeKeyCode> for NativeKey {
/// Represents the location of a physical key. /// Represents the location of a physical key.
/// ///
/// This type is a superset of [`KeyCode`], including an [`Unidentified`][Self::Unidentified] /// This type is a superset of [`KeyCode`], including an [`Unidentified`](Self::Unidentified)
/// variant. /// variant.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -442,8 +442,7 @@ pub enum KeyCode {
Tab, Tab,
/// Japanese: <kbd>変</kbd> (henkan) /// Japanese: <kbd>変</kbd> (henkan)
Convert, Convert,
/// Japanese: <kbd>カタカナ</kbd>/<kbd>ひらがな</kbd>/<kbd>ローマ字</kbd> /// Japanese: <kbd>カタカナ</kbd>/<kbd>ひらがな</kbd>/<kbd>ローマ字</kbd> (katakana/hiragana/romaji)
/// (katakana/hiragana/romaji)
KanaMode, KanaMode,
/// Korean: HangulMode <kbd>한/영</kbd> (han/yeong) /// Korean: HangulMode <kbd>한/영</kbd> (han/yeong)
/// ///
@@ -491,8 +490,7 @@ pub enum KeyCode {
NumLock, NumLock,
/// <kbd>0 Ins</kbd> on a keyboard. <kbd>0</kbd> on a phone or remote control /// <kbd>0 Ins</kbd> on a keyboard. <kbd>0</kbd> on a phone or remote control
Numpad0, Numpad0,
/// <kbd>1 End</kbd> on a keyboard. <kbd>1</kbd> or <kbd>1 QZ</kbd> on a phone or remote /// <kbd>1 End</kbd> on a keyboard. <kbd>1</kbd> or <kbd>1 QZ</kbd> on a phone or remote control
/// control
Numpad1, Numpad1,
/// <kbd>2 ↓</kbd> on a keyboard. <kbd>2 ABC</kbd> on a phone or remote control /// <kbd>2 ↓</kbd> on a keyboard. <kbd>2 ABC</kbd> on a phone or remote control
Numpad2, Numpad2,
@@ -796,14 +794,13 @@ pub enum NamedKey {
// Legacy modifier key. // Legacy modifier key.
Hyper, Hyper,
/// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard /// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard
/// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` /// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` key.
/// key.
/// ///
/// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key. /// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key.
Super, Super,
/// The `Enter` or `↵` key. Used to activate current selection or accept current input. This /// The `Enter` or `↵` key. Used to activate current selection or accept current input. This key
/// key value is also used for the `Return` (Macintosh numpad) key. This key value is also /// value is also used for the `Return` (Macintosh numpad) key. This key value is also used for
/// used for the Android `KEYCODE_DPAD_CENTER`. /// the Android `KEYCODE_DPAD_CENTER`.
Enter, Enter,
/// The Horizontal Tabulation `Tab` key. /// The Horizontal Tabulation `Tab` key.
Tab, Tab,
@@ -839,8 +836,8 @@ pub enum NamedKey {
CrSel, CrSel,
/// Cut the current selection. (`APPCOMMAND_CUT`) /// Cut the current selection. (`APPCOMMAND_CUT`)
Cut, Cut,
/// Used to delete the character to the right of the cursor. This key value is also used for /// Used to delete the character to the right of the cursor. This key value is also used for the
/// the key labeled `Delete` on MacOS keyboards when `Fn` is active. /// key labeled `Delete` on MacOS keyboards when `Fn` is active.
Delete, Delete,
/// The Erase to End of Field key. This key deletes all characters from the current cursor /// The Erase to End of Field key. This key deletes all characters from the current cursor
/// position to the end of the current field. /// position to the end of the current field.
@@ -924,8 +921,8 @@ pub enum NamedKey {
/// their code points. /// their code points.
CodeInput, CodeInput,
/// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a /// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a
/// manner similar to a dead key, triggering a mode where subsequent key presses are combined /// manner similar to a dead key, triggering a mode where subsequent key presses are combined to
/// to produce a different character. /// produce a different character.
Compose, Compose,
/// Convert the current input method sequence. /// Convert the current input method sequence.
Convert, Convert,
@@ -964,9 +961,9 @@ pub enum NamedKey {
/// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from /// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from
/// romaji mode). /// romaji mode).
KanaMode, KanaMode,
/// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key is
/// is typically used to switch to a hiragana keyboard for the purpose of converting input /// typically used to switch to a hiragana keyboard for the purpose of converting input into
/// into kanji. (`KEYCODE_KANA`) /// kanji. (`KEYCODE_KANA`)
KanjiMode, KanjiMode,
/// The Katakana (Japanese Kana characters) key. /// The Katakana (Japanese Kana characters) key.
Katakana, Katakana,
@@ -1568,15 +1565,10 @@ impl NamedKey {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # #[cfg(web_platform)]
/// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
/// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)]
/// # fn main() {
/// use winit::keyboard::NamedKey; /// use winit::keyboard::NamedKey;
/// ///
/// assert_eq!(NamedKey::Enter.to_text(), Some("\r")); /// assert_eq!(NamedKey::Enter.to_text(), Some("\r"));
/// assert_eq!(NamedKey::F20.to_text(), None); /// assert_eq!(NamedKey::F20.to_text(), None);
/// # }
/// ``` /// ```
pub fn to_text(&self) -> Option<&str> { pub fn to_text(&self) -> Option<&str> {
match self { match self {
@@ -1596,16 +1588,11 @@ impl Key {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # #[cfg(web_platform)] /// use winit::keyboard::{NamedKey, Key};
/// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
/// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)]
/// # fn main() {
/// use winit::keyboard::{Key, NamedKey};
/// ///
/// assert_eq!(Key::Character("a".into()).to_text(), Some("a")); /// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
/// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r")); /// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
/// assert_eq!(Key::Named(NamedKey::F20).to_text(), None); /// assert_eq!(Key::Named(NamedKey::F20).to_text(), None);
/// # }
/// ``` /// ```
pub fn to_text(&self) -> Option<&str> { pub fn to_text(&self) -> Option<&str> {
match self { match self {
@@ -1623,18 +1610,17 @@ impl Key {
/// keys can be above the letters or on the numpad. This enum allows the user to differentiate /// keys can be above the letters or on the numpad. This enum allows the user to differentiate
/// them. /// them.
/// ///
/// See the documentation for the [`location`] field on the [`KeyEvent`] struct for more /// See the documentation for the [`location`] field on the [`KeyEvent`] struct for more information.
/// information.
/// ///
/// [`location`]: ../event/struct.KeyEvent.html#structfield.location /// [`location`]: ../event/struct.KeyEvent.html#structfield.location
/// [`KeyEvent`]: crate::event::KeyEvent /// [`KeyEvent`]: crate::event::KeyEvent
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum KeyLocation { pub enum KeyLocation {
/// The key is in its "normal" location on the keyboard. /// The key is in its "normal" location on the keyboard.
/// ///
/// For instance, the "1" key above the "Q" key on a QWERTY keyboard will use this location. /// For instance, the "1" key above the "Q" key on a QWERTY keyboard will use this location. This
/// This invariant is also returned when the location of the key cannot be identified. /// invariant is also returned when the location of the key cannot be identified.
/// ///
/// ![Standard 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_standard_1_key.svg) /// ![Standard 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_standard_1_key.svg)
/// ///
@@ -1700,7 +1686,6 @@ bitflags! {
/// ///
/// Each flag represents a modifier and is set if this modifier is active. /// Each flag represents a modifier and is set if this modifier is active.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModifiersState: u32 { pub struct ModifiersState: u32 {
/// The "shift" key. /// The "shift" key.
const SHIFT = 0b100; const SHIFT = 0b100;
@@ -1718,17 +1703,14 @@ impl ModifiersState {
pub fn shift_key(&self) -> bool { pub fn shift_key(&self) -> bool {
self.intersects(Self::SHIFT) self.intersects(Self::SHIFT)
} }
/// Returns `true` if the control key is pressed. /// Returns `true` if the control key is pressed.
pub fn control_key(&self) -> bool { pub fn control_key(&self) -> bool {
self.intersects(Self::CONTROL) self.intersects(Self::CONTROL)
} }
/// Returns `true` if the alt key is pressed. /// Returns `true` if the alt key is pressed.
pub fn alt_key(&self) -> bool { pub fn alt_key(&self) -> bool {
self.intersects(Self::ALT) self.intersects(Self::ALT)
} }
/// Returns `true` if the super key is pressed. /// Returns `true` if the super key is pressed.
pub fn super_key(&self) -> bool { pub fn super_key(&self) -> bool {
self.intersects(Self::SUPER) self.intersects(Self::SUPER)
@@ -1736,8 +1718,7 @@ impl ModifiersState {
} }
/// The state of the particular modifiers key. /// The state of the particular modifiers key.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ModifiersKeyState { pub enum ModifiersKeyState {
/// The particular key is pressed. /// The particular key is pressed.
Pressed, Pressed,
@@ -1756,7 +1737,6 @@ pub enum ModifiersKeyState {
// on macOS due to their AltGr/Option situation. // on macOS due to their AltGr/Option situation.
bitflags! { bitflags! {
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub(crate) struct ModifiersKeys: u8 { pub(crate) struct ModifiersKeys: u8 {
const LSHIFT = 0b0000_0001; const LSHIFT = 0b0000_0001;
const RSHIFT = 0b0000_0010; const RSHIFT = 0b0000_0010;
@@ -1768,3 +1748,54 @@ bitflags! {
const RSUPER = 0b1000_0000; const RSUPER = 0b1000_0000;
} }
} }
#[cfg(feature = "serde")]
mod modifiers_serde {
use super::ModifiersState;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Default, Serialize, Deserialize)]
#[serde(default)]
#[serde(rename = "ModifiersState")]
pub struct ModifiersStateSerialize {
pub shift_key: bool,
pub control_key: bool,
pub alt_key: bool,
pub super_key: bool,
}
impl Serialize for ModifiersState {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = ModifiersStateSerialize {
shift_key: self.shift_key(),
control_key: self.control_key(),
alt_key: self.alt_key(),
super_key: self.super_key(),
};
s.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ModifiersState {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let ModifiersStateSerialize {
shift_key,
control_key,
alt_key,
super_key,
} = ModifiersStateSerialize::deserialize(deserializer)?;
let mut m = ModifiersState::empty();
m.set(ModifiersState::SHIFT, shift_key);
m.set(ModifiersState::CONTROL, control_key);
m.set(ModifiersState::ALT, alt_key);
m.set(ModifiersState::SUPER, super_key);
Ok(m)
}
}
}

View File

@@ -1,91 +1,65 @@
//! Winit is a cross-platform window creation and event loop management library. //! Winit is a cross-platform window creation and event loop management library.
//! //!
//! # Building windows
//!
//! Before you can create a [`Window`], you first need to build an [`EventLoop`]. This is done with
//! the [`EventLoop::new()`] function.
//!
//! ```no_run
//! use winit::event_loop::EventLoop;
//! let event_loop = EventLoop::new().unwrap();
//! ```
//!
//! Then you create a [`Window`] with [`create_window`].
//!
//! # Event handling //! # Event handling
//! //!
//! Basically all of the functionality that Winit exposes requires an [`ActiveEventLoop`], which you
//! can get access to by running an [`EventLoop`] using [`EventLoop::new()`] and
//! [`EventLoop::run()`].
//!
//! Once it's running, you can create your [`Window`]s with [`ActiveEventLoop::create_window()`] by
//! passing in the desired [`WindowAttributes`].
//!
//! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can //! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can
//! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the //! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the
//! window or a key getting pressed while the window is focused. Devices can generate //! window or a key getting pressed while the window is focused. Devices can generate
//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window. //! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window.
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a //! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`]. //! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if desired.
//! //!
//! You retrieve by implementing [`ApplicationHandler`] for a new type, which will be the state of //! You can retrieve events by calling [`EventLoop::run()`]. This function will
//! your application. The methods in this trait will continuously receive events until //! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
//! [`ActiveEventLoop::exit()`] is used, at which point your application state will be dropped, and //! will run until [`exit()`] is used, at which point [`Event::LoopExiting`].
//! the application shuts down.
//! //!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop //! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
//! model, since that can't be implemented properly on some platforms (e.g Web, iOS) and works //! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on
//! poorly on most other platforms. However, this model can be re-implemented to an extent with //! most other platforms. However, this model can be re-implemented to an extent with
#![cfg_attr( #![cfg_attr(
any(windows_platform, macos_platform, android_platform, x11_platform, wayland_platform), any(
doc = "[`EventLoopExtPumpEvents::pump_app_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_app_events()]" windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
),
doc = "[`EventLoopExtPumpEvents::pump_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_events()]"
)] )]
#![cfg_attr( #![cfg_attr(
not(any(windows_platform, macos_platform, android_platform, x11_platform, wayland_platform)), not(any(
doc = "`EventLoopExtPumpEvents::pump_app_events()`" windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
)),
doc = "`EventLoopExtPumpEvents::pump_events()`"
)] )]
//! [^1]. See that method's documentation for more reasons about why //! [^1]. See that method's documentation for more reasons about why
//! it's discouraged beyond compatibility reasons. //! it's discouraged beyond compatibility reasons.
//! //!
//! //!
//! ```no_run //! ```no_run
//! use winit::application::ApplicationHandler; //! use winit::{
//! use winit::event::WindowEvent; //! event::{Event, WindowEvent},
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; //! event_loop::{ControlFlow, EventLoop},
//! use winit::window::{Window, WindowId}; //! window::Window,
//! };
//! //!
//! struct App { //! let event_loop = EventLoop::new().unwrap();
//! window: Box<dyn Window>,
//! }
//!
//! impl ApplicationHandler for App {
//! fn window_event(
//! &mut self,
//! event_loop: &dyn ActiveEventLoop,
//! id: WindowId,
//! event: WindowEvent,
//! ) {
//! // Called by `EventLoop::run` when a new event happens on the window.
//! match event {
//! WindowEvent::CloseRequested => {
//! println!("The close button was pressed; stopping");
//! event_loop.exit();
//! },
//! WindowEvent::RedrawRequested => {
//! // Redraw the application.
//! //
//! // It's preferable for applications that do not render continuously to render in
//! // this event rather than in AboutToWait, since rendering in here allows
//! // the program to gracefully handle redraws requested by the OS.
//!
//! // Draw.
//!
//! // Queue a RedrawRequested event.
//! //
//! // You only need to call this if you've determined that you need to redraw in
//! // applications which do not always need to. Applications that redraw continuously
//! // can render here instead.
//! self.window.request_redraw();
//! },
//! _ => (),
//! }
//! }
//! }
//!
//! # // Intentionally use `fn main` for clarity
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Create a new event loop.
//! let event_loop = EventLoop::new()?;
//!
//! // Configure settings before launching.
//! //!
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't //! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications. //! // dispatched any events. This is ideal for games and similar applications.
@@ -96,22 +70,43 @@
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll. //! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! event_loop.set_control_flow(ControlFlow::Wait); //! event_loop.set_control_flow(ControlFlow::Wait);
//! //!
//! // Launch and begin running our event loop. //! let mut window = None;
//! event_loop.run(|event_loop| {
//! // The event loop has launched, and we can initialize our UI state in this closure.
//! //!
//! // Create a simple window with default attributes. //! event_loop.run(move |event, event_loop| {
//! let window = event_loop //! match event {
//! .create_window(Window::default_attributes()) //! Event::Resumed => {
//! .expect("failed creating window"); //! window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
//!
//! // Give our newly created application state to Winit, which will, when necessary, call
//! // the `ApplicationHandler` methods configured above.
//! App { window }
//! })?;
//!
//! Ok(())
//! } //! }
//! Event::WindowEvent {
//! event: WindowEvent::CloseRequested,
//! ..
//! } => {
//! println!("The close button was pressed; stopping");
//! event_loop.exit();
//! },
//! Event::AboutToWait => {
//! // Application update code.
//!
//! // Queue a RedrawRequested event.
//! //
//! // You only need to call this if you've determined that you need to redraw in
//! // applications which do not always need to. Applications that redraw continuously
//! // can render here instead.
//! window.as_ref().unwrap().request_redraw();
//! },
//! Event::WindowEvent {
//! event: WindowEvent::RedrawRequested,
//! ..
//! } => {
//! // Redraw the application.
//! //
//! // It's preferable for applications that do not render continuously to render in
//! // this event rather than in AboutToWait, since rendering in here allows
//! // the program to gracefully handle redraws requested by the OS.
//! },
//! _ => ()
//! }
//! });
//! ``` //! ```
//! //!
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be //! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
@@ -120,55 +115,16 @@
//! //!
//! # Drawing on the window //! # Drawing on the window
//! //!
//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However, it allows you //! Winit doesn't directly provide any methods for drawing on a [`Window`]. However, it allows you to
//! to retrieve the raw handle of the window and display (see the [`platform`] module and/or the //! retrieve the raw handle of the window and display (see the [`platform`] module and/or the
//! [`raw_window_handle`] and [`raw_display_handle`] methods), which in turn allows //! [`raw_window_handle`] and [`raw_display_handle`] methods), which in turn allows
//! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics. //! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
//! //!
//! Note that many platforms will display garbage data in the window's client area if the //! Note that many platforms will display garbage data in the window's client area if the
//! application doesn't render anything to the window by the time the desktop compositor is ready to //! application doesn't render anything to the window by the time the desktop compositor is ready to
//! display the window to the user. If you notice this happening, you should create the window with //! display the window to the user. If you notice this happening, you should create the window with
//! [`visible` set to `false`][crate::window::WindowAttributes::with_visible] and explicitly make //! [`visible` set to `false`](crate::window::WindowAttributes::with_visible) and explicitly make the
//! the window visible only once you're ready to render into it. //! window visible only once you're ready to render into it.
//!
//! There is another important concept you need to know about when drawing: the "safe area". This
//! can be accessed with [`Window::safe_area`], and describes a rectangle in the surface that is not
//! obscured by notches, the status bar, and so on. You should be drawing your background and
//! non-important content on the entire surface, but restrict important content (such as
//! interactable UIs, text, etc.) to only being drawn inside the safe area.
//!
//! [`Window::safe_area`]: crate::window::Window::safe_area
//!
//! # Coordinate systems
//!
//! Windowing systems use many different coordinate systems, and this is reflected in Winit as well;
//! there are "desktop coordinates", which is the coordinates of a window or monitor relative to the
//! desktop at large, "window coordinates" which is the coordinates of the surface, relative to the
//! window, and finally "surface coordinates", which is the coordinates relative to the drawn
//! surface. All of these coordinates are relative to the top-left corner of their respective
//! origin.
//!
//! Most of the functionality in Winit works with surface coordinates, so usually you only need to
//! concern yourself with those. In case you need to convert to some other coordinate system, Winit
//! provides [`Window::surface_position`] and [`Window::surface_size`] to describe the surface's
//! location in window coordinates, and Winit provides [`Window::outer_position`] and
//! [`Window::outer_size`] to describe the window's location in desktop coordinates. Using these
//! methods, you should be able to convert a position in one coordinate system to another.
//!
//! An overview of how these four methods fit together can be seen in the image below:
#![doc = concat!("\n\n", include_str!("../docs/res/coordinate-systems-desktop.svg"), "\n\n")] // Rustfmt removes \n, adding them like this works around that.
//! On mobile, the situation is usually a bit different; because of the smaller screen space,
//! windows usually fill the whole screen at a time, and as such there is _rarely_ a difference
//! between these three coordinate systems, although you should still strive to handle this, as
//! they're still relevant in more niche area such as Mac Catalyst, or multi-tasking on tablets.
//!
//! This is illustrated in the image below, along with the safe area since it's often relevant on
//! mobile.
#![doc = concat!("\n\n", include_str!("../docs/res/coordinate-systems-mobile.svg"), "\n\n")] // Rustfmt removes \n, adding them like this works around that.
//! [`Window::surface_position`]: crate::window::Window::surface_position
//! [`Window::surface_size`]: crate::window::Window::surface_size
//! [`Window::outer_position`]: crate::window::Window::outer_position
//! [`Window::outer_size`]: crate::window::Window::outer_size
//! //!
//! # UI scaling //! # UI scaling
//! //!
@@ -194,108 +150,56 @@
//! Winit provides the following Cargo features: //! Winit provides the following Cargo features:
//! //!
//! * `x11` (enabled by default): On Unix platforms, enables the X11 backend. //! * `x11` (enabled by default): On Unix platforms, enables the X11 backend.
//! * `wayland` (enabled by default): On Unix platforms, enables the Wayland backend. //! * `wayland` (enabled by default): On Unix platforms, enables the Wayland
//! backend.
//! * `rwh_04`: Implement `raw-window-handle v0.4` traits.
//! * `rwh_05`: Implement `raw-window-handle v0.5` traits.
//! * `rwh_06`: Implement `raw-window-handle v0.6` traits. //! * `rwh_06`: Implement `raw-window-handle v0.6` traits.
//! * `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde). //! * `serde`: Enables serialization/deserialization of certain types with
//! [Serde](https://crates.io/crates/serde).
//! * `mint`: Enables mint (math interoperability standard types) conversions. //! * `mint`: Enables mint (math interoperability standard types) conversions.
//! //!
//! See the [`platform`] module for documentation on platform-specific cargo //! See the [`platform`] module for documentation on platform-specific cargo
//! features. //! features.
//! //!
//! # Platform/Architecture Support
//!
//! Platform support on `winit` has two tiers: Tier 1 and Tier 2.
//!
//! - Tier 1 is **guaranteed to work**. Targets in this tier are actively tested both in CI and by
//! maintainers.
//! - Tier 2 is **guaranteed to build**. Code compilation is tested in CI, but deeper testing is not
//! done.
//!
//! Please open an issue if you would like to add a Tier 2 target, or if you would
//! like a Tier 2 target moved to Tier 1.
//!
//! ## Tier 1 Targets
//!
//! |Target Name |Target Triple |APIs |
//! |-------------------------------|------------------------------------|---------------|
//! |32-Bit x86 Windows with MSVC |`i686-pc-windows-msvc` |Win32 |
//! |64-Bit x86 Windows with MSVC |`x86_64-pc-windows-msvc` |Win32 |
//! |32-Bit x86 Windows with glibc |`i686-pc-windows-gnu` |Win32 |
//! |64-Bit x86 Windows with glibc |`x86_64-pc-windows-gnu` |Win32 |
//! |32-Bit x86 Linux with glibc |`i686-unknown-linux-gnu` |X11, Wayland |
//! |64-Bit x86 Linux with glibc |`x86_64-unknown-linux-gnu` |X11, Wayland |
//! |64-Bit ARM Android |`aarch64-linux-android` |Android |
//! |64-Bit x86 Redox OS |`x86_64-unknown-redox` |Orbital |
//! |32-Bit x86 Redox OS |`i686-unknown-redox` |Orbital |
//! |64-Bit ARM Redox OS |`aarch64-unknown-redox` |Orbital |
//! |64-bit x64 macOS |`x86_64-apple-darwin` |AppKit |
//! |64-bit ARM macOS |`aarch64-apple-darwin` |AppKit |
//! |32-bit Wasm Web browser |`wasm32-unknown-unknown` |`wasm-bindgen` |
//!
//! ## Tier 2 Targets
//!
//! |Target Name |Target Triple |APIs |
//! |------------------------------------|------------------------------------|---------------|
//! |64-Bit ARM Windows with MSVC |`aarch64-pc-windows-msvc` |Win32 |
//! |32-Bit x86 Windows 7 with MSVC |`i686-win7-windows-msvc` |Win32 |
//! |64-Bit x86 Windows 7 with MSVC |`x86_64-win7-windows-msvc` |Win32 |
//! |64-bit x86 Linux with Musl |`x86_64-unknown-linux-musl` |X11, Wayland |
//! |64-bit x86 Linux with 32-bit glibc |`x86_64-unknown-linux-gnux32` |X11, Wayland |
//! |64-bit x86 Android |`x86_64-linux-android` |Android |
//! |64-bit x64 iOS |`x86_64-apple-ios` |UIKit |
//! |64-bit ARM iOS |`aarch64-apple-ios` |UIKit |
//! |64-bit ARM Mac Catalyst |`aarch64-apple-ios-macabi` |UIKit |
//! |32-bit x86 Android |`i686-linux-android` |Android |
//! |64-bit x86 FreeBSD |`x86_64-unknown-freebsd` |X11, Wayland |
//! |64-bit x86 NetBSD |`x86_64-unknown-netbsd` |X11 |
//! |32-bit x86 Linux with Musl |`i686-unknown-linux-musl` |X11, Wayland |
//! |64-bit RISC-V Linux with glibc |`riscv64gc-unknown-linux-gnu` |X11, Wayland |
//! |64-bit ARM Linux with glibc |`aarch64-unknown-linux-gnu` |X11, Wayland |
//! |64-bit ARM Linux with Musl |`aarch64-unknown-linux-musl` |X11, Wayland |
//! |64-bit PowerPC Linux with glibc |`powerpc64le-unknown-linux-gnu` |X11, Wayland |
//! |32-Bit ARM Linux with glibc |`armv5te-unknown-linux-gnueabi` |X11, Wayland |
//! |64-Bit Linux on IBM Supercomputers |`s390x-unknown-linux-gnu` |X11, Wayland |
//! |32-bit ARM Android |`arm-linux-androideabi` |Android |
//! |64-bit SPARC Linux with glibc |`sparc64-unknown-linux-gnu` |X11, Wayland |
//!
//! [`ActiveEventLoop`]: event_loop::ActiveEventLoop
//! [`EventLoop`]: event_loop::EventLoop //! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoop::new()`]: event_loop::EventLoop::new //! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [`EventLoop::run()`]: event_loop::EventLoop::run //! [`EventLoop::run()`]: event_loop::EventLoop::run
//! [`ActiveEventLoop::exit()`]: event_loop::ActiveEventLoop::exit //! [`exit()`]: event_loop::ActiveEventLoop::exit
//! [`Window`]: window::Window //! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId //! [`WindowId`]: window::WindowId
//! [`WindowAttributes`]: window::WindowAttributes //! [`WindowAttributes`]: window::WindowAttributes
//! [window_new]: window::Window::new //! [window_new]: window::Window::new
//! [`ActiveEventLoop::create_window()`]: event_loop::ActiveEventLoop::create_window //! [`create_window`]: event_loop::ActiveEventLoop::create_window
//! [`Window::id()`]: window::Window::id //! [`Window::id()`]: window::Window::id
//! [`WindowEvent`]: event::WindowEvent //! [`WindowEvent`]: event::WindowEvent
//! [`DeviceEvent`]: event::DeviceEvent //! [`DeviceEvent`]: event::DeviceEvent
//! [`ApplicationHandler`]: application::ApplicationHandler
//! [`Event::UserEvent`]: event::Event::UserEvent //! [`Event::UserEvent`]: event::Event::UserEvent
//! [`Event::LoopExiting`]: event::Event::LoopExiting
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle //! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle //! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland. //! [^1]: `EventLoopExtPumpEvents::pump_events()` is only available on Windows, macOS, Android, X11 and Wayland.
#![deny(rust_2018_idioms)] #![deny(rust_2018_idioms)]
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
#![deny(clippy::all)] #![deny(clippy::all)]
#![deny(unsafe_op_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)]
#![cfg_attr(clippy, deny(warnings))] #![cfg_attr(clippy, deny(warnings))]
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly // Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
// doc #![cfg_attr(
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))] docsrs,
feature(doc_auto_cfg, doc_cfg_hide),
doc(cfg_hide(doc, docsrs))
)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#![warn(clippy::uninlined_format_args)]
#[cfg(feature = "rwh_06")]
pub use rwh_06 as raw_window_handle;
// Re-export DPI types so that users don't have to put it in Cargo.toml. // Re-export DPI types so that users don't have to put it in Cargo.toml.
#[doc(inline)] #[doc(inline)]
pub use dpi; pub use dpi;
pub use rwh_06 as raw_window_handle;
pub mod application;
#[cfg(any(doc, doctest, test))]
pub mod changelog;
#[macro_use] #[macro_use]
pub mod error; pub mod error;
mod cursor; mod cursor;

View File

@@ -3,12 +3,16 @@
//! If you want to get basic information about a monitor, you can use the //! If you want to get basic information about a monitor, you can use the
//! [`MonitorHandle`] type. This is retrieved from one of the following //! [`MonitorHandle`] type. This is retrieved from one of the following
//! methods, which return an iterator of [`MonitorHandle`]: //! methods, which return an iterator of [`MonitorHandle`]:
//! - [`ActiveEventLoop::available_monitors`][crate::event_loop::ActiveEventLoop::available_monitors]. //! - [`ActiveEventLoop::available_monitors`](crate::event_loop::ActiveEventLoop::available_monitors).
//! - [`Window::available_monitors`][crate::window::Window::available_monitors]. //! - [`Window::available_monitors`](crate::window::Window::available_monitors).
use std::num::{NonZeroU16, NonZeroU32}; use crate::{
dpi::{PhysicalPosition, PhysicalSize},
platform_impl,
};
use crate::dpi::{PhysicalPosition, PhysicalSize}; /// Deprecated! Use `VideoModeHandle` instead.
use crate::platform_impl; #[deprecated = "Renamed to `VideoModeHandle`"]
pub type VideoMode = VideoModeHandle;
/// Describes a fullscreen video mode of a monitor. /// Describes a fullscreen video mode of a monitor.
/// ///
@@ -46,10 +50,7 @@ impl Ord for VideoModeHandle {
} }
impl VideoModeHandle { impl VideoModeHandle {
/// Returns the resolution of this video mode. This **must not** be used to create your /// Returns the resolution of this video mode.
/// rendering surface. Use [`Window::surface_size()`] instead.
///
/// [`Window::surface_size()`]: crate::window::Window::surface_size
#[inline] #[inline]
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
self.video_mode.size() self.video_mode.size()
@@ -58,14 +59,19 @@ impl VideoModeHandle {
/// Returns the bit depth of this video mode, as in how many bits you have /// Returns the bit depth of this video mode, as in how many bits you have
/// available per color. This is generally 24 bits or 32 bits on modern /// available per color. This is generally 24 bits or 32 bits on modern
/// systems, depending on whether the alpha channel is counted or not. /// systems, depending on whether the alpha channel is counted or not.
///
/// ## Platform-specific
///
/// - **Wayland / Orbital:** Always returns 32.
/// - **iOS:** Always returns 32.
#[inline] #[inline]
pub fn bit_depth(&self) -> Option<NonZeroU16> { pub fn bit_depth(&self) -> u16 {
self.video_mode.bit_depth() self.video_mode.bit_depth()
} }
/// Returns the refresh rate of this video mode in mHz. /// Returns the refresh rate of this video mode in mHz.
#[inline] #[inline]
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> { pub fn refresh_rate_millihertz(&self) -> u32 {
self.video_mode.refresh_rate_millihertz() self.video_mode.refresh_rate_millihertz()
} }
@@ -73,7 +79,9 @@ impl VideoModeHandle {
/// a separate set of valid video modes. /// a separate set of valid video modes.
#[inline] #[inline]
pub fn monitor(&self) -> MonitorHandle { pub fn monitor(&self) -> MonitorHandle {
MonitorHandle { inner: self.video_mode.monitor() } MonitorHandle {
inner: self.video_mode.monitor(),
}
} }
} }
@@ -81,11 +89,11 @@ impl std::fmt::Display for VideoModeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
"{}x{} {}{}", "{}x{} @ {} mHz ({} bpp)",
self.size().width, self.size().width,
self.size().height, self.size().height,
self.refresh_rate_millihertz().map(|rate| format!("@ {rate} mHz ")).unwrap_or_default(), self.refresh_rate_millihertz(),
self.bit_depth().map(|bit_depth| format!("({bit_depth} bpp)")).unwrap_or_default(), self.bit_depth()
) )
} }
} }
@@ -94,72 +102,46 @@ impl std::fmt::Display for VideoModeHandle {
/// ///
/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation. /// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
/// ///
/// ## Platform-specific
///
/// **Web:** A [`MonitorHandle`] created without
#[cfg_attr(
any(web_platform, docsrs),
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
/// will always represent the current monitor the browser window is in instead of a specific
/// monitor. See
#[cfg_attr(
any(web_platform, docsrs),
doc = "[`MonitorHandleExtWeb::is_detailed()`][crate::platform::web::MonitorHandleExtWeb::is_detailed]"
)]
#[cfg_attr(not(any(web_platform, docsrs)), doc = "`MonitorHandleExtWeb::is_detailed()`")]
/// to check.
///
/// [`Window`]: crate::window::Window /// [`Window`]: crate::window::Window
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct MonitorHandle { pub struct MonitorHandle {
pub(crate) inner: platform_impl::MonitorHandle, pub(crate) inner: platform_impl::MonitorHandle,
} }
impl std::fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl MonitorHandle { impl MonitorHandle {
/// Returns a human-readable name of the monitor. /// Returns a human-readable name of the monitor.
/// ///
/// Returns `None` if the monitor doesn't exist anymore. /// Returns `None` if the monitor doesn't exist anymore.
///
/// ## Platform-specific
///
/// **Web:** Always returns [`None`] without
#[cfg_attr(
any(web_platform, docsrs),
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
#[inline] #[inline]
pub fn name(&self) -> Option<String> { pub fn name(&self) -> Option<String> {
self.inner.name() self.inner.name()
} }
/// Returns the top-left corner position of the monitor in desktop coordinates. /// Returns the monitor's resolution.
///
/// This position is in the same coordinate system as [`Window::outer_position`].
///
/// [`Window::outer_position`]: crate::window::Window::outer_position
///
/// ## Platform-specific
///
/// **Web:** Always returns [`None`] without
#[cfg_attr(
any(web_platform, docsrs),
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
#[inline] #[inline]
pub fn position(&self) -> Option<PhysicalPosition<i32>> { pub fn size(&self) -> PhysicalSize<u32> {
self.inner.size()
}
/// Returns the top-left corner position of the monitor relative to the larger full
/// screen area.
#[inline]
pub fn position(&self) -> PhysicalPosition<i32> {
self.inner.position() self.inner.position()
} }
/// The monitor refresh rate used by the system.
///
/// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor
/// the window is on is removed.
///
/// When using exclusive fullscreen, the refresh rate of the [`VideoModeHandle`] that was
/// used to enter fullscreen should be used instead.
#[inline]
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
self.inner.refresh_rate_millihertz()
}
/// Returns the scale factor of the underlying monitor. To map logical pixels to physical /// Returns the scale factor of the underlying monitor. To map logical pixels to physical
/// pixels and vice versa, use [`Window::scale_factor`]. /// pixels and vice versa, use [`Window::scale_factor`].
/// ///
@@ -170,29 +152,22 @@ impl MonitorHandle {
/// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable. /// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
/// - **Wayland:** May differ from [`Window::scale_factor`]. /// - **Wayland:** May differ from [`Window::scale_factor`].
/// - **Android:** Always returns 1.0. /// - **Android:** Always returns 1.0.
/// - **Web:** Always returns `0.0` without
#[cfg_attr(
any(web_platform, docsrs),
doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(any(web_platform, docsrs)), doc = " detailed monitor permissions.")]
/// ///
#[rustfmt::skip]
/// [`Window::scale_factor`]: crate::window::Window::scale_factor /// [`Window::scale_factor`]: crate::window::Window::scale_factor
#[inline] #[inline]
pub fn scale_factor(&self) -> f64 { pub fn scale_factor(&self) -> f64 {
self.inner.scale_factor() self.inner.scale_factor()
} }
/// Returns the currently active video mode of this monitor.
#[inline]
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
self.inner.current_video_mode().map(|video_mode| VideoModeHandle { video_mode })
}
/// Returns all fullscreen video modes supported by this monitor. /// Returns all fullscreen video modes supported by this monitor.
///
/// ## Platform-specific
///
/// - **Web:** Always returns an empty iterator
#[inline] #[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
self.inner.video_modes().map(|video_mode| VideoModeHandle { video_mode }) self.inner
.video_modes()
.map(|video_mode| VideoModeHandle { video_mode })
} }
} }

View File

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

View File

@@ -1,79 +1,51 @@
//! # iOS / UIKit //! # iOS / UIKit
//! //!
//! Winit has [the same iOS version requirements as `rustc`][rustc-ios-version], although it's //! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on
//! frequently only tested on newer iOS versions. //! iOS 9.3.
//! //!
//! [rustc-ios-version]: https://doc.rust-lang.org/rustc/platform-support/apple-ios.html#os-version //! iOS's main `UIApplicationMain` does some init work that's required by all
//! UI-related code (see issue [#1705]). It is best to create your windows
//! inside `Event::Resumed`.
//! //!
//! ## Running on Mac Catalyst //! [#1705]: https://github.com/rust-windowing/winit/issues/1705
//! //!
//! Mac Catalyst allows running applications using UIKit on macOS, which can be very useful for //! ## Building app
//! testing. See [`rustc`'s documentation on Mac Catalyst][rustc-mac-catalyst] for details on how to
//! use these targets. To use these with Winit, you'll need to bundle your application before
//! running it, otherwise UIKit will exit with an error.
//! //!
//! To run e.g. the `window` example in the Winit repository, you can use [`cargo-bundle`] as //! To build ios app you will need rustc built for this targets:
//! follows:
//! //!
//! ```console //! - armv7-apple-ios
//! $ cargo +nightly bundle --format=ios --target=aarch64-apple-ios-macabi --example=window //! - armv7s-apple-ios
//! $ ./target/aarch64-apple-ios-macabi/debug/examples/bundle/ios/winit.app/window //! - i386-apple-ios
//! - aarch64-apple-ios
//! - x86_64-apple-ios
//!
//! Then
//!
//! ```
//! cargo build --target=...
//! ```
//! The simplest way to integrate your app into xcode environment is to build it
//! as a static library. Wrap your main function and export it.
//!
//! ```rust, ignore
//! #[no_mangle]
//! pub extern fn start_winit_app() {
//! start_inner()
//! }
//!
//! fn start_inner() {
//! ...
//! }
//! ``` //! ```
//! //!
//! [rustc-mac-catalyst]: https://doc.rust-lang.org/rustc/platform-support/apple-ios-macabi.html //! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
//! [`cargo-bundle`]: https://github.com/burtonageo/cargo-bundle
//! //!
//! ## Introduction to building an app //! ```ignore
//! //! void start_winit_app();
//! Building and running your application in the iOS simulator, or on a real device, is a bit more
//! complicated than Mac Catalyst - fundamentally, you must use Xcode, since the binary needs to be
//! bundled, signed, notarized and uploaded to the device (there is [an open source work-in-progress
//! on re-implementing parts of this][apple-platform-rs], but the user-story around it is not yet
//! clear).
//!
//! This means that you're left with effectively two options: Use a tool that manages the Xcode
//! configuration for you, or use Xcode directly. [`cargo-dinghy`] and [`cargo-mobile2`] are notable
//! projects in the ecosystem that attempt the former, and [`cargo-xcode`] is an excellent project
//! that attempts the latter. We will also attempt to describe here how you would go about using
//! Xcode directly:
//!
//! First off, you'll need the correct Rust targets, see [`rustc`'s documentation on iOS][rustc-ios]
//! for details. Nowadays, the correct targets are usually `aarch64-apple-ios-sim` for the
//! simulator, and `aarch64-apple-ios` for the actual device.
//!
//! Next, create a new Xcode project using the "App" template. The exact configuration does not
//! really matter, as we're going to delete most of it, since it's tailored for Objective-C and/or
//! Swift, and Rust/Winit is neither. Specifically, we need to delete:
//! - Everything relating to storyboards (unless you want to use e.g. a launch screen). This
//! includes the relevant keys in `Info.plist`.
//! - All the generated C header, Objective-C and/or Swift files.
//!
//! Now that we have a fairly clean slate that we can build upon, you can add a "run script" build
//! phase to your Xcode target, which will get invoked instead of the "compile sources" and "link
//! binary" steps. The basic script should look something like:
//!
//! ```sh
//! # Build desired targets based on `ARCHS` environment variable
//! cargo build --target=aarch64-apple-ios --target=armv7s-apple-ios
//! # Merge these with `lipo`, and place the result in "$TARGET_BUILD_DIR/$EXECUTABLE_PATH", which
//! # is understood by Xcode
//! lipo "$TARGET_BUILD_DIR/$EXECUTABLE_PATH" target/aarch64-apple-ios/debug/my_app target/armv7s-apple-ios/debug/my_app
//! ``` //! ```
//! //!
//! Note that this is very much the overall idea; the script needs to be much more involved to //! Use start_winit_app inside your xcode's main function.
//! properly deal with different target architectures, invoking `lipo` when needed, incremental
//! rebuild change detection, and so on. `cargo-xcode` has a script [here][cargo-xcode-script] that
//! handles most of this complexity, you might be able to build upon that.
//! //!
//! Apologies that we're not able to provide you with more than this; work is in-progress on
//! improving the situation, but it's slow-going.
//!
//! [apple-platform-rs]: https://github.com/indygreg/apple-platform-rs
//! [`cargo-dinghy`]: https://github.com/sonos/dinghy
//! [`cargo-mobile2`]: https://github.com/tauri-apps/cargo-mobile2
//! [`cargo-xcode`]: https://crates.io/crates/cargo-xcode
//! [rustc-ios]: https://doc.rust-lang.org/rustc/platform-support/apple-ios.html
//! [cargo-xcode-script]: https://gitlab.com/kornelski/cargo-xcode/-/blob/main/src/xcodebuild.sh
//! //!
//! ## App lifecycle and events //! ## App lifecycle and events
//! //!
@@ -91,24 +63,26 @@
//! opengl will result in segfault. //! opengl will result in segfault.
//! //!
//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed. //! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
//!
//! ## Custom `UIApplicationDelegate`
//!
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
//! though, you might want to access some of the more niche stuff that [the application
//! delegate][app-delegate] provides. This functionality is not exposed directly in Winit, since it
//! would increase the API surface by quite a lot. Instead, Winit guarantees that it will not
//! register an application delegate, so you can set up a custom one in a nib file instead.
//!
//! [app-delegate]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate?language=objc
use std::os::raw::c_void; use std::os::raw::c_void;
#[cfg(feature = "serde")] use crate::{
use serde::{Deserialize, Serialize}; event_loop::EventLoop,
monitor::{MonitorHandle, VideoModeHandle},
window::{Window, WindowAttributes},
};
use crate::monitor::{MonitorHandle, VideoModeHandle}; /// Additional methods on [`EventLoop`] that are specific to iOS.
use crate::window::{Window, WindowAttributes}; pub trait EventLoopExtIOS {
/// Returns the [`Idiom`] (phone/tablet/tv/etc) for the current device.
fn idiom(&self) -> Idiom;
}
impl<T: 'static> EventLoopExtIOS for EventLoop<T> {
fn idiom(&self) -> Idiom {
self.event_loop.idiom()
}
}
/// Additional methods on [`Window`] that are specific to iOS. /// Additional methods on [`Window`] that are specific to iOS.
pub trait WindowExtIOS { pub trait WindowExtIOS {
@@ -183,21 +157,6 @@ pub trait WindowExtIOS {
/// The default is to not recognize gestures. /// The default is to not recognize gestures.
fn recognize_pinch_gesture(&self, should_recognize: bool); fn recognize_pinch_gesture(&self, should_recognize: bool);
/// Sets whether the [`Window`] should recognize pan gestures.
///
/// The default is to not recognize gestures.
/// Installs [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer) onto view
///
/// Set the minimum number of touches required: [`minimumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-minimumnumberoftouches)
///
/// Set the maximum number of touches recognized: [`maximumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-maximumnumberoftouches)
fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
);
/// Sets whether the [`Window`] should recognize double tap gestures. /// Sets whether the [`Window`] should recognize double tap gestures.
/// ///
/// The default is to not recognize gestures. /// The default is to not recognize gestures.
@@ -209,78 +168,60 @@ pub trait WindowExtIOS {
fn recognize_rotation_gesture(&self, should_recognize: bool); fn recognize_rotation_gesture(&self, should_recognize: bool);
} }
impl WindowExtIOS for dyn Window + '_ { impl WindowExtIOS for Window {
#[inline] #[inline]
fn set_scale_factor(&self, scale_factor: f64) { fn set_scale_factor(&self, scale_factor: f64) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.set_scale_factor(scale_factor)); .maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor))
} }
#[inline] #[inline]
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) { fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.set_valid_orientations(valid_orientations)); .maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations))
} }
#[inline] #[inline]
fn set_prefers_home_indicator_hidden(&self, hidden: bool) { fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden)); .maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden))
} }
#[inline] #[inline]
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) { fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.maybe_queue_on_main(move |w| {
window.maybe_wait_on_main(move |w| {
w.set_preferred_screen_edges_deferring_system_gestures(edges) w.set_preferred_screen_edges_deferring_system_gestures(edges)
}); })
} }
#[inline] #[inline]
fn set_prefers_status_bar_hidden(&self, hidden: bool) { fn set_prefers_status_bar_hidden(&self, hidden: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.set_prefers_status_bar_hidden(hidden)); .maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
} }
#[inline] #[inline]
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) { fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style)) .maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
} }
#[inline] #[inline]
fn recognize_pinch_gesture(&self, should_recognize: bool) { fn recognize_pinch_gesture(&self, should_recognize: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.recognize_pinch_gesture(should_recognize)); .maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
}
#[inline]
fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| {
w.recognize_pan_gesture(
should_recognize,
minimum_number_of_touches,
maximum_number_of_touches,
)
});
} }
#[inline] #[inline]
fn recognize_doubletap_gesture(&self, should_recognize: bool) { fn recognize_doubletap_gesture(&self, should_recognize: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.recognize_doubletap_gesture(should_recognize)); .maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
} }
#[inline] #[inline]
fn recognize_rotation_gesture(&self, should_recognize: bool) { fn recognize_rotation_gesture(&self, should_recognize: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.recognize_rotation_gesture(should_recognize)); .maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
} }
} }
@@ -360,7 +301,8 @@ impl WindowAttributesExtIOS for WindowAttributes {
#[inline] #[inline]
fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self { fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
self.platform_specific.preferred_screen_edges_deferring_system_gestures = edges; self.platform_specific
.preferred_screen_edges_deferring_system_gestures = edges;
self self
} }
@@ -394,19 +336,20 @@ impl MonitorHandleExtIOS for MonitorHandle {
#[inline] #[inline]
fn ui_screen(&self) -> *mut c_void { fn ui_screen(&self) -> *mut c_void {
// SAFETY: The marker is only used to get the pointer of the screen // SAFETY: The marker is only used to get the pointer of the screen
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() };
objc2::rc::Retained::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void objc2::rc::Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
} }
#[inline] #[inline]
fn preferred_video_mode(&self) -> VideoModeHandle { fn preferred_video_mode(&self) -> VideoModeHandle {
VideoModeHandle { video_mode: self.inner.preferred_video_mode() } VideoModeHandle {
video_mode: self.inner.preferred_video_mode(),
}
} }
} }
/// Valid orientations for a particular [`Window`]. /// Valid orientations for a particular [`Window`].
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ValidOrientations { pub enum ValidOrientations {
/// Excludes `PortraitUpsideDown` on iphone /// Excludes `PortraitUpsideDown` on iphone
#[default] #[default]
@@ -418,12 +361,29 @@ pub enum ValidOrientations {
Portrait, Portrait,
} }
/// The device [idiom].
///
/// [idiom]: https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom?language=objc
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Idiom {
Unspecified,
/// iPhone and iPod touch.
Phone,
/// iPad.
Pad,
/// tvOS and Apple TV.
TV,
CarPlay,
}
bitflags::bitflags! { bitflags::bitflags! {
/// The [edges] of a screen. /// The [edges] of a screen.
/// ///
/// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc /// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ScreenEdge: u8 { pub struct ScreenEdge: u8 {
const NONE = 0; const NONE = 0;
const TOP = 1 << 0; const TOP = 1 << 0;
@@ -435,8 +395,7 @@ bitflags::bitflags! {
} }
} }
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum StatusBarStyle { pub enum StatusBarStyle {
#[default] #[default]
Default, Default,

View File

@@ -1,84 +1,29 @@
//! # macOS / AppKit //! # macOS / AppKit
//! //!
//! Winit has [the same macOS version requirements as `rustc`][rustc-macos-version], and is tested //! Winit has an OS requirement of macOS 10.11 or higher (same as Rust
//! once in a while on as low as macOS 10.14. //! itself), and is regularly tested on macOS 10.14.
//! //!
//! [rustc-macos-version]: https://doc.rust-lang.org/rustc/platform-support/apple-darwin.html#os-version //! 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].
//! //!
//! ## Custom `NSApplicationDelegate` //! If you encounter problems, you should try doing your initialization inside
//! `Event::Resumed`.
//! //!
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes, //! [#2238]: https://github.com/rust-windowing/winit/issues/2238
//! though, you might want to do more niche stuff, such as [handle when the user re-activates the //! [#2051]: https://github.com/rust-windowing/winit/issues/2051
//! application][reopen]. Such functionality is not exposed directly in Winit, since it would //! [#2087]: https://github.com/rust-windowing/winit/issues/2087
//! increase the API surface by quite a lot.
//!
//! [reopen]: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc
//!
//! Instead, Winit guarantees that it will not register an application delegate, so the solution is
//! to register your own application delegate, as outlined in the following example (see
//! `objc2-app-kit` for more detailed information).
#![cfg_attr(target_os = "macos", doc = "```")]
#![cfg_attr(not(target_os = "macos"), doc = "```ignore")]
//! use objc2::rc::Retained;
//! use objc2::runtime::ProtocolObject;
//! use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
//! use objc2_app_kit::{NSApplication, NSApplicationDelegate};
//! use objc2_foundation::{NSArray, NSURL, MainThreadMarker, NSObject, NSObjectProtocol};
//! use winit::event_loop::EventLoop;
//!
//! declare_class!(
//! struct AppDelegate;
//!
//! unsafe impl ClassType for AppDelegate {
//! type Super = NSObject;
//! type Mutability = mutability::MainThreadOnly;
//! const NAME: &'static str = "MyAppDelegate";
//! }
//!
//! impl DeclaredClass for AppDelegate {}
//!
//! unsafe impl NSObjectProtocol for AppDelegate {}
//!
//! unsafe impl NSApplicationDelegate for AppDelegate {
//! #[method(application:openURLs:)]
//! fn application_openURLs(&self, application: &NSApplication, urls: &NSArray<NSURL>) {
//! // Note: To specifically get `application:openURLs:` to work, you _might_
//! // have to bundle your application. This is not done in this example.
//! println!("open urls: {application:?}, {urls:?}");
//! }
//! }
//! );
//!
//! impl AppDelegate {
//! fn new(mtm: MainThreadMarker) -> Retained<Self> {
//! unsafe { msg_send_id![super(mtm.alloc().set_ivars(())), init] }
//! }
//! }
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let event_loop = EventLoop::new()?;
//!
//! let mtm = MainThreadMarker::new().unwrap();
//! let delegate = AppDelegate::new(mtm);
//! // Important: Call `sharedApplication` after `EventLoop::new`,
//! // doing it before is not yet supported.
//! let app = NSApplication::sharedApplication(mtm);
//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
//!
//! // event_loop.run(|event_loop| { ... })?;
//! Ok(())
//! }
//! ```
use std::os::raw::c_void; use std::os::raw::c_void;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::application::ApplicationHandler; use crate::{
use crate::event_loop::{ActiveEventLoop, EventLoopBuilder}; event_loop::{ActiveEventLoop, EventLoopBuilder},
use crate::monitor::MonitorHandle; monitor::MonitorHandle,
use crate::window::{Window, WindowAttributes, WindowId}; window::{Window, WindowAttributes},
};
/// Additional methods on [`Window`] that are specific to MacOS. /// Additional methods on [`Window`] that are specific to MacOS.
pub trait WindowExtMacOS { pub trait WindowExtMacOS {
@@ -92,9 +37,6 @@ pub trait WindowExtMacOS {
/// This is how fullscreen used to work on macOS in versions before Lion. /// This is how fullscreen used to work on macOS in versions before Lion.
/// And allows the user to have a fullscreen window without using another /// And allows the user to have a fullscreen window without using another
/// space or taking control over the entire monitor. /// space or taking control over the entire monitor.
///
/// Make sure you only draw your important content inside the safe area so that it does not
/// overlap with the notch on newer devices, see [`Window::safe_area`] for details.
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool; fn set_simple_fullscreen(&self, fullscreen: bool) -> bool;
/// Returns whether or not the window has shadow. /// Returns whether or not the window has shadow.
@@ -154,134 +96,88 @@ pub trait WindowExtMacOS {
/// Getter for the [`WindowExtMacOS::set_option_as_alt`]. /// Getter for the [`WindowExtMacOS::set_option_as_alt`].
fn option_as_alt(&self) -> OptionAsAlt; fn option_as_alt(&self) -> OptionAsAlt;
/// Disable the Menu Bar and Dock in Borderless Fullscreen mode. Useful for games.
fn set_borderless_game(&self, borderless_game: bool);
/// Getter for the [`WindowExtMacOS::set_borderless_game`].
fn is_borderless_game(&self) -> bool;
/// Makes the titlebar bigger, effectively adding more space around the
/// window controls if the titlebar is invisible.
fn set_unified_titlebar(&self, unified_titlebar: bool);
/// Getter for the [`WindowExtMacOS::set_unified_titlebar`].
fn unified_titlebar(&self) -> bool;
} }
impl WindowExtMacOS for dyn Window + '_ { impl WindowExtMacOS for Window {
#[inline] #[inline]
fn simple_fullscreen(&self) -> bool { fn simple_fullscreen(&self) -> bool {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.maybe_wait_on_main(|w| w.simple_fullscreen())
window.maybe_wait_on_main(|w| w.simple_fullscreen())
} }
#[inline] #[inline]
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool { fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen)) .maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
} }
#[inline] #[inline]
fn has_shadow(&self) -> bool { fn has_shadow(&self) -> bool {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.maybe_wait_on_main(|w| w.has_shadow())
window.maybe_wait_on_main(|w| w.has_shadow())
} }
#[inline] #[inline]
fn set_has_shadow(&self, has_shadow: bool) { fn set_has_shadow(&self, has_shadow: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.set_has_shadow(has_shadow)); .maybe_queue_on_main(move |w| w.set_has_shadow(has_shadow))
} }
#[inline] #[inline]
fn set_tabbing_identifier(&self, identifier: &str) { fn set_tabbing_identifier(&self, identifier: &str) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier)) .maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
} }
#[inline] #[inline]
fn tabbing_identifier(&self) -> String { fn tabbing_identifier(&self) -> String {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.maybe_wait_on_main(|w| w.tabbing_identifier())
window.maybe_wait_on_main(|w| w.tabbing_identifier())
} }
#[inline] #[inline]
fn select_next_tab(&self) { fn select_next_tab(&self) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.maybe_queue_on_main(|w| w.select_next_tab())
window.maybe_wait_on_main(|w| w.select_next_tab());
} }
#[inline] #[inline]
fn select_previous_tab(&self) { fn select_previous_tab(&self) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.maybe_queue_on_main(|w| w.select_previous_tab())
window.maybe_wait_on_main(|w| w.select_previous_tab());
} }
#[inline] #[inline]
fn select_tab_at_index(&self, index: usize) { fn select_tab_at_index(&self, index: usize) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.select_tab_at_index(index)); .maybe_queue_on_main(move |w| w.select_tab_at_index(index))
} }
#[inline] #[inline]
fn num_tabs(&self) -> usize { fn num_tabs(&self) -> usize {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.maybe_wait_on_main(|w| w.num_tabs())
window.maybe_wait_on_main(|w| w.num_tabs())
} }
#[inline] #[inline]
fn is_document_edited(&self) -> bool { fn is_document_edited(&self) -> bool {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.maybe_wait_on_main(|w| w.is_document_edited())
window.maybe_wait_on_main(|w| w.is_document_edited())
} }
#[inline] #[inline]
fn set_document_edited(&self, edited: bool) { fn set_document_edited(&self, edited: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.set_document_edited(edited)); .maybe_queue_on_main(move |w| w.set_document_edited(edited))
} }
#[inline] #[inline]
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) { fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window
window.maybe_wait_on_main(move |w| w.set_option_as_alt(option_as_alt)); .maybe_queue_on_main(move |w| w.set_option_as_alt(option_as_alt))
} }
#[inline] #[inline]
fn option_as_alt(&self) -> OptionAsAlt { fn option_as_alt(&self) -> OptionAsAlt {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.maybe_wait_on_main(|w| w.option_as_alt())
window.maybe_wait_on_main(|w| w.option_as_alt())
}
#[inline]
fn set_borderless_game(&self, borderless_game: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game))
}
#[inline]
fn is_borderless_game(&self) -> bool {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.is_borderless_game())
}
#[inline]
fn set_unified_titlebar(&self, unified_titlebar: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.set_unified_titlebar(unified_titlebar))
}
#[inline]
fn unified_titlebar(&self) -> bool {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.unified_titlebar())
} }
} }
/// Corresponds to `NSApplicationActivationPolicy`. /// Corresponds to `NSApplicationActivationPolicy`.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ActivationPolicy { pub enum ActivationPolicy {
/// Corresponds to `NSApplicationActivationPolicyRegular`. /// Corresponds to `NSApplicationActivationPolicyRegular`.
#[default] #[default]
@@ -296,8 +192,7 @@ pub enum ActivationPolicy {
/// Additional methods on [`WindowAttributes`] that are specific to MacOS. /// Additional methods on [`WindowAttributes`] that are specific to MacOS.
/// ///
/// **Note:** Properties dealing with the titlebar will be overwritten by the /// **Note:** Properties dealing with the titlebar will be overwritten by the [`WindowAttributes::with_decorations`] method:
/// [`WindowAttributes::with_decorations`] method:
/// - `with_titlebar_transparent` /// - `with_titlebar_transparent`
/// - `with_title_hidden` /// - `with_title_hidden`
/// - `with_titlebar_hidden` /// - `with_titlebar_hidden`
@@ -328,10 +223,6 @@ pub trait WindowAttributesExtMacOS {
/// ///
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set. /// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self; fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
/// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
fn with_borderless_game(self, borderless_game: bool) -> Self;
/// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set.
fn with_unified_titlebar(self, unified_titlebar: bool) -> Self;
} }
impl WindowAttributesExtMacOS for WindowAttributes { impl WindowAttributesExtMacOS for WindowAttributes {
@@ -391,7 +282,9 @@ impl WindowAttributesExtMacOS for WindowAttributes {
#[inline] #[inline]
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self { fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
self.platform_specific.tabbing_identifier.replace(tabbing_identifier.to_string()); self.platform_specific
.tabbing_identifier
.replace(tabbing_identifier.to_string());
self self
} }
@@ -400,38 +293,23 @@ impl WindowAttributesExtMacOS for WindowAttributes {
self.platform_specific.option_as_alt = option_as_alt; self.platform_specific.option_as_alt = option_as_alt;
self self
} }
#[inline]
fn with_borderless_game(mut self, borderless_game: bool) -> Self {
self.platform_specific.borderless_game = borderless_game;
self
}
#[inline]
fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self {
self.platform_specific.unified_titlebar = unified_titlebar;
self
}
} }
pub trait EventLoopBuilderExtMacOS { pub trait EventLoopBuilderExtMacOS {
/// Sets the activation policy for the application. If used, this will override /// Sets the activation policy for the application.
/// any relevant settings provided in the package manifest.
/// For instance, `with_activation_policy(ActivationPolicy::Regular)` will prevent
/// the application from running as an "agent", even if LSUIElement is set to true.
/// ///
/// If unused, the Winit will honor the package manifest. /// It is set to [`ActivationPolicy::Regular`] by default.
/// ///
/// # Example /// # Example
/// ///
/// Set the activation policy to "accessory". /// Set the activation policy to "accessory".
/// ///
/// ``` /// ```
/// use winit::event_loop::EventLoop; /// use winit::event_loop::EventLoopBuilder;
/// #[cfg(target_os = "macos")] /// #[cfg(target_os = "macos")]
/// use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS}; /// use winit::platform::macos::{EventLoopBuilderExtMacOS, ActivationPolicy};
/// ///
/// let mut builder = EventLoop::builder(); /// let mut builder = EventLoopBuilder::new();
/// #[cfg(target_os = "macos")] /// #[cfg(target_os = "macos")]
/// builder.with_activation_policy(ActivationPolicy::Accessory); /// builder.with_activation_policy(ActivationPolicy::Accessory);
/// # if false { // We can't test this part /// # if false { // We can't test this part
@@ -449,11 +327,11 @@ pub trait EventLoopBuilderExtMacOS {
/// Disable creating a default menubar. /// Disable creating a default menubar.
/// ///
/// ``` /// ```
/// use winit::event_loop::EventLoop; /// use winit::event_loop::EventLoopBuilder;
/// #[cfg(target_os = "macos")] /// #[cfg(target_os = "macos")]
/// use winit::platform::macos::EventLoopBuilderExtMacOS; /// use winit::platform::macos::EventLoopBuilderExtMacOS;
/// ///
/// let mut builder = EventLoop::builder(); /// let mut builder = EventLoopBuilder::new();
/// #[cfg(target_os = "macos")] /// #[cfg(target_os = "macos")]
/// builder.with_default_menu(false); /// builder.with_default_menu(false);
/// # if false { // We can't test this part /// # if false { // We can't test this part
@@ -469,10 +347,10 @@ pub trait EventLoopBuilderExtMacOS {
fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self; fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self;
} }
impl EventLoopBuilderExtMacOS for EventLoopBuilder { impl<T> EventLoopBuilderExtMacOS for EventLoopBuilder<T> {
#[inline] #[inline]
fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self { fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self {
self.platform_specific.activation_policy = Some(activation_policy); self.platform_specific.activation_policy = activation_policy;
self self
} }
@@ -505,18 +383,18 @@ impl MonitorHandleExtMacOS for MonitorHandle {
fn ns_screen(&self) -> Option<*mut c_void> { fn ns_screen(&self) -> Option<*mut c_void> {
// SAFETY: We only use the marker to get a pointer // SAFETY: We only use the marker to get a pointer
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() };
self.inner.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _) self.inner
.ns_screen(mtm)
.map(|s| objc2::rc::Id::as_ptr(&s) as _)
} }
} }
/// Additional methods on [`ActiveEventLoop`] that are specific to macOS. /// Additional methods on [`ActiveEventLoop`] that are specific to macOS.
pub trait ActiveEventLoopExtMacOS { pub trait ActiveEventLoopExtMacOS {
/// Hide the entire application. In most applications this is typically triggered with /// Hide the entire application. In most applications this is typically triggered with Command-H.
/// Command-H.
fn hide_application(&self); fn hide_application(&self);
/// Hide the other applications. In most applications this is typically triggered with /// Hide the other applications. In most applications this is typically triggered with Command+Option-H.
/// Command+Option-H.
fn hide_other_applications(&self); fn hide_other_applications(&self);
/// Set whether the system can automatically organize windows into tabs. /// Set whether the system can automatically organize windows into tabs.
/// ///
@@ -526,44 +404,28 @@ pub trait ActiveEventLoopExtMacOS {
fn allows_automatic_window_tabbing(&self) -> bool; fn allows_automatic_window_tabbing(&self) -> bool;
} }
impl ActiveEventLoopExtMacOS for dyn ActiveEventLoop + '_ { impl ActiveEventLoopExtMacOS for ActiveEventLoop {
fn hide_application(&self) { fn hide_application(&self) {
let event_loop = self self.p.hide_application()
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.hide_application()
} }
fn hide_other_applications(&self) { fn hide_other_applications(&self) {
let event_loop = self self.p.hide_other_applications()
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.hide_other_applications()
} }
fn set_allows_automatic_window_tabbing(&self, enabled: bool) { fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
let event_loop = self self.p.set_allows_automatic_window_tabbing(enabled);
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.set_allows_automatic_window_tabbing(enabled);
} }
fn allows_automatic_window_tabbing(&self) -> bool { fn allows_automatic_window_tabbing(&self) -> bool {
let event_loop = self self.p.allows_automatic_window_tabbing()
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.allows_automatic_window_tabbing()
} }
} }
/// Option as alt behavior. /// Option as alt behavior.
/// ///
/// The default is `None`. /// The default is `None`.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum OptionAsAlt { pub enum OptionAsAlt {
/// The left `Option` key is treated as `Alt`. /// The left `Option` key is treated as `Alt`.
@@ -579,52 +441,3 @@ pub enum OptionAsAlt {
#[default] #[default]
None, None,
} }
/// Additional events on [`ApplicationHandler`] that are specific to macOS.
///
/// This can be registered with [`ApplicationHandler::macos_handler`].
pub trait ApplicationHandlerExtMacOS: ApplicationHandler {
/// The system interpreted a keypress as a standard key binding command.
///
/// Examples include inserting tabs and newlines, or moving the insertion point, see
/// [`NSStandardKeyBindingResponding`] for the full list of key bindings. They are often text
/// editing related.
///
/// This corresponds to the [`doCommandBySelector:`] method on `NSTextInputClient`.
///
/// The `action` parameter contains the string representation of the selector. Examples include
/// `"insertBacktab:"`, `"indent:"` and `"noop:"`.
///
/// # Example
///
/// ```ignore
/// impl ApplicationHandlerExtMacOS for App {
/// fn standard_key_binding(
/// &mut self,
/// event_loop: &dyn ActiveEventLoop,
/// window_id: WindowId,
/// action: &str,
/// ) {
/// match action {
/// "moveBackward:" => self.cursor.position -= 1,
/// "moveForward:" => self.cursor.position += 1,
/// _ => {} // Ignore other actions
/// }
/// }
/// }
/// ```
///
/// [`NSStandardKeyBindingResponding`]: https://developer.apple.com/documentation/appkit/nsstandardkeybindingresponding?language=objc
/// [`doCommandBySelector:`]: https://developer.apple.com/documentation/appkit/nstextinputclient/1438256-docommandbyselector?language=objc
#[doc(alias = "doCommandBySelector:")]
fn standard_key_binding(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
action: &str,
) {
let _ = event_loop;
let _ = window_id;
let _ = action;
}
}

View File

@@ -21,7 +21,6 @@ pub mod windows;
#[cfg(any(x11_platform, docsrs))] #[cfg(any(x11_platform, docsrs))]
pub mod x11; pub mod x11;
#[allow(unused_imports)]
#[cfg(any( #[cfg(any(
windows_platform, windows_platform,
macos_platform, macos_platform,
@@ -52,5 +51,11 @@ pub mod pump_events;
))] ))]
pub mod modifier_supplement; pub mod modifier_supplement;
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, docsrs))] #[cfg(any(
windows_platform,
macos_platform,
x11_platform,
wayland_platform,
docsrs
))]
pub mod scancode; pub mod scancode;

View File

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

View File

@@ -1,10 +1,23 @@
use std::time::Duration; use std::time::Duration;
use crate::application::ApplicationHandler; use crate::{
use crate::event_loop::EventLoop; event::Event,
event_loop::{ActiveEventLoop, EventLoop},
};
/// The return status for `pump_events`
pub enum PumpStatus {
/// Continue running external loop.
Continue,
/// Exit external loop.
Exit(i32),
}
/// Additional methods on [`EventLoop`] for pumping events within an external event loop /// Additional methods on [`EventLoop`] for pumping events within an external event loop
pub trait EventLoopExtPumpEvents { pub trait EventLoopExtPumpEvents {
/// A type provided by the user that can be passed through [`Event::UserEvent`].
type UserEvent;
/// Pump the `EventLoop` to check for and dispatch pending events. /// Pump the `EventLoop` to check for and dispatch pending events.
/// ///
/// This API is designed to enable applications to integrate Winit into an /// This API is designed to enable applications to integrate Winit into an
@@ -70,21 +83,22 @@ pub trait EventLoopExtPumpEvents {
/// ///
/// ## Unsupported Platforms /// ## Unsupported Platforms
/// ///
/// - **Web:** This API is fundamentally incompatible with the event-based way in which Web /// - **Web:** This API is fundamentally incompatible with the event-based way in which
/// browsers work because it's not possible to have a long-running external loop that would /// Web browsers work because it's not possible to have a long-running external
/// block the browser and there is nothing that can be polled to ask for new new events. /// loop that would block the browser and there is nothing that can be
/// Events are delivered via callbacks based on an event loop that is internal to the browser /// polled to ask for new new events. Events are delivered via callbacks based
/// itself. /// on an event loop that is internal to the browser itself.
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so /// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so
/// there's no way to support the same approach to polling as on MacOS. /// there's no way to support the same approach to polling as on MacOS.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Windows**: The implementation will use `PeekMessage` when checking for window messages /// - **Windows**: The implementation will use `PeekMessage` when checking for
/// to avoid blocking your external event loop. /// window messages to avoid blocking your external event loop.
/// ///
/// - **MacOS**: The implementation works in terms of stopping the global application whenever /// - **MacOS**: The implementation works in terms of stopping the global application
/// the application `RunLoop` indicates that it is preparing to block and wait for new events. /// whenever the application `RunLoop` indicates that it is preparing to block
/// and wait for new events.
/// ///
/// This is very different to the polling APIs that are available on other /// This is very different to the polling APIs that are available on other
/// platforms (the lower level polling primitives on MacOS are private /// platforms (the lower level polling primitives on MacOS are private
@@ -99,28 +113,18 @@ pub trait EventLoopExtPumpEvents {
/// If you render outside of Winit you are likely to see window resizing artifacts /// If you render outside of Winit you are likely to see window resizing artifacts
/// since MacOS expects applications to render synchronously during any `drawRect` /// since MacOS expects applications to render synchronously during any `drawRect`
/// callback. /// callback.
fn pump_app_events<A: ApplicationHandler>( fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
&mut self, where
timeout: Option<Duration>, F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
app: A,
) -> PumpStatus;
} }
impl EventLoopExtPumpEvents for EventLoop { impl<T> EventLoopExtPumpEvents for EventLoop<T> {
fn pump_app_events<A: ApplicationHandler>( type UserEvent = T;
&mut self,
timeout: Option<Duration>, fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
app: A, where
) -> PumpStatus { F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
self.event_loop.pump_app_events(timeout, app) {
self.event_loop.pump_events(timeout, event_handler)
} }
} }
/// The return status for `pump_events`
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum PumpStatus {
/// Continue running external loop.
Continue,
/// Exit external loop.
Exit(i32),
}

View File

@@ -1,15 +1,22 @@
use crate::application::ApplicationHandler; use crate::{
use crate::error::EventLoopError; error::EventLoopError,
use crate::event_loop::{ActiveEventLoop, EventLoop}; event::Event,
event_loop::{ActiveEventLoop, EventLoop},
};
#[cfg(doc)] #[cfg(doc)]
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window}; use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
/// Additional methods on [`EventLoop`] to return control flow to the caller. /// Additional methods on [`EventLoop`] to return control flow to the caller.
pub trait EventLoopExtRunOnDemand { pub trait EventLoopExtRunOnDemand {
/// Run the application with the event loop on the calling thread. /// A type provided by the user that can be passed through [`Event::UserEvent`].
type UserEvent;
/// Runs the event loop in the calling thread and calls the given `event_handler` closure
/// to dispatch any window system events.
/// ///
/// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) /// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures
/// closures and it is possible to return control back to the caller without /// and it is possible to return control back to the caller without
/// consuming the `EventLoop` (by using [`exit()`]) and /// consuming the `EventLoop` (by using [`exit()`]) and
/// so the event loop can be re-run after it has exit. /// so the event loop can be re-run after it has exit.
/// ///
@@ -19,27 +26,22 @@ pub trait EventLoopExtRunOnDemand {
/// ///
/// This API is not designed to run an event loop in bursts that you can exit from and return /// This API is not designed to run an event loop in bursts that you can exit from and return
/// to while maintaining the full state of your application. (If you need something like this /// to while maintaining the full state of your application. (If you need something like this
/// you can look at the [`EventLoopExtPumpEvents::pump_app_events()`] API) /// you can look at the [`EventLoopExtPumpEvents::pump_events()`] API)
/// ///
/// Each time `run_on_demand` is called the startup sequence of `init`, followed by /// Each time `run_on_demand` is called the `event_handler` can expect to receive a
/// `resume` is being preserved. /// `NewEvents(Init)` and `Resumed` event (even on platforms that have no suspend/resume
/// lifecycle) - which can be used to consistently initialize application state.
/// ///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
/// # Caveats /// # Caveats
/// - This extension isn't available on all platforms, since it's not always possible to return /// - This extension isn't available on all platforms, since it's not always possible to return
/// to the caller (specifically this is impossible on iOS and Web - though with the Web /// to the caller (specifically this is impossible on iOS and Web - though with the Web
/// backend it is possible to use /// backend it is possible to use `EventLoopExtWebSys::spawn()`[^1] more than once instead).
#[cfg_attr(
any(web_platform, docsrs),
doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
)]
#[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")]
/// [^1] more than once instead).
/// - No [`Window`] state can be carried between separate runs of the event loop. /// - No [`Window`] state can be carried between separate runs of the event loop.
/// ///
/// You are strongly encouraged to use [`EventLoop::run()`] for portability, unless you /// You are strongly encouraged to use [`EventLoop::run()`] for portability, unless you specifically need
/// specifically need the ability to re-run a single event loop more than once /// the ability to re-run a single event loop more than once
/// ///
/// # Supported Platforms /// # Supported Platforms
/// - Windows /// - Windows
@@ -48,28 +50,41 @@ pub trait EventLoopExtRunOnDemand {
/// - Android /// - Android
/// ///
/// # Unsupported Platforms /// # Unsupported Platforms
/// - **Web:** This API is fundamentally incompatible with the event-based way in which Web /// - **Web:** This API is fundamentally incompatible with the event-based way in which
/// browsers work because it's not possible to have a long-running external loop that would /// Web browsers work because it's not possible to have a long-running external
/// block the browser and there is nothing that can be polled to ask for new events. Events /// loop that would block the browser and there is nothing that can be
/// are delivered via callbacks based on an event loop that is internal to the browser itself. /// polled to ask for new events. Events are delivered via callbacks based
/// on an event loop that is internal to the browser itself.
/// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS. /// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS.
/// ///
/// [^1]: `spawn_app()` is only available on the Web platforms. #[cfg_attr(
not(web_platform),
doc = "[^1]: `spawn()` is only available on `wasm` platforms."
)]
/// ///
/// [`exit()`]: ActiveEventLoop::exit() /// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_on_demand<A: ApplicationHandler>( fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
&mut self, where
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A, F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
) -> Result<(), EventLoopError>;
} }
impl EventLoopExtRunOnDemand for EventLoop { impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
fn run_on_demand<A: ApplicationHandler>( type UserEvent = T;
&mut self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A, fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
) -> Result<(), EventLoopError> { where
self.event_loop.run_on_demand(init_closure) F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
{
self.event_loop.window_target().clear_exit();
self.event_loop.run_on_demand(event_handler)
}
}
impl ActiveEventLoop {
/// Clear exit status.
pub(crate) fn clear_exit(&self) {
self.p.clear_exit()
} }
} }

View File

@@ -2,8 +2,8 @@ use crate::keyboard::{KeyCode, PhysicalKey};
// TODO: Describe what this value contains for each platform // TODO: Describe what this value contains for each platform
/// Additional methods for the [`PhysicalKey`] type that allow the user to access the /// Additional methods for the [`PhysicalKey`] type that allow the user to access the platform-specific
/// platform-specific scancode. /// scancode.
/// ///
/// [`PhysicalKey`]: crate::keyboard::PhysicalKey /// [`PhysicalKey`]: crate::keyboard::PhysicalKey
pub trait PhysicalKeyExtScancode { pub trait PhysicalKeyExtScancode {

View File

@@ -23,10 +23,8 @@
use std::env; use std::env;
use crate::error::{NotSupportedError, RequestError}; use crate::error::NotSupportedError;
use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial}; use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial};
#[cfg(wayland_platform)]
use crate::platform::wayland::ActiveEventLoopExtWayland;
use crate::window::{ActivationToken, Window, WindowAttributes}; use crate::window::{ActivationToken, Window, WindowAttributes};
/// The variable which is used mostly on X11. /// The variable which is used mostly on X11.
@@ -46,7 +44,7 @@ pub trait WindowExtStartupNotify {
/// Request a new activation token. /// Request a new activation token.
/// ///
/// The token will be delivered inside /// The token will be delivered inside
fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError>; fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError>;
} }
pub trait WindowAttributesExtStartupNotify { pub trait WindowAttributesExtStartupNotify {
@@ -57,37 +55,22 @@ pub trait WindowAttributesExtStartupNotify {
fn with_activation_token(self, token: ActivationToken) -> Self; fn with_activation_token(self, token: ActivationToken) -> Self;
} }
impl EventLoopExtStartupNotify for dyn ActiveEventLoop + '_ { impl EventLoopExtStartupNotify for ActiveEventLoop {
fn read_token_from_env(&self) -> Option<ActivationToken> { fn read_token_from_env(&self) -> Option<ActivationToken> {
#[cfg(x11_platform)] match self.p {
let _is_wayland = false;
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
let _is_wayland = self.is_wayland(); crate::platform_impl::ActiveEventLoop::Wayland(_) => env::var(WAYLAND_VAR),
#[cfg(x11_platform)]
if _is_wayland { crate::platform_impl::ActiveEventLoop::X(_) => env::var(X11_VAR),
env::var(WAYLAND_VAR).ok().map(ActivationToken::_new)
} else {
env::var(X11_VAR).ok().map(ActivationToken::_new)
} }
.ok()
.map(ActivationToken::_new)
} }
} }
impl WindowExtStartupNotify for dyn Window + '_ { impl WindowExtStartupNotify for Window {
fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> { fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
#[cfg(wayland_platform)] self.window.request_activation_token()
if let Some(window) = self.as_any().downcast_ref::<crate::platform_impl::wayland::Window>()
{
return window.request_activation_token();
}
#[cfg(x11_platform)]
if let Some(window) =
self.as_any().downcast_ref::<crate::platform_impl::x11::window::Window>()
{
return window.request_activation_token();
}
Err(NotSupportedError::new("startup notify is not supported").into())
} }
} }

View File

@@ -13,10 +13,13 @@
//! * `wayland-csd-adwaita` (default). //! * `wayland-csd-adwaita` (default).
//! * `wayland-csd-adwaita-crossfont`. //! * `wayland-csd-adwaita-crossfont`.
//! * `wayland-csd-adwaita-notitle`. //! * `wayland-csd-adwaita-notitle`.
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; use crate::{
use crate::monitor::MonitorHandle; event_loop::{ActiveEventLoop, EventLoopBuilder},
monitor::MonitorHandle,
window::{Window, WindowAttributes},
};
pub use crate::window::Theme; pub use crate::window::Theme;
use crate::window::{Window as CoreWindow, WindowAttributes};
/// Additional methods on [`ActiveEventLoop`] that are specific to Wayland. /// Additional methods on [`ActiveEventLoop`] that are specific to Wayland.
pub trait ActiveEventLoopExtWayland { pub trait ActiveEventLoopExtWayland {
@@ -24,23 +27,10 @@ pub trait ActiveEventLoopExtWayland {
fn is_wayland(&self) -> bool; fn is_wayland(&self) -> bool;
} }
impl ActiveEventLoopExtWayland for dyn ActiveEventLoop + '_ { impl ActiveEventLoopExtWayland for ActiveEventLoop {
#[inline] #[inline]
fn is_wayland(&self) -> bool { fn is_wayland(&self) -> bool {
self.as_any().downcast_ref::<crate::platform_impl::wayland::ActiveEventLoop>().is_some() self.p.is_wayland()
}
}
/// Additional methods on [`EventLoop`] that are specific to Wayland.
pub trait EventLoopExtWayland {
/// True if the [`EventLoop`] uses Wayland.
fn is_wayland(&self) -> bool;
}
impl EventLoopExtWayland for EventLoop {
#[inline]
fn is_wayland(&self) -> bool {
self.event_loop.is_wayland()
} }
} }
@@ -56,7 +46,7 @@ pub trait EventLoopBuilderExtWayland {
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self; fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
} }
impl EventLoopBuilderExtWayland for EventLoopBuilder { impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> {
#[inline] #[inline]
fn with_wayland(&mut self) -> &mut Self { fn with_wayland(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland); self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland);
@@ -71,11 +61,9 @@ impl EventLoopBuilderExtWayland for EventLoopBuilder {
} }
/// Additional methods on [`Window`] that are specific to Wayland. /// Additional methods on [`Window`] that are specific to Wayland.
///
/// [`Window`]: crate::window::Window
pub trait WindowExtWayland {} pub trait WindowExtWayland {}
impl WindowExtWayland for dyn CoreWindow + '_ {} impl WindowExtWayland for Window {}
/// Additional methods on [`WindowAttributes`] that are specific to Wayland. /// Additional methods on [`WindowAttributes`] that are specific to Wayland.
pub trait WindowAttributesExtWayland { pub trait WindowAttributesExtWayland {
@@ -92,8 +80,10 @@ pub trait WindowAttributesExtWayland {
impl WindowAttributesExtWayland for WindowAttributes { impl WindowAttributesExtWayland for WindowAttributes {
#[inline] #[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self { fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.platform_specific.name = self.platform_specific.name = Some(crate::platform_impl::ApplicationName::new(
Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into())); general.into(),
instance.into(),
));
self self
} }
} }

View File

@@ -1,23 +1,24 @@
//! # Web //! # Web
//! //!
//! Winit supports running in Browsers by compiling to WebAssembly with //! The officially supported browsers are Chrome, Firefox and Safari 13.1+,
//! [`wasm-bindgen`][wasm_bindgen]. For information on using Rust on WebAssembly, check out the //! though forks of these should work fine.
//! [Rust and WebAssembly book].
//! //!
//! The officially supported browsers are Chrome, Firefox and Safari 13.1+, though forks of these //! Winit supports compiling to the `wasm32-unknown-unknown` target with
//! should work fine. //! `web-sys`.
//! //!
//! On the Web platform, a Winit [`Window`] is backed by a [`HTMLCanvasElement`][canvas]. Winit will //! On the web platform, a Winit window is backed by a `<canvas>` element. You
//! create that canvas for you or you can [provide your own][with_canvas]. Then you can either let //! can either [provide Winit with a `<canvas>` element][with_canvas], or
//! Winit [insert it into the DOM for you][insert], or [retrieve the canvas][get] and insert it //! [let Winit create a `<canvas>` element which you can then retrieve][get]
//! yourself. //! and insert it into the DOM yourself.
//! //!
//! [canvas]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement //! Currently, there is no example code using Winit on Web, see [#3473]. For
//! [with_canvas]: WindowAttributesExtWeb::with_canvas //! information on using Rust on WebAssembly, check out the [Rust and
//! [get]: WindowExtWeb::canvas //! WebAssembly book].
//! [insert]: WindowAttributesExtWeb::with_append //!
#![cfg_attr(not(web_platform), doc = "[wasm_bindgen]: https://docs.rs/wasm-bindgen")] //! [with_canvas]: WindowAttributesExtWebSys::with_canvas
//! [Rust and WebAssembly book]: https://rustwasm.github.io/book //! [get]: WindowExtWebSys::canvas
//! [#3473]: https://github.com/rust-windowing/winit/issues/3473
//! [Rust and WebAssembly book]: https://rustwasm.github.io/book/
//! //!
//! ## CSS properties //! ## CSS properties
//! //!
@@ -27,21 +28,21 @@
//! - [`padding`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding) //! - [`padding`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding)
//! //!
//! The following APIs can't take them into account and will therefore provide inaccurate results: //! The following APIs can't take them into account and will therefore provide inaccurate results:
//! - [`WindowEvent::SurfaceResized`] and [`Window::(set_)surface_size()`] //! - [`WindowEvent::Resized`] and [`Window::(set_)inner_size()`]
//! - [`WindowEvent::Occluded`] //! - [`WindowEvent::Occluded`]
//! - [`WindowEvent::PointerMoved`], [`WindowEvent::PointerEntered`] and //! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`],
//! [`WindowEvent::PointerLeft`]. //! and [`WindowEvent::Touch`].
//! - [`Window::set_outer_position()`] //! - [`Window::set_outer_position()`]
//! //!
//! [`WindowEvent::SurfaceResized`]: crate::event::WindowEvent::SurfaceResized //! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
//! [`Window::(set_)surface_size()`]: crate::window::Window::surface_size //! [`Window::(set_)inner_size()`]: crate::window::Window::inner_size
//! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded //! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded
//! [`WindowEvent::PointerMoved`]: crate::event::WindowEvent::PointerMoved //! [`WindowEvent::CursorMoved`]: crate::event::WindowEvent::CursorMoved
//! [`WindowEvent::PointerEntered`]: crate::event::WindowEvent::PointerEntered //! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered
//! [`WindowEvent::PointerLeft`]: crate::event::WindowEvent::PointerLeft //! [`WindowEvent::CursorLeft`]: crate::event::WindowEvent::CursorLeft
//! [`WindowEvent::Touch`]: crate::event::WindowEvent::Touch
//! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position //! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position
use std::cell::Ref;
use std::error::Error; use std::error::Error;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::future::Future; use std::future::Future;
@@ -49,38 +50,29 @@ use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time::Duration; use std::time::Duration;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(web_platform)] #[cfg(web_platform)]
use web_sys::HtmlCanvasElement; use web_sys::HtmlCanvasElement;
use crate::application::ApplicationHandler;
use crate::cursor::CustomCursorSource; use crate::cursor::CustomCursorSource;
use crate::error::NotSupportedError; use crate::event::Event;
use crate::event_loop::{ActiveEventLoop, EventLoop}; use crate::event_loop::{ActiveEventLoop, EventLoop};
use crate::monitor::MonitorHandle;
use crate::platform_impl::PlatformCustomCursorSource;
#[cfg(web_platform)] #[cfg(web_platform)]
use crate::platform_impl::{ use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
CustomCursorFuture as PlatformCustomCursorFuture, use crate::platform_impl::PlatformCustomCursorSource;
HasMonitorPermissionFuture as PlatformHasMonitorPermissionFuture,
MonitorPermissionFuture as PlatformMonitorPermissionFuture,
OrientationLockFuture as PlatformOrientationLockFuture,
};
use crate::window::{CustomCursor, Window, WindowAttributes}; use crate::window::{CustomCursor, Window, WindowAttributes};
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
#[doc(hidden)] #[doc(hidden)]
pub struct HtmlCanvasElement; pub struct HtmlCanvasElement;
pub trait WindowExtWeb { pub trait WindowExtWebSys {
/// Only returns the canvas if called from inside the window context (the /// Only returns the canvas if called from inside the window context (the
/// main thread). /// main thread).
fn canvas(&self) -> Option<Ref<'_, HtmlCanvasElement>>; fn canvas(&self) -> Option<HtmlCanvasElement>;
/// Returns [`true`] if calling `event.preventDefault()` is enabled. /// Returns [`true`] if calling `event.preventDefault()` is enabled.
/// ///
/// See [`WindowExtWeb::set_prevent_default()`] for more details. /// See [`Window::set_prevent_default()`] for more details.
fn prevent_default(&self) -> bool; fn prevent_default(&self) -> bool;
/// Sets whether `event.preventDefault()` should be called on events on the /// Sets whether `event.preventDefault()` should be called on events on the
@@ -92,61 +84,41 @@ pub trait WindowExtWeb {
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser /// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
/// context menu with Shift+Rightclick. /// context menu with Shift+Rightclick.
fn set_prevent_default(&self, prevent_default: bool); fn set_prevent_default(&self, prevent_default: bool);
/// Returns whether using [`CursorGrabMode::Locked`] returns raw, un-accelerated mouse input.
///
/// This is the same as [`ActiveEventLoopExtWeb::is_cursor_lock_raw()`], and is provided for
/// convenience.
///
/// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked
fn is_cursor_lock_raw(&self) -> bool;
} }
impl WindowExtWeb for dyn Window + '_ { impl WindowExtWebSys for Window {
#[inline] #[inline]
fn canvas(&self) -> Option<Ref<'_, HtmlCanvasElement>> { fn canvas(&self) -> Option<HtmlCanvasElement> {
self.as_any() self.window.canvas()
.downcast_ref::<crate::platform_impl::Window>()
.expect("non Web window on Web")
.canvas()
} }
fn prevent_default(&self) -> bool { fn prevent_default(&self) -> bool {
self.as_any() self.window.prevent_default()
.downcast_ref::<crate::platform_impl::Window>()
.expect("non Web window on Web")
.prevent_default()
} }
fn set_prevent_default(&self, prevent_default: bool) { fn set_prevent_default(&self, prevent_default: bool) {
self.as_any() self.window.set_prevent_default(prevent_default)
.downcast_ref::<crate::platform_impl::Window>()
.expect("non Web window on Web")
.set_prevent_default(prevent_default)
}
fn is_cursor_lock_raw(&self) -> bool {
self.as_any()
.downcast_ref::<crate::platform_impl::Window>()
.expect("non Web window on Web")
.is_cursor_lock_raw()
} }
} }
pub trait WindowAttributesExtWeb { pub trait WindowAttributesExtWebSys {
/// Pass an [`HtmlCanvasElement`] to be used for this [`Window`]. If [`None`], /// Pass an [`HtmlCanvasElement`] to be used for this [`Window`]. If [`None`],
/// [`WindowAttributes::default()`] will create one. /// [`WindowAttributes::default()`] will create one.
/// ///
/// In any case, the canvas won't be automatically inserted into the Web page. /// In any case, the canvas won't be automatically inserted into the web page.
/// ///
/// [`None`] by default. /// [`None`] by default.
#[cfg_attr(not(web_platform), doc = "", doc = "[`HtmlCanvasElement`]: #only-available-on-wasm")] #[cfg_attr(
not(web_platform),
doc = "",
doc = "[`HtmlCanvasElement`]: #only-available-on-wasm"
)]
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self; fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
/// Sets whether `event.preventDefault()` should be called on events on the /// Sets whether `event.preventDefault()` should be called on events on the
/// canvas that have side effects. /// canvas that have side effects.
/// ///
/// See [`WindowExtWeb::set_prevent_default()`] for more details. /// See [`Window::set_prevent_default()`] for more details.
/// ///
/// Enabled by default. /// Enabled by default.
fn with_prevent_default(self, prevent_default: bool) -> Self; fn with_prevent_default(self, prevent_default: bool) -> Self;
@@ -157,13 +129,13 @@ pub trait WindowAttributesExtWeb {
/// Enabled by default. /// Enabled by default.
fn with_focusable(self, focusable: bool) -> Self; fn with_focusable(self, focusable: bool) -> Self;
/// On window creation, append the canvas element to the Web page if it isn't already. /// On window creation, append the canvas element to the web page if it isn't already.
/// ///
/// Disabled by default. /// Disabled by default.
fn with_append(self, append: bool) -> Self; fn with_append(self, append: bool) -> Self;
} }
impl WindowAttributesExtWeb for WindowAttributes { impl WindowAttributesExtWebSys for WindowAttributes {
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self { fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
self.platform_specific.set_canvas(canvas); self.platform_specific.set_canvas(canvas);
self self
@@ -185,12 +157,18 @@ impl WindowAttributesExtWeb for WindowAttributes {
} }
} }
/// Additional methods on `EventLoop` that are specific to the Web. /// Additional methods on `EventLoop` that are specific to the web.
pub trait EventLoopExtWeb { pub trait EventLoopExtWebSys {
/// A type provided by the user that can be passed through `Event::UserEvent`.
type UserEvent;
/// Initializes the winit event loop. /// Initializes the winit event loop.
/// ///
/// Unlike /// Unlike
#[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run()`")] #[cfg_attr(
all(web_platform, target_feature = "exception-handling"),
doc = "`run()`"
)]
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run()`]" doc = "[`run()`]"
@@ -202,15 +180,29 @@ pub trait EventLoopExtWeb {
/// by calling this function again. This can be useful if you want to recreate the event loop /// by calling this function again. This can be useful if you want to recreate the event loop
/// while the WebAssembly module is still loaded. For example, this can be used to recreate the /// while the WebAssembly module is still loaded. For example, this can be used to recreate the
/// event loop when switching between tabs on a single page application. /// event loop when switching between tabs on a single page application.
#[rustfmt::skip]
/// ///
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run()`]: EventLoop::run()" doc = "[`run()`]: EventLoop::run()"
)] )]
/// [^1]: `run()` is _not_ available on Wasm when the target supports `exception-handling`. /// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`.
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A); fn spawn<F>(self, event_handler: F)
where
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
}
impl<T> EventLoopExtWebSys for EventLoop<T> {
type UserEvent = T;
fn spawn<F>(self, event_handler: F)
where
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
{
self.event_loop.spawn(event_handler)
}
}
pub trait ActiveEventLoopExtWebSys {
/// Sets the strategy for [`ControlFlow::Poll`]. /// Sets the strategy for [`ControlFlow::Poll`].
/// ///
/// See [`PollStrategy`]. /// See [`PollStrategy`].
@@ -225,224 +217,30 @@ pub trait EventLoopExtWeb {
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll /// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
fn poll_strategy(&self) -> PollStrategy; fn poll_strategy(&self) -> PollStrategy;
/// Sets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);
/// Gets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn wait_until_strategy(&self) -> WaitUntilStrategy;
/// Returns if the users device has multiple screens. Useful to check before prompting the user
/// with [`EventLoopExtWeb::request_detailed_monitor_permission()`].
///
/// Browsers might always return [`false`] to reduce fingerprinting.
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError>;
/// Prompts the user for permission to query detailed information about available monitors. The
/// returned [`MonitorPermissionFuture`] can be dropped without aborting the request.
///
/// Check [`EventLoopExtWeb::has_multiple_screens()`] before unnecessarily prompting the user
/// for such permissions.
///
/// [`MonitorHandle`]s don't automatically make use of this after permission is granted. New
/// [`MonitorHandle`]s have to be created instead.
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture;
/// Returns whether the user has given permission to access detailed monitor information.
///
/// [`MonitorHandle`]s don't automatically make use of detailed monitor information after
/// permission is granted. New [`MonitorHandle`]s have to be created instead.
fn has_detailed_monitor_permission(&self) -> HasMonitorPermissionFuture;
}
impl EventLoopExtWeb for EventLoop {
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A) {
self.event_loop.spawn_app(app);
}
fn set_poll_strategy(&self, strategy: PollStrategy) {
self.event_loop.set_poll_strategy(strategy);
}
fn poll_strategy(&self) -> PollStrategy {
self.event_loop.poll_strategy()
}
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
self.event_loop.set_wait_until_strategy(strategy);
}
fn wait_until_strategy(&self) -> WaitUntilStrategy {
self.event_loop.wait_until_strategy()
}
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError> {
self.event_loop.has_multiple_screens()
}
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
MonitorPermissionFuture(self.event_loop.request_detailed_monitor_permission())
}
fn has_detailed_monitor_permission(&self) -> HasMonitorPermissionFuture {
HasMonitorPermissionFuture(self.event_loop.has_detailed_monitor_permission())
}
}
pub trait ActiveEventLoopExtWeb {
/// Sets the strategy for [`ControlFlow::Poll`].
///
/// See [`PollStrategy`].
///
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
fn set_poll_strategy(&self, strategy: PollStrategy);
/// Gets the strategy for [`ControlFlow::Poll`].
///
/// See [`PollStrategy`].
///
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
fn poll_strategy(&self) -> PollStrategy;
/// Sets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);
/// Gets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn wait_until_strategy(&self) -> WaitUntilStrategy;
/// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the /// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the
/// cursor has completely finished loading. /// cursor has completely finished loading.
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture; fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture;
/// Returns whether using [`CursorGrabMode::Locked`] returns raw, un-accelerated mouse input.
///
/// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked
fn is_cursor_lock_raw(&self) -> bool;
/// Returns if the users device has multiple screens. Useful to check before prompting the user
/// with [`EventLoopExtWeb::request_detailed_monitor_permission()`].
///
/// Browsers might always return [`false`] to reduce fingerprinting.
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError>;
/// Prompts the user for permission to query detailed information about available monitors. The
/// returned [`MonitorPermissionFuture`] can be dropped without aborting the request.
///
/// Check [`EventLoopExtWeb::has_multiple_screens()`] before unnecessarily prompting the user
/// for such permissions.
///
/// [`MonitorHandle`]s don't automatically make use of this after permission is granted. New
/// [`MonitorHandle`]s have to be created instead.
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture;
/// Returns whether the user has given permission to access detailed monitor information.
///
/// [`MonitorHandle`]s don't automatically make use of detailed monitor information after
/// permission is granted. New [`MonitorHandle`]s have to be created instead.
fn has_detailed_monitor_permission(&self) -> bool;
} }
impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { impl ActiveEventLoopExtWebSys for ActiveEventLoop {
#[inline] #[inline]
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture { fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture {
let event_loop = self self.p.create_custom_cursor_async(source)
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.create_custom_cursor_async(source)
} }
#[inline] #[inline]
fn set_poll_strategy(&self, strategy: PollStrategy) { fn set_poll_strategy(&self, strategy: PollStrategy) {
let event_loop = self self.p.set_poll_strategy(strategy);
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.set_poll_strategy(strategy);
} }
#[inline] #[inline]
fn poll_strategy(&self) -> PollStrategy { fn poll_strategy(&self) -> PollStrategy {
let event_loop = self self.p.poll_strategy()
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.poll_strategy()
}
#[inline]
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.set_wait_until_strategy(strategy);
}
#[inline]
fn wait_until_strategy(&self) -> WaitUntilStrategy {
let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.wait_until_strategy()
}
#[inline]
fn is_cursor_lock_raw(&self) -> bool {
let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.is_cursor_lock_raw()
}
#[inline]
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError> {
let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.has_multiple_screens()
}
#[inline]
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
MonitorPermissionFuture(event_loop.request_detailed_monitor_permission())
}
#[inline]
fn has_detailed_monitor_permission(&self) -> bool {
let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.has_detailed_monitor_permission()
} }
} }
/// Strategy used for [`ControlFlow::Poll`][crate::event_loop::ControlFlow::Poll]. /// Strategy used for [`ControlFlow::Poll`](crate::event_loop::ControlFlow::Poll).
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PollStrategy { pub enum PollStrategy {
/// Uses [`Window.requestIdleCallback()`] to queue the next event loop. If not available /// Uses [`Window.requestIdleCallback()`] to queue the next event loop. If not available
/// this will fallback to [`setTimeout()`]. /// this will fallback to [`setTimeout()`].
@@ -467,31 +265,7 @@ pub enum PollStrategy {
Scheduler, Scheduler,
} }
/// Strategy used for [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil]. pub trait CustomCursorExtWebSys {
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum WaitUntilStrategy {
/// Uses the [Prioritized Task Scheduling API] to queue the next event loop. If not available
/// this will fallback to [`setTimeout()`].
///
/// This strategy is commonly not affected by browser throttling unless the window is not
/// focused.
///
/// This is the default strategy.
///
/// [Prioritized Task Scheduling API]: https://developer.mozilla.org/en-US/docs/Web/API/Prioritized_Task_Scheduling_API
/// [`setTimeout()`]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
#[default]
Scheduler,
/// Equal to [`Scheduler`][Self::Scheduler] but wakes up the event loop from a [worker].
///
/// This strategy is commonly not affected by browser throttling regardless of window focus.
///
/// [worker]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
Worker,
}
pub trait CustomCursorExtWeb {
/// Returns if this cursor is an animation. /// Returns if this cursor is an animation.
fn is_animation(&self) -> bool; fn is_animation(&self) -> bool;
@@ -510,13 +284,19 @@ pub trait CustomCursorExtWeb {
) -> Result<CustomCursorSource, BadAnimation>; ) -> Result<CustomCursorSource, BadAnimation>;
} }
impl CustomCursorExtWeb for CustomCursor { impl CustomCursorExtWebSys for CustomCursor {
fn is_animation(&self) -> bool { fn is_animation(&self) -> bool {
self.inner.animation self.inner.animation
} }
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource { fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource {
CustomCursorSource { inner: PlatformCustomCursorSource::Url { url, hotspot_x, hotspot_y } } CustomCursorSource {
inner: PlatformCustomCursorSource::Url {
url,
hotspot_x,
hotspot_y,
},
}
} }
fn from_animation( fn from_animation(
@@ -538,8 +318,7 @@ impl CustomCursorExtWeb for CustomCursor {
} }
/// An error produced when using [`CustomCursor::from_animation`] with invalid arguments. /// An error produced when using [`CustomCursor::from_animation`] with invalid arguments.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BadAnimation { pub enum BadAnimation {
/// Produced when no cursors were supplied. /// Produced when no cursors were supplied.
Empty, Empty,
@@ -551,7 +330,7 @@ impl fmt::Display for BadAnimation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Empty => write!(f, "No cursors supplied"), Self::Empty => write!(f, "No cursors supplied"),
Self::Animation => write!(f, "A supplied cursor is an animation"), Self::Animation => write!(f, "A supplied cursor is an animtion"),
} }
} }
} }
@@ -568,15 +347,17 @@ impl Future for CustomCursorFuture {
type Output = Result<CustomCursor, CustomCursorError>; type Output = Result<CustomCursor, CustomCursorError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(cx).map_ok(|cursor| CustomCursor { inner: cursor }) Pin::new(&mut self.0)
.poll(cx)
.map_ok(|cursor| CustomCursor { inner: cursor })
} }
} }
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CustomCursorError { pub enum CustomCursorError {
Blob, Blob,
Decode(String), Decode(String),
Animation,
} }
impl Display for CustomCursorError { impl Display for CustomCursorError {
@@ -584,198 +365,10 @@ impl Display for CustomCursorError {
match self { match self {
Self::Blob => write!(f, "failed to create `Blob`"), Self::Blob => write!(f, "failed to create `Blob`"),
Self::Decode(error) => write!(f, "failed to decode image: {error}"), Self::Decode(error) => write!(f, "failed to decode image: {error}"),
} Self::Animation => write!(
}
}
impl Error for CustomCursorError {}
#[cfg(not(web_platform))]
struct PlatformMonitorPermissionFuture;
/// Can be dropped without aborting the request for detailed monitor permissions.
#[derive(Debug)]
pub struct MonitorPermissionFuture(pub(crate) PlatformMonitorPermissionFuture);
impl Future for MonitorPermissionFuture {
type Output = Result<(), MonitorPermissionError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(cx)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum MonitorPermissionError {
/// User has explicitly denied permission to query detailed monitor information.
Denied,
/// User has not decided to give permission to query detailed monitor information.
Prompt,
/// Browser does not support detailed monitor information.
Unsupported,
}
impl Display for MonitorPermissionError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
MonitorPermissionError::Denied => write!(
f, f,
"User has explicitly denied permission to query detailed monitor information" "found `CustomCursor` that is an animation when building an animation"
), ),
MonitorPermissionError::Prompt => write!(
f,
"User has not decided to give permission to query detailed monitor information"
),
MonitorPermissionError::Unsupported => {
write!(f, "Browser does not support detailed monitor information")
},
} }
} }
} }
impl Error for MonitorPermissionError {}
#[cfg(not(web_platform))]
struct PlatformHasMonitorPermissionFuture;
#[derive(Debug)]
pub struct HasMonitorPermissionFuture(PlatformHasMonitorPermissionFuture);
impl Future for HasMonitorPermissionFuture {
type Output = bool;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(cx)
}
}
/// Additional methods on [`MonitorHandle`] that are specific to the Web.
pub trait MonitorHandleExtWeb {
/// Returns whether the screen is internal to the device or external.
///
/// External devices are generally manufactured separately from the device they are attached to
/// and can be connected and disconnected as needed, whereas internal screens are part of
/// the device and not intended to be disconnected.
fn is_internal(&self) -> Option<bool>;
/// Returns screen orientation data for this monitor.
fn orientation(&self) -> OrientationData;
/// Lock the screen orientation. The returned [`OrientationLockFuture`] can be dropped without
/// aborting the request.
///
/// Will fail if another locking call is in progress.
fn request_lock(&self, orientation: OrientationLock) -> OrientationLockFuture;
/// Unlock the screen orientation.
///
/// Will fail if a locking call is in progress.
fn unlock(&self) -> Result<(), OrientationLockError>;
/// Returns whether this [`MonitorHandle`] was created using detailed monitor permissions. If
/// [`false`] will always represent the current monitor the browser window is in instead of a
/// specific monitor.
///
/// See [`ActiveEventLoopExtWeb::request_detailed_monitor_permission()`].
fn is_detailed(&self) -> bool;
}
impl MonitorHandleExtWeb for MonitorHandle {
fn is_internal(&self) -> Option<bool> {
self.inner.is_internal()
}
fn orientation(&self) -> OrientationData {
self.inner.orientation()
}
fn request_lock(&self, orientation_lock: OrientationLock) -> OrientationLockFuture {
OrientationLockFuture(self.inner.request_lock(orientation_lock))
}
fn unlock(&self) -> Result<(), OrientationLockError> {
self.inner.unlock()
}
fn is_detailed(&self) -> bool {
self.inner.is_detailed()
}
}
/// Screen orientation data.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct OrientationData {
/// The orientation.
pub orientation: Orientation,
/// [`true`] if the [`orientation`](Self::orientation) is flipped upside down.
pub flipped: bool,
/// The most natural orientation for the screen. Computer monitors are commonly naturally
/// landscape mode, while mobile phones are commonly naturally portrait mode.
pub natural: Orientation,
}
/// Screen orientation.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Orientation {
/// The screen's aspect ratio has a width greater than the height.
Landscape,
/// The screen's aspect ratio has a height greater than the width.
Portrait,
}
/// Screen orientation lock options. Reoresents which orientations a user can use.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum OrientationLock {
/// User is free to use any orientation.
Any,
/// User is locked to the most upright natural orientation for the screen. Computer monitors
/// are commonly naturally landscape mode, while mobile phones are commonly
/// naturally portrait mode.
Natural,
/// User is locked to landscape mode.
Landscape {
/// - [`None`]: User is locked to both upright or upside down landscape mode.
/// - [`true`]: User is locked to upright landscape mode.
/// - [`false`]: User is locked to upside down landscape mode.
flipped: Option<bool>,
},
/// User is locked to portrait mode.
Portrait {
/// - [`None`]: User is locked to both upright or upside down portrait mode.
/// - [`true`]: User is locked to upright portrait mode.
/// - [`false`]: User is locked to upside down portrait mode.
flipped: Option<bool>,
},
}
#[cfg(not(web_platform))]
struct PlatformOrientationLockFuture;
/// Can be dropped without aborting the request to lock the screen.
#[derive(Debug)]
pub struct OrientationLockFuture(PlatformOrientationLockFuture);
impl Future for OrientationLockFuture {
type Output = Result<(), OrientationLockError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(cx)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum OrientationLockError {
Unsupported,
Busy,
}
impl Display for OrientationLockError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Unsupported => write!(f, "Locking the screen orientation is not supported"),
Self::Busy => write!(f, "Another locking call is in progress"),
}
}
}
impl Error for OrientationLockError {}

View File

@@ -2,21 +2,15 @@
//! //!
//! The supported OS version is Windows 7 or higher, though Windows 10 is //! The supported OS version is Windows 7 or higher, though Windows 10 is
//! tested regularly. //! tested regularly.
use std::borrow::Borrow; use std::{ffi::c_void, path::Path};
use std::ffi::c_void;
use std::ops::Deref;
use std::path::Path;
#[cfg(feature = "serde")] use crate::{
use serde::{Deserialize, Serialize}; dpi::PhysicalSize,
#[cfg(windows_platform)] event::DeviceId,
use windows_sys::Win32::Foundation::HANDLE; event_loop::EventLoopBuilder,
monitor::MonitorHandle,
use crate::dpi::PhysicalSize; window::{BadIcon, Icon, Window, WindowAttributes},
use crate::event::DeviceId; };
use crate::event_loop::EventLoopBuilder;
use crate::monitor::MonitorHandle;
use crate::window::{BadIcon, Icon, Window, WindowAttributes};
/// Window Handle type used by Win32 API /// Window Handle type used by Win32 API
pub type HWND = isize; pub type HWND = isize;
@@ -31,7 +25,6 @@ pub type HMONITOR = isize;
/// ///
/// [`DWM_SYSTEMBACKDROP_TYPE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type /// [`DWM_SYSTEMBACKDROP_TYPE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BackdropType { pub enum BackdropType {
/// Corresponds to `DWMSBT_AUTO`. /// Corresponds to `DWMSBT_AUTO`.
/// ///
@@ -61,15 +54,14 @@ pub enum BackdropType {
/// Describes a color used by Windows /// Describes a color used by Windows
#[repr(transparent)] #[repr(transparent)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Color(u32); pub struct Color(u32);
impl Color { impl Color {
// Special constant only valid for the window border and therefore modeled using Option<Color>
// for user facing code
const NONE: Color = Color(0xfffffffe);
/// Use the system's default color /// Use the system's default color
pub const SYSTEM_DEFAULT: Color = Color(0xffffffff); pub const SYSTEM_DEFAULT: Color = Color(0xFFFFFFFF);
//Special constant only valid for the window border and therefore modeled using Option<Color> for user facing code
const NONE: Color = Color(0xFFFFFFFE);
/// Create a new color from the given RGB values /// Create a new color from the given RGB values
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self { pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
@@ -90,7 +82,6 @@ impl Default for Color {
/// [`DWM_WINDOW_CORNER_PREFERENCE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference /// [`DWM_WINDOW_CORNER_PREFERENCE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference
#[repr(i32)] #[repr(i32)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CornerPreference { pub enum CornerPreference {
/// Corresponds to `DWMWCP_DEFAULT`. /// Corresponds to `DWMWCP_DEFAULT`.
/// ///
@@ -114,34 +105,6 @@ pub enum CornerPreference {
RoundSmall = 3, RoundSmall = 3,
} }
/// A wrapper around a [`Window`] that ignores thread-specific window handle limitations.
///
/// See [`WindowBorrowExtWindows::any_thread`] for more information.
#[derive(Clone, Debug)]
pub struct AnyThread<W: Window>(W);
impl<W: Window> AnyThread<W> {
/// Get a reference to the inner window.
#[inline]
pub fn get_ref(&self) -> &dyn Window {
&self.0
}
}
impl<W: Window> Deref for AnyThread<W> {
type Target = W;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<W: Window> rwh_06::HasWindowHandle for AnyThread<W> {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
// SAFETY: The top level user has asserted this is only used safely.
unsafe { self.get_ref().window_handle_any_thread() }
}
}
/// Additional methods on `EventLoop` that are specific to Windows. /// Additional methods on `EventLoop` that are specific to Windows.
pub trait EventLoopBuilderExtWindows { pub trait EventLoopBuilderExtWindows {
/// Whether to allow the event loop to be created off of the main thread. /// Whether to allow the event loop to be created off of the main thread.
@@ -166,11 +129,11 @@ pub trait EventLoopBuilderExtWindows {
/// Disable process-wide DPI awareness. /// Disable process-wide DPI awareness.
/// ///
/// ``` /// ```
/// use winit::event_loop::EventLoop; /// use winit::event_loop::EventLoopBuilder;
/// #[cfg(target_os = "windows")] /// #[cfg(target_os = "windows")]
/// use winit::platform::windows::EventLoopBuilderExtWindows; /// use winit::platform::windows::EventLoopBuilderExtWindows;
/// ///
/// let mut builder = EventLoop::builder(); /// let mut builder = EventLoopBuilder::new();
/// #[cfg(target_os = "windows")] /// #[cfg(target_os = "windows")]
/// builder.with_dpi_aware(false); /// builder.with_dpi_aware(false);
/// # if false { // We can't test this part /// # if false { // We can't test this part
@@ -186,11 +149,11 @@ pub trait EventLoopBuilderExtWindows {
/// ///
/// ``` /// ```
/// # use windows_sys::Win32::UI::WindowsAndMessaging::{ACCEL, CreateAcceleratorTableW, TranslateAcceleratorW, DispatchMessageW, TranslateMessage, MSG}; /// # use windows_sys::Win32::UI::WindowsAndMessaging::{ACCEL, CreateAcceleratorTableW, TranslateAcceleratorW, DispatchMessageW, TranslateMessage, MSG};
/// use winit::event_loop::EventLoop; /// use winit::event_loop::EventLoopBuilder;
/// #[cfg(target_os = "windows")] /// #[cfg(target_os = "windows")]
/// use winit::platform::windows::EventLoopBuilderExtWindows; /// use winit::platform::windows::EventLoopBuilderExtWindows;
/// ///
/// let mut builder = EventLoop::builder(); /// let mut builder = EventLoopBuilder::new();
/// #[cfg(target_os = "windows")] /// #[cfg(target_os = "windows")]
/// builder.with_msg_hook(|msg|{ /// builder.with_msg_hook(|msg|{
/// let msg = msg as *const MSG; /// let msg = msg as *const MSG;
@@ -210,7 +173,7 @@ pub trait EventLoopBuilderExtWindows {
F: FnMut(*const c_void) -> bool + 'static; F: FnMut(*const c_void) -> bool + 'static;
} }
impl EventLoopBuilderExtWindows for EventLoopBuilder { impl<T> EventLoopBuilderExtWindows for EventLoopBuilder<T> {
#[inline] #[inline]
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self { fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
self.platform_specific.any_thread = any_thread; self.platform_specific.any_thread = any_thread;
@@ -239,8 +202,8 @@ pub trait WindowExtWindows {
/// ///
/// A window must be enabled before it can be activated. /// A window must be enabled before it can be activated.
/// If an application has create a modal dialog box by disabling its owner window /// If an application has create a modal dialog box by disabling its owner window
/// (as described in [`WindowAttributesExtWindows::with_owner_window`]), the application must /// (as described in [`WindowAttributesExtWindows::with_owner_window`]), the application must enable
/// enable the owner window before destroying the dialog box. /// the owner window before destroying the dialog box.
/// Otherwise, another window will receive the keyboard focus and be activated. /// Otherwise, another window will receive the keyboard focus and be activated.
/// ///
/// If a child window is disabled, it is ignored when the system tries to determine which /// If a child window is disabled, it is ignored when the system tries to determine which
@@ -285,173 +248,65 @@ pub trait WindowExtWindows {
/// ///
/// Supported starting with Windows 11 Build 22000. /// Supported starting with Windows 11 Build 22000.
fn set_corner_preference(&self, preference: CornerPreference); fn set_corner_preference(&self, preference: CornerPreference);
/// Get the raw window handle for this [`Window`] without checking for thread affinity.
///
/// Window handles in Win32 have a property called "thread affinity" that ties them to their
/// origin thread. Some operations can only happen on the window's origin thread, while others
/// can be called from any thread. For example, [`SetWindowSubclass`] is not thread safe while
/// [`GetDC`] is thread safe.
///
/// In Rust terms, the window handle is `Send` sometimes but `!Send` other times.
///
/// Therefore, in order to avoid confusing threading errors, [`Window`] only returns the
/// window handle when the [`window_handle`] function is called from the thread that created
/// the window. In other cases, it returns an [`Unavailable`] error.
///
/// However in some cases you may already know that you are using the window handle for
/// operations that are guaranteed to be thread-safe. In which case this function aims
/// to provide an escape hatch so these functions are still accessible from other threads.
///
/// # Safety
///
/// It is the responsibility of the user to only pass the window handle into thread-safe
/// Win32 APIs.
///
/// [`SetWindowSubclass`]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-setwindowsubclass
/// [`GetDC`]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc
/// [`Window`]: crate::window::Window
/// [`window_handle`]: https://docs.rs/raw-window-handle/latest/raw_window_handle/trait.HasWindowHandle.html#tymethod.window_handle
/// [`Unavailable`]: https://docs.rs/raw-window-handle/latest/raw_window_handle/enum.HandleError.html#variant.Unavailable
///
/// ## Example
///
/// ```no_run
/// # use winit::window::Window;
/// # fn scope(window: Box<dyn Window>) {
/// use std::thread;
///
/// use winit::platform::windows::WindowExtWindows;
/// use winit::raw_window_handle::HasWindowHandle;
///
/// // We can get the window handle on the current thread.
/// let handle = window.window_handle().unwrap();
///
/// // However, on another thread, we can't!
/// thread::spawn(move || {
/// assert!(window.window_handle().is_err());
///
/// // We can use this function as an escape hatch.
/// let handle = unsafe { window.window_handle_any_thread().unwrap() };
/// });
/// # }
/// ```
unsafe fn window_handle_any_thread(
&self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>;
} }
impl WindowExtWindows for dyn Window + '_ { impl WindowExtWindows for Window {
#[inline] #[inline]
fn set_enable(&self, enabled: bool) { fn set_enable(&self, enabled: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.set_enable(enabled)
window.set_enable(enabled)
} }
#[inline] #[inline]
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) { fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.set_taskbar_icon(taskbar_icon)
window.set_taskbar_icon(taskbar_icon)
} }
#[inline] #[inline]
fn set_skip_taskbar(&self, skip: bool) { fn set_skip_taskbar(&self, skip: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.set_skip_taskbar(skip)
window.set_skip_taskbar(skip)
} }
#[inline] #[inline]
fn set_undecorated_shadow(&self, shadow: bool) { fn set_undecorated_shadow(&self, shadow: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.set_undecorated_shadow(shadow)
window.set_undecorated_shadow(shadow)
} }
#[inline] #[inline]
fn set_system_backdrop(&self, backdrop_type: BackdropType) { fn set_system_backdrop(&self, backdrop_type: BackdropType) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.set_system_backdrop(backdrop_type)
window.set_system_backdrop(backdrop_type)
} }
#[inline] #[inline]
fn set_border_color(&self, color: Option<Color>) { fn set_border_color(&self, color: Option<Color>) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.set_border_color(color.unwrap_or(Color::NONE))
window.set_border_color(color.unwrap_or(Color::NONE))
} }
#[inline] #[inline]
fn set_title_background_color(&self, color: Option<Color>) { fn set_title_background_color(&self, color: Option<Color>) {
// The windows docs don't mention NONE as a valid options but it works in practice and is // The windows docs don't mention NONE as a valid options but it works in practice and is useful
// useful to circumvent the Windows option "Show accent color on title bars and // to circumvent the Windows option "Show accent color on title bars and window borders"
// window borders" self.window
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); .set_title_background_color(color.unwrap_or(Color::NONE))
window.set_title_background_color(color.unwrap_or(Color::NONE))
} }
#[inline] #[inline]
fn set_title_text_color(&self, color: Color) { fn set_title_text_color(&self, color: Color) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.set_title_text_color(color)
window.set_title_text_color(color)
} }
#[inline] #[inline]
fn set_corner_preference(&self, preference: CornerPreference) { fn set_corner_preference(&self, preference: CornerPreference) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap(); self.window.set_corner_preference(preference)
window.set_corner_preference(preference)
}
unsafe fn window_handle_any_thread(
&self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
unsafe {
let handle = window.rwh_06_no_thread_check()?;
// SAFETY: The handle is valid in this context.
Ok(rwh_06::WindowHandle::borrow_raw(handle))
}
} }
} }
/// Additional methods for anything that dereference to [`Window`].
///
/// [`Window`]: crate::window::Window
pub trait WindowBorrowExtWindows: Borrow<dyn Window> + Sized {
/// Create an object that allows accessing the inner window handle in a thread-unsafe way.
///
/// It is possible to call [`window_handle_any_thread`] to get around Windows's thread
/// affinity limitations. However, it may be desired to pass the [`Window`] into something
/// that requires the [`HasWindowHandle`] trait, while ignoring thread affinity limitations.
///
/// This function wraps anything that implements `Borrow<Window>` into a structure that
/// uses the inner window handle as a mean of implementing [`HasWindowHandle`]. It wraps
/// `Window`, `&Window`, `Arc<Window>`, and other reference types.
///
/// # Safety
///
/// It is the responsibility of the user to only pass the window handle into thread-safe
/// Win32 APIs.
///
/// [`Window`]: crate::window::Window
/// [`HasWindowHandle`]: rwh_06::HasWindowHandle
/// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread
unsafe fn any_thread(self) -> AnyThread<Self>
where
Self: Window,
{
AnyThread(self)
}
}
impl<W: Borrow<dyn Window> + Sized> WindowBorrowExtWindows for W {}
/// Additional methods on `WindowAttributes` that are specific to Windows. /// Additional methods on `WindowAttributes` that are specific to Windows.
#[allow(rustdoc::broken_intra_doc_links)] #[allow(rustdoc::broken_intra_doc_links)]
pub trait WindowAttributesExtWindows { pub trait WindowAttributesExtWindows {
/// Set an owner to the window to be created. Can be used to create a dialog box, for example. /// Set an owner to the window to be created. Can be used to create a dialog box, for example.
/// This only works when [`WindowAttributes::with_parent_window`] isn't called or set to `None`. /// This only works when [`WindowAttributes::with_parent_window`] isn't called or set to `None`.
/// Can be used in combination with /// Can be used in combination with [`WindowExtWindows::set_enable(false)`](WindowExtWindows::set_enable)
/// [`WindowExtWindows::set_enable(false)`][WindowExtWindows::set_enable] on the owner /// on the owner window to create a modal dialog box.
/// window to create a modal dialog box.
/// ///
/// From MSDN: /// From MSDN:
/// - An owned window is always above its owner in the z-order. /// - An owned window is always above its owner in the z-order.
@@ -467,14 +322,17 @@ pub trait WindowAttributesExtWindows {
/// ///
/// The menu must have been manually created beforehand with [`CreateMenu`] or similar. /// The menu must have been manually created beforehand with [`CreateMenu`] or similar.
/// ///
/// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how /// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look.
/// the menus look. If you use this, it is recommended that you combine it with /// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect.
/// `with_theme(Some(Theme::Light))` to avoid a jarring effect. ///
#[cfg_attr( #[cfg_attr(
windows_platform, platform_windows,
doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu" doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
)] )]
#[cfg_attr(not(windows_platform), doc = "[`CreateMenu`]: #only-available-on-windows")] #[cfg_attr(
not(platform_windows),
doc = "[`CreateMenu`]: #only-available-on-windows"
)]
fn with_menu(self, menu: HMENU) -> Self; fn with_menu(self, menu: HMENU) -> Self;
/// This sets `ICON_BIG`. A good ceiling here is 256x256. /// This sets `ICON_BIG`. A good ceiling here is 256x256.
@@ -483,12 +341,12 @@ pub trait WindowAttributesExtWindows {
/// This sets `WS_EX_NOREDIRECTIONBITMAP`. /// This sets `WS_EX_NOREDIRECTIONBITMAP`.
fn with_no_redirection_bitmap(self, flag: bool) -> Self; fn with_no_redirection_bitmap(self, flag: bool) -> Self;
/// Enables or disables drag and drop support (enabled by default). Will interfere with other /// Enables or disables drag and drop support (enabled by default). Will interfere with other crates
/// crates that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` /// that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` instead of
/// instead of `COINIT_APARTMENTTHREADED`) on the same thread. Note that winit may still /// `COINIT_APARTMENTTHREADED`) on the same thread. Note that winit may still attempt to initialize
/// attempt to initialize COM API regardless of this option. Currently only fullscreen mode /// COM API regardless of this option. Currently only fullscreen mode does that, but there may be more in the future.
/// does that, but there may be more in the future. If you need COM API with /// If you need COM API with `COINIT_MULTITHREADED` you must initialize it before calling any winit functions.
/// `COINIT_MULTITHREADED` you must initialize it before calling any winit functions. See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information. /// See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information.
fn with_drag_and_drop(self, flag: bool) -> Self; fn with_drag_and_drop(self, flag: bool) -> Self;
/// Whether show or hide the window icon in the taskbar. /// Whether show or hide the window icon in the taskbar.
@@ -647,15 +505,10 @@ pub trait DeviceIdExtWindows {
fn persistent_identifier(&self) -> Option<String>; fn persistent_identifier(&self) -> Option<String>;
} }
#[cfg(windows_platform)]
impl DeviceIdExtWindows for DeviceId { impl DeviceIdExtWindows for DeviceId {
#[inline]
fn persistent_identifier(&self) -> Option<String> { fn persistent_identifier(&self) -> Option<String> {
let raw_id = self.into_raw(); self.0.persistent_identifier()
if raw_id != 0 {
crate::platform_impl::raw_input::get_raw_input_device_name(raw_id as HANDLE)
} else {
None
}
} }
} }

View File

@@ -2,22 +2,24 @@
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{
event_loop::{ActiveEventLoop, EventLoopBuilder},
monitor::MonitorHandle,
window::{Window, WindowAttributes},
};
use crate::dpi::Size; use crate::dpi::Size;
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
use crate::monitor::MonitorHandle;
use crate::window::{Window as CoreWindow, WindowAttributes};
/// X window type. Maps directly to /// X window type. Maps directly to
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html). /// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum WindowType { pub enum WindowType {
/// A desktop feature. This can include a single window containing desktop icons with the same /// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
/// dimensions as the screen, allowing the desktop environment to have full control of the /// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
/// desktop, without the need for proxying root window clicks. /// root window clicks.
Desktop, Desktop,
/// A dock or panel feature. Typically a Window Manager would keep such windows on top of all /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
/// other windows.
Dock, Dock,
/// Toolbar windows. "Torn off" from the main application. /// Toolbar windows. "Torn off" from the main application.
Toolbar, Toolbar,
@@ -35,8 +37,8 @@ pub enum WindowType {
/// A popup menu that usually appears when the user right clicks on an object. /// A popup menu that usually appears when the user right clicks on an object.
/// This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
PopupMenu, PopupMenu,
/// A tooltip window. Usually used to show additional information when hovering over an object /// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.
/// with the cursor. This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
Tooltip, Tooltip,
/// The window is a notification. /// The window is a notification.
/// This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
@@ -44,7 +46,7 @@ pub enum WindowType {
/// This should be used on the windows that are popped up by combo boxes. /// This should be used on the windows that are popped up by combo boxes.
/// This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
Combo, Combo,
/// This indicates the window is being dragged. /// This indicates the the window is being dragged.
/// This property is typically used on override-redirect windows. /// This property is typically used on override-redirect windows.
Dnd, Dnd,
/// This is a normal, top-level window. /// This is a normal, top-level window.
@@ -80,7 +82,12 @@ pub type XWindow = u32;
#[inline] #[inline]
pub fn register_xlib_error_hook(hook: XlibErrorHook) { pub fn register_xlib_error_hook(hook: XlibErrorHook) {
// Append new hook. // Append new hook.
crate::platform_impl::XLIB_ERROR_HOOKS.lock().unwrap().push(hook); unsafe {
crate::platform_impl::XLIB_ERROR_HOOKS
.lock()
.unwrap()
.push(hook);
}
} }
/// Additional methods on [`ActiveEventLoop`] that are specific to X11. /// Additional methods on [`ActiveEventLoop`] that are specific to X11.
@@ -89,23 +96,10 @@ pub trait ActiveEventLoopExtX11 {
fn is_x11(&self) -> bool; fn is_x11(&self) -> bool;
} }
impl ActiveEventLoopExtX11 for dyn ActiveEventLoop + '_ { impl ActiveEventLoopExtX11 for ActiveEventLoop {
#[inline] #[inline]
fn is_x11(&self) -> bool { fn is_x11(&self) -> bool {
self.as_any().downcast_ref::<crate::platform_impl::x11::ActiveEventLoop>().is_some() !self.p.is_wayland()
}
}
/// Additional methods on [`EventLoop`] that are specific to X11.
pub trait EventLoopExtX11 {
/// True if the [`EventLoop`] uses X11.
fn is_x11(&self) -> bool;
}
impl EventLoopExtX11 for EventLoop {
#[inline]
fn is_x11(&self) -> bool {
!self.event_loop.is_wayland()
} }
} }
@@ -121,7 +115,7 @@ pub trait EventLoopBuilderExtX11 {
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self; fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
} }
impl EventLoopBuilderExtX11 for EventLoopBuilder { impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> {
#[inline] #[inline]
fn with_x11(&mut self) -> &mut Self { fn with_x11(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X); self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X);
@@ -136,11 +130,9 @@ impl EventLoopBuilderExtX11 for EventLoopBuilder {
} }
/// Additional methods on [`Window`] that are specific to X11. /// Additional methods on [`Window`] that are specific to X11.
///
/// [`Window`]: crate::window::Window
pub trait WindowExtX11 {} pub trait WindowExtX11 {}
impl WindowExtX11 for dyn CoreWindow {} impl WindowExtX11 for Window {}
/// Additional methods on [`WindowAttributes`] that are specific to X11. /// Additional methods on [`WindowAttributes`] that are specific to X11.
pub trait WindowAttributesExtX11 { pub trait WindowAttributesExtX11 {
@@ -152,8 +144,7 @@ pub trait WindowAttributesExtX11 {
/// Build window with the given `general` and `instance` names. /// Build window with the given `general` and `instance` names.
/// ///
/// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the /// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the
/// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "instance", /// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "instance", "general"`.
/// "general"`.
/// ///
/// For details about application ID conventions, see the /// For details about application ID conventions, see the
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id) /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
@@ -169,13 +160,13 @@ pub trait WindowAttributesExtX11 {
/// ///
/// ``` /// ```
/// # use winit::dpi::{LogicalSize, PhysicalSize}; /// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::window::{Window, WindowAttributes}; /// # use winit::window::Window;
/// # use winit::platform::x11::WindowAttributesExtX11; /// # use winit::platform::x11::WindowAttributesExtX11;
/// // Specify the size in logical dimensions like this: /// // Specify the size in logical dimensions like this:
/// WindowAttributes::default().with_base_size(LogicalSize::new(400.0, 200.0)); /// Window::default_attributes().with_base_size(LogicalSize::new(400.0, 200.0));
/// ///
/// // Or specify the size in physical dimensions like this: /// // Or specify the size in physical dimensions like this:
/// WindowAttributes::default().with_base_size(PhysicalSize::new(400, 200)); /// Window::default_attributes().with_base_size(PhysicalSize::new(400, 200));
/// ``` /// ```
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self; fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
@@ -184,12 +175,12 @@ pub trait WindowAttributesExtX11 {
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
/// use winit::window::{Window, WindowAttributes}; /// use winit::window::Window;
/// use winit::event_loop::ActiveEventLoop; /// use winit::event_loop::ActiveEventLoop;
/// use winit::platform::x11::{XWindow, WindowAttributesExtX11}; /// use winit::platform::x11::{XWindow, WindowAttributesExtX11};
/// # fn create_window(event_loop: &dyn ActiveEventLoop) -> Result<(), Box<dyn std::error::Error>> { /// # fn create_window(event_loop: &ActiveEventLoop) -> Result<(), Box<dyn std::error::Error>> {
/// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?; /// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?;
/// let window_attributes = WindowAttributes::default().with_embed_parent_window(parent_window_id); /// let window_attributes = Window::default_attributes().with_embed_parent_window(parent_window_id);
/// let window = event_loop.create_window(window_attributes)?; /// let window = event_loop.create_window(window_attributes)?;
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
@@ -211,8 +202,10 @@ impl WindowAttributesExtX11 for WindowAttributes {
#[inline] #[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self { fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.platform_specific.name = self.platform_specific.name = Some(crate::platform_impl::ApplicationName::new(
Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into())); general.into(),
instance.into(),
));
self self
} }

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,89 +0,0 @@
#![allow(clippy::unnecessary_cast)]
use std::rc::Rc;
use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
use objc2_foundation::{MainThreadMarker, NSObject};
use super::app_state::AppState;
use crate::event::{DeviceEvent, ElementState};
declare_class!(
pub(super) struct WinitApplication;
unsafe impl ClassType for WinitApplication {
#[inherits(NSResponder, NSObject)]
type Super = NSApplication;
type Mutability = mutability::MainThreadOnly;
const NAME: &'static str = "WinitApplication";
}
impl DeclaredClass for WinitApplication {}
unsafe impl WinitApplication {
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
#[method(sendEvent:)]
fn send_event(&self, event: &NSEvent) {
// For posterity, there are some undocumented event types
// (https://github.com/servo/cocoa-rs/issues/155)
// but that doesn't really matter here.
let event_type = unsafe { event.r#type() };
let modifier_flags = unsafe { event.modifierFlags() };
if event_type == NSEventType::KeyUp
&& modifier_flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand)
{
if let Some(key_window) = self.keyWindow() {
key_window.sendEvent(event);
}
} else {
let app_state = AppState::get(MainThreadMarker::from(self));
maybe_dispatch_device_event(&app_state, event);
unsafe { msg_send![super(self), sendEvent: event] }
}
}
}
);
fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
let event_type = unsafe { event.r#type() };
#[allow(non_upper_case_globals)]
match event_type {
NSEventType::MouseMoved
| NSEventType::LeftMouseDragged
| NSEventType::OtherMouseDragged
| NSEventType::RightMouseDragged => {
let delta_x = unsafe { event.deltaX() } as f64;
let delta_y = unsafe { event.deltaY() } as f64;
if delta_x != 0.0 || delta_y != 0.0 {
app_state.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, None, DeviceEvent::PointerMotion {
delta: (delta_x, delta_y),
});
});
}
},
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
let button = unsafe { event.buttonNumber() } as u32;
app_state.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, None, DeviceEvent::Button {
button,
state: ElementState::Pressed,
});
});
},
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
let button = unsafe { event.buttonNumber() } as u32;
app_state.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, None, DeviceEvent::Button {
button,
state: ElementState::Released,
});
});
},
_ => (),
}
}

View File

@@ -1,407 +0,0 @@
use std::cell::{Cell, OnceCell, RefCell};
use std::mem;
use std::rc::{Rc, Weak};
use std::sync::atomic::Ordering as AtomicOrdering;
use std::sync::Arc;
use std::time::Instant;
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningApplication};
use objc2_foundation::{MainThreadMarker, NSNotification};
use super::super::event_handler::EventHandler;
use super::event_loop::{stop_app_immediately, EventLoopProxy, PanicInfo};
use super::menu;
use super::observer::{EventLoopWaker, RunLoop};
use crate::application::ApplicationHandler;
use crate::event::{StartCause, WindowEvent};
use crate::event_loop::{ActiveEventLoop, ControlFlow};
use crate::window::WindowId;
#[derive(Debug)]
pub(super) struct AppState {
mtm: MainThreadMarker,
activation_policy: Option<NSApplicationActivationPolicy>,
default_menu: bool,
activate_ignoring_other_apps: bool,
run_loop: RunLoop,
event_loop_proxy: Arc<EventLoopProxy>,
event_handler: EventHandler,
stop_on_launch: Cell<bool>,
stop_before_wait: Cell<bool>,
stop_after_wait: Cell<bool>,
stop_on_redraw: Cell<bool>,
/// Whether `applicationDidFinishLaunching:` has been run or not.
is_launched: Cell<bool>,
/// Whether an `EventLoop` is currently running.
is_running: Cell<bool>,
/// Whether the user has requested the event loop to exit.
exit: Cell<bool>,
control_flow: Cell<ControlFlow>,
waker: RefCell<EventLoopWaker>,
start_time: Cell<Option<Instant>>,
wait_timeout: Cell<Option<Instant>>,
pending_redraw: RefCell<Vec<WindowId>>,
// NOTE: This is strongly referenced by our `NSWindowDelegate` and our `NSView` subclass, and
// as such should be careful to not add fields that, in turn, strongly reference those.
}
// TODO(madsmtm): Use `MainThreadBound` once that is possible in `static`s.
struct StaticMainThreadBound<T>(T);
impl<T> StaticMainThreadBound<T> {
const fn get(&self, _mtm: MainThreadMarker) -> &T {
&self.0
}
}
unsafe impl<T> Send for StaticMainThreadBound<T> {}
unsafe impl<T> Sync for StaticMainThreadBound<T> {}
// SAFETY: Creating `StaticMainThreadBound` in a `const` context, where there is no concept of the
// main thread.
static GLOBAL: StaticMainThreadBound<OnceCell<Rc<AppState>>> =
StaticMainThreadBound(OnceCell::new());
impl AppState {
pub(super) fn setup_global(
mtm: MainThreadMarker,
activation_policy: Option<NSApplicationActivationPolicy>,
default_menu: bool,
activate_ignoring_other_apps: bool,
) -> Rc<Self> {
let this = Rc::new(AppState {
mtm,
activation_policy,
event_loop_proxy: Arc::new(EventLoopProxy::new()),
default_menu,
activate_ignoring_other_apps,
run_loop: RunLoop::main(mtm),
event_handler: EventHandler::new(),
stop_on_launch: Cell::new(false),
stop_before_wait: Cell::new(false),
stop_after_wait: Cell::new(false),
stop_on_redraw: Cell::new(false),
is_launched: Cell::new(false),
is_running: Cell::new(false),
exit: Cell::new(false),
control_flow: Cell::new(ControlFlow::default()),
waker: RefCell::new(EventLoopWaker::new()),
start_time: Cell::new(None),
wait_timeout: Cell::new(None),
pending_redraw: RefCell::new(vec![]),
});
GLOBAL.get(mtm).set(this.clone()).expect("application state can only be set once");
this
}
pub fn get(mtm: MainThreadMarker) -> Rc<Self> {
GLOBAL
.get(mtm)
.get()
.expect("tried to get application state before it was registered")
.clone()
}
// NOTE: This notification will, globally, only be emitted once,
// no matter how many `EventLoop`s the user creates.
pub fn did_finish_launching(self: &Rc<Self>, _notification: &NSNotification) {
trace_scope!("NSApplicationDidFinishLaunchingNotification");
self.is_launched.set(true);
let app = NSApplication::sharedApplication(self.mtm);
// We need to delay setting the activation policy and activating the app
// until `applicationDidFinishLaunching` has been called. Otherwise the
// menu bar is initially unresponsive on macOS 10.15.
if let Some(activation_policy) = self.activation_policy {
app.setActivationPolicy(activation_policy);
} else {
// If no activation policy is explicitly provided, and the application
// is bundled, do not set the activation policy at all, to allow the
// package manifest to define the behavior via LSUIElement.
//
// See:
// - https://github.com/rust-windowing/winit/issues/261
// - https://github.com/rust-windowing/winit/issues/3958
let is_bundled =
unsafe { NSRunningApplication::currentApplication().bundleIdentifier().is_some() };
if !is_bundled {
app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
}
}
#[allow(deprecated)]
app.activateIgnoringOtherApps(self.activate_ignoring_other_apps);
if self.default_menu {
// The menubar initialization should be before the `NewEvents` event, to allow
// overriding of the default menu even if it's created
menu::initialize(&app);
}
self.waker.borrow_mut().start();
self.set_is_running(true);
self.dispatch_init_events();
// If the application is being launched via `EventLoop::pump_app_events()` then we'll
// want to stop the app once it is launched (and return to the external loop)
//
// In this case we still want to consider Winit's `EventLoop` to be "running",
// so we call `start_running()` above.
if self.stop_on_launch.get() {
// NOTE: the original idea had been to only stop the underlying `RunLoop`
// for the app but that didn't work as expected (`-[NSApplication run]`
// effectively ignored the attempt to stop the RunLoop and re-started it).
//
// So we return from `pump_events` by stopping the application.
let app = NSApplication::sharedApplication(self.mtm);
stop_app_immediately(&app);
}
}
pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) {
trace_scope!("NSApplicationWillTerminateNotification");
// TODO: Notify every window that it will be destroyed, like done in iOS?
self.with_handler(|app, event_loop| {
app.exiting(event_loop);
});
self.internal_exit();
}
/// Place the event handler in the application state for the duration
/// of the given closure.
pub fn set_init_closure<A: ApplicationHandler, R>(
&self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
closure: impl FnOnce() -> R,
) -> R {
let init_closure = Box::new(
|active_event_loop: &'_ dyn ActiveEventLoop| -> Box<dyn ApplicationHandler + '_> {
Box::new(init_closure(active_event_loop))
},
);
self.event_handler.set(init_closure, closure)
}
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.event_loop_proxy
}
/// If `pump_events` is called to progress the event loop then we
/// bootstrap the event loop via `-[NSApplication run]` but will use
/// `CFRunLoopRunInMode` for subsequent calls to `pump_events`.
pub fn set_stop_on_launch(&self) {
self.stop_on_launch.set(true);
}
pub fn set_stop_before_wait(&self, value: bool) {
self.stop_before_wait.set(value)
}
pub fn set_stop_after_wait(&self, value: bool) {
self.stop_after_wait.set(value)
}
pub fn set_stop_on_redraw(&self, value: bool) {
self.stop_on_redraw.set(value)
}
pub fn set_wait_timeout(&self, value: Option<Instant>) {
self.wait_timeout.set(value)
}
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits.
///
/// NOTE: that if the `NSApplication` has been launched then that state is preserved,
/// and we won't need to re-launch the app if subsequent EventLoops are run.
pub fn internal_exit(self: &Rc<Self>) {
self.set_is_running(false);
self.set_stop_on_redraw(false);
self.set_stop_before_wait(false);
self.set_stop_after_wait(false);
self.set_wait_timeout(None);
}
pub fn is_launched(&self) -> bool {
self.is_launched.get()
}
pub fn set_is_running(&self, value: bool) {
self.is_running.set(value)
}
pub fn is_running(&self) -> bool {
self.is_running.get()
}
pub fn exit(&self) {
self.exit.set(true)
}
pub fn clear_exit(&self) {
self.exit.set(false)
}
pub fn exiting(&self) -> bool {
self.exit.get()
}
pub fn set_control_flow(&self, value: ControlFlow) {
self.control_flow.set(value)
}
pub fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}
pub fn handle_redraw(self: &Rc<Self>, window_id: WindowId) {
// Redraw request might come out of order from the OS.
// -> Don't go back into the event handler when our callstack originates from there
if !self.event_handler.in_use() {
self.with_handler(|app, event_loop| {
app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
});
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
// events as a way to ensure that `pump_events` can't block an external loop
// indefinitely
if self.stop_on_redraw.get() {
let app = NSApplication::sharedApplication(self.mtm);
stop_app_immediately(&app);
}
}
}
pub fn queue_redraw(&self, window_id: WindowId) {
let mut pending_redraw = self.pending_redraw.borrow_mut();
if !pending_redraw.contains(&window_id) {
pending_redraw.push(window_id);
}
self.run_loop.wakeup();
}
#[track_caller]
pub fn maybe_queue_with_handler(
self: &Rc<Self>,
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop) + 'static,
) {
// Most programmer actions in AppKit (e.g. change window fullscreen, set focused, etc.)
// result in an event being queued, and applied at a later point.
//
// However, it is not documented which actions do this, and which ones are done immediately,
// so to make sure that we don't encounter re-entrancy issues, we first check if we're
// currently handling another event, and if we are, we queue the event instead.
if !self.event_handler.in_use() {
self.with_handler(callback);
} else {
tracing::debug!("had to queue event since another is currently being handled");
let this = Rc::clone(self);
self.run_loop.queue_closure(move || {
this.with_handler(callback);
});
}
}
#[track_caller]
fn with_handler(
self: &Rc<Self>,
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop),
) {
let event_loop = ActiveEventLoop { app_state: Rc::clone(self), mtm: self.mtm };
self.event_handler.handle(|app| callback(app, &event_loop));
}
/// dispatch `NewEvents(Init)` + `Resumed`
pub fn dispatch_init_events(self: &Rc<Self>) {
self.with_handler(|app, event_loop| app.new_events(event_loop, StartCause::Init));
// NB: For consistency all platforms must call `can_create_surfaces` even though macOS
// applications don't themselves have a formal surface destroy/create lifecycle.
self.with_handler(|app, event_loop| app.can_create_surfaces(event_loop));
}
// Called by RunLoopObserver after finishing waiting for new events
pub fn wakeup(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
let panic_info = panic_info
.upgrade()
.expect("The panic info must exist here. This failure indicates a developer error.");
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
return;
}
if self.stop_after_wait.get() {
let app = NSApplication::sharedApplication(self.mtm);
stop_app_immediately(&app);
}
let start = self.start_time.get().unwrap();
let cause = match self.control_flow() {
ControlFlow::Poll => StartCause::Poll,
ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
ControlFlow::WaitUntil(requested_resume) => {
if Instant::now() >= requested_resume {
StartCause::ResumeTimeReached { start, requested_resume }
} else {
StartCause::WaitCancelled { start, requested_resume: Some(requested_resume) }
}
},
};
self.with_handler(|app, event_loop| app.new_events(event_loop, cause));
}
// Called by RunLoopObserver before waiting for new events
pub fn cleared(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
let panic_info = panic_info
.upgrade()
.expect("The panic info must exist here. This failure indicates a developer error.");
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
// XXX: how does it make sense that `event_handler.ready()` can ever return `false` here if
// we're about to return to the `CFRunLoop` to poll for new events?
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
return;
}
if self.event_loop_proxy.wake_up.swap(false, AtomicOrdering::Relaxed) {
self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
}
let redraw = mem::take(&mut *self.pending_redraw.borrow_mut());
for window_id in redraw {
self.with_handler(|app, event_loop| {
app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
});
}
self.with_handler(|app, event_loop| {
app.about_to_wait(event_loop);
});
if self.exiting() {
let app = NSApplication::sharedApplication(self.mtm);
stop_app_immediately(&app);
}
if self.stop_before_wait.get() {
let app = NSApplication::sharedApplication(self.mtm);
stop_app_immediately(&app);
}
self.start_time.set(Some(Instant::now()));
let wait_timeout = self.wait_timeout.get(); // configured by pump_events
let app_timeout = match self.control_flow() {
ControlFlow::Wait => None,
ControlFlow::Poll => Some(Instant::now()),
ControlFlow::WaitUntil(instant) => Some(instant),
};
self.waker.borrow_mut().start_at(min_timeout(wait_timeout, app_timeout));
}
}
/// Returns the minimum `Option<Instant>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`)
fn min_timeout(a: Option<Instant>, b: Option<Instant>) -> Option<Instant> {
a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
}

View File

@@ -1,497 +0,0 @@
use std::any::Any;
use std::cell::Cell;
use std::os::raw::c_void;
use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe};
use std::ptr;
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use core_foundation::base::{CFIndex, CFRelease};
use core_foundation::runloop::{
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
};
use objc2::rc::{autoreleasepool, Retained};
use objc2::{msg_send_id, sel, ClassType};
use objc2_app_kit::{
NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
NSApplicationWillTerminateNotification, NSWindow,
};
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject, NSObjectProtocol};
use rwh_06::HasDisplayHandle;
use super::super::notification_center::create_observer;
use super::app::WinitApplication;
use super::app_state::AppState;
use super::cursor::CustomCursor;
use super::event::dummy_event;
use super::monitor;
use super::observer::setup_control_flow_observers;
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, RequestError};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform::macos::ActivationPolicy;
use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::Window;
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme};
#[derive(Default)]
pub struct PanicInfo {
inner: Cell<Option<Box<dyn Any + Send + 'static>>>,
}
// WARNING:
// As long as this struct is used through its `impl`, it is UnwindSafe.
// (If `get_mut` is called on `inner`, unwind safety may get broken.)
impl UnwindSafe for PanicInfo {}
impl RefUnwindSafe for PanicInfo {}
impl PanicInfo {
pub fn is_panicking(&self) -> bool {
let inner = self.inner.take();
let result = inner.is_some();
self.inner.set(inner);
result
}
/// Overwrites the current state if the current state is not panicking
pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) {
if !self.is_panicking() {
self.inner.set(Some(p));
}
}
pub fn take(&self) -> Option<Box<dyn Any + Send + 'static>> {
self.inner.take()
}
}
#[derive(Debug)]
pub struct ActiveEventLoop {
pub(super) app_state: Rc<AppState>,
pub(super) mtm: MainThreadMarker,
}
impl ActiveEventLoop {
pub(crate) fn hide_application(&self) {
NSApplication::sharedApplication(self.mtm).hide(None)
}
pub(crate) fn hide_other_applications(&self) {
NSApplication::sharedApplication(self.mtm).hideOtherApplications(None)
}
pub(crate) fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
NSWindow::setAllowsAutomaticWindowTabbing(enabled, self.mtm)
}
pub(crate) fn allows_automatic_window_tabbing(&self) -> bool {
NSWindow::allowsAutomaticWindowTabbing(self.mtm)
}
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(self.app_state.event_loop_proxy().clone())
}
fn create_window(
&self,
window_attributes: crate::window::WindowAttributes,
) -> Result<Box<dyn crate::window::Window>, RequestError> {
Ok(Box::new(Window::new(self, window_attributes)?))
}
fn create_custom_cursor(
&self,
source: CustomCursorSource,
) -> Result<RootCustomCursor, RequestError> {
Ok(RootCustomCursor { inner: CustomCursor::new(source.inner)? })
}
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
Box::new(monitor::available_monitors().into_iter().map(|inner| RootMonitorHandle { inner }))
}
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
let monitor = monitor::primary_monitor();
Some(RootMonitorHandle { inner: monitor })
}
fn listen_device_events(&self, _allowed: DeviceEvents) {}
fn system_theme(&self) -> Option<Theme> {
let app = NSApplication::sharedApplication(self.mtm);
if app.respondsToSelector(sel!(effectiveAppearance)) {
Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
} else {
Some(Theme::Light)
}
}
fn set_control_flow(&self, control_flow: ControlFlow) {
self.app_state.set_control_flow(control_flow)
}
fn control_flow(&self) -> ControlFlow {
self.app_state.control_flow()
}
fn exit(&self) {
self.app_state.exit()
}
fn exiting(&self) -> bool {
self.app_state.exiting()
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
}
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
}
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}
pub struct EventLoop {
/// Store a reference to the application for convenience.
///
/// We intentionally don't store `WinitApplication` since we want to have
/// the possibility of swapping that out at some point.
app: Retained<NSApplication>,
app_state: Rc<AppState>,
window_target: ActiveEventLoop,
panic_info: Rc<PanicInfo>,
// Since macOS 10.11, we no longer need to remove the observers before they are deallocated;
// the system instead cleans it up next time it would have posted a notification to it.
//
// Though we do still need to keep the observers around to prevent them from being deallocated.
_did_finish_launching_observer: Retained<NSObject>,
_will_terminate_observer: Retained<NSObject>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct PlatformSpecificEventLoopAttributes {
pub(crate) activation_policy: Option<ActivationPolicy>,
pub(crate) default_menu: bool,
pub(crate) activate_ignoring_other_apps: bool,
}
impl Default for PlatformSpecificEventLoopAttributes {
fn default() -> Self {
Self { activation_policy: None, default_menu: true, activate_ignoring_other_apps: true }
}
}
impl EventLoop {
pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes,
) -> Result<Self, EventLoopError> {
let mtm = MainThreadMarker::new()
.expect("on macOS, `EventLoop` must be created on the main thread!");
let app: Retained<NSApplication> =
unsafe { msg_send_id![WinitApplication::class(), sharedApplication] };
if !app.is_kind_of::<WinitApplication>() {
panic!(
"`winit` requires control over the principal class. You must create the event \
loop before other parts of your application initialize NSApplication"
);
}
let activation_policy = match attributes.activation_policy {
None => None,
Some(ActivationPolicy::Regular) => Some(NSApplicationActivationPolicy::Regular),
Some(ActivationPolicy::Accessory) => Some(NSApplicationActivationPolicy::Accessory),
Some(ActivationPolicy::Prohibited) => Some(NSApplicationActivationPolicy::Prohibited),
};
let app_state = AppState::setup_global(
mtm,
activation_policy,
attributes.default_menu,
attributes.activate_ignoring_other_apps,
);
let center = unsafe { NSNotificationCenter::defaultCenter() };
let weak_app_state = Rc::downgrade(&app_state);
let _did_finish_launching_observer = create_observer(
&center,
// `applicationDidFinishLaunching:`
unsafe { NSApplicationDidFinishLaunchingNotification },
move |notification| {
if let Some(app_state) = weak_app_state.upgrade() {
app_state.did_finish_launching(notification);
}
},
);
let weak_app_state = Rc::downgrade(&app_state);
let _will_terminate_observer = create_observer(
&center,
// `applicationWillTerminate:`
unsafe { NSApplicationWillTerminateNotification },
move |notification| {
if let Some(app_state) = weak_app_state.upgrade() {
app_state.will_terminate(notification);
}
},
);
let panic_info: Rc<PanicInfo> = Default::default();
setup_control_flow_observers(mtm, Rc::downgrade(&panic_info));
Ok(EventLoop {
app,
app_state: app_state.clone(),
window_target: ActiveEventLoop { app_state, mtm },
panic_info,
_did_finish_launching_observer,
_will_terminate_observer,
})
}
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
&self.window_target
}
pub fn run<A: ApplicationHandler>(
mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
}
// NB: we don't base this on `pump_events` because for `MacOs` we can't support
// `pump_events` elegantly (we just ask to run the loop for a "short" amount of
// time and so a layered implementation would end up using a lot of CPU due to
// redundant wake ups.
pub fn run_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.app_state.clear_exit();
self.app_state.set_init_closure(init_closure, || {
autoreleasepool(|_| {
// clear / normalize pump_events state
self.app_state.set_wait_timeout(None);
self.app_state.set_stop_before_wait(false);
self.app_state.set_stop_after_wait(false);
self.app_state.set_stop_on_redraw(false);
if self.app_state.is_launched() {
debug_assert!(!self.app_state.is_running());
self.app_state.set_is_running(true);
self.app_state.dispatch_init_events();
}
// SAFETY: We do not run the application re-entrantly
unsafe { self.app.run() };
// While the app is running it's possible that we catch a panic
// to avoid unwinding across an objective-c ffi boundary, which
// will lead to us stopping the `NSApplication` and saving the
// `PanicInfo` so that we can resume the unwind at a controlled,
// safe point in time.
if let Some(panic) = self.panic_info.take() {
resume_unwind(panic);
}
self.app_state.internal_exit()
})
});
Ok(())
}
pub fn pump_app_events<A: ApplicationHandler>(
&mut self,
timeout: Option<Duration>,
mut app: A,
) -> PumpStatus {
self.app_state.set_event_handler(&mut app, || {
autoreleasepool(|_| {
// As a special case, if the application hasn't been launched yet then we at least
// run the loop until it has fully launched.
if !self.app_state.is_launched() {
debug_assert!(!self.app_state.is_running());
self.app_state.set_stop_on_launch();
// SAFETY: We do not run the application re-entrantly
unsafe { self.app.run() };
// Note: we dispatch `NewEvents(Init)` + `Resumed` events after the application
// has launched
} else if !self.app_state.is_running() {
// Even though the application may have been launched, it's possible we aren't
// running if the `EventLoop` was run before and has since
// exited. This indicates that we just starting to re-run
// the same `EventLoop` again.
self.app_state.set_is_running(true);
self.app_state.dispatch_init_events();
} else {
// Only run for as long as the given `Duration` allows so we don't block the
// external loop.
match timeout {
Some(Duration::ZERO) => {
self.app_state.set_wait_timeout(None);
self.app_state.set_stop_before_wait(true);
},
Some(duration) => {
self.app_state.set_stop_before_wait(false);
let timeout = Instant::now() + duration;
self.app_state.set_wait_timeout(Some(timeout));
self.app_state.set_stop_after_wait(true);
},
None => {
self.app_state.set_wait_timeout(None);
self.app_state.set_stop_before_wait(false);
self.app_state.set_stop_after_wait(true);
},
}
self.app_state.set_stop_on_redraw(true);
// SAFETY: We do not run the application re-entrantly
unsafe { self.app.run() };
}
// While the app is running it's possible that we catch a panic
// to avoid unwinding across an objective-c ffi boundary, which
// will lead to us stopping the application and saving the
// `PanicInfo` so that we can resume the unwind at a controlled,
// safe point in time.
if let Some(panic) = self.panic_info.take() {
resume_unwind(panic);
}
if self.app_state.exiting() {
self.app_state.internal_exit();
PumpStatus::Exit(0)
} else {
PumpStatus::Continue
}
})
})
}
}
pub(crate) struct OwnedDisplayHandle;
impl HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}
pub(super) fn stop_app_immediately(app: &NSApplication) {
autoreleasepool(|_| {
app.stop(None);
// To stop event loop immediately, we need to post some event here.
// See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752
app.postEvent_atStart(&dummy_event().unwrap(), true);
});
}
/// Catches panics that happen inside `f` and when a panic
/// happens, stops the `sharedApplication`
#[inline]
pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
mtm: MainThreadMarker,
panic_info: Weak<PanicInfo>,
f: F,
) -> Option<R> {
match catch_unwind(f) {
Ok(r) => Some(r),
Err(e) => {
// It's important that we set the panic before requesting a `stop`
// because some callback are still called during the `stop` message
// and we need to know in those callbacks if the application is currently
// panicking
{
let panic_info = panic_info.upgrade().unwrap();
panic_info.set_panic(e);
}
let app = NSApplication::sharedApplication(mtm);
stop_app_immediately(&app);
None
},
}
}
#[derive(Debug)]
pub struct EventLoopProxy {
pub(crate) wake_up: AtomicBool,
source: CFRunLoopSourceRef,
}
unsafe impl Send for EventLoopProxy {}
unsafe impl Sync for EventLoopProxy {}
impl Drop for EventLoopProxy {
fn drop(&mut self) {
unsafe {
CFRelease(self.source as _);
}
}
}
impl EventLoopProxy {
pub(crate) fn new() -> Self {
unsafe {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
// adding a Source to the main CFRunLoop lets us wake it up and
// process user events through the normal OS EventLoop mechanisms.
let rl = CFRunLoopGetMain();
let mut context = CFRunLoopSourceContext {
version: 0,
info: ptr::null_mut(),
retain: None,
release: None,
copyDescription: None,
equal: None,
hash: None,
schedule: None,
cancel: None,
perform: event_loop_proxy_handler,
};
let source = CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::MAX - 1, &mut context);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);
EventLoopProxy { wake_up: AtomicBool::new(false), source }
}
}
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.wake_up.store(true, AtomicOrdering::Relaxed);
unsafe {
// Let the main thread know there's a new event.
CFRunLoopSourceSignal(self.source);
let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl);
}
}
}

View File

@@ -1,27 +0,0 @@
#[macro_use]
mod util;
mod app;
mod app_state;
mod cursor;
mod event;
mod event_loop;
mod ffi;
mod menu;
mod monitor;
mod observer;
mod view;
mod window;
mod window_delegate;
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey, KeyEventExtra};
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::Window;
pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;

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