Compare commits

..

67 Commits

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: John Nunley <dev@notgull.net>
Closes: https://github.com/rust-windowing/winit/issues/3558
2024-03-04 20:49:04 +04:00
daxpedda
388c40b1e0 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-03-02 13:22:36 +01:00
daxpedda
7a40aa43dc Web: fix crash with ControlFlow::Wait|WaitUntil 2024-03-02 12:26:26 +01:00
John Nunley
944347696a Replace log with tracing
Tracing is a modern replacement for the log crate that allows for
annotating log messages with the function that they come from.

Signed-off-by: John Nunley <dev@notgull.net>
Closes: #3482
2024-03-01 20:45:31 +04:00
Kirill Chibisov
96172693fe Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-03-01 15:21:06 +04:00
Mads Marquart
32004405ee Add documentation example of ignoring key repeats (#3538) 2024-03-01 11:27:47 +01:00
Kirill Chibisov
22e932b5ab On X11, fix use after free during xinput2 processing
Fixes #3536.
2024-03-01 13:40:20 +04:00
John Nunley
3d4c53459a On X11, filter out tiny device mouse events
Usually, if mouse events are equal to (0, 0) we filter them out.
However, if the event is very close to zero it will still be given to
the user. In some cases this can be caused by bad float math on the X11
server side.

Fix it by filtering absolute values smaller than floating point epsilon.

Signed-off-by: John Nunley <dev@notgull.net>
Closes: #3500
2024-03-01 13:11:28 +04:00
Kirill Chibisov
f0b4889227 Add LICENSE file to dpi crate
While the license was set to follow workspace the file itself should
be present to make package shippable in e.g. Debian/Fedora.
2024-02-28 23:13:14 +04:00
Kirill Chibisov
c81448e0b8 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-02-28 22:54:37 +04:00
Dimitris Apostolou
f498ff0369 Fix feature = "cargo-clippy" deprecation 2024-02-28 21:50:05 +04:00
Mads Marquart
c4e6e94b80 Remove a few unnecessary usages of Lazy (#3531)
* Convert usage of Lazy to OnceLock on macOS and iOS

* Remove a few uses of Lazy that wrapped Mutex or RwLock

The `new` functions on these were made `const` in Rust 1.63.0

* Use AtomicBool instead of RwLock
2024-02-28 12:28:26 +01:00
Mads Marquart
a5dbd3ee52 macOS: Refactor event handler storage (#3532)
This makes our use of `unsafe` to make the event handler temporarily 'static be local to a module, in a way that's (hopefully) much easier to reason about.
2024-02-28 04:33:47 +01:00
Mads Marquart
a4480a0652 Clean up iOS ffi.rs (#3530)
This makes it easier to transition to a future autogenerated version of UIKit.
2024-02-27 21:07:52 +01:00
Mads Marquart
e41f0eabb1 Split dpi module out into a separate crate (#3518)
Co-authored-by: John Nunley <dev@notgull.net>
2024-02-26 14:52:00 +01:00
Kirill Chibisov
7e28d7615e On X11, replay modifiers consumed by XIM 2024-02-26 12:59:41 +04:00
daxpedda
010787a430 Fix nightly CI (#3526) 2024-02-26 09:46:12 +01:00
James Liu
352e70b8ac m: Remove once_cell dependency
Removes the once_cell dependency, instead using std::sync::OnceLock and a
minimal polyfill for std::sync::LazyLock, which may be stabilized soon
(see rust-lang/rust#121377).

This should not require a bump in MSRV, as OnceLock was stabilized in 1.70,
which this crate is using.
2024-02-25 08:19:27 -08:00
Friz64
990bbf178f Update documentation regarding set_cursor_position (#3521) 2024-02-24 23:15:38 +01:00
Mads Marquart
97cfdd4b09 Move some of our documentation to docs.rs (#3478)
* Move platform-specific documentation to `winit::platform` module

* Document cargo features in crate docs

* Move version requirements to crate-level docs
2024-02-23 15:35:18 +01:00
Mads Marquart
a127bd6f66 iOS: Split classes in view.rs into separate files (#3511)
* Move application delegate to its own file
* Move window subclass to window.rs
* Split view controller to separate file
2024-02-22 22:28:49 +01:00
Kirill Chibisov
4a8648be57 On X11, force resend modifiers when focus changes
Given that `ModifiersChanged` is a window event, it means that clients
may track it for each window individually, thus not sending it between
focus changes may result in modifiers getting desynced on the consumer
side.
2024-02-22 08:30:39 +04:00
Kirill Chibisov
a8c7109e89 Pin bumpalo to 3.14.0 on CI 2024-02-21 14:44:29 +04:00
Kirill Chibisov
7abd427216 Create custom cursor with directly with event loop
Replace the `CustomCursorBuilder` with the `CustomCursorSource` and
perform the loading of the cursor via the
`EventLoop::create_custom_cursor` instead of passing it to the builder
itself.

This follows the `EventLoop::create_window` API.
2024-02-21 14:44:29 +04:00
Kirill Chibisov
3fb93b4f83 Deprecate window creation with stale event loop
Creating window when event loop is not running generally doesn't work,
since a bunch of events and sync OS requests can't be processed. This
is also an issue on e.g. Android, since window can't be created outside
event loop easily.

Thus deprecate the window creation when event loop is not running,
as well as other resource creation to running event loop.

Given that all the examples use the bad pattern of creating the window
when event loop is not running and also most example existence is
questionable, since they show single thing and the majority of their
code is window/event loop initialization, they wore merged into
a single example 'window.rs' example that showcases very simple
application using winit.

Fixes #3399.
2024-02-21 14:44:29 +04:00
Andriy
19190a95a0 On Wayland, send DeviceEvent::Motion 2024-02-20 13:34:05 +04:00
Bruce Mitchener
c4310af83c Fix various typos
Mainly fix typos in comments, but also some minor code changes:

* Rename `apply_on_poiner` to `apply_on_pointer`.
* Rename `ImeState::Commited` to `ImeState::Committed`
* Correct `cfg_attr` usage: `wayland_platfrom` -> `wayland_platform`.
2024-02-19 08:58:44 +04:00
Kirill Chibisov
542d1938ce Fix warnings with latest nightly 2024-02-19 08:47:32 +04:00
Mads Marquart
31f8b816bd Make EventLoopProxy Sync
Co-authored-by: daxpedda <daxpedda@gmail.com>
Closes: #3448
2024-02-19 08:47:32 +04:00
Kirill Chibisov
e61a7320a2 On X11, use events modifiers to detect state
While there's a separate event to deliver modifiers for keyboard,
unfortunately, it's not even remotely reflects the modifiers state.

Thus use events along side regular modifier updates to correctly
detect the state. Also, apply the modifiers from the regular
key event by converting their state to xkb modifiers state.

Links: https://github.com/alacritty/alacritty/issues/7549
Closes: #3388
2024-02-18 01:39:42 +04:00
Mads Marquart
db41938deb Remove Testers and Contributors table (#3497) 2024-02-17 09:54:29 +01:00
Mads Marquart
2bc1943188 Pin ahash in CI (#3498)
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2024-02-16 17:51:48 +01:00
Kirill Chibisov
385c4b3c88 On Wayland, update title from AboutToWait
Fixes #3472.
2024-02-14 00:48:52 +04:00
Kirill Chibisov
ea70f773d3 On X11, don't require XIM to be present
In general, we may want to use xinput v2 for keyboard input in such
cases, so we have compose going, but for now just don't crash if
there's no XIM.
2024-02-13 07:49:58 +04:00
AryaveerSR
83012f4c1c Fix Documentation for ScaleFactorChanged in WindowEvent. (#3489) 2024-02-12 18:20:03 +01:00
Jeremy Soller
6825fed073 On Orbital, implement various Window methods
Implement the following methods on the `Window`:
  - `Window::set_cursor_grab`.
  - `Window::set_cursor_visible`.
  - `Window::drag_window`.
  - `Window::drag_resize_window`.
  - `Window::set_transparent`.
  - `Window::set_visible`.
  - `Window::is_visible`.
  - `Window::set_resizable`.
  - `Window::is_resizable`.
  - `Window::set_maximized`.
  - `Window::is_maximized`.
  - `Window::set_decorations`.
  - `Window::is_decorated`.
  - `Window::set_window_level`.

To make locked pointer useful, the `DeviceEvent::MouseMotion`
event was also implemented.
2024-02-11 04:40:06 +04:00
Kirill Chibisov
273984a385 On X11, extract event handlers
Make code more clear wrt explicit returns during event handling,
which may lead to skipped IME event handling.
2024-02-11 03:31:47 +04:00
Kirill Chibisov
dbe0f852da On X11, store window target on EventProcessor
Remove the redundant `Rc` to access the window target.
2024-02-11 03:31:47 +04:00
Kirill Chibisov
d1902aa15a On X11, don't require XSETTINGS
We could fail to setup property watcher and fail to start, thus
don't require XSETTINGS to work.

Fixes: df8805c0 (On X11, reload DPI on _XSETTINGS_SETTINGS)
2024-02-10 00:24:03 +04:00
Mads Marquart
4112fccc12 Update meeting time (#3475) 2024-02-09 17:20:46 +01:00
Jeremy Soller
bd6ce32860 On Orbital, map keys to NamedKey when possible 2024-02-09 11:28:30 +04:00
Kirill Chibisov
8936fe1acd Fix nightly CI dead_code warnings 2024-02-08 13:28:26 +04:00
Jeremy Soller
fedb86ea5a On Orbital, implement KeyEventExtModifiersSupplement
This also fixes `logical_key` and `text` not reported in `KeyEvent`.
2024-02-08 12:55:11 +04:00
Kirill Chibisov
20687fef1c Fix compatibility with platforms without AtomicU64
Fixes #3456.
2024-02-08 00:58:43 +04:00
316 changed files with 13310 additions and 11438 deletions

View File

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

3
.github/CODEOWNERS vendored
View File

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

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

View File

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

View File

@@ -17,6 +17,24 @@ jobs:
- name: Check Formatting - name: Check Formatting
run: cargo fmt -- --check run: cargo fmt -- --check
typos:
name: Check for typos
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: taiki-e/install-action@v2
with:
tool: typos-cli
- name: run typos
run: typos
- name: Typos info
if: failure()
run: |
echo 'To fix typos, please run `typos -w`'
echo 'To check for a diff, run `typos`'
echo 'You can find typos here: https://crates.io/crates/typos'
tests: tests:
name: Test ${{ matrix.toolchain }} ${{ matrix.platform.name }} name: Test ${{ matrix.toolchain }} ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }} runs-on: ${{ matrix.platform.os }}
@@ -35,7 +53,7 @@ jobs:
- { name: 'Linux 64bit', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, } - { name: 'Linux 64bit', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
- { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' } - { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' } - { name: '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: '--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', target: x86_64-apple-darwin, os: macos-latest, } - { name: 'macOS', target: x86_64-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, }
@@ -89,7 +107,7 @@ jobs:
- name: Generate lockfile - name: Generate lockfile
# Also updates the crates.io index # Also updates the crates.io index
run: cargo generate-lockfile run: cargo generate-lockfile && cargo update -p ahash --precise 0.8.7 && cargo update -p bumpalo --precise 3.14.0
- name: Install GCC Multilib - name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686') if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
@@ -125,13 +143,20 @@ jobs:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }}' RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }}'
- name: Build crate - name: Build crate
run: cargo $CMD build -p winit $OPTIONS run: cargo $CMD build $OPTIONS
# Test only on Linux x86_64, so we avoid spending unnecessary CI hours.
- name: Test dpi crate
if: >
contains(matrix.platform.name, 'Linux 64bit') &&
matrix.toolchain != '1.70.0'
run: cargo test -p dpi
- name: Build tests - name: Build tests
if: > if: >
!contains(matrix.platform.target, 'redox') && !contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.70.0' matrix.toolchain != '1.70.0'
run: cargo $CMD test -p winit --no-run $OPTIONS run: cargo $CMD test --no-run $OPTIONS
- name: Run tests - name: Run tests
if: > if: >
@@ -150,7 +175,7 @@ jobs:
if: > if: >
!contains(matrix.platform.target, 'redox') && !contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.70.0' matrix.toolchain != '1.70.0'
run: cargo $CMD test --no-run $OPTIONS -p winit --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: >
@@ -165,7 +190,7 @@ jobs:
if: matrix.toolchain == 'nightly' if: matrix.toolchain == 'nightly'
run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
env: env:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs' RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs --cfg=unreleased_changelogs'
# See restore step above # See restore step above
- name: Save cache of cargo folder - name: Save cache of cargo folder
@@ -177,6 +202,41 @@ jobs:
~/.cargo/git/db/ ~/.cargo/git/db/
key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }} key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}
it:
name: Run integration tests on ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }}
strategy:
fail-fast: false
matrix:
toolchain: [stable, nightly]
platform:
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
- { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
env:
# Set more verbose terminal output
CARGO_TERM_VERBOSE: true
RUST_BACKTRACE: 1
# Faster compilation and error on warnings
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}'
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
- name: Log into GHCR
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
- name: Common tests
run: cargo run -p gui-test-runner -- common-tests ${{ matrix.platform.target }}
cargo-deny: cargo-deny:
name: Run cargo-deny on ${{ matrix.platform.name }} name: Run cargo-deny on ${{ matrix.platform.name }}
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -27,7 +27,7 @@ jobs:
- name: Run Rustdoc - name: Run Rustdoc
env: env:
RUSTDOCFLAGS: --crate-version master --cfg=docsrs RUSTDOCFLAGS: --crate-version master --cfg=docsrs --cfg=unreleased_changelogs
run: | run: |
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity

File diff suppressed because it is too large Load Diff

6
CODE_OF_CONDUCT.md Normal file
View File

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

View File

@@ -26,7 +26,7 @@ When making a code contribution to winit, before opening your pull request, plea
- you updated any relevant documentation in winit - you updated any relevant documentation in winit
- you left comments in your code explaining any part that is not straightforward, so that the - you left comments in your code explaining any part that is not straightforward, so that the
maintainers and future contributors don't have to try to guess what your code is supposed to do maintainers and future contributors don't have to try to guess what your code is supposed to do
- your PR adds an entry to the changelog file if the introduced change is relevant to winit users. - your PR adds an entry to the current changelog if the introduced change is relevant to winit users.
You needn't worry about the added entry causing conflicts, the maintainer that merges the PR will You needn't worry about the added entry causing conflicts, the maintainer that merges the PR will
handle those for you when merging (see below). handle those for you when merging (see below).
@@ -38,34 +38,35 @@ Once your PR is open, you can ask for a review by a maintainer of your platform.
is that a PR must be approved by at least two maintainers of winit before being merged, including is that a PR must be approved by at least two maintainers of winit before being merged, including
at least a maintainer of the platform (a maintainer making a PR themselves counts as approving it). at least a maintainer of the platform (a maintainer making a PR themselves counts as approving it).
Once your PR is deemed ready, the merging maintainer will take care of resolving conflicts in Once your PR is deemed ready, the merging maintainer will take care of resolving conflicts in the
`CHANGELOG.md` (but you must resolve other conflicts yourself). Doing this requires that you check the `changelog` module (but you must resolve other conflicts yourself). Doing this requires that you check the
"give contributors write access to the branch" checkbox when creating the PR. "give contributors write access to the branch" checkbox when creating the PR.
## Maintainers & Testers ## Maintainers
The current maintainers are listed in the [CODEOWNERS](.github/CODEOWNERS) file. The current maintainers for each platform are listed in the [CODEOWNERS](.github/CODEOWNERS) file.
If you are interested in being pinged when testing is needed for a specific platform, please add yourself to the [Testers and Contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors) table!
## Release process ## Release process
Given that winit is a widely used library, we should be able to make a patch Given that winit is a widely used library, we should be able to make a patch
releases at any time we want without blocking the development of new features. releases at any time we want without blocking the development of new features.
To achieve these goals, a new branch is created for every new release. Releases and later patch releases are committed and tagged in this branch. To achieve these goals, a new branch is created for every new release. Releases
and later patch releases are committed and tagged in this branch.
The exact steps for an exemplary `0.2.0` release might look like this: The exact steps for an exemplary `0.2.0` release might look like this:
1. Initially, the version on the latest master is `0.1.0` 1. Initially, the version on the latest master is `0.1.0`
2. A new `v0.2.x` branch is created for the release 2. A new `v0.2.x` branch is created for the release
3. In the branch, the version is bumped to `v0.2.0` 3. Update released `cfg_attr` in `src/changelog/mod.rs` to `v0.2.md`
4. The new commit in the branch is tagged `v0.2.0` 4. Move entries from `src/changelog/unreleased.md` into `src/changelog/v0.2.md`
5. The version is pushed to crates.io 5. In the branch, the version is bumped to `v0.2.0`
6. A GitHub release is created for the `v0.2.0` tag 6. The new commit in the branch is tagged `v0.2.0`
7. On master, the version is bumped to `0.2.0`, and the CHANGELOG is updated 7. The version is pushed to crates.io
8. A GitHub release is created for the `v0.2.0` tag
9. On master, the version is bumped to `0.2.0`, and the changelog is updated
When doing a patch release, the process is similar: When doing a patch release, the process is similar:
1. Initially, the version of the latest release is `0.2.0` 1. Initially, the version of the latest release is `0.2.0`
2. Checkout the `v0.2.x` branch 2. Checkout the `v0.2.x` branch
3. Cherry-pick the required non-breaking changes into the `v0.2.x` 3. Cherry-pick the required non-breaking changes into the `v0.2.x`
4. Follow steps 3-7 of the regular release example 4. Follow steps 4-9 of the regular release example

View File

@@ -1,16 +1,285 @@
[workspace] [package]
members = [ name = "winit"
"run-wasm", version = "0.29.15"
"winit", authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
"winit-core" description = "Cross-platform window creation library."
keywords = ["windowing"]
readme = "README.md"
documentation = "https://docs.rs/winit"
categories = ["gui"]
rust-version.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
[package.metadata.docs.rs]
features = [
"rwh_04",
"rwh_05",
"rwh_06",
"serde",
"mint",
# Enabled to get docs to compile
"android-native-activity",
] ]
# These are all tested in CI
targets = [
# Windows
"i686-pc-windows-msvc",
"x86_64-pc-windows-msvc",
# macOS
"x86_64-apple-darwin",
# Unix (X11 & Wayland)
"i686-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
# iOS
"x86_64-apple-ios",
# Android
"aarch64-linux-android",
# Web
"wasm32-unknown-unknown",
]
rustdoc-args = ["--cfg", "docsrs"]
# Features are documented in either `lib.rs` or under `winit::platform`.
[features]
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"]
wayland-dlopen = ["wayland-backend/dlopen"]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
android-native-activity = ["android-activity/native-activity"]
android-game-activity = ["android-activity/game-activity"]
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "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]
cfg_aliases = "0.2.0"
[dependencies]
bitflags = "2"
cursor-icon = "1.1.0"
dpi = { version = "0.1.1", path = "dpi" }
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
serde = { workspace = true, optional = true }
smol_str = "0.2.0"
tracing = { version = "0.1.40", default_features = false }
[dev-dependencies]
image = { version = "0.24.0", default-features = false, features = ["png"] }
tracing = { version = "0.1.40", default_features = false, features = ["log"] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
winit = { path = ".", features = ["rwh_05"] }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
softbuffer = { version = "0.3.0", default-features = false, features = ["x11", "x11-dlopen", "wayland", "wayland-dlopen"] }
[target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.5.0"
ndk = { version = "0.8.0", default-features = false }
ndk-sys = "0.5.0"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
core-foundation = "0.9.3"
objc2 = "0.5.0"
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.23.1"
[target.'cfg(target_os = "macos")'.dependencies.icrate]
version = "0.1.0"
features = [
"dispatch",
"Foundation",
"Foundation_NSArray",
"Foundation_NSAttributedString",
"Foundation_NSMutableAttributedString",
"Foundation_NSData",
"Foundation_NSDictionary",
"Foundation_NSString",
"Foundation_NSProcessInfo",
"Foundation_NSThread",
"Foundation_NSNumber",
"AppKit",
"AppKit_NSAppearance",
"AppKit_NSApplication",
"AppKit_NSBitmapImageRep",
"AppKit_NSButton",
"AppKit_NSColor",
"AppKit_NSControl",
"AppKit_NSCursor",
"AppKit_NSEvent",
"AppKit_NSGraphicsContext",
"AppKit_NSImage",
"AppKit_NSImageRep",
"AppKit_NSMenu",
"AppKit_NSMenuItem",
"AppKit_NSPasteboard",
"AppKit_NSResponder",
"AppKit_NSScreen",
"AppKit_NSTextInputContext",
"AppKit_NSView",
"AppKit_NSWindow",
"AppKit_NSWindowTabGroup",
]
[target.'cfg(target_os = "ios")'.dependencies.icrate]
version = "0.1.0"
features = [
"dispatch",
"Foundation",
"Foundation_NSArray",
"Foundation_NSString",
"Foundation_NSProcessInfo",
"Foundation_NSThread",
"Foundation_NSSet",
]
[target.'cfg(target_os = "windows")'.dependencies]
unicode-segmentation = "1.7.1"
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.48"
features = [
"Win32_Devices_HumanInterfaceDevice",
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_Media",
"Win32_System_Com_StructuredStorage",
"Win32_System_Com",
"Win32_System_LibraryLoader",
"Win32_System_Ole",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_System_WindowsProgramming",
"Win32_UI_Accessibility",
"Win32_UI_Controls",
"Win32_UI_HiDpi",
"Win32_UI_Input_Ime",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Input_Pointer",
"Win32_UI_Input_Touch",
"Win32_UI_Shell",
"Win32_UI_TextServices",
"Win32_UI_WindowsAndMessaging",
]
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
ahash = { version = "0.8.7", features = ["no-rng"], optional = true }
bytemuck = { version = "1.13.1", default-features = false, optional = true }
calloop = "0.12.3"
libc = "0.2.64"
memmap2 = { version = "0.9.0", optional = true }
percent-encoding = { version = "2.0", optional = true }
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
sctk-adwaita = { version = "0.8.0", default_features = false, optional = true }
wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
wayland-client = { version = "0.31.1", optional = true }
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
x11-dl = { version = "2.19.1", optional = true }
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
xkbcommon-dl = "0.4.2"
[target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.47", default-features = false }
redox_syscall = "0.4.1"
[target.'cfg(target_family = "wasm")'.dependencies.web_sys]
package = "web-sys"
version = "0.3.64"
features = [
'AbortController',
'AbortSignal',
'Blob',
'console',
'CssStyleDeclaration',
'Document',
'DomException',
'DomRect',
'DomRectReadOnly',
'Element',
'Event',
'EventTarget',
'FocusEvent',
'HtmlCanvasElement',
'HtmlElement',
'HtmlImageElement',
'ImageBitmap',
'ImageBitmapOptions',
'ImageBitmapRenderingContext',
'ImageData',
'IntersectionObserver',
'IntersectionObserverEntry',
'KeyboardEvent',
'MediaQueryList',
'MessageChannel',
'MessagePort',
'Node',
'PageTransitionEvent',
'PointerEvent',
'PremultiplyAlpha',
'ResizeObserver',
'ResizeObserverBoxOptions',
'ResizeObserverEntry',
'ResizeObserverOptions',
'ResizeObserverSize',
'VisibilityState',
'Window',
'WheelEvent',
'Url',
]
[target.'cfg(target_family = "wasm")'.dependencies]
js-sys = "0.3.64"
pin-project = "1"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-time = "1"
[target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies]
atomic-waker = "1"
concurrent-queue = { version = "2", default-features = false }
[target.'cfg(target_family = "wasm")'.dev-dependencies]
console_log = "1"
web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
[[example]]
doc-scrape-examples = true
name = "window"
[workspace]
resolver = "2" resolver = "2"
members = [
"dpi",
"it/common-tests",
"it/gui-test",
"it/gui-test-runner",
"run-wasm",
]
[workspace.package]
rust-version = "1.70.0"
repository = "https://github.com/rust-windowing/winit"
license = "Apache-2.0"
edition = "2021"
[workspace.dependencies] [workspace.dependencies]
bitflags = "2" async-io = "2.3.1"
cfg_aliases = "0.2.0" gui-test = { path = "it/gui-test" }
cursor-icon = "1.1.0" mint = "0.5.6"
serde = { version = "1", features = ["serde_derive"] } serde = { version = "1", features = ["serde_derive"] }
smol_str = "0.2.0" serde_json = "1.0.114"
web-time = "1" winit = { path = "." }
winit-core = { path = "./winit-core", default-features = false, features = ["std"] }

View File

@@ -3,8 +3,8 @@
Winit aims to expose an interface that abstracts over window creation and input handling and can Winit aims to expose an interface that abstracts over window creation and input handling and can
be used to create both games and applications. It supports the following main graphical platforms: be used to create both games and applications. It supports the following main graphical platforms:
- Desktop - Desktop
- Windows 7+ (10+ is tested regularly) - Windows
- macOS 10.7+ (10.14+ is tested regularly) - macOS
- Unix - Unix
- via X11 - via X11
- via Wayland - via Wayland
@@ -13,9 +13,6 @@ be used to create both games and applications. It supports the following main gr
- iOS - iOS
- Android - Android
- Web - Web
- Chrome
- Firefox
- Safari 13.1+
Most platforms expose capabilities that cannot be meaningfully transposed onto others. Winit does not Most platforms expose capabilities that cannot be meaningfully transposed onto others. Winit does not
aim to support every single feature of every platform, but rather to abstract over the common features aim to support every single feature of every platform, but rather to abstract over the common features
@@ -151,7 +148,6 @@ If your PR makes notable changes to Winit's features, please update this section
* Setting the X11 parent window * Setting the X11 parent window
### iOS ### iOS
* `winit` has a minimum OS requirement of iOS 8
* Get the `UIScreen` object pointer * Get the `UIScreen` object pointer
* Setting the `UIView` hidpi factor * Setting the `UIView` hidpi factor
* Valid orientations * Valid orientations
@@ -164,9 +160,6 @@ If your PR makes notable changes to Winit's features, please update this section
### Web ### Web
* Get if the systems preferred color scheme is "dark" * Get if the systems preferred color scheme is "dark"
## Usability
* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial)
## Compatibility Matrix ## Compatibility Matrix
Legend: Legend:

128
README.md
View File

@@ -8,7 +8,7 @@
```toml ```toml
[dependencies] [dependencies]
winit = "0.29.10" winit = "0.29.15"
``` ```
## [Documentation](https://docs.rs/winit) ## [Documentation](https://docs.rs/winit)
@@ -21,7 +21,7 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
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). 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 14. 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).
## Usage ## Usage
@@ -33,14 +33,6 @@ Winit is designed to be a low-level brick in a hierarchy of libraries. Consequen
show something on the window you need to use the platform-specific getters provided by winit, or show something on the window you need to use the platform-specific getters provided by winit, or
another library. another library.
### Cargo Features
Winit provides the following features, which can be enabled in your `Cargo.toml` file:
* `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde).
* `x11` (enabled by default): On Unix platform, compiles with the X11 backend
* `wayland` (enabled by default): On Unix platform, compiles with the Wayland backend
* `mint`: Enables mint (math interoperability standard types) conversions.
## MSRV Policy ## MSRV Policy
This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to
@@ -71,118 +63,4 @@ same MSRV policy.
### Platform-specific usage ### Platform-specific usage
#### Wayland Check out the [`winit::platform`](https://rust-windowing.github.io/winit/winit/platform/index.html) module for platform-specific usage.
Note that windows don't appear on Wayland until you draw/present to them.
#### Web
To run the web example: `cargo run-wasm --example web`
Winit supports compiling to the `wasm32-unknown-unknown` target with `web-sys`.
On the web platform, a Winit window is backed by a `<canvas>` element. You can
either [provide Winit with a `<canvas>` element][web with_canvas], or [let Winit
create a `<canvas>` element which you can then retrieve][web canvas getter] and
insert it into the DOM yourself.
For the example code using Winit on Web, check out the [web example]. For
information on using Rust on WebAssembly, check out the [Rust and WebAssembly
book].
[web with_canvas]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowBuilderExtWebSys.html#tymethod.with_canvas
[web canvas getter]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowExtWebSys.html#tymethod.canvas
[web example]: ./examples/web.rs
[Rust and WebAssembly book]: https://rustwasm.github.io/book/
#### Android
The Android backend builds on (and exposes types from) the [`ndk`](https://docs.rs/ndk/latest/ndk/) crate.
Native Android applications need some form of "glue" crate that is responsible
for defining the main entry point for your Rust application as well as tracking
various life-cycle events and synchronizing with the main JVM thread.
Winit uses the [android-activity](https://github.com/rib/android-activity) as a
glue crate (prior to `0.28` it used
[ndk-glue](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue)).
The version of the glue crate that your application depends on _must_ match the
version that Winit depends on because the glue crate is responsible for your
application's main entry point. If Cargo resolves multiple versions, they will
clash.
`winit` glue compatibility table:
| winit | ndk-glue |
| :---: | :--------------------------: |
| 0.29 | `android-activity = "0.5"` |
| 0.28 | `android-activity = "0.4"` |
| 0.27 | `ndk-glue = "0.7"` |
| 0.26 | `ndk-glue = "0.5"` |
| 0.25 | `ndk-glue = "0.3"` |
| 0.24 | `ndk-glue = "0.2"` |
The recommended way to avoid a conflict with the glue version is to avoid explicitly
depending on the `android-activity` crate, and instead consume the API that
is re-exported by Winit under `winit::platform::android::activity::*`
Running on an Android device needs a dynamic system library. Add this to Cargo.toml:
```toml
[lib]
name = "main"
crate-type = ["cdylib"]
```
All Android applications are based on an `Activity` subclass, and the
`android-activity` crate is designed to support different choices for this base
class. Your application _must_ specify the base class it needs via a feature flag:
| Base Class | Feature Flag | Notes |
| :--------------: | :---------------: | :-----: |
| `NativeActivity` | `android-native-activity` | Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass `NativeActivity` to access some platform features. It does not derive from the [`AndroidAppCompat`] base class.|
| [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`], a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
[`GameActivity`]: https://developer.android.com/games/agdk/game-activity
[`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input
[`AndroidAppCompat`]: https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity
[agdk_jetpack]: https://developer.android.com/jetpack/androidx/releases/games
[agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
[Gradle]: https://developer.android.com/studio/build
For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples).
##### Converting from `ndk-glue` to `android-activity`
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
1. Remove `ndk-glue` from your `Cargo.toml`
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.10", features = [ "android-native-activity" ] }`
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
#### MacOS
A lot of functionality expects the application to be ready before you start
doing anything; this includes creating windows, fetching monitors, drawing,
and so on, see issues [#2238], [#2051] and [#2087].
If you encounter problems, you should try doing your initialization inside
`Event::Resumed`.
#### iOS
Similar to macOS, iOS's main `UIApplicationMain` does some init work that's required
by all UI-related code (see issue [#1705]). It would be best to consider creating your windows
inside `Event::Resumed`.
[#2238]: https://github.com/rust-windowing/winit/issues/2238
[#2051]: https://github.com/rust-windowing/winit/issues/2051
[#2087]: https://github.com/rust-windowing/winit/issues/2087
[#1705]: https://github.com/rust-windowing/winit/issues/1705
#### Redox OS
Redox OS has some functionality not yet present that will be implemented when
its orbital display server provides it.

View File

@@ -35,9 +35,7 @@ skip = [
{ name = "bitflags" }, # the ecosystem is in the process of migrating. { name = "bitflags" }, # the ecosystem is in the process of migrating.
{ name = "libloading" }, # x11rb uses a different version until the next update { name = "libloading" }, # x11rb uses a different version until the next update
] ]
skip-tree = [ skip-tree = []
{ name = "run-wasm", version = "0.1.0" }
]
[licenses] [licenses]

View File

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

20
dpi/CHANGELOG.md Normal file
View File

@@ -0,0 +1,20 @@
# Changelog
All notable changes to this project will be documented in this file.
Please keep one empty line before and after all headers. (This is required for
`git` to produce a conflict when a release is made while a PR is open and the
PR's changelog entry would go into the wrong section).
And please only add new entries to the top of this list, right below the `#
Unreleased` header.
## Unreleased
## 0.1.1
- Derive `Debug`, `Copy`, `Clone`, `PartialEq`, `Serialize`, `Deserialize` traits for `PixelUnit`.
## 0.1.0
- Add `LogicalUnit`, `PhysicalUnit` and `PixelUnit` types and related functions.

39
dpi/Cargo.toml Normal file
View File

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

1
dpi/LICENSE Symbolic link
View File

@@ -0,0 +1 @@
../LICENSE

View File

@@ -1,4 +1,4 @@
//! UI scaling is important, so read the docs for this module if you don't want to be confused. //! # DPI
//! //!
//! ## Why should I care about UI scaling? //! ## Why should I care about UI scaling?
//! //!
@@ -35,14 +35,11 @@
//! //!
//! ### Position and Size types //! ### Position and Size types
//! //!
//! Winit's [`PhysicalPosition`] / [`PhysicalSize`] types correspond with the actual pixels on the //! The [`PhysicalPosition`] / [`PhysicalSize`] / [`PhysicalUnit`] types correspond with the actual pixels on the
//! device, and the [`LogicalPosition`] / [`LogicalSize`] types correspond to the physical pixels //! device, and the [`LogicalPosition`] / [`LogicalSize`] / [`LogicalUnit`] types correspond to the physical pixels
//! divided by the scale factor. //! divided by the scale factor.
//! All of Winit's functions return physical types, but can take either logical or physical
//! coordinates as input, allowing you to use the most convenient coordinate system for your
//! particular application.
//! //!
//! Winit's 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
//! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch //! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch
//! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so //! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so
@@ -51,58 +48,24 @@
//! rounding properly. Note that precision loss will still occur when rounding from a float to an //! rounding properly. Note that precision loss will still occur when rounding from a float to an
//! int, although rounding lessens the problem. //! int, although rounding lessens the problem.
//! //!
//! ### Events //! ## Cargo Features
//! //!
//! Winit will dispatch a [`ScaleFactorChanged`] event whenever a window's scale factor has changed. //! This crate provides the following Cargo features:
//! This can happen if the user drags their window from a standard-resolution monitor to a high-DPI
//! monitor or if the user changes their DPI settings. This allows you to rescale your application's
//! UI elements and adjust how the platform changes the window's size to reflect the new scale
//! factor. If a window hasn't received a [`ScaleFactorChanged`] event, its scale factor
//! can be found by calling [`window.scale_factor()`].
//! //!
//! ## How is the scale factor calculated? //! * `serde`: Enables serialization/deserialization of certain types with
//! //! [Serde](https://crates.io/crates/serde).
//! The scale factor is calculated differently on different platforms: //! * `mint`: Enables mint (math interoperability standard types) conversions.
//!
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the
//! display settings. While users are free to select any option they want, they're only given a
//! selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7. The scale factor is
//! global and changing it requires logging out. See [this article][windows_1] for technical
//! details.
//! - **macOS:** Recent macOS versions allow the user to change the scaling factor for specific
//! displays. When available, the user may pick a per-monitor scaling factor from a set of
//! pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default,
//! but the specific value varies across devices.
//! - **X11:** Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit
//! currently uses a three-pronged approach:
//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable if present.
//! + If not present, use the value set in `Xft.dpi` in Xresources.
//! + Otherwise, calculate the scale factor based on the millimeter monitor dimensions provided by XRandR.
//!
//! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the
//! XRandR scaling method. Generally speaking, you should try to configure the standard system
//! variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`.
//! - **Wayland:** The scale factor is suggested by the compositor for each window individually. The
//! monitor scale factor may differ from the window scale factor.
//! - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range
//! from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more
//! information.
//! - **Android:** Scale factors are set by the manufacturer to the value that best suits the
//! device, and range from `1.0` to `4.0`. See [this article][android_1] for more information.
//! - **Web:** The scale factor is the ratio between CSS pixels and the physical device pixels.
//! In other words, it is the value of [`window.devicePixelRatio`][web_1]. It is affected by
//! both the screen scaling and the browser zoom level and can go below `1.0`.
//! //!
//! //!
//! [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)
//! [`ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged
//! [`window.scale_factor()`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.scale_factor #![cfg_attr(
//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows docsrs,
//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html feature(doc_auto_cfg, doc_cfg_hide),
//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/ doc(cfg_hide(doc, docsrs))
//! [android_1]: https://developer.android.com/training/multiscreen/screendensities )]
//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio #![forbid(unsafe_code)]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -165,10 +128,244 @@ pub fn validate_scale_factor(scale_factor: f64) -> bool {
scale_factor.is_sign_positive() && scale_factor.is_normal() scale_factor.is_sign_positive() && scale_factor.is_normal()
} }
/// A logical pixel unit.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalUnit<P>(pub P);
impl<P> LogicalUnit<P> {
/// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a logical unit of `0_f64`.
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
#[inline]
pub const fn new(v: P) -> Self {
LogicalUnit(v)
}
}
impl<P: Pixel> LogicalUnit<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalUnit<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<X> {
assert!(validate_scale_factor(scale_factor));
PhysicalUnit::new(self.0.into() * scale_factor).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalUnit<X> {
LogicalUnit(self.0.cast())
}
}
impl<P: Pixel, X: Pixel> From<X> for LogicalUnit<P> {
fn from(v: X) -> LogicalUnit<P> {
LogicalUnit::new(v.cast())
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u8 {
fn from(v: LogicalUnit<P>) -> u8 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u16 {
fn from(v: LogicalUnit<P>) -> u16 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u32 {
fn from(v: LogicalUnit<P>) -> u32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i8 {
fn from(v: LogicalUnit<P>) -> i8 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i16 {
fn from(v: LogicalUnit<P>) -> i16 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i32 {
fn from(v: LogicalUnit<P>) -> i32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for f32 {
fn from(v: LogicalUnit<P>) -> f32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for f64 {
fn from(v: LogicalUnit<P>) -> f64 {
v.0.cast()
}
}
/// A physical pixel unit.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalUnit<P>(pub P);
impl<P> PhysicalUnit<P> {
/// Represents a minimum physical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a physical unit of `0_f64`.
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
/// Represents a maximum physical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
#[inline]
pub const fn new(v: P) -> Self {
PhysicalUnit(v)
}
}
impl<P: Pixel> PhysicalUnit<P> {
#[inline]
pub fn from_logical<T: Into<LogicalUnit<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalUnit<X> {
assert!(validate_scale_factor(scale_factor));
LogicalUnit::new(self.0.into() / scale_factor).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalUnit<X> {
PhysicalUnit(self.0.cast())
}
}
impl<P: Pixel, X: Pixel> From<X> for PhysicalUnit<P> {
fn from(v: X) -> PhysicalUnit<P> {
PhysicalUnit::new(v.cast())
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u8 {
fn from(v: PhysicalUnit<P>) -> u8 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u16 {
fn from(v: PhysicalUnit<P>) -> u16 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u32 {
fn from(v: PhysicalUnit<P>) -> u32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i8 {
fn from(v: PhysicalUnit<P>) -> i8 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i16 {
fn from(v: PhysicalUnit<P>) -> i16 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i32 {
fn from(v: PhysicalUnit<P>) -> i32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for f32 {
fn from(v: PhysicalUnit<P>) -> f32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for f64 {
fn from(v: PhysicalUnit<P>) -> f64 {
v.0.cast()
}
}
/// A pixel unit that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PixelUnit {
Physical(PhysicalUnit<i32>),
Logical(LogicalUnit<f64>),
}
impl PixelUnit {
/// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MIN));
/// Represents a logical unit of `0_f64`.
pub const ZERO: PixelUnit = PixelUnit::Logical(LogicalUnit::new(0.0));
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MAX));
pub fn new<S: Into<PixelUnit>>(unit: S) -> PixelUnit {
unit.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalUnit<P> {
match *self {
PixelUnit::Physical(unit) => unit.to_logical(scale_factor),
PixelUnit::Logical(unit) => unit.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<P> {
match *self {
PixelUnit::Physical(unit) => unit.cast(),
PixelUnit::Logical(unit) => unit.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for PixelUnit {
#[inline]
fn from(unit: PhysicalUnit<P>) -> PixelUnit {
PixelUnit::Physical(unit.cast())
}
}
impl<P: Pixel> From<LogicalUnit<P>> for PixelUnit {
#[inline]
fn from(unit: LogicalUnit<P>) -> PixelUnit {
PixelUnit::Logical(unit.cast())
}
}
/// A position represented in logical pixels. /// A position represented in logical pixels.
/// ///
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the /// The position is stored as floats, so please be careful. Casting floats to integers truncates the
/// fractional part, which can cause noticable issues. To help with that, an `Into<(i32, i32)>` /// fractional part, which can cause noticeable issues. To help with that, an `Into<(i32, i32)>`
/// implementation is provided which does the rounding for you. /// implementation is provided which does the rounding for you.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -587,15 +784,13 @@ impl<P: Pixel> From<LogicalPosition<P>> for Position {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::dpi; use super::*;
use std::collections::HashSet; use std::collections::HashSet;
macro_rules! test_pixel_int_impl { macro_rules! test_pixel_int_impl {
($($name:ident => $ty:ty),*) => {$( ($($name:ident => $ty:ty),*) => {$(
#[test] #[test]
fn $name() { fn $name() {
use dpi::Pixel;
assert_eq!( assert_eq!(
<$ty as Pixel>::from_f64(37.0), <$ty as Pixel>::from_f64(37.0),
37, 37,
@@ -664,8 +859,6 @@ mod tests {
($($name:ident => $ty:ty),*) => {$( ($($name:ident => $ty:ty),*) => {$(
#[test] #[test]
fn $name() { fn $name() {
use dpi::Pixel;
assert_approx_eq!( assert_approx_eq!(
<$ty as Pixel>::from_f64(37.0), <$ty as Pixel>::from_f64(37.0),
37.0, 37.0,
@@ -758,46 +951,77 @@ mod tests {
#[test] #[test]
fn test_validate_scale_factor() { fn test_validate_scale_factor() {
assert!(dpi::validate_scale_factor(1.0)); assert!(validate_scale_factor(1.0));
assert!(dpi::validate_scale_factor(2.0)); assert!(validate_scale_factor(2.0));
assert!(dpi::validate_scale_factor(3.0)); assert!(validate_scale_factor(3.0));
assert!(dpi::validate_scale_factor(1.5)); assert!(validate_scale_factor(1.5));
assert!(dpi::validate_scale_factor(0.5)); assert!(validate_scale_factor(0.5));
assert!(!dpi::validate_scale_factor(0.0)); assert!(!validate_scale_factor(0.0));
assert!(!dpi::validate_scale_factor(-1.0)); assert!(!validate_scale_factor(-1.0));
assert!(!dpi::validate_scale_factor(f64::INFINITY)); assert!(!validate_scale_factor(f64::INFINITY));
assert!(!dpi::validate_scale_factor(f64::NAN)); assert!(!validate_scale_factor(f64::NAN));
assert!(!dpi::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 = dpi::LogicalPosition::new(1.0, 2.0); let log_pos = LogicalPosition::new(1.0, 2.0);
assert_eq!( assert_eq!(log_pos.to_physical::<u32>(1.0), PhysicalPosition::new(1, 2));
log_pos.to_physical::<u32>(1.0), assert_eq!(log_pos.to_physical::<u32>(2.0), PhysicalPosition::new(2, 4));
dpi::PhysicalPosition::new(1, 2) assert_eq!(log_pos.cast::<u32>(), LogicalPosition::new(1, 2));
);
assert_eq!(
log_pos.to_physical::<u32>(2.0),
dpi::PhysicalPosition::new(2, 4)
);
assert_eq!(log_pos.cast::<u32>(), dpi::LogicalPosition::new(1, 2));
assert_eq!( assert_eq!(
log_pos, log_pos,
dpi::LogicalPosition::from_physical(dpi::PhysicalPosition::new(1.0, 2.0), 1.0) LogicalPosition::from_physical(PhysicalPosition::new(1.0, 2.0), 1.0)
); );
assert_eq!( assert_eq!(
log_pos, log_pos,
dpi::LogicalPosition::from_physical(dpi::PhysicalPosition::new(2.0, 4.0), 2.0) LogicalPosition::from_physical(PhysicalPosition::new(2.0, 4.0), 2.0)
); );
assert_eq!( assert_eq!(
dpi::LogicalPosition::from((2.0, 2.0)), LogicalPosition::from((2.0, 2.0)),
dpi::LogicalPosition::new(2.0, 2.0) LogicalPosition::new(2.0, 2.0)
); );
assert_eq!( assert_eq!(
dpi::LogicalPosition::from([2.0, 3.0]), LogicalPosition::from([2.0, 3.0]),
dpi::LogicalPosition::new(2.0, 3.0) LogicalPosition::new(2.0, 3.0)
); );
let x: (f64, f64) = log_pos.into(); let x: (f64, f64) = log_pos.into();
@@ -809,56 +1033,44 @@ mod tests {
#[test] #[test]
fn test_physical_position() { fn test_physical_position() {
assert_eq!( assert_eq!(
dpi::PhysicalPosition::from_logical(dpi::LogicalPosition::new(1.0, 2.0), 1.0), PhysicalPosition::from_logical(LogicalPosition::new(1.0, 2.0), 1.0),
dpi::PhysicalPosition::new(1, 2) PhysicalPosition::new(1, 2)
); );
assert_eq!( assert_eq!(
dpi::PhysicalPosition::from_logical(dpi::LogicalPosition::new(2.0, 4.0), 0.5), PhysicalPosition::from_logical(LogicalPosition::new(2.0, 4.0), 0.5),
dpi::PhysicalPosition::new(1, 2) PhysicalPosition::new(1, 2)
); );
assert_eq!( assert_eq!(
dpi::PhysicalPosition::from((2.0, 2.0)), PhysicalPosition::from((2.0, 2.0)),
dpi::PhysicalPosition::new(2.0, 2.0) PhysicalPosition::new(2.0, 2.0)
); );
assert_eq!( assert_eq!(
dpi::PhysicalPosition::from([2.0, 3.0]), PhysicalPosition::from([2.0, 3.0]),
dpi::PhysicalPosition::new(2.0, 3.0) PhysicalPosition::new(2.0, 3.0)
); );
let x: (f64, f64) = dpi::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));
let x: [f64; 2] = dpi::PhysicalPosition::new(1, 2).into(); let x: [f64; 2] = PhysicalPosition::new(1, 2).into();
assert_eq!(x, [1.0, 2.0]); assert_eq!(x, [1.0, 2.0]);
} }
#[test] #[test]
fn test_logical_size() { fn test_logical_size() {
let log_size = dpi::LogicalSize::new(1.0, 2.0); let log_size = LogicalSize::new(1.0, 2.0);
assert_eq!( assert_eq!(log_size.to_physical::<u32>(1.0), PhysicalSize::new(1, 2));
log_size.to_physical::<u32>(1.0), assert_eq!(log_size.to_physical::<u32>(2.0), PhysicalSize::new(2, 4));
dpi::PhysicalSize::new(1, 2) assert_eq!(log_size.cast::<u32>(), LogicalSize::new(1, 2));
);
assert_eq!(
log_size.to_physical::<u32>(2.0),
dpi::PhysicalSize::new(2, 4)
);
assert_eq!(log_size.cast::<u32>(), dpi::LogicalSize::new(1, 2));
assert_eq!( assert_eq!(
log_size, log_size,
dpi::LogicalSize::from_physical(dpi::PhysicalSize::new(1.0, 2.0), 1.0) LogicalSize::from_physical(PhysicalSize::new(1.0, 2.0), 1.0)
); );
assert_eq!( assert_eq!(
log_size, log_size,
dpi::LogicalSize::from_physical(dpi::PhysicalSize::new(2.0, 4.0), 2.0) LogicalSize::from_physical(PhysicalSize::new(2.0, 4.0), 2.0)
);
assert_eq!(
dpi::LogicalSize::from((2.0, 2.0)),
dpi::LogicalSize::new(2.0, 2.0)
);
assert_eq!(
dpi::LogicalSize::from([2.0, 3.0]),
dpi::LogicalSize::new(2.0, 3.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));
let x: (f64, f64) = log_size.into(); let x: (f64, f64) = log_size.into();
assert_eq!(x, (1.0, 2.0)); assert_eq!(x, (1.0, 2.0));
@@ -869,136 +1081,164 @@ mod tests {
#[test] #[test]
fn test_physical_size() { fn test_physical_size() {
assert_eq!( assert_eq!(
dpi::PhysicalSize::from_logical(dpi::LogicalSize::new(1.0, 2.0), 1.0), PhysicalSize::from_logical(LogicalSize::new(1.0, 2.0), 1.0),
dpi::PhysicalSize::new(1, 2) PhysicalSize::new(1, 2)
); );
assert_eq!( assert_eq!(
dpi::PhysicalSize::from_logical(dpi::LogicalSize::new(2.0, 4.0), 0.5), PhysicalSize::from_logical(LogicalSize::new(2.0, 4.0), 0.5),
dpi::PhysicalSize::new(1, 2) PhysicalSize::new(1, 2)
);
assert_eq!(
dpi::PhysicalSize::from((2.0, 2.0)),
dpi::PhysicalSize::new(2.0, 2.0)
);
assert_eq!(
dpi::PhysicalSize::from([2.0, 3.0]),
dpi::PhysicalSize::new(2.0, 3.0)
); );
assert_eq!(PhysicalSize::from((2.0, 2.0)), PhysicalSize::new(2.0, 2.0));
assert_eq!(PhysicalSize::from([2.0, 3.0]), PhysicalSize::new(2.0, 3.0));
let x: (f64, f64) = dpi::PhysicalSize::new(1, 2).into(); let x: (f64, f64) = PhysicalSize::new(1, 2).into();
assert_eq!(x, (1.0, 2.0)); assert_eq!(x, (1.0, 2.0));
let x: [f64; 2] = dpi::PhysicalSize::new(1, 2).into(); let x: [f64; 2] = PhysicalSize::new(1, 2).into();
assert_eq!(x, [1.0, 2.0]); assert_eq!(x, [1.0, 2.0]);
} }
#[test] #[test]
fn test_size() { fn test_size() {
assert_eq!( assert_eq!(
dpi::Size::new(dpi::PhysicalSize::new(1, 2)), Size::new(PhysicalSize::new(1, 2)),
dpi::Size::Physical(dpi::PhysicalSize::new(1, 2)) Size::Physical(PhysicalSize::new(1, 2))
); );
assert_eq!( assert_eq!(
dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)), Size::new(LogicalSize::new(1.0, 2.0)),
dpi::Size::Logical(dpi::LogicalSize::new(1.0, 2.0)) Size::Logical(LogicalSize::new(1.0, 2.0))
); );
assert_eq!( assert_eq!(
dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_logical::<f64>(1.0), Size::new(PhysicalSize::new(1, 2)).to_logical::<f64>(1.0),
dpi::LogicalSize::new(1.0, 2.0) LogicalSize::new(1.0, 2.0)
); );
assert_eq!( assert_eq!(
dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_logical::<f64>(2.0), Size::new(PhysicalSize::new(1, 2)).to_logical::<f64>(2.0),
dpi::LogicalSize::new(0.5, 1.0) LogicalSize::new(0.5, 1.0)
); );
assert_eq!( assert_eq!(
dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_logical::<f64>(1.0), Size::new(LogicalSize::new(1.0, 2.0)).to_logical::<f64>(1.0),
dpi::LogicalSize::new(1.0, 2.0) LogicalSize::new(1.0, 2.0)
); );
assert_eq!( assert_eq!(
dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_physical::<u32>(1.0), Size::new(PhysicalSize::new(1, 2)).to_physical::<u32>(1.0),
dpi::PhysicalSize::new(1, 2) PhysicalSize::new(1, 2)
); );
assert_eq!( assert_eq!(
dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_physical::<u32>(2.0), Size::new(PhysicalSize::new(1, 2)).to_physical::<u32>(2.0),
dpi::PhysicalSize::new(1, 2) PhysicalSize::new(1, 2)
); );
assert_eq!( assert_eq!(
dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_physical::<u32>(1.0), Size::new(LogicalSize::new(1.0, 2.0)).to_physical::<u32>(1.0),
dpi::PhysicalSize::new(1, 2) PhysicalSize::new(1, 2)
); );
assert_eq!( assert_eq!(
dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_physical::<u32>(2.0), Size::new(LogicalSize::new(1.0, 2.0)).to_physical::<u32>(2.0),
dpi::PhysicalSize::new(2, 4) PhysicalSize::new(2, 4)
); );
let small = dpi::Size::Physical((1, 2).into()); let small = Size::Physical((1, 2).into());
let medium = dpi::Size::Logical((3, 4).into()); let medium = Size::Logical((3, 4).into());
let medium_physical = dpi::Size::new(medium.to_physical::<u32>(1.0)); let medium_physical = Size::new(medium.to_physical::<u32>(1.0));
let large = dpi::Size::Physical((5, 6).into()); let large = Size::Physical((5, 6).into());
assert_eq!(dpi::Size::clamp(medium, small, large, 1.0), medium_physical); assert_eq!(Size::clamp(medium, small, large, 1.0), medium_physical);
assert_eq!(dpi::Size::clamp(small, medium, large, 1.0), medium_physical); assert_eq!(Size::clamp(small, medium, large, 1.0), medium_physical);
assert_eq!(dpi::Size::clamp(large, small, medium, 1.0), medium_physical); assert_eq!(Size::clamp(large, small, medium, 1.0), medium_physical);
} }
#[test] #[test]
fn test_position() { fn test_position() {
assert_eq!( assert_eq!(
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)), Position::new(PhysicalPosition::new(1, 2)),
dpi::Position::Physical(dpi::PhysicalPosition::new(1, 2)) Position::Physical(PhysicalPosition::new(1, 2))
); );
assert_eq!( assert_eq!(
dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)), Position::new(LogicalPosition::new(1.0, 2.0)),
dpi::Position::Logical(dpi::LogicalPosition::new(1.0, 2.0)) Position::Logical(LogicalPosition::new(1.0, 2.0))
); );
assert_eq!( assert_eq!(
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_logical::<f64>(1.0), Position::new(PhysicalPosition::new(1, 2)).to_logical::<f64>(1.0),
dpi::LogicalPosition::new(1.0, 2.0) LogicalPosition::new(1.0, 2.0)
); );
assert_eq!( assert_eq!(
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_logical::<f64>(2.0), Position::new(PhysicalPosition::new(1, 2)).to_logical::<f64>(2.0),
dpi::LogicalPosition::new(0.5, 1.0) LogicalPosition::new(0.5, 1.0)
); );
assert_eq!( assert_eq!(
dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_logical::<f64>(1.0), Position::new(LogicalPosition::new(1.0, 2.0)).to_logical::<f64>(1.0),
dpi::LogicalPosition::new(1.0, 2.0) LogicalPosition::new(1.0, 2.0)
); );
assert_eq!( assert_eq!(
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_physical::<u32>(1.0), Position::new(PhysicalPosition::new(1, 2)).to_physical::<u32>(1.0),
dpi::PhysicalPosition::new(1, 2) PhysicalPosition::new(1, 2)
); );
assert_eq!( assert_eq!(
dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_physical::<u32>(2.0), Position::new(PhysicalPosition::new(1, 2)).to_physical::<u32>(2.0),
dpi::PhysicalPosition::new(1, 2) PhysicalPosition::new(1, 2)
); );
assert_eq!( assert_eq!(
dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(1.0), Position::new(LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(1.0),
dpi::PhysicalPosition::new(1, 2) PhysicalPosition::new(1, 2)
); );
assert_eq!( assert_eq!(
dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(2.0), Position::new(LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(2.0),
dpi::PhysicalPosition::new(2, 4) PhysicalPosition::new(2, 4)
); );
} }
// Eat coverage for the Debug impls et al // Eat coverage for the Debug impls et al
#[test] #[test]
fn ensure_attrs_do_not_panic() { fn ensure_attrs_do_not_panic() {
let _ = format!("{:?}", dpi::LogicalPosition::<u32>::default().clone()); let _ = format!("{:?}", LogicalPosition::<u32>::default().clone());
HashSet::new().insert(dpi::LogicalPosition::<u32>::default()); HashSet::new().insert(LogicalPosition::<u32>::default());
let _ = format!("{:?}", dpi::PhysicalPosition::<u32>::default().clone()); let _ = format!("{:?}", PhysicalPosition::<u32>::default().clone());
HashSet::new().insert(dpi::PhysicalPosition::<u32>::default()); HashSet::new().insert(PhysicalPosition::<u32>::default());
let _ = format!("{:?}", dpi::LogicalSize::<u32>::default().clone()); let _ = format!("{:?}", LogicalSize::<u32>::default().clone());
HashSet::new().insert(dpi::LogicalSize::<u32>::default()); HashSet::new().insert(LogicalSize::<u32>::default());
let _ = format!("{:?}", dpi::PhysicalSize::<u32>::default().clone()); let _ = format!("{:?}", PhysicalSize::<u32>::default().clone());
HashSet::new().insert(dpi::PhysicalSize::<u32>::default()); HashSet::new().insert(PhysicalSize::<u32>::default());
let _ = format!("{:?}", dpi::Size::Physical((1, 2).into()).clone()); let _ = format!("{:?}", Size::Physical((1, 2).into()).clone());
let _ = format!("{:?}", dpi::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,10 +1,3 @@
#[cfg(all(
feature = "rwh_06",
any(x11_platform, macos_platform, windows_platform)
))]
#[path = "util/fill.rs"]
mod fill;
#[cfg(all( #[cfg(all(
feature = "rwh_06", feature = "rwh_06",
any(x11_platform, macos_platform, windows_platform) any(x11_platform, macos_platform, windows_platform)
@@ -13,58 +6,57 @@ mod fill;
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
use std::collections::HashMap; use std::collections::HashMap;
use winit::{ use winit::dpi::{LogicalPosition, LogicalSize, Position};
dpi::{LogicalPosition, LogicalSize, Position}, use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
event::{ElementState, Event, KeyEvent, WindowEvent}, use winit::event_loop::{ActiveEventLoop, EventLoop};
event_loop::{EventLoop, EventLoopWindowTarget}, use winit::raw_window_handle::HasRawWindowHandle;
raw_window_handle::HasRawWindowHandle, use winit::window::Window;
window::{Window, WindowId},
};
fn spawn_child_window( #[path = "util/fill.rs"]
parent: &Window, mod fill;
event_loop: &EventLoopWindowTarget,
windows: &mut HashMap<WindowId, Window>, fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window {
) {
let parent = parent.raw_window_handle().unwrap(); let parent = parent.raw_window_handle().unwrap();
let mut builder = Window::builder() let mut window_attributes = Window::default_attributes()
.with_title("child window") .with_title("child window")
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32)) .with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_visible(true); .with_visible(true);
// `with_parent_window` is unsafe. Parent window must be a valid window. // `with_parent_window` is unsafe. Parent window must be a valid window.
builder = unsafe { builder.with_parent_window(Some(parent)) }; window_attributes = unsafe { window_attributes.with_parent_window(Some(parent)) };
let child_window = builder.build(event_loop).unwrap();
let id = child_window.id(); event_loop.create_window(window_attributes).unwrap()
windows.insert(id, child_window);
println!("child window created with id: {id:?}");
} }
let mut windows = HashMap::new(); let mut windows = HashMap::new();
let event_loop: EventLoop<()> = EventLoop::new().unwrap(); let event_loop: EventLoop<()> = EventLoop::new().unwrap();
let parent_window = Window::builder() let mut parent_window_id = None;
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32))
.build(&event_loop)
.unwrap();
println!("parent window: {parent_window:?})"); 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();
event_loop.run(move |event: Event<()>, elwt| { parent_window_id = Some(window.id());
if let Event::WindowEvent { event, window_id } = event {
match event { println!("Parent window id: {parent_window_id:?})");
windows.insert(window.id(), window);
}
Event::WindowEvent { window_id, event } => match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
windows.clear(); windows.clear();
elwt.exit(); event_loop.exit();
} }
WindowEvent::CursorEntered { device_id: _ } => { WindowEvent::CursorEntered { device_id: _ } => {
// On x11, println when the cursor entered in a window even if the child window is created // On x11, println when the cursor entered in a window even if the child window is created
// by some key inputs. // by some key inputs.
// the child windows are always placed at (0, 0) with size (200, 200) in the parent window, // the child windows are always placed at (0, 0) with size (200, 200) in the parent window,
// so we also can see this log when we move the cursor arround (200, 200) in 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:?}"); println!("cursor entered in the window {window_id:?}");
} }
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
@@ -75,7 +67,11 @@ fn main() -> Result<(), impl std::error::Error> {
}, },
.. ..
} => { } => {
spawn_child_window(&parent_window, elwt, &mut windows); 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 => { WindowEvent::RedrawRequested => {
if let Some(window) = windows.get(&window_id) { if let Some(window) = windows.get(&window_id) {
@@ -83,15 +79,16 @@ fn main() -> Result<(), impl std::error::Error> {
} }
} }
_ => (), _ => (),
} },
_ => (),
} }
}) })
} }
#[cfg(not(all( #[cfg(all(
feature = "rwh_06", feature = "rwh_06",
any(x11_platform, macos_platform, windows_platform) not(any(x11_platform, macos_platform, windows_platform))
)))] ))]
fn main() { fn main() {
panic!("This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature enabled."); panic!("This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature enabled.");
} }

147
examples/control_flow.rs Normal file
View File

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

View File

Before

Width:  |  Height:  |  Size: 159 B

After

Width:  |  Height:  |  Size: 159 B

View File

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 129 B

View File

Before

Width:  |  Height:  |  Size: 229 B

After

Width:  |  Height:  |  Size: 229 B

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

84
examples/pump_events.rs Normal file
View File

@@ -0,0 +1,84 @@
#![allow(clippy::single_match)]
// Limit this example to only compatible platforms.
#[cfg(any(
windows_platform,
macos_platform,
x11_platform,
wayland_platform,
android_platform,
))]
fn main() -> std::process::ExitCode {
use std::{process::ExitCode, thread::sleep, time::Duration};
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus};
use winit::window::{Window, WindowId};
#[path = "util/fill.rs"]
mod fill;
#[derive(Default)]
struct PumpDemo {
window: Option<Window>,
}
impl ApplicationHandler for PumpDemo {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
println!("{event:?}");
let window = match self.window.as_ref() {
Some(window) => window,
None => return,
};
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
fill::fill_window(window);
window.request_redraw();
}
_ => (),
}
}
}
let mut event_loop = EventLoop::new().unwrap();
tracing_subscriber::fmt::init();
let mut app = PumpDemo::default();
loop {
let timeout = Some(Duration::ZERO);
let status = event_loop.pump_app_events(timeout, &mut app);
if let PumpStatus::Exit(exit_code) = status {
break ExitCode::from(exit_code as u8);
}
// Sleep for 1/60 second to simulate application work
//
// Since `pump_events` doesn't block it will be important to
// throttle the loop in the app somehow.
println!("Update()");
sleep(Duration::from_millis(16));
}
}
#[cfg(any(ios_platform, web_platform, orbital_platform))]
fn main() {
println!("This platform doesn't support pump_events.");
}

98
examples/run_on_demand.rs Normal file
View File

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

View File

@@ -28,7 +28,7 @@ mod platform {
// ManuallyDrop to prevent destructors from running. // ManuallyDrop to prevent destructors from running.
// //
// A static, thread-local map of graphics contexts to open windows. // A static, thread-local map of graphics contexts to open windows.
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None)); static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) };
} }
/// The graphics context used to draw to a window. /// The graphics context used to draw to a window.

984
examples/window.rs Normal file
View File

@@ -0,0 +1,984 @@
//! Simple winit application.
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fmt::Debug;
#[cfg(not(any(android_platform, ios_platform)))]
use std::num::NonZeroU32;
use std::path::Path;
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::application::ApplicationHandler;
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event::{DeviceEvent, DeviceId, Ime, WindowEvent};
use winit::event::{MouseButton, MouseScrollDelta};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::keyboard::{Key, ModifiersState};
use winit::window::{
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, 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,
};
/// The amount of points to around the window for drag resize direction calculations.
const BORDER_SIZE: f64 = 20.;
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 state = Application::new(&event_loop);
event_loop.run_app(&mut state).map_err(Into::into)
}
#[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: Option<Context>,
}
impl Application {
fn new<T>(event_loop: &EventLoop<T>) -> Self {
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
#[cfg(not(any(android_platform, ios_platform)))]
let context = Some(unsafe { Context::from_raw(event_loop.raw_display_handle()).unwrap() });
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
// 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");
let icon = load_icon(Path::new(path));
println!("Loading cursor assets");
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);
}
let window_state = WindowState::new(self, window)?;
let window_id = window_state.window.id();
println!("Created new window with id={window_id:?}");
self.windows.insert(window_id, window_state);
Ok(window_id)
}
fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
// let cursor_position = self.cursor_position;
let window = self.windows.get_mut(&window_id).unwrap();
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.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 => window.toggle_resize_increments(),
Action::ToggleCursorVisibility => window.toggle_cursor_visibility(),
Action::ToggleResizable => window.toggle_resizable(),
Action::ToggleDecorations => window.toggle_decorations(),
Action::ToggleFullscreen => window.toggle_fullscreen(),
Action::ToggleMaximize => window.toggle_maximize(),
Action::ToggleImeInput => window.toggle_ime(),
Action::Minimize => window.minimize(),
Action::NextCursor => window.next_cursor(),
Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors),
Action::CycleCursorGrab => window.cycle_cursor_grab(),
Action::DragWindow => window.drag_window(),
Action::DragResizeWindow => window.drag_resize_window(),
Action::ShowWindowMenu => window.show_menu(),
Action::PrintHelp => self.print_help(),
#[cfg(macos_platform)]
Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
#[cfg(macos_platform)]
Action::CreateNewTab => {
let tab_id = window.window.tabbing_identifier();
if let Err(err) = self.create_window(event_loop, Some(tab_id)) {
eprintln!("Error creating new window: {err}");
}
}
}
}
fn dump_monitors(&self, event_loop: &ActiveEventLoop) {
println!("Monitors information");
let primary_monitor = event_loop.primary_monitor();
for monitor in event_loop.available_monitors() {
let intro = if primary_monitor.as_ref() == Some(&monitor) {
"Primary monitor"
} else {
"Monitor"
};
if let Some(name) = monitor.name() {
println!("{intro}: {name}");
} else {
println!("{intro}: [no name]");
}
let PhysicalSize { width, height } = monitor.size();
print!(" Current mode: {width}x{height}");
if let Some(m_hz) = monitor.refresh_rate_millihertz() {
println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
} else {
println!();
}
let PhysicalPosition { x, y } = monitor.position();
println!(" Position: {x},{y}");
println!(" Scale factor: {}", monitor.scale_factor());
println!(" Available modes (width x height x bit-depth):");
for mode in monitor.video_modes() {
let PhysicalSize { width, height } = mode.size();
let bits = mode.bit_depth();
let m_hz = mode.refresh_rate_millihertz();
println!(
" {width}x{height}x{bits} @ {}.{} Hz",
m_hz / 1000,
m_hz % 1000
);
}
}
}
/// Process the key binding.
fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
KEY_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&key, mods)
.then_some(binding.action)
})
}
/// Process mouse binding.
fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> {
MOUSE_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&button, mods)
.then_some(binding.action)
})
}
fn print_help(&self) {
println!("Keyboard bindings:");
for binding in KEY_BINDINGS {
println!(
"{}{:<10} - {} ({})",
modifiers_to_string(binding.mods),
binding.trigger,
binding.action,
binding.action.help(),
);
}
println!("Mouse bindings:");
for binding in MOUSE_BINDINGS {
println!(
"{}{:<10} - {} ({})",
modifiers_to_string(binding.mods),
mouse_button_to_string(binding.trigger),
binding.action,
binding.action.help(),
);
}
}
}
impl ApplicationHandler<UserEvent> for Application {
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
println!("User event: {event:?}");
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
let window = match self.windows.get_mut(&window_id) {
Some(window) => window,
None => return,
};
match event {
WindowEvent::Resized(size) => {
window.resize(size);
}
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:?}");
window.set_theme(theme);
}
WindowEvent::RedrawRequested => {
if let Err(err) = window.draw() {
eprintln!("Error drawing window: {err}");
}
}
WindowEvent::Occluded(occluded) => {
window.set_occluded(occluded);
}
WindowEvent::CloseRequested => {
println!("Closing Window={window_id:?}");
self.windows.remove(&window_id);
}
WindowEvent::ModifiersChanged(modifiers) => {
window.modifiers = modifiers.state();
println!("Modifiers changed to {:?}", window.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,
is_synthetic: false,
..
} => {
let mods = window.modifiers;
// Dispatch actions only on press.
if event.state.is_pressed() {
let action = if let Key::Character(ch) = event.logical_key.as_ref() {
Self::process_key_binding(&ch.to_uppercase(), &mods)
} else {
None
};
if let Some(action) = action {
self.handle_action(event_loop, window_id, action);
}
}
}
WindowEvent::MouseInput { button, state, .. } => {
let mods = window.modifiers;
if let Some(action) = state
.is_pressed()
.then(|| Self::process_mouse_binding(button, &mods))
.flatten()
{
self.handle_action(event_loop, window_id, action);
}
}
WindowEvent::CursorLeft { .. } => {
println!("Cursor left Window={window_id:?}");
window.cursor_left();
}
WindowEvent::CursorMoved { position, .. } => {
println!("Moved cursor to {position:?}");
window.cursor_moved(position);
}
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!("Committed: {}", text);
}
Ime::Disabled => println!("IME disabled for Window={window_id:?}"),
},
WindowEvent::PinchGesture { delta, .. } => {
window.zoom += delta;
let zoom = window.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, .. } => {
window.rotated += delta;
let rotated = window.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::KeyboardInput { .. }
| WindowEvent::CursorEntered { .. }
| WindowEvent::AxisMotion { .. }
| WindowEvent::DroppedFile(_)
| WindowEvent::HoveredFile(_)
| WindowEvent::Destroyed
| WindowEvent::Touch(_)
| WindowEvent::Moved(_) => (),
}
}
fn device_event(
&mut self,
_event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
println!("Device {device_id:?} event: {event:?}");
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
println!("Resumed the event loop");
self.dump_monitors(event_loop);
// Create initial window.
self.create_window(event_loop, None)
.expect("failed to create initial window");
self.print_help();
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if self.windows.is_empty() {
println!("No windows left, exiting...");
event_loop.exit();
}
}
#[cfg(not(any(android_platform, ios_platform)))]
fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
// We must drop the context here.
self.context = None;
}
}
/// State of the window.
struct WindowState {
/// 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 actual winit Window.
window: Window,
/// 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,
#[cfg(macos_platform)]
option_as_alt: OptionAsAlt,
// Cursor states.
named_idx: usize,
custom_idx: usize,
cursor_hidden: bool,
}
impl WindowState {
fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
// it doesn't outlive it.
#[cfg(not(any(android_platform, ios_platform)))]
let surface = unsafe { Surface::new(app.context.as_ref().unwrap(), &window)? };
let theme = window.theme().unwrap_or(Theme::Dark);
println!("Theme: {theme:?}");
let named_idx = 0;
window.set_cursor(CURSORS[named_idx]);
// Allow IME out of the box.
let ime = true;
window.set_ime_allowed(ime);
let size = window.inner_size();
let mut state = Self {
#[cfg(macos_platform)]
option_as_alt: window.option_as_alt(),
custom_idx: app.custom_cursors.len() - 1,
cursor_grab: CursorGrabMode::None,
named_idx,
#[cfg(not(any(android_platform, ios_platform)))]
surface,
window,
theme,
ime,
cursor_position: Default::default(),
cursor_hidden: Default::default(),
modifiers: Default::default(),
occluded: Default::default(),
rotated: Default::default(),
zoom: Default::default(),
};
state.resize(size);
Ok(state)
}
pub fn toggle_ime(&mut self) {
self.ime = !self.ime;
self.window.set_ime_allowed(self.ime);
if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
self.window
.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
}
}
pub fn minimize(&mut self) {
self.window.set_minimized(true);
}
pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
self.cursor_position = Some(position);
if self.ime {
self.window
.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
}
}
pub fn cursor_left(&mut self) {
self.cursor_position = None;
}
/// Toggle maximized.
fn toggle_maximize(&self) {
let maximized = self.window.is_maximized();
self.window.set_maximized(!maximized);
}
/// Toggle window decorations.
fn toggle_decorations(&self) {
let decorated = self.window.is_decorated();
self.window.set_decorations(!decorated);
}
/// Toggle window resizable state.
fn toggle_resizable(&self) {
let resizable = self.window.is_resizable();
self.window.set_resizable(!resizable);
}
/// Toggle cursor visibility
fn toggle_cursor_visibility(&mut self) {
self.cursor_hidden = !self.cursor_hidden;
self.window.set_cursor_visible(!self.cursor_hidden);
}
/// Toggle resize increments on a window.
fn toggle_resize_increments(&mut self) {
let new_increments = match self.window.resize_increments() {
Some(_) => None,
None => Some(LogicalSize::new(25.0, 25.0)),
};
println!("Had increments: {}", new_increments.is_none());
self.window.set_resize_increments(new_increments);
}
/// Toggle fullscreen.
fn toggle_fullscreen(&self) {
let fullscreen = if self.window.fullscreen().is_some() {
None
} else {
Some(Fullscreen::Borderless(None))
};
self.window.set_fullscreen(fullscreen);
}
/// Cycle through the grab modes ignoring errors.
fn cycle_cursor_grab(&mut self) {
self.cursor_grab = match self.cursor_grab {
CursorGrabMode::None => CursorGrabMode::Confined,
CursorGrabMode::Confined => CursorGrabMode::Locked,
CursorGrabMode::Locked => CursorGrabMode::None,
};
println!("Changing cursor grab mode to {:?}", self.cursor_grab);
if let Err(err) = self.window.set_cursor_grab(self.cursor_grab) {
eprintln!("Error setting cursor grab: {err}");
}
}
#[cfg(macos_platform)]
fn cycle_option_as_alt(&mut self) {
self.option_as_alt = match self.option_as_alt {
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
OptionAsAlt::Both => OptionAsAlt::None,
};
println!("Setting option as alt {:?}", self.option_as_alt);
self.window.set_option_as_alt(self.option_as_alt);
}
/// Pick the next cursor.
fn next_cursor(&mut self) {
self.named_idx = (self.named_idx + 1) % CURSORS.len();
println!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]);
self.window
.set_cursor(Cursor::Icon(CURSORS[self.named_idx]));
}
/// Pick the next custom cursor.
fn next_custom_cursor(&mut self, custom_cursors: &[CustomCursor]) {
self.custom_idx = (self.custom_idx + 1) % custom_cursors.len();
let cursor = Cursor::Custom(custom_cursors[self.custom_idx].clone());
self.window.set_cursor(cursor);
}
/// Resize the window to the new size.
fn resize(&mut self, _size: PhysicalSize<u32>) {
#[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,
};
self.surface
.resize(width, height)
.expect("failed to resize inner buffer");
}
self.window.request_redraw();
}
/// Change the theme.
fn set_theme(&mut self, theme: Theme) {
self.theme = theme;
self.window.request_redraw();
}
/// Show window menu.
fn show_menu(&self) {
if let Some(position) = self.cursor_position {
self.window.show_window_menu(position);
}
}
/// Drag the window.
fn drag_window(&self) {
if let Err(err) = self.window.drag_window() {
println!("Error starting window drag: {err}");
} else {
println!("Dragging window Window={:?}", self.window.id());
}
}
/// Drag-resize the window.
fn drag_resize_window(&self) {
let position = match self.cursor_position {
Some(position) => position,
None => {
println!("Drag-resize requires cursor to be inside the window");
return;
}
};
let win_size = self.window.inner_size();
let border_size = BORDER_SIZE * self.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) = self.window.drag_resize_window(direction) {
println!("Error starting window drag-resize: {err}");
} else {
println!("Drag-resizing window Window={:?}", self.window.id());
}
}
/// Change window occlusion state.
fn set_occluded(&mut self, occluded: bool) {
self.occluded = occluded;
if !occluded {
self.window.request_redraw();
}
}
/// Draw the window contents.
#[cfg(not(any(android_platform, ios_platform)))]
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
if self.occluded {
println!("Skipping drawing occluded window={:?}", self.window.id());
return Ok(());
}
const WHITE: u32 = 0xFFFFFFFF;
const DARK_GRAY: u32 = 0xFF181818;
let color = match self.theme {
Theme::Light => WHITE,
Theme::Dark => DARK_GRAY,
};
let mut buffer = self.surface.buffer_mut()?;
buffer.fill(color);
self.window.pre_present_notify();
buffer.present()?;
Ok(())
}
#[cfg(any(android_platform, ios_platform))]
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
println!("Drawing but without rendering...");
Ok(())
}
}
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
}
}
#[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",
}
}
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self, f)
}
}
fn decode_cursor(bytes: &[u8]) -> CustomCursorSource {
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()
}
fn load_icon(path: &Path) -> Icon {
let (icon_rgba, icon_width, icon_height) = {
let image = image::open(path)
.expect("Failed to open icon path")
.into_rgba8();
let (width, height) = image.dimensions();
let rgba = image.into_raw();
(rgba, width, height)
};
Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
}
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
}
fn mouse_button_to_string(button: MouseButton) -> &'static str {
match button {
MouseButton::Left => "LMB",
MouseButton::Right => "RMB",
MouseButton::Middle => "MMB",
MouseButton::Back => "Back",
MouseButton::Forward => "Forward",
MouseButton::Other(_) => "",
}
}
/// 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,
];
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,
),
];

72
examples/x11_embed.rs Normal file
View File

@@ -0,0 +1,72 @@
//! A demonstration of embedding a winit window in an existing X11 application.
use std::error::Error;
#[cfg(x11_platform)]
fn main() -> Result<(), Box<dyn Error>> {
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::platform::x11::WindowAttributesExtX11;
use winit::window::{Window, WindowId};
#[path = "util/fill.rs"]
mod fill;
pub struct XEmbedDemo {
parent_window_id: u32,
window: Option<Window>,
}
impl ApplicationHandler for XEmbedDemo {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes()
.with_title("An embedded window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(self.parent_window_id);
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let window = self.window.as_ref().unwrap();
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
window.pre_present_notify();
fill::fill_window(window);
}
_ => (),
}
}
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
self.window.as_ref().unwrap().request_redraw();
}
}
// First argument should be a 32-bit X11 window ID.
let parent_window_id = std::env::args()
.nth(1)
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
.parse::<u32>()?;
tracing_subscriber::fmt::init();
let event_loop = EventLoop::new()?;
let mut app = XEmbedDemo {
parent_window_id,
window: None,
};
event_loop.run_app(&mut app).map_err(Into::into)
}
#[cfg(not(x11_platform))]
fn main() -> Result<(), Box<dyn Error>> {
println!("This example is only supported on X11 platforms.");
Ok(())
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -1,7 +1,10 @@
[package] [package]
name = "run-wasm" name = "run-wasm"
version = "0.1.0" version = "0.1.0"
edition = "2021" rust-version.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
publish = false publish = false
[dependencies] [dependencies]

221
src/application.rs Normal file
View File

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

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

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

View File

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -5,15 +5,14 @@ use std::{error::Error, hash::Hash};
use cursor_icon::CursorIcon; use cursor_icon::CursorIcon;
use crate::event_loop::EventLoopWindowTarget; use crate::platform_impl::{PlatformCustomCursor, PlatformCustomCursorSource};
use crate::platform_impl::{self, PlatformCustomCursor, PlatformCustomCursorBuilder};
/// The maximum width and height for a cursor when using [`CustomCursor::from_rgba`]. /// The maximum width and height for a cursor when using [`CustomCursor::from_rgba`].
pub const MAX_CURSOR_SIZE: u16 = 2048; 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),
@@ -49,31 +48,28 @@ impl From<CustomCursor> for Cursor {
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
/// use winit::{ /// # use winit::event_loop::ActiveEventLoop;
/// event::{Event, WindowEvent}, /// # use winit::window::Window;
/// event_loop::{ControlFlow, EventLoop}, /// # fn scope(event_loop: &ActiveEventLoop, window: &Window) {
/// window::{CustomCursor, Window}, /// use winit::window::CustomCursor;
/// };
///
/// let mut event_loop = EventLoop::new().unwrap();
/// ///
/// let w = 10; /// let w = 10;
/// let h = 10; /// let h = 10;
/// let rgba = vec![255; (w * h * 4) as usize]; /// let rgba = vec![255; (w * h * 4) as usize];
/// ///
/// #[cfg(not(target_family = "wasm"))] /// #[cfg(not(target_family = "wasm"))]
/// let builder = CustomCursor::from_rgba(rgba, w, h, w / 2, h / 2).unwrap(); /// let source = CustomCursor::from_rgba(rgba, w, h, w / 2, h / 2).unwrap();
/// ///
/// #[cfg(target_family = "wasm")] /// #[cfg(target_family = "wasm")]
/// let builder = { /// let source = {
/// use winit::platform::web::CustomCursorExtWebSys; /// 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)
/// }; /// };
/// ///
/// let custom_cursor = builder.build(&event_loop); /// let custom_cursor = event_loop.create_custom_cursor(source);
/// ///
/// let window = Window::new(&event_loop).unwrap();
/// window.set_cursor(custom_cursor.clone()); /// window.set_cursor(custom_cursor.clone());
/// # }
/// ``` /// ```
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct CustomCursor { pub struct CustomCursor {
@@ -91,9 +87,18 @@ impl CustomCursor {
height: u16, height: u16,
hotspot_x: u16, hotspot_x: u16,
hotspot_y: u16, hotspot_y: u16,
) -> Result<CustomCursorBuilder, BadImage> { ) -> Result<CustomCursorSource, BadImage> {
Ok(CustomCursorBuilder { let _span = tracing::debug_span!(
inner: PlatformCustomCursorBuilder::from_rgba( "winit::Cursor::from_rgba",
width,
height,
hotspot_x,
hotspot_y
)
.entered();
Ok(CustomCursorSource {
inner: PlatformCustomCursorSource::from_rgba(
rgba.into(), rgba.into(),
width, width,
height, height,
@@ -104,20 +109,12 @@ impl CustomCursor {
} }
} }
/// Builds a [`CustomCursor`]. /// Source for [`CustomCursor`].
/// ///
/// See [`CustomCursor`] for more details. /// See [`CustomCursor`] for more details.
#[derive(Debug)] #[derive(Debug)]
pub struct CustomCursorBuilder { pub struct CustomCursorSource {
pub(crate) inner: PlatformCustomCursorBuilder, pub(crate) inner: PlatformCustomCursorSource,
}
impl CustomCursorBuilder {
pub fn build(self, window_target: &EventLoopWindowTarget) -> CustomCursor {
CustomCursor {
inner: PlatformCustomCursor::build(self.inner, &window_target.p),
}
}
} }
/// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments. /// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments.
@@ -178,12 +175,14 @@ impl fmt::Display for BadImage {
impl Error for BadImage {} impl Error for BadImage {}
/// Platforms export this directly as `PlatformCustomCursorBuilder` if they need to only work with images. /// Platforms export this directly as `PlatformCustomCursorSource` if they need to only work with
/// images.
#[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct OnlyCursorImageBuilder(pub(crate) CursorImage); pub(crate) struct OnlyCursorImageSource(pub(crate) CursorImage);
#[allow(dead_code)] #[allow(dead_code)]
impl OnlyCursorImageBuilder { impl OnlyCursorImageSource {
pub(crate) fn from_rgba( pub(crate) fn from_rgba(
rgba: Vec<u8>, rgba: Vec<u8>,
width: u16, width: u16,
@@ -196,6 +195,7 @@ impl OnlyCursorImageBuilder {
} }
/// 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>);
@@ -213,16 +213,6 @@ impl PartialEq for OnlyCursorImage {
impl Eq for OnlyCursorImage {} impl Eq for OnlyCursorImage {}
#[allow(dead_code)]
impl OnlyCursorImage {
pub(crate) fn build(
builder: OnlyCursorImageBuilder,
_: &platform_impl::EventLoopWindowTarget,
) -> Self {
Self(Arc::new(builder.0))
}
}
#[derive(Debug)] #[derive(Debug)]
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) struct CursorImage { pub(crate) struct CursorImage {
@@ -297,8 +287,4 @@ impl NoCustomCursor {
CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y)?; CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y)?;
Ok(Self) Ok(Self)
} }
fn build(self, _: &platform_impl::EventLoopWindowTarget) -> NoCustomCursor {
self
}
} }

View File

@@ -1,8 +1,6 @@
use crate::platform_impl;
use std::{error, fmt}; use std::{error, fmt};
#[doc(inline)] use crate::platform_impl;
pub use winit_core::error::NotSupportedError;
// TODO: Rename // TODO: Rename
/// An error that may be generated when requesting Winit state /// An error that may be generated when requesting Winit state
@@ -16,10 +14,10 @@ pub enum ExternalError {
Os(OsError), Os(OsError),
} }
impl From<winit_core::event::InnerSizeIgnored> for ExternalError { /// The error type for when the requested operation is not supported by the backend.
fn from(_: winit_core::event::InnerSizeIgnored) -> Self { #[derive(Clone)]
Self::Ignored pub struct NotSupportedError {
} _marker: (),
} }
/// The error type for when the OS cannot perform the requested operation. /// The error type for when the OS cannot perform the requested operation.
@@ -49,6 +47,14 @@ impl From<OsError> for EventLoopError {
} }
} }
impl NotSupportedError {
#[inline]
#[allow(dead_code)]
pub(crate) fn new() -> NotSupportedError {
NotSupportedError { _marker: () }
}
}
impl OsError { impl OsError {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError { pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError {
@@ -82,6 +88,18 @@ impl fmt::Display for ExternalError {
} }
} }
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 { impl fmt::Display for EventLoopError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self { match self {
@@ -95,6 +113,7 @@ impl fmt::Display for EventLoopError {
impl error::Error for OsError {} impl error::Error for OsError {}
impl error::Error for ExternalError {} impl error::Error for ExternalError {}
impl error::Error for NotSupportedError {}
impl error::Error for EventLoopError {} impl error::Error for EventLoopError {}
#[cfg(test)] #[cfg(test)]
@@ -106,6 +125,11 @@ mod tests {
// Eat attributes for testing // Eat attributes for testing
#[test] #[test]
fn ensure_fmt_does_not_panic() { fn ensure_fmt_does_not_panic() {
let _ = format!(
"{:?}, {}",
NotSupportedError::new(),
NotSupportedError::new().clone()
);
let _ = format!( let _ = format!(
"{:?}, {}", "{:?}, {}",
ExternalError::NotSupported(NotSupportedError::new()), ExternalError::NotSupported(NotSupportedError::new()),

View File

@@ -1,263 +1,120 @@
//! Incoming notifications from the GUI system. //! The [`Event`] enum and assorted supporting types.
//!
use crate::dpi::{PhysicalPosition, PhysicalSize}; //! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get
use crate::event_loop::AsyncRequestSerial; //! processed and used to modify the program state. For more details, see the root-level documentation.
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState}; //!
use crate::window::{ActivationToken, Theme, WindowId}; //! Some of these events represent different "parts" of a traditional event-handling loop. You could
//! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this:
use std::fmt; //!
//! ```rust,ignore
//! let mut start_cause = StartCause::Init;
//!
//! while !elwt.exiting() {
//! app.new_events(event_loop, start_cause);
//!
//! for event in (window events, user events, device events) {
//! // This will pick the right method on the application based on the event.
//! app.handle_event(event_loop, event);
//! }
//!
//! for window_id in (redraw windows) {
//! app.window_event(event_loop, window_id, RedrawRequested);
//! }
//!
//! app.about_to_wait(event_loop);
//! start_cause = wait_if_necessary();
//! }
//!
//! app.exiting(event_loop);
//! ```
//!
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
//! describes what happens in what order.
//!
//! [`EventLoop::run_app(...)`]: crate::event_loop::EventLoop::run_app
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Mutex, Weak}; use std::sync::{Mutex, Weak};
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
use std::time::Instant; use std::time::Instant;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time::Instant; use web_time::Instant;
#[cfg(feature = "serde")] use crate::error::ExternalError;
pub use serde::{Deserialize, Serialize}; #[cfg(doc)]
use crate::window::Window;
use smol_str::SmolStr; use crate::{
dpi::{PhysicalPosition, PhysicalSize},
/// Identifier of an input device. event_loop::AsyncRequestSerial,
/// keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState},
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which platform_impl,
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or window::{ActivationToken, Theme, WindowId},
/// physical. Virtual devices typically aggregate inputs from multiple physical devices. };
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(u64);
impl DeviceId {
/// Returns a dummy id, useful for unit testing.
///
/// # Safety
///
/// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `DeviceId`.
///
/// **Passing this into a winit function will result in undefined behavior.**
pub const unsafe fn dummy() -> Self {
DeviceId(0)
}
}
impl From<u64> for DeviceId {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<DeviceId> for u64 {
fn from(value: DeviceId) -> Self {
value.0
}
}
/// Describes a generic event. /// Describes a generic event.
/// ///
/// See the module-level docs for more information on the event loop manages each event. /// See the module-level docs for more information on the event loop manages each event.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Event<T: 'static, KeyExtra> { pub enum Event<T: 'static> {
/// Emitted when new events arrive from the OS to be processed. /// See [`ApplicationHandler::new_events`] for details.
/// ///
/// This event type is useful as a place to put code that should be done before you start /// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events
/// processing events, such as updating frame timing information for benchmarking or checking
/// the [`StartCause`] to see if a timer set by
/// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed.
NewEvents(StartCause), NewEvents(StartCause),
/// Emitted when the OS sends an event to a winit window. /// See [`ApplicationHandler::window_event`] for details.
///
/// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event
WindowEvent { WindowEvent {
window_id: WindowId, window_id: WindowId,
event: WindowEvent<KeyExtra>, event: WindowEvent,
}, },
/// Emitted when the OS sends an event to a device. /// See [`ApplicationHandler::device_event`] for details.
///
/// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event
DeviceEvent { DeviceEvent {
device_id: DeviceId, device_id: DeviceId,
event: DeviceEvent, event: DeviceEvent,
}, },
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](https://docs.rs/winit/latest/winit/event_loop/struct.EventLoopProxy.html#method.send_event) /// See [`ApplicationHandler::user_event`] for details.
///
/// [`ApplicationHandler::user_event`]: crate::application::ApplicationHandler::user_event
UserEvent(T), UserEvent(T),
/// Emitted when the application has been suspended. /// See [`ApplicationHandler::suspended`] for details.
/// ///
/// # Portability /// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended
///
/// Not all platforms support the notion of suspending applications, and there may be no
/// technical way to guarantee being able to emit a `Suspended` event if the OS has
/// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason,
/// Winit does not currently try to emit pseudo `Suspended` events before the application
/// quits on platforms without an application lifecycle.
///
/// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] events.
///
/// Also see [`Resumed`] notes.
///
/// ## Android
///
/// On Android, the `Suspended` event is only sent when the application's associated
/// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`]
/// lifecycle event but there may technically be a discrepancy.
///
/// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause()
///
/// Applications that need to run on Android should assume their [`SurfaceView`] has been
/// destroyed, which indirectly invalidates any existing render surfaces that may have been
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
///
/// After being `Suspended` on Android applications must drop all render surfaces before
/// the event callback completes, which may be re-created when the application is next [`Resumed`].
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Suspended` event is currently emitted in response to an
/// [`applicationWillResignActive`] callback which means that the application is
/// about to transition from the active to inactive state (according to the
/// [iOS application lifecycle]).
///
/// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event
/// with the property [`persisted`] being true, which means that the page is being
/// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a
/// complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
///
/// [`Resumed`]: Self::Resumed
Suspended, Suspended,
/// Emitted when the application has been resumed. /// See [`ApplicationHandler::resumed`] for details.
/// ///
/// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a /// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
/// formal suspend/resume lifecycle. For systems without a standard suspend/resume lifecycle
/// the `Resumed` event is always emitted after the [`NewEvents(StartCause::Init)`][StartCause::Init]
/// event.
///
/// # Portability
///
/// It's recommended that applications should only initialize their graphics context and create
/// a window after they have received their first `Resumed` event. Some systems
/// (specifically Android) won't allow applications to create a render surface until they are
/// resumed.
///
/// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events.
///
/// Also see [`Suspended`] notes.
///
/// ## Android
///
/// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is
/// expected to closely correlate with the [`onResume`] lifecycle event but there may technically
/// be a discrepancy.
///
/// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
///
/// Applications that need to run on Android must wait until they have been `Resumed`
/// before they will be able to create a render surface (such as an `EGLSurface`,
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a
/// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their
/// render surfaces are invalid and should be dropped.
///
/// Also see [`Suspended`] notes.
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`]
/// callback which means the application is "active" (according to the
/// [iOS application lifecycle]).
///
/// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event
/// with the property [`persisted`] being true, which means that the page is being
/// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
/// stores a complete snapshot of a page (including the JavaScript heap) as the
/// user is navigating away.
///
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
///
/// [`Suspended`]: Self::Suspended
Resumed, Resumed,
/// Emitted when the event loop is about to block and wait for new events. /// See [`ApplicationHandler::about_to_wait`] for details.
/// ///
/// Most applications shouldn't need to hook into this event since there is no real relationship /// [`ApplicationHandler::about_to_wait`]: crate::application::ApplicationHandler::about_to_wait
/// between how often the event loop needs to wake up and the dispatching of any specific events.
///
/// High frequency event sources, such as input devices could potentially lead to lots of wake
/// ups and also lots of corresponding `AboutToWait` events.
///
/// This is not an ideal event to drive application rendering from and instead applications
/// should render in response to [`WindowEvent::RedrawRequested`] events.
AboutToWait, AboutToWait,
/// Emitted when the event loop is being shut down. /// See [`ApplicationHandler::exiting`] for details.
/// ///
/// This is irreversible - if this event is emitted, it is guaranteed to be the last event that /// [`ApplicationHandler::exiting`]: crate::application::ApplicationHandler::exiting
/// gets emitted. You generally want to treat this as a "do on quit" event.
LoopExiting, LoopExiting,
/// Emitted when the application has received a memory warning. /// See [`ApplicationHandler::memory_warning`] for details.
/// ///
/// ## Platform-specific /// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning
///
/// ### Android
///
/// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application
/// must [release memory] or risk being killed.
///
/// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory()
/// [release memory]: https://developer.android.com/topic/performance/memory#release
///
/// ### iOS
///
/// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`]
/// callback. The application must free as much memory as possible or risk being terminated, see
/// [how to respond to memory warnings].
///
/// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni
/// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings
///
/// ### Others
///
/// - **macOS / Wayland / Windows / Orbital:** Unsupported.
MemoryWarning, MemoryWarning,
} }
impl<T, Extra> Event<T, Extra> { impl<T> Event<T> {
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
pub fn map_nonuser_event<U>(self) -> Result<Event<U, Extra>, Event<T, Extra>> { pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
use self::Event::*; use self::Event::*;
match self { match self {
UserEvent(_) => Err(self), UserEvent(_) => Err(self),
@@ -304,14 +161,12 @@ pub enum StartCause {
} }
/// Describes an event from a [`Window`]. /// Describes an event from a [`Window`].
///
/// [`Window`]: https://docs.rs/winit/latest/winit/window/struct.Window.html
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum WindowEvent<KeyExtra> { pub enum WindowEvent {
/// The activation token was delivered back and now could be used. /// The activation token was delivered back and now could be used.
/// ///
#[cfg_attr( #[cfg_attr(
not(any(x11_platform, wayland_platfrom)), not(any(x11_platform, wayland_platform)),
allow(rustdoc::broken_intra_doc_links) allow(rustdoc::broken_intra_doc_links)
)] )]
/// Delivered in response to [`request_activation_token`]. /// Delivered in response to [`request_activation_token`].
@@ -369,7 +224,7 @@ pub enum WindowEvent<KeyExtra> {
/// events which are not marked as `is_synthetic`. /// events which are not marked as `is_synthetic`.
KeyboardInput { KeyboardInput {
device_id: DeviceId, device_id: DeviceId,
event: KeyEvent<KeyExtra>, event: KeyEvent,
/// If `true`, the event was generated synthetically by winit /// If `true`, the event was generated synthetically by winit
/// in one of the following circumstances: /// in one of the following circumstances:
@@ -393,8 +248,6 @@ pub enum WindowEvent<KeyExtra> {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web / Orbital:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
///
/// [`Window::set_ime_allowed`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_allowed
Ime(Ime), Ime(Ime),
/// The cursor has moved on the window. /// The cursor has moved on the window.
@@ -477,7 +330,7 @@ pub enum WindowEvent<KeyExtra> {
/// The event is general enough that its generating gesture is allowed to vary /// The event is general enough that its generating gesture is allowed to vary
/// across platforms. It could also be generated by another device. /// across platforms. It could also be generated by another device.
/// ///
/// Unfortunatly, neither [Windows](https://support.microsoft.com/en-us/windows/touch-gestures-for-windows-a9d28305-4818-a5df-4e2b-e5590f850741) /// Unfortunately, neither [Windows](https://support.microsoft.com/en-us/windows/touch-gestures-for-windows-a9d28305-4818-a5df-4e2b-e5590f850741)
/// nor [Wayland](https://wayland.freedesktop.org/libinput/doc/latest/gestures.html) /// nor [Wayland](https://wayland.freedesktop.org/libinput/doc/latest/gestures.html)
/// support this gesture or any other gesture with the same effect. /// support this gesture or any other gesture with the same effect.
/// ///
@@ -540,11 +393,10 @@ pub enum WindowEvent<KeyExtra> {
/// * Changing the display's scale factor (e.g. in Control Panel on Windows). /// * Changing the display's scale factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different scale factor. /// * Moving the window to a display with a different scale factor.
/// ///
/// After this event callback has been processed, the window will be resized to whatever value /// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the window is
/// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested /// resized to the value suggested by the OS, but it can be changed to any value.
/// by the OS, but it can be changed to any value.
/// ///
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module. /// For more information about DPI in general, see the [`dpi`] crate.
ScaleFactorChanged { ScaleFactorChanged {
scale_factor: f64, scale_factor: f64,
/// Handle to update inner size during scale changes. /// Handle to update inner size during scale changes.
@@ -600,11 +452,33 @@ pub enum WindowEvent<KeyExtra> {
/// ///
/// Winit will aggregate duplicate redraw requests into a single event, to /// Winit will aggregate duplicate redraw requests into a single event, to
/// help avoid duplicating rendering work. /// help avoid duplicating rendering work.
///
/// [`Window::request_redraw`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.request_redraw
RedrawRequested, RedrawRequested,
} }
/// Identifier of an input device.
///
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
/// physical. Virtual devices typically aggregate inputs from multiple physical devices.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(pub(crate) platform_impl::DeviceId);
impl DeviceId {
/// Returns a dummy id, useful for unit testing.
///
/// # Safety
///
/// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `DeviceId`.
///
/// **Passing this into a winit function will result in undefined behavior.**
pub const unsafe fn dummy() -> Self {
#[allow(unused_unsafe)]
DeviceId(unsafe { platform_impl::DeviceId::dummy() })
}
}
/// Represents raw hardware events that are not associated with any particular window. /// Represents raw hardware events that are not associated with any particular window.
/// ///
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person /// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person
@@ -665,7 +539,7 @@ pub struct RawKeyEvent {
/// Describes a keyboard input targeting a window. /// Describes a keyboard input targeting a window.
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeyEvent<Extra> { pub struct KeyEvent {
/// Represents the position of a key independent of the currently active layout. /// Represents the position of a key independent of the currently active layout.
/// ///
/// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode).
@@ -715,7 +589,7 @@ pub struct KeyEvent<Extra> {
/// - **Web:** Dead keys might be reported as the real key instead /// - **Web:** Dead keys might be reported as the real key instead
/// of `Dead` depending on the browser/OS. /// of `Dead` depending on the browser/OS.
/// ///
/// [`key_without_modifiers`]: https://docs.rs/winit/latest/winit/platform/modifier_supplement/trait.KeyEventExtModifierSupplement.html#tymethod.key_without_modifiers /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
pub logical_key: keyboard::Key, pub logical_key: keyboard::Key,
/// Contains the text produced by this keypress. /// Contains the text produced by this keypress.
@@ -764,6 +638,31 @@ pub struct KeyEvent<Extra> {
/// On some systems, holding down a key for some period of time causes that key to be repeated /// On some systems, holding down a key for some period of time causes that key to be repeated
/// as though it were being pressed and released repeatedly. This field is `true` if and only if /// as though it were being pressed and released repeatedly. This field is `true` if and only if
/// this event is the result of one of those repeats. /// this event is the result of one of those repeats.
///
/// # Example
///
/// In games, you often want to ignore repated key events - this can be
/// done by ignoring events where this property is set.
///
/// ```
/// use winit::event::{WindowEvent, KeyEvent, ElementState};
/// use winit::keyboard::{KeyCode, PhysicalKey};
/// # let window_event = WindowEvent::RedrawRequested; // To make the example compile
/// match window_event {
/// WindowEvent::KeyboardInput {
/// event: KeyEvent {
/// physical_key: PhysicalKey::Code(KeyCode::KeyW),
/// state: ElementState::Pressed,
/// repeat: false,
/// ..
/// },
/// ..
/// } => {
/// // The physical key `W` was pressed, and it was not a repeat
/// }
/// _ => {} // Handle other events
/// }
/// ```
pub repeat: bool, pub repeat: bool,
/// Platform-specific key event information. /// Platform-specific key event information.
@@ -772,7 +671,7 @@ pub struct KeyEvent<Extra> {
/// modifiers applied. /// modifiers applied.
/// ///
/// On Android, iOS, Redox and Web, this type is a no-op. /// On Android, iOS, Redox and Web, this type is a no-op.
pub extra: Extra, pub(crate) platform_specific: platform_impl::KeyEventExtra,
} }
/// Describes keyboard modifiers event. /// Describes keyboard modifiers event.
@@ -787,15 +686,6 @@ pub struct Modifiers {
} }
impl Modifiers { impl Modifiers {
/// Only `winit` should instantiate this!
#[doc(hidden)]
pub fn new(state: ModifiersState, pressed_mods: ModifiersKeys) -> Self {
Self {
state,
pressed_mods,
}
}
/// The state of the modifiers. /// The state of the modifiers.
pub fn state(&self) -> ModifiersState { pub fn state(&self) -> ModifiersState {
self.state self.state
@@ -895,18 +785,14 @@ impl From<ModifiersState> for Modifiers {
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. /// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
/// Ime::Commit("啊不") /// Ime::Commit("啊不")
/// ``` /// ```
///
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Ime { pub enum Ime {
/// Notifies when the IME was enabled. /// Notifies when the IME was enabled.
/// ///
/// After getting this event you could receive [`Preedit`](Self::Preedit) and /// After getting this event you could receive [`Preedit`][Self::Preedit] and
/// [`Commit`](Self::Commit) events. You should also start performing IME related requests /// [`Commit`][Self::Commit] events. You should also start performing IME related requests
/// like [`Window::set_ime_cursor_area`]. /// like [`Window::set_ime_cursor_area`].
///
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
Enabled, Enabled,
/// Notifies when a new composing text should be set at the cursor position. /// Notifies when a new composing text should be set at the cursor position.
@@ -925,12 +811,10 @@ pub enum Ime {
/// Notifies when the IME was disabled. /// Notifies when the IME was disabled.
/// ///
/// After receiving this event you won't get any more [`Preedit`](Self::Preedit) or /// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or
/// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You should /// [`Commit`][Self::Commit] events until the next [`Enabled`][Self::Enabled] event. You should
/// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear pending /// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear pending
/// preedit text. /// preedit text.
///
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
Disabled, Disabled,
} }
@@ -1110,7 +994,7 @@ pub enum MouseScrollDelta {
PixelDelta(PhysicalPosition<f64>), PixelDelta(PhysicalPosition<f64>),
} }
/// Handle to synchroniously change the size of the window from the /// Handle to synchronously change the size of the window from the
/// [`WindowEvent`]. /// [`WindowEvent`].
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InnerSizeWriter { pub struct InnerSizeWriter {
@@ -1119,27 +1003,22 @@ pub struct InnerSizeWriter {
impl InnerSizeWriter { impl InnerSizeWriter {
#[cfg(not(orbital_platform))] #[cfg(not(orbital_platform))]
pub fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self { pub(crate) fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
Self { new_inner_size } Self { new_inner_size }
} }
/// Try to request inner size which will be set synchroniously on the window. /// Try to request inner size which will be set synchronously on the window.
pub fn request_inner_size( pub fn request_inner_size(
&mut self, &mut self,
new_inner_size: PhysicalSize<u32>, new_inner_size: PhysicalSize<u32>,
) -> Result<(), InnerSizeIgnored> { ) -> Result<(), ExternalError> {
if let Some(inner) = self.new_inner_size.upgrade() { if let Some(inner) = self.new_inner_size.upgrade() {
*inner.lock().unwrap() = new_inner_size; *inner.lock().unwrap() = new_inner_size;
Ok(()) Ok(())
} else { } else {
Err(InnerSizeIgnored { _private: () }) Err(ExternalError::Ignored)
} }
} }
/// Get the underlying size.
pub fn get(&self) -> PhysicalSize<u32> {
*self.new_inner_size.upgrade().unwrap().lock().unwrap()
}
} }
impl PartialEq for InnerSizeWriter { impl PartialEq for InnerSizeWriter {
@@ -1148,25 +1027,6 @@ impl PartialEq for InnerSizeWriter {
} }
} }
/// Could not write the inner size.
pub struct InnerSizeIgnored {
_private: (),
}
impl fmt::Debug for InnerSizeIgnored {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InnerSizeIgnored").finish_non_exhaustive()
}
}
impl fmt::Display for InnerSizeIgnored {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("tried to write inner size after")
}
}
impl std::error::Error for InnerSizeIgnored {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::event; use crate::event;
@@ -1292,7 +1152,7 @@ mod tests {
#[allow(clippy::redundant_clone)] #[allow(clippy::redundant_clone)]
#[test] #[test]
fn test_event_clone() { fn test_event_clone() {
foreach_event!(|event: event::Event<(), ()>| { foreach_event!(|event: event::Event<()>| {
let event2 = event.clone(); let event2 = event.clone();
assert_eq!(event, event2); assert_eq!(event, event2);
}) })
@@ -1300,7 +1160,7 @@ mod tests {
#[test] #[test]
fn test_map_nonuser_event() { fn test_map_nonuser_event() {
foreach_event!(|event: event::Event<(), ()>| { foreach_event!(|event: event::Event<()>| {
let is_user = matches!(event, event::Event::UserEvent(())); let is_user = matches!(event, event::Event::UserEvent(()));
let event2 = event.map_nonuser_event::<()>(); let event2 = event.map_nonuser_event::<()>();
if is_user { if is_user {
@@ -1334,7 +1194,7 @@ mod tests {
#[allow(clippy::clone_on_copy)] #[allow(clippy::clone_on_copy)]
#[test] #[test]
fn ensure_attrs_do_not_panic() { fn ensure_attrs_do_not_panic() {
foreach_event!(|event: event::Event<(), ()>| { foreach_event!(|event: event::Event<()>| {
let _ = format!("{:?}", event); let _ = format!("{:?}", event);
}); });
let _ = event::StartCause::Init.clone(); let _ = event::StartCause::Init.clone();

View File

@@ -3,22 +3,25 @@
//! //!
//! If you want to send custom events to the event loop, use //! If you want to send custom events to the event loop, use
//! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its //! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its
//! [`send_event`](`EventLoopProxy::send_event`) method. //! [`send_event`][EventLoopProxy::send_event] method.
//! //!
//! See the root-level documentation for information on how to create and use an event loop to //! See the root-level documentation for information on how to create and use an event loop to
//! handle events. //! handle events.
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref;
#[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, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::{error, fmt}; use std::{error, fmt};
use crate::error::EventLoopError; #[cfg(not(web_platform))]
use crate::{event::Event, monitor::MonitorHandle, platform_impl}; use std::time::{Duration, Instant};
#[cfg(web_platform)]
use web_time::{Duration, Instant};
#[doc(inline)] use crate::application::ApplicationHandler;
pub use winit_core::event_loop::{AsyncRequestSerial, ControlFlow}; use crate::error::{EventLoopError, OsError};
use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
/// 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.
@@ -43,11 +46,9 @@ pub struct EventLoop<T: 'static> {
/// Target that associates windows with an [`EventLoop`]. /// Target that associates windows with an [`EventLoop`].
/// ///
/// This type exists to allow you to create new windows while Winit executes /// This type exists to allow you to create new windows while Winit executes
/// your callback. [`EventLoop`] will coerce into this type (`impl<T> Deref for /// your callback.
/// EventLoop<T>`), so functions that take this as a parameter can also take pub struct ActiveEventLoop {
/// `&EventLoop`. pub(crate) p: platform_impl::ActiveEventLoop,
pub struct EventLoopWindowTarget {
pub(crate) p: platform_impl::EventLoopWindowTarget,
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
} }
@@ -109,6 +110,8 @@ impl<T> EventLoopBuilder<T> {
)] )]
#[inline] #[inline]
pub fn build(&mut self) -> Result<EventLoop<T>, 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);
} }
@@ -133,9 +136,53 @@ impl<T> fmt::Debug for EventLoop<T> {
} }
} }
impl fmt::Debug for EventLoopWindowTarget { impl fmt::Debug for ActiveEventLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EventLoopWindowTarget { .. }") f.pad("ActiveEventLoop { .. }")
}
}
/// Set through [`ActiveEventLoop::set_control_flow()`].
///
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
///
/// Defaults to [`Wait`].
///
/// [`Wait`]: Self::Wait
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum ControlFlow {
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process.
Poll,
/// When the current loop iteration finishes, suspend the thread until another event arrives.
#[default]
Wait,
/// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the given time is reached.
///
/// Useful for implementing efficient timers. Applications which want to render at the display's
/// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
/// to reduce odds of missed frames.
///
/// [`Poll`]: Self::Poll
WaitUntil(Instant),
}
impl ControlFlow {
/// Creates a [`ControlFlow`] that waits until a timeout has expired.
///
/// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
/// instead set to [`Wait`].
///
/// [`WaitUntil`]: Self::WaitUntil
/// [`Wait`]: Self::Wait
pub fn wait_duration(timeout: Duration) -> Self {
match Instant::now().checked_add(timeout) {
Some(instant) => Self::WaitUntil(instant),
None => Self::Wait,
}
} }
} }
@@ -169,8 +216,22 @@ impl<T> EventLoop<T> {
} }
} }
/// Runs the event loop in the calling thread and calls the given `event_handler` closure /// See [`run_app`].
/// to dispatch any pending events. ///
/// [`run_app`]: Self::run_app
#[inline]
#[deprecated = "use `EventLoop::run_app` instead"]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &ActiveEventLoop),
{
let _span = tracing::debug_span!("winit::EventLoop::run").entered();
self.event_loop.run(event_handler)
}
/// Run the application with the event loop on the calling thread.
/// ///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
@@ -185,40 +246,100 @@ impl<T> EventLoop<T> {
/// Web applications are recommended to use /// Web applications are recommended to use
#[cfg_attr( #[cfg_attr(
web_platform, web_platform,
doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]" doc = "[`EventLoopExtWebSys::spawn_app()`][crate::platform::web::EventLoopExtWebSys::spawn_app()]"
)] )]
#[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")] #[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")]
/// [^1] instead of [`run()`] to avoid the need /// [^1] instead of [`run_app()`] to avoid the need
/// for the Javascript exception trick, and to make it clearer that the event loop runs /// for the Javascript exception trick, and to make it clearer that the event loop runs
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the /// asynchronously (via the browser's own, internal, event loop) and doesn't block the
/// current thread of execution like it does on other platforms. /// current thread of execution like it does on other platforms.
/// ///
/// This function won't be available with `target_feature = "exception-handling"`. /// This function won't be available with `target_feature = "exception-handling"`.
/// ///
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`run()`]: Self::run() /// [`run_app()`]: Self::run_app()
/// [^1]: `EventLoopExtWebSys::spawn()` is only available on Web. /// [^1]: `EventLoopExtWebSys::spawn_app()` is only available on Web.
#[inline] #[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))] #[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError> pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> {
where self.event_loop
F: FnMut(Event<T>, &EventLoopWindowTarget), .run(|event, event_loop| dispatch_event_for_app(app, event_loop, event))
{
self.event_loop.run(event_handler)
} }
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events to the main event loop. /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread.
pub fn create_proxy(&self) -> EventLoopProxy<T> { pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy { EventLoopProxy {
event_loop_proxy: self.event_loop.create_proxy(), event_loop_proxy: self.event_loop.create_proxy(),
} }
} }
/// Gets a persistent reference to the underlying platform display.
///
/// See the [`OwnedDisplayHandle`] type for more information.
pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle {
platform: self.event_loop.window_target().p.owned_display_handle(),
}
}
/// Change if or when [`DeviceEvent`]s are captured.
///
/// See [`ActiveEventLoop::listen_device_events`] for details.
///
/// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn listen_device_events(&self, allowed: DeviceEvents) {
let _span = tracing::debug_span!(
"winit::EventLoop::listen_device_events",
allowed = ?allowed
)
.entered();
self.event_loop
.window_target()
.p
.listen_device_events(allowed);
}
/// Sets the [`ControlFlow`].
pub fn set_control_flow(&self, control_flow: ControlFlow) {
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 _span = tracing::debug_span!(
"winit::EventLoop::create_window",
window_attributes = ?window_attributes
)
.entered();
let window =
platform_impl::Window::new(&self.event_loop.window_target().p, window_attributes)?;
Ok(Window { window })
}
/// Create custom cursor.
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
self.event_loop
.window_target()
.p
.create_custom_cursor(custom_cursor)
}
} }
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
impl<T> rwh_06::HasDisplayHandle for EventLoop<T> { impl<T> rwh_06::HasDisplayHandle for EventLoop<T> {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
rwh_06::HasDisplayHandle::display_handle(&**self) rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target())
} }
} }
@@ -226,7 +347,7 @@ impl<T> rwh_06::HasDisplayHandle for EventLoop<T> {
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> { unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop. /// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle { fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
rwh_05::HasRawDisplayHandle::raw_display_handle(&**self) rwh_05::HasRawDisplayHandle::raw_display_handle(self.event_loop.window_target())
} }
} }
@@ -234,11 +355,11 @@ unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
impl<T> AsFd for EventLoop<T> { impl<T> AsFd for EventLoop<T> {
/// Get the underlying [EventLoop]'s `fd` which you can register /// Get the underlying [EventLoop]'s `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_events`] API. /// loop must be polled with the [`pump_app_events`] API.
/// ///
/// [`calloop`]: https://crates.io/crates/calloop /// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio /// [`mio`]: https://crates.io/crates/mio
/// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events /// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events
fn as_fd(&self) -> BorrowedFd<'_> { fn as_fd(&self) -> BorrowedFd<'_> {
self.event_loop.as_fd() self.event_loop.as_fd()
} }
@@ -248,27 +369,49 @@ impl<T> AsFd for EventLoop<T> {
impl<T> AsRawFd for EventLoop<T> { impl<T> AsRawFd for EventLoop<T> {
/// Get the underlying [EventLoop]'s raw `fd` which you can register /// Get the underlying [EventLoop]'s raw `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_events`] API. /// loop must be polled with the [`pump_app_events`] API.
/// ///
/// [`calloop`]: https://crates.io/crates/calloop /// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio /// [`mio`]: https://crates.io/crates/mio
/// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events /// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.event_loop.as_raw_fd() self.event_loop.as_raw_fd()
} }
} }
impl<T> Deref for EventLoop<T> { impl ActiveEventLoop {
type Target = EventLoopWindowTarget; /// Create the window.
fn deref(&self) -> &EventLoopWindowTarget { ///
self.event_loop.window_target() /// Possible causes of error include denied permission, incompatible system, and lack of memory.
} ///
} /// ## Platform-specific
///
/// - **Web:** The window is created but not inserted into the web page automatically. Please
/// see the web platform module for more information.
#[inline]
pub fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError> {
let _span = tracing::debug_span!(
"winit::ActiveEventLoop::create_window",
window_attributes = ?window_attributes
)
.entered();
let window = platform_impl::Window::new(&self.p, window_attributes)?;
Ok(Window { window })
}
/// Create custom cursor.
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
let _span = tracing::debug_span!("winit::ActiveEventLoop::create_custom_cursor",).entered();
self.p.create_custom_cursor(custom_cursor)
}
impl EventLoopWindowTarget {
/// Returns the list of all the monitors available on the system. /// Returns the list of all the monitors available on the system.
#[inline] #[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
let _span = tracing::debug_span!("winit::ActiveEventLoop::available_monitors",).entered();
#[allow(clippy::useless_conversion)] // false positive on some platforms #[allow(clippy::useless_conversion)] // false positive on some platforms
self.p self.p
.available_monitors() .available_monitors()
@@ -285,6 +428,8 @@ impl EventLoopWindowTarget {
/// **Wayland / Web:** Always returns `None`. /// **Wayland / Web:** Always returns `None`.
#[inline] #[inline]
pub fn primary_monitor(&self) -> Option<MonitorHandle> { pub fn primary_monitor(&self) -> Option<MonitorHandle> {
let _span = tracing::debug_span!("winit::ActiveEventLoop::primary_monitor",).entered();
self.p self.p
.primary_monitor() .primary_monitor()
.map(|inner| MonitorHandle { inner }) .map(|inner| MonitorHandle { inner })
@@ -302,6 +447,12 @@ impl EventLoopWindowTarget {
/// ///
/// [`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!(
"winit::ActiveEventLoop::listen_device_events",
allowed = ?allowed
)
.entered();
self.p.listen_device_events(allowed); self.p.listen_device_events(allowed);
} }
@@ -317,14 +468,16 @@ impl EventLoopWindowTarget {
/// This exits the event loop. /// This exits the event loop.
/// ///
/// See [`LoopExiting`](Event::LoopExiting). /// See [`LoopExiting`][Event::LoopExiting].
pub fn exit(&self) { pub fn exit(&self) {
let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered();
self.p.exit() 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].
pub fn exiting(&self) -> bool { pub fn exiting(&self) -> bool {
self.p.exiting() self.p.exiting()
} }
@@ -340,7 +493,7 @@ impl EventLoopWindowTarget {
} }
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for EventLoopWindowTarget { impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.p.raw_display_handle_rwh_06()?; let raw = self.p.raw_display_handle_rwh_06()?;
// SAFETY: The display will never be deallocated while the event loop is alive. // SAFETY: The display will never be deallocated while the event loop is alive.
@@ -349,7 +502,7 @@ impl rwh_06::HasDisplayHandle for EventLoopWindowTarget {
} }
#[cfg(feature = "rwh_05")] #[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawDisplayHandle for EventLoopWindowTarget { unsafe impl rwh_05::HasRawDisplayHandle for ActiveEventLoop {
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop. /// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle { fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.p.raw_display_handle_rwh_05() self.p.raw_display_handle_rwh_05()
@@ -360,7 +513,7 @@ unsafe impl rwh_05::HasRawDisplayHandle for EventLoopWindowTarget {
/// ///
/// The purpose of this type is to provide a cheaply clonable 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 [`EventLoopWindowTarget`] /// 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
/// needed. /// needed.
/// ///
@@ -424,6 +577,8 @@ impl<T: 'static> EventLoopProxy<T> {
/// ///
/// [`UserEvent(event)`]: Event::UserEvent /// [`UserEvent(event)`]: Event::UserEvent
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
let _span = tracing::debug_span!("winit::EventLoopProxy::send_event",).entered();
self.event_loop_proxy.send_event(event) self.event_loop_proxy.send_event(event)
} }
} }
@@ -460,3 +615,49 @@ pub enum DeviceEvents {
/// Never capture device events. /// Never capture device events.
Never, Never,
} }
/// A unique identifier of the winit's async request.
///
/// This could be used to identify the async request once it's done
/// and a specific action must be taken.
///
/// One of the handling scenarios could be to maintain a working list
/// containing [`AsyncRequestSerial`] and some closure associated with it.
/// Then once event is arriving the working list is being traversed and a job
/// executed and removed from the list.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AsyncRequestSerial {
serial: usize,
}
impl AsyncRequestSerial {
// TODO(kchibisov): Remove `cfg` when the clipboard will be added.
#[allow(dead_code)]
pub(crate) fn get() -> Self {
static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0);
// NOTE: We rely on wrap around here, while the user may just request
// in the loop usize::MAX times that's issue is considered on them.
let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
Self { serial }
}
}
/// Shim for various run APIs.
#[inline(always)]
pub(crate) fn dispatch_event_for_app<T: 'static, A: ApplicationHandler<T>>(
app: &mut A,
event_loop: &ActiveEventLoop,
event: Event<T>,
) {
match event {
Event::NewEvents(cause) => app.new_events(event_loop, cause),
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
Event::UserEvent(event) => app.user_event(event_loop, event),
Event::Suspended => app.suspended(event_loop),
Event::Resumed => app.resumed(event_loop),
Event::AboutToWait => app.about_to_wait(event_loop),
Event::LoopExiting => app.exiting(event_loop),
Event::MemoryWarning => app.memory_warning(event_loop),
}
}

View File

@@ -118,6 +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 { Ok(Icon {
inner: PlatformIcon::from_rgba(rgba, width, height)?, inner: PlatformIcon::from_rgba(rgba, width, height)?,
}) })

View File

@@ -20,7 +20,7 @@
// the W3C short notice apply to the `Key` and `KeyCode` enums and their variants and the // the W3C short notice apply to the `Key` and `KeyCode` enums and their variants and the
// documentation attached to their variants. // documentation attached to their variants.
// --------- BEGGINING OF W3C LICENSE -------------------------------------------------------------- // --------- BEGINNING OF W3C LICENSE --------------------------------------------------------------
// //
// License // License
// //
@@ -55,7 +55,7 @@
// //
// --------- END OF W3C LICENSE -------------------------------------------------------------------- // --------- END OF W3C LICENSE --------------------------------------------------------------------
// --------- BEGGINING OF W3C SHORT NOTICE --------------------------------------------------------- // --------- BEGINNING OF W3C SHORT NOTICE ---------------------------------------------------------
// //
// winit: https://github.com/rust-windowing/winit // winit: https://github.com/rust-windowing/winit
// //
@@ -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))]
@@ -744,7 +744,7 @@ pub enum KeyCode {
/// - The `Super` variant here, is named `Meta` in the aforementioned specification. (There's /// - The `Super` variant here, is named `Meta` in the aforementioned specification. (There's
/// another key which the specification calls `Super`. That does not exist here.) /// another key which the specification calls `Super`. That does not exist here.)
/// - The `Space` variant here, can be identified by the character it generates in the /// - The `Space` variant here, can be identified by the character it generates in the
/// specificaiton. /// specification.
/// ///
/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/ /// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
#[non_exhaustive] #[non_exhaustive]
@@ -914,7 +914,7 @@ pub enum NamedKey {
Standby, Standby,
/// The WakeUp key. (`KEYCODE_WAKEUP`) /// The WakeUp key. (`KEYCODE_WAKEUP`)
WakeUp, WakeUp,
/// Initate the multi-candidate mode. /// Initiate the multi-candidate mode.
AllCandidates, AllCandidates,
Alphanumeric, Alphanumeric,
/// Initiate the Code Input mode to allow characters to be entered by /// Initiate the Code Input mode to allow characters to be entered by
@@ -1454,35 +1454,12 @@ pub enum NamedKey {
F35, F35,
} }
// NOTE: the exact modifier key is not used to represent modifiers state in the
// first place due to a fact that modifiers state could be changed without any
// key being pressed and on some platforms like Wayland/X11 which key resulted
// in modifiers change is hidden, also, not that it really matters.
//
// The reason this API is even exposed is mostly to provide a way for users
// to treat modifiers differently based on their position, which is required
// on macOS due to their AltGr/Option situation.
bitflags! {
#[doc(hidden)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModifiersKeys: u8 {
const LSHIFT = 0b0000_0001;
const RSHIFT = 0b0000_0010;
const LCONTROL = 0b0000_0100;
const RCONTROL = 0b0000_1000;
const LALT = 0b0001_0000;
const RALT = 0b0010_0000;
const LSUPER = 0b0100_0000;
const RSUPER = 0b1000_0000;
}
}
/// Key represents the meaning of a keypress. /// Key represents the meaning of a keypress.
/// ///
/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with /// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with
/// additions: /// additions:
/// - All simple variants are wrapped under the `Named` variant /// - All simple variants are wrapped under the `Named` variant
/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`. /// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`.
/// - The `Dead` variant here, can specify the character which is inserted when pressing the /// - The `Dead` variant here, can specify the character which is inserted when pressing the
/// dead-key twice. /// dead-key twice.
/// ///
@@ -1588,7 +1565,7 @@ impl NamedKey {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use winit_core::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);
@@ -1611,7 +1588,7 @@ impl Key {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use winit_core::keyboard::{NamedKey, Key}; /// use winit::keyboard::{NamedKey, Key};
/// ///
/// assert_eq!(Key::Character("a".into()).to_text(), Some("a")); /// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
/// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r")); /// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
@@ -1750,6 +1727,28 @@ pub enum ModifiersKeyState {
Unknown, Unknown,
} }
// NOTE: the exact modifier key is not used to represent modifiers state in the
// first place due to a fact that modifiers state could be changed without any
// key being pressed and on some platforms like Wayland/X11 which key resulted
// in modifiers change is hidden, also, not that it really matters.
//
// The reason this API is even exposed is mostly to provide a way for users
// to treat modifiers differently based on their position, which is required
// on macOS due to their AltGr/Option situation.
bitflags! {
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct ModifiersKeys: u8 {
const LSHIFT = 0b0000_0001;
const RSHIFT = 0b0000_0010;
const LCONTROL = 0b0000_0100;
const RCONTROL = 0b0000_1000;
const LALT = 0b0001_0000;
const RALT = 0b0010_0000;
const LSUPER = 0b0100_0000;
const RSUPER = 0b1000_0000;
}
}
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
mod modifiers_serde { mod modifiers_serde {
use super::ModifiersState; use super::ModifiersState;

219
src/lib.rs Normal file
View File

@@ -0,0 +1,219 @@
//! 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
//!
//! 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
//! 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.
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if desired.
//!
//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
//! will run until [`exit()`] is used, at which point [`Event::LoopExiting`].
//!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on
//! most other platforms. However, this model can be re-implemented to an extent with
#![cfg_attr(
any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
),
doc = "[`EventLoopExtPumpEvents::pump_app_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_app_events()]"
)]
#![cfg_attr(
not(any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
)),
doc = "`EventLoopExtPumpEvents::pump_app_events()`"
)]
//! [^1]. See that method's documentation for more reasons about why
//! it's discouraged beyond compatibility reasons.
//!
//!
//! ```no_run
//! use winit::application::ApplicationHandler;
//! use winit::event::WindowEvent;
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
//! use winit::window::{Window, WindowId};
//!
//! #[derive(Default)]
//! struct App {
//! window: Option<Window>,
//! }
//!
//! impl ApplicationHandler for App {
//! fn resumed(&mut self, event_loop: &ActiveEventLoop) {
//! self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
//! }
//!
//! fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
//! match event {
//! WindowEvent::CloseRequested => {
//! println!("The close button was pressed; stopping");
//! event_loop.exit();
//! },
//! WindowEvent::RedrawRequested => {
//! // Redraw the application.
//! //
//! // It's preferable for applications that do not render continuously to render in
//! // this event rather than in AboutToWait, since rendering in here allows
//! // the program to gracefully handle redraws requested by the OS.
//!
//! // Draw.
//!
//! // Queue a RedrawRequested event.
//! //
//! // You only need to call this if you've determined that you need to redraw in
//! // applications which do not always need to. Applications that redraw continuously
//! // can render here instead.
//! self.window.as_ref().unwrap().request_redraw();
//! }
//! _ => (),
//! }
//! }
//! }
//!
//! let event_loop = EventLoop::new().unwrap();
//!
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications.
//! event_loop.set_control_flow(ControlFlow::Poll);
//!
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
//! // This is ideal for non-game applications that only update in response to user
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! event_loop.set_control_flow(ControlFlow::Wait);
//!
//! let mut app = App::default();
//! event_loop.run_app(&mut app);
//! ```
//!
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
//! compared to the value returned by [`Window::id()`] to determine which [`Window`]
//! dispatched the event.
//!
//! # Drawing on the window
//!
//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However, it allows you to
//! 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
//! 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
//! 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
//! [`visible` set to `false`][crate::window::WindowAttributes::with_visible] and explicitly make the
//! window visible only once you're ready to render into it.
//!
//! # UI scaling
//!
//! UI scaling is important, go read the docs for the [`dpi`] crate for an
//! introduction.
//!
//! All of Winit's functions return physical types, but can take either logical or physical
//! coordinates as input, allowing you to use the most convenient coordinate system for your
//! particular application.
//!
//! Winit will dispatch a [`ScaleFactorChanged`] event whenever a window's scale factor has changed.
//! This can happen if the user drags their window from a standard-resolution monitor to a high-DPI
//! monitor or if the user changes their DPI settings. This allows you to rescale your application's
//! UI elements and adjust how the platform changes the window's size to reflect the new scale
//! factor. If a window hasn't received a [`ScaleFactorChanged`] event, its scale factor
//! can be found by calling [`window.scale_factor()`].
//!
//! [`ScaleFactorChanged`]: event::WindowEvent::ScaleFactorChanged
//! [`window.scale_factor()`]: window::Window::scale_factor
//!
//! # Cargo Features
//!
//! Winit provides the following Cargo features:
//!
//! * `x11` (enabled by default): On Unix platforms, enables the X11 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.
//! * `serde`: Enables serialization/deserialization of certain types with
//! [Serde](https://crates.io/crates/serde).
//! * `mint`: Enables mint (math interoperability standard types) conversions.
//!
//! See the [`platform`] module for documentation on platform-specific cargo
//! features.
//!
//! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app
//! [`exit()`]: event_loop::ActiveEventLoop::exit
//! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId
//! [`WindowAttributes`]: window::WindowAttributes
//! [window_new]: window::Window::new
//! [`create_window`]: event_loop::ActiveEventLoop::create_window
//! [`Window::id()`]: window::Window::id
//! [`WindowEvent`]: event::WindowEvent
//! [`DeviceEvent`]: event::DeviceEvent
//! [`Event::UserEvent`]: event::Event::UserEvent
//! [`Event::LoopExiting`]: event::Event::LoopExiting
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_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.
#![deny(rust_2018_idioms)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(clippy::all)]
#![deny(unsafe_op_in_unsafe_fn)]
#![cfg_attr(clippy, deny(warnings))]
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
#![cfg_attr(
docsrs,
feature(doc_auto_cfg, doc_cfg_hide),
doc(cfg_hide(doc, docsrs))
)]
#![allow(clippy::missing_safety_doc)]
#[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.
#[doc(inline)]
pub use dpi;
pub mod application;
#[cfg(any(doc, doctest, test))]
pub mod changelog;
#[macro_use]
pub mod error;
mod cursor;
pub mod event;
pub mod event_loop;
mod icon;
pub mod keyboard;
pub mod monitor;
mod platform_impl;
mod utils;
pub mod window;
pub mod platform;

View File

@@ -3,8 +3,8 @@
//! If you want to get basic information about a monitor, you can use the //! If you want to get basic information about a monitor, you can use the
//! [`MonitorHandle`] type. This is retrieved from one of the following //! [`MonitorHandle`] type. This is retrieved from one of the following
//! methods, which return an iterator of [`MonitorHandle`]: //! methods, which return an iterator of [`MonitorHandle`]:
//! - [`EventLoopWindowTarget::available_monitors`](crate::event_loop::EventLoopWindowTarget::available_monitors). //! - [`ActiveEventLoop::available_monitors`][crate::event_loop::ActiveEventLoop::available_monitors].
//! - [`Window::available_monitors`](crate::window::Window::available_monitors). //! - [`Window::available_monitors`][crate::window::Window::available_monitors].
use crate::{ use crate::{
dpi::{PhysicalPosition, PhysicalSize}, dpi::{PhysicalPosition, PhysicalSize},
platform_impl, platform_impl,
@@ -145,7 +145,7 @@ impl MonitorHandle {
/// 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`].
/// ///
/// See the [`dpi`](crate::dpi) module for more information. /// See the [`dpi`] module for more information.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///

170
src/platform/android.rs Normal file
View File

@@ -0,0 +1,170 @@
//! # Android
//!
//! The Android backend builds on (and exposes types from) the [`ndk`](https://docs.rs/ndk/) crate.
//!
//! Native Android applications need some form of "glue" crate that is responsible
//! for defining the main entry point for your Rust application as well as tracking
//! various life-cycle events and synchronizing with the main JVM thread.
//!
//! Winit uses the [android-activity](https://docs.rs/android-activity/) as a
//! glue crate (prior to `0.28` it used
//! [ndk-glue](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue)).
//!
//! The version of the glue crate that your application depends on _must_ match the
//! version that Winit depends on because the glue crate is responsible for your
//! application's main entry point. If Cargo resolves multiple versions, they will
//! clash.
//!
//! `winit` glue compatibility table:
//!
//! | winit | ndk-glue |
//! | :---: | :--------------------------: |
//! | 0.29 | `android-activity = "0.5"` |
//! | 0.28 | `android-activity = "0.4"` |
//! | 0.27 | `ndk-glue = "0.7"` |
//! | 0.26 | `ndk-glue = "0.5"` |
//! | 0.25 | `ndk-glue = "0.3"` |
//! | 0.24 | `ndk-glue = "0.2"` |
//!
//! The recommended way to avoid a conflict with the glue version is to avoid explicitly
//! depending on the `android-activity` crate, and instead consume the API that
//! is re-exported by Winit under `winit::platform::android::activity::*`
//!
//! Running on an Android device needs a dynamic system library. Add this to Cargo.toml:
//!
//! ```toml
//! [lib]
//! name = "main"
//! crate-type = ["cdylib"]
//! ```
//!
//! All Android applications are based on an `Activity` subclass, and the
//! `android-activity` crate is designed to support different choices for this base
//! class. Your application _must_ specify the base class it needs via a feature flag:
//!
//! | Base Class | Feature Flag | Notes |
//! | :--------------: | :---------------: | :-----: |
//! | `NativeActivity` | `android-native-activity` | Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass `NativeActivity` to access some platform features. It does not derive from the [`AndroidAppCompat`] base class.|
//! | [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`], a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
//!
//! [`GameActivity`]: https://developer.android.com/games/agdk/game-activity
//! [`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input
//! [`AndroidAppCompat`]: https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity
//! [agdk_jetpack]: https://developer.android.com/jetpack/androidx/releases/games
//! [agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
//! [Gradle]: https://developer.android.com/studio/build
//!
//! For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples).
//!
//! ## Converting from `ndk-glue` to `android-activity`
//!
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
//! 1. Remove `ndk-glue` from your `Cargo.toml`
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.15", features = [ "android-native-activity" ] }`
//! 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
//! 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
use crate::{
event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder},
window::{Window, WindowAttributes},
};
use self::activity::{AndroidApp, ConfigurationRef, Rect};
/// Additional methods on [`EventLoop`] that are specific to Android.
pub trait EventLoopExtAndroid {}
impl<T> EventLoopExtAndroid for EventLoop<T> {}
/// Additional methods on [`ActiveEventLoop`] that are specific to Android.
pub trait ActiveEventLoopExtAndroid {}
/// Additional methods on [`Window`] that are specific to Android.
pub trait WindowExtAndroid {
fn content_rect(&self) -> Rect;
fn config(&self) -> ConfigurationRef;
}
impl WindowExtAndroid for Window {
fn content_rect(&self) -> Rect {
self.window.content_rect()
}
fn config(&self) -> ConfigurationRef {
self.window.config()
}
}
impl ActiveEventLoopExtAndroid for ActiveEventLoop {}
/// Additional methods on [`WindowAttributes`] that are specific to Android.
pub trait WindowAttributesExtAndroid {}
impl WindowAttributesExtAndroid for WindowAttributes {}
pub trait EventLoopBuilderExtAndroid {
/// 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.
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self;
/// Calling this will mark the volume keys to be manually handled by the application
///
/// Default is to let the operating system handle the volume keys
fn handle_volume_keys(&mut self) -> &mut Self;
}
impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self {
self.platform_specific.android_app = Some(app);
self
}
fn handle_volume_keys(&mut self) -> &mut Self {
self.platform_specific.ignore_volume_keys = false;
self
}
}
/// Re-export of the `android_activity` API
///
/// Winit re-exports the `android_activity` API for convenience so that most
/// applications can rely on the Winit crate to resolve the required version of
/// `android_activity` and avoid any chance of a conflict between Winit and the
/// application crate.
///
/// Unlike most libraries there can only be a single implementation
/// of the `android_activity` glue crate linked with an application because
/// it is responsible for the application's `android_main()` entry point.
///
/// Since Winit depends on a specific version of `android_activity` the simplest
/// way to avoid creating a conflict is for applications to avoid explicitly
/// depending on the `android_activity` crate, and instead consume the API that
/// is re-exported by Winit.
///
/// For compatibility applications should then import the `AndroidApp` type for
/// their `android_main(app: AndroidApp)` function like:
/// ```rust
/// #[cfg(target_os="android")]
/// use winit::platform::android::activity::AndroidApp;
/// ```
pub mod activity {
// We enable the `"native-activity"` feature just so that we can build the
// docs, but it'll be very confusing for users to see the docs with that
// feature enabled, so we avoid inlining it so that they're forced to view
// it on the crate's own docs.rs page.
#[doc(no_inline)]
#[cfg(android_platform)]
pub use android_activity::*;
#[cfg(not(android_platform))]
#[doc(hidden)]
pub struct Rect;
#[cfg(not(android_platform))]
#[doc(hidden)]
pub struct ConfigurationRef;
#[cfg(not(android_platform))]
#[doc(hidden)]
pub struct AndroidApp;
}

View File

@@ -1,9 +1,75 @@
//! # iOS / UIKit
//!
//! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on
//! iOS 9.3.
//!
//! 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`.
//!
//! [#1705]: https://github.com/rust-windowing/winit/issues/1705
//!
//! ## Building app
//!
//! To build ios app you will need rustc built for this targets:
//!
//! - armv7-apple-ios
//! - armv7s-apple-ios
//! - 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() {
//! ...
//! }
//! ```
//!
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
//!
//! ```ignore
//! void start_winit_app();
//! ```
//!
//! Use start_winit_app inside your xcode's main function.
//!
//!
//! ## App lifecycle and events
//!
//! iOS environment is very different from other platforms and you must be very
//! careful with it's events. Familiarize yourself with
//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
//!
//! This is how those event are represented in winit:
//!
//! - applicationDidBecomeActive is Resumed
//! - applicationWillResignActive is Suspended
//! - applicationWillTerminate is LoopExiting
//!
//! Keep in mind that after LoopExiting event is received every attempt to draw with
//! opengl will result in segfault.
//!
//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
use std::os::raw::c_void; use std::os::raw::c_void;
use crate::{ use crate::{
event_loop::EventLoop, event_loop::EventLoop,
monitor::{MonitorHandle, VideoModeHandle}, monitor::{MonitorHandle, VideoModeHandle},
window::{Window, WindowBuilder}, window::{Window, WindowAttributes},
}; };
/// Additional methods on [`EventLoop`] that are specific to iOS. /// Additional methods on [`EventLoop`] that are specific to iOS.
@@ -159,8 +225,8 @@ impl WindowExtIOS for Window {
} }
} }
/// Additional methods on [`WindowBuilder`] that are specific to iOS. /// Additional methods on [`WindowAttributes`] that are specific to iOS.
pub trait WindowBuilderExtIOS { pub trait WindowAttributesExtIOS {
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`. /// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
/// ///
/// The default value is device dependent, and it's recommended GLES or Metal applications set /// The default value is device dependent, and it's recommended GLES or Metal applications set
@@ -214,42 +280,41 @@ pub trait WindowBuilderExtIOS {
fn with_preferred_status_bar_style(self, status_bar_style: StatusBarStyle) -> Self; fn with_preferred_status_bar_style(self, status_bar_style: StatusBarStyle) -> Self;
} }
impl WindowBuilderExtIOS for WindowBuilder { impl WindowAttributesExtIOS for WindowAttributes {
#[inline] #[inline]
fn with_scale_factor(mut self, scale_factor: f64) -> Self { fn with_scale_factor(mut self, scale_factor: f64) -> Self {
self.window.platform_specific.scale_factor = Some(scale_factor); self.platform_specific.scale_factor = Some(scale_factor);
self self
} }
#[inline] #[inline]
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self { fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self {
self.window.platform_specific.valid_orientations = valid_orientations; self.platform_specific.valid_orientations = valid_orientations;
self self
} }
#[inline] #[inline]
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self { fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self {
self.window.platform_specific.prefers_home_indicator_hidden = hidden; self.platform_specific.prefers_home_indicator_hidden = hidden;
self self
} }
#[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.window self.platform_specific
.platform_specific
.preferred_screen_edges_deferring_system_gestures = edges; .preferred_screen_edges_deferring_system_gestures = edges;
self self
} }
#[inline] #[inline]
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self { fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self {
self.window.platform_specific.prefers_status_bar_hidden = hidden; self.platform_specific.prefers_status_bar_hidden = hidden;
self self
} }
#[inline] #[inline]
fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self { fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self {
self.window.platform_specific.preferred_status_bar_style = status_bar_style; self.platform_specific.preferred_status_bar_style = status_bar_style;
self self
} }
} }

View File

@@ -1,12 +1,28 @@
//! # macOS / AppKit
//!
//! Winit has an OS requirement of macOS 10.11 or higher (same as Rust
//! itself), and is regularly tested on macOS 10.14.
//!
//! A lot of functionality expects the application to be ready before you
//! start doing anything; this includes creating windows, fetching monitors,
//! drawing, and so on, see issues [#2238], [#2051] and [#2087].
//!
//! If you encounter problems, you should try doing your initialization inside
//! `Event::Resumed`.
//!
//! [#2238]: https://github.com/rust-windowing/winit/issues/2238
//! [#2051]: https://github.com/rust-windowing/winit/issues/2051
//! [#2087]: https://github.com/rust-windowing/winit/issues/2087
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::{ use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget}, event_loop::{ActiveEventLoop, EventLoopBuilder},
monitor::MonitorHandle, monitor::MonitorHandle,
window::{Window, WindowBuilder}, window::{Window, WindowAttributes},
}; };
/// Additional methods on [`Window`] that are specific to MacOS. /// Additional methods on [`Window`] that are specific to MacOS.
@@ -174,15 +190,15 @@ pub enum ActivationPolicy {
Prohibited, Prohibited,
} }
/// Additional methods on [`WindowBuilder`] 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 [`WindowBuilder::with_decorations`] method: /// **Note:** Properties dealing with the titlebar will be overwritten by the [`WindowAttributes::with_decorations`] method:
/// - `with_titlebar_transparent` /// - `with_titlebar_transparent`
/// - `with_title_hidden` /// - `with_title_hidden`
/// - `with_titlebar_hidden` /// - `with_titlebar_hidden`
/// - `with_titlebar_buttons_hidden` /// - `with_titlebar_buttons_hidden`
/// - `with_fullsize_content_view` /// - `with_fullsize_content_view`
pub trait WindowBuilderExtMacOS { pub trait WindowAttributesExtMacOS {
/// Enables click-and-drag behavior for the entire window, not just the titlebar. /// Enables click-and-drag behavior for the entire window, not just the titlebar.
fn with_movable_by_window_background(self, movable_by_window_background: bool) -> Self; fn with_movable_by_window_background(self, movable_by_window_background: bool) -> Self;
/// Makes the titlebar transparent and allows the content to appear behind it. /// Makes the titlebar transparent and allows the content to appear behind it.
@@ -209,65 +225,64 @@ pub trait WindowBuilderExtMacOS {
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self; fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
} }
impl WindowBuilderExtMacOS for WindowBuilder { impl WindowAttributesExtMacOS for WindowAttributes {
#[inline] #[inline]
fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self { fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self {
self.window.platform_specific.movable_by_window_background = movable_by_window_background; self.platform_specific.movable_by_window_background = movable_by_window_background;
self self
} }
#[inline] #[inline]
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self { fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self {
self.window.platform_specific.titlebar_transparent = titlebar_transparent; self.platform_specific.titlebar_transparent = titlebar_transparent;
self self
} }
#[inline] #[inline]
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self { fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self {
self.window.platform_specific.titlebar_hidden = titlebar_hidden; self.platform_specific.titlebar_hidden = titlebar_hidden;
self self
} }
#[inline] #[inline]
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self { fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self {
self.window.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden; self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
self self
} }
#[inline] #[inline]
fn with_title_hidden(mut self, title_hidden: bool) -> Self { fn with_title_hidden(mut self, title_hidden: bool) -> Self {
self.window.platform_specific.title_hidden = title_hidden; self.platform_specific.title_hidden = title_hidden;
self self
} }
#[inline] #[inline]
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self { fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self {
self.window.platform_specific.fullsize_content_view = fullsize_content_view; self.platform_specific.fullsize_content_view = fullsize_content_view;
self self
} }
#[inline] #[inline]
fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self { fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self {
self.window.platform_specific.disallow_hidpi = disallow_hidpi; self.platform_specific.disallow_hidpi = disallow_hidpi;
self self
} }
#[inline] #[inline]
fn with_has_shadow(mut self, has_shadow: bool) -> Self { fn with_has_shadow(mut self, has_shadow: bool) -> Self {
self.window.platform_specific.has_shadow = has_shadow; self.platform_specific.has_shadow = has_shadow;
self self
} }
#[inline] #[inline]
fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self { fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self {
self.window.platform_specific.accepts_first_mouse = accepts_first_mouse; self.platform_specific.accepts_first_mouse = accepts_first_mouse;
self self
} }
#[inline] #[inline]
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self { fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
self.window self.platform_specific
.platform_specific
.tabbing_identifier .tabbing_identifier
.replace(tabbing_identifier.to_string()); .replace(tabbing_identifier.to_string());
self self
@@ -275,7 +290,7 @@ impl WindowBuilderExtMacOS for WindowBuilder {
#[inline] #[inline]
fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self { fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self {
self.window.platform_specific.option_as_alt = option_as_alt; self.platform_specific.option_as_alt = option_as_alt;
self self
} }
} }
@@ -375,8 +390,8 @@ impl MonitorHandleExtMacOS for MonitorHandle {
} }
} }
/// Additional methods on [`EventLoopWindowTarget`] that are specific to macOS. /// Additional methods on [`ActiveEventLoop`] that are specific to macOS.
pub trait EventLoopWindowTargetExtMacOS { pub trait ActiveEventLoopExtMacOS {
/// Hide the entire application. In most applications this is typically triggered with Command-H. /// Hide the entire application. In most applications this is typically triggered with Command-H.
fn hide_application(&self); fn hide_application(&self);
/// Hide the other applications. In most applications this is typically triggered with Command+Option-H. /// Hide the other applications. In most applications this is typically triggered with Command+Option-H.
@@ -389,7 +404,7 @@ pub trait EventLoopWindowTargetExtMacOS {
fn allows_automatic_window_tabbing(&self) -> bool; fn allows_automatic_window_tabbing(&self) -> bool;
} }
impl EventLoopWindowTargetExtMacOS for EventLoopWindowTarget { impl ActiveEventLoopExtMacOS for ActiveEventLoop {
fn hide_application(&self) { fn hide_application(&self) {
self.p.hide_application() self.p.hide_application()
} }

View File

@@ -46,6 +46,7 @@ pub mod pump_events;
macos_platform, macos_platform,
x11_platform, x11_platform,
wayland_platform, wayland_platform,
orbital_platform,
docsrs docsrs
))] ))]
pub mod modifier_supplement; pub mod modifier_supplement;

View File

@@ -25,8 +25,7 @@ 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.extra self.platform_specific
.extra
.text_with_all_modifiers .text_with_all_modifiers
.as_ref() .as_ref()
.map(|s| s.as_str()) .map(|s| s.as_str())
@@ -34,6 +33,6 @@ impl KeyEventExtModifierSupplement for KeyEvent {
#[inline] #[inline]
fn key_without_modifiers(&self) -> Key { fn key_without_modifiers(&self) -> Key {
self.extra.extra.key_without_modifiers.clone() self.platform_specific.key_without_modifiers.clone()
} }
} }

6
src/platform/orbital.rs Normal file
View File

@@ -0,0 +1,6 @@
//! # Orbital / Redox OS
//!
//! Redox OS has some functionality not yet present that will be implemented
//! when its orbital display server provides it.
// There are no Orbital specific traits yet.

View File

@@ -1,22 +1,13 @@
use std::time::Duration; use std::time::Duration;
use crate::{ use crate::application::ApplicationHandler;
event::Event, use crate::event::Event;
event_loop::{EventLoop, EventLoopWindowTarget}, use crate::event_loop::{self, ActiveEventLoop, EventLoop};
};
/// The return status for `pump_events`
pub enum PumpStatus {
/// Continue running external loop.
Continue,
/// Exit external loop.
Exit(i32),
}
/// Additional methods on [`EventLoop`] for pumping events within an external event loop /// Additional methods on [`EventLoop`] for pumping events within an external event loop
pub trait EventLoopExtPumpEvents { pub trait EventLoopExtPumpEvents {
/// A type provided by the user that can be passed through [`Event::UserEvent`]. /// A type provided by the user that can be passed through [`Event::UserEvent`].
type UserEvent; type UserEvent: 'static;
/// Pump the `EventLoop` to check for and dispatch pending events. /// Pump the `EventLoop` to check for and dispatch pending events.
/// ///
@@ -32,68 +23,6 @@ pub trait EventLoopExtPumpEvents {
/// Passing a `timeout` of `None` means that it may wait indefinitely for new /// Passing a `timeout` of `None` means that it may wait indefinitely for new
/// events before returning control back to the external loop. /// events before returning control back to the external loop.
/// ///
/// ## Example
///
/// ```rust,no_run
/// # // Copied from examples/window_pump_events.rs
/// # #[cfg(any(
/// # windows_platform,
/// # macos_platform,
/// # x11_platform,
/// # wayland_platform,
/// # android_platform,
/// # ))]
/// fn main() -> std::process::ExitCode {
/// # use std::{process::ExitCode, thread::sleep, time::Duration};
/// #
/// # use simple_logger::SimpleLogger;
/// # use winit::{
/// # event::{Event, WindowEvent},
/// # event_loop::EventLoop,
/// # platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
/// # window::Window,
/// # };
/// let mut event_loop = EventLoop::new().unwrap();
/// #
/// # SimpleLogger::new().init().unwrap();
/// let window = Window::builder()
/// .with_title("A fantastic window!")
/// .build(&event_loop)
/// .unwrap();
///
/// 'main: loop {
/// let timeout = Some(Duration::ZERO);
/// let status = event_loop.pump_events(timeout, |event, elwt| {
/// # if let Event::WindowEvent { event, .. } = &event {
/// # // Print only Window events to reduce noise
/// # println!("{event:?}");
/// # }
/// #
/// match event {
/// Event::WindowEvent {
/// event: WindowEvent::CloseRequested,
/// window_id,
/// } if window_id == window.id() => elwt.exit(),
/// Event::AboutToWait => {
/// window.request_redraw();
/// }
/// _ => (),
/// }
/// });
/// if let PumpStatus::Exit(exit_code) = status {
/// break 'main ExitCode::from(exit_code as u8);
/// }
///
/// // Sleep for 1/60 second to simulate application work
/// //
/// // Since `pump_events` doesn't block it will be important to
/// // throttle the loop in the app somehow.
/// println!("Update()");
/// sleep(Duration::from_millis(16));
/// }
/// }
/// ```
///
/// **Note:** This is not a portable API, and its usage involves a number of /// **Note:** This is not a portable API, and its usage involves a number of
/// caveats and trade offs that should be considered before using this API! /// caveats and trade offs that should be considered before using this API!
/// ///
@@ -137,12 +66,14 @@ pub trait EventLoopExtPumpEvents {
/// other lifecycle events occur while the event is buffered. /// other lifecycle events occur while the event is buffered.
/// ///
/// ## Supported Platforms /// ## Supported Platforms
///
/// - Windows /// - Windows
/// - Linux /// - Linux
/// - MacOS /// - MacOS
/// - Android /// - Android
/// ///
/// ## Unsupported Platforms /// ## Unsupported Platforms
///
/// - **Web:** This API is fundamentally incompatible with the event-based way in which /// - **Web:** This API is fundamentally incompatible with the event-based way in which
/// Web browsers work because it's not possible to have a long-running external /// Web browsers work because it's not possible to have a long-running external
/// loop that would block the browser and there is nothing that can be /// loop that would block the browser and there is nothing that can be
@@ -152,6 +83,7 @@ pub trait EventLoopExtPumpEvents {
/// 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 /// - **Windows**: The implementation will use `PeekMessage` when checking for
/// window messages to avoid blocking your external event loop. /// window messages to avoid blocking your external event loop.
/// ///
@@ -172,9 +104,24 @@ pub trait EventLoopExtPumpEvents {
/// If you render outside of Winit you are likely to see window resizing artifacts /// If you render outside of Winit you are likely to see window resizing artifacts
/// since MacOS expects applications to render synchronously during any `drawRect` /// since MacOS expects applications to render synchronously during any `drawRect`
/// callback. /// callback.
fn pump_app_events<A: ApplicationHandler<Self::UserEvent>>(
&mut self,
timeout: Option<Duration>,
app: &mut A,
) -> PumpStatus {
#[allow(deprecated)]
self.pump_events(timeout, |event, event_loop| {
event_loop::dispatch_event_for_app(app, event_loop, event)
})
}
/// See [`pump_app_events`].
///
/// [`pump_app_events`]: Self::pump_app_events
#[deprecated = "use EventLoopExtPumpEvents::pump_app_events"]
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget); F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
} }
impl<T> EventLoopExtPumpEvents for EventLoop<T> { impl<T> EventLoopExtPumpEvents for EventLoop<T> {
@@ -182,8 +129,16 @@ impl<T> EventLoopExtPumpEvents for EventLoop<T> {
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget), F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
{ {
self.event_loop.pump_events(timeout, event_handler) self.event_loop.pump_events(timeout, event_handler)
} }
} }
/// The return status for `pump_events`
pub enum PumpStatus {
/// Continue running external loop.
Continue,
/// Exit external loop.
Exit(i32),
}

View File

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

View File

@@ -9,7 +9,7 @@ use crate::keyboard::{KeyCode, PhysicalKey};
pub trait PhysicalKeyExtScancode { pub trait PhysicalKeyExtScancode {
/// The raw value of the platform-specific physical key identifier. /// The raw value of the platform-specific physical key identifier.
/// ///
/// Returns `Some(key_id)` if the conversion was succesful; returns `None` otherwise. /// Returns `Some(key_id)` if the conversion was successful; returns `None` otherwise.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// - **Windows:** A 16bit extended scancode /// - **Windows:** A 16bit extended scancode

View File

@@ -24,8 +24,8 @@
use std::env; use std::env;
use crate::error::NotSupportedError; use crate::error::NotSupportedError;
use crate::event_loop::{AsyncRequestSerial, EventLoopWindowTarget}; use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial};
use crate::window::{ActivationToken, Window, WindowBuilder}; use crate::window::{ActivationToken, Window, WindowAttributes};
/// The variable which is used mostly on X11. /// The variable which is used mostly on X11.
const X11_VAR: &str = "DESKTOP_STARTUP_ID"; const X11_VAR: &str = "DESKTOP_STARTUP_ID";
@@ -47,7 +47,7 @@ pub trait WindowExtStartupNotify {
fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError>; fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError>;
} }
pub trait WindowBuilderExtStartupNotify { pub trait WindowAttributesExtStartupNotify {
/// Use this [`ActivationToken`] during window creation. /// Use this [`ActivationToken`] during window creation.
/// ///
/// Not using such a token upon a window could make your window not gaining /// Not using such a token upon a window could make your window not gaining
@@ -55,16 +55,16 @@ pub trait WindowBuilderExtStartupNotify {
fn with_activation_token(self, token: ActivationToken) -> Self; fn with_activation_token(self, token: ActivationToken) -> Self;
} }
impl EventLoopExtStartupNotify for EventLoopWindowTarget { impl EventLoopExtStartupNotify for ActiveEventLoop {
fn read_token_from_env(&self) -> Option<ActivationToken> { fn read_token_from_env(&self) -> Option<ActivationToken> {
match self.p { match self.p {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
crate::platform_impl::EventLoopWindowTarget::Wayland(_) => env::var(WAYLAND_VAR), crate::platform_impl::ActiveEventLoop::Wayland(_) => env::var(WAYLAND_VAR),
#[cfg(x11_platform)] #[cfg(x11_platform)]
crate::platform_impl::EventLoopWindowTarget::X(_) => env::var(X11_VAR), crate::platform_impl::ActiveEventLoop::X(_) => env::var(X11_VAR),
} }
.ok() .ok()
.map(ActivationToken::new) .map(ActivationToken::_new)
} }
} }
@@ -74,9 +74,9 @@ impl WindowExtStartupNotify for Window {
} }
} }
impl WindowBuilderExtStartupNotify for WindowBuilder { impl WindowAttributesExtStartupNotify for WindowAttributes {
fn with_activation_token(mut self, token: ActivationToken) -> Self { fn with_activation_token(mut self, token: ActivationToken) -> Self {
self.window.platform_specific.activation_token = Some(token); self.platform_specific.activation_token = Some(token);
self self
} }
} }
@@ -94,6 +94,6 @@ pub fn reset_activation_token_env() {
/// ///
/// This could be used before running daemon processes. /// This could be used before running daemon processes.
pub fn set_activation_token_env(token: ActivationToken) { pub fn set_activation_token_env(token: ActivationToken) {
env::set_var(X11_VAR, token.token()); env::set_var(X11_VAR, &token._token);
env::set_var(WAYLAND_VAR, token.token()); env::set_var(WAYLAND_VAR, token._token);
} }

View File

@@ -1,18 +1,33 @@
//! # Wayland
//!
//! **Note:** Windows don't appear on Wayland until you draw/present to them.
//!
//! By default, Winit loads system libraries using `dlopen`. This can be
//! disabled by disabling the `"wayland-dlopen"` cargo feature.
//!
//! ## Client-side decorations
//!
//! Winit provides client-side decorations by default, but the behaviour can
//! be controlled with the following feature flags:
//!
//! * `wayland-csd-adwaita` (default).
//! * `wayland-csd-adwaita-crossfont`.
//! * `wayland-csd-adwaita-notitle`.
use crate::{ use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget}, event_loop::{ActiveEventLoop, EventLoopBuilder},
monitor::MonitorHandle, monitor::MonitorHandle,
window::{Window, WindowBuilder}, window::{Window, WindowAttributes},
}; };
pub use crate::window::Theme; pub use crate::window::Theme;
/// Additional methods on [`EventLoopWindowTarget`] that are specific to Wayland. /// Additional methods on [`ActiveEventLoop`] that are specific to Wayland.
pub trait EventLoopWindowTargetExtWayland { pub trait ActiveEventLoopExtWayland {
/// True if the [`EventLoopWindowTarget`] uses Wayland. /// True if the [`ActiveEventLoop`] uses Wayland.
fn is_wayland(&self) -> bool; fn is_wayland(&self) -> bool;
} }
impl EventLoopWindowTargetExtWayland for EventLoopWindowTarget { impl ActiveEventLoopExtWayland for ActiveEventLoop {
#[inline] #[inline]
fn is_wayland(&self) -> bool { fn is_wayland(&self) -> bool {
self.p.is_wayland() self.p.is_wayland()
@@ -50,22 +65,22 @@ pub trait WindowExtWayland {}
impl WindowExtWayland for Window {} impl WindowExtWayland for Window {}
/// Additional methods on [`WindowBuilder`] that are specific to Wayland. /// Additional methods on [`WindowAttributes`] that are specific to Wayland.
pub trait WindowBuilderExtWayland { pub trait WindowAttributesExtWayland {
/// Build window with the given name. /// Build window with the given name.
/// ///
/// The `general` name sets an application ID, which should match the `.desktop` /// The `general` name sets an application ID, which should match the `.desktop`
/// file destributed with your program. The `instance` is a `no-op`. /// file distributed with your program. The `instance` is a `no-op`.
/// ///
/// 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)
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self; fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;
} }
impl WindowBuilderExtWayland for WindowBuilder { 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.window.platform_specific.name = Some(crate::platform_impl::ApplicationName::new( self.platform_specific.name = Some(crate::platform_impl::ApplicationName::new(
general.into(), general.into(),
instance.into(), instance.into(),
)); ));

View File

@@ -1,12 +1,31 @@
//! The web target does not automatically insert the canvas element object into the web page, to //! # Web
//! allow end users to determine how the page should be laid out. Use the [`WindowExtWebSys`] trait //!
//! to retrieve the canvas from the Window. Alternatively, use the [`WindowBuilderExtWebSys`] trait //! The officially supported browsers are Chrome, Firefox and Safari 13.1+,
//! to provide your own canvas. //! though forks of these should work fine.
//!
//! Winit supports compiling to the `wasm32-unknown-unknown` target with
//! `web-sys`.
//!
//! On the web platform, a Winit window is backed by a `<canvas>` element. You
//! can either [provide Winit with a `<canvas>` element][with_canvas], or
//! [let Winit create a `<canvas>` element which you can then retrieve][get]
//! and insert it into the DOM yourself.
//!
//! Currently, there is no example code using Winit on Web, see [#3473]. For
//! information on using Rust on WebAssembly, check out the [Rust and
//! WebAssembly book].
//!
//! [with_canvas]: WindowAttributesExtWebSys::with_canvas
//! [get]: WindowExtWebSys::canvas
//! [#3473]: https://github.com/rust-windowing/winit/issues/3473
//! [Rust and WebAssembly book]: https://rustwasm.github.io/book/
//!
//! ## CSS properties
//! //!
//! It is recommended **not** to apply certain CSS properties to the canvas: //! It is recommended **not** to apply certain CSS properties to the canvas:
//! - [`transform`] //! - [`transform`](https://developer.mozilla.org/en-US/docs/Web/CSS/transform)
//! - [`border`] //! - [`border`](https://developer.mozilla.org/en-US/docs/Web/CSS/border)
//! - [`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::Resized`] and [`Window::(set_)inner_size()`] //! - [`WindowEvent::Resized`] and [`Window::(set_)inner_size()`]
@@ -16,16 +35,13 @@
//! - [`Window::set_outer_position()`] //! - [`Window::set_outer_position()`]
//! //!
//! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized //! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
//! [`Window::(set_)inner_size()`]: crate::window::Window::inner_size() //! [`Window::(set_)inner_size()`]: crate::window::Window::inner_size
//! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded //! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded
//! [`WindowEvent::CursorMoved`]: crate::event::WindowEvent::CursorMoved //! [`WindowEvent::CursorMoved`]: crate::event::WindowEvent::CursorMoved
//! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered //! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered
//! [`WindowEvent::CursorLeft`]: crate::event::WindowEvent::CursorLeft //! [`WindowEvent::CursorLeft`]: crate::event::WindowEvent::CursorLeft
//! [`WindowEvent::Touch`]: crate::event::WindowEvent::Touch //! [`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
//! [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
use std::error::Error; use std::error::Error;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
@@ -37,13 +53,14 @@ use std::time::Duration;
#[cfg(web_platform)] #[cfg(web_platform)]
use web_sys::HtmlCanvasElement; use web_sys::HtmlCanvasElement;
use crate::cursor::CustomCursorBuilder; use crate::application::ApplicationHandler;
use crate::cursor::CustomCursorSource;
use crate::event::Event; use crate::event::Event;
use crate::event_loop::{EventLoop, EventLoopWindowTarget}; use crate::event_loop::{self, ActiveEventLoop, EventLoop};
#[cfg(web_platform)] #[cfg(web_platform)]
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture; use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
use crate::platform_impl::{PlatformCustomCursor, PlatformCustomCursorBuilder}; use crate::platform_impl::PlatformCustomCursorSource;
use crate::window::{CustomCursor, Window, WindowBuilder}; use crate::window::{CustomCursor, Window, WindowAttributes};
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
#[doc(hidden)] #[doc(hidden)]
@@ -85,9 +102,9 @@ impl WindowExtWebSys for Window {
} }
} }
pub trait WindowBuilderExtWebSys { 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`],
/// [`WindowBuilder::build()`] 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.
/// ///
@@ -119,24 +136,24 @@ pub trait WindowBuilderExtWebSys {
fn with_append(self, append: bool) -> Self; fn with_append(self, append: bool) -> Self;
} }
impl WindowBuilderExtWebSys for WindowBuilder { impl WindowAttributesExtWebSys for WindowAttributes {
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self { fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
self.window.platform_specific.set_canvas(canvas); self.platform_specific.set_canvas(canvas);
self self
} }
fn with_prevent_default(mut self, prevent_default: bool) -> Self { fn with_prevent_default(mut self, prevent_default: bool) -> Self {
self.window.platform_specific.prevent_default = prevent_default; self.platform_specific.prevent_default = prevent_default;
self self
} }
fn with_focusable(mut self, focusable: bool) -> Self { fn with_focusable(mut self, focusable: bool) -> Self {
self.window.platform_specific.focusable = focusable; self.platform_specific.focusable = focusable;
self self
} }
fn with_append(mut self, append: bool) -> Self { fn with_append(mut self, append: bool) -> Self {
self.window.platform_specific.append = append; self.platform_specific.append = append;
self self
} }
} }
@@ -144,18 +161,18 @@ impl WindowBuilderExtWebSys for WindowBuilder {
/// Additional methods on `EventLoop` that are specific to the web. /// Additional methods on `EventLoop` that are specific to the web.
pub trait EventLoopExtWebSys { pub trait EventLoopExtWebSys {
/// A type provided by the user that can be passed through `Event::UserEvent`. /// A type provided by the user that can be passed through `Event::UserEvent`.
type UserEvent; type UserEvent: 'static;
/// Initializes the winit event loop. /// Initializes the winit event loop.
/// ///
/// Unlike /// Unlike
#[cfg_attr( #[cfg_attr(
all(web_platform, target_feature = "exception-handling"), all(web_platform, target_feature = "exception-handling"),
doc = "`run()`" doc = "`run_app()`"
)] )]
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run()`]" doc = "[`run_app()`]"
)] )]
/// [^1], this returns immediately, and doesn't throw an exception in order to /// [^1], this returns immediately, and doesn't throw an exception in order to
/// satisfy its [`!`] return type. /// satisfy its [`!`] return type.
@@ -167,26 +184,38 @@ pub trait EventLoopExtWebSys {
/// ///
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run()`]: EventLoop::run()" doc = "[`run_app()`]: EventLoop::run_app()"
)] )]
/// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`. /// [^1]: `run_app()` is _not_ available on WASM when the target supports `exception-handling`.
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, app: A);
/// See [`spawn_app`].
///
/// [`spawn_app`]: Self::spawn_app
#[deprecated = "use EventLoopExtWebSys::spawn_app"]
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget); F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
} }
impl<T> EventLoopExtWebSys for EventLoop<T> { impl<T> EventLoopExtWebSys for EventLoop<T> {
type UserEvent = T; type UserEvent = T;
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, mut app: A) {
self.event_loop.spawn(move |event, event_loop| {
event_loop::dispatch_event_for_app(&mut app, event_loop, event)
});
}
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget), F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
{ {
self.event_loop.spawn(event_handler) self.event_loop.spawn(event_handler)
} }
} }
pub trait EventLoopWindowTargetExtWebSys { pub trait ActiveEventLoopExtWebSys {
/// Sets the strategy for [`ControlFlow::Poll`]. /// Sets the strategy for [`ControlFlow::Poll`].
/// ///
/// See [`PollStrategy`]. /// See [`PollStrategy`].
@@ -200,9 +229,18 @@ pub trait EventLoopWindowTargetExtWebSys {
/// ///
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll /// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
fn poll_strategy(&self) -> PollStrategy; fn poll_strategy(&self) -> PollStrategy;
/// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the
/// cursor has completely finished loading.
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture;
} }
impl EventLoopWindowTargetExtWebSys for EventLoopWindowTarget { impl ActiveEventLoopExtWebSys for ActiveEventLoop {
#[inline]
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture {
self.p.create_custom_cursor_async(source)
}
#[inline] #[inline]
fn set_poll_strategy(&self, strategy: PollStrategy) { fn set_poll_strategy(&self, strategy: PollStrategy) {
self.p.set_poll_strategy(strategy); self.p.set_poll_strategy(strategy);
@@ -214,7 +252,7 @@ impl EventLoopWindowTargetExtWebSys for EventLoopWindowTarget {
} }
} }
/// Strategy used for [`ControlFlow::Poll`](crate::event_loop::ControlFlow::Poll). /// Strategy used for [`ControlFlow::Poll`][crate::event_loop::ControlFlow::Poll].
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum PollStrategy { pub enum PollStrategy {
/// Uses [`Window.requestIdleCallback()`] to queue the next event loop. If not available /// Uses [`Window.requestIdleCallback()`] to queue the next event loop. If not available
@@ -249,14 +287,14 @@ pub trait CustomCursorExtWebSys {
/// but browser support for image formats is inconsistent. Using [PNG] is recommended. /// but browser support for image formats is inconsistent. Using [PNG] is recommended.
/// ///
/// [PNG]: https://en.wikipedia.org/wiki/PNG /// [PNG]: https://en.wikipedia.org/wiki/PNG
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder; fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource;
/// Crates a new animated cursor from multiple [`CustomCursor`]s. /// Crates a new animated cursor from multiple [`CustomCursor`]s.
/// Supplied `cursors` can't be empty or other animations. /// Supplied `cursors` can't be empty or other animations.
fn from_animation( fn from_animation(
duration: Duration, duration: Duration,
cursors: Vec<CustomCursor>, cursors: Vec<CustomCursor>,
) -> Result<CustomCursorBuilder, BadAnimation>; ) -> Result<CustomCursorSource, BadAnimation>;
} }
impl CustomCursorExtWebSys for CustomCursor { impl CustomCursorExtWebSys for CustomCursor {
@@ -264,9 +302,9 @@ impl CustomCursorExtWebSys for CustomCursor {
self.inner.animation self.inner.animation
} }
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder { fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource {
CustomCursorBuilder { CustomCursorSource {
inner: PlatformCustomCursorBuilder::Url { inner: PlatformCustomCursorSource::Url {
url, url,
hotspot_x, hotspot_x,
hotspot_y, hotspot_y,
@@ -277,7 +315,7 @@ impl CustomCursorExtWebSys for CustomCursor {
fn from_animation( fn from_animation(
duration: Duration, duration: Duration,
cursors: Vec<CustomCursor>, cursors: Vec<CustomCursor>,
) -> Result<CustomCursorBuilder, BadAnimation> { ) -> Result<CustomCursorSource, BadAnimation> {
if cursors.is_empty() { if cursors.is_empty() {
return Err(BadAnimation::Empty); return Err(BadAnimation::Empty);
} }
@@ -286,8 +324,8 @@ impl CustomCursorExtWebSys for CustomCursor {
return Err(BadAnimation::Animation); return Err(BadAnimation::Animation);
} }
Ok(CustomCursorBuilder { Ok(CustomCursorSource {
inner: PlatformCustomCursorBuilder::Animation { duration, cursors }, inner: PlatformCustomCursorSource::Animation { duration, cursors },
}) })
} }
} }
@@ -312,26 +350,11 @@ impl fmt::Display for BadAnimation {
impl Error for BadAnimation {} impl Error for BadAnimation {}
pub trait CustomCursorBuilderExtWebSys {
/// Async version of [`CustomCursorBuilder::build()`] which waits until the
/// cursor has completely finished loading.
fn build_async(self, window_target: &EventLoopWindowTarget) -> CustomCursorFuture;
}
impl CustomCursorBuilderExtWebSys for CustomCursorBuilder {
fn build_async(self, window_target: &EventLoopWindowTarget) -> CustomCursorFuture {
CustomCursorFuture(PlatformCustomCursor::build_async(
self.inner,
&window_target.p,
))
}
}
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
struct PlatformCustomCursorFuture; struct PlatformCustomCursorFuture;
#[derive(Debug)] #[derive(Debug)]
pub struct CustomCursorFuture(PlatformCustomCursorFuture); pub struct CustomCursorFuture(pub(crate) PlatformCustomCursorFuture);
impl Future for CustomCursorFuture { impl Future for CustomCursorFuture {
type Output = Result<CustomCursor, CustomCursorError>; type Output = Result<CustomCursor, CustomCursorError>;

View File

@@ -1,3 +1,7 @@
//! # Windows
//!
//! The supported OS version is Windows 7 or higher, though Windows 10 is
//! tested regularly.
use std::{ffi::c_void, path::Path}; use std::{ffi::c_void, path::Path};
use crate::{ use crate::{
@@ -5,7 +9,7 @@ use crate::{
event::DeviceId, event::DeviceId,
event_loop::EventLoopBuilder, event_loop::EventLoopBuilder,
monitor::MonitorHandle, monitor::MonitorHandle,
window::{BadIcon, Icon, Window, WindowBuilder}, window::{BadIcon, Icon, Window, WindowAttributes},
}; };
/// Window Handle type used by Win32 API /// Window Handle type used by Win32 API
@@ -198,7 +202,7 @@ 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 [`WindowBuilderExtWindows::with_owner_window`]), the application must enable /// (as described in [`WindowAttributesExtWindows::with_owner_window`]), the application must 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.
/// ///
@@ -296,12 +300,12 @@ impl WindowExtWindows for Window {
} }
} }
/// Additional methods on `WindowBuilder` 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 WindowBuilderExtWindows { 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 [`WindowBuilder::with_parent_window`] isn't called or set to `None`. /// This only works when [`WindowAttributes::with_parent_window`] isn't called or set to `None`.
/// Can be used in combination with [`WindowExtWindows::set_enable(false)`](WindowExtWindows::set_enable) /// Can be used in combination with [`WindowExtWindows::set_enable(false)`][WindowExtWindows::set_enable]
/// on the owner window to create a modal dialog box. /// on the owner window to create a modal dialog box.
/// ///
/// From MSDN: /// From MSDN:
@@ -386,88 +390,88 @@ pub trait WindowBuilderExtWindows {
fn with_corner_preference(self, corners: CornerPreference) -> Self; fn with_corner_preference(self, corners: CornerPreference) -> Self;
} }
impl WindowBuilderExtWindows for WindowBuilder { impl WindowAttributesExtWindows for WindowAttributes {
#[inline] #[inline]
fn with_owner_window(mut self, parent: HWND) -> Self { fn with_owner_window(mut self, parent: HWND) -> Self {
self.window.platform_specific.owner = Some(parent); self.platform_specific.owner = Some(parent);
self self
} }
#[inline] #[inline]
fn with_menu(mut self, menu: HMENU) -> Self { fn with_menu(mut self, menu: HMENU) -> Self {
self.window.platform_specific.menu = Some(menu); self.platform_specific.menu = Some(menu);
self self
} }
#[inline] #[inline]
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self { fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self {
self.window.platform_specific.taskbar_icon = taskbar_icon; self.platform_specific.taskbar_icon = taskbar_icon;
self self
} }
#[inline] #[inline]
fn with_no_redirection_bitmap(mut self, flag: bool) -> Self { fn with_no_redirection_bitmap(mut self, flag: bool) -> Self {
self.window.platform_specific.no_redirection_bitmap = flag; self.platform_specific.no_redirection_bitmap = flag;
self self
} }
#[inline] #[inline]
fn with_drag_and_drop(mut self, flag: bool) -> Self { fn with_drag_and_drop(mut self, flag: bool) -> Self {
self.window.platform_specific.drag_and_drop = flag; self.platform_specific.drag_and_drop = flag;
self self
} }
#[inline] #[inline]
fn with_skip_taskbar(mut self, skip: bool) -> Self { fn with_skip_taskbar(mut self, skip: bool) -> Self {
self.window.platform_specific.skip_taskbar = skip; self.platform_specific.skip_taskbar = skip;
self self
} }
#[inline] #[inline]
fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self { fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self {
self.window.platform_specific.class_name = class_name.into(); self.platform_specific.class_name = class_name.into();
self self
} }
#[inline] #[inline]
fn with_undecorated_shadow(mut self, shadow: bool) -> Self { fn with_undecorated_shadow(mut self, shadow: bool) -> Self {
self.window.platform_specific.decoration_shadow = shadow; self.platform_specific.decoration_shadow = shadow;
self self
} }
#[inline] #[inline]
fn with_system_backdrop(mut self, backdrop_type: BackdropType) -> Self { fn with_system_backdrop(mut self, backdrop_type: BackdropType) -> Self {
self.window.platform_specific.backdrop_type = backdrop_type; self.platform_specific.backdrop_type = backdrop_type;
self self
} }
#[inline] #[inline]
fn with_clip_children(mut self, flag: bool) -> Self { fn with_clip_children(mut self, flag: bool) -> Self {
self.window.platform_specific.clip_children = flag; self.platform_specific.clip_children = flag;
self self
} }
#[inline] #[inline]
fn with_border_color(mut self, color: Option<Color>) -> Self { fn with_border_color(mut self, color: Option<Color>) -> Self {
self.window.platform_specific.border_color = Some(color.unwrap_or(Color::NONE)); self.platform_specific.border_color = Some(color.unwrap_or(Color::NONE));
self self
} }
#[inline] #[inline]
fn with_title_background_color(mut self, color: Option<Color>) -> Self { fn with_title_background_color(mut self, color: Option<Color>) -> Self {
self.window.platform_specific.title_background_color = Some(color.unwrap_or(Color::NONE)); self.platform_specific.title_background_color = Some(color.unwrap_or(Color::NONE));
self self
} }
#[inline] #[inline]
fn with_title_text_color(mut self, color: Color) -> Self { fn with_title_text_color(mut self, color: Color) -> Self {
self.window.platform_specific.title_text_color = Some(color); self.platform_specific.title_text_color = Some(color);
self self
} }
#[inline] #[inline]
fn with_corner_preference(mut self, corners: CornerPreference) -> Self { fn with_corner_preference(mut self, corners: CornerPreference) -> Self {
self.window.platform_specific.corner_preference = Some(corners); self.platform_specific.corner_preference = Some(corners);
self self
} }
} }
@@ -504,7 +508,7 @@ pub trait DeviceIdExtWindows {
impl DeviceIdExtWindows for DeviceId { impl DeviceIdExtWindows for DeviceId {
#[inline] #[inline]
fn persistent_identifier(&self) -> Option<String> { fn persistent_identifier(&self) -> Option<String> {
crate::platform_impl::persistent_identifier(*self) self.0.persistent_identifier()
} }
} }

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