Compare commits

..

135 Commits

Author SHA1 Message Date
Kirill Chibisov
9ea7a54130 api: show example for platform specific application trait 2024-08-08 22:54:24 +03:00
daxpedda
3bab4ef4fb Android: remove MonitorHandle support
Because we don't want to force all methods on `VideoModeHandle` to return `Option`, we decided to remove the already incomplete support in Android for both types.
2024-08-08 01:29:53 +02:00
daxpedda
58142680ce Various Monitor/VideoModeHandle methods now return an Option
`VideoModeHandle::refresh_rate_millihertz()` and `bit_depth()` now return a `Option<NonZero*>`.
`MonitorHandle::position()` now returns an `Option`.
On Orbital `MonitorHandle::name()` now returns `None` instead of a dummy name.
2024-08-08 01:29:51 +02:00
daxpedda
0ffcfd8a3a Remove MonitorHandle::size/refresh_rate_millihertz() 2024-08-08 00:53:01 +02:00
daxpedda
2e53533cc1 Add MonitorHandle::current_video_mode() 2024-08-08 00:47:20 +02:00
daxpedda
1168cd4113 Implement common Std traits on many types (#3796) 2024-08-08 00:46:28 +02:00
daxpedda
9dff801f93 Introduce FingerId (#3783) 2024-08-08 00:36:36 +02:00
Kirill Chibisov
f07153b8e0 api: refactor ActiveEventLoop into trait
This should help with further extensions because the backend event
loops are used directly now.
2024-08-06 21:02:53 +03:00
daxpedda
f5304815a1 Change create_custom_cursor() to return Result (#3844) 2024-08-06 18:57:03 +02:00
Kirill Chibisov
29e1041987 chore: drop libera link
It has low amount of users and not active, so better direct people
straight to matrix at this point.
2024-08-06 15:04:58 +03:00
daxpedda
15b79b18e1 Add ActiveEventLoop::system_theme()
This also fixes macOS returning `None` in `Window::theme()` if no theme
override is set, instead it now returns the system theme.

MacOS and Wayland were the only ones working correctly according to the
documentation, which was an oversight. The documentation was "fixed"
now.

Fixes #3837.
2024-08-05 21:51:38 +03:00
Pascal Hertleif
54ff9c3bb5 macOS: skip releasing unavailable monitors
Prevent assertion error when trying to close a fullscreen window that
was on a display that got disconnected.
2024-08-05 13:44:18 +03:00
daxpedda
546962c904 Remove DeviceEvent::Motion and WindowEvent::AxisMotion
We decided to remove them because they contained too little information
for the user to be useful. The assumption is that they were originally
implemented to enable gamepad support, which we already decided we are
not going to add directly to Winit.
2024-08-05 13:15:53 +03:00
daxpedda
b7a7f59298 Remove DeviceEvent::Added and Removed
This had no real use because we don't expose any information on
`DeviceId` except on Windows. But there we only expose the name. The
assumption is that this was originally added for gamepad support, which
never made it into Winit.
2024-08-05 13:01:31 +03:00
daxpedda
586255ac0a Web: monitor API improvements (#3847)
- Improved the documentation to point users into the right direction from all kinds of methods and types.
- De-duplicated some code and added more comments.
- Implement an ID system to correctly and efficiently implement `Eq`, `Hash`, `Ord`, `PartialEq` and `PartialOrd`.
- Fixed screen locking support being cached thread local, ergo calling from a different thread would require to make the check again, not fulfilling its purpose as a fast-path at all.
2024-08-04 16:49:19 +02:00
dependabot[bot]
42ba0a74e0 build(deps): bump EmbarkStudios/cargo-deny-action
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: daxpedda <daxpedda@gmail.com>
2024-08-04 15:14:12 +03:00
daxpedda
34a37b8747 Windows: use Box::leak() instead of Box::into_raw()
This was detected by a new change in Nightly Rust that applied
`#[must_use]` to the return value of `Box::into_raw()`.
2024-08-04 13:03:26 +03:00
daxpedda
02a0a91a94 Remove raw-window-handle v0.4 and v0.5 support (#3831) 2024-08-04 00:18:39 +02:00
dependabot[bot]
e3fbfb81d7 Web: bump ESLint to v9 (#3843) 2024-07-31 20:37:10 +02:00
daxpedda
0909bf3d9d Add to .git-blame-ignore-revs file 2024-07-28 17:39:04 +02:00
daxpedda
3398ebe467 Use Taplo for TOML formatting 2024-07-28 17:38:44 +02:00
dependabot[bot]
21c121f9b3 Bump GitHub Actions actions/cache and actions/configure-pages (#3836) 2024-07-28 17:06:44 +02:00
daxpedda
ce0d1dfe1b Add GitHub Actions to Dependabot (#3834)
Add GitHub Issues label to NPM package upgrades.
2024-07-28 16:55:49 +02:00
daxpedda
7892e86731 Move to TypeScript (#3830)
This moves our JS file to use TS instead, which allows us to use a proper linter to check the code.
All related files where moved out from the root in a dedicated folder to avoid polluting the Rust environment.
2024-07-27 18:26:52 +02:00
Kirill Chibisov
7b0104b54c android: add {Active,}EventLoopExtAndroid::android_app
This type comes from the user and stored for the entire lifetime, so
no need to hide it from them after they've passed it to winit.

Fixes #3818.
2024-07-26 21:49:32 +03:00
Kirill Chibisov
facb809f12 chore: drop v0.30 deprecated APIs
Co-authored-by: daxpedda <daxpedda@gmail.com>
2024-07-26 16:44:47 +03:00
daxpedda
4e2e764e4a Fix CI for Rust v1.80 (#3822)
`clippy::doc_lazy_continuation` was added, which needed some fixing from our side.
2024-07-25 15:15:21 +02:00
daxpedda
a0bc3e5dc8 Web: Implement MonitorHandle (#3801)
Requires getting permission from the user to get "detailed" support.
Also enables users to go fullscreen on specific monitors.
Exposes platform-specific orientation API.

Most functionality depends on browser support, currently only Chromium.
2024-07-23 20:33:10 +02:00
daxpedda
2e97ab3d4f Web: use raw data in DeviceEvent::MouseMotion (#3803)
Only supported on Chrome MacOS and Windows.
2024-07-23 17:05:55 +02:00
daxpedda
ef580b817d Web: async improvements (#3805)
- Internal: Fix dropping `Notifier` without sending a result causing `Future`s to never complete. This should never happen anyway, but now we get a panic instead of nothing if we hit a bug.
- Internal: Remove a bunch of `unwrap()`s that aren't required when correctly using `MainThreadMarker`.
- `Window::canvas()` is now able to return a reference instead of an owned value.

Extracted from #3801.
2024-07-23 16:47:35 +02:00
daxpedda
5ec934b1b0 Web: don't block pen input (#3813) 2024-07-23 15:32:20 +02:00
daxpedda
c9c260ca08 Exclude Redox OS from the MSRV policy (#3811)
Redox OS requires a Rust nightly toolchain to compile at the moment.
2024-07-23 13:46:35 +02:00
daxpedda
b6109d4a17 macOS: fix building with feature = "rwh_04" 2024-07-22 13:11:27 +03:00
Speykious
eef2848c98 feat: Implement smooth resizing on X11 with _NET_WM_SYNC_REQUEST
Without smooth resizing, the window will appear to jitter when it is being
resized. This is because X11 completes the resize before the client gets a
chance to draw a new frame.

This is fixed by using the "sync" extension to ensure that the resize of the
X11 window is synchronized with the server.

Closes #2153
2024-07-21 09:39:43 -07:00
daxpedda
73c01fff96 Run doc tests on Web as well (#3799) 2024-07-20 21:20:18 +02:00
daxpedda
88bbdb33da Web: avoid using js_sys::Reflect (#3804) 2024-07-20 20:33:21 +02:00
daxpedda
652ff7576c Web: remove unused CustomCursorError::Animation (#3794) 2024-07-17 10:38:30 +02:00
daxpedda
d741c58ac3 Web: implement Error for CustomCursorError (#3793) 2024-07-17 02:33:06 +02:00
Kirill Chibisov
944ab60eda Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-07-16 22:04:12 +03:00
daxpedda
8e23d1608a Web: fix WindowEvent::Resized not using rAF (#3790) 2024-07-16 12:29:27 +02:00
Kirill Chibisov
dc99920612 wayland: bump dependencies
Update SCTK as a follow-up to 1170554dbd (ignore events to dead
objects) fixing it for wl_output.
2024-07-16 12:58:03 +03:00
Mads Marquart
ee3ab33a7c Remove internal ActiveEventLoop::clear_exit
How to store and clear the internal state should be internal to the
implementation on each backend.
2024-07-14 21:51:38 +03:00
daxpedda
5b8f5cb54a Make DeviceId/WindowId::dummy() safe (#3784) 2024-07-14 13:14:32 +02:00
Mads Marquart
bf97def398 Change run_app(app: &mut A) to run_app(app: A) (#3721)
This allows the user more control over how they pass their application state
to Winit, and will hopefully allow `Drop` implementations on the application
handler to work in the future on all platforms.
2024-07-11 15:38:09 +02:00
daxpedda
d5fd8682eb Web: use the word "Web" correctly and consistently (#3785) 2024-07-10 16:17:39 +02:00
daxpedda
39a7d5b738 Add to .git-blame-ignore-revs file 2024-07-07 18:43:32 +02:00
daxpedda
2665c12098 Rustfmt: use group_imports 2024-07-07 18:38:50 +02:00
daxpedda
936da131c2 Web: fix MouseMotion coordinate space (#3770) 2024-07-05 16:07:01 +02:00
daxpedda
850dd97177 Fix CI (#3775) 2024-07-05 15:54:46 +02:00
Kirill Chibisov
1170554dbd wayland: ignore events for dead objects
Nothing wrong will happen if we ignore events when compositor is at
wrong, at least crashing because compositor is just _wrong_ probably is
not a great option.

Links: https://github.com/alacritty/alacritty/issues/8065
2024-07-05 13:09:23 +03:00
daxpedda
75ce71f05a Implement ApplicationHandler::can_create|destroy_surfaces() (#3765) 2024-06-30 00:41:57 +02:00
daxpedda
a0d69c782a add: ActiveEventLoop::create_proxy()
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2024-06-29 18:19:09 +03:00
daxpedda
2e93e48a3b Web: Remove some unnecessary compatibility code (#3766) 2024-06-29 00:52:05 +02:00
Mads Marquart
82d9bbe559 Remove EventLoopExtIOS::idiom and ios::Idiom (#2924)
Introduced in 3a7350c with unclear motivation.

Nowadays, this feature is incomplete and unsound, and the equivalent
functionality can be trivially achieved outside of `winit` using
`objc2-ui-kit`.
2024-06-24 22:56:20 +02:00
Mads Marquart
8bdd4d620e Use self:: when re-importing local module (#3757)
Follow-up to https://github.com/rust-windowing/winit/pull/3755.
2024-06-24 22:40:49 +02:00
Mads Marquart
9d5412ffe1 Move iOS and macOS implementations into new apple module (#3756)
Move iOS and macOS implementations to a shared folder called `apple`, to allow
us to reduce the code-duplication between these platforms in the future.

The folder structure is now:
- `src/platform_impl/apple/`
  - `appkit/`
  - `uikit/`
  - `example_shared_file.rs`
  - `mod.rs`

* Add preliminary support for tvOS, watchOS and visionOS
* Reduce duplication in Cargo.toml when specifying dependencies
2024-06-24 13:26:49 +02:00
Kirill Chibisov
ecb887e5c3 event_loop: remove generic user event
Let the users wake up the event loop and then they could poll their
user sources.

Co-authored-by: Mads Marquart <mads@marquart.dk>
Co-authored-by: daxpedda <daxpedda@gmail.com>
2024-06-24 13:04:55 +03:00
Mads Marquart
7d1287958f Avoid path when importing modules (#3755)
Rust tooling generally works better this way. This includes
rust-analyzer, but more noticeably the output from `tracing` typically
prints the module path, which did not correspond to the actual file
system before.

Concretely, tracing output from the macOS backend changes from printing:
`winit::platform_impl::platform::util`
To printing:
`winit::platform_impl::macos::util`
2024-06-24 03:57:48 +02:00
Mads Marquart
c0c14aaf00 macOS: Call ApplicationHandler directly instead of using Event (#3753)
Additionally, always queue events in `handle_scale_factor_changed`.

These events were intentionally not queued before, since they are
executed inside `handle_scale_factor_changed`, which is itself queued.
Though that's a bit too subtle, so let's just take the minuscule perf
hit of redundantly checking whether we need to queue again.

Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2024-06-23 23:14:22 +02:00
John Nunley
10dc0674bb ci: Use taiki-e/checkout-action
taiki-e/checkout-action has a few advantages over actions/checkout,
such as:

- It is written in Bash rather than Node.js
- It does not have frequent breaking changes, reducing churn

Signed-off-by: John Nunley <dev@notgull.net>
2024-06-23 12:28:58 -07:00
msiglreith
4f59796e8a Add notgull as Windows maintainer 2024-06-22 08:41:37 -07:00
msiglreith
32097d75c7 Update codeowner list 2024-06-22 08:41:37 -07:00
Bruce Mitchener
c6c4395c3b Use default-features, not default_features (#3746)
The latter syntax is deprecated and will be removed in Rust
2024 edition. This also generates a warning with current
versions of Rust.
2024-06-22 11:38:42 +02:00
Kirill Chibisov
38e6f9ad84 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-06-21 21:40:16 +03:00
daxpedda
3e6092b8ed Web: implement WaitUntilStrategy (#3739) 2024-06-20 23:07:42 +02:00
daxpedda
b4e83a5966 Web: set control flow strategies on EventLoop (#3740) 2024-06-20 22:56:08 +02:00
Mads Marquart
db2c97a995 macOS: set the theme on the NSWindow, instead of application-wide
This new implementation uses:
- The NSAppearanceCustomization protocol for retrieving the appearance
  of the window, instead of using the application-wide
  `-[NSApplication effectiveAppearance]`.
- Key-Value observing for observing the `effectiveAppearance` to compute
  the `ThemeChanged` event, instead of using the undocumented
  `AppleInterfaceThemeChangedNotification` notification.

This also fixes `WindowBuilder::with_theme` not having any effect, and
the conversion between `Theme` and `NSAppearance` is made a bit more
robust.
2024-06-20 17:05:34 +03:00
daxpedda
1552eb21f7 Bump MSRV to v1.73 (#3743) 2024-06-20 11:09:15 +02:00
Kirill Chibisov
d8ffd4bb26 x11: fix build on arm
The c_char type, which was used under the hood is different depending
on arch, thus use it directly instead of i8.

Fixes #3735.
2024-06-17 13:51:08 +03:00
Kirill Chibisov
34c15608e0 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-06-15 23:50:40 +03:00
Kirill Chibisov
eef6977f45 macOS: fix opacity handling
Not using `NSColor::clearColor()` results in Quartz thinking that the
window is not transparent at all, which results in artifacts.

However, not setting the `windowBackgroundColor` in
`Window::set_transparent` results in border not properly rendered.

Fixes: 94664ff687 (Don't set the background color)
2024-06-15 15:41:34 +03:00
Kirill Chibisov
078b46720b chore: address 1.79 clippy lints 2024-06-15 15:26:26 +03:00
daxpedda
3b4e064a07 Web: fix crash InnerSizeWriter::request_inner_size() (#3727) 2024-06-12 00:22:03 +02:00
daxpedda
39bc139500 Web: don't overwrite cursor with CursorIcon::Default (#3729) 2024-06-12 00:12:14 +02:00
daxpedda
9522670081 Web: queue EventLoopProxy::send_event() to microtask 2024-06-12 00:02:26 +02:00
Kirill Chibisov
9a1ef49dc3 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-06-10 17:13:33 -07:00
Mads Marquart
3a624e0f52 macOS/iOS: Various refactorings in application state (#3720)
I'm preparing to get rid of our application delegate in favour of registering
notification observers, to do so I'm renaming `app_delegate.rs` to
`app_state.rs`, and moving the functionality out of the Objective-C method
into a normal method.

Additionally, `AppState` previously implemented `Default`, but really, this
was a hack done because someone (probably myself) was too lazy to write out
the full initialization in `AppDelegate::new`.
2024-06-06 14:39:31 +02:00
Mads Marquart
279e3edc54 macOS: Improve event queuing (#3708)
* Use AppKit's internal queuing mechanisms

This allows events to be queued in a more consistent order, they're now
interleaved with events that we handle immediately (like redraw events),
instead of being handled afterwards.

* Only queue events if necessary

This makes the call stack / backtraces easier to understand whenever
possible, and generally improves upon the order in which events are
delivered.
2024-06-06 12:32:02 +02:00
Philippe Renon
0e74d37ff5 doc: clarify Window::pre_present_notify availability
Fixes #3703.
2024-06-06 12:11:06 +03:00
ShikiSuen
2d1382f7d6 Handle _selected_range sent to NSTextInputClient.setMarkedText(). (#3619)
Co-authored-by: Mads Marquart <mads@marquart.dk>
2024-05-31 08:54:20 +02:00
Mads Marquart
5d8091fc7f Implement ApplicationHandler for &mut A and Box<A> (#3709) 2024-05-29 11:51:53 +02:00
Mads Marquart
d7abe0316e Update objc2 to v0.2.2 (#3702)
- Use new `bitflags!` support.
- Use `objc2-ui-kit`.
- Change usage of `Id` to `Retained`.
2024-05-27 14:49:22 +02:00
Diggory Hardy
5ea20fc905 event_loop: add is_x11 and is_wayland on EventLoop 2024-05-26 17:38:10 +04:00
Golden Water
e108fa2fbf Resize when size changes on scale change on macOS
This fixes an issue where the window glitched due to resize
when the user doesn't actually change the window, but macOS
function to update window size was still called.
2024-05-23 20:40:07 +04:00
Kirill Chibisov
fff6788c12 chore: explicitly use cfg_aliases 0.2.1
This correctly handles recent nightly lint that requires to explicitly
define the CFG guards.
2024-05-22 15:51:29 +04:00
Kirill Chibisov
3e8fa41073 event_loop: remove deprecated run APIs
The APIs are not well suited for the `&dyn ApplicationHandler` model and
`Box<dyn EventLoop>` structure, thus remove them.
2024-05-20 20:27:36 +04:00
Kevin Müller
2b1c8cea1b bugfix: Replace pointer dereference with read_unaligned
On Raspberry Pi, using the Rust crate eframe caused the program to crash on
mouse movement. The Backtrace lead to this specific line of code, and the exact
error was a "misaligned pointer dereference: address must be a multiple of 0x8
but is xxxx"

The edit has been tested with the Raspberry Pi, which works now.
2024-05-19 15:08:14 -07:00
Ryan Burleson
ab33fb8eda fix doc typo in application.rs (#3676) 2024-05-07 21:24:02 +02:00
linkmauve
b0b64a9a15 Reexport older versions of raw-window-handle
When the user decides to use an older version of raw-window-handle,
through the rwh_04 or rwh_05 features, it makes sense to reexport the
crate so they don’t have to depend on it manually and can instead use
winit::raw_window_handle.
2024-05-06 19:50:25 +04:00
Mads Marquart
d5d202d60e Reduce usage of direct msg_send! 2024-05-06 19:09:38 +04:00
Mads Marquart
cb39ab29f4 macOS: Move util::EMPTY_RANGE to usage spot (#3685) 2024-05-06 16:54:52 +02:00
Mads Marquart
0a3cacd577 Retain ApplicationDelegate in NSWindowDelegate and NSView
The delegate is only weakly referenced by NSApplication, so getting it
from there may fail if the event loop has been dropped.

Fixes #3668.
2024-05-06 18:29:07 +04:00
Mads Marquart
16fd2baba0 Use rustc-check-cfg (#3682) 2024-05-06 07:11:57 +02:00
daxpedda
7f8771a362 Web: fix Clippy v1.78 FPs (#3678) 2024-05-03 22:24:54 +02:00
Kirill Chibisov
337d50779c Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-04-27 20:02:40 +04:00
Joshua Pedrick
fd477986de Add UIGestureRecognizerDelegate and PanGestureRecogniser (#3597)
- Allow all gestures simultaneously recognized.
- Add PanGestureRecogniser with min/max number of touches.
- Fix sending delta values relative to Update instead to match macOS.
- Fix rotation gesture units from iOS to be in degrees instead of radians.

Co-authored-by: Mads Marquart <mads@marquart.dk>
2024-04-27 15:55:04 +02:00
Mads Marquart
94664ff687 Don't set the background color when initializing with transparency (#3657)
Setting the background color changes how the window title bar appears,
which is something that the application should customize itself if it
wants this behaviour (and also, it wasn't set when calling
`set_transparent`, so the behaviour wasn't consistent).
2024-04-27 15:41:14 +02:00
growfrow
0fc8c37721 chore: fix some typos in comments (#3635)
Signed-off-by: growfrow <growfrow@outlook.com>
2024-04-27 15:29:11 +02:00
Kirill Chibisov
ec29c81ad2 chore: ensure that .cargo config is not published
Just in case, so the correct changelog will be rendered when pulling
the crate from the crates.io as archive and trying to build it.
2024-04-27 17:03:28 +04:00
Marijn Suijten
304a585493 android: bump to ndk 0.9.0 and android-activity 0.6.0 2024-04-27 13:35:11 +04:00
John Nunley
1682703b5c bugfix(win32): Only return win handle on OK thread
On Windows, it is generally unsafe to use the HWND outside of the thread
that it originates from. In reality, the HWND is an index into a
thread-local table, so using it outside of the GUI thread can result in
another window being used instead, following by code unsoundness. This
is why the WindowHandle type is !Send. However, Window is Send and Sync,
which means we have to account for this.

Thus far the best solution seems to be to check if we are not in the GUI
thread. If we aren't, refuse the return the window handle.

For users who want to ensure the safety themselves, the unsafe API
was added.

Signed-off-by: John Nunley <dev@notgull.net>
2024-04-26 20:28:10 +04:00
Mads Marquart
bdd80c8af2 Add .git-blame-ignore-revs
The user has to explicitly opt into this, the file itself documents how
to do so.
2024-04-26 20:19:45 +04:00
Kirill Chibisov
7b0c7b6cb2 chore(rustfmt): use nightly (#2325)
Stable rustfmt lacks a lot of features resulting in worse formatted
code, thus use nightly formatter.
2024-04-26 17:11:44 +02:00
Marijn Suijten
7006c7ceca bugfix(android): Allow Volume* keys to be passed to the user 2024-04-23 21:51:23 +04:00
Kirill Chibisov
2491f2bbd6 windows: bump windows-sys to 0.52 2024-04-22 17:21:53 +04:00
tetra
babbb715c5 Windows: implement resize increments (#3623) 2024-04-21 15:05:41 +02:00
John Nunley
be79e8979a docs: Don't reference EventLoopBuilderExtUnix
This replaces a reference to EventLoopBuilderExtUnix in a panic
message with EventLoopBuilderExtX11 or EventLoopBuilderExtWayland.

Closes #3488

Signed-off-by: John Nunley <dev@notgull.net>
2024-04-20 19:45:07 -07:00
Kirill Chibisov
9ab4c03e89 wayland: fix CSD decorations glitch when closing
In rare cases destroying subsurfaces before the main surface could
result in a frame where the window is still shown, but decorations
got hidden, right before the window itself disappears.
2024-04-19 14:40:00 +04:00
daxpedda
4f47a4e793 Update dev-dependencies (#3641) 2024-04-18 21:52:38 +02:00
daxpedda
c15fa6e433 Re-introduce Web examples (#3637) 2024-04-18 19:43:39 +02:00
Kirill Chibisov
24faacf497 docs: fix release steps in CONTRIBUTING.md
The steps were rebased incorrectly.

Fixes: 575d978202 (docs: Rework CONTRIBUTING guidelines)
2024-04-18 20:27:31 +04:00
Mads Marquart
080556f2c8 Fix icrate's clippy customization 2024-04-18 20:04:42 +04:00
Mads Marquart
259e868c05 Update objc2 crates (#3634)
Changes relevant to Winit:
- `icrate` has been deprecated in favour of separate crates per
  framework, in our case `objc2-foundation` and `objc2-app-kit` (and in
  the future `objc2-ui-kit` on iOS).
- Moved `MainThreadMarker::run_on_main` to free-standing function
  `run_on_main`.
- Changed how features work, this should result in less code that we
  need to compile.
- Enums are now real structs instead of type-aliases and free constants.
2024-04-18 17:34:19 +02:00
Kirill Chibisov
575d978202 docs: Rework CONTRIBUTING guidelines
Give better recommendations for contributors on how to propose start the
work, write commit messages, and handle review by explicitly stating
expectations, however make it clear that maintainers will help you if
you have issues.

Write down the guidelines for maintainers on how they should handle
contributions and interact with other maintainers to help maintaining
good code quality level by listing maintainers responsibilities and
providing suggestions on how to handle various situations.

Co-authored-by: John Nunley <dev@notgull.net>
Signed-off-by: John Nunley <dev@notgull.net>
2024-04-16 17:06:36 -07:00
Bruce Mitchener
7dd7dc1fc8 example: Fix typo: "fosus" -> "focus" (#3629) 2024-04-15 23:59:58 +02:00
Bruce Mitchener
df7c496a5d chore: more concise Debug output for WindowId 2024-04-14 21:06:18 +04:00
Kirill Chibisov
0086d7153b wayland: bump sctk-adwaita to 0.9.0
This is a breaking change, because the system versions of the libraries
used by sctk-adwaita were changed. Such changes cascade through all
the deps, so all libraries using winit MUST do a breaking change.
2024-04-14 20:38:20 +04:00
Bruce Mitchener
b79acd8a5a ci: Update all jobs to actions/checkout@v4
Updates the checkout action to the latest version
2024-04-13 20:50:41 -07:00
Bruce Mitchener
c2951e0194 docs: Fix copy/paste error in changelog
Due to a copy/paste error a comment in the migration in the changelog mentioned
"window events" in a context that was clearly meant for device events. This
commit fixes this issue.
2024-04-13 18:12:39 -07:00
Kirill Chibisov
8998e36994 chore(changelog): write migrations for 0.30
Add a migrations for the big API changes showcasing how to upgrade
some common snippets of old code to new APIs.

Co-authored-by: Mads Marquart <mads@marquart.dk>
2024-04-12 21:55:44 +04:00
Kirill Chibisov
5a7169c7a6 chore(changelog): use 'keep a changelog' format
Links: https://keepachangelog.com/en/1.0.0/
Co-authored-by: Mads Marquart <mads@marquart.dk>
2024-04-12 21:38:50 +04:00
Mads Marquart
4cd6877e8e Fix CI failing when updating typos to 1.20.3 (#3620) 2024-04-03 00:38:55 +02:00
Colin Kinloch
44aabdddcc example: add an example of request_inner_size
Add the shortcut "Alt + R" to swap the size of the window axes in
window example.
2024-03-28 23:39:19 +04:00
James Tucker
c4415009c0 windows: remove github URLs from binaries
There are  AV rules out there which cause almost any
program that contains github URLs to become marked as malware.

While those rules are spurious, they're years old, and AV vendors have a
very poor reputation at appropriately following up with these problems.

Remove these strings from the panic data present in the binary
prevents binaries linking the winit from being spuriously marked as
Trojan:Win32/Wacatac.B!ml.
2024-03-28 22:45:34 +04:00
Mads Marquart
63a7c02492 Consistently emit extra mouse motion events (#3601)
In particular, we don't want to emit those events inside of
`pressureChangeWithEvent:`, since the mouse motion value is sometimes
outdated.

Additionally, we want to ensure the events have been emitted during
other gestures.

Fixes https://github.com/rust-windowing/winit/issues/3516
2024-03-28 19:18:03 +01:00
John Nunley
7b0ef160fc chore: silence clippy
It appears that clippy's dead code detection has gotten better. This
commit fixes winit's code to match.

Signed-off-by: John Nunley <dev@notgull.net>
2024-03-27 12:20:21 +04:00
wannacu
962241e2a0 bugfix(x11): Fix window position calculation overflow during dragging
Closes #3594

Signed-off-by: wannacu <wannacu2049@gmail.com>
2024-03-25 21:16:29 -07:00
Mads Marquart
3efa6d855d chore: Fix Clippy after Rust 1.77
- There are some thread_local instances that can be made constant.
- An inner attribute can be moved to an outer one.
2024-03-21 19:32:20 -07:00
Smith Chang
9067426dca chore: remove repetitive words (#3599) 2024-03-21 18:44:23 +01:00
Fredrik Fornwall
ba10c35240 fix(windows): configure hidden and grabbed cursor
This fixes issues where a hidden and grabbed cursor could leave the
window and become visible on top of the windows taskbar (and potentially
leave the window altogether if the taskbar is clicked) under at least
two occasions:

    - When a window is overlapping the taskbar.
    - When a window is maximized and Automatically hide the taskbar has
      been enabled.

This approach of confining the cursor to the center of the window is
used in SDL.
2024-03-18 23:50:40 +04: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
246 changed files with 14111 additions and 13627 deletions

View File

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

8
.git-blame-ignore-revs Normal file
View File

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

17
.github/CODEOWNERS vendored
View File

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

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

22
.github/dependabot.yaml vendored Normal file
View File

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

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ jobs:
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:
@@ -27,12 +27,12 @@ jobs:
- name: Run Rustdoc - name: Run Rustdoc
env: env:
RUSTDOCFLAGS: --crate-version master --cfg=docsrs 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=serde,mint,android-native-activity
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v5
- name: Fix permissions - name: Fix permissions
run: | run: |

6
.gitignore vendored
View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,8 +9,12 @@ PR's changelog entry would go into the wrong section).
And please only add new entries to the top of this list, right below the `# And please only add new entries to the top of this list, right below the `#
Unreleased` header. Unreleased` header.
# Unreleased ## Unreleased
# 0.1.0 ## 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. - Add `LogicalUnit`, `PhysicalUnit` and `PixelUnit` types and related functions.

View File

@@ -1,25 +1,26 @@
[package] [package]
name = "dpi"
version = "0.1.0"
description = "Types for handling UI scaling"
keywords = ["DPI", "HiDPI", "scale-factor"]
categories = ["gui"] categories = ["gui"]
rust-version.workspace = true description = "Types for handling UI scaling"
repository.workspace = true
license.workspace = true
edition.workspace = true edition.workspace = true
keywords = ["DPI", "HiDPI", "scale-factor"]
license.workspace = true
name = "dpi"
repository.workspace = true
rust-version.workspace = true
version = "0.1.1"
[features] [features]
serde = ["dep:serde"]
mint = ["dep:mint"] mint = ["dep:mint"]
serde = ["dep:serde"]
[dependencies] [dependencies]
serde = { workspace = true, optional = true }
mint = { workspace = true, optional = true } mint = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["serde", "mint"] features = ["mint", "serde"]
# These are all tested in CI # These are all tested in CI
rustdoc-args = ["--cfg", "docsrs"]
targets = [ targets = [
# Windows # Windows
"i686-pc-windows-msvc", "i686-pc-windows-msvc",
@@ -36,4 +37,3 @@ targets = [
# Web # Web
"wasm32-unknown-unknown", "wasm32-unknown-unknown",
] ]
rustdoc-args = ["--cfg", "docsrs"]

View File

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

View File

@@ -1,21 +1,78 @@
#[cfg(all( #[cfg(any(x11_platform, macos_platform, windows_platform))]
feature = "rwh_06",
any(x11_platform, macos_platform, windows_platform)
))]
#[allow(deprecated)] #[allow(deprecated)]
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
use std::collections::HashMap; use std::collections::HashMap;
use winit::application::ApplicationHandler;
use winit::dpi::{LogicalPosition, LogicalSize, Position}; use winit::dpi::{LogicalPosition, LogicalSize, Position};
use winit::event::{ElementState, Event, KeyEvent, WindowEvent}; use winit::event::{ElementState, KeyEvent, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::raw_window_handle::HasRawWindowHandle; use winit::raw_window_handle::HasRawWindowHandle;
use winit::window::Window; use winit::window::{Window, WindowId};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window { #[derive(Default)]
struct Application {
parent_window_id: Option<WindowId>,
windows: HashMap<WindowId, Window>,
}
impl ApplicationHandler for Application {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
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();
println!("Parent window id: {:?})", window.id());
self.parent_window_id = Some(window.id());
self.windows.insert(window.id(), window);
}
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: winit::window::WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::CloseRequested => {
self.windows.clear();
event_loop.exit();
},
WindowEvent::CursorEntered { device_id: _ } => {
// On x11, println when the cursor entered in a window even if the child window
// is created by some key inputs.
// the child windows are always placed at (0, 0) with size (200, 200) in the
// parent window, so we also can see this log when we move
// the cursor around (200, 200) in parent window.
println!("cursor entered in the window {window_id:?}");
},
WindowEvent::KeyboardInput {
event: KeyEvent { state: ElementState::Pressed, .. },
..
} => {
let parent_window = self.windows.get(&self.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:?}");
self.windows.insert(child_id, child_window);
},
WindowEvent::RedrawRequested => {
if let Some(window) = self.windows.get(&window_id) {
fill::fill_window(window);
}
},
_ => (),
}
}
}
fn spawn_child_window(parent: &Window, event_loop: &dyn ActiveEventLoop) -> Window {
let parent = parent.raw_window_handle().unwrap(); let parent = parent.raw_window_handle().unwrap();
let mut window_attributes = Window::default_attributes() let mut window_attributes = Window::default_attributes()
.with_title("child window") .with_title("child window")
@@ -28,67 +85,14 @@ fn main() -> Result<(), impl std::error::Error> {
event_loop.create_window(window_attributes).unwrap() event_loop.create_window(window_attributes).unwrap()
} }
let mut windows = HashMap::new(); let event_loop = EventLoop::new().unwrap();
event_loop.run_app(Application::default())
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
let mut parent_window_id = None;
event_loop.run(move |event: Event<()>, event_loop| {
match event {
Event::Resumed => {
let attributes = Window::default_attributes()
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
let window = event_loop.create_window(attributes).unwrap();
parent_window_id = Some(window.id());
println!("Parent window id: {parent_window_id:?})");
windows.insert(window.id(), window);
}
Event::WindowEvent { window_id, event } => match event {
WindowEvent::CloseRequested => {
windows.clear();
event_loop.exit();
}
WindowEvent::CursorEntered { device_id: _ } => {
// On x11, println when the cursor entered in a window even if the child window is created
// by some key inputs.
// the child windows are always placed at (0, 0) with size (200, 200) in the parent window,
// so we also can see this log when we move the cursor around (200, 200) in parent window.
println!("cursor entered in the window {window_id:?}");
}
WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
..
},
..
} => {
let parent_window = windows.get(&parent_window_id.unwrap()).unwrap();
let child_window = spawn_child_window(parent_window, event_loop);
let child_id = child_window.id();
println!("Child window created with id: {child_id:?}");
windows.insert(child_id, child_window);
}
WindowEvent::RedrawRequested => {
if let Some(window) = windows.get(&window_id) {
fill::fill_window(window);
}
}
_ => (),
},
_ => (),
}
})
} }
#[cfg(all( #[cfg(not(any(x11_platform, macos_platform, windows_platform)))]
feature = "rwh_06",
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."
);
} }

View File

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

View File

@@ -1,15 +1,11 @@
#![allow(clippy::single_match)] #![allow(clippy::single_match)]
// Limit this example to only compatible platforms. // Limit this example to only compatible platforms.
#[cfg(any( #[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, android_platform,))]
windows_platform,
macos_platform,
x11_platform,
wayland_platform,
android_platform,
))]
fn main() -> std::process::ExitCode { fn main() -> std::process::ExitCode {
use std::{process::ExitCode, thread::sleep, time::Duration}; use std::process::ExitCode;
use std::thread::sleep;
use std::time::Duration;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::WindowEvent;
@@ -26,14 +22,14 @@ fn main() -> std::process::ExitCode {
} }
impl ApplicationHandler for PumpDemo { impl ApplicationHandler for PumpDemo {
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = Window::default_attributes().with_title("A fantastic window!"); let window_attributes = Window::default_attributes().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap()); self.window = Some(event_loop.create_window(window_attributes).unwrap());
} }
fn window_event( fn window_event(
&mut self, &mut self,
event_loop: &ActiveEventLoop, event_loop: &dyn ActiveEventLoop,
_window_id: WindowId, _window_id: WindowId,
event: WindowEvent, event: WindowEvent,
) { ) {
@@ -49,7 +45,7 @@ fn main() -> std::process::ExitCode {
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
fill::fill_window(window); fill::fill_window(window);
window.request_redraw(); window.request_redraw();
} },
_ => (), _ => (),
} }
} }

View File

@@ -22,13 +22,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
impl ApplicationHandler for App { impl ApplicationHandler for App {
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
if let Some(window) = self.window.as_ref() { if let Some(window) = self.window.as_ref() {
window.request_redraw(); window.request_redraw();
} }
} }
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = Window::default_attributes() let window_attributes = Window::default_attributes()
.with_title("Fantastic window number one!") .with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)); .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0));
@@ -39,7 +39,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
fn window_event( fn window_event(
&mut self, &mut self,
event_loop: &ActiveEventLoop, event_loop: &dyn ActiveEventLoop,
window_id: WindowId, window_id: WindowId,
event: WindowEvent, event: WindowEvent,
) { ) {
@@ -60,13 +60,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
match event { match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
println!("--------------------------------------------------------- Window {} CloseRequested", self.idx); println!(
"--------------------------------------------------------- Window {} \
CloseRequested",
self.idx
);
fill::cleanup_window(window); fill::cleanup_window(window);
self.window = None; self.window = None;
} },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
fill::fill_window(window); fill::fill_window(window);
} },
_ => (), _ => (),
} }
} }
@@ -76,10 +80,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
let mut app = App { let mut app = App { idx: 1, ..Default::default() };
idx: 1,
..Default::default()
};
event_loop.run_app_on_demand(&mut app)?; event_loop.run_app_on_demand(&mut app)?;
println!("--------------------------------------------------------- Finished first loop"); println!("--------------------------------------------------------- Finished first loop");

View File

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

25
examples/util/tracing.rs Normal file
View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,7 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
impl ApplicationHandler for XEmbedDemo { impl ApplicationHandler for XEmbedDemo {
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = Window::default_attributes() let window_attributes = Window::default_attributes()
.with_title("An embedded window!") .with_title("An embedded window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
@@ -29,7 +29,7 @@ fn main() -> Result<(), Box<dyn Error>> {
fn window_event( fn window_event(
&mut self, &mut self,
event_loop: &ActiveEventLoop, event_loop: &dyn ActiveEventLoop,
_window_id: WindowId, _window_id: WindowId,
event: WindowEvent, event: WindowEvent,
) { ) {
@@ -39,12 +39,12 @@ fn main() -> Result<(), Box<dyn Error>> {
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
window.pre_present_notify(); window.pre_present_notify();
fill::fill_window(window); fill::fill_window(window);
} },
_ => (), _ => (),
} }
} }
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
self.window.as_ref().unwrap().request_redraw(); self.window.as_ref().unwrap().request_redraw();
} }
} }
@@ -58,11 +58,7 @@ fn main() -> Result<(), Box<dyn Error>> {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let event_loop = EventLoop::new()?; let event_loop = EventLoop::new()?;
let mut app = XEmbedDemo { Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?)
parent_window_id,
window: None,
};
event_loop.run_app(&mut app).map_err(Into::into)
} }
#[cfg(not(x11_platform))] #[cfg(not(x11_platform))]

View File

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

View File

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

View File

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

View File

@@ -1,97 +1,195 @@
//! End user application handling. //! End user application handling.
use std::any::Any;
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent}; use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
use crate::event_loop::ActiveEventLoop; use crate::event_loop::ActiveEventLoop;
use crate::window::WindowId; use crate::window::WindowId;
/// The handler of the application events. /// The handler of the application events.
pub trait ApplicationHandler<T: 'static = ()> { pub trait ApplicationHandler {
/// Emitted when new events arrive from the OS to be processed. /// 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 /// This is a useful place to put code that should be done before you start processing
/// events, such as updating frame timing information for benchmarking or checking the /// events, such as updating frame timing information for benchmarking or checking the
/// [`StartCause`] to see if a timer set by /// [`StartCause`] to see if a timer set by
/// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed. /// [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil] has elapsed.
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
let _ = (event_loop, cause); let _ = (event_loop, cause);
} }
/// Emitted when the application has been resumed. /// Emitted when the application has been resumed.
/// ///
/// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a /// See [`suspended()`][Self::suspended].
/// 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 /// ## Platform-specific
/// ///
/// It's recommended that applications should only initialize their graphics context and create /// ### iOS
/// 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 /// On iOS, the [`resumed()`] method is called in response to an [`applicationDidBecomeActive`]
/// driven by multiple platform-specific events, and that there may be subtle differences across /// callback which means the application is about to transition from the inactive to active
/// platforms with how these internal events are delivered, it's recommended that applications /// state (according to the [iOS application lifecycle]).
/// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events.
/// ///
/// Also see [`Suspended`] notes. /// [`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
/// ///
/// ## Android /// ### Web
/// ///
/// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is /// On Web, the [`resumed()`] method is called in response to a [`pageshow`] event if the
/// expected to closely correlate with the [`onResume`] lifecycle event but there may technically /// page is being restored from the [`bfcache`] (back/forward cache) - an in-memory cache
/// be a discrepancy. /// that stores a complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`bfcache`]: https://web.dev/bfcache/
///
/// ### Others
///
/// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported.
///
/// [`resumed()`]: Self::resumed
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted from the point onwards the application should create render surfaces.
///
/// See [`destroy_surfaces()`].
///
/// ## Portability
///
/// It's recommended that applications should only initialize their render surfaces after the
/// [`can_create_surfaces()`] method is called. Some systems (specifically Android) won't allow
/// applications to create a render surface until that point.
///
/// For consistency, all platforms call this method even if they don't themselves have a formal
/// surface destroy/create lifecycle. For systems without a surface destroy/create lifecycle the
/// [`can_create_surfaces()`] event is always emitted after the [`StartCause::Init`] event.
///
/// Applications should be able to gracefully handle back-to-back [`can_create_surfaces()`] and
/// [`destroy_surfaces()`] calls.
///
/// ## Platform-specific
///
/// ### Android
///
/// On Android, the [`can_create_surfaces()`] method is called when a new [`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() /// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
/// ///
/// Applications that need to run on Android must wait until they have been `Resumed` /// Applications that need to run on Android must wait until they have been "resumed" before
/// before they will be able to create a render surface (such as an `EGLSurface`, /// they will be able to create a render surface (such as an `EGLSurface`, [`VkSurfaceKHR`]
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a /// or [`wgpu::Surface`]) which depend on having a [`SurfaceView`]. Applications must also
/// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their /// assume that if they are [suspended], then their render surfaces are invalid and should
/// render surfaces are invalid and should be dropped. /// be dropped.
///
/// Also see [`Suspended`] notes.
/// ///
/// [suspended]: Self::destroy_surfaces
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html /// [`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 /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
/// ///
/// ## iOS /// [`can_create_surfaces()`]: Self::can_create_surfaces
/// /// [`destroy_surfaces()`]: Self::destroy_surfaces
/// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`] fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop);
/// 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`]. /// Called after a wake up is requested using [`EventLoopProxy::wake_up()`].
/// ///
/// [`EventLoopProxy::send_event`]: crate::event_loop::EventLoopProxy::send_event /// Multiple calls to the aforementioned method will be merged, and will only wake the event
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) { /// loop once; however, due to the nature of multi-threading some wake ups may appear
let _ = (event_loop, event); /// spuriously. For these reasons, you should not rely on the number of times that this was
/// called.
///
/// The order in which this is emitted in relation to other events is not guaranteed. The time
/// at which this will be emitted is not guaranteed, only that it will happen "soon". That is,
/// there may be several executions of the event loop, including multiple redraws to windows,
/// between [`EventLoopProxy::wake_up()`] being called and the event being delivered.
///
/// [`EventLoopProxy::wake_up()`]: crate::event_loop::EventLoopProxy::wake_up
///
/// # Example
///
/// Use a [`std::sync::mpsc`] channel to handle events from a different thread.
///
/// ```no_run
/// use std::sync::mpsc;
/// use std::thread;
/// use std::time::Duration;
///
/// use winit::application::ApplicationHandler;
/// use winit::event_loop::{ActiveEventLoop, EventLoop};
///
/// struct MyApp {
/// receiver: mpsc::Receiver<u64>,
/// }
///
/// impl ApplicationHandler for MyApp {
/// # fn window_event(
/// # &mut self,
/// # _event_loop: &dyn ActiveEventLoop,
/// # _window_id: winit::window::WindowId,
/// # _event: winit::event::WindowEvent,
/// # ) {
/// # }
/// #
/// # fn can_create_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {}
/// #
/// fn proxy_wake_up(&mut self, _event_loop: &dyn ActiveEventLoop) {
/// // Iterate current events, since wake-ups may have been merged.
/// //
/// // Note: We take care not to use `recv` or `iter` here, as those are blocking,
/// // and that would be bad for performance and might lead to a deadlock.
/// for i in self.receiver.try_iter() {
/// println!("received: {i}");
/// }
/// }
///
/// // Rest of `ApplicationHandler`
/// }
///
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let event_loop = EventLoop::new()?;
///
/// let (sender, receiver) = mpsc::channel();
///
/// let mut app = MyApp { receiver };
///
/// // Send an event in a loop
/// let proxy = event_loop.create_proxy();
/// let background_thread = thread::spawn(move || {
/// let mut i = 0;
/// loop {
/// println!("sending: {i}");
/// if sender.send(i).is_err() {
/// // Stop sending once `MyApp` is dropped
/// break;
/// }
/// // Trigger the wake-up _after_ we placed the event in the channel.
/// // Otherwise, `proxy_wake_up` might be triggered prematurely.
/// proxy.wake_up();
/// i += 1;
/// thread::sleep(Duration::from_secs(1));
/// }
/// });
///
/// event_loop.run_app(&mut app)?;
///
/// drop(app);
/// background_thread.join().unwrap();
///
/// Ok(())
/// }
/// ```
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
} }
/// Emitted when the OS sends an event to a winit window. /// Emitted when the OS sends an event to a winit window.
fn window_event( fn window_event(
&mut self, &mut self,
event_loop: &ActiveEventLoop, event_loop: &dyn ActiveEventLoop,
window_id: WindowId, window_id: WindowId,
event: WindowEvent, event: WindowEvent,
); );
@@ -99,7 +197,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// Emitted when the OS sends an event to a device. /// Emitted when the OS sends an event to a device.
fn device_event( fn device_event(
&mut self, &mut self,
event_loop: &ActiveEventLoop, event_loop: &dyn ActiveEventLoop,
device_id: DeviceId, device_id: DeviceId,
event: DeviceEvent, event: DeviceEvent,
) { ) {
@@ -109,37 +207,61 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// Emitted when the event loop is about to block and wait for new events. /// Emitted when the event loop is about to block and wait for new events.
/// ///
/// Most applications shouldn't need to hook into this event since there is no real relationship /// Most applications shouldn't need to hook into this event since there is no real relationship
/// between how often the event loop needs to wake up and the dispatching of any specific events. /// 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 /// High frequency event sources, such as input devices could potentially lead to lots of wake
/// ups and also lots of corresponding `AboutToWait` events. /// ups and also lots of corresponding `AboutToWait` events.
/// ///
/// This is not an ideal event to drive application rendering from and instead applications /// This is not an ideal event to drive application rendering from and instead applications
/// should render in response to [`WindowEvent::RedrawRequested`] events. /// should render in response to [`WindowEvent::RedrawRequested`] events.
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop; let _ = event_loop;
} }
/// Emitted when the application has been suspended. /// Emitted when the application has been suspended.
/// ///
/// # Portability /// See [`resumed()`][Self::resumed].
/// ///
/// Not all platforms support the notion of suspending applications, and there may be no /// ## Platform-specific
/// 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 /// ### iOS
/// 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. /// On iOS, the [`suspended()`] method is called in response to an
/// [`applicationWillResignActive`] callback which means that the application is about to
/// transition from the active to inactive state (according to the [iOS application lifecycle]).
/// ///
/// ## Android /// [`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
/// ///
/// On Android, the `Suspended` event is only sent when the application's associated /// ### Web
///
/// On Web, the [`suspended()`] method is called in response to a [`pagehide`] event if the
/// page is being restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
/// stores a complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
/// [`bfcache`]: https://web.dev/bfcache/
///
/// ### Others
///
/// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported.
///
/// [`suspended()`]: Self::suspended
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the application must destroy its render surfaces.
///
/// See [`can_create_surfaces()`] for more details.
///
/// ## Platform-specific
///
/// ### Android
///
/// On Android, the [`destroy_surfaces()`] method is called when the application's associated
/// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`] /// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`]
/// lifecycle event but there may technically be a discrepancy. /// lifecycle event but there may technically be a discrepancy.
/// ///
@@ -149,45 +271,32 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// destroyed, which indirectly invalidates any existing render surfaces that may have 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`]). /// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
/// ///
/// After being `Suspended` on Android applications must drop all render surfaces before /// After being [suspended] on Android applications must drop all render surfaces before
/// the event callback completes, which may be re-created when the application is next [`Resumed`]. /// the event callback completes, which may be re-created when the application is next
/// [resumed].
/// ///
/// [suspended]: Self::destroy_surfaces
/// [resumed]: Self::can_create_surfaces
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html /// [`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 /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
/// ///
/// ## iOS /// ### Others
/// ///
/// On iOS, the `Suspended` event is currently emitted in response to an /// - **iOS / macOS / Orbital / Wayland / Web / Windows / X11:** Unsupported.
/// [`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 /// [`can_create_surfaces()`]: Self::can_create_surfaces
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle /// [`destroy_surfaces()`]: Self::destroy_surfaces
/// fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
/// ## 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; let _ = event_loop;
} }
/// Emitted when the event loop is being shut down. /// Emitted when the event loop is being shut down.
/// ///
/// This is irreversible - if this method is called, it is guaranteed that the event loop /// This is irreversible - if this method is called, it is guaranteed that the event loop
/// will exist right after. /// will exit right after.
fn exiting(&mut self, event_loop: &ActiveEventLoop) { fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop; let _ = event_loop;
} }
@@ -197,17 +306,17 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// ///
/// ### Android /// ### Android
/// ///
/// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application /// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The
/// must [release memory] or risk being killed. /// application must [release memory] or risk being killed.
/// ///
/// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory() /// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory()
/// [release memory]: https://developer.android.com/topic/performance/memory#release /// [release memory]: https://developer.android.com/topic/performance/memory#release
/// ///
/// ### iOS /// ### iOS
/// ///
/// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`] /// On iOS, the `MemoryWarning` event is emitted in response to an
/// callback. The application must free as much memory as possible or risk being terminated, see /// [`applicationDidReceiveMemoryWarning`] callback. The application must free as much
/// [how to respond to memory warnings]. /// memory as possible or risk being terminated, see [how to respond to memory warnings].
/// ///
/// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni /// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni
/// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings /// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings
@@ -215,7 +324,161 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// ### Others /// ### Others
/// ///
/// - **macOS / Orbital / Wayland / Web / Windows:** Unsupported. /// - **macOS / Orbital / Wayland / Web / Windows:** Unsupported.
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) { fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop; let _ = event_loop;
} }
/// Get the [`ApplicationHandler`] as [`Any`].
///
/// This is useful for downcasting to a concrete application type.
#[inline(always)]
fn as_any(&mut self) -> Option<&mut dyn Any> {
None
}
}
#[deny(clippy::missing_trait_methods)]
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
#[inline(always)]
fn as_any(&mut self) -> Option<&mut dyn Any> {
(**self).as_any()
}
#[inline]
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
}
#[inline]
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).resumed(event_loop);
}
#[inline]
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).can_create_surfaces(event_loop);
}
#[inline]
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).proxy_wake_up(event_loop);
}
#[inline]
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, event);
}
#[inline]
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
}
#[inline]
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).about_to_wait(event_loop);
}
#[inline]
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).suspended(event_loop);
}
#[inline]
fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).destroy_surfaces(event_loop);
}
#[inline]
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
}
#[deny(clippy::missing_trait_methods)]
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
#[inline(always)]
fn as_any(&mut self) -> Option<&mut dyn Any> {
(**self).as_any()
}
#[inline]
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
}
#[inline]
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).resumed(event_loop);
}
#[inline]
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).can_create_surfaces(event_loop);
}
#[inline]
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).proxy_wake_up(event_loop);
}
#[inline]
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, event);
}
#[inline]
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
}
#[inline]
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).about_to_wait(event_loop);
}
#[inline]
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).suspended(event_loop);
}
#[inline]
fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).destroy_surfaces(event_loop);
}
#[inline]
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
} }

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

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

132
src/changelog/unreleased.md Normal file
View File

@@ -0,0 +1,132 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
The sections should follow the order `Added`, `Changed`, `Deprecated`,
`Removed`, and `Fixed`.
Platform specific changed should be added to the end of the section and grouped
by platform name. Common API additions should have `, implemented` at the end
for platforms where the API was initially implemented. See the following example
on how to add them:
```md
### Added
- Add `Window::turbo()`, implemented on X11, Wayland, and Web.
- On X11, add `Window::some_rare_api`.
- On X11, add `Window::even_more_rare_api`.
- On Wayland, add `Window::common_api`.
- On Windows, add `Window::some_rare_api`.
```
When the change requires non-trivial amount of work for users to comply
with it, the migration guide should be added below the entry, like:
```md
- Deprecate `Window` creation outside of `EventLoop::run`
This was done to simply migration in the future. Consider the
following code:
// Code snippet.
To migrate it we should do X, Y, and then Z, for example:
// Code snippet.
```
The migration guide could reference other migration examples in the current
changelog entry.
## Unreleased
### Added
- Add `ActiveEventLoop::create_proxy()`.
- On Web, implement `Error` for `platform::web::CustomCursorError`.
- On Web, add `ActiveEventLoopExtWeb::is_cursor_lock_raw()` to determine if
`DeviceEvent::MouseMotion` is returning raw data, not OS accelerated, when using
`CursorGrabMode::Locked`.
- On Web, implement `MonitorHandle` and `VideoModeHandle`.
Without prompting the user for permission, only the current monitor is returned. But when
prompting and being granted permission through
`ActiveEventLoop::request_detailed_monitor_permission()`, access to all monitors and their
information is available. This "detailed monitors" can be used in `Window::set_fullscreen()` as
well.
- On Android, add `{Active,}EventLoopExtAndroid::android_app()` to access the app used to create the loop.
- Add `ActiveEventLoop::system_theme()`, returning the current system theme.
- Add `Touch::finger_id` with a new type `FingerId`.
- On Web and Windows, add `FingerIdExt*::is_primary()`, exposing a way to determine
the primary finger in a multi-touch interaction.
- Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd`
and `Serialize` on many types.
- Add `MonitorHandle::current_video_mode()`.
### Changed
- Change `ActiveEventLoop` to be a trait.
- `ApplicationHandler` now uses `dyn ActiveEventLoop`.
- On Web, let events wake up event loop immediately when using `ControlFlow::Poll`.
- Bump MSRV from `1.70` to `1.73`.
- Changed `ApplicationHandler::user_event` to `user_wake_up`, removing the
generic user event.
Winit will now only indicate that wake up happened, you will have to pair
this with an external mechanism like `std::sync::mpsc::channel` if you want
to send specific data to be processed on the main thread.
- Changed `EventLoopProxy::send_event` to `EventLoopProxy::wake_up`, it now
only wakes up the loop.
- On X11, implement smooth resizing through the sync extension API.
- `ApplicationHandler::create|destroy_surfaces()` was split off from
`ApplicationHandler::resumed/suspended()`.
`ApplicationHandler::can_create_surfaces()` should, for portability reasons
to Android, be the only place to create render surfaces.
`ApplicationHandler::resumed/suspended()` are now only emitted by iOS and Web
and now signify actually resuming/suspending the application.
- Rename `platform::web::*ExtWebSys` to `*ExtWeb`.
- Change signature of `EventLoop::run_app`, `EventLoopExtPumpEvents::pump_app_events` and
`EventLoopExtRunOnDemand::run_app_on_demand` to accept a `impl ApplicationHandler` directly,
instead of requiring a `&mut` reference to it.
- On Web, `Window::canvas()` now returns a reference.
- On Web, `CursorGrabMode::Locked` now lets `DeviceEvent::MouseMotion` return raw data, not OS
accelerated, if the browser supports it.
- `(Active)EventLoop::create_custom_cursor()` now returns a `Result<CustomCursor, ExternalError>`.
- Changed how `ModifiersState` is serialized by Serde.
- `VideoModeHandle::refresh_rate_millihertz()` and `bit_depth()` now return a `Option<NonZero*>`.
- `MonitorHandle::position()` now returns an `Option`.
### Removed
- Remove `Event`.
- Remove already deprecated APIs:
- `EventLoop::create_window()`
- `EventLoop::run`.
- `EventLoopBuilder::new()`
- `EventLoopExtPumpEvents::pump_events`.
- `EventLoopExtRunOnDemand::run_on_demand`.
- `VideoMode`
- `WindowAttributes::new()`
- `Window::set_cursor_icon()`
- On iOS, remove `platform::ios::EventLoopExtIOS` and related `platform::ios::Idiom` type.
This feature was incomplete, and the equivalent functionality can be trivially achieved outside
of `winit` using `objc2-ui-kit` and calling `UIDevice::currentDevice().userInterfaceIdiom()`.
- On Web, remove unused `platform::web::CustomCursorError::Animation`.
- Remove the `rwh_04` and `rwh_05` cargo feature and the corresponding `raw-window-handle` v0.4 and
v0.5 support. v0.6 remains in place and is enabled by default.
- Remove `DeviceEvent::Added` and `DeviceEvent::Removed`.
- Remove `DeviceEvent::Motion` and `WindowEvent::AxisMotion`.
- Remove `Touch::id` in favor of `Touch::finger_id`.
- Remove `MonitorHandle::size()` and `refresh_rate_millihertz()` in favor of
`MonitorHandle::current_video_mode()`.
- On Android, remove all `MonitorHandle` support instead of emitting false data.
### Fixed
- On Web, pen events are now routed through to `WindowEvent::Cursor*`.
- On macOS, fix panic when releasing not available monitor.
- On MacOS, return the system theme in `Window::theme()` if no theme override is set.
- On Orbital, `MonitorHandle::name()` now returns `None` instead of a dummy name.

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`.

285
src/changelog/v0.30.md Normal file
View File

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

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

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

View File

@@ -15,7 +15,8 @@ pub enum ExternalError {
} }
/// The error type for when the requested operation is not supported by the backend. /// The error type for when the requested operation is not supported by the backend.
#[derive(Clone)] #[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NotSupportedError { pub struct NotSupportedError {
_marker: (), _marker: (),
} }
@@ -71,10 +72,7 @@ macro_rules! os_error {
impl fmt::Display for OsError { impl fmt::Display for OsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.pad(&format!( f.pad(&format!("os error at {}:{}: {}", self.file, self.line, self.error))
"os error at {}:{}: {}",
self.file, self.line, self.error
))
} }
} }
@@ -117,19 +115,14 @@ impl error::Error for NotSupportedError {}
impl error::Error for EventLoopError {} impl error::Error for EventLoopError {}
#[cfg(test)] #[cfg(test)]
#[allow(clippy::redundant_clone)]
mod tests { mod tests {
#![allow(clippy::redundant_clone)]
use super::*; use super::*;
// Eat attributes for testing // Eat attributes for testing
#[test] #[test]
fn ensure_fmt_does_not_panic() { fn ensure_fmt_does_not_panic() {
let _ = format!( let _ = format!("{:?}, {}", NotSupportedError::new(), NotSupportedError::new().clone());
"{:?}, {}",
NotSupportedError::new(),
NotSupportedError::new().clone()
);
let _ = format!( let _ = format!(
"{:?}, {}", "{:?}, {}",
ExternalError::NotSupported(NotSupportedError::new()), ExternalError::NotSupported(NotSupportedError::new()),

View File

@@ -1,7 +1,8 @@
//! The [`Event`] enum and assorted supporting types. //! The event enums and assorted supporting types.
//! //!
//! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get //! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get
//! processed and used to modify the program state. For more details, see the root-level documentation. //! processed and used to modify the program state. For more details, see the root-level
//! documentation.
//! //!
//! Some of these events represent different "parts" of a traditional event-handling loop. You could //! Some of these events represent different "parts" of a traditional event-handling loop. You could
//! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this: //! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this:
@@ -44,22 +45,24 @@ use smol_str::SmolStr;
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time::Instant; use web_time::Instant;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::error::ExternalError; use crate::error::ExternalError;
use crate::event_loop::AsyncRequestSerial;
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState};
use crate::platform_impl;
#[cfg(doc)] #[cfg(doc)]
use crate::window::Window; use crate::window::Window;
use crate::{ use crate::window::{ActivationToken, Theme, WindowId};
dpi::{PhysicalPosition, PhysicalSize},
event_loop::AsyncRequestSerial,
keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState},
platform_impl,
window::{ActivationToken, Theme, WindowId},
};
// TODO: Remove once the backends can call `ApplicationHandler` methods directly. For now backends
// like Windows and Web require `Event` to wire user events, otherwise each backend will have to
// wrap `Event` in some other structure.
/// 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.
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Event<T: 'static> { pub(crate) enum Event {
/// See [`ApplicationHandler::new_events`] for details. /// See [`ApplicationHandler::new_events`] for details.
/// ///
/// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events /// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events
@@ -68,29 +71,25 @@ pub enum Event<T: 'static> {
/// See [`ApplicationHandler::window_event`] for details. /// See [`ApplicationHandler::window_event`] for details.
/// ///
/// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event /// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event
WindowEvent { #[allow(clippy::enum_variant_names)]
window_id: WindowId, WindowEvent { window_id: WindowId, event: WindowEvent },
event: WindowEvent,
},
/// See [`ApplicationHandler::device_event`] for details. /// See [`ApplicationHandler::device_event`] for details.
/// ///
/// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event /// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event
DeviceEvent { #[allow(clippy::enum_variant_names)]
device_id: DeviceId, DeviceEvent { device_id: DeviceId, event: DeviceEvent },
event: DeviceEvent,
},
/// See [`ApplicationHandler::user_event`] for details.
///
/// [`ApplicationHandler::user_event`]: crate::application::ApplicationHandler::user_event
UserEvent(T),
/// See [`ApplicationHandler::suspended`] for details. /// See [`ApplicationHandler::suspended`] for details.
/// ///
/// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended /// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended
Suspended, Suspended,
/// See [`ApplicationHandler::can_create_surfaces`] for details.
///
/// [`ApplicationHandler::can_create_surfaces`]: crate::application::ApplicationHandler::can_create_surfaces
CreateSurfaces,
/// See [`ApplicationHandler::resumed`] for details. /// See [`ApplicationHandler::resumed`] for details.
/// ///
/// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed /// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
@@ -110,45 +109,24 @@ pub enum Event<T: 'static> {
/// ///
/// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning /// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning
MemoryWarning, MemoryWarning,
}
impl<T> Event<T> { /// User requested a wake up.
#[allow(clippy::result_large_err)] UserWakeUp,
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
use self::Event::*;
match self {
UserEvent(_) => Err(self),
WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }),
DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }),
NewEvents(cause) => Ok(NewEvents(cause)),
AboutToWait => Ok(AboutToWait),
LoopExiting => Ok(LoopExiting),
Suspended => Ok(Suspended),
Resumed => Ok(Resumed),
MemoryWarning => Ok(MemoryWarning),
}
}
} }
/// Describes the reason the event loop is resuming. /// Describes the reason the event loop is resuming.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StartCause { pub enum StartCause {
/// Sent if the time specified by [`ControlFlow::WaitUntil`] has been reached. Contains the /// Sent if the time specified by [`ControlFlow::WaitUntil`] has been reached. Contains the
/// moment the timeout was requested and the requested resume time. The actual resume time is /// moment the timeout was requested and the requested resume time. The actual resume time is
/// guaranteed to be equal to or after the requested resume time. /// guaranteed to be equal to or after the requested resume time.
/// ///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil /// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
ResumeTimeReached { ResumeTimeReached { start: Instant, requested_resume: Instant },
start: Instant,
requested_resume: Instant,
},
/// Sent if the OS has new events to send to the window, after a wait was requested. Contains /// Sent if the OS has new events to send to the window, after a wait was requested. Contains
/// the moment the wait was requested and the resume time, if requested. /// the moment the wait was requested and the resume time, if requested.
WaitCancelled { WaitCancelled { start: Instant, requested_resume: Option<Instant> },
start: Instant,
requested_resume: Option<Instant>,
},
/// Sent if the event loop is being resumed after the loop's control flow was set to /// Sent if the event loop is being resumed after the loop's control flow was set to
/// [`ControlFlow::Poll`]. /// [`ControlFlow::Poll`].
@@ -164,18 +142,11 @@ pub enum StartCause {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum WindowEvent { pub enum WindowEvent {
/// The activation token was delivered back and now could be used. /// The activation token was delivered back and now could be used.
/// #[cfg_attr(not(any(x11_platform, wayland_platform)), allow(rustdoc::broken_intra_doc_links))]
#[cfg_attr(
not(any(x11_platform, wayland_platform)),
allow(rustdoc::broken_intra_doc_links)
)]
/// Delivered in response to [`request_activation_token`]. /// Delivered in response to [`request_activation_token`].
/// ///
/// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token /// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token
ActivationTokenDone { ActivationTokenDone { serial: AsyncRequestSerial, token: ActivationToken },
serial: AsyncRequestSerial,
token: ActivationToken,
},
/// The size of the window has changed. Contains the client area's new dimensions. /// The size of the window has changed. Contains the client area's new dimensions.
Resized(PhysicalSize<u32>), Resized(PhysicalSize<u32>),
@@ -229,10 +200,10 @@ pub enum WindowEvent {
/// If `true`, the event was generated synthetically by winit /// If `true`, the event was generated synthetically by winit
/// in one of the following circumstances: /// in one of the following circumstances:
/// ///
/// * Synthetic key press events are generated for all keys pressed /// * Synthetic key press events are generated for all keys pressed when a window gains
/// when a window gains focus. Likewise, synthetic key release events /// focus. Likewise, synthetic key release events are generated for all keys pressed when
/// are generated for all keys pressed when a window goes out of focus. /// a window goes out of focus. ***Currently, this is only functional on X11 and
/// ***Currently, this is only functional on X11 and Windows*** /// Windows***
/// ///
/// Otherwise, this value is always `false`. /// Otherwise, this value is always `false`.
is_synthetic: bool, is_synthetic: bool,
@@ -262,9 +233,10 @@ pub enum WindowEvent {
CursorMoved { CursorMoved {
device_id: DeviceId, device_id: DeviceId,
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is /// (x,y) coords in pixels relative to the top-left corner of the window. Because the range
/// limited by the display area and it may have been transformed by the OS to implement effects such as cursor /// of this data is limited by the display area and it may have been transformed by
/// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control. /// the OS to implement effects such as cursor acceleration, it should not be used
/// to implement non-cursor-like interactions such as 3D camera control.
position: PhysicalPosition<f64>, position: PhysicalPosition<f64>,
}, },
@@ -291,18 +263,10 @@ pub enum WindowEvent {
CursorLeft { device_id: DeviceId }, CursorLeft { device_id: DeviceId },
/// A mouse wheel movement or touchpad scroll occurred. /// A mouse wheel movement or touchpad scroll occurred.
MouseWheel { MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase },
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
},
/// An mouse button press has been received. /// An mouse button press has been received.
MouseInput { MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton },
device_id: DeviceId,
state: ElementState,
button: MouseButton,
},
/// Two-finger pinch gesture, often used for magnification. /// Two-finger pinch gesture, often used for magnification.
/// ///
@@ -320,6 +284,19 @@ pub enum WindowEvent {
phase: TouchPhase, phase: TouchPhase,
}, },
/// N-finger pan gesture
///
/// ## Platform-specific
///
/// - Only available on **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
PanGesture {
device_id: DeviceId,
/// Change in pixels of pan gesture from last update.
delta: PhysicalPosition<f32>,
phase: TouchPhase,
},
/// Double tap gesture. /// Double tap gesture.
/// ///
/// On a Mac, smart magnification is triggered by a double tap with two fingers /// On a Mac, smart magnification is triggered by a double tap with two fingers
@@ -351,6 +328,7 @@ pub enum WindowEvent {
/// - On iOS, not recognized by default. It must be enabled when needed. /// - On iOS, not recognized by default. It must be enabled when needed.
RotationGesture { RotationGesture {
device_id: DeviceId, device_id: DeviceId,
/// change in rotation in degrees
delta: f32, delta: f32,
phase: TouchPhase, phase: TouchPhase,
}, },
@@ -358,20 +336,9 @@ pub enum WindowEvent {
/// Touchpad pressure event. /// Touchpad pressure event.
/// ///
/// At the moment, only supported on Apple forcetouch-capable macbooks. /// At the moment, only supported on Apple forcetouch-capable macbooks.
/// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad /// The parameters are: pressure level (value between 0 and 1 representing how hard the
/// is being pressed) and stage (integer representing the click level). /// touchpad is being pressed) and stage (integer representing the click level).
TouchpadPressure { TouchpadPressure { device_id: DeviceId, pressure: f32, stage: i64 },
device_id: DeviceId,
pressure: f32,
stage: i64,
},
/// Motion on some analog axis. May report data redundant to other, more specific events.
AxisMotion {
device_id: DeviceId,
axis: AxisId,
value: f64,
},
/// Touch event has been received /// Touch event has been received
/// ///
@@ -393,8 +360,8 @@ pub enum WindowEvent {
/// * Changing the display's scale factor (e.g. in Control Panel on Windows). /// * Changing the display's scale factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different scale factor. /// * Moving the window to a display with a different scale factor.
/// ///
/// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the window is /// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the
/// resized to the value suggested by the OS, but it can be changed to any value. /// window is resized to the value suggested by the OS, but it can be changed to any value.
/// ///
/// For more information about DPI in general, see the [`dpi`] crate. /// For more information about DPI in general, see the [`dpi`] crate.
ScaleFactorChanged { ScaleFactorChanged {
@@ -410,6 +377,8 @@ pub enum WindowEvent {
/// Applications might wish to react to this to change the theme of the content of the window /// Applications might wish to react to this to change the theme of the content of the window
/// when the system changes the window theme. /// when the system changes the window theme.
/// ///
/// This only reports a change if the window theme was not overridden by [`Window::set_theme`].
///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported. /// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported.
@@ -424,10 +393,11 @@ pub enum WindowEvent {
/// ///
/// ### iOS /// ### iOS
/// ///
/// On iOS, the `Occluded(false)` event is emitted in response to an [`applicationWillEnterForeground`] /// On iOS, the `Occluded(false)` event is emitted in response to an
/// callback which means the application should start preparing its data. The `Occluded(true)` event is /// [`applicationWillEnterForeground`] callback which means the application should start
/// emitted in response to an [`applicationDidEnterBackground`] callback which means the application /// preparing its data. The `Occluded(true)` event is emitted in response to an
/// should free resources (according to the [iOS application lifecycle]). /// [`applicationDidEnterBackground`] callback which means the application should free
/// resources (according to the [iOS application lifecycle]).
/// ///
/// [`applicationWillEnterForeground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground /// [`applicationWillEnterForeground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground
/// [`applicationDidEnterBackground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground /// [`applicationDidEnterBackground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground
@@ -457,44 +427,82 @@ pub enum WindowEvent {
/// Identifier of an input device. /// Identifier of an input device.
/// ///
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which /// Whenever you receive an event arising from a particular input device, this event contains a
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or /// `DeviceId` which identifies its origin. Note that devices may be virtual (representing an
/// physical. Virtual devices typically aggregate inputs from multiple physical devices. /// 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)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(pub(crate) platform_impl::DeviceId); pub struct DeviceId(pub(crate) platform_impl::DeviceId);
impl Default for DeviceId {
fn default() -> Self {
Self::dummy()
}
}
impl DeviceId { impl DeviceId {
/// Returns a dummy id, useful for unit testing. /// Returns a dummy id, useful for unit testing.
/// ///
/// # Safety /// # Notes
/// ///
/// The only guarantee made about the return value of this function is that /// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function. /// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `DeviceId`. /// No other guarantees are made. This may be equal to a real `DeviceId`.
pub const fn dummy() -> Self {
DeviceId(platform_impl::DeviceId::dummy())
}
}
/// Identifier of a finger in a touch event.
///
/// Whenever a touch event is received it contains a `FingerId` which uniquely identifies the finger
/// used for the current interaction.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId(pub(crate) platform_impl::FingerId);
impl FingerId {
/// Returns a dummy id, useful for unit testing.
/// ///
/// **Passing this into a winit function will result in undefined behavior.** /// # Notes
pub const unsafe fn dummy() -> Self { ///
#[allow(unused_unsafe)] /// The only guarantee made about the return value of this function is that
DeviceId(unsafe { platform_impl::DeviceId::dummy() }) /// 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 `FingerId`.
pub const fn dummy() -> Self {
FingerId(platform_impl::FingerId::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
/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because /// or first-person game controls. Many physical actions, such as mouse movement, can produce both
/// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs /// device and window events. Because window events typically arise from virtual devices
/// may not match. /// (corresponding to GUI cursors and keyboard focus) the device IDs may not match.
/// ///
/// Note that these events are delivered regardless of input focus. /// Note that these events are delivered regardless of input focus.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum DeviceEvent { pub enum DeviceEvent {
Added,
Removed,
/// Change in physical position of a pointing device. /// Change in physical position of a pointing device.
/// ///
/// This represents raw, unfiltered physical motion. Not to be confused with [`WindowEvent::CursorMoved`]. /// This represents raw, unfiltered physical motion. Not to be confused with
/// [`WindowEvent::CursorMoved`].
///
/// ## Platform-specific
///
/// **Web:** Only returns raw data, not OS accelerated, if [`CursorGrabMode::Locked`] is used
/// and browser support is available, see
#[cfg_attr(
any(web_platform, docsrs),
doc = "[`ActiveEventLoopExtWeb::is_cursor_lock_raw()`][crate::platform::web::ActiveEventLoopExtWeb::is_cursor_lock_raw()]."
)]
#[cfg_attr(
not(any(web_platform, docsrs)),
doc = "`ActiveEventLoopExtWeb::is_cursor_lock_raw()`."
)]
///
#[rustfmt::skip]
/// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked
MouseMotion { MouseMotion {
/// (x, y) change in position in unspecified units. /// (x, y) change in position in unspecified units.
/// ///
@@ -507,14 +515,6 @@ pub enum DeviceEvent {
delta: MouseScrollDelta, delta: MouseScrollDelta,
}, },
/// Motion on some analog axis. This event will be reported for all arbitrary input devices
/// that winit supports on this platform, including mouse devices. If the device is a mouse
/// device then this will be reported alongside the MouseMotion event.
Motion {
axis: AxisId,
value: f64,
},
Button { Button {
button: ButtonId, button: ButtonId,
state: ElementState, state: ElementState,
@@ -530,7 +530,7 @@ pub enum DeviceEvent {
/// repeat or the initial keypress. An application may emulate this by, for /// repeat or the initial keypress. An application may emulate this by, for
/// example keeping a Map/Set of pressed keys and determining whether a keypress /// example keeping a Map/Set of pressed keys and determining whether a keypress
/// corresponds to an already pressed key. /// corresponds to an already pressed key.
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RawKeyEvent { pub struct RawKeyEvent {
pub physical_key: keyboard::PhysicalKey, pub physical_key: keyboard::PhysicalKey,
@@ -551,11 +551,11 @@ pub struct KeyEvent {
/// ## Caveats /// ## Caveats
/// ///
/// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that /// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that
/// implements DVORAK in hardware (or firmware) /// implements DVORAK in hardware (or firmware)
/// - Your application will likely have to handle keyboards which are missing keys that your /// - Your application will likely have to handle keyboards which are missing keys that your
/// own keyboard has. /// own keyboard has.
/// - Certain `KeyCode`s will move between a couple of different positions depending on what /// - Certain `KeyCode`s will move between a couple of different positions depending on what
/// layout the keyboard was manufactured to support. /// layout the keyboard was manufactured to support.
/// ///
/// **Because of these caveats, it is important that you provide users with a way to configure /// **Because of these caveats, it is important that you provide users with a way to configure
/// most (if not all) keybinds in your application.** /// most (if not all) keybinds in your application.**
@@ -577,8 +577,7 @@ pub struct KeyEvent {
/// ///
/// This has two use cases: /// This has two use cases:
/// - Allows querying whether the current input is a Dead key. /// - Allows querying whether the current input is a Dead key.
/// - Allows handling key-bindings on platforms which don't /// - Allows handling key-bindings on platforms which don't support [`key_without_modifiers`].
/// support [`key_without_modifiers`].
/// ///
/// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard /// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard
/// shortcuts, **it is important that you provide users with a way to configure your /// shortcuts, **it is important that you provide users with a way to configure your
@@ -586,8 +585,8 @@ pub struct KeyEvent {
/// incompatible keyboard layout.** /// incompatible keyboard layout.**
/// ///
/// ## Platform-specific /// ## Platform-specific
/// - **Web:** Dead keys might be reported as the real key instead /// - **Web:** Dead keys might be reported as the real key instead of `Dead` depending on the
/// of `Dead` depending on the browser/OS. /// browser/OS.
/// ///
/// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
pub logical_key: keyboard::Key, pub logical_key: keyboard::Key,
@@ -615,13 +614,13 @@ pub struct KeyEvent {
/// Contains the location of this key on the keyboard. /// Contains the location of this key on the keyboard.
/// ///
/// Certain keys on the keyboard may appear in more than once place. For example, the "Shift" key /// Certain keys on the keyboard may appear in more than once place. For example, the "Shift"
/// appears on the left side of the QWERTY keyboard as well as the right side. However, both keys /// key appears on the left side of the QWERTY keyboard as well as the right side. However,
/// have the same symbolic value. Another example of this phenomenon is the "1" key, which appears /// both keys have the same symbolic value. Another example of this phenomenon is the "1"
/// both above the "Q" key and as the "Keypad 1" key. /// key, which appears both above the "Q" key and as the "Keypad 1" key.
/// ///
/// This field allows the user to differentiate between keys like this that have the same symbolic /// This field allows the user to differentiate between keys like this that have the same
/// value but different locations on the keyboard. /// symbolic value but different locations on the keyboard.
/// ///
/// See the [`KeyLocation`] type for more details. /// See the [`KeyLocation`] type for more details.
/// ///
@@ -636,46 +635,48 @@ pub struct KeyEvent {
/// Whether or not this key is a key repeat event. /// Whether or not this key is a key repeat event.
/// ///
/// On some systems, holding down a key for some period of time causes that key to be repeated /// On some systems, holding down a key for some period of time causes that key to be repeated
/// as though it were being pressed and released repeatedly. This field is `true` if and only if /// as though it were being pressed and released repeatedly. This field is `true` if and only
/// this event is the result of one of those repeats. /// if this event is the result of one of those repeats.
/// ///
/// # Example /// # Example
/// ///
/// In games, you often want to ignore repated key events - this can be /// In games, you often want to ignore repated key events - this can be
/// done by ignoring events where this property is set. /// done by ignoring events where this property is set.
/// ///
/// ``` /// ```no_run
/// use winit::event::{WindowEvent, KeyEvent, ElementState}; /// use winit::event::{ElementState, KeyEvent, WindowEvent};
/// use winit::keyboard::{KeyCode, PhysicalKey}; /// use winit::keyboard::{KeyCode, PhysicalKey};
/// # let window_event = WindowEvent::RedrawRequested; // To make the example compile /// # let window_event = WindowEvent::RedrawRequested; // To make the example compile
/// match window_event { /// match window_event {
/// WindowEvent::KeyboardInput { /// WindowEvent::KeyboardInput {
/// event: KeyEvent { /// event:
/// physical_key: PhysicalKey::Code(KeyCode::KeyW), /// KeyEvent {
/// state: ElementState::Pressed, /// physical_key: PhysicalKey::Code(KeyCode::KeyW),
/// repeat: false, /// state: ElementState::Pressed,
/// .. /// repeat: false,
/// }, /// ..
/// },
/// .. /// ..
/// } => { /// } => {
/// // The physical key `W` was pressed, and it was not a repeat /// // The physical key `W` was pressed, and it was not a repeat
/// } /// },
/// _ => {} // Handle other events /// _ => {}, // Handle other events
/// } /// }
/// ``` /// ```
pub repeat: bool, pub repeat: bool,
/// Platform-specific key event information. /// Platform-specific key event information.
/// ///
/// On Windows, Linux and macOS, this type contains the key without modifiers and the text with all /// On Windows, Linux and macOS, this type contains the key without modifiers and the text with
/// modifiers applied. /// all modifiers applied.
/// ///
/// On Android, iOS, Redox and Web, this type is a no-op. /// On Android, iOS, Redox and Web, this type is a no-op.
pub(crate) platform_specific: platform_impl::KeyEventExtra, pub(crate) platform_specific: platform_impl::KeyEventExtra,
} }
/// Describes keyboard modifiers event. /// Describes keyboard modifiers event.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Modifiers { pub struct Modifiers {
pub(crate) state: ModifiersState, pub(crate) state: ModifiersState,
@@ -742,10 +743,7 @@ impl Modifiers {
impl From<ModifiersState> for Modifiers { impl From<ModifiersState> for Modifiers {
fn from(value: ModifiersState) -> Self { fn from(value: ModifiersState) -> Self {
Self { Self { state: value, pressed_mods: Default::default() }
state: value,
pressed_mods: Default::default(),
}
} }
} }
@@ -753,12 +751,16 @@ impl From<ModifiersState> for Modifiers {
/// ///
/// This is also called a "composition event". /// This is also called a "composition event".
/// ///
/// Most keypresses using a latin-like keyboard layout simply generate a [`WindowEvent::KeyboardInput`]. /// Most keypresses using a latin-like keyboard layout simply generate a
/// However, one couldn't possibly have a key for every single unicode character that the user might want to type /// [`WindowEvent::KeyboardInput`]. However, one couldn't possibly have a key for every single
/// - so the solution operating systems employ is to allow the user to type these using _a sequence of keypresses_ instead. /// unicode character that the user might want to type
/// - so the solution operating systems employ is to allow the user to type these using _a sequence
/// of keypresses_ instead.
///
/// A prominent example of this is accents - many keyboard layouts allow you to first click the
/// "accent key", and then the character you want to apply the accent to. In this case, some
/// platforms will generate the following event sequence:
/// ///
/// A prominent example of this is accents - many keyboard layouts allow you to first click the "accent key", and then
/// the character you want to apply the accent to. In this case, some platforms will generate the following event sequence:
/// ```ignore /// ```ignore
/// // Press "`" key /// // Press "`" key
/// Ime::Preedit("`", Some((0, 0))) /// Ime::Preedit("`", Some((0, 0)))
@@ -767,11 +769,13 @@ impl From<ModifiersState> for Modifiers {
/// Ime::Commit("é") /// Ime::Commit("é")
/// ``` /// ```
/// ///
/// Additionally, certain input devices are configured to display a candidate box that allow the user to select the /// Additionally, certain input devices are configured to display a candidate box that allow the
/// desired character interactively. (To properly position this box, you must use [`Window::set_ime_cursor_area`].) /// user to select the desired character interactively. (To properly position this box, you must use
/// [`Window::set_ime_cursor_area`].)
///
/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keyboard the
/// following event sequence could be obtained:
/// ///
/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keyboard the following event
/// sequence could be obtained:
/// ```ignore /// ```ignore
/// // Press "A" key /// // Press "A" key
/// Ime::Preedit("a", Some((1, 1))) /// Ime::Preedit("a", Some((1, 1)))
@@ -790,8 +794,8 @@ impl From<ModifiersState> for Modifiers {
pub enum Ime { pub enum Ime {
/// Notifies when the IME was enabled. /// Notifies when the IME was enabled.
/// ///
/// After getting this event you could receive [`Preedit`](Self::Preedit) and /// After getting this event you could receive [`Preedit`][Self::Preedit] and
/// [`Commit`](Self::Commit) events. You should also start performing IME related requests /// [`Commit`][Self::Commit] events. You should also start performing IME related requests
/// like [`Window::set_ime_cursor_area`]. /// like [`Window::set_ime_cursor_area`].
Enabled, Enabled,
@@ -811,10 +815,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
/// preedit text. /// pending preedit text.
Disabled, Disabled,
} }
@@ -865,15 +869,16 @@ pub struct Touch {
/// ///
/// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**. /// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**.
/// - **Android**: This will never be [None]. If the device doesn't support pressure /// - **Android**: This will never be [None]. If the device doesn't support pressure
/// sensitivity, force will either be 0.0 or 1.0. Also see the /// sensitivity, force will either be 0.0 or 1.0. Also see the
/// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE). /// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE).
pub force: Option<Force>, pub force: Option<Force>,
/// Unique identifier of a finger. /// Unique identifier of a finger.
pub id: u64, pub finger_id: FingerId,
} }
/// Describes the force of a touch event /// Describes the force of a touch event
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Force { pub enum Force {
/// On iOS, the force is calibrated so that the same number corresponds to /// On iOS, the force is calibrated so that the same number corresponds to
/// roughly the same amount of pressure on the screen regardless of the /// roughly the same amount of pressure on the screen regardless of the
@@ -913,17 +918,13 @@ impl Force {
/// consistent across devices. /// consistent across devices.
pub fn normalized(&self) -> f64 { pub fn normalized(&self) -> f64 {
match self { match self {
Force::Calibrated { Force::Calibrated { force, max_possible_force, altitude_angle } => {
force,
max_possible_force,
altitude_angle,
} => {
let force = match altitude_angle { let force = match altitude_angle {
Some(altitude_angle) => force / altitude_angle.sin(), Some(altitude_angle) => force / altitude_angle.sin(),
None => *force, None => *force,
}; };
force / max_possible_force force / max_possible_force
} },
Force::Normalized(force) => *force, Force::Normalized(force) => *force,
} }
} }
@@ -1027,25 +1028,31 @@ impl PartialEq for InnerSizeWriter {
} }
} }
impl Eq for InnerSizeWriter {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::event;
use std::collections::{BTreeSet, HashSet}; use std::collections::{BTreeSet, HashSet};
use crate::dpi::PhysicalPosition;
use crate::event;
macro_rules! foreach_event { macro_rules! foreach_event {
($closure:expr) => {{ ($closure:expr) => {{
#[allow(unused_mut)] #[allow(unused_mut)]
let mut x = $closure; let mut x = $closure;
let did = unsafe { event::DeviceId::dummy() }; let did = event::DeviceId::dummy();
let fid = event::FingerId::dummy();
#[allow(deprecated)] #[allow(deprecated)]
{ {
use crate::event::{Event::*, Ime::Enabled, WindowEvent::*}; use crate::event::Event::*;
use crate::event::Ime::Enabled;
use crate::event::WindowEvent::*;
use crate::window::WindowId; use crate::window::WindowId;
// Mainline events. // Mainline events.
let wid = unsafe { WindowId::dummy() }; let wid = WindowId::dummy();
x(UserEvent(()));
x(NewEvents(event::StartCause::Init)); x(NewEvents(event::StartCause::Init));
x(AboutToWait); x(AboutToWait);
x(LoopExiting); x(LoopExiting);
@@ -1053,12 +1060,7 @@ mod tests {
x(Resumed); x(Resumed);
// Window events. // Window events.
let with_window_event = |wev| { let with_window_event = |wev| x(WindowEvent { window_id: wid, event: wev });
x(WindowEvent {
window_id: wid,
event: wev,
})
};
with_window_event(CloseRequested); with_window_event(CloseRequested);
with_window_event(Destroyed); with_window_event(Destroyed);
@@ -1069,10 +1071,7 @@ mod tests {
with_window_event(HoveredFile("x.txt".into())); with_window_event(HoveredFile("x.txt".into()));
with_window_event(HoveredFileCancelled); with_window_event(HoveredFileCancelled);
with_window_event(Ime(Enabled)); with_window_event(Ime(Enabled));
with_window_event(CursorMoved { with_window_event(CursorMoved { device_id: did, position: (0, 0).into() });
device_id: did,
position: (0, 0).into(),
});
with_window_event(ModifiersChanged(event::Modifiers::default())); with_window_event(ModifiersChanged(event::Modifiers::default()));
with_window_event(CursorEntered { device_id: did }); with_window_event(CursorEntered { device_id: did });
with_window_event(CursorLeft { device_id: did }); with_window_event(CursorLeft { device_id: did });
@@ -1097,21 +1096,17 @@ mod tests {
delta: 0.0, delta: 0.0,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
}); });
with_window_event(TouchpadPressure { with_window_event(PanGesture {
device_id: did, device_id: did,
pressure: 0.0, delta: PhysicalPosition::<f32>::new(0.0, 0.0),
stage: 0, phase: event::TouchPhase::Started,
});
with_window_event(AxisMotion {
device_id: did,
axis: 0,
value: 0.0,
}); });
with_window_event(TouchpadPressure { device_id: did, pressure: 0.0, stage: 0 });
with_window_event(Touch(event::Touch { with_window_event(Touch(event::Touch {
device_id: did, device_id: did,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
location: (0.0, 0.0).into(), location: (0.0, 0.0).into(),
id: 0, finger_id: fid,
force: Some(event::Force::Normalized(0.0)), force: Some(event::Force::Normalized(0.0)),
})); }));
with_window_event(ThemeChanged(crate::window::Theme::Light)); with_window_event(ThemeChanged(crate::window::Theme::Light));
@@ -1122,29 +1117,14 @@ mod tests {
{ {
use event::DeviceEvent::*; use event::DeviceEvent::*;
let with_device_event = |dev_ev| { let with_device_event =
x(event::Event::DeviceEvent { |dev_ev| x(event::Event::DeviceEvent { device_id: did, event: dev_ev });
device_id: did,
event: dev_ev,
})
};
with_device_event(Added); with_device_event(MouseMotion { delta: (0.0, 0.0).into() });
with_device_event(Removed);
with_device_event(MouseMotion {
delta: (0.0, 0.0).into(),
});
with_device_event(MouseWheel { with_device_event(MouseWheel {
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
}); });
with_device_event(Motion { with_device_event(Button { button: 0, state: event::ElementState::Pressed });
axis: 0,
value: 0.0,
});
with_device_event(Button {
button: 0,
state: event::ElementState::Pressed,
});
} }
}}; }};
} }
@@ -1152,35 +1132,19 @@ 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);
}) })
} }
#[test]
fn test_map_nonuser_event() {
foreach_event!(|event: event::Event<()>| {
let is_user = matches!(event, event::Event::UserEvent(()));
let event2 = event.map_nonuser_event::<()>();
if is_user {
assert_eq!(event2, Err(event::Event::UserEvent(())));
} else {
assert!(event2.is_ok());
}
})
}
#[test] #[test]
fn test_force_normalize() { fn test_force_normalize() {
let force = event::Force::Normalized(0.0); let force = event::Force::Normalized(0.0);
assert_eq!(force.normalized(), 0.0); assert_eq!(force.normalized(), 0.0);
let force2 = event::Force::Calibrated { let force2 =
force: 5.0, event::Force::Calibrated { force: 5.0, max_possible_force: 2.5, altitude_angle: None };
max_possible_force: 2.5,
altitude_angle: None,
};
assert_eq!(force2.normalized(), 2.0); assert_eq!(force2.normalized(), 2.0);
let force3 = event::Force::Calibrated { let force3 = event::Force::Calibrated {
@@ -1194,12 +1158,13 @@ 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();
let did = unsafe { crate::event::DeviceId::dummy() }.clone(); let did = crate::event::DeviceId::dummy().clone();
let fid = crate::event::FingerId::dummy().clone();
HashSet::new().insert(did); HashSet::new().insert(did);
let mut set = [did, did, did]; let mut set = [did, did, did];
set.sort_unstable(); set.sort_unstable();
@@ -1215,15 +1180,12 @@ mod tests {
device_id: did, device_id: did,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
location: (0.0, 0.0).into(), location: (0.0, 0.0).into(),
id: 0, finger_id: fid,
force: Some(event::Force::Normalized(0.0)), force: Some(event::Force::Normalized(0.0)),
} }
.clone(); .clone();
let _ = event::Force::Calibrated { let _ =
force: 0.0, event::Force::Calibrated { force: 0.0, max_possible_force: 0.0, altitude_angle: None }
max_possible_force: 0.0, .clone();
altitude_angle: None,
}
.clone();
} }
} }

View File

@@ -3,25 +3,28 @@
//! //!
//! 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. //! [`wake_up`][EventLoopProxy::wake_up] method. Then during handling the wake up
//! you can poll your event sources.
//! //!
//! See the root-level documentation for information on how to create and use an event loop to //! See the root-level documentation for information on how to create and use an event loop to
//! handle events. //! handle events.
use std::any::Any;
use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::{error, fmt};
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time::{Duration, Instant}; use web_time::{Duration, Instant};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, OsError}; use crate::error::{EventLoopError, ExternalError, OsError};
use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes}; use crate::monitor::MonitorHandle;
use crate::{event::Event, monitor::MonitorHandle, platform_impl}; use crate::platform_impl;
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes};
/// Provides a way to retrieve events from the system and from the windows that were registered to /// Provides a way to retrieve events from the system and from the windows that were registered to
/// the events loop. /// the events loop.
@@ -33,22 +36,13 @@ use crate::{event::Event, monitor::MonitorHandle, platform_impl};
/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs. /// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs.
/// ///
/// Note that this cannot be shared across threads (due to platform-dependant logic /// Note that this cannot be shared across threads (due to platform-dependant logic
/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access, the /// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access,
/// [`Window`] created from this _can_ be sent to an other thread, and the /// the [`Window`] created from this _can_ be sent to an other thread, and the
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread. /// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
/// ///
/// [`Window`]: crate::window::Window /// [`Window`]: crate::window::Window
pub struct EventLoop<T: 'static> { pub struct EventLoop {
pub(crate) event_loop: platform_impl::EventLoop<T>, pub(crate) event_loop: platform_impl::EventLoop,
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
}
/// Target that associates windows with an [`EventLoop`].
///
/// This type exists to allow you to create new windows while Winit executes
/// your callback.
pub struct ActiveEventLoop {
pub(crate) p: platform_impl::ActiveEventLoop,
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
} }
@@ -57,25 +51,15 @@ pub struct ActiveEventLoop {
/// This is used to make specifying options that affect the whole application /// This is used to make specifying options that affect the whole application
/// easier. But note that constructing multiple event loops is not supported. /// easier. But note that constructing multiple event loops is not supported.
/// ///
/// This can be created using [`EventLoop::new`] or [`EventLoop::with_user_event`]. /// This can be created using [`EventLoop::builder`].
#[derive(Default)] #[derive(Default, PartialEq, Eq, Hash)]
pub struct EventLoopBuilder<T: 'static> { pub struct EventLoopBuilder {
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes, pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
_p: PhantomData<T>,
} }
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl EventLoopBuilder<()> { impl EventLoopBuilder {
/// Start building a new event loop.
#[inline]
#[deprecated = "use `EventLoop::builder` instead"]
pub fn new() -> Self {
EventLoop::builder()
}
}
impl<T> EventLoopBuilder<T> {
/// Builds a new event loop. /// Builds a new event loop.
/// ///
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread, /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
@@ -94,22 +78,23 @@ impl<T> EventLoopBuilder<T> {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY` /// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY` or
/// or `DISPLAY` respectively when building the event loop. /// `DISPLAY` respectively when building the event loop.
/// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling /// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling
/// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic. /// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic.
/// ///
/// [`platform`]: crate::platform /// [`platform`]: crate::platform
#[cfg_attr( #[cfg_attr(
android, android_platform,
doc = "[`.with_android_app(app)`]: crate::platform::android::EventLoopBuilderExtAndroid::with_android_app" doc = "[`.with_android_app(app)`]: \
crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
)] )]
#[cfg_attr( #[cfg_attr(
not(android), not(android_platform),
doc = "[`.with_android_app(app)`]: #only-available-on-android" doc = "[`.with_android_app(app)`]: #only-available-on-android"
)] )]
#[inline] #[inline]
pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> { pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered(); 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) {
@@ -130,26 +115,27 @@ impl<T> EventLoopBuilder<T> {
} }
} }
impl<T> fmt::Debug for EventLoop<T> { impl fmt::Debug for EventLoopBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EventLoop { .. }") f.debug_struct("EventLoopBuilder").finish_non_exhaustive()
} }
} }
impl fmt::Debug for ActiveEventLoop { impl fmt::Debug for EventLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("ActiveEventLoop { .. }") f.debug_struct("EventLoop").finish_non_exhaustive()
} }
} }
/// Set through [`ActiveEventLoop::set_control_flow()`]. /// Set through [`ActiveEventLoop::set_control_flow()`].
/// ///
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted. /// Indicates the desired behavior of the event loop after [`about_to_wait`] is called.
/// ///
/// Defaults to [`Wait`]. /// Defaults to [`Wait`].
/// ///
/// [`Wait`]: Self::Wait /// [`Wait`]: Self::Wait
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] /// [`about_to_wait`]: crate::application::ApplicationHandler::about_to_wait
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub enum ControlFlow { pub enum ControlFlow {
/// When the current loop iteration finishes, immediately begin a new iteration regardless of /// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process. /// whether or not new events are available to process.
@@ -162,9 +148,9 @@ pub enum ControlFlow {
/// When the current loop iteration finishes, suspend the thread until either another event /// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the given time is reached. /// arrives or the given time is reached.
/// ///
/// Useful for implementing efficient timers. Applications which want to render at the display's /// Useful for implementing efficient timers. Applications which want to render at the
/// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API /// display's native refresh rate should instead use [`Poll`] and the VSync functionality
/// to reduce odds of missed frames. /// of a graphics API to reduce odds of missed frames.
/// ///
/// [`Poll`]: Self::Poll /// [`Poll`]: Self::Poll
WaitUntil(Instant), WaitUntil(Instant),
@@ -186,12 +172,12 @@ impl ControlFlow {
} }
} }
impl EventLoop<()> { impl EventLoop {
/// Create the event loop. /// Create the event loop.
/// ///
/// This is an alias of `EventLoop::builder().build()`. /// This is an alias of `EventLoop::builder().build()`.
#[inline] #[inline]
pub fn new() -> Result<EventLoop<()>, EventLoopError> { pub fn new() -> Result<EventLoop, EventLoopError> {
Self::builder().build() Self::builder().build()
} }
@@ -201,36 +187,12 @@ impl EventLoop<()> {
/// ///
/// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that. /// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that.
#[inline] #[inline]
pub fn builder() -> EventLoopBuilder<()> { pub fn builder() -> EventLoopBuilder {
Self::with_user_event() EventLoopBuilder { platform_specific: Default::default() }
} }
} }
impl<T> EventLoop<T> { impl EventLoop {
/// Start building a new event loop, with the given type as the user event
/// type.
pub fn with_user_event() -> EventLoopBuilder<T> {
EventLoopBuilder {
platform_specific: Default::default(),
_p: PhantomData,
}
}
/// See [`run_app`].
///
/// [`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. /// 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.
@@ -239,48 +201,44 @@ impl<T> EventLoop<T> {
/// ///
/// - **iOS:** Will never return to the caller and so values not passed to this function will /// - **iOS:** Will never return to the caller and so values not passed to this function will
/// *not* be dropped before the process exits. /// *not* be dropped before the process exits.
/// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript exception /// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript
/// (that Rust doesn't see) that will also mean that the rest of the function is never executed /// exception (that Rust doesn't see) that will also mean that the rest of the function is
/// and any values not passed to this function will *not* be dropped. /// never executed and any values not passed to this function will *not* be dropped.
/// ///
/// Web applications are recommended to use /// Web applications are recommended to use
#[cfg_attr( #[cfg_attr(
web_platform, any(web_platform, docsrs),
doc = "[`EventLoopExtWebSys::spawn_app()`][crate::platform::web::EventLoopExtWebSys::spawn_app()]" doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
)] )]
#[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")] #[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")]
/// [^1] instead of [`run_app()`] to avoid the need /// [^1] instead of [`run_app()`] to avoid the need for the Javascript exception trick, and to
/// for the Javascript exception trick, and to make it clearer that the event loop runs /// make it clearer that the event loop runs asynchronously (via the browser's own,
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the /// internal, event loop) and doesn't block the current thread of execution like it does
/// current thread of execution like it does on other platforms. /// on other platforms.
/// ///
/// This function won't be available with `target_feature = "exception-handling"`. /// This function won't be available with `target_feature = "exception-handling"`.
/// ///
/// [^1]: `spawn_app()` is only available on the Web platform.
///
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`run_app()`]: Self::run_app() /// [`run_app()`]: Self::run_app()
/// [^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_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> { pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
self.event_loop self.event_loop.run_app(app)
.run(|event, event_loop| dispatch_event_for_app(app, event_loop, event))
} }
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread. /// to the main event loop, possibly from another thread.
pub fn create_proxy(&self) -> EventLoopProxy<T> { pub fn create_proxy(&self) -> EventLoopProxy {
EventLoopProxy { self.event_loop.window_target().create_proxy()
event_loop_proxy: self.event_loop.create_proxy(),
}
} }
/// Gets a persistent reference to the underlying platform display. /// Gets a persistent reference to the underlying platform display.
/// ///
/// See the [`OwnedDisplayHandle`] type for more information. /// See the [`OwnedDisplayHandle`] type for more information.
pub fn owned_display_handle(&self) -> OwnedDisplayHandle { pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle { self.event_loop.window_target().owned_display_handle()
platform: self.event_loop.window_target().p.owned_display_handle(),
}
} }
/// Change if or when [`DeviceEvent`]s are captured. /// Change if or when [`DeviceEvent`]s are captured.
@@ -294,65 +252,36 @@ impl<T> EventLoop<T> {
allowed = ?allowed allowed = ?allowed
) )
.entered(); .entered();
self.event_loop.window_target().listen_device_events(allowed)
self.event_loop
.window_target()
.p
.listen_device_events(allowed);
} }
/// Sets the [`ControlFlow`]. /// Sets the [`ControlFlow`].
pub fn set_control_flow(&self, control_flow: ControlFlow) { pub fn set_control_flow(&self, control_flow: ControlFlow) {
self.event_loop self.event_loop.window_target().set_control_flow(control_flow);
.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. /// Create custom cursor.
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor { ///
self.event_loop /// ## Platform-specific
.window_target() ///
.p /// **iOS / Android / Orbital:** Unsupported.
.create_custom_cursor(custom_cursor) pub fn create_custom_cursor(
&self,
custom_cursor: CustomCursorSource,
) -> Result<CustomCursor, ExternalError> {
self.event_loop.window_target().create_custom_cursor(custom_cursor)
} }
} }
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
impl<T> rwh_06::HasDisplayHandle for EventLoop<T> { impl rwh_06::HasDisplayHandle for EventLoop {
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.event_loop.window_target()) rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
}
}
#[cfg(feature = "rwh_05")]
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
rwh_05::HasRawDisplayHandle::raw_display_handle(self.event_loop.window_target())
} }
} }
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
impl<T> AsFd for EventLoop<T> { impl AsFd for EventLoop {
/// Get the underlying [EventLoop]'s `fd` which you can register /// Get the underlying [EventLoop]'s `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_app_events`] API. /// loop must be polled with the [`pump_app_events`] API.
@@ -366,7 +295,7 @@ impl<T> AsFd for EventLoop<T> {
} }
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
impl<T> AsRawFd for EventLoop<T> { impl AsRawFd for EventLoop {
/// Get the underlying [EventLoop]'s raw `fd` which you can register /// Get the underlying [EventLoop]'s raw `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_app_events`] API. /// loop must be polled with the [`pump_app_events`] API.
@@ -379,45 +308,42 @@ impl<T> AsRawFd for EventLoop<T> {
} }
} }
impl ActiveEventLoop { pub trait ActiveEventLoop {
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread.
fn create_proxy(&self) -> EventLoopProxy;
/// Create the window. /// Create the window.
/// ///
/// Possible causes of error include denied permission, incompatible system, and lack of memory. /// Possible causes of error include denied permission, incompatible system, and lack of memory.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Web:** The window is created but not inserted into the web page automatically. Please /// - **Web:** The window is created but not inserted into the Web page automatically. Please
/// see the web platform module for more information. /// see the Web platform module for more information.
#[inline] fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError>;
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. /// Create custom cursor.
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor { ///
let _span = tracing::debug_span!("winit::ActiveEventLoop::create_custom_cursor",).entered(); /// ## Platform-specific
///
self.p.create_custom_cursor(custom_cursor) /// **iOS / Android / Orbital:** Unsupported.
} fn create_custom_cursor(
&self,
custom_cursor: CustomCursorSource,
) -> Result<CustomCursor, ExternalError>;
/// Returns the list of all the monitors available on the system. /// Returns the list of all the monitors available on the system.
#[inline] ///
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { /// ## Platform-specific
let _span = tracing::debug_span!("winit::ActiveEventLoop::available_monitors",).entered(); ///
/// **Web:** Only returns the current monitor without
#[allow(clippy::useless_conversion)] // false positive on some platforms #[cfg_attr(
self.p any(web_platform, docsrs),
.available_monitors() doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
.into_iter() )]
.map(|inner| MonitorHandle { inner }) #[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")]
} fn available_monitors(&self) -> Box<dyn Iterator<Item = MonitorHandle>>;
/// Returns the primary monitor of the system. /// Returns the primary monitor of the system.
/// ///
@@ -425,15 +351,14 @@ impl ActiveEventLoop {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// **Wayland / Web:** Always returns `None`. /// - **Wayland:** Always returns `None`.
#[inline] /// - **Web:** Always returns `None` without
pub fn primary_monitor(&self) -> Option<MonitorHandle> { #[cfg_attr(
let _span = tracing::debug_span!("winit::ActiveEventLoop::primary_monitor",).entered(); any(web_platform, docsrs),
doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
self.p )]
.primary_monitor() #[cfg_attr(not(any(web_platform, docsrs)), doc = " detailed monitor permissions.")]
.map(|inner| MonitorHandle { inner }) fn primary_monitor(&self) -> Option<MonitorHandle>;
}
/// Change if or when [`DeviceEvent`]s are captured. /// Change if or when [`DeviceEvent`]s are captured.
/// ///
@@ -446,67 +371,46 @@ impl ActiveEventLoop {
/// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported. /// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
/// ///
/// [`DeviceEvent`]: crate::event::DeviceEvent /// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn listen_device_events(&self, allowed: DeviceEvents) { 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); /// Returns the current system theme.
} ///
/// Returns `None` if it cannot be determined on the current platform.
///
/// ## Platform-specific
///
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
fn system_theme(&self) -> Option<Theme>;
/// Sets the [`ControlFlow`]. /// Sets the [`ControlFlow`].
pub fn set_control_flow(&self, control_flow: ControlFlow) { fn set_control_flow(&self, control_flow: ControlFlow);
self.p.set_control_flow(control_flow)
}
/// Gets the current [`ControlFlow`]. /// Gets the current [`ControlFlow`].
pub fn control_flow(&self) -> ControlFlow { fn control_flow(&self) -> ControlFlow;
self.p.control_flow()
}
/// This exits the event loop. /// This exits the event loop.
/// ///
/// See [`LoopExiting`](Event::LoopExiting). /// See [`exiting`][crate::application::ApplicationHandler::exiting].
pub fn exit(&self) { fn exit(&self);
let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered();
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 { fn exiting(&self) -> bool;
self.p.exiting()
}
/// Gets a persistent reference to the underlying platform display. /// Gets a persistent reference to the underlying platform display.
/// ///
/// See the [`OwnedDisplayHandle`] type for more information. /// See the [`OwnedDisplayHandle`] type for more information.
pub fn owned_display_handle(&self) -> OwnedDisplayHandle { fn owned_display_handle(&self) -> OwnedDisplayHandle;
OwnedDisplayHandle {
platform: self.p.owned_display_handle(),
}
}
}
#[cfg(feature = "rwh_06")] /// Get the [`ActiveEventLoop`] as [`Any`].
impl rwh_06::HasDisplayHandle for ActiveEventLoop { ///
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { /// This is useful for downcasting to a concrete event loop type.
let raw = self.p.raw_display_handle_rwh_06()?; fn as_any(&self) -> &dyn Any;
// SAFETY: The display will never be deallocated while the event loop is alive.
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) })
}
}
#[cfg(feature = "rwh_05")] /// Get the raw-window-handle handle.
unsafe impl rwh_05::HasRawDisplayHandle for ActiveEventLoop { #[cfg(feature = "rwh_06")]
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop. fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.p.raw_display_handle_rwh_05()
}
} }
/// A proxy for the underlying display handle. /// A proxy for the underlying display handle.
@@ -521,10 +425,10 @@ unsafe impl rwh_05::HasRawDisplayHandle for ActiveEventLoop {
/// ///
/// - A zero-sized type that is likely optimized out. /// - A zero-sized type that is likely optimized out.
/// - A reference-counted pointer to the underlying type. /// - A reference-counted pointer to the underlying type.
#[derive(Clone)] #[derive(Clone, PartialEq, Eq)]
pub struct OwnedDisplayHandle { pub struct OwnedDisplayHandle {
#[cfg_attr(not(any(feature = "rwh_05", feature = "rwh_06")), allow(dead_code))] #[cfg_attr(not(feature = "rwh_06"), allow(dead_code))]
platform: platform_impl::OwnedDisplayHandle, pub(crate) platform: platform_impl::OwnedDisplayHandle,
} }
impl fmt::Debug for OwnedDisplayHandle { impl fmt::Debug for OwnedDisplayHandle {
@@ -547,65 +451,42 @@ impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
} }
} }
#[cfg(feature = "rwh_05")] /// Control the [`EventLoop`], possibly from a different thread, without referencing it directly.
unsafe impl rwh_05::HasRawDisplayHandle for OwnedDisplayHandle { #[derive(Clone)]
#[inline] pub struct EventLoopProxy {
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle { pub(crate) event_loop_proxy: platform_impl::EventLoopProxy,
self.platform.raw_display_handle_rwh_05()
}
} }
/// Used to send custom events to [`EventLoop`]. impl EventLoopProxy {
pub struct EventLoopProxy<T: 'static> { /// Wake up the [`EventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being
event_loop_proxy: platform_impl::EventLoopProxy<T>, /// called.
}
impl<T: 'static> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
Self {
event_loop_proxy: self.event_loop_proxy.clone(),
}
}
}
impl<T: 'static> EventLoopProxy<T> {
/// Send an event to the [`EventLoop`] from which this proxy was created. This emits a
/// `UserEvent(event)` event in the event loop, where `event` is the value passed to this
/// function.
/// ///
/// Returns an `Err` if the associated [`EventLoop`] no longer exists. /// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the
/// documentation on that for details.
/// ///
/// [`UserEvent(event)`]: Event::UserEvent /// If the event loop is no longer running, this is a no-op.
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> { ///
let _span = tracing::debug_span!("winit::EventLoopProxy::send_event",).entered(); /// [`proxy_wake_up`]: ApplicationHandler::proxy_wake_up
///
self.event_loop_proxy.send_event(event) /// # Platform-specific
///
/// - **Windows**: The wake-up may be ignored under high contention, see [#3687].
///
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
pub fn wake_up(&self) {
self.event_loop_proxy.wake_up();
} }
} }
impl<T: 'static> fmt::Debug for EventLoopProxy<T> { impl fmt::Debug for EventLoopProxy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EventLoopProxy { .. }") f.debug_struct("ActiveEventLoop").finish_non_exhaustive()
} }
} }
/// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] that
/// no longer exists.
///
/// Contains the original event given to [`EventLoopProxy::send_event`].
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct EventLoopClosed<T>(pub T);
impl<T> fmt::Display for EventLoopClosed<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Tried to wake up a closed `EventLoop`")
}
}
impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {}
/// Control when device events are captured. /// Control when device events are captured.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DeviceEvents { pub enum DeviceEvents {
/// Report device events regardless of window focus. /// Report device events regardless of window focus.
Always, Always,
@@ -625,7 +506,7 @@ pub enum DeviceEvents {
/// containing [`AsyncRequestSerial`] and some closure associated with it. /// containing [`AsyncRequestSerial`] and some closure associated with it.
/// Then once event is arriving the working list is being traversed and a job /// Then once event is arriving the working list is being traversed and a job
/// executed and removed from the list. /// executed and removed from the list.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AsyncRequestSerial { pub struct AsyncRequestSerial {
serial: usize, serial: usize,
} }
@@ -641,23 +522,3 @@ impl AsyncRequestSerial {
Self { serial } Self { serial }
} }
} }
/// Shim for various run APIs.
#[inline(always)]
pub(crate) fn dispatch_event_for_app<T: 'static, A: ApplicationHandler<T>>(
app: &mut A,
event_loop: &ActiveEventLoop,
event: Event<T>,
) {
match event {
Event::NewEvents(cause) => app.new_events(event_loop, cause),
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
Event::UserEvent(event) => app.user_event(event_loop, event),
Event::Suspended => app.suspended(event_loop),
Event::Resumed => app.resumed(event_loop),
Event::AboutToWait => app.about_to_wait(event_loop),
Event::LoopExiting => app.exiting(event_loop),
Event::MemoryWarning => app.memory_warning(event_loop),
}
}

View File

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

View File

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

View File

@@ -19,33 +19,21 @@
//! window or a key getting pressed while the window is focused. Devices can generate //! window or a key getting pressed while the window is focused. Devices can generate
//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window. //! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window.
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a //! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if desired. //! [`DeviceEvent`].
//! //!
//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will //! You can retrieve events by calling [`EventLoop::run_app()`]. This function will
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and //! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
//! will run until [`exit()`] is used, at which point [`Event::LoopExiting`]. //! will run until [`exit()`] is used, at which point [`exiting()`] is called.
//! //!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop //! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on //! model, since that can't be implemented properly on some platforms (e.g Web, iOS) and works
//! most other platforms. However, this model can be re-implemented to an extent with //! poorly on most other platforms. However, this model can be re-implemented to an extent with
#![cfg_attr( #![cfg_attr(
any( any(windows_platform, macos_platform, android_platform, x11_platform, wayland_platform),
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
),
doc = "[`EventLoopExtPumpEvents::pump_app_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_app_events()]" doc = "[`EventLoopExtPumpEvents::pump_app_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_app_events()]"
)] )]
#![cfg_attr( #![cfg_attr(
not(any( not(any(windows_platform, macos_platform, android_platform, x11_platform, wayland_platform)),
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
)),
doc = "`EventLoopExtPumpEvents::pump_app_events()`" doc = "`EventLoopExtPumpEvents::pump_app_events()`"
)] )]
//! [^1]. See that method's documentation for more reasons about why //! [^1]. See that method's documentation for more reasons about why
@@ -64,11 +52,11 @@
//! } //! }
//! //!
//! impl ApplicationHandler for App { //! impl ApplicationHandler for App {
//! fn resumed(&mut self, event_loop: &ActiveEventLoop) { //! fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
//! self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap()); //! self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
//! } //! }
//! //!
//! fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { //! fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, id: WindowId, event: WindowEvent) {
//! match event { //! match event {
//! WindowEvent::CloseRequested => { //! WindowEvent::CloseRequested => {
//! println!("The close button was pressed; stopping"); //! println!("The close button was pressed; stopping");
@@ -116,16 +104,16 @@
//! //!
//! # Drawing on the window //! # Drawing on the window
//! //!
//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However, it allows you to //! Winit doesn't directly provide any methods for drawing on a [`Window`]. However, it allows you
//! retrieve the raw handle of the window and display (see the [`platform`] module and/or the //! 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 //! [`raw_window_handle`] and [`raw_display_handle`] methods), which in turn allows
//! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics. //! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
//! //!
//! Note that many platforms will display garbage data in the window's client area if the //! Note that many platforms will display garbage data in the window's client area if the
//! application doesn't render anything to the window by the time the desktop compositor is ready to //! application doesn't render anything to the window by the time the desktop compositor is ready to
//! display the window to the user. If you notice this happening, you should create the window with //! display the window to the user. If you notice this happening, you should create the window with
//! [`visible` set to `false`](crate::window::WindowAttributes::with_visible) and explicitly make the //! [`visible` set to `false`][crate::window::WindowAttributes::with_visible] and explicitly make
//! window visible only once you're ready to render into it. //! the window visible only once you're ready to render into it.
//! //!
//! # UI scaling //! # UI scaling
//! //!
@@ -151,13 +139,9 @@
//! Winit provides the following Cargo features: //! Winit provides the following Cargo features:
//! //!
//! * `x11` (enabled by default): On Unix platforms, enables the X11 backend. //! * `x11` (enabled by default): On Unix platforms, enables the X11 backend.
//! * `wayland` (enabled by default): On Unix platforms, enables the Wayland //! * `wayland` (enabled by default): On Unix platforms, enables the Wayland backend.
//! backend.
//! * `rwh_04`: Implement `raw-window-handle v0.4` traits.
//! * `rwh_05`: Implement `raw-window-handle v0.5` traits.
//! * `rwh_06`: Implement `raw-window-handle v0.6` traits. //! * `rwh_06`: Implement `raw-window-handle v0.6` traits.
//! * `serde`: Enables serialization/deserialization of certain types with //! * `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde).
//! [Serde](https://crates.io/crates/serde).
//! * `mint`: Enables mint (math interoperability standard types) conversions. //! * `mint`: Enables mint (math interoperability standard types) conversions.
//! //!
//! See the [`platform`] module for documentation on platform-specific cargo //! See the [`platform`] module for documentation on platform-specific cargo
@@ -176,7 +160,7 @@
//! [`WindowEvent`]: event::WindowEvent //! [`WindowEvent`]: event::WindowEvent
//! [`DeviceEvent`]: event::DeviceEvent //! [`DeviceEvent`]: event::DeviceEvent
//! [`Event::UserEvent`]: event::Event::UserEvent //! [`Event::UserEvent`]: event::Event::UserEvent
//! [`Event::LoopExiting`]: event::Event::LoopExiting //! [`exiting()`]: crate::application::ApplicationHandler::exiting
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle //! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle //! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland. //! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland.
@@ -186,22 +170,20 @@
#![deny(clippy::all)] #![deny(clippy::all)]
#![deny(unsafe_op_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)]
#![cfg_attr(clippy, deny(warnings))] #![cfg_attr(clippy, deny(warnings))]
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc // Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly
#![cfg_attr( // doc
docsrs, #![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
feature(doc_auto_cfg, doc_cfg_hide),
doc(cfg_hide(doc, docsrs))
)]
#![allow(clippy::missing_safety_doc)] #![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. // Re-export DPI types so that users don't have to put it in Cargo.toml.
#[doc(inline)] #[doc(inline)]
pub use dpi; pub use dpi;
#[cfg(feature = "rwh_06")]
pub use rwh_06 as raw_window_handle;
pub mod application; pub mod application;
#[cfg(any(doc, doctest, test))]
pub mod changelog;
#[macro_use] #[macro_use]
pub mod error; pub mod error;
mod cursor; mod cursor;

View File

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

View File

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

View File

@@ -66,23 +66,11 @@
use std::os::raw::c_void; use std::os::raw::c_void;
use crate::{ #[cfg(feature = "serde")]
event_loop::EventLoop, use serde::{Deserialize, Serialize};
monitor::{MonitorHandle, VideoModeHandle},
window::{Window, WindowAttributes},
};
/// Additional methods on [`EventLoop`] that are specific to iOS. use crate::monitor::{MonitorHandle, VideoModeHandle};
pub trait EventLoopExtIOS { use crate::window::{Window, WindowAttributes};
/// Returns the [`Idiom`] (phone/tablet/tv/etc) for the current device.
fn idiom(&self) -> Idiom;
}
impl<T: 'static> EventLoopExtIOS for EventLoop<T> {
fn idiom(&self) -> Idiom {
self.event_loop.idiom()
}
}
/// Additional methods on [`Window`] that are specific to iOS. /// Additional methods on [`Window`] that are specific to iOS.
pub trait WindowExtIOS { pub trait WindowExtIOS {
@@ -157,6 +145,21 @@ pub trait WindowExtIOS {
/// The default is to not recognize gestures. /// The default is to not recognize gestures.
fn recognize_pinch_gesture(&self, should_recognize: bool); fn recognize_pinch_gesture(&self, should_recognize: bool);
/// Sets whether the [`Window`] should recognize pan gestures.
///
/// The default is to not recognize gestures.
/// Installs [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer) onto view
///
/// Set the minimum number of touches required: [`minimumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-minimumnumberoftouches)
///
/// Set the maximum number of touches recognized: [`maximumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-maximumnumberoftouches)
fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
);
/// Sets whether the [`Window`] should recognize double tap gestures. /// Sets whether the [`Window`] should recognize double tap gestures.
/// ///
/// The default is to not recognize gestures. /// The default is to not recognize gestures.
@@ -171,20 +174,17 @@ pub trait WindowExtIOS {
impl WindowExtIOS for Window { impl WindowExtIOS for Window {
#[inline] #[inline]
fn set_scale_factor(&self, scale_factor: f64) { fn set_scale_factor(&self, scale_factor: f64) {
self.window self.window.maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor))
.maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor))
} }
#[inline] #[inline]
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) { fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
self.window self.window.maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations))
.maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations))
} }
#[inline] #[inline]
fn set_prefers_home_indicator_hidden(&self, hidden: bool) { fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
self.window self.window.maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden))
.maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden))
} }
#[inline] #[inline]
@@ -196,32 +196,43 @@ impl WindowExtIOS for Window {
#[inline] #[inline]
fn set_prefers_status_bar_hidden(&self, hidden: bool) { fn set_prefers_status_bar_hidden(&self, hidden: bool) {
self.window self.window.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
} }
#[inline] #[inline]
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) { fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
self.window self.window.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
} }
#[inline] #[inline]
fn recognize_pinch_gesture(&self, should_recognize: bool) { fn recognize_pinch_gesture(&self, should_recognize: bool) {
self.window self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize)); }
#[inline]
fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
) {
self.window.maybe_queue_on_main(move |w| {
w.recognize_pan_gesture(
should_recognize,
minimum_number_of_touches,
maximum_number_of_touches,
)
});
} }
#[inline] #[inline]
fn recognize_doubletap_gesture(&self, should_recognize: bool) { fn recognize_doubletap_gesture(&self, should_recognize: bool) {
self.window self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
} }
#[inline] #[inline]
fn recognize_rotation_gesture(&self, should_recognize: bool) { fn recognize_rotation_gesture(&self, should_recognize: bool) {
self.window self.window.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
} }
} }
@@ -301,8 +312,7 @@ impl WindowAttributesExtIOS for WindowAttributes {
#[inline] #[inline]
fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self { fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
self.platform_specific self.platform_specific.preferred_screen_edges_deferring_system_gestures = edges;
.preferred_screen_edges_deferring_system_gestures = edges;
self self
} }
@@ -336,20 +346,19 @@ impl MonitorHandleExtIOS for MonitorHandle {
#[inline] #[inline]
fn ui_screen(&self) -> *mut c_void { fn ui_screen(&self) -> *mut c_void {
// SAFETY: The marker is only used to get the pointer of the screen // SAFETY: The marker is only used to get the pointer of the screen
let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() }; let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
objc2::rc::Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void objc2::rc::Retained::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
} }
#[inline] #[inline]
fn preferred_video_mode(&self) -> VideoModeHandle { fn preferred_video_mode(&self) -> VideoModeHandle {
VideoModeHandle { VideoModeHandle { video_mode: self.inner.preferred_video_mode() }
video_mode: self.inner.preferred_video_mode(),
}
} }
} }
/// Valid orientations for a particular [`Window`]. /// Valid orientations for a particular [`Window`].
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ValidOrientations { pub enum ValidOrientations {
/// Excludes `PortraitUpsideDown` on iphone /// Excludes `PortraitUpsideDown` on iphone
#[default] #[default]
@@ -361,29 +370,12 @@ pub enum ValidOrientations {
Portrait, Portrait,
} }
/// The device [idiom].
///
/// [idiom]: https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom?language=objc
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Idiom {
Unspecified,
/// iPhone and iPod touch.
Phone,
/// iPad.
Pad,
/// tvOS and Apple TV.
TV,
CarPlay,
}
bitflags::bitflags! { bitflags::bitflags! {
/// The [edges] of a screen. /// The [edges] of a screen.
/// ///
/// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc /// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ScreenEdge: u8 { pub struct ScreenEdge: u8 {
const NONE = 0; const NONE = 0;
const TOP = 1 << 0; const TOP = 1 << 0;
@@ -395,7 +387,8 @@ bitflags::bitflags! {
} }
} }
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum StatusBarStyle { pub enum StatusBarStyle {
#[default] #[default]
Default, Default,

View File

@@ -19,11 +19,9 @@ 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::{ActiveEventLoop, EventLoopBuilder};
event_loop::{ActiveEventLoop, EventLoopBuilder}, use crate::monitor::MonitorHandle;
monitor::MonitorHandle, use crate::window::{Window, WindowAttributes};
window::{Window, WindowAttributes},
};
/// Additional methods on [`Window`] that are specific to MacOS. /// Additional methods on [`Window`] that are specific to MacOS.
pub trait WindowExtMacOS { pub trait WindowExtMacOS {
@@ -106,8 +104,7 @@ impl WindowExtMacOS for Window {
#[inline] #[inline]
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool { fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
self.window self.window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
} }
#[inline] #[inline]
@@ -117,14 +114,12 @@ impl WindowExtMacOS for Window {
#[inline] #[inline]
fn set_has_shadow(&self, has_shadow: bool) { fn set_has_shadow(&self, has_shadow: bool) {
self.window self.window.maybe_queue_on_main(move |w| w.set_has_shadow(has_shadow))
.maybe_queue_on_main(move |w| w.set_has_shadow(has_shadow))
} }
#[inline] #[inline]
fn set_tabbing_identifier(&self, identifier: &str) { fn set_tabbing_identifier(&self, identifier: &str) {
self.window self.window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
} }
#[inline] #[inline]
@@ -144,8 +139,7 @@ impl WindowExtMacOS for Window {
#[inline] #[inline]
fn select_tab_at_index(&self, index: usize) { fn select_tab_at_index(&self, index: usize) {
self.window self.window.maybe_queue_on_main(move |w| w.select_tab_at_index(index))
.maybe_queue_on_main(move |w| w.select_tab_at_index(index))
} }
#[inline] #[inline]
@@ -160,14 +154,12 @@ impl WindowExtMacOS for Window {
#[inline] #[inline]
fn set_document_edited(&self, edited: bool) { fn set_document_edited(&self, edited: bool) {
self.window self.window.maybe_queue_on_main(move |w| w.set_document_edited(edited))
.maybe_queue_on_main(move |w| w.set_document_edited(edited))
} }
#[inline] #[inline]
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) { fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
self.window self.window.maybe_queue_on_main(move |w| w.set_option_as_alt(option_as_alt))
.maybe_queue_on_main(move |w| w.set_option_as_alt(option_as_alt))
} }
#[inline] #[inline]
@@ -178,6 +170,7 @@ impl WindowExtMacOS for Window {
/// Corresponds to `NSApplicationActivationPolicy`. /// Corresponds to `NSApplicationActivationPolicy`.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ActivationPolicy { pub enum ActivationPolicy {
/// Corresponds to `NSApplicationActivationPolicyRegular`. /// Corresponds to `NSApplicationActivationPolicyRegular`.
#[default] #[default]
@@ -192,7 +185,8 @@ pub enum ActivationPolicy {
/// Additional methods on [`WindowAttributes`] that are specific to MacOS. /// Additional methods on [`WindowAttributes`] that are specific to MacOS.
/// ///
/// **Note:** Properties dealing with the titlebar will be overwritten by the [`WindowAttributes::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`
@@ -282,9 +276,7 @@ impl WindowAttributesExtMacOS for WindowAttributes {
#[inline] #[inline]
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self { fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
self.platform_specific self.platform_specific.tabbing_identifier.replace(tabbing_identifier.to_string());
.tabbing_identifier
.replace(tabbing_identifier.to_string());
self self
} }
@@ -307,7 +299,7 @@ pub trait EventLoopBuilderExtMacOS {
/// ``` /// ```
/// use winit::event_loop::EventLoopBuilder; /// use winit::event_loop::EventLoopBuilder;
/// #[cfg(target_os = "macos")] /// #[cfg(target_os = "macos")]
/// use winit::platform::macos::{EventLoopBuilderExtMacOS, ActivationPolicy}; /// use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS};
/// ///
/// let mut builder = EventLoopBuilder::new(); /// let mut builder = EventLoopBuilder::new();
/// #[cfg(target_os = "macos")] /// #[cfg(target_os = "macos")]
@@ -347,7 +339,7 @@ pub trait EventLoopBuilderExtMacOS {
fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self; fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self;
} }
impl<T> EventLoopBuilderExtMacOS for EventLoopBuilder<T> { impl EventLoopBuilderExtMacOS for EventLoopBuilder {
#[inline] #[inline]
fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self { fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self {
self.platform_specific.activation_policy = activation_policy; self.platform_specific.activation_policy = activation_policy;
@@ -383,18 +375,18 @@ impl MonitorHandleExtMacOS for MonitorHandle {
fn ns_screen(&self) -> Option<*mut c_void> { fn ns_screen(&self) -> Option<*mut c_void> {
// SAFETY: We only use the marker to get a pointer // SAFETY: We only use the marker to get a pointer
let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() }; let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
self.inner self.inner.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _)
.ns_screen(mtm)
.map(|s| objc2::rc::Id::as_ptr(&s) as _)
} }
} }
/// Additional methods on [`ActiveEventLoop`] that are specific to macOS. /// Additional methods on [`ActiveEventLoop`] that are specific to macOS.
pub trait ActiveEventLoopExtMacOS { pub trait ActiveEventLoopExtMacOS {
/// Hide the entire application. In most applications this is typically triggered with 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.
fn hide_other_applications(&self); fn hide_other_applications(&self);
/// Set whether the system can automatically organize windows into tabs. /// Set whether the system can automatically organize windows into tabs.
/// ///
@@ -404,28 +396,44 @@ pub trait ActiveEventLoopExtMacOS {
fn allows_automatic_window_tabbing(&self) -> bool; fn allows_automatic_window_tabbing(&self) -> bool;
} }
impl ActiveEventLoopExtMacOS for ActiveEventLoop { impl ActiveEventLoopExtMacOS for &dyn ActiveEventLoop {
fn hide_application(&self) { fn hide_application(&self) {
self.p.hide_application() let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.hide_application()
} }
fn hide_other_applications(&self) { fn hide_other_applications(&self) {
self.p.hide_other_applications() let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.hide_other_applications()
} }
fn set_allows_automatic_window_tabbing(&self, enabled: bool) { fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
self.p.set_allows_automatic_window_tabbing(enabled); let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.set_allows_automatic_window_tabbing(enabled);
} }
fn allows_automatic_window_tabbing(&self) -> bool { fn allows_automatic_window_tabbing(&self) -> bool {
self.p.allows_automatic_window_tabbing() let event_loop = self
.as_any()
.downcast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.allows_automatic_window_tabbing()
} }
} }
/// Option as alt behavior. /// Option as alt behavior.
/// ///
/// The default is `None`. /// The default is `None`.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum OptionAsAlt { pub enum OptionAsAlt {
/// The left `Option` key is treated as `Alt`. /// The left `Option` key is treated as `Alt`.

View File

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

View File

@@ -25,10 +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.platform_specific self.platform_specific.text_with_all_modifiers.as_ref().map(|s| s.as_str())
.text_with_all_modifiers
.as_ref()
.map(|s| s.as_str())
} }
#[inline] #[inline]

View File

@@ -1,14 +1,10 @@
use std::time::Duration; use std::time::Duration;
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::event::Event; use crate::event_loop::EventLoop;
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
/// Additional methods on [`EventLoop`] for pumping events within an external event loop /// Additional methods on [`EventLoop`] for pumping events within an external event loop
pub trait EventLoopExtPumpEvents { pub trait EventLoopExtPumpEvents {
/// A type provided by the user that can be passed through [`Event::UserEvent`].
type UserEvent: 'static;
/// Pump the `EventLoop` to check for and dispatch pending events. /// Pump the `EventLoop` to check for and dispatch pending events.
/// ///
/// This API is designed to enable applications to integrate Winit into an /// This API is designed to enable applications to integrate Winit into an
@@ -52,18 +48,18 @@ pub trait EventLoopExtPumpEvents {
/// - `RedrawRequested` events, used to schedule rendering. /// - `RedrawRequested` events, used to schedule rendering.
/// ///
/// macOS for example uses a `drawRect` callback to drive rendering /// macOS for example uses a `drawRect` callback to drive rendering
/// within applications and expects rendering to be finished before /// within applications and expects rendering to be finished before
/// the `drawRect` callback returns. /// the `drawRect` callback returns.
/// ///
/// For portability it's strongly recommended that applications should /// For portability it's strongly recommended that applications should
/// keep their rendering inside the closure provided to Winit. /// keep their rendering inside the closure provided to Winit.
/// - Any lifecycle events, such as `Suspended` / `Resumed`. /// - Any lifecycle events, such as `Suspended` / `Resumed`.
/// ///
/// The handling of these events needs to be synchronized with the /// The handling of these events needs to be synchronized with the
/// operating system and it would never be appropriate to buffer a /// operating system and it would never be appropriate to buffer a
/// notification that your application has been suspended or resumed and /// notification that your application has been suspended or resumed and
/// then handled that later since there would always be a chance that /// then handled that later since there would always be a chance that
/// other lifecycle events occur while the event is buffered. /// other lifecycle events occur while the event is buffered.
/// ///
/// ## Supported Platforms /// ## Supported Platforms
/// ///
@@ -74,22 +70,21 @@ pub trait EventLoopExtPumpEvents {
/// ///
/// ## 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
/// Web browsers work because it's not possible to have a long-running external /// browsers work because it's not possible to have a long-running external loop that would
/// loop that would block the browser and there is nothing that can be /// block the browser and there is nothing that can be polled to ask for new new events.
/// polled to ask for new new events. Events are delivered via callbacks based /// Events are delivered via callbacks based on an event loop that is internal to the browser
/// on an event loop that is internal to the browser itself. /// itself.
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so /// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so
/// there's no way to support the same approach to polling as on MacOS. /// there's no way to support the same approach to polling as on MacOS.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Windows**: The implementation will use `PeekMessage` when checking for /// - **Windows**: The implementation will use `PeekMessage` when checking for window messages
/// window messages to avoid blocking your external event loop. /// to avoid blocking your external event loop.
/// ///
/// - **MacOS**: The implementation works in terms of stopping the global application /// - **MacOS**: The implementation works in terms of stopping the global application whenever
/// whenever the application `RunLoop` indicates that it is preparing to block /// the application `RunLoop` indicates that it is preparing to block and wait for new events.
/// and wait for new events.
/// ///
/// This is very different to the polling APIs that are available on other /// This is very different to the polling APIs that are available on other
/// platforms (the lower level polling primitives on MacOS are private /// platforms (the lower level polling primitives on MacOS are private
@@ -104,38 +99,25 @@ 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>>( fn pump_app_events<A: ApplicationHandler>(
&mut self, &mut self,
timeout: Option<Duration>, timeout: Option<Duration>,
app: &mut A, app: A,
) -> PumpStatus { ) -> 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
where
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
} }
impl<T> EventLoopExtPumpEvents for EventLoop<T> { impl EventLoopExtPumpEvents for EventLoop {
type UserEvent = T; fn pump_app_events<A: ApplicationHandler>(
&mut self,
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus timeout: Option<Duration>,
where app: A,
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop), ) -> PumpStatus {
{ self.event_loop.pump_app_events(timeout, app)
self.event_loop.pump_events(timeout, event_handler)
} }
} }
/// The return status for `pump_events` /// The return status for `pump_events`
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum PumpStatus { pub enum PumpStatus {
/// Continue running external loop. /// Continue running external loop.
Continue, Continue,

View File

@@ -1,28 +1,17 @@
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::EventLoopError; use crate::error::EventLoopError;
use crate::event::Event; use crate::event_loop::EventLoop;
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
#[cfg(doc)] #[cfg(doc)]
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window}; use crate::{
event_loop::ActiveEventLoop, 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`].
type UserEvent: 'static;
/// See [`run_app_on_demand`].
///
/// [`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. /// 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 /// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`)
/// and it is possible to return control back to the caller without /// closures and it is possible to return control back to the caller without
/// consuming the `EventLoop` (by using [`exit()`]) and /// consuming the `EventLoop` (by using [`exit()`]) and
/// so the event loop can be re-run after it has exit. /// so the event loop can be re-run after it has exit.
/// ///
@@ -42,7 +31,13 @@ pub trait EventLoopExtRunOnDemand {
/// # Caveats /// # Caveats
/// - This extension isn't available on all platforms, since it's not always possible to return /// - This extension isn't available on all platforms, since it's not always possible to return
/// to the caller (specifically this is impossible on iOS and Web - though with the Web /// to the caller (specifically this is impossible on iOS and Web - though with the Web
/// backend it is possible to use `EventLoopExtWebSys::spawn()`[^1] more than once instead). /// backend it is possible to use
#[cfg_attr(
any(web_platform, docsrs),
doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
)]
#[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")]
/// [^1] more than once instead).
/// - No [`Window`] state can be carried between separate runs of the event loop. /// - No [`Window`] state can be carried between separate runs of the event loop.
/// ///
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you /// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you
@@ -55,47 +50,22 @@ pub trait EventLoopExtRunOnDemand {
/// - 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
/// Web browsers work because it's not possible to have a long-running external /// browsers work because it's not possible to have a long-running external loop that would
/// loop that would block the browser and there is nothing that can be /// block the browser and there is nothing that can be polled to ask for new events. Events
/// polled to ask for new events. Events are delivered via callbacks based /// are delivered via callbacks based on an event loop that is internal to the browser itself.
/// on an event loop that is internal to the browser itself.
/// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS. /// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS.
/// ///
#[cfg_attr( /// [^1]: `spawn_app()` is only available on the Web platforms.
not(web_platform),
doc = "[^1]: `spawn()` is only available on `wasm` platforms."
)]
/// ///
/// [`exit()`]: ActiveEventLoop::exit() /// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>( fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>;
&mut self,
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 EventLoopExtRunOnDemand for EventLoop {
type UserEvent = T; fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError> {
self.event_loop.run_app_on_demand(app)
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
{
self.event_loop.window_target().clear_exit();
self.event_loop.run_on_demand(event_handler)
}
}
impl ActiveEventLoop {
/// Clear exit status.
pub(crate) fn clear_exit(&self) {
self.p.clear_exit()
} }
} }

View File

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

View File

@@ -25,6 +25,8 @@ use std::env;
use crate::error::NotSupportedError; use crate::error::NotSupportedError;
use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial}; use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial};
#[cfg(wayland_platform)]
use crate::platform::wayland::ActiveEventLoopExtWayland;
use crate::window::{ActivationToken, Window, WindowAttributes}; use crate::window::{ActivationToken, Window, WindowAttributes};
/// The variable which is used mostly on X11. /// The variable which is used mostly on X11.
@@ -55,16 +57,18 @@ pub trait WindowAttributesExtStartupNotify {
fn with_activation_token(self, token: ActivationToken) -> Self; fn with_activation_token(self, token: ActivationToken) -> Self;
} }
impl EventLoopExtStartupNotify for ActiveEventLoop { impl EventLoopExtStartupNotify for &dyn ActiveEventLoop {
fn read_token_from_env(&self) -> Option<ActivationToken> { fn read_token_from_env(&self) -> Option<ActivationToken> {
match self.p { #[cfg(x11_platform)]
#[cfg(wayland_platform)] let _is_wayland = false;
crate::platform_impl::ActiveEventLoop::Wayland(_) => env::var(WAYLAND_VAR), #[cfg(wayland_platform)]
#[cfg(x11_platform)] let _is_wayland = self.is_wayland();
crate::platform_impl::ActiveEventLoop::X(_) => env::var(X11_VAR),
if _is_wayland {
env::var(WAYLAND_VAR).ok().map(ActivationToken::_new)
} else {
env::var(X11_VAR).ok().map(ActivationToken::_new)
} }
.ok()
.map(ActivationToken::_new)
} }
} }

View File

@@ -13,13 +13,11 @@
//! * `wayland-csd-adwaita` (default). //! * `wayland-csd-adwaita` (default).
//! * `wayland-csd-adwaita-crossfont`. //! * `wayland-csd-adwaita-crossfont`.
//! * `wayland-csd-adwaita-notitle`. //! * `wayland-csd-adwaita-notitle`.
use crate::{ use crate::application::ApplicationHandler;
event_loop::{ActiveEventLoop, EventLoopBuilder}, use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
monitor::MonitorHandle, use crate::monitor::MonitorHandle;
window::{Window, WindowAttributes},
};
pub use crate::window::Theme; pub use crate::window::Theme;
use crate::window::{Window, WindowAttributes};
/// Additional methods on [`ActiveEventLoop`] that are specific to Wayland. /// Additional methods on [`ActiveEventLoop`] that are specific to Wayland.
pub trait ActiveEventLoopExtWayland { pub trait ActiveEventLoopExtWayland {
@@ -27,10 +25,45 @@ pub trait ActiveEventLoopExtWayland {
fn is_wayland(&self) -> bool; fn is_wayland(&self) -> bool;
} }
impl ActiveEventLoopExtWayland for ActiveEventLoop { pub trait WaylandApplicationHandler: ApplicationHandler + 'static {
fn wayland_callback(&mut self);
}
impl ActiveEventLoopExtWayland for &dyn ActiveEventLoop {
#[inline] #[inline]
fn is_wayland(&self) -> bool { fn is_wayland(&self) -> bool {
self.p.is_wayland() self.as_any().downcast_ref::<crate::platform_impl::wayland::ActiveEventLoop>().is_some()
}
}
/// Additional methods on [`EventLoop`] that are specific to Wayland.
pub trait EventLoopExtWayland {
/// True if the [`EventLoop`] uses Wayland.
fn is_wayland(&self) -> bool;
fn register_wayland_callback<T: WaylandApplicationHandler>(&mut self);
}
impl EventLoopExtWayland for EventLoop {
#[inline]
fn is_wayland(&self) -> bool {
self.event_loop.is_wayland()
}
fn register_wayland_callback<T: WaylandApplicationHandler>(&mut self) {
let event_loop = match &self.event_loop {
crate::platform_impl::EventLoop::Wayland(event_loop) => &event_loop.active_event_loop,
#[cfg(x11_platform)]
crate::platform_impl::EventLoop::X(_) => return,
};
event_loop.wayland_callback.set(Some(|app: &mut dyn ApplicationHandler| {
app.as_any()
.expect("as_any_mut is not implemented")
.downcast_mut::<T>()
.unwrap()
.wayland_callback()
}));
} }
} }
@@ -46,7 +79,7 @@ pub trait EventLoopBuilderExtWayland {
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self; fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
} }
impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> { impl EventLoopBuilderExtWayland for EventLoopBuilder {
#[inline] #[inline]
fn with_wayland(&mut self) -> &mut Self { fn with_wayland(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland); self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland);
@@ -80,10 +113,8 @@ pub trait WindowAttributesExtWayland {
impl WindowAttributesExtWayland for WindowAttributes { impl WindowAttributesExtWayland for WindowAttributes {
#[inline] #[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self { fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.platform_specific.name = Some(crate::platform_impl::ApplicationName::new( self.platform_specific.name =
general.into(), Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into()));
instance.into(),
));
self self
} }
} }

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,11 @@
#![allow(clippy::unnecessary_cast)] #![allow(clippy::unnecessary_cast)]
use icrate::AppKit::{
NSApplication, NSEvent, NSEventModifierFlagCommand, NSEventTypeKeyUp, NSEventTypeLeftMouseDown,
NSEventTypeLeftMouseDragged, NSEventTypeLeftMouseUp, NSEventTypeMouseMoved,
NSEventTypeOtherMouseDown, NSEventTypeOtherMouseDragged, NSEventTypeOtherMouseUp,
NSEventTypeRightMouseDown, NSEventTypeRightMouseDragged, NSEventTypeRightMouseUp, NSResponder,
};
use icrate::Foundation::{MainThreadMarker, NSObject};
use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass}; use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
use objc2_foundation::{MainThreadMarker, NSObject};
use super::app_delegate::ApplicationDelegate; use super::app_state::ApplicationDelegate;
use super::event::flags_contains; use super::DEVICE_ID;
use crate::event::{DeviceEvent, ElementState}; use crate::event::{DeviceEvent, ElementState};
declare_class!( declare_class!(
@@ -36,8 +31,8 @@ declare_class!(
// but that doesn't really matter here. // but that doesn't really matter here.
let event_type = unsafe { event.r#type() }; let event_type = unsafe { event.r#type() };
let modifier_flags = unsafe { event.modifierFlags() }; let modifier_flags = unsafe { event.modifierFlags() };
if event_type == NSEventTypeKeyUp if event_type == NSEventType::KeyUp
&& flags_contains(modifier_flags, NSEventModifierFlagCommand) && modifier_flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand)
{ {
if let Some(key_window) = self.keyWindow() { if let Some(key_window) = self.keyWindow() {
key_window.sendEvent(event); key_window.sendEvent(event);
@@ -55,45 +50,39 @@ fn maybe_dispatch_device_event(delegate: &ApplicationDelegate, event: &NSEvent)
let event_type = unsafe { event.r#type() }; let event_type = unsafe { event.r#type() };
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
match event_type { match event_type {
NSEventTypeMouseMoved NSEventType::MouseMoved
| NSEventTypeLeftMouseDragged | NSEventType::LeftMouseDragged
| NSEventTypeOtherMouseDragged | NSEventType::OtherMouseDragged
| NSEventTypeRightMouseDragged => { | NSEventType::RightMouseDragged => {
let delta_x = unsafe { event.deltaX() } as f64; let delta_x = unsafe { event.deltaX() } as f64;
let delta_y = unsafe { event.deltaY() } as f64; let delta_y = unsafe { event.deltaY() } as f64;
if delta_x != 0.0 {
delegate.queue_device_event(DeviceEvent::Motion {
axis: 0,
value: delta_x,
});
}
if delta_y != 0.0 {
delegate.queue_device_event(DeviceEvent::Motion {
axis: 1,
value: delta_y,
})
}
if delta_x != 0.0 || delta_y != 0.0 { if delta_x != 0.0 || delta_y != 0.0 {
delegate.queue_device_event(DeviceEvent::MouseMotion { delegate.maybe_queue_with_handler(move |app, event_loop| {
delta: (delta_x, delta_y), app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseMotion {
delta: (delta_x, delta_y),
});
}); });
} }
} },
NSEventTypeLeftMouseDown | NSEventTypeRightMouseDown | NSEventTypeOtherMouseDown => { NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
delegate.queue_device_event(DeviceEvent::Button { let button = unsafe { event.buttonNumber() } as u32;
button: unsafe { event.buttonNumber() } as u32, delegate.maybe_queue_with_handler(move |app, event_loop| {
state: ElementState::Pressed, app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
button,
state: ElementState::Pressed,
});
}); });
} },
NSEventTypeLeftMouseUp | NSEventTypeRightMouseUp | NSEventTypeOtherMouseUp => { NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
delegate.queue_device_event(DeviceEvent::Button { let button = unsafe { event.buttonNumber() } as u32;
button: unsafe { event.buttonNumber() } as u32, delegate.maybe_queue_with_handler(move |app, event_loop| {
state: ElementState::Released, app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button {
button,
state: ElementState::Released,
});
}); });
} },
_ => (), _ => (),
} }
} }

View File

@@ -1,31 +1,31 @@
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
use std::mem; use std::mem;
use std::rc::Weak; use std::rc::Weak;
use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
use std::sync::Arc;
use std::time::Instant; use std::time::Instant;
use icrate::AppKit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate}; use objc2::rc::Retained;
use icrate::Foundation::{MainThreadMarker, NSObject, NSObjectProtocol, NSSize};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
use objc2_foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol};
use super::event_handler::EventHandler; use super::event_handler::EventHandler;
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo}; use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
use super::observer::{EventLoopWaker, RunLoop}; use super::observer::{EventLoopWaker, RunLoop};
use super::window::WinitWindow; use super::{menu, WindowId};
use super::{menu, WindowId, DEVICE_ID}; use crate::application::ApplicationHandler;
use crate::dpi::PhysicalSize; use crate::event::{StartCause, WindowEvent};
use crate::event::{DeviceEvent, Event, InnerSizeWriter, StartCause, WindowEvent}; use crate::event_loop::ControlFlow;
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
use crate::window::WindowId as RootWindowId; use crate::window::WindowId as RootWindowId;
#[derive(Debug, Default)] #[derive(Debug)]
pub(super) struct State { pub(super) struct AppState {
activation_policy: NSApplicationActivationPolicy, activation_policy: NSApplicationActivationPolicy,
default_menu: bool, default_menu: bool,
activate_ignoring_other_apps: bool, activate_ignoring_other_apps: bool,
run_loop: RunLoop,
proxy_wake_up: Arc<AtomicBool>,
event_handler: EventHandler, event_handler: EventHandler,
stop_on_launch: Cell<bool>, stop_on_launch: Cell<bool>,
stop_before_wait: Cell<bool>, stop_before_wait: Cell<bool>,
@@ -41,8 +41,9 @@ pub(super) struct State {
waker: RefCell<EventLoopWaker>, waker: RefCell<EventLoopWaker>,
start_time: Cell<Option<Instant>>, start_time: Cell<Option<Instant>>,
wait_timeout: Cell<Option<Instant>>, wait_timeout: Cell<Option<Instant>>,
pending_events: RefCell<VecDeque<QueuedEvent>>,
pending_redraw: RefCell<Vec<WindowId>>, pending_redraw: RefCell<Vec<WindowId>>,
// NOTE: This is strongly referenced by our `NSWindowDelegate` and our `NSView` subclass, and
// as such should be careful to not add fields that, in turn, strongly reference those.
} }
declare_class!( declare_class!(
@@ -56,62 +57,20 @@ declare_class!(
} }
impl DeclaredClass for ApplicationDelegate { impl DeclaredClass for ApplicationDelegate {
type Ivars = State; type Ivars = AppState;
} }
unsafe impl NSObjectProtocol for ApplicationDelegate {} unsafe impl NSObjectProtocol for ApplicationDelegate {}
unsafe impl NSApplicationDelegate for ApplicationDelegate { unsafe impl NSApplicationDelegate for ApplicationDelegate {
// Note: This will, globally, only be run once, no matter how many
// `EventLoop`s the user creates.
#[method(applicationDidFinishLaunching:)] #[method(applicationDidFinishLaunching:)]
fn did_finish_launching(&self, _sender: Option<&AnyObject>) { fn app_did_finish_launching(&self, notification: &NSNotification) {
trace_scope!("applicationDidFinishLaunching:"); self.did_finish_launching(notification)
self.ivars().is_launched.set(true);
let mtm = MainThreadMarker::from(self);
let app = NSApplication::sharedApplication(mtm);
// We need to delay setting the activation policy and activating the app
// until `applicationDidFinishLaunching` has been called. Otherwise the
// menu bar is initially unresponsive on macOS 10.15.
app.setActivationPolicy(self.ivars().activation_policy);
window_activation_hack(&app);
#[allow(deprecated)]
app.activateIgnoringOtherApps(self.ivars().activate_ignoring_other_apps);
if self.ivars().default_menu {
// The menubar initialization should be before the `NewEvents` event, to allow
// overriding of the default menu even if it's created
menu::initialize(&app);
}
self.ivars().waker.borrow_mut().start();
self.set_is_running(true);
self.dispatch_init_events();
// If the application is being launched via `EventLoop::pump_app_events()` then we'll
// want to stop the app once it is launched (and return to the external loop)
//
// In this case we still want to consider Winit's `EventLoop` to be "running",
// so we call `start_running()` above.
if self.ivars().stop_on_launch.get() {
// Note: the original idea had been to only stop the underlying `RunLoop`
// for the app but that didn't work as expected (`-[NSApplication run]`
// effectively ignored the attempt to stop the RunLoop and re-started it).
//
// So we return from `pump_events` by stopping the application.
let app = NSApplication::sharedApplication(mtm);
stop_app_immediately(&app);
}
} }
#[method(applicationWillTerminate:)] #[method(applicationWillTerminate:)]
fn will_terminate(&self, _sender: Option<&AnyObject>) { fn app_will_terminate(&self, notification: &NSNotification) {
trace_scope!("applicationWillTerminate:"); self.will_terminate(notification)
// TODO: Notify every window that it will be destroyed, like done in iOS?
self.internal_exit();
} }
} }
); );
@@ -122,23 +81,87 @@ impl ApplicationDelegate {
activation_policy: NSApplicationActivationPolicy, activation_policy: NSApplicationActivationPolicy,
default_menu: bool, default_menu: bool,
activate_ignoring_other_apps: bool, activate_ignoring_other_apps: bool,
) -> Id<Self> { ) -> Retained<Self> {
let this = mtm.alloc().set_ivars(State { let this = mtm.alloc().set_ivars(AppState {
activation_policy, activation_policy,
proxy_wake_up: Arc::new(AtomicBool::new(false)),
default_menu, default_menu,
activate_ignoring_other_apps, activate_ignoring_other_apps,
..Default::default() run_loop: RunLoop::main(mtm),
event_handler: EventHandler::new(),
stop_on_launch: Cell::new(false),
stop_before_wait: Cell::new(false),
stop_after_wait: Cell::new(false),
stop_on_redraw: Cell::new(false),
is_launched: Cell::new(false),
is_running: Cell::new(false),
exit: Cell::new(false),
control_flow: Cell::new(ControlFlow::default()),
waker: RefCell::new(EventLoopWaker::new()),
start_time: Cell::new(None),
wait_timeout: Cell::new(None),
pending_redraw: RefCell::new(vec![]),
}); });
unsafe { msg_send_id![super(this), init] } unsafe { msg_send_id![super(this), init] }
} }
pub fn get(mtm: MainThreadMarker) -> Id<Self> { // NOTE: This will, globally, only be run once, no matter how many
// `EventLoop`s the user creates.
fn did_finish_launching(&self, _notification: &NSNotification) {
trace_scope!("applicationDidFinishLaunching:");
self.ivars().is_launched.set(true);
let mtm = MainThreadMarker::from(self);
let app = NSApplication::sharedApplication(mtm);
// We need to delay setting the activation policy and activating the app
// until `applicationDidFinishLaunching` has been called. Otherwise the
// menu bar is initially unresponsive on macOS 10.15.
app.setActivationPolicy(self.ivars().activation_policy);
window_activation_hack(&app);
#[allow(deprecated)]
app.activateIgnoringOtherApps(self.ivars().activate_ignoring_other_apps);
if self.ivars().default_menu {
// The menubar initialization should be before the `NewEvents` event, to allow
// overriding of the default menu even if it's created
menu::initialize(&app);
}
self.ivars().waker.borrow_mut().start();
self.set_is_running(true);
self.dispatch_init_events();
// If the application is being launched via `EventLoop::pump_app_events()` then we'll
// want to stop the app once it is launched (and return to the external loop)
//
// In this case we still want to consider Winit's `EventLoop` to be "running",
// so we call `start_running()` above.
if self.ivars().stop_on_launch.get() {
// NOTE: the original idea had been to only stop the underlying `RunLoop`
// for the app but that didn't work as expected (`-[NSApplication run]`
// effectively ignored the attempt to stop the RunLoop and re-started it).
//
// So we return from `pump_events` by stopping the application.
let app = NSApplication::sharedApplication(mtm);
stop_app_immediately(&app);
}
}
fn will_terminate(&self, _notification: &NSNotification) {
trace_scope!("applicationWillTerminate:");
// TODO: Notify every window that it will be destroyed, like done in iOS?
self.internal_exit();
}
pub fn get(mtm: MainThreadMarker) -> Retained<Self> {
let app = NSApplication::sharedApplication(mtm); let app = NSApplication::sharedApplication(mtm);
let delegate = let delegate =
unsafe { app.delegate() }.expect("a delegate was not configured on the application"); unsafe { app.delegate() }.expect("a delegate was not configured on the application");
if delegate.is_kind_of::<Self>() { if delegate.is_kind_of::<Self>() {
// SAFETY: Just checked that the delegate is an instance of `ApplicationDelegate` // SAFETY: Just checked that the delegate is an instance of `ApplicationDelegate`
unsafe { Id::cast(delegate) } unsafe { Retained::cast(delegate) }
} else { } else {
panic!("tried to get a delegate that was not the one Winit has registered") panic!("tried to get a delegate that was not the one Winit has registered")
} }
@@ -148,12 +171,16 @@ impl ApplicationDelegate {
/// of the given closure. /// of the given closure.
pub fn set_event_handler<R>( pub fn set_event_handler<R>(
&self, &self,
handler: impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop), handler: &mut dyn ApplicationHandler,
closure: impl FnOnce() -> R, closure: impl FnOnce() -> R,
) -> R { ) -> R {
self.ivars().event_handler.set(handler, closure) self.ivars().event_handler.set(handler, closure)
} }
pub fn proxy_wake_up(&self) -> Arc<AtomicBool> {
self.ivars().proxy_wake_up.clone()
}
/// If `pump_events` is called to progress the event loop then we /// If `pump_events` is called to progress the event loop then we
/// bootstrap the event loop via `-[NSApplication run]` but will use /// bootstrap the event loop via `-[NSApplication run]` but will use
/// `CFRunLoopRunInMode` for subsequent calls to `pump_events`. /// `CFRunLoopRunInMode` for subsequent calls to `pump_events`.
@@ -179,10 +206,12 @@ impl ApplicationDelegate {
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits. /// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits.
/// ///
/// Note: that if the `NSApplication` has been launched then that state is preserved, /// NOTE: that if the `NSApplication` has been launched then that state is preserved,
/// and we won't need to re-launch the app if subsequent EventLoops are run. /// and we won't need to re-launch the app if subsequent EventLoops are run.
pub fn internal_exit(&self) { pub fn internal_exit(&self) {
self.handle_event(Event::LoopExiting); self.with_handler(|app, event_loop| {
app.exiting(event_loop);
});
self.set_is_running(false); self.set_is_running(false);
self.set_stop_on_redraw(false); self.set_stop_on_redraw(false);
@@ -223,48 +252,18 @@ impl ApplicationDelegate {
self.ivars().control_flow.get() self.ivars().control_flow.get()
} }
pub fn queue_window_event(&self, window_id: WindowId, event: WindowEvent) {
self.ivars()
.pending_events
.borrow_mut()
.push_back(QueuedEvent::WindowEvent(window_id, event));
}
pub fn queue_device_event(&self, event: DeviceEvent) {
self.ivars()
.pending_events
.borrow_mut()
.push_back(QueuedEvent::DeviceEvent(event));
}
pub fn queue_static_scale_factor_changed_event(
&self,
window: Id<WinitWindow>,
suggested_size: PhysicalSize<u32>,
scale_factor: f64,
) {
self.ivars()
.pending_events
.borrow_mut()
.push_back(QueuedEvent::ScaleFactorChanged {
window,
suggested_size,
scale_factor,
});
}
pub fn handle_redraw(&self, window_id: WindowId) { pub fn handle_redraw(&self, window_id: WindowId) {
let mtm = MainThreadMarker::from(self); let mtm = MainThreadMarker::from(self);
// Redraw request might come out of order from the OS. // Redraw request might come out of order from the OS.
// -> Don't go back into the event handler when our callstack originates from there // -> Don't go back into the event handler when our callstack originates from there
if !self.ivars().event_handler.in_use() { if !self.ivars().event_handler.in_use() {
self.handle_event(Event::WindowEvent { self.with_handler(|app, event_loop| {
window_id: RootWindowId(window_id), app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
event: WindowEvent::RedrawRequested,
}); });
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested events // `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
// as a way to ensure that `pump_events` can't block an external loop indefinitely // events as a way to ensure that `pump_events` can't block an external loop
// indefinitely
if self.ivars().stop_on_redraw.get() { if self.ivars().stop_on_redraw.get() {
let app = NSApplication::sharedApplication(mtm); let app = NSApplication::sharedApplication(mtm);
stop_app_immediately(&app); stop_app_immediately(&app);
@@ -277,21 +276,44 @@ impl ApplicationDelegate {
if !pending_redraw.contains(&window_id) { if !pending_redraw.contains(&window_id) {
pending_redraw.push(window_id); pending_redraw.push(window_id);
} }
unsafe { RunLoop::get() }.wakeup(); self.ivars().run_loop.wakeup();
} }
fn handle_event(&self, event: Event<HandlePendingUserEvents>) { #[track_caller]
self.ivars() pub fn maybe_queue_with_handler(
.event_handler &self,
.handle_event(event, &ActiveEventLoop::new_root(self.retain())) callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop) + 'static,
) {
// Most programmer actions in AppKit (e.g. change window fullscreen, set focused, etc.)
// result in an event being queued, and applied at a later point.
//
// However, it is not documented which actions do this, and which ones are done immediately,
// so to make sure that we don't encounter re-entrancy issues, we first check if we're
// currently handling another event, and if we are, we queue the event instead.
if !self.ivars().event_handler.in_use() {
self.with_handler(callback);
} else {
tracing::debug!("had to queue event since another is currently being handled");
let this = self.retain();
self.ivars().run_loop.queue_closure(move || {
this.with_handler(callback);
});
}
}
#[track_caller]
fn with_handler(&self, callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop)) {
let event_loop =
ActiveEventLoop { delegate: self.retain(), mtm: MainThreadMarker::from(self) };
self.ivars().event_handler.handle(callback, &event_loop);
} }
/// dispatch `NewEvents(Init)` + `Resumed` /// dispatch `NewEvents(Init)` + `Resumed`
pub fn dispatch_init_events(&self) { pub fn dispatch_init_events(&self) {
self.handle_event(Event::NewEvents(StartCause::Init)); self.with_handler(|app, event_loop| app.new_events(event_loop, StartCause::Init));
// NB: For consistency all platforms must emit a 'resumed' event even though macOS // NB: For consistency all platforms must call `can_create_surfaces` even though macOS
// applications don't themselves have a formal suspend/resume lifecycle. // applications don't themselves have a formal surface destroy/create lifecycle.
self.handle_event(Event::Resumed); self.with_handler(|app, event_loop| app.can_create_surfaces(event_loop));
} }
// Called by RunLoopObserver after finishing waiting for new events // Called by RunLoopObserver after finishing waiting for new events
@@ -314,26 +336,17 @@ impl ApplicationDelegate {
let start = self.ivars().start_time.get().unwrap(); let start = self.ivars().start_time.get().unwrap();
let cause = match self.control_flow() { let cause = match self.control_flow() {
ControlFlow::Poll => StartCause::Poll, ControlFlow::Poll => StartCause::Poll,
ControlFlow::Wait => StartCause::WaitCancelled { ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
start,
requested_resume: None,
},
ControlFlow::WaitUntil(requested_resume) => { ControlFlow::WaitUntil(requested_resume) => {
if Instant::now() >= requested_resume { if Instant::now() >= requested_resume {
StartCause::ResumeTimeReached { StartCause::ResumeTimeReached { start, requested_resume }
start,
requested_resume,
}
} else { } else {
StartCause::WaitCancelled { StartCause::WaitCancelled { start, requested_resume: Some(requested_resume) }
start,
requested_resume: Some(requested_resume),
}
} }
} },
}; };
self.handle_event(Event::NewEvents(cause)); self.with_handler(|app, event_loop| app.new_events(event_loop, cause));
} }
// Called by RunLoopObserver before waiting for new events // Called by RunLoopObserver before waiting for new events
@@ -350,65 +363,19 @@ impl ApplicationDelegate {
return; return;
} }
self.handle_event(Event::UserEvent(HandlePendingUserEvents)); if self.ivars().proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
let events = mem::take(&mut *self.ivars().pending_events.borrow_mut());
for event in events {
match event {
QueuedEvent::WindowEvent(window_id, event) => {
self.handle_event(Event::WindowEvent {
window_id: RootWindowId(window_id),
event,
});
}
QueuedEvent::DeviceEvent(event) => {
self.handle_event(Event::DeviceEvent {
device_id: DEVICE_ID,
event,
});
}
QueuedEvent::ScaleFactorChanged {
window,
suggested_size,
scale_factor,
} => {
let new_inner_size = Arc::new(Mutex::new(suggested_size));
let scale_factor_changed_event = Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::ScaleFactorChanged {
scale_factor,
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
&new_inner_size,
)),
},
};
self.handle_event(scale_factor_changed_event);
let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size);
let logical_size = physical_size.to_logical(scale_factor);
let size = NSSize::new(logical_size.width, logical_size.height);
window.setContentSize(size);
let resized_event = Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::Resized(physical_size),
};
self.handle_event(resized_event);
}
}
} }
let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut()); let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut());
for window_id in redraw { for window_id in redraw {
self.handle_event(Event::WindowEvent { self.with_handler(|app, event_loop| {
window_id: RootWindowId(window_id), app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
event: WindowEvent::RedrawRequested,
}); });
} }
self.with_handler(|app, event_loop| {
self.handle_event(Event::AboutToWait); app.about_to_wait(event_loop);
});
if self.exiting() { if self.exiting() {
let app = NSApplication::sharedApplication(mtm); let app = NSApplication::sharedApplication(mtm);
@@ -426,34 +393,15 @@ impl ApplicationDelegate {
ControlFlow::Poll => Some(Instant::now()), ControlFlow::Poll => Some(Instant::now()),
ControlFlow::WaitUntil(instant) => Some(instant), ControlFlow::WaitUntil(instant) => Some(instant),
}; };
self.ivars() self.ivars().waker.borrow_mut().start_at(min_timeout(wait_timeout, app_timeout));
.waker
.borrow_mut()
.start_at(min_timeout(wait_timeout, app_timeout));
} }
} }
#[derive(Debug)]
pub(crate) enum QueuedEvent {
WindowEvent(WindowId, WindowEvent),
DeviceEvent(DeviceEvent),
ScaleFactorChanged {
window: Id<WinitWindow>,
suggested_size: PhysicalSize<u32>,
scale_factor: f64,
},
}
#[derive(Debug)]
pub(crate) struct HandlePendingUserEvents;
/// Returns the minimum `Option<Instant>`, taking into account that `None` /// Returns the minimum `Option<Instant>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use /// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`) /// `Option::min`)
fn min_timeout(a: Option<Instant>, b: Option<Instant>) -> Option<Instant> { fn min_timeout(a: Option<Instant>, b: Option<Instant>) -> Option<Instant> {
a.map_or(b, |a_timeout| { a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout)))
})
} }
/// A hack to make activation of multiple windows work when creating them before /// A hack to make activation of multiple windows work when creating them before

View File

@@ -2,34 +2,35 @@ use std::ffi::c_uchar;
use std::slice; use std::slice;
use std::sync::OnceLock; use std::sync::OnceLock;
use icrate::AppKit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage}; use objc2::rc::Retained;
use icrate::Foundation::{ use objc2::runtime::Sel;
use objc2::{msg_send_id, sel, ClassType};
use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
use objc2_foundation::{
ns_string, NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSSize, ns_string, NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSSize,
NSString, NSString,
}; };
use objc2::rc::Id;
use objc2::runtime::Sel;
use objc2::{msg_send_id, sel, ClassType};
use crate::cursor::CursorImage; use super::OsError;
use crate::cursor::OnlyCursorImageSource; use crate::cursor::{CursorImage, OnlyCursorImageSource};
use crate::error::ExternalError;
use crate::window::CursorIcon; use crate::window::CursorIcon;
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CustomCursor(pub(crate) Id<NSCursor>); pub struct CustomCursor(pub(crate) Retained<NSCursor>);
// SAFETY: NSCursor is immutable and thread-safe // SAFETY: NSCursor is immutable and thread-safe
// TODO(madsmtm): Put this logic in icrate itself // TODO(madsmtm): Put this logic in objc2-app-kit itself
unsafe impl Send for CustomCursor {} unsafe impl Send for CustomCursor {}
unsafe impl Sync for CustomCursor {} unsafe impl Sync for CustomCursor {}
impl CustomCursor { impl CustomCursor {
pub(crate) fn new(cursor: OnlyCursorImageSource) -> CustomCursor { pub(crate) fn new(cursor: OnlyCursorImageSource) -> Result<CustomCursor, ExternalError> {
Self(cursor_from_image(&cursor.0)) cursor_from_image(&cursor.0).map(Self)
} }
} }
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Id<NSCursor> { pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result<Retained<NSCursor>, ExternalError> {
let width = cursor.width; let width = cursor.width;
let height = cursor.height; let height = cursor.height;
@@ -46,8 +47,8 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Id<NSCursor> {
NSDeviceRGBColorSpace, NSDeviceRGBColorSpace,
width as isize * 4, width as isize * 4,
32, 32,
).unwrap() )
}; }.ok_or_else(|| ExternalError::Os(os_error!(OsError::CreationError("parent view should be installed in a window"))))?;
let bitmap_data = unsafe { slice::from_raw_parts_mut(bitmap.bitmapData(), cursor.rgba.len()) }; let bitmap_data = unsafe { slice::from_raw_parts_mut(bitmap.bitmapData(), cursor.rgba.len()) };
bitmap_data.copy_from_slice(&cursor.rgba); bitmap_data.copy_from_slice(&cursor.rgba);
@@ -58,17 +59,17 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Id<NSCursor> {
let hotspot = NSPoint::new(cursor.hotspot_x as f64, cursor.hotspot_y as f64); let hotspot = NSPoint::new(cursor.hotspot_x as f64, cursor.hotspot_y as f64);
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot) Ok(NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot))
} }
pub(crate) fn default_cursor() -> Id<NSCursor> { pub(crate) fn default_cursor() -> Retained<NSCursor> {
NSCursor::arrowCursor() NSCursor::arrowCursor()
} }
unsafe fn try_cursor_from_selector(sel: Sel) -> Option<Id<NSCursor>> { unsafe fn try_cursor_from_selector(sel: Sel) -> Option<Retained<NSCursor>> {
let cls = NSCursor::class(); let cls = NSCursor::class();
if cls.responds_to(sel) { if cls.responds_to(sel) {
let cursor: Id<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] }; let cursor: Retained<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] };
Some(cursor) Some(cursor)
} else { } else {
tracing::warn!("cursor `{sel}` appears to be invalid"); tracing::warn!("cursor `{sel}` appears to be invalid");
@@ -83,7 +84,7 @@ macro_rules! def_undocumented_cursor {
)*} => {$( )*} => {$(
$(#[$($m)*])* $(#[$($m)*])*
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn $name() -> Id<NSCursor> { fn $name() -> Retained<NSCursor> {
unsafe { try_cursor_from_selector(sel!($name)).unwrap_or_else(|| default_cursor()) } unsafe { try_cursor_from_selector(sel!($name)).unwrap_or_else(|| default_cursor()) }
} }
)*}; )*};
@@ -113,12 +114,15 @@ def_undocumented_cursor!(
// Note that loading `busybutclickable` with this code won't animate // Note that loading `busybutclickable` with this code won't animate
// the frames; instead you'll just get them all in a column. // the frames; instead you'll just get them all in a column.
unsafe fn load_webkit_cursor(name: &NSString) -> Id<NSCursor> { unsafe fn load_webkit_cursor(name: &NSString) -> Retained<NSCursor> {
// Snatch a cursor from WebKit; They fit the style of the native // Snatch a cursor from WebKit; They fit the style of the native
// cursors, and will seem completely standard to macOS users. // cursors, and will seem completely standard to macOS users.
// //
// https://stackoverflow.com/a/21786835/5435443 // https://stackoverflow.com/a/21786835/5435443
let root = ns_string!("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors"); let root = ns_string!(
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/\
HIServices.framework/Versions/A/Resources/cursors"
);
let cursor_path = root.stringByAppendingPathComponent(name); let cursor_path = root.stringByAppendingPathComponent(name);
let pdf_path = cursor_path.stringByAppendingPathComponent(ns_string!("cursor.pdf")); let pdf_path = cursor_path.stringByAppendingPathComponent(ns_string!("cursor.pdf"));
@@ -126,7 +130,7 @@ unsafe fn load_webkit_cursor(name: &NSString) -> Id<NSCursor> {
// TODO: Handle PLists better // TODO: Handle PLists better
let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist")); let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist"));
let info: Id<NSDictionary<NSObject, NSObject>> = unsafe { let info: Retained<NSDictionary<NSObject, NSObject>> = unsafe {
msg_send_id![ msg_send_id![
<NSDictionary<NSObject, NSObject>>::class(), <NSDictionary<NSObject, NSObject>>::class(),
dictionaryWithContentsOfFile: &*info_path, dictionaryWithContentsOfFile: &*info_path,
@@ -153,26 +157,26 @@ unsafe fn load_webkit_cursor(name: &NSString) -> Id<NSCursor> {
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot) NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
} }
fn webkit_move() -> Id<NSCursor> { fn webkit_move() -> Retained<NSCursor> {
unsafe { load_webkit_cursor(ns_string!("move")) } unsafe { load_webkit_cursor(ns_string!("move")) }
} }
fn webkit_cell() -> Id<NSCursor> { fn webkit_cell() -> Retained<NSCursor> {
unsafe { load_webkit_cursor(ns_string!("cell")) } unsafe { load_webkit_cursor(ns_string!("cell")) }
} }
pub(crate) fn invisible_cursor() -> Id<NSCursor> { pub(crate) fn invisible_cursor() -> Retained<NSCursor> {
// 16x16 GIF data for invisible cursor // 16x16 GIF data for invisible cursor
// You can reproduce this via ImageMagick. // You can reproduce this via ImageMagick.
// $ convert -size 16x16 xc:none cursor.gif // $ convert -size 16x16 xc:none cursor.gif
static CURSOR_BYTES: &[u8] = &[ static CURSOR_BYTES: &[u8] = &[
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x84, 0x8F, 0xA9, 0xCB, 0xED, 0x0F, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0e, 0x84, 0x8f, 0xa9, 0xcb, 0xed, 0x0f,
0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B, 0xa3, 0x9c, 0xb4, 0xda, 0x8b, 0xb3, 0x3e, 0x05, 0x00, 0x3b,
]; ];
fn new_invisible() -> Id<NSCursor> { fn new_invisible() -> Retained<NSCursor> {
// TODO: Consider using `dataWithBytesNoCopy:` // TODO: Consider using `dataWithBytesNoCopy:`
let data = NSData::with_bytes(CURSOR_BYTES); let data = NSData::with_bytes(CURSOR_BYTES);
let image = NSImage::initWithData(NSImage::alloc(), &data).unwrap(); let image = NSImage::initWithData(NSImage::alloc(), &data).unwrap();
@@ -182,13 +186,10 @@ pub(crate) fn invisible_cursor() -> Id<NSCursor> {
// Cache this for efficiency // Cache this for efficiency
static CURSOR: OnceLock<CustomCursor> = OnceLock::new(); static CURSOR: OnceLock<CustomCursor> = OnceLock::new();
CURSOR CURSOR.get_or_init(|| CustomCursor(new_invisible())).0.clone()
.get_or_init(|| CustomCursor(new_invisible()))
.0
.clone()
} }
pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Id<NSCursor> { pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Retained<NSCursor> {
match icon { match icon {
CursorIcon::Default => default_cursor(), CursorIcon::Default => default_cursor(),
CursorIcon::Pointer => NSCursor::pointingHandCursor(), CursorIcon::Pointer => NSCursor::pointingHandCursor(),

View File

@@ -1,25 +1,17 @@
use std::ffi::c_void; use std::ffi::c_void;
use core_foundation::{ use core_foundation::base::CFRelease;
base::CFRelease, use core_foundation::data::{CFDataGetBytePtr, CFDataRef};
data::{CFDataGetBytePtr, CFDataRef}, use objc2::rc::Retained;
}; use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSEventType};
use icrate::AppKit::{ use objc2_foundation::{run_on_main, NSPoint};
NSEvent, NSEventModifierFlagCommand, NSEventModifierFlagControl, NSEventModifierFlagOption,
NSEventModifierFlagShift, NSEventModifierFlags, NSEventSubtypeWindowExposed,
NSEventTypeApplicationDefined,
};
use icrate::Foundation::{MainThreadMarker, NSPoint};
use objc2::rc::Id;
use smol_str::SmolStr; use smol_str::SmolStr;
use crate::{ use super::ffi;
event::{ElementState, KeyEvent, Modifiers}, use crate::event::{ElementState, KeyEvent, Modifiers};
keyboard::{ use crate::keyboard::{
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
NativeKeyCode, PhysicalKey, PhysicalKey,
},
platform_impl::platform::ffi,
}; };
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -48,7 +40,7 @@ pub fn get_modifierless_char(scancode: u16) -> Key {
} }
layout = CFDataGetBytePtr(layout_data as CFDataRef) as *const ffi::UCKeyboardLayout; layout = CFDataGetBytePtr(layout_data as CFDataRef) as *const ffi::UCKeyboardLayout;
} }
let keyboard_type = MainThreadMarker::run_on_main(|_mtm| unsafe { ffi::LMGetKbdType() }); let keyboard_type = run_on_main(|_mtm| unsafe { ffi::LMGetKbdType() });
let mut result_len = 0; let mut result_len = 0;
let mut dead_keys = 0; let mut dead_keys = 0;
@@ -71,10 +63,7 @@ pub fn get_modifierless_char(scancode: u16) -> Key {
CFRelease(input_source as *mut c_void); CFRelease(input_source as *mut c_void);
} }
if translate_result != 0 { if translate_result != 0 {
tracing::error!( tracing::error!("`UCKeyTranslate` returned with the non-zero value: {}", translate_result);
"`UCKeyTranslate` returned with the non-zero value: {}",
translate_result
);
return Key::Unidentified(NativeKey::MacOS(scancode)); return Key::Unidentified(NativeKey::MacOS(scancode));
} }
if result_len == 0 { if result_len == 0 {
@@ -125,9 +114,8 @@ pub(crate) fn create_key_event(
let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() { let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() {
None None
} else { } else {
let characters = unsafe { ns_event.characters() } let characters =
.map(|s| s.to_string()) unsafe { ns_event.characters() }.map(|s| s.to_string()).unwrap_or_default();
.unwrap_or_default();
if characters.is_empty() { if characters.is_empty() {
None None
} else { } else {
@@ -145,8 +133,8 @@ pub(crate) fn create_key_event(
let key_without_modifiers = get_modifierless_char(scancode); let key_without_modifiers = get_modifierless_char(scancode);
let modifiers = unsafe { ns_event.modifierFlags() }; let modifiers = unsafe { ns_event.modifierFlags() };
let has_ctrl = flags_contains(modifiers, NSEventModifierFlagControl); let has_ctrl = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagControl);
let has_cmd = flags_contains(modifiers, NSEventModifierFlagCommand); let has_cmd = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagCommand);
let logical_key = match text_with_all_modifiers.as_ref() { let logical_key = match text_with_all_modifiers.as_ref() {
// Only checking for ctrl and cmd here, not checking for alt because we DO want to // Only checking for ctrl and cmd here, not checking for alt because we DO want to
@@ -157,7 +145,7 @@ pub(crate) fn create_key_event(
Some(text) if !has_ctrl && !has_cmd => { Some(text) if !has_ctrl && !has_cmd => {
// Character heeding both SHIFT and ALT. // Character heeding both SHIFT and ALT.
Key::Character(text.clone()) Key::Character(text.clone())
} },
_ => match key_without_modifiers.as_ref() { _ => match key_without_modifiers.as_ref() {
// Character heeding just SHIFT, ignoring ALT. // Character heeding just SHIFT, ignoring ALT.
@@ -173,11 +161,7 @@ pub(crate) fn create_key_event(
(key_from_code.clone(), key_from_code) (key_from_code.clone(), key_from_code)
}; };
let text = if is_press { let text = if is_press { logical_key.to_text().map(SmolStr::new) } else { None };
logical_key.to_text().map(SmolStr::new)
} else {
None
};
let location = code_to_location(physical_key); let location = code_to_location(physical_key);
@@ -188,10 +172,7 @@ pub(crate) fn create_key_event(
repeat: is_repeat, repeat: is_repeat,
state, state,
text, text,
platform_specific: KeyEventExtra { platform_specific: KeyEventExtra { text_with_all_modifiers, key_without_modifiers },
text_with_all_modifiers,
key_without_modifiers,
},
} }
} }
@@ -315,25 +296,21 @@ pub fn extra_function_key_to_code(scancode: u16, string: &str) -> PhysicalKey {
} }
// The values are from the https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h#L258-L259 // The values are from the https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h#L258-L259
const NX_DEVICELCTLKEYMASK: NSEventModifierFlags = 0x00000001; const NX_DEVICELCTLKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000001);
const NX_DEVICELSHIFTKEYMASK: NSEventModifierFlags = 0x00000002; const NX_DEVICELSHIFTKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000002);
const NX_DEVICERSHIFTKEYMASK: NSEventModifierFlags = 0x00000004; const NX_DEVICERSHIFTKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000004);
const NX_DEVICELCMDKEYMASK: NSEventModifierFlags = 0x00000008; const NX_DEVICELCMDKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000008);
const NX_DEVICERCMDKEYMASK: NSEventModifierFlags = 0x00000010; const NX_DEVICERCMDKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000010);
const NX_DEVICELALTKEYMASK: NSEventModifierFlags = 0x00000020; const NX_DEVICELALTKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000020);
const NX_DEVICERALTKEYMASK: NSEventModifierFlags = 0x00000040; const NX_DEVICERALTKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000040);
const NX_DEVICERCTLKEYMASK: NSEventModifierFlags = 0x00002000; const NX_DEVICERCTLKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00002000);
pub(super) fn flags_contains(flags: NSEventModifierFlags, value: NSEventModifierFlags) -> bool {
flags & value == value
}
pub(super) fn lalt_pressed(event: &NSEvent) -> bool { pub(super) fn lalt_pressed(event: &NSEvent) -> bool {
flags_contains(unsafe { event.modifierFlags() }, NX_DEVICELALTKEYMASK) unsafe { event.modifierFlags() }.contains(NX_DEVICELALTKEYMASK)
} }
pub(super) fn ralt_pressed(event: &NSEvent) -> bool { pub(super) fn ralt_pressed(event: &NSEvent) -> bool {
flags_contains(unsafe { event.modifierFlags() }, NX_DEVICERALTKEYMASK) unsafe { event.modifierFlags() }.contains(NX_DEVICERALTKEYMASK)
} }
pub(super) fn event_mods(event: &NSEvent) -> Modifiers { pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
@@ -341,74 +318,42 @@ pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
let mut state = ModifiersState::empty(); let mut state = ModifiersState::empty();
let mut pressed_mods = ModifiersKeys::empty(); let mut pressed_mods = ModifiersKeys::empty();
state.set( state
ModifiersState::SHIFT, .set(ModifiersState::SHIFT, flags.contains(NSEventModifierFlags::NSEventModifierFlagShift));
flags_contains(flags, NSEventModifierFlagShift), pressed_mods.set(ModifiersKeys::LSHIFT, flags.contains(NX_DEVICELSHIFTKEYMASK));
); pressed_mods.set(ModifiersKeys::RSHIFT, flags.contains(NX_DEVICERSHIFTKEYMASK));
pressed_mods.set(
ModifiersKeys::LSHIFT,
flags_contains(flags, NX_DEVICELSHIFTKEYMASK),
);
pressed_mods.set(
ModifiersKeys::RSHIFT,
flags_contains(flags, NX_DEVICERSHIFTKEYMASK),
);
state.set( state.set(
ModifiersState::CONTROL, ModifiersState::CONTROL,
flags_contains(flags, NSEventModifierFlagControl), flags.contains(NSEventModifierFlags::NSEventModifierFlagControl),
);
pressed_mods.set(
ModifiersKeys::LCONTROL,
flags_contains(flags, NX_DEVICELCTLKEYMASK),
);
pressed_mods.set(
ModifiersKeys::RCONTROL,
flags_contains(flags, NX_DEVICERCTLKEYMASK),
); );
pressed_mods.set(ModifiersKeys::LCONTROL, flags.contains(NX_DEVICELCTLKEYMASK));
pressed_mods.set(ModifiersKeys::RCONTROL, flags.contains(NX_DEVICERCTLKEYMASK));
state.set( state.set(ModifiersState::ALT, flags.contains(NSEventModifierFlags::NSEventModifierFlagOption));
ModifiersState::ALT, pressed_mods.set(ModifiersKeys::LALT, flags.contains(NX_DEVICELALTKEYMASK));
flags_contains(flags, NSEventModifierFlagOption), pressed_mods.set(ModifiersKeys::RALT, flags.contains(NX_DEVICERALTKEYMASK));
);
pressed_mods.set(
ModifiersKeys::LALT,
flags_contains(flags, NX_DEVICELALTKEYMASK),
);
pressed_mods.set(
ModifiersKeys::RALT,
flags_contains(flags, NX_DEVICERALTKEYMASK),
);
state.set( state.set(
ModifiersState::SUPER, ModifiersState::SUPER,
flags_contains(flags, NSEventModifierFlagCommand), flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand),
);
pressed_mods.set(
ModifiersKeys::LSUPER,
flags_contains(flags, NX_DEVICELCMDKEYMASK),
);
pressed_mods.set(
ModifiersKeys::RSUPER,
flags_contains(flags, NX_DEVICERCMDKEYMASK),
); );
pressed_mods.set(ModifiersKeys::LSUPER, flags.contains(NX_DEVICELCMDKEYMASK));
pressed_mods.set(ModifiersKeys::RSUPER, flags.contains(NX_DEVICERCMDKEYMASK));
Modifiers { Modifiers { state, pressed_mods }
state,
pressed_mods,
}
} }
pub(super) fn dummy_event() -> Option<Id<NSEvent>> { pub(super) fn dummy_event() -> Option<Retained<NSEvent>> {
unsafe { unsafe {
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2( NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2(
NSEventTypeApplicationDefined, NSEventType::ApplicationDefined,
NSPoint::new(0.0, 0.0), NSPoint::new(0.0, 0.0),
0, // Empty NSEventModifierFlags NSEventModifierFlags(0),
0.0, 0.0,
0, 0,
None, None,
NSEventSubtypeWindowExposed, NSEventSubtype::WindowExposed.0,
0, 0,
0, 0,
) )
@@ -549,7 +494,7 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0x07 => KeyCode::KeyX, 0x07 => KeyCode::KeyX,
0x08 => KeyCode::KeyC, 0x08 => KeyCode::KeyC,
0x09 => KeyCode::KeyV, 0x09 => KeyCode::KeyV,
//0x0a => World 1, // 0x0a => World 1,
0x0b => KeyCode::KeyB, 0x0b => KeyCode::KeyB,
0x0c => KeyCode::KeyQ, 0x0c => KeyCode::KeyQ,
0x0d => KeyCode::KeyW, 0x0d => KeyCode::KeyW,
@@ -591,7 +536,7 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0x31 => KeyCode::Space, 0x31 => KeyCode::Space,
0x32 => KeyCode::Backquote, 0x32 => KeyCode::Backquote,
0x33 => KeyCode::Backspace, 0x33 => KeyCode::Backspace,
//0x34 => unknown, // 0x34 => unknown,
0x35 => KeyCode::Escape, 0x35 => KeyCode::Escape,
0x36 => KeyCode::SuperRight, 0x36 => KeyCode::SuperRight,
0x37 => KeyCode::SuperLeft, 0x37 => KeyCode::SuperLeft,
@@ -605,22 +550,23 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0x3f => KeyCode::Fn, 0x3f => KeyCode::Fn,
0x40 => KeyCode::F17, 0x40 => KeyCode::F17,
0x41 => KeyCode::NumpadDecimal, 0x41 => KeyCode::NumpadDecimal,
//0x42 -> unknown, // 0x42 -> unknown,
0x43 => KeyCode::NumpadMultiply, 0x43 => KeyCode::NumpadMultiply,
//0x44 => unknown, // 0x44 => unknown,
0x45 => KeyCode::NumpadAdd, 0x45 => KeyCode::NumpadAdd,
//0x46 => unknown, // 0x46 => unknown,
0x47 => KeyCode::NumLock, 0x47 => KeyCode::NumLock,
//0x48 => KeyCode::NumpadClear, // 0x48 => KeyCode::NumpadClear,
// TODO: (Artur) for me, kVK_VolumeUp is 0x48 // TODO: (Artur) for me, kVK_VolumeUp is 0x48
// macOS 10.11 // macOS 10.11
// /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h // /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/
// Versions/A/Headers/Events.h
0x49 => KeyCode::AudioVolumeUp, 0x49 => KeyCode::AudioVolumeUp,
0x4a => KeyCode::AudioVolumeDown, 0x4a => KeyCode::AudioVolumeDown,
0x4b => KeyCode::NumpadDivide, 0x4b => KeyCode::NumpadDivide,
0x4c => KeyCode::NumpadEnter, 0x4c => KeyCode::NumpadEnter,
//0x4d => unknown, // 0x4d => unknown,
0x4e => KeyCode::NumpadSubtract, 0x4e => KeyCode::NumpadSubtract,
0x4f => KeyCode::F18, 0x4f => KeyCode::F18,
0x50 => KeyCode::F19, 0x50 => KeyCode::F19,
@@ -637,25 +583,25 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0x5b => KeyCode::Numpad8, 0x5b => KeyCode::Numpad8,
0x5c => KeyCode::Numpad9, 0x5c => KeyCode::Numpad9,
0x5d => KeyCode::IntlYen, 0x5d => KeyCode::IntlYen,
//0x5e => JIS Ro, // 0x5e => JIS Ro,
//0x5f => unknown, // 0x5f => unknown,
0x60 => KeyCode::F5, 0x60 => KeyCode::F5,
0x61 => KeyCode::F6, 0x61 => KeyCode::F6,
0x62 => KeyCode::F7, 0x62 => KeyCode::F7,
0x63 => KeyCode::F3, 0x63 => KeyCode::F3,
0x64 => KeyCode::F8, 0x64 => KeyCode::F8,
0x65 => KeyCode::F9, 0x65 => KeyCode::F9,
//0x66 => JIS Eisuu (macOS), // 0x66 => JIS Eisuu (macOS),
0x67 => KeyCode::F11, 0x67 => KeyCode::F11,
//0x68 => JIS Kanna (macOS), // 0x68 => JIS Kanna (macOS),
0x69 => KeyCode::F13, 0x69 => KeyCode::F13,
0x6a => KeyCode::F16, 0x6a => KeyCode::F16,
0x6b => KeyCode::F14, 0x6b => KeyCode::F14,
//0x6c => unknown, // 0x6c => unknown,
0x6d => KeyCode::F10, 0x6d => KeyCode::F10,
//0x6e => unknown, // 0x6e => unknown,
0x6f => KeyCode::F12, 0x6f => KeyCode::F12,
//0x70 => unknown, // 0x70 => unknown,
0x71 => KeyCode::F15, 0x71 => KeyCode::F15,
0x72 => KeyCode::Insert, 0x72 => KeyCode::Insert,
0x73 => KeyCode::Home, 0x73 => KeyCode::Home,
@@ -670,7 +616,7 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0x7c => KeyCode::ArrowRight, 0x7c => KeyCode::ArrowRight,
0x7d => KeyCode::ArrowDown, 0x7d => KeyCode::ArrowDown,
0x7e => KeyCode::ArrowUp, 0x7e => KeyCode::ArrowUp,
//0x7f => unknown, // 0x7f => unknown,
// 0xA is the caret (^) an macOS's German QERTZ layout. This key is at the same location as // 0xA is the caret (^) an macOS's German QERTZ layout. This key is at the same location as
// backquote (`) on Windows' US layout. // backquote (`) on Windows' US layout.

View File

@@ -1,32 +1,34 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt; use std::{fmt, mem};
use std::mem;
use super::app_delegate::HandlePendingUserEvents; use crate::application::ApplicationHandler;
use crate::event::Event; use crate::platform_impl::ActiveEventLoop;
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
struct EventHandlerData { #[derive(Default)]
#[allow(clippy::type_complexity)]
handler: Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'static>,
}
impl fmt::Debug for EventHandlerData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventHandlerData").finish_non_exhaustive()
}
}
#[derive(Debug, Default)]
pub(crate) struct EventHandler { pub(crate) struct EventHandler {
/// This can be in the following states: /// This can be in the following states:
/// - Not registered by the event loop (None). /// - Not registered by the event loop (None).
/// - Present (Some(handler)). /// - Present (Some(handler)).
/// - Currently executing the handler / in use (RefCell borrowed). /// - Currently executing the handler / in use (RefCell borrowed).
inner: RefCell<Option<EventHandlerData>>, inner: RefCell<Option<&'static mut dyn ApplicationHandler>>,
}
impl fmt::Debug for EventHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state = match self.inner.try_borrow().as_deref() {
Ok(Some(_)) => "<available>",
Ok(None) => "<not set>",
Err(_) => "<in use>",
};
f.debug_struct("EventHandler").field("state", &state).finish_non_exhaustive()
}
} }
impl EventHandler { impl EventHandler {
pub(crate) fn new() -> Self {
Self { inner: RefCell::new(None) }
}
/// Set the event loop handler for the duration of the given closure. /// Set the event loop handler for the duration of the given closure.
/// ///
/// This is similar to using the `scoped-tls` or `scoped-tls-hkt` crates /// This is similar to using the `scoped-tls` or `scoped-tls-hkt` crates
@@ -34,7 +36,7 @@ impl EventHandler {
/// from within the closure. /// from within the closure.
pub(crate) fn set<'handler, R>( pub(crate) fn set<'handler, R>(
&self, &self,
handler: impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'handler, app: &'handler mut dyn ApplicationHandler,
closure: impl FnOnce() -> R, closure: impl FnOnce() -> R,
) -> R { ) -> R {
// SAFETY: We extend the lifetime of the handler here so that we can // SAFETY: We extend the lifetime of the handler here so that we can
@@ -45,21 +47,21 @@ impl EventHandler {
// extended beyond `'handler`. // extended beyond `'handler`.
let handler = unsafe { let handler = unsafe {
mem::transmute::< mem::transmute::<
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'handler>, &'handler mut dyn ApplicationHandler,
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + 'static>, &'static mut dyn ApplicationHandler,
>(Box::new(handler)) >(app)
}; };
match self.inner.try_borrow_mut().as_deref_mut() { match self.inner.try_borrow_mut().as_deref_mut() {
Ok(Some(_)) => { Ok(Some(_)) => {
unreachable!("tried to set handler while another was already set"); unreachable!("tried to set handler while another was already set");
} },
Ok(data @ None) => { Ok(data @ None) => {
*data = Some(EventHandlerData { handler }); *data = Some(handler);
} },
Err(_) => { Err(_) => {
unreachable!("tried to set handler that is currently in use"); unreachable!("tried to set handler that is currently in use");
} },
} }
struct ClearOnDrop<'a>(&'a EventHandler); struct ClearOnDrop<'a>(&'a EventHandler);
@@ -69,10 +71,10 @@ impl EventHandler {
match self.0.inner.try_borrow_mut().as_deref_mut() { match self.0.inner.try_borrow_mut().as_deref_mut() {
Ok(data @ Some(_)) => { Ok(data @ Some(_)) => {
*data = None; *data = None;
} },
Ok(None) => { Ok(None) => {
tracing::error!("tried to clear handler, but no handler was set"); tracing::error!("tried to clear handler, but no handler was set");
} },
Err(_) => { Err(_) => {
// Note: This is not expected to ever happen, this // Note: This is not expected to ever happen, this
// module generally controls the `RefCell`, and // module generally controls the `RefCell`, and
@@ -83,7 +85,7 @@ impl EventHandler {
// weren't able to unset the handler. // weren't able to unset the handler.
eprintln!("tried to clear handler that is currently in use"); eprintln!("tried to clear handler that is currently in use");
std::process::abort(); std::process::abort();
} },
} }
} }
} }
@@ -106,31 +108,31 @@ impl EventHandler {
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_))) matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
} }
pub(crate) fn handle_event( pub(crate) fn handle(
&self, &self,
event: Event<HandlePendingUserEvents>, callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop),
event_loop: &RootActiveEventLoop, event_loop: &ActiveEventLoop,
) { ) {
match self.inner.try_borrow_mut().as_deref_mut() { match self.inner.try_borrow_mut().as_deref_mut() {
Ok(Some(EventHandlerData { handler })) => { Ok(Some(user_app)) => {
// It is important that we keep the reference borrowed here, // It is important that we keep the reference borrowed here,
// so that `in_use` can properly detect that the handler is // so that `in_use` can properly detect that the handler is
// still in use. // still in use.
// //
// If the handler unwinds, the `RefMut` will ensure that the // If the handler unwinds, the `RefMut` will ensure that the
// handler is no longer borrowed. // handler is no longer borrowed.
(handler)(event, event_loop); callback(*user_app, event_loop);
} },
Ok(None) => { Ok(None) => {
// `NSApplication`, our app delegate and this handler are all // `NSApplication`, our app delegate and this handler are all
// global state and so it's not impossible that we could get // global state and so it's not impossible that we could get
// an event after the application has exited the `EventLoop`. // an event after the application has exited the `EventLoop`.
tracing::error!("tried to run event handler, but no handler was set"); tracing::error!("tried to run event handler, but no handler was set");
} },
Err(_) => { Err(_) => {
// Prevent re-entrancy. // Prevent re-entrancy.
panic!("tried to handle event while another event is currently being handled"); panic!("tried to handle event while another event is currently being handled");
} },
} }
} }
} }

View File

@@ -1,46 +1,42 @@
use std::{ use std::any::Any;
any::Any, use std::cell::Cell;
cell::Cell, use std::os::raw::c_void;
collections::VecDeque, use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe};
marker::PhantomData, use std::ptr;
os::raw::c_void, use std::rc::{Rc, Weak};
panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe}, use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
ptr, use std::sync::Arc;
rc::{Rc, Weak}, use std::time::{Duration, Instant};
sync::mpsc,
time::{Duration, Instant},
};
use core_foundation::base::{CFIndex, CFRelease}; use core_foundation::base::{CFIndex, CFRelease};
use core_foundation::runloop::{ use core_foundation::runloop::{
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext, kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp, CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
}; };
use icrate::AppKit::{ use objc2::rc::{autoreleasepool, Retained};
NSApplication, NSApplicationActivationPolicyAccessory, NSApplicationActivationPolicyProhibited, use objc2::runtime::ProtocolObject;
NSApplicationActivationPolicyRegular, NSWindow, use objc2::{msg_send_id, sel, ClassType};
}; use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSWindow};
use icrate::Foundation::{MainThreadMarker, NSObjectProtocol}; use objc2_foundation::{MainThreadMarker, NSObjectProtocol};
use objc2::{msg_send_id, ClassType};
use objc2::{
rc::{autoreleasepool, Id},
runtime::ProtocolObject,
};
use super::app::WinitApplication;
use super::app_state::ApplicationDelegate;
use super::cursor::CustomCursor;
use super::event::dummy_event; use super::event::dummy_event;
use super::{ use super::monitor;
app::WinitApplication, use super::observer::setup_control_flow_observers;
app_delegate::{ApplicationDelegate, HandlePendingUserEvents}, use crate::application::ApplicationHandler;
monitor::{self, MonitorHandle}, use crate::error::{EventLoopError, ExternalError};
observer::setup_control_flow_observers, use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
}; };
use crate::platform_impl::platform::cursor::CustomCursor; use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource}; use crate::platform::macos::ActivationPolicy;
use crate::{ use crate::platform::pump_events::PumpStatus;
error::EventLoopError, use crate::platform_impl::Window;
event::Event, use crate::window::{
event_loop::{ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents, EventLoopClosed}, CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as RootWindow,
platform::{macos::ActivationPolicy, pump_events::PumpStatus},
}; };
#[derive(Default)] #[derive(Default)]
@@ -60,12 +56,14 @@ impl PanicInfo {
self.inner.set(inner); self.inner.set(inner);
result result
} }
/// Overwrites the curret state if the current state is not panicking
/// Overwrites the current state if the current state is not panicking
pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) { pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) {
if !self.is_panicking() { if !self.is_panicking() {
self.inner.set(Some(p)); self.inner.set(Some(p));
} }
} }
pub fn take(&self) -> Option<Box<dyn Any + Send + 'static>> { pub fn take(&self) -> Option<Box<dyn Any + Send + 'static>> {
self.inner.take() self.inner.take()
} }
@@ -73,82 +71,15 @@ impl PanicInfo {
#[derive(Debug)] #[derive(Debug)]
pub struct ActiveEventLoop { pub struct ActiveEventLoop {
delegate: Id<ApplicationDelegate>, pub(super) delegate: Retained<ApplicationDelegate>,
pub(super) mtm: MainThreadMarker, pub(super) mtm: MainThreadMarker,
} }
impl ActiveEventLoop { impl ActiveEventLoop {
pub(super) fn new_root(delegate: Id<ApplicationDelegate>) -> RootWindowTarget { pub(super) fn app_delegate(&self) -> &ApplicationDelegate {
let mtm = MainThreadMarker::from(&*delegate); &self.delegate
let p = Self { delegate, mtm };
RootWindowTarget {
p,
_marker: PhantomData,
}
} }
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
RootCustomCursor {
inner: CustomCursor::new(source.inner),
}
}
#[inline]
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
monitor::available_monitors()
}
#[inline]
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
let monitor = monitor::primary_monitor();
Some(monitor)
}
#[inline]
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
#[cfg(feature = "rwh_05")]
#[inline]
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
rwh_05::RawDisplayHandle::AppKit(rwh_05::AppKitDisplayHandle::empty())
}
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::AppKit(
rwh_06::AppKitDisplayHandle::new(),
))
}
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.delegate.set_control_flow(control_flow)
}
pub(crate) fn control_flow(&self) -> ControlFlow {
self.delegate.control_flow()
}
pub(crate) fn exit(&self) {
self.delegate.exit()
}
pub(crate) fn clear_exit(&self) {
self.delegate.clear_exit()
}
pub(crate) fn exiting(&self) -> bool {
self.delegate.exiting()
}
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle
}
}
impl ActiveEventLoop {
pub(crate) fn hide_application(&self) { pub(crate) fn hide_application(&self) {
NSApplication::sharedApplication(self.mtm).hide(None) NSApplication::sharedApplication(self.mtm).hide(None)
} }
@@ -166,37 +97,99 @@ impl ActiveEventLoop {
} }
} }
fn map_user_event<T: 'static>( impl RootActiveEventLoop for ActiveEventLoop {
mut handler: impl FnMut(Event<T>, &RootWindowTarget), fn create_proxy(&self) -> RootEventLoopProxy {
receiver: Rc<mpsc::Receiver<T>>, let event_loop_proxy = EventLoopProxy::new(self.delegate.proxy_wake_up());
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget) { RootEventLoopProxy { event_loop_proxy }
move |event, window_target| match event.map_nonuser_event() { }
Ok(event) => (handler)(event, window_target),
Err(_) => { fn create_window(
for event in receiver.try_iter() { &self,
(handler)(Event::UserEvent(event), window_target); window_attributes: crate::window::WindowAttributes,
} ) -> Result<crate::window::Window, crate::error::OsError> {
let window = Window::new(self, window_attributes)?;
Ok(RootWindow { window })
}
fn create_custom_cursor(
&self,
source: CustomCursorSource,
) -> Result<RootCustomCursor, ExternalError> {
Ok(RootCustomCursor { inner: CustomCursor::new(source.inner)? })
}
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
Box::new(monitor::available_monitors().into_iter().map(|inner| RootMonitorHandle { inner }))
}
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
let monitor = monitor::primary_monitor();
Some(RootMonitorHandle { inner: monitor })
}
fn listen_device_events(&self, _allowed: DeviceEvents) {}
fn system_theme(&self) -> Option<Theme> {
let app = NSApplication::sharedApplication(self.mtm);
if app.respondsToSelector(sel!(effectiveAppearance)) {
Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
} else {
Some(Theme::Light)
} }
} }
fn set_control_flow(&self, control_flow: ControlFlow) {
self.delegate.set_control_flow(control_flow)
}
fn control_flow(&self) -> ControlFlow {
self.delegate.control_flow()
}
fn exit(&self) {
self.delegate.exit()
}
fn exiting(&self) -> bool {
self.delegate.exiting()
}
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
}
fn as_any(&self) -> &dyn Any {
self
}
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
} }
pub struct EventLoop<T: 'static> { #[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}
pub struct EventLoop {
/// Store a reference to the application for convenience. /// Store a reference to the application for convenience.
/// ///
/// We intentionally don't store `WinitApplication` since we want to have /// We intentionally don't store `WinitApplication` since we want to have
/// the possibility of swapping that out at some point. /// the possibility of swapping that out at some point.
app: Id<NSApplication>, app: Retained<NSApplication>,
/// The application delegate that we've registered. /// The application delegate that we've registered.
/// ///
/// The delegate is only weakly referenced by NSApplication, so we must /// The delegate is only weakly referenced by NSApplication, so we must
/// keep it around here as well. /// keep it around here as well.
delegate: Id<ApplicationDelegate>, delegate: Retained<ApplicationDelegate>,
// Event sender and receiver, used for EventLoopProxy. window_target: ActiveEventLoop,
sender: mpsc::Sender<T>,
receiver: Rc<mpsc::Receiver<T>>,
window_target: RootWindowTarget,
panic_info: Rc<PanicInfo>, panic_info: Rc<PanicInfo>,
} }
@@ -217,25 +210,29 @@ impl Default for PlatformSpecificEventLoopAttributes {
} }
} }
impl<T> EventLoop<T> { impl EventLoop {
pub(crate) fn new( pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes, attributes: &PlatformSpecificEventLoopAttributes,
) -> Result<Self, EventLoopError> { ) -> Result<Self, EventLoopError> {
let mtm = MainThreadMarker::new() let mtm = MainThreadMarker::new()
.expect("on macOS, `EventLoop` must be created on the main thread!"); .expect("on macOS, `EventLoop` must be created on the main thread!");
let app: Id<NSApplication> = let app: Retained<NSApplication> =
unsafe { msg_send_id![WinitApplication::class(), sharedApplication] }; unsafe { msg_send_id![WinitApplication::class(), sharedApplication] };
if !app.is_kind_of::<WinitApplication>() { if !app.is_kind_of::<WinitApplication>() {
panic!("`winit` requires control over the principal class. You must create the event loop before other parts of your application initialize NSApplication"); panic!(
"`winit` requires control over the principal class. You must create the event \
loop before other parts of your application initialize NSApplication"
);
} }
let activation_policy = match attributes.activation_policy { let activation_policy = match attributes.activation_policy {
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular, ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular,
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory, ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory,
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited, ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited,
}; };
let delegate = ApplicationDelegate::new( let delegate = ApplicationDelegate::new(
mtm, mtm,
activation_policy, activation_policy,
@@ -248,44 +245,34 @@ impl<T> EventLoop<T> {
}); });
let panic_info: Rc<PanicInfo> = Default::default(); let panic_info: Rc<PanicInfo> = Default::default();
setup_control_flow_observers(Rc::downgrade(&panic_info)); setup_control_flow_observers(mtm, Rc::downgrade(&panic_info));
let (sender, receiver) = mpsc::channel();
Ok(EventLoop { Ok(EventLoop {
app, app,
delegate: delegate.clone(), delegate: delegate.clone(),
sender, window_target: ActiveEventLoop { delegate, mtm },
receiver: Rc::new(receiver),
window_target: RootWindowTarget {
p: ActiveEventLoop { delegate, mtm },
_marker: PhantomData,
},
panic_info, panic_info,
}) })
} }
pub fn window_target(&self) -> &RootWindowTarget { pub fn window_target(&self) -> &dyn RootActiveEventLoop {
&self.window_target &self.window_target
} }
pub fn run<F>(mut self, handler: F) -> Result<(), EventLoopError> pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
where self.run_app_on_demand(app)
F: FnMut(Event<T>, &RootWindowTarget),
{
self.run_on_demand(handler)
} }
// NB: we don't base this on `pump_events` because for `MacOs` we can't support // NB: we don't base this on `pump_events` because for `MacOs` we can't support
// `pump_events` elegantly (we just ask to run the loop for a "short" amount of // `pump_events` elegantly (we just ask to run the loop for a "short" amount of
// time and so a layered implementation would end up using a lot of CPU due to // time and so a layered implementation would end up using a lot of CPU due to
// redundant wake ups. // redundant wake ups.
pub fn run_on_demand<F>(&mut self, handler: F) -> Result<(), EventLoopError> pub fn run_app_on_demand<A: ApplicationHandler>(
where &mut self,
F: FnMut(Event<T>, &RootWindowTarget), mut app: A,
{ ) -> Result<(), EventLoopError> {
let handler = map_user_event(handler, self.receiver.clone()); self.delegate.clear_exit();
self.delegate.set_event_handler(&mut app, || {
self.delegate.set_event_handler(handler, || {
autoreleasepool(|_| { autoreleasepool(|_| {
// clear / normalize pump_events state // clear / normalize pump_events state
self.delegate.set_wait_timeout(None); self.delegate.set_wait_timeout(None);
@@ -318,16 +305,15 @@ impl<T> EventLoop<T> {
Ok(()) Ok(())
} }
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, handler: F) -> PumpStatus pub fn pump_app_events<A: ApplicationHandler>(
where &mut self,
F: FnMut(Event<T>, &RootWindowTarget), timeout: Option<Duration>,
{ mut app: A,
let handler = map_user_event(handler, self.receiver.clone()); ) -> PumpStatus {
self.delegate.set_event_handler(&mut app, || {
self.delegate.set_event_handler(handler, || {
autoreleasepool(|_| { autoreleasepool(|_| {
// As a special case, if the application hasn't been launched yet then we at least run // As a special case, if the application hasn't been launched yet then we at least
// the loop until it has fully launched. // run the loop until it has fully launched.
if !self.delegate.is_launched() { if !self.delegate.is_launched() {
debug_assert!(!self.delegate.is_running()); debug_assert!(!self.delegate.is_running());
@@ -335,31 +321,34 @@ impl<T> EventLoop<T> {
// SAFETY: We do not run the application re-entrantly // SAFETY: We do not run the application re-entrantly
unsafe { self.app.run() }; unsafe { self.app.run() };
// Note: we dispatch `NewEvents(Init)` + `Resumed` events after the application has launched // Note: we dispatch `NewEvents(Init)` + `Resumed` events after the application
// has launched
} else if !self.delegate.is_running() { } else if !self.delegate.is_running() {
// Even though the application may have been launched, it's possible we aren't running // Even though the application may have been launched, it's possible we aren't
// if the `EventLoop` was run before and has since exited. This indicates that // running if the `EventLoop` was run before and has since
// we just starting to re-run the same `EventLoop` again. // exited. This indicates that we just starting to re-run
// the same `EventLoop` again.
self.delegate.set_is_running(true); self.delegate.set_is_running(true);
self.delegate.dispatch_init_events(); self.delegate.dispatch_init_events();
} else { } else {
// Only run for as long as the given `Duration` allows so we don't block the external loop. // Only run for as long as the given `Duration` allows so we don't block the
// external loop.
match timeout { match timeout {
Some(Duration::ZERO) => { Some(Duration::ZERO) => {
self.delegate.set_wait_timeout(None); self.delegate.set_wait_timeout(None);
self.delegate.set_stop_before_wait(true); self.delegate.set_stop_before_wait(true);
} },
Some(duration) => { Some(duration) => {
self.delegate.set_stop_before_wait(false); self.delegate.set_stop_before_wait(false);
let timeout = Instant::now() + duration; let timeout = Instant::now() + duration;
self.delegate.set_wait_timeout(Some(timeout)); self.delegate.set_wait_timeout(Some(timeout));
self.delegate.set_stop_after_wait(true); self.delegate.set_stop_after_wait(true);
} },
None => { None => {
self.delegate.set_wait_timeout(None); self.delegate.set_wait_timeout(None);
self.delegate.set_stop_before_wait(false); self.delegate.set_stop_before_wait(false);
self.delegate.set_stop_after_wait(true); self.delegate.set_stop_after_wait(true);
} },
} }
self.delegate.set_stop_on_redraw(true); self.delegate.set_stop_on_redraw(true);
// SAFETY: We do not run the application re-entrantly // SAFETY: We do not run the application re-entrantly
@@ -384,22 +373,12 @@ impl<T> EventLoop<T> {
}) })
}) })
} }
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.sender.clone())
}
} }
#[derive(Clone)] #[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle; pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle { impl OwnedDisplayHandle {
#[cfg(feature = "rwh_05")]
#[inline]
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
rwh_05::AppKitDisplayHandle::empty().into()
}
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
#[inline] #[inline]
pub fn raw_display_handle_rwh_06( pub fn raw_display_handle_rwh_06(
@@ -440,19 +419,19 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
let app = NSApplication::sharedApplication(mtm); let app = NSApplication::sharedApplication(mtm);
stop_app_immediately(&app); stop_app_immediately(&app);
None None
} },
} }
} }
pub struct EventLoopProxy<T> { pub struct EventLoopProxy {
sender: mpsc::Sender<T>, proxy_wake_up: Arc<AtomicBool>,
source: CFRunLoopSourceRef, source: CFRunLoopSourceRef,
} }
unsafe impl<T: Send> Send for EventLoopProxy<T> {} unsafe impl Send for EventLoopProxy {}
unsafe impl<T: Send> Sync for EventLoopProxy<T> {} unsafe impl Sync for EventLoopProxy {}
impl<T> Drop for EventLoopProxy<T> { impl Drop for EventLoopProxy {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
CFRelease(self.source as _); CFRelease(self.source as _);
@@ -460,14 +439,14 @@ impl<T> Drop for EventLoopProxy<T> {
} }
} }
impl<T> Clone for EventLoopProxy<T> { impl Clone for EventLoopProxy {
fn clone(&self) -> Self { fn clone(&self) -> Self {
EventLoopProxy::new(self.sender.clone()) EventLoopProxy::new(self.proxy_wake_up.clone())
} }
} }
impl<T> EventLoopProxy<T> { impl EventLoopProxy {
fn new(sender: mpsc::Sender<T>) -> Self { fn new(proxy_wake_up: Arc<AtomicBool>) -> Self {
unsafe { unsafe {
// just wake up the eventloop // just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {} extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
@@ -487,25 +466,21 @@ impl<T> EventLoopProxy<T> {
cancel: None, cancel: None,
perform: event_loop_proxy_handler, perform: event_loop_proxy_handler,
}; };
let source = let source = CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::MAX - 1, &mut context);
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl); CFRunLoopWakeUp(rl);
EventLoopProxy { sender, source } EventLoopProxy { proxy_wake_up, source }
} }
} }
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> { pub fn wake_up(&self) {
self.sender self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
.send(event)
.map_err(|mpsc::SendError(x)| EventLoopClosed(x))?;
unsafe { unsafe {
// let the main thread know there's a new event // let the main thread know there's a new event
CFRunLoopSourceSignal(self.source); CFRunLoopSourceSignal(self.source);
let rl = CFRunLoopGetMain(); let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl); CFRunLoopWakeUp(rl);
} }
Ok(())
} }
} }

View File

@@ -4,14 +4,14 @@
use std::ffi::c_void; use std::ffi::c_void;
use core_foundation::{ use core_foundation::array::CFArrayRef;
array::CFArrayRef, dictionary::CFDictionaryRef, string::CFStringRef, uuid::CFUUIDRef, use core_foundation::dictionary::CFDictionaryRef;
}; use core_foundation::string::CFStringRef;
use core_graphics::{ use core_foundation::uuid::CFUUIDRef;
base::CGError, use core_graphics::base::CGError;
display::{CGDirectDisplayID, CGDisplayConfigRef}, use core_graphics::display::{CGDirectDisplayID, CGDisplayConfigRef};
}; use objc2::ffi::NSInteger;
use objc2::{ffi::NSInteger, runtime::AnyObject}; use objc2::runtime::AnyObject;
pub type CGDisplayFadeInterval = f32; pub type CGDisplayFadeInterval = f32;
pub type CGDisplayReservationInterval = f32; pub type CGDisplayReservationInterval = f32;

View File

@@ -1,11 +1,8 @@
use icrate::AppKit::{ use objc2::rc::Retained;
NSApplication, NSEventModifierFlagCommand, NSEventModifierFlagOption, NSEventModifierFlags,
NSMenu, NSMenuItem,
};
use icrate::Foundation::{ns_string, MainThreadMarker, NSProcessInfo, NSString};
use objc2::rc::Id;
use objc2::runtime::Sel; use objc2::runtime::Sel;
use objc2::sel; use objc2::sel;
use objc2_app_kit::{NSApplication, NSEventModifierFlags, NSMenu, NSMenuItem};
use objc2_foundation::{ns_string, MainThreadMarker, NSProcessInfo, NSString};
struct KeyEquivalent<'a> { struct KeyEquivalent<'a> {
key: &'a NSString, key: &'a NSString,
@@ -23,12 +20,8 @@ pub fn initialize(app: &NSApplication) {
// About menu item // About menu item
let about_item_title = ns_string!("About ").stringByAppendingString(&process_name); let about_item_title = ns_string!("About ").stringByAppendingString(&process_name);
let about_item = menu_item( let about_item =
mtm, menu_item(mtm, &about_item_title, Some(sel!(orderFrontStandardAboutPanel:)), None);
&about_item_title,
Some(sel!(orderFrontStandardAboutPanel:)),
None,
);
// Services menu item // Services menu item
let services_menu = NSMenu::new(mtm); let services_menu = NSMenu::new(mtm);
@@ -44,10 +37,7 @@ pub fn initialize(app: &NSApplication) {
mtm, mtm,
&hide_item_title, &hide_item_title,
Some(sel!(hide:)), Some(sel!(hide:)),
Some(KeyEquivalent { Some(KeyEquivalent { key: ns_string!("h"), masks: None }),
key: ns_string!("h"),
masks: None,
}),
); );
// Hide other applications menu item // Hide other applications menu item
@@ -58,18 +48,17 @@ pub fn initialize(app: &NSApplication) {
Some(sel!(hideOtherApplications:)), Some(sel!(hideOtherApplications:)),
Some(KeyEquivalent { Some(KeyEquivalent {
key: ns_string!("h"), key: ns_string!("h"),
masks: Some(NSEventModifierFlagOption | NSEventModifierFlagCommand), masks: Some(
NSEventModifierFlags::NSEventModifierFlagOption
| NSEventModifierFlags::NSEventModifierFlagCommand,
),
}), }),
); );
// Show applications menu item // Show applications menu item
let show_all_item_title = ns_string!("Show All"); let show_all_item_title = ns_string!("Show All");
let show_all_item = menu_item( let show_all_item =
mtm, menu_item(mtm, show_all_item_title, Some(sel!(unhideAllApplications:)), None);
show_all_item_title,
Some(sel!(unhideAllApplications:)),
None,
);
// Separator menu item // Separator menu item
let sep = NSMenuItem::separatorItem(mtm); let sep = NSMenuItem::separatorItem(mtm);
@@ -80,10 +69,7 @@ pub fn initialize(app: &NSApplication) {
mtm, mtm,
&quit_item_title, &quit_item_title,
Some(sel!(terminate:)), Some(sel!(terminate:)),
Some(KeyEquivalent { Some(KeyEquivalent { key: ns_string!("q"), masks: None }),
key: ns_string!("q"),
masks: None,
}),
); );
app_menu.addItem(&about_item); app_menu.addItem(&about_item);
@@ -105,7 +91,7 @@ fn menu_item(
title: &NSString, title: &NSString,
selector: Option<Sel>, selector: Option<Sel>,
key_equivalent: Option<KeyEquivalent<'_>>, key_equivalent: Option<KeyEquivalent<'_>>,
) -> Id<NSMenuItem> { ) -> Retained<NSMenuItem> {
let (key, masks) = match key_equivalent { let (key, masks) = match key_equivalent {
Some(ke) => (ke.key, ke.masks), Some(ke) => (ke.key, ke.masks),
None => (ns_string!(""), None), None => (ns_string!(""), None),

View File

@@ -2,7 +2,7 @@
mod util; mod util;
mod app; mod app;
mod app_delegate; mod app_state;
mod cursor; mod cursor;
mod event; mod event;
mod event_handler; mod event_handler;
@@ -17,21 +17,17 @@ mod window_delegate;
use std::fmt; use std::fmt;
pub(crate) use self::{
event::{physicalkey_to_scancode, scancode_to_physicalkey, KeyEventExtra},
event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
},
monitor::{MonitorHandle, VideoModeHandle},
window::WindowId,
window_delegate::PlatformSpecificWindowAttributes,
};
use crate::event::DeviceId as RootDeviceId;
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor; pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
pub(crate) use self::window::Window; pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey, KeyEventExtra};
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::{Window, WindowId};
pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
use crate::event::DeviceId as RootDeviceId;
pub(crate) use crate::icon::NoIcon as PlatformIcon; pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; pub(crate) use crate::platform_impl::Fullscreen;
@@ -39,7 +35,7 @@ pub(crate) use crate::platform_impl::Fullscreen;
pub struct DeviceId; pub struct DeviceId;
impl DeviceId { impl DeviceId {
pub const unsafe fn dummy() -> Self { pub const fn dummy() -> Self {
DeviceId DeviceId
} }
} }
@@ -47,6 +43,15 @@ impl DeviceId {
// Constant device ID; to be removed when if backend is updated to report real device IDs. // Constant device ID; to be removed when if backend is updated to report real device IDs.
pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId); pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId;
impl FingerId {
pub const fn dummy() -> Self {
FingerId
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum OsError { pub enum OsError {
CGError(core_graphics::base::CGError), CGError(core_graphics::base::CGError),

View File

@@ -1,18 +1,19 @@
#![allow(clippy::unnecessary_cast)] #![allow(clippy::unnecessary_cast)]
use std::{collections::VecDeque, fmt}; use std::collections::VecDeque;
use std::fmt;
use std::num::{NonZeroU16, NonZeroU32};
use core_foundation::{ use core_foundation::array::{CFArrayGetCount, CFArrayGetValueAtIndex};
array::{CFArrayGetCount, CFArrayGetValueAtIndex}, use core_foundation::base::{CFRelease, TCFType};
base::{CFRelease, TCFType}, use core_foundation::string::CFString;
string::CFString,
};
use core_graphics::display::{ use core_graphics::display::{
CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode, CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode,
}; };
use icrate::AppKit::NSScreen; use objc2::rc::Retained;
use icrate::Foundation::{ns_string, MainThreadMarker, NSNumber, NSPoint, NSRect}; use objc2::runtime::AnyObject;
use objc2::{rc::Id, runtime::AnyObject}; use objc2_app_kit::NSScreen;
use objc2_foundation::{ns_string, run_on_main, MainThreadMarker, NSNumber, NSPoint, NSRect};
use super::ffi; use super::ffi;
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
@@ -20,8 +21,8 @@ use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
#[derive(Clone)] #[derive(Clone)]
pub struct VideoModeHandle { pub struct VideoModeHandle {
size: PhysicalSize<u32>, size: PhysicalSize<u32>,
bit_depth: u16, bit_depth: Option<NonZeroU16>,
refresh_rate_millihertz: u32, refresh_rate_millihertz: Option<NonZeroU32>,
pub(crate) monitor: MonitorHandle, pub(crate) monitor: MonitorHandle,
pub(crate) native_mode: NativeDisplayMode, pub(crate) native_mode: NativeDisplayMode,
} }
@@ -80,15 +81,47 @@ impl Clone for NativeDisplayMode {
} }
impl VideoModeHandle { impl VideoModeHandle {
fn new(
monitor: MonitorHandle,
mode: NativeDisplayMode,
refresh_rate_millihertz: Option<NonZeroU32>,
) -> Self {
unsafe {
let pixel_encoding =
CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode.0))
.to_string();
let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) {
32
} else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) {
16
} else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO30BitDirectPixels) {
30
} else {
unimplemented!()
};
VideoModeHandle {
size: PhysicalSize::new(
ffi::CGDisplayModeGetPixelWidth(mode.0) as u32,
ffi::CGDisplayModeGetPixelHeight(mode.0) as u32,
),
refresh_rate_millihertz,
bit_depth: NonZeroU16::new(bit_depth),
monitor: monitor.clone(),
native_mode: mode,
}
}
}
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
self.size self.size
} }
pub fn bit_depth(&self) -> u16 { pub fn bit_depth(&self) -> Option<NonZeroU16> {
self.bit_depth self.bit_depth
} }
pub fn refresh_rate_millihertz(&self) -> u32 { pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
self.refresh_rate_millihertz self.refresh_rate_millihertz
} }
@@ -158,10 +191,8 @@ impl fmt::Debug for MonitorHandle {
f.debug_struct("MonitorHandle") f.debug_struct("MonitorHandle")
.field("name", &self.name()) .field("name", &self.name())
.field("native_identifier", &self.native_identifier()) .field("native_identifier", &self.native_identifier())
.field("size", &self.size())
.field("position", &self.position()) .field("position", &self.position())
.field("scale_factor", &self.scale_factor()) .field("scale_factor", &self.scale_factor())
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz())
.finish_non_exhaustive() .finish_non_exhaustive()
} }
} }
@@ -184,26 +215,18 @@ impl MonitorHandle {
self.0 self.0
} }
pub fn size(&self) -> PhysicalSize<u32> {
let MonitorHandle(display_id) = *self;
let display = CGDisplay::new(display_id);
let height = display.pixels_high();
let width = display.pixels_wide();
PhysicalSize::from_logical::<_, f64>((width as f64, height as f64), self.scale_factor())
}
#[inline] #[inline]
pub fn position(&self) -> PhysicalPosition<i32> { pub fn position(&self) -> Option<PhysicalPosition<i32>> {
// This is already in screen coordinates. If we were using `NSScreen`, // This is already in screen coordinates. If we were using `NSScreen`,
// then a conversion would've been needed: // then a conversion would've been needed:
// flip_window_screen_coordinates(self.ns_screen(mtm)?.frame()) // flip_window_screen_coordinates(self.ns_screen(mtm)?.frame())
let bounds = unsafe { CGDisplayBounds(self.native_identifier()) }; let bounds = unsafe { CGDisplayBounds(self.native_identifier()) };
let position = LogicalPosition::new(bounds.origin.x, bounds.origin.y); let position = LogicalPosition::new(bounds.origin.x, bounds.origin.y);
position.to_physical(self.scale_factor()) Some(position.to_physical(self.scale_factor()))
} }
pub fn scale_factor(&self) -> f64 { pub fn scale_factor(&self) -> f64 {
MainThreadMarker::run_on_main(|mtm| { run_on_main(|mtm| {
match self.ns_screen(mtm) { match self.ns_screen(mtm) {
Some(screen) => screen.backingScaleFactor() as f64, Some(screen) => screen.backingScaleFactor() as f64,
None => 1.0, // default to 1.0 when we can't find the screen None => 1.0, // default to 1.0 when we can't find the screen
@@ -211,36 +234,20 @@ impl MonitorHandle {
}) })
} }
pub fn refresh_rate_millihertz(&self) -> Option<u32> { fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
unsafe { let current_display_mode =
let current_display_mode = NativeDisplayMode(CGDisplayCopyDisplayMode(self.0) as _); NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _);
let refresh_rate = ffi::CGDisplayModeGetRefreshRate(current_display_mode.0); refresh_rate_millihertz(self.0, &current_display_mode)
if refresh_rate > 0.0 { }
return Some((refresh_rate * 1000.0).round() as u32);
}
let mut display_link = std::ptr::null_mut(); pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
if ffi::CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link) let mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _);
!= ffi::kCVReturnSuccess let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode);
{ Some(VideoModeHandle::new(self.clone(), mode, refresh_rate_millihertz))
return None;
}
let time = ffi::CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link);
ffi::CVDisplayLinkRelease(display_link);
// This value is indefinite if an invalid display link was specified
if time.flags & ffi::kCVTimeIsIndefinite != 0 {
return None;
}
(time.time_scale as i64)
.checked_div(time.time_value)
.map(|v| (v * 1000) as u32)
}
} }
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
let refresh_rate_millihertz = self.refresh_rate_millihertz().unwrap_or(0); let refresh_rate_millihertz = self.refresh_rate_millihertz();
let monitor = self.clone(); let monitor = self.clone();
unsafe { unsafe {
@@ -265,39 +272,21 @@ impl MonitorHandle {
// CGDisplayModeGetRefreshRate returns 0.0 for any display that // CGDisplayModeGetRefreshRate returns 0.0 for any display that
// isn't a CRT // isn't a CRT
let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 { let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 {
(cg_refresh_rate_hertz * 1000) as u32 NonZeroU32::new((cg_refresh_rate_hertz * 1000) as u32)
} else { } else {
refresh_rate_millihertz refresh_rate_millihertz
}; };
let pixel_encoding = VideoModeHandle::new(
CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode)) monitor.clone(),
.to_string(); NativeDisplayMode(mode),
let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) {
32
} else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) {
16
} else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO30BitDirectPixels) {
30
} else {
unimplemented!()
};
VideoModeHandle {
size: PhysicalSize::new(
ffi::CGDisplayModeGetPixelWidth(mode) as u32,
ffi::CGDisplayModeGetPixelHeight(mode) as u32,
),
refresh_rate_millihertz, refresh_rate_millihertz,
bit_depth, )
monitor: monitor.clone(),
native_mode: NativeDisplayMode(mode),
}
}) })
} }
} }
pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option<Id<NSScreen>> { pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option<Retained<NSScreen>> {
let uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) }; let uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) };
NSScreen::screens(mtm).into_iter().find(|screen| { NSScreen::screens(mtm).into_iter().find(|screen| {
let other_native_id = get_display_id(screen); let other_native_id = get_display_id(screen);
@@ -351,3 +340,29 @@ pub(crate) fn flip_window_screen_coordinates(frame: NSRect) -> NSPoint {
let y = main_screen_height - frame.size.height - frame.origin.y; let y = main_screen_height - frame.size.height - frame.origin.y;
NSPoint::new(frame.origin.x, y) NSPoint::new(frame.origin.x, y)
} }
fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> Option<NonZeroU32> {
unsafe {
let refresh_rate = ffi::CGDisplayModeGetRefreshRate(mode.0);
if refresh_rate > 0.0 {
return NonZeroU32::new((refresh_rate * 1000.0).round() as u32);
}
let mut display_link = std::ptr::null_mut();
if ffi::CVDisplayLinkCreateWithCGDisplay(id, &mut display_link) != ffi::kCVReturnSuccess {
return None;
}
let time = ffi::CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link);
ffi::CVDisplayLinkRelease(display_link);
// This value is indefinite if an invalid display link was specified
if time.flags & ffi::kCVTimeIsIndefinite != 0 {
return None;
}
(time.time_scale as i64)
.checked_div(time.time_value)
.map(|v| (v * 1000) as u32)
.and_then(NonZeroU32::new)
}
}

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