Compare commits

..

227 Commits

Author SHA1 Message Date
daxpedda
dbeeaeffd9 Winit version 0.29.10 2024-01-15 12:59:15 +04:00
daxpedda
c4bfbbe417 ci: Fix dead code error on nightly
See rust-lang/rust#118297
2024-01-15 12:59:15 +04:00
daxpedda
978ec7dfec Web: increase cursor position accuracy (#3380) 2024-01-15 12:59:15 +04:00
daxpedda
87f44ecffb Web: account for canvas being focused already (#3369) 2024-01-15 12:59:15 +04:00
Kirill Chibisov
da82971f52 Winit version 0.29.9 2024-01-05 15:09:05 +04:00
Emil Ernerfeldt
324dd5fa86 On macOS, reported shifted key with shift+Ctrl/Cmd
Fixes #3078.
2024-01-05 15:09:05 +04:00
Kirill Chibisov
fdedda38d2 On X11, fix error propagation in EventLoop::new
Fixes #3350.
2024-01-05 15:09:05 +04:00
Kirill Chibisov
cf0a533461 Issue resize due to scale change on Wayland
This is a regression from 8f6de4ef.

Links: https://github.com/alacritty/alacritty/issues/7559
2024-01-05 15:09:05 +04:00
Kirill Chibisov
017ff26e7d On X11 and Wayland, fix numpad up being ArrowLeft
Links: https://github.com/alacritty/alacritty/issues/7533
2024-01-05 15:09:05 +04:00
Kirill Chibisov
6eb79f04c8 Winit version 0.29.8 2023-12-31 20:13:31 +04:00
Kirill Chibisov
2bf12c74dc On X11, fix IME input lagging behind
IME events and requests where drained on one-by-one basis, however
we should drain all of them at once and send to user.

Links: https://github.com/alacritty/alacritty/issues/7514
2023-12-31 20:13:31 +04:00
John Nunley
2998bbf7db On X11, cache the XRandR extension version 2023-12-31 20:13:31 +04:00
Kirill Chibisov
3f82a6a90d On X11, fix ModifiersChanged from xdotool
xdotool will update modifiers before Xkb will actually send event
updating them, thus the modifiers will be updating even before the
actual update, which is unfortunate.

Links: https://github.com/alacritty/alacritty/issues/7502
2023-12-31 20:13:31 +04:00
Kirill Chibisov
2e610111b0 On X11, update keymap on XkbMapNotify
This is required to handle xmodmap.

Fixes #3338.
2023-12-31 20:13:31 +04:00
Kirill Chibisov
63d52aae32 On Wayland, fix Window::request_inner_size during resize
The user may change the size during the on-going resize, meaning that
the size will desync with winit's internal loop which breaks viewporter
setup with fractional scaling.

Links: https://github.com/alacritty/alacritty/issues/7474
2023-12-31 20:13:31 +04:00
John Nunley
5b4f97edac On X11, query for higher Xrandr version
This appears to be the solution for the elusive #3335 issue. Previously,
in the Xlib backend, we used the "XRRQueryVersion" function to query for
the Xrandr version, and used that to determine whether we should use the
"GetScreenResources" call or the "GetScreenResourcesCurrent" call.

However, we passed the version "0, 0" into "XRRQueryVersion".
Previously with Xlib this wasn't a problem, as Xlib ignores the version
you pass in and substitutes it with the version of RandR it expects.

https://gitlab.freedesktop.org/xorg/lib/libxrandr/-/blob/master/src/Xrandr.c?ref_type=heads#L386-387

The way that "XRRQueryVersion" is implemented on the server end, it
compares the version passed into the request with the version supported
by the server. If the server's version is greater than the client
version, it just returns the client version. If the client's version is
greater, it passes the server's version. Since we were passing in "0, 0"
this means that the server returned "0, 0".

https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/randr/rrdispatch.c?ref_type=heads#L50-59

To determine whether we use "GetScreenResources" or
"GetScreenResourcesCurrent", we compare the version returned by the
server against "1, 3". Since we got "0, 0"- a version of XRandR so old
it doesn't even exist- we use "GetScreenResources".

The problem manifests in that "GetScreenResources" can take several
seconds to query the screen state based on the current hardware
configuration. On the other hand, "GetScreenResourcesCurrent" is fast;
it uses the server's hardware cache if it is available.

This problem is visible in XTrace. On the latest `master`:

```
000:<:00c2: 12: RANDR-Request(140,0): QueryVersion major-version=0 minor-version=0
000:>:00c2:32: Reply to QueryVersion: major-version=0 minor-version=0
000:<:00c3:  8: RANDR-Request(140,8): GetScreenResources window=0x0000076e
000:>:00c3:1600: Reply to GetScreenResources:
```

On the `v0.28.0` tag:

```
000:<:0019: 12: RANDR-Request(140,0): QueryVersion major-version=1 minor-version=6
000:>:0019:32: Reply to QueryVersion: major-version=1 minor-version=6
...later
000:<:002d:  8: RANDR-Request(140,25): GetScreenResourcesCurrent window=0x0000076e
000:>:002d:1600: Reply to GetScreenResourcesCurrent
```

This commit fixes this issue by requesting "1, 3" instead. This returns
the version we expect, where we can now use "GetScreenResourcesCurrent"
properly.

Fixes #3335

Signed-off-by: John Nunley <dev@notgull.net>
2023-12-31 20:13:31 +04:00
Kirill Chibisov
9135eb4024 Winit version 0.29.7 2023-12-27 10:10:59 +04:00
John Nunley
23b3c127fd bugfix: Change value sent to X server during minimize
Closes #3327

Signed-off-by: John Nunley <dev@notgull.net>
2023-12-27 10:10:59 +04:00
John Nunley
b343f45500 bugfix: Reload Xft database on DPI change
Closes #1228
2023-12-27 10:10:59 +04:00
Kirill Chibisov
572d61f9ba Winit version 0.29.6 2023-12-24 23:55:50 +04:00
Uli Schlachter
87fc19826b On X11, simplify available_monitors() impl
This code confused me. I tried to understand it. I tried to simplify it
while keeping the functional style. But in the end, this just seems too
complicated for its own good. Just doing the exact same thing with a
match statement and the question mark operator makes it sooo much more
obvious what is happening.

Signed-off-by: Uli Schlachter <psychon@znc.in>
2023-12-24 23:55:50 +04:00
Kirill Chibisov
11d1b7a980 Fix run_on_demand exiting on consequent call
Fixes #3284.
2023-12-24 23:55:50 +04:00
Kirill Chibisov
5ca810ba8f On Wayland, fix WindowEvent::Destroyed delivery 2023-12-24 23:55:50 +04:00
Alex Butler
2d1607b3f7 bugfix(rwh): Bump rwh_05 min version to 0.5.2
Correct min version to support "std" feature
2023-12-24 23:55:50 +04:00
Markus Siglreithmaier
a32e232020 On Windows, remove internal WindowWrapper (#3294)
HWND in windows-sys doesn't require a newtype wrapper for Send/Sync.
2023-12-24 23:55:50 +04:00
daxpedda
9b03bb7276 Fix some doc nits (#3274) 2023-12-24 23:55:50 +04:00
Markus Siglreithmaier
e39596151c On Windows, refactor dynamic function definitions and raw input keyboard handling (#3286) 2023-12-24 23:55:50 +04:00
daxpedda
5289b4f206 On Web, fix context menu not being disabled 2023-12-24 23:55:50 +04:00
Kirill Chibisov
380dc4c451 Winit version 0.29.5 2023-12-22 00:39:22 +04:00
wjian23
6fbdbce6dd On Windows, fix IME area not working 2023-12-22 00:39:22 +04:00
Kirill Chibisov
cafcaa2cdc On Wayland, ensure initial resize delivery
While we correctly configure the sizes, we also need to actually resize
the frame on initial configure and send geometry.

Fixes #3277.
2023-12-22 00:39:22 +04:00
Kirill Chibisov
e00204e626 On windows, remove empty file 2023-12-22 00:39:22 +04:00
Kirill Chibisov
a5b89bfe5a On Wayland, fix resize being sent on focus change
Fixes #3263.
2023-12-22 00:39:22 +04:00
Amr Bashir
44052a093e On Windows, fix set_fullscreen early return for Fullscreen::Borderless(None) 2023-12-22 00:39:22 +04:00
Friz64
40cee238e2 Update sctk-adwaita to 0.8 2023-12-22 00:39:22 +04:00
Héctor Ramón
3dc5c42387 Fix typo in get_xft_dpi 2023-12-22 00:39:22 +04:00
Marijn Suijten
8c4a6ddcb4 On Wayland, make wl_subcompositor protocol optional
This protocol is only used for (optional) Client Side Decorations
(where) the compositor still takes the burden of compositing various
window parts together, via subsurfaces that all belong to a single
window.

If this core protocol is not available, as is the case on gamescope,
disable CSD.
2023-12-22 00:39:22 +04:00
Uli Schlachter
5011a67f6d m: Update to x11rb 0.13.0
The only breaking change is that x11rb no longer reports an error when
querying the WmSizeHints of a window that does not have this property
set. For this reason, the return type of WmSizeHintsCookie::Reply()
changed from Result<WmSizeHints, SomeError> to
Result<Option<WmSizeHints>, SomeError>.

In update_normal_hints(), previously a cryptic error would be reported
to the caller. Instead, this now uses unwrap_or_default() to get a
WmSizeHints. All fields of WmSizeHints are Options, so this produces an
empty object.

resize_increments() queries a value from the window and returns an
Option. Previously, the error for "missing property" was turned into
None via .ok(). This commit adds a call to flatten() to also turn
"property not set" into None.

Finally, request_user_attention() queries a window's WmHints property
and updates one field of it. The code already uses unwrap_or_default()
to deal with missing properties, so just a call to flatten() is needed
to merge "missing property" and "error while querying" into one.

Other changes in x11rb do not seem to affect this crate.

x11rb's MSRV increased from 1.56 to 1.63, which is still below the MSRV
of this crate, which is 1.65.

Signed-off-by: Uli Schlachter <psychon@znc.in>
2023-12-22 00:39:22 +04:00
Fredrik Fornwall
d621ab5018 On Windows, avoid panic in video_modes() 2023-12-22 00:39:22 +04:00
Leon
966c033a6c Changes and improvements to the documentation (#3253)
* FEATURES.md improvements

* docs improvements

* typo fix: 'mean' -> 'main'

Co-authored-by: daxpedda <daxpedda@gmail.com>

---------

Co-authored-by: daxpedda <daxpedda@gmail.com>
2023-12-22 00:39:22 +04:00
Xiaopeng Li
1681410ca8 fix refresh_rate_millihertz on macOS (#3254)
* fix refresh_rate_millihertz on macOS

* round after conversion to mHz

* add changelog entry
2023-12-22 00:39:22 +04:00
John Nunley
a82327c73f bugfix(x11): Use the right atom type in focus_window()
Closes #3248 by removing an Xlibism I forgot about

Signed-off-by: John Nunley <dev@notgull.net>
2023-12-22 00:39:22 +04:00
John Nunley
e71f765dea bugfix(x11): Properly interpret float data in drag ops
Closes #3245

notgull forgot to properly interpret float data from the X server,
making him tonight's biggest loser.

Signed-off-by: John Nunley <dev@notgull.net>
2023-12-22 00:39:22 +04:00
Mads Marquart
0738528931 Remove unused .gitmodules 2023-12-22 00:39:22 +04:00
Emil Ernerfeldt
8119c72d64 On macOS, remove spurious error logging when handling Fn
Fixes #3246.
2023-12-22 00:39:22 +04:00
Kirill Chibisov
43f29f0481 Winit version 0.29.4 2023-11-24 18:13:46 +04:00
John Nunley
266219f27f On X11, reload DPI on PropertyChange
Signed-off-by: John Nunley <dev@notgull.net>
Fixes #1228.
2023-11-24 18:13:46 +04:00
Kirill Chibisov
7449534ba2 Fix infinite recursion in BadIcon reporting (#3237) 2023-11-24 18:13:46 +04:00
Mads Marquart
7aa202b872 Make Android docs build on docs.rs (#3236) 2023-11-24 18:13:46 +04:00
Arend van Beelen jr
06cec065d4 Fix crash when running iPad build on macOS 2023-11-24 18:13:46 +04:00
Kirill Chibisov
f968e64ac8 On macOS, fix assertion when pressing Fn key 2023-11-24 18:13:46 +04:00
Kirill Chibisov
a97309690e On Wayland, fix wl_surface being dropped first
The surface was automatically dropped due to new RAII type in SCTK
when dropping the Window, which was not the case at some point with
SCTK.

Thus destroying objects associated with it where causing issues
with some window managers.

Links: https://github.com/neovide/neovide/issues/2109
2023-11-24 18:13:46 +04:00
Olivier Goffart
dec45ce0ff On Windows, fix set_control_flow from `AboutToWait
In case the AboutToWait event sets the control flow to another value
it's not being used on this iteration.

Fixes #3215.
2023-11-24 18:13:46 +04:00
Nathan Lilienthal
f709ac667f On macOS, send a Resized event after ScaleFactorChanged
Fixes #3213.
2023-11-24 18:13:46 +04:00
Kirill Chibisov
ecbe04caa7 On X11, try alternative cursor icon names as well
This should cover more icons.
2023-11-24 18:13:46 +04:00
Marijn Suijten
7103514ae8 Disable default-features for the ndk crate
We decided to add `rwh_06` to the `default` list of features in the
`ndk` to [nudge users to upgrade], but this forces `winit` to always
(transitively) include `raw-window-handle 0.6` even if the user has
set a different `rwh_xx` feature on the `winit` crate.  `winit` already
forwards the respective `rwh_xx` feaure to the `ndk` crate anyway, so
this default should just be turned off.

At the time of writing this is the only `default` feature of the `ndk`.

Links: https://github.com/rust-mobile/ndk/pull/434#issuecomment-1752089087
2023-11-24 18:13:46 +04:00
DevJac
8b5aa33a88 Fix typo in pre_present_notify docs
Fix typo and other small grammar corrections.
2023-11-24 18:13:46 +04:00
Linda_pp
7a3b486965 Fix crash when minimizing example on Windows 2023-11-24 18:13:46 +04:00
Jasper Bekkers
fc9c78cb56 On Windows, fix MT safety when starting drag 2023-11-24 18:13:46 +04:00
Kirill Chibisov
525219716c Winit version 0.29.3 2023-10-28 20:55:35 +04:00
Kirill Chibisov
7f851fe433 Clarify scale_factor docs
Wayland scales each window individually, thus make it clear. Also
recommend against using the `MonitorHandle::scale_factor`.

Fixes #3183.
2023-10-28 20:55:35 +04:00
Kirill Chibisov
5dea2a4734 On macOS, add support for Window::set_blur 2023-10-28 20:55:35 +04:00
Kirill Chibisov
821fc63a9c On Windows, add support for Window::set_transparent 2023-10-28 20:55:35 +04:00
Kirill Chibisov
8e9a3d2dd3 On Wayland, improve initial user size handling
Keep the user provided size in the original values and convert only
when we're getting a `configure` event. On some compositors will
have a scale available, so it'll work, however with some we'll
still have old 'pick 1` as default.

Also configure_bounds when compositor tells the user to pick the size,
that will ensure that initial `with_inner_size` won't grow beyond the
working area.

Fixes #3187.
2023-10-28 20:55:35 +04:00
Kirill Chibisov
70a77b8534 On Wayland, fix RedrawRequsted loop
The `dirty` is never cleared when decorations are hidden without
`sctk-adwaita`.

Fixes #3177.
2023-10-28 20:55:35 +04:00
Marijn Suijten
a5bb6d67f7 On wasm, provide intradoc-link for spawn() function in EventLoop docs (#3178) 2023-10-28 20:55:35 +04:00
J-P Nurmi
33a2e4cebd On macOS, fix deadlock during nested event loops (e.g. rfd) 2023-10-28 20:55:35 +04:00
Kirill Chibisov
0ee26986d8 On Winows, Fix deedlock with WM_MOUSEMOVE
The lock was still present in `None` path.

Fixes: d37d1a03b(On Windows, fix deadlock during `Cursor{Enter,Leave}`)
2023-10-28 20:55:35 +04:00
Kirill Chibisov
ec41dddd0d Fix unused import warnings on nightly 2023-10-28 20:55:35 +04:00
Kirill Chibisov
7a872903a4 On Windows, fix deadlock during Cursor{Enter,Leave}
The lock was not released when calling back to the user.

Fixes #3171.
2023-10-28 20:55:35 +04:00
Kirill Chibisov
d82886bddc Winit version 0.29.2 2023-10-21 11:40:41 +04:00
Kirill Chibisov
08edda1b0b On X11, fix cursor_hittest not reloaded on Resize
The cursor hittest was not reloaded on window size changes, only
when `Window::request_inner_size` was called leading to regions
of the window being not clickable.

Also, don't try to apply hittest logic when user never requested a
hittest.

Links: https://github.com/alacritty/alacritty/pull/7220
2023-10-21 11:40:41 +04:00
Diggory Hardy
7de33bca40 Fix rwhd_05 doc links 2023-10-21 11:40:41 +04:00
Valaphee The Meerkat
40ba9a7ce7 feat(windows): Fix inconsistency in mouse button device events, add hwheel device event on Windows
While working with device events, I noticed that there was an inconsistency in the mouse button device events between Windows/X11 and for example web, because web uses the same ids/order as the MouseButton enum, and Windows/X11 are using the X11 ids, and hwheel device event was ignored on Windows.

Mouse button device events are now using the same order as the MouseButton enum, and I also added hwheel device events for Windows.
2023-10-21 11:40:41 +04:00
Kirill Chibisov
0656c54c3b On Windows, fix IME APIs MT-safety
Execute the calls to the IME from the main thread.

Fixes #3123.
2023-10-21 11:40:41 +04:00
Kirill Chibisov
74fcf7f9c0 On Windows, fix RedrawRequested delivery
When calling `Window::request_redraw` from the `RedrawRequested`
handler the `RedrawWindow` won't result in `WM_PAINT` being delivered
due since user callback is run before `DefWindowProcW` is called.

Track whether the user called `Window::request_redraw` and ask for
`RedrawWindow` after running the said function during `WM_PAINT`
handling.

Fixes #3150.
2023-10-21 11:40:41 +04:00
Diggory Hardy
0bc8f5e33a Implement Ord/PartialOrd for ModifiersState 2023-10-21 11:40:41 +04:00
Xiaopeng Li
f6cc6c1472 On Windows, fix invalid hmonitor panic
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2023-10-21 11:40:41 +04:00
Arend van Beelen jr
20384d2f02 On iOS, add configuration for status bar style
Co-authored-by: Mads Marquart <mads@marquart.dk>
2023-10-21 11:40:41 +04:00
Kirill Chibisov
cdee616812 On macOS, fix tabGroup misuse
The property is marked as `Weak`, however we used strong `Id`.

Links: https://github.com/alacritty/alacritty/issues/7249
2023-10-21 11:40:41 +04:00
Kirill Chibisov
f58fb69446 Remove garbage from README
The docs are in the src/lib.rs anyway and are present on docs.rs.
2023-10-21 11:40:41 +04:00
Diggory Hardy
6b445219c1 Revise Key and KeyCode enums
Split `Key` into clear categories, like `Named`, `Dead`, Character`, `Unidentified`
removing the `#[non_exhaustive]` from the `Key` itself.

Similar action was done for the `KeyCode`.

Fixes: #2995
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2023-10-21 11:40:41 +04:00
Kirill Chibisov
18b8569161 Ensure that DISPLAY vars are non-empty before using
It's common to disable Wayland by `WAYLAND_DISPLAY= <application>`.
2023-10-21 11:40:41 +04:00
Kirill Chibisov
08b0464ac3 Fix examples not render on Wayland
The `rwh_05` feature was not enabled.

Fixes: e41fac825c (Update to new raw-window-handle strategy)
2023-10-21 11:40:41 +04:00
YouKnow
df2f5adfba On Windows, fix CursorEntered/CursorLeft not sent during mouse grab
Fixes #3153.
2023-10-21 11:40:41 +04:00
Kirill Chibisov
99f86d729f On macOS, fix globe key triggering assertion
Sometimes FlagsChanged events don't carry any KeyCode information, thus
we can't create a synthetic presses events for them.

However in such cases, modifiers information is still accurate, thus
propagate it.

Fixes #2872.
2023-10-21 11:40:41 +04:00
Kirill Chibisov
d06deeecf6 Make WindowBuilder Send + Sync
Window builder is always accessed by winit on the thread event loop
is on, thus it's safe to mark the data it gets as `Send + Sync`.
Each unsafe object is marked individually as `Send + Sync` instead
of just implementing `Send` and `Sync` for the whole builder.
2023-10-21 11:40:41 +04:00
Kirill Chibisov
e6d2fd7287 Remove resolved deny.toml entries 2023-10-21 11:40:41 +04:00
Marijn Suijten
f2edd23542 Upgrade to ndk 0.8, ndk-sys 0.5 + android-activity 0.5 releases
Fixes #2905.
Co-authored-by: Robert Bragg <robert@sixbynine.org>
2023-10-21 11:40:41 +04:00
daxpedda
70e6ddd210 Web Async Rework (#3082) 2023-10-21 11:40:41 +04:00
Kirill Chibisov
f3fb27c17b Add a note on Window::request_redraw on Windows
Fixing this could require a massive rework to how redraw is handled
on windows to the point of removing `WM_PAINT`, since it's not reliable
by any means for our use case.

For now at least document that the API is broken. It was broken like
that for a long while.
2023-10-21 11:40:41 +04:00
Kirill Chibisov
75b463a368 Implement AsFd/AsRawFd for EventLoop<T>
This should help other crates to integrate winit's event loop into
their bigger event loop without adding an extra thread.
2023-10-21 11:40:41 +04:00
John Nunley
ea8604e175 Fix potentially unaligned references in X11 device
Fixes #3125
Signed-off-by: John Nunley <dev@notgull.net>
2023-10-21 11:40:41 +04:00
Kirill Chibisov
b1bd0f77fb Update SCTK to 0.18.0
The update is pretty minor, however we support now
`WindowEvent::Occluded` when xdg-shell v6 is available.

It also adds support for `Window::show_window_menu`.

Fixes #2927.
2023-10-21 11:40:41 +04:00
Kirill Chibisov
1fded249d0 Fix ndk deps versions 2023-10-21 11:40:41 +04:00
John Nunley
349a3e7b8c Update to new raw-window-handle strategy
Signed-off-by: John Nunley <dev@notgull.net>
Co-authored-by: TornaxO7 <tornax@proton.me>
2023-10-21 11:40:41 +04:00
Ryan Hileman
f2d277e599 feat: Implement set_cursor_hittest for X11 2023-10-21 11:40:41 +04:00
Kirill Chibisov
8d5d612456 Fix CHANGELOG entry for Event::MemoryWarning
While the changelog entries for beta releases doesn't really matter. The
change wasn't marked as breaking, while it is.

Fixes: 93f1000a0 (Add Occluded and MemoryWarning events for iOS/Android)
2023-10-21 11:40:41 +04:00
François
5788319632 Add Occluded and MemoryWarning events for iOS/Android
Hook `Occluded` event to foreground/background evens on iOS.

This commit also enabled the `MemoryWarning` event, since it's
emitted from the windowing system.

Co-authored-by: Dusty DeWeese <dustin.deweese@gmail.com>
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2023-10-21 11:40:41 +04:00
YouKnow
976023bfc0 Add Window::show_window_menu
Add a method to request a system menu. The implementation
is provided only on Windows for now.

Co-authored-by: daxpedda <daxpedda@gmail.com>
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2023-10-21 11:40:41 +04:00
daxpedda
0f9b95814e Fix reset to Poll after the event loop starts 2023-10-21 11:40:41 +04:00
baneyue
112dcc808a On Wayland, fix MonitorHandle position 2023-10-21 11:40:41 +04:00
Kirill Chibisov
4a381fb1db Remove obsolete docs about wayland CSD env variable
The env variable was removed a while ago, yet it was still present in
the user docs.
2023-10-21 11:40:41 +04:00
daxpedda
8339ddf368 Web: fix ControlFlow::WaitUntil to never wake up **before** the given time (#3133) 2023-10-21 11:40:41 +04:00
Dmitry Sharshakov
b41f01c990 Add Window::set_blur
Allow clients to request blur behind their window, implemented on
Wayland for now.
2023-10-21 11:40:41 +04:00
daxpedda
570f3101e5 Web: remove unnecessary usage of once_cell::unsync::Lazy (#3134) 2023-10-21 11:40:41 +04:00
daxpedda
3923c59fd8 Update Clippy to v1.73 (#3135) 2023-10-21 11:40:41 +04:00
epimeletes
75ae402a24 Rename run_ondemand to run_on_demand 2023-10-21 11:40:41 +04:00
Fredrik Fornwall
4385c17cbb Make DeviceId contain device id's on Android 2023-10-21 11:40:41 +04:00
Mads Marquart
3af256260e Link to areweguiyet.com and arewegameyet.rs for extra deps 2023-10-21 11:40:41 +04:00
Mads Marquart
d9363219e1 X11: Add #[deny(unsafe_op_in_unsafe_fn)] (#3121)
* X11: Add #[deny(unsafe_op_in_unsafe_fn)]

* Enable #![deny(unsafe_op_in_unsafe_fn)] everywhere
2023-10-21 11:40:41 +04:00
Mads Marquart
a52a6d47ca Windows: Add #[deny(unsafe_op_in_unsafe_fn)] (#3070) 2023-10-21 11:40:41 +04:00
Mads Marquart
ec83de3938 Bump version on master (#3119)
This commit does not represent a release and only synchronizes CHANGELOG from the latest release.
2023-10-21 11:40:41 +04:00
Neil Macneale V
43d6eac871 Fix transparent windows on X11 2023-10-21 11:40:41 +04:00
Kirill Chibisov
1f101b2654 Remove DeviceEvent::Text event
The event is never constructed inside the winit.
2023-10-21 11:40:41 +04:00
lucasmerlin
709929fcab Pass force on touch events on android 2023-10-21 11:40:41 +04:00
daxpedda
220a2d32d5 Make ControlFlow::Wait the default (#3106) 2023-10-21 11:40:41 +04:00
Kirill Chibisov
c5cef46060 Remove old docs about EventLoop::run 2023-10-21 11:40:41 +04:00
Pavel Strakhov
367a2ae057 On X11, fix WaitUntil and Poll behavior
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2023-10-21 11:40:41 +04:00
StarStarJ
e038597e81 Implement PartialOrd and Ord for MouseButton 2023-10-21 11:40:41 +04:00
Kirill Chibisov
27cd20739d Correct Wayland section in dpi docs
Fixes #3100.
2023-10-21 11:40:41 +04:00
daxpedda
8d9fd3d3d6 Install cargo-apk with the stable toolchain 2023-10-21 11:40:41 +04:00
daxpedda
56427e47a7 Ignore foreign-types* duplicate deps on macOS
The dependency is duplicated due to examples, yet we still need to
exclude checking it.

Fixes #3093.
2023-10-21 11:40:41 +04:00
daxpedda
c744b9aea5 Rename PollType to PollStrategy (#3089) 2023-10-21 11:40:41 +04:00
John Nunley
ef9ed71f1b Add an MSRV policy to the README (#3046) 2023-10-21 11:40:41 +04:00
daxpedda
dda8053bd3 Add Window.requestIdleCallback() support (#3084) 2023-10-21 11:40:41 +04:00
Fredrik Fornwall
779212da33 Correct set_exit() -> exit() in the changelog (#3088) 2023-10-21 11:40:41 +04:00
John Nunley
28552c9cc1 Revert select_xkb_events to its previous impl
The new implementation of select_xkb_events apparently misconfigures
the server. This commit does a temporary fix by just reverting it to its
previous implementation.

This is temporary until I can figure out what Xlib is doing behind the
scenes or until I read xkbproto.pdf.

Fixes: #3079
Signed-off-by: John Nunley <dev@notgull.net>
2023-10-21 11:40:41 +04:00
daxpedda
48647b506f Move ControlFlow to EventLoopWindowTarget
Fixes #3042.
2023-10-21 11:40:41 +04:00
John Nunley
42243ce288 Allow the user to force X11 under Wayland
Use forced backend over the env variables. 

Signed-off-by: John Nunley <dev@notgull.net>
Fixes: #3057
2023-10-21 11:40:41 +04:00
daxpedda
84d9bfd59e Remove T from EventLoopTargetWindow (#3081)
Co-authored-by: nerditation <12248559+nerditation@users.noreply.github.com>
2023-10-21 11:40:41 +04:00
Kirill Chibisov
cd5c1fb724 Mark startup_notify unsafe functions as safe
They are safe, since they use the rust `std::env` stuff. Making them
safe lets downstream to determine that `std::env` is used and not the
`libc` env manipulation routines, which are unsafe.
2023-10-21 11:40:41 +04:00
Mads Marquart
8455f3415e Slightly reduce number of cfgs (#3071)
* Make Linux platforms less dependent on the root monitor handle

* Add various functions to the Wayland platform to reduce cfgs

* Don't use a cfg in listen_device_events

* Don't use a cfg in set_content_protected

* Fix instance of a target_os cfg
2023-10-21 11:40:41 +04:00
Kirill Chibisov
c59d6bc809 On Wayland, fix TouchPhase::Canceled sent for Move
Fixes #3035.
2023-10-21 11:40:41 +04:00
Mads Marquart
4681133eca Make EventLoopWindowTarget independent of the user type on Orbital (#3055) 2023-10-21 11:40:41 +04:00
Mads Marquart
b278aa859f Ensure that winit initializes NSApplication (#3069) 2023-10-21 11:40:41 +04:00
Mads Marquart
ee4ec43cf3 Fix missing quote (#3068) 2023-10-21 11:40:41 +04:00
John Nunley
25b629f117 Implement X11 extensions using x11rb instead of Xlib
Removes Xlib code by replacing it with the x11rb equivalent,
the commit handles xrandr, xinput, xinput2, and xkb.

Signed-off-by: John Nunley <dev@notgull.net>
2023-10-21 11:40:41 +04:00
daxpedda
4b30f9ce22 Web: Fullscreen Overhaul (#3063) 2023-10-21 11:40:41 +04:00
daxpedda
2428224c09 Enable event propagation (#3062) 2023-10-21 11:40:41 +04:00
Mads Marquart
2d9b852a95 Fix macOS deminiaturize (#3054) 2023-10-21 11:40:41 +04:00
Kirill Chibisov
246d53d5a1 Lock the cargo-apk deps on CI 2023-10-21 11:40:41 +04:00
Mads Marquart
865afd22be Make iOS fully thread safe (#3045)
* macOS & iOS: Refactor EventWrapper

* macOS & iOS: Make EventLoopWindowTarget independent of the user event

* iOS: Use MainThreadMarker instead of marking functions unsafe

* Make iOS thread safe
2023-10-21 11:40:41 +04:00
Mads Marquart
05130cb329 Improve CI caching, and give each job names
This improves CI performance by around 20% (down from 10 to 8 minutes).
2023-10-21 11:40:41 +04:00
daxpedda
a1a6f7baf9 Move Event::RedrawRequested to WindowEvent (#3049) 2023-10-21 11:40:41 +04:00
daxpedda
f160a6003c On Web, never return a MonitorHandle (#3051) 2023-10-21 11:40:41 +04:00
daxpedda
a24d092fa1 Use setTimeout() trick instead of Window.requestIdleCallback() (#3044) 2023-10-21 11:40:41 +04:00
Mads Marquart
a8a0462c0d Use frame instead of visibleRect (#3043) 2023-10-21 11:40:41 +04:00
Mads Marquart
647c320ca7 Fix recent CI failures (#3041)
* Fix new clippy lints

* Fix nightly documentation warnings
2023-10-21 11:40:41 +04:00
StarStarJ
5144337253 Implement PartialOrd/Ord for KeyCode/NativeKeyCode 2023-10-21 11:40:41 +04:00
Kirill Chibisov
7f1aaa652d Winit version 0.29.1-beta
Cargo automatically pulls pre-releases, so bump semver for each
pre-release as well, since those can't be resolved automatically.

We don't do betas for the patch bumps, so it should work just fine.
2023-08-16 15:57:50 +04:00
Kirill Chibisov
00b5de0a68 Winit version 0.29.0-beta.1 2023-08-16 12:40:18 +04:00
Kirill Chibisov
80d1e49354 Use beta versions of android crates 2023-08-16 12:39:57 +04:00
Kirill Chibisov
07dd45f8e3 Bump MSRV to 1.65 2023-08-16 12:39:56 +04:00
Mads Marquart
4e6ce00ec5 Improve macOS/iOS/Web thread safety
Co-authored-by: daxpedda <daxpedda@gmail.com>
2023-08-15 13:10:02 +04:00
Kirill Chibisov
65c2482d74 Pin android-activity git dependency 2023-08-15 13:10:02 +04:00
Kirill Chibisov
ba2bfd064f Reexport raw-window-handle in window module
We use raw-window-handle extensive in the public API as well as we
force the users to use it to get some essential data for interop, thus
reexport it.

Fixes: #2913.
2023-08-15 13:10:02 +04:00
Kirill Chibisov
08ad3f19e2 Propagate error from EventLoop creation
Inner panics could make it hard to trouble shoot the issues and for some
users it's not desirable.

The inner panics were left only when they are used to `assert!` during
development.

This reverts commit 9f91bc413fe20618bd7090829832bb074aab15c3 which
reverted the original patch which was merged without a proper review.

Fixes: #500.
2023-08-15 13:10:02 +04:00
lucasmerlin
e3fbfd6792 Fix touch force for Apple Pencil 2023-08-15 13:10:02 +04:00
John Nunley
c40af0062b Add a way to embed the X11 window into another
Signed-off-by: John Nunley <dev@notgull.net>
Tested-by: Kirill Chibisov <contact@kchibisov.com>
2023-08-15 13:10:02 +04:00
Fredrik Fornwall
511bf53889 iOS: Use NSOperatingSystemVersion from icrate (#3019) 2023-08-15 13:10:02 +04:00
Robert Bragg
7451c4b88c Android: Support unicode character mapping + dead keys
Up until now the Android backend has been directly mapping key codes
which essentially just represent the "physical" cap of the key (quoted
since this also related to virtual keyboards).

Since we didn't account for any meta keys either it meant the backend
only supported a 1:1 mapping from key codes, which only covers a tiny
subset of characters. For example you couldn't type a colon since
there's no keycode for that and we didn't try and map Shift+Semicolon
into a colon character.

This has been tricky to support because the `NativeActivity` class doesn't
have direct access to the Java `KeyEvent` object which exposes a more
convenient `getUnicodeChar` API.

It is now possible to query a `KeyCharcterMap` for the device associated
with a `KeyEvent` via the `AndroidApp::device_key_character_map` API
which provides a binding to the SDK `KeyCharacterMap` API in Java:

 https://developer.android.com/reference/android/view/KeyCharacterMap

This is effectively what `getUnicodeChar` is implemented based on and is
a bit more general purpose.

`KeyCharacterMap` lets us map a key_code + meta_state from a `KeyEvent`
into either a unicode character or dead key accent that can be combined
with the following key. This mapping is done based on the user's chosen
layout for the keyboard.

To enable support for key character maps the
`AndroidApp::input_events()` API was replaced by
`AndroidApp::input_events_iter()` which returns a (lending) iterator for
events. This was changed because the previous design made it difficult
to allow other AndroidApp APIs to be used while iterating events (mainly
because AndroidApp held a lock over the backend during iteration)
2023-08-15 13:10:02 +04:00
Kirill Chibisov
42ecef7b31 Fix event loop not waking up due to repeat source
Force the wake up from the repeat source as well.

Fixes: cad327755 (On Wayland, reduce amount of spurious wakeups)
2023-08-15 13:10:02 +04:00
Kirill Chibisov
5d9ce7f5f4 On X11, set visual_id in raw-window-handle
Fixes #2681.
2023-08-15 13:10:02 +04:00
Kirill Chibisov
ef5b71d658 Revert "Propagate error from EventLoop creation" (#3010)
This reverts commit ed26dd58fd.
The patched was merged with a review by accident.
2023-08-15 13:10:02 +04:00
Kirill Chibisov
4ab36f336c Propagate error from EventLoop creation
Inner panics could make it hard to trouble shoot the issues and for some
users ints not desirable.

The inner panics were left only when they are used to `assert!` during
development.
2023-08-15 13:10:02 +04:00
John Nunley
2791cbd65e Make with_x11_visual take ID instead of a pointer
At the moment, the with_x11_visual function takes a pointer and
immediately dereferences it to get the visual info inside. As it is safe
to pass a null pointer to this function, it is unsound. This commit
replaces the pointer parameter with a visual ID, and then uses that ID
to look up the actual visual under
the X11 setup. As this is what was already practically happening before,
this change shouldn't cause any performance downgrades.

This is a breaking change, but it's done in the name of soundness so it
should be okay. It should be trivial for end users to accommodate it,
as it's just a matter of getting the visual ID from the pointer to the
visual before passing it in.

Signed-off-by: John Nunley <dev@notgull.net>
2023-08-15 13:10:02 +04:00
Kirill Chibisov
03bf83f45e Remove 'static requirement on run
There's no need to force the static on the users, given that internally
some backends were not using static in the first place.

Co-authored-by: daxpedda <daxpedda@gmail.com>
2023-08-15 13:10:02 +04:00
Kirill Chibisov
02870202cb On Wayland, reduce amount of spurious wakeups
Mark it as breaking, since some clients relied on that behavior, simply
because dispatching clients queue always woke up a winit, meaning that
they won't be able to use user events for this sake.
2023-08-15 13:10:02 +04:00
Mads Marquart
c268922def Remove functionality already exposed through raw-window-handle
Nothing changed from the user point of view, other than they should
use the `raw-window-handle`, which is objectively better, given that
it reduces the amount of `cfg` guards in downstream code.
2023-08-15 13:10:02 +04:00
John Nunley
61b921c466 Increase test coverage for generic modules 2023-08-15 13:10:02 +04:00
dAxpeDDa
794d0c1f73 On Web, use requestAnimationFrame for RedrawRequested 2023-08-15 13:10:02 +04:00
Kirill Chibisov
8ce58c7053 On Wayland, use frame callbacks to throttle RedrawRequested
Throttle RedrawRequested events by the frame callbacks, so the users
could render at the display refresh rate.
2023-08-15 13:10:02 +04:00
Kirill Chibisov
cff9b01052 Add Window::on_present_notify to ack about drawing
That's a way to communicate to winit that you'll present to the window.
While it's a no-op for now, it'll be used to throttle drawing.
2023-08-15 13:10:02 +04:00
Diggory Hardy
7e9dc147d8 Export smol_str and impl Ord for Key
Fixes #2996.
2023-08-15 13:10:02 +04:00
Mads Marquart
d7827b36d3 Update icrate to v0.0.4 (#2992) 2023-08-15 13:10:02 +04:00
Marijn Suijten
5b90a4e194 On X11, remove the now-unrefrenced events.rs source file
#2662 renamed `VirtualKeyCode` to `Key` yet references to the former
type still exist in `src/platform_impl/linux/x11/events.rs`.  As it
turns out the `mod events;` in `x11/mod.rs` was removed in the same PR,
but the file accidentally stuck around without being referenced anywhere
else.
2023-08-15 13:10:02 +04:00
Kirill Chibisov
281077a0d8 Remove lifetime from the Event
Lifetimes don't work nicely when dealing with multithreaded environments
in the current design of the existing winit's event handling model, so
remove it in favor of `InnerSizeWriter` fences passed to client, so they
could try to update the size.

Fixes #1387.
2023-08-15 13:10:02 +04:00
Tobias Hunger
d21395bb3f On Windows, keep window maximized when setting size bounds (#2899) 2023-08-15 13:10:02 +04:00
Géraud-Loup
f69616ac2c On Windows, add option to customize window class name (#2978) 2023-08-15 13:10:02 +04:00
Mads Marquart
645b1ff00f Update objc2 version (#2936)
* Upgrade to objc2 v0.4.0 and icrate v0.0.3

* Fix `touchBar` method

* Use ClassType::alloc

* Use #[method_id(...)] functionality in declare_class!
2023-08-15 13:10:02 +04:00
Robert Bragg
3925281652 Remove RedrawEventsCleared + MainEventsCleared, and added AboutToWait
The idea that redraw events are dispatched with a specific ordering
that makes it possible to specifically report when we have finished
dispatching redraw events isn't portable and the way in which we
dispatched RedrawEventsCleared was inconsistent across backends.

More generally speaking, there is no inherent relationship between
redrawing and event loop iterations. An event loop may wake up at any
frequency depending on what sources of input events are being listened
to but redrawing is generally throttled and in some way synchronized
with the display frequency.

Similarly there's no inherent relationship between a single event loop
iteration and the dispatching of any specific kind of "main" event.

An event loop wakes up when there are events to read (e.g. input
events or responses from a display server / compositor) and goes back
to waiting when there's nothing else to read.

There isn't really a special kind of "main" event that is dispatched
in order with respect to other events.

What we can do more portably is emit an event when the event loop
is about to block and wait for new events.

In practice this is very similar to how MainEventsCleared was
implemented except it wasn't the very last event previously since
redraw events could be dispatched afterwards.

The main backend where we don't strictly know when we're going to
wait for events is Web (since the real event loop is internal to
the browser). For now we emulate AboutToWait on Web similar to how
MainEventsCleared was dispatched.

In practice most applications almost certainly shouldn't care about
AboutToWait because the frequency of event loop iterations is
essentially arbitrary and usually irrelevant.
2023-08-15 13:10:02 +04:00
Robert Bragg
3bf0fa9ec8 Rename LoopDestroyed to LoopExiting
Considering the possibility of re-running an event loop via run_ondemand
then it's more correct to say that the loop is about to exit without
assuming it's going to be destroyed.
2023-08-15 13:10:02 +04:00
François
7de2bc7ae6 iOS: Always set timer when polling to avoid slow waking (#2979)
* Always set timer when polling to avoid slow waking

* add comment and changelog

---------

Co-authored-by: Dusty DeWeese <dustin.deweese@gmail.com>
Co-authored-by: Mads Marquart <mads@marquart.dk>
2023-08-15 13:10:02 +04:00
Robert Bragg
3f44eb1fd9 Add timeout argument to pump_events
This renames all internal implementations of pump_events_with_timeout
to pump_events and makes them public.

Since all platforms that support pump_events support timeouts there's
no need to have a separate API.
2023-08-15 13:10:02 +04:00
Robert Bragg
456c735bfe Windows: implement pump_events_with_timeout internally 2023-08-15 13:10:02 +04:00
Robert Bragg
973e6ad400 MacOS: implement pump_events_with_timeout internally
This layers pump_events on a pump_events_with_timeout API, like we have
for Linux and Android.

This is just an internal implementation detail for now but we could
consider making pump_events_with_timeout public, or just making it so
that pump_events() takes the timeout argument.
2023-08-15 13:10:02 +04:00
Robert Bragg
07652c76fb window_ondemand: wait for Destroyed event before exiting app
Considering the strict requirement that applications can't keep windows
across run_ondemand calls, this tries to make the window_ondemand example
explicitly wait for its Window to be destroyed before exiting each
run_ondemand iteration.

This updates the example to only `.set_exit()` after it gets a
`Destroyed` event after the Window has been dropped.

On Windows this works to ensure the Window is destroyed before the
example waits for 5 seconds.

Unfortunately though:
1. The Wayland backend doesn't emit `Destroyed` events for windows
2. The macOS backend emits `Destroyed` events before the window is
   really destroyed.

and so the example isn't currently portable.
2023-08-15 13:10:02 +04:00
Robert Bragg
7d93c34e42 Linux: Sync with server/compositor before exiting run_ondemand
Although we document that applications can't keep windows between
separate run_ondemand calls it's possible that the application has only
just dropped their windows and we need to flush these requests to the
server/compositor.

This fixes the window_ondemand example - by ensuring the window from
the first loop really is destroyed before waiting for 5 seconds
and starting the second loop.
2023-08-15 13:10:02 +04:00
Robert Bragg
a2e1a0ac19 Update CHANGELOG.md 2023-08-15 13:10:02 +04:00
Robert Bragg
f1a64b3155 Add examples/window_ondemand
A minimal example that shows an application running the event loop more
than once via `run_ondemand`

There is a 5 second delay between each run to help highlight problems
with destroying the window from the first loop.
2023-08-15 13:10:02 +04:00
Robert Bragg
f8ffa314d0 Add examples/window_pump_events
A minimal example of an application based on an external event loop that
calls `pump_events` for each iteration of the external loop.
2023-08-15 13:10:02 +04:00
Robert Bragg
e28974bc04 Re-work event loop run() API so it can return a Result
This re-works the portable `run()` API that consumes the `EventLoop` and
runs the loop on the calling thread until the app exits.

This can be supported across _all_ platforms and compared to the
previous `run() -> !` API is now able to return a `Result` status on all
platforms except iOS and Web. Fixes: #2709

By moving away from `run() -> !` we stop calling `std::process::exit()`
internally as a means to kill the process without returning which means
it's possible to return an exit status and applications can return from
their `main()` function normally.

This also fixes Android support where an Activity runs in a thread but
we can't assume to have full ownership of the process (other services
could be running in separate threads).

Additionally all examples have generally been updated so that `main()`
returns a `Result` from `run()`

Fixes: #2709
2023-08-15 13:10:02 +04:00
Robert Bragg
93f5f1ac3c Remove EventLoopExtRunReturn 2023-08-15 13:10:02 +04:00
Robert Bragg
a02c680a87 Linux: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand
Wayland:

I found the calloop abstraction a little awkward to work with while I was
trying to understand why there was surprising workaround code in the wayland
backend for manually dispatching pending events.

Investigating this further it looks like there may currently be several issues
with the calloop WaylandSource (with how prepare_read is used and with (not)
flushing writes before polling)

Considering the current minimal needs for polling in all winit backends I do
personally tend to think it would be simpler to just own the responsibility for
polling more directly, so the logic for wayland-client `prepare_read` wouldn't
be in a separate crate (and in this current situation would also be easier to fix)

I've tried to maintain the status quo with calloop + workarounds.

X11:

I found that the recent changes (4ac2006cbc) to port the X11 backend
from mio to calloop lost the ability to check for pending events before
needing to poll/dispatch. (The `has_pending` state being queried
before dispatching() was based on state that was filled in during
dispatching)

As part of the rebase this re-introduces the PeekableReceiver and
WakeSender which are small utilities on top of
`std::sync::mpsc::channel()`. This adds a calloop `PingSource`
so we can use a `Ping` as a generic event loop waker.

For taking into account false positive wake ups the X11 source now
tracks when the file descriptor is readable so after we poll via
calloop we can then specifically check if there are new X11 events
or pending redraw/user events when deciding whether to skip the
event loop iteration.
2023-08-15 13:10:02 +04:00
Robert Bragg
6bb62d0b13 MacOS: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand
The implementation of `pump_events` essentially works by hooking into the
`RunLoopObserver` and requesting that the app should be stopped the next time
that the `RunLoop` prepares to wait for new events.

Originally I had thought I would poke the `CFRunLoop` for the app directly and
I was originally going to implement `pump_events` based on a timeout which I'd
seen SDL doing.

I found that `[NSApp run]` wasn't actually being stopped by asking the RunLoop
to stop directly and inferred that `NSApp run` will actually catch this and
re-start the loop.

Hooking into the observer and calling `[NSApp stop]` actually seems like a
better solution that doesn't need a hacky constant timeout.

The end result is quite similar to what happens with existing apps that
call `run_return` inside an external loop and cause the loop to exit for
each iteration (that also results in the `NSApp` stopping each
iteration).
2023-08-15 13:10:02 +04:00
Robert Bragg
fae4cbd2aa Windows: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand
A surprising amount of work was required to enable these extensions
on Windows.

I had originally assumed that pump_events was going to be very similar
to run except would use PeekMessageW instead of GetMessageW to avoid
blocking the external loop but I found the Windows backend broke
several assumptions I had.

Overall I think these changes can hopefully be considered a quite a
significant simplification (I think it's a net deletion of a fair amount
of code) and I think it also helps bring it into slightly closer alignment
with other backends too

Key changes:
- I have removed the `wait_thread` that was a fairly fiddly way of handling
  `ControlFlow::WaitUntil` timeouts in favor of using `SetTimer` which works
  with the same messages picked up by `GetMessage` and `PeekMessage`.
- I have removed the ordering guarantees between `MainEventsCleared`,
  `RedrawRequested` and `RedrawEventsCleared` events due to the complexity in
  maintaining this artificial ordering, which is already not supported
  consistently across backends anyway (in particular this ordering already
  isn't compatible with how MacOS / iOS work).
- `RedrawRequested` events are now directly dispatched via `WM_PAINT` messages
  - comparable to how `RedrawRequested` is dispatched via `drawRect` in the
  MacOS backend.
- I have re-worked how `NewEvents`, `MainEventsCleared`, and `RedrawEventsCleared`
  get dispatched to be more in line with the MacOS backend and also more in line
  with how we have recently discussed defining them for all platforms.

  `NewEvents` is conceptually delivered when the event loop "wakes up" and
  `MainEventsCleared` gets dispatched when the event loop is about to ask the
  OS to wait for new events.

  This is a more portable model, and is already how these events work in the
  MacOS backend.

  `RedrawEventsCleared` are just delivered after `MainEventsCleared` but this
  event no longer has a useful meaning.

Probably the most controversial thing here is that this "breaks" the ordering
rules for redraw event handling, but since my changes interacted with how the
order is maintained I was very reluctant to figure out how to continue
maintaining something that we have recently been discussing changing:

https://github.com/rust-windowing/winit/issues/2640.

Additionally, since the MacOS backend already doesn't strictly maintain this
order it's somewhat academic to see this as a breakage if Winit applications
can't really rely on it already.

This updates the documentation for `request_redraw()` to reflect that we
no longer guarantee that `RedrawRequested` events must be dispatched
after `MainEventsCleared`.
2023-08-15 13:10:02 +04:00
Robert Bragg
7a954c7e08 Android: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand 2023-08-15 13:10:02 +04:00
Robert Bragg
164dce2b8a Add EventLoopExtPumpEvents and EventLoopExtRunOnDemand
This adds two new extensions for running a Winit event loop which will
replace `EventLoopExtRunReturn`

The `run_return` API is trying to solve multiple problems and address
multiple, unrelated, use cases but in doing so it is not succeeding
at addressing any of them fully.

The notable use cases we have are:
1. Applications want to be able to implement their own external
   event loop and call some Winit API to poll / pump events, once
   per iteration of their own loop, without blocking the outer,
   external loop. Addressing #2706
2. Applications want to be able to re-run separate instantiations
   of some Winit-based GUI and want to allow the event loop to exit with
   a status, and then later be able to run the loop again for a new
   instantiation of their GUI. Addressing #2431

It's very notable that these use cases can't be supported across
all platforms and so they are extensions, similar to
`EventLoopExtRunReturn`

The intention is to support these extensions on:
- Windows
- Linux (X11 + Wayland)
- macOS
- Android

These extensions aren't compatible with Web or iOS though.

Each method of running the loop will behave consistently in terms of how
`NewEvents(Init)`, `Resumed` and `LoopDestroyed` events are dispatched
(so portable application code wouldn't necessarily need to have any awareness
of which method of running the loop was being used)

Once all backends have support for these extensions then we can
remove `EventLoopExtRunReturn`

For simplicity, the extensions are documented with the assumption that
the above platforms will be supported.

This patch makes no functional change, it only introduces these new
extensions so we can then handle adding platform-specific backends
in separate pull requests, so the work can be landed in stages.
2023-08-15 13:10:02 +04:00
daxpedda
0ba4283c29 Correctly detect that we don't support Emscripten (#2971) 2023-08-15 13:10:02 +04:00
John Nunley
62b4ba8b50 Replace libc with rustix in some modules
Unfortunately this isn't a total removal, for two reasons:

- We still need "libc" for the Xlib XIM implementation, for locales.
- BSD requires libc to check for main-threadedness.

First one we can likely resolve in the near future, not so sure about
the second one without using some weird pthreads trick.
2023-08-15 13:10:02 +04:00
Venceslas Duet
afebe2e7d1 On Windows, add drag_resize_window method support (#2966) 2023-08-15 13:10:02 +04:00
Kirill Chibisov
0efcfaf5a9 Add platform::startup_notify for Wayland/X11
The utils in this module should help the users to activate the windows
they create, as well as manage activation tokens environment variables.

The API is essential for Wayland in the first place, since some
compositors may decide initial focus of the window based on whether
the activation token was during the window creation.

Fixes #2279.

Co-authored-by: John Nunley <jtnunley01@gmail.com>
2023-08-15 13:10:02 +04:00
Venceslas Duet
d86ce9de9f On Wayland, fix Window::is_decorated with CSD 2023-08-15 13:10:02 +04:00
Kirill Chibisov
7fa7cea700 On Wayland, make the CSD frame double click reliable
It was discovered that on GNOME the click sometimes being swallowed
by the mutter's `wl_pointer::enter/leave` sequences. This was happening
due to `xdg_toplevel::move` making the pointer to leave the surface.

To make handling of that more robust, we could start the
`xdg_toplevel::move` when the actual pointer motion is being performed.

Links: https://github.com/alacritty/alacritty/issues/7011
Links: https://gitlab.gnome.org/GNOME/mutter/-/issues/2669#note_1790825
2023-08-15 13:10:02 +04:00
Kirill Chibisov
36ebad3246 On macOS, add a way to query amount of tabbed windows
This should provide a way to iterate all the tabs and select the last
tab. The tab indicies are now zero based as any other sane index.

Follow-up-to: c5941d105f (add tabbing API)
2023-08-15 13:10:02 +04:00
Sam
912c45e9f7 On macOS, set that we prefer tabbing
Winit now supports tabbing identifiers, thus set that we prefer tabbing,
in particular it'll make windows tab when using the same tabbing identifiers,
which is desirable for the end users.
2023-08-15 13:10:02 +04:00
Kirill Chibisov
e2c71a4422 On macOS, move automatic tabbing setting to ELWT
Those are global for the application, so it's better to keep them
on EventLoopWindowTarget.
2023-08-15 13:10:02 +04:00
Kirill Chibisov
b50d9a0228 On macOS, add tabbing APIs
This should let the users control macOS tabbing and allow to create
windows in tab.

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
2023-08-15 13:10:02 +04:00
daxpedda
692f15c49f On Web, add WindowBuilderExtWebSys::with_append() (#2953) 2023-08-15 13:10:02 +04:00
daxpedda
5366694db2 Improve documentation of WindowBuilderExtWebSys methods (#2952) 2023-08-15 13:10:02 +04:00
John Nunley
7962271faa Replace parts of the Xlib backend with x11-rb 2023-08-15 13:10:02 +04:00
daxpedda
78b5f2feb8 On Web, remove Window::is_dark_mode() (#2951) 2023-08-15 13:10:02 +04:00
daxpedda
4baab2d93e Fix mentions of Wasm (#2950) 2023-08-15 13:10:02 +04:00
daxpedda
79385ecd1f On Web, implement and fix missing methods on Window(Builder) (#2949) 2023-08-15 13:10:02 +04:00
daxpedda
8d18043a3c Add Fullscreen API compatibility for Safari (#2948) 2023-08-15 13:10:02 +04:00
daxpedda
297c3f80eb On Web, cache commonly used values (#2947) 2023-08-15 13:10:02 +04:00
daxpedda
1d80005b91 Increase accuracy of various Web APIs (#2946) 2023-08-15 13:10:02 +04:00
daxpedda
fab0f62c5a Improve Web specific documentation for various APIs (#2941)
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2023-08-15 13:10:02 +04:00
Kirill Chibisov
d83188befd Rename Window::set_inner_size to Window::request_inner_size
Some systems could resize the window immediately and we'd rather
inform the users right away if that was the case, so they could
create e.g. EGLSurface without waiting for resize, which is really
important for Wayland.

Fixes #2868.
2023-08-15 13:10:02 +04:00
daxpedda
b9d89e97ed Fix touch location accuracy (#2944) 2023-08-15 13:10:02 +04:00
daxpedda
5fa4b8f003 On Web, implement WindowEvent::Occluded (#2940) 2023-08-15 13:10:02 +04:00
Imbris
7a4ce631bd On X11, avoid false positive key repeats
Instead of a single `bool` indicating that a key press has occured and
no key has been released since then, we store the scancode of the last
pressed key (if it is a key that repeats when held). This fixes a bug
where pressing a new key while one is already held down will be flagged
as a repeat even though it is obviously not a repeat.
2023-08-15 13:10:02 +04:00
Mads Marquart
8d5f82f0c0 Stop using &mut in Objective-C delegate methods (#2925)
* Make iOS declared classes not use &mut

* Prepare `init` methods for not having access to &mut self

* Prepare WinitWindow methods for not having access to &mut self

* Convert a bit of WinitView's to use interior mutability

* Convert a bit more of WinitView's to use interior mutability

* Convert the rest of WinitView to use interior mutability

* Use interior mutability instead of a Mutex for the CursorState

* Use interior mutability in WinitWindowDelegate
2023-08-15 13:10:02 +04:00
daxpedda
08fe32eac3 Don't unnecessarily clone canvas on Web (#2934) 2023-08-15 13:10:02 +04:00
daxpedda
1cddc96a0b Fix typos on Web (#2933) 2023-08-15 13:10:02 +04:00
Kirill Chibisov
84ef89eb1c Winit version 0.29.0-beta.0 2023-06-30 23:33:29 +04:00
268 changed files with 10056 additions and 11952 deletions

16
.github/CODEOWNERS vendored
View File

@@ -1,6 +1,12 @@
# Core maintainers:
# - @msiglreith
# - @kchibisov
# - @madsmtm
# - @maroider
# Android
/src/platform/android.rs @msiglreith @MarijnS95
/src/platform_impl/android @msiglreith @MarijnS95
/src/platform/android.rs @msiglreith
/src/platform_impl/android @msiglreith
# iOS
/src/platform/ios.rs @madsmtm
@@ -14,14 +20,14 @@
/src/platform_impl/linux/wayland @kchibisov
# X11
/src/platform/x11.rs @kchibisov @notgull
/src/platform_impl/linux/x11 @kchibisov @notgull
/src/platform/x11.rs @kchibisov
/src/platform_impl/linux/x11 @kchibisov
# macOS
/src/platform/macos.rs @madsmtm
/src/platform_impl/macos @madsmtm
# Web
# Web (no maintainer)
/src/platform/web.rs @daxpedda
/src/platform_impl/web @daxpedda

View File

@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
toolchain: [stable, nightly, '1.70.0']
toolchain: [stable, nightly, '1.65.0']
platform:
# 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, }
@@ -35,7 +35,7 @@ jobs:
- { name: 'Linux 64bit', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
- { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--features=android-native-activity', cmd: 'apk --' }
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
- { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, }
- { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, }
- { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, }
@@ -43,20 +43,11 @@ jobs:
- { name: 'web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
exclude:
# Android is tested on stable-3
- toolchain: '1.70.0'
- toolchain: '1.65.0'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
include:
- toolchain: '1.70.0'
- toolchain: '1.69.0'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
- toolchain: 'nightly'
platform: {
name: 'web Atomic',
target: wasm32-unknown-unknown,
os: ubuntu-latest,
options: '-Zbuild-std=panic_abort,std',
rustflags: '-Ctarget-feature=+atomics,+bulk-memory',
components: rust-src,
}
env:
# Set more verbose terminal output
@@ -64,7 +55,8 @@ jobs:
RUST_BACKTRACE: 1
# Faster compilation and error on warnings
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}'
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings'
RUSTDOCFLAGS: '--deny=warnings'
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
CMD: ${{ matrix.platform.cmd }}
@@ -117,21 +109,19 @@ jobs:
with:
toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
targets: ${{ matrix.platform.target }}
components: clippy, ${{ matrix.platform.components }}
components: clippy
- name: Check documentation
run: cargo doc --no-deps $OPTIONS --document-private-items
env:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }}'
- name: Build crate
run: cargo $CMD build -p winit $OPTIONS
run: cargo $CMD build $OPTIONS
- name: Build tests
if: >
!contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.70.0'
run: cargo $CMD test -p winit --no-run $OPTIONS
matrix.toolchain != '1.65.0'
run: cargo $CMD test --no-run $OPTIONS
- name: Run tests
if: >
@@ -139,7 +129,7 @@ jobs:
!contains(matrix.platform.target, 'ios') &&
!contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.70.0'
matrix.toolchain != '1.65.0'
run: cargo $CMD test $OPTIONS
- name: Lint with clippy
@@ -149,8 +139,8 @@ jobs:
- name: Build tests with serde enabled
if: >
!contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.70.0'
run: cargo $CMD test --no-run $OPTIONS -p winit --features serde
matrix.toolchain != '1.65.0'
run: cargo $CMD test --no-run $OPTIONS --features serde
- name: Run tests with serde enabled
if: >
@@ -158,15 +148,9 @@ jobs:
!contains(matrix.platform.target, 'ios') &&
!contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.70.0'
matrix.toolchain != '1.65.0'
run: cargo $CMD test $OPTIONS --features serde
- name: Check docs.rs documentation
if: matrix.toolchain == 'nightly'
run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
env:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs'
# See restore step above
- name: Save cache of cargo folder
uses: actions/cache/save@v3

View File

@@ -1,50 +0,0 @@
name: Docs
on:
push:
branches: [master]
jobs:
docs:
name: Documentation
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}winit
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- name: Run Rustdoc
env:
RUSTDOCFLAGS: --crate-version master --cfg=docsrs
run: |
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Fix permissions
run: |
chmod -c -R +rX "target/doc" | while read line; do
echo "::warning title=Invalid file permissions automatically fixed::$line"
done
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: target/doc
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View File

@@ -11,53 +11,6 @@ Unreleased` header.
# Unreleased
- On X11, fix swapped instance and general class names.
- **Breaking:** Removed unnecessary generic parameter `T` from `EventLoopWindowTarget`.
- On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example.
- **Breaking:** Remove `Window::set_cursor_icon`
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a `CursorIcon` or `CustomCursor`
- Add `CustomCursor`
- 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.
- On macOS, add services menu.
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
- On Web, fix setting cursor icon overriding cursor visibility.
- **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
- **Breaking:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available.
- **Breaking:** Bump MSRV from `1.65` to `1.70`.
- On Web, add the ability to toggle calling `Event.preventDefault()` on `Window`.
- **Breaking:** Remove `WindowAttributes::fullscreen()` and expose as field directly.
- **Breaking:** Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold static data.
- **Breaking:** No longer export `platform::x11::XNotSupported`.
- **Breaking:** Renamed `platform::x11::XWindowType` to `platform::x11::WindowType`.
- Add the `OwnedDisplayHandle` type for allowing safe display handle usage outside of trivial cases.
- **Breaking:** Rename `TouchpadMagnify` to `PinchGesture`, `SmartMagnify` to `DoubleTapGesture` and `TouchpadRotate` to `RotationGesture` to represent the action rather than the intent.
- on iOS, add detection support for `PinchGesture`, `DoubleTapGesture` and `RotationGesture`.
- on Windows: add `with_system_backdrop`, `with_border_color`, `with_title_background_color`, `with_title_text_color` and `with_corner_preference`
- On Windows, Remove `WS_CAPTION`, `WS_BORDER` and `WS_EX_WINDOWEDGE` styles for child windows without decorations.
- On Windows, fixed a race condition when sending an event through the loop proxy.
- **Breaking:** Removed `EventLoopError::AlreadyRunning`, which can't happen as it is already prevented by the type system.
- On Wayland, disable `Occluded` event handling.
- Added `EventLoop::builder`, which is intended to replace the (now deprecated) `EventLoopBuilder::new`.
- **Breaking:** Changed the signature of `EventLoop::with_user_event` to return a builder.
- **Breaking:** Removed `EventLoopBuilder::with_user_event`, the functionality is now available in `EventLoop::with_user_event`.
- Add `Window::builder`, which is intended to replace the (now deprecated) `WindowBuilder::new`.
- On X11, reload dpi on `_XSETTINGS_SETTINGS` update.
- On X11, fix deadlock when adjusting DPI and resizing at the same time.
- 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 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.
- **Breaking:** Move some types to the `winit-core` crate. Most types are
re-exported verbatim; however:
- `Event`, `WindowEvent` and `KeyEvent` now take a generic that `winit`
implements.
- `ActivationToken` and `InnerSizeWriter` expose new methods to make them
useful.
- `InnerSizeWriter::request_inner_size` returns an `InnerSizeIgnored` error
instead of an `ExternalError`.
# 0.29.10
- On Web, account for canvas being focused already before event loop starts.
@@ -113,7 +66,6 @@ Unreleased` header.
- 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

View File

@@ -20,7 +20,7 @@ your description of the issue as detailed as possible:
When making a code contribution to winit, before opening your pull request, please make sure that:
- your patch builds with Winit's minimal supported rust version - Rust 1.70.
- your patch builds with Winit's minimal supported rust version - Rust 1.65.
- you tested your modifications on all the platforms impacted, or if not possible, detail which platforms
were not tested, and what should be tested, so that a maintainer or another contributor can test them
- you updated any relevant documentation in winit

View File

@@ -1,16 +1,226 @@
[package]
name = "winit"
version = "0.29.10"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
edition = "2021"
keywords = ["windowing"]
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/rust-windowing/winit"
documentation = "https://docs.rs/winit"
categories = ["gui"]
rust-version = "1.65.0"
[package.metadata.docs.rs]
features = [
"rwh_04",
"rwh_05",
"rwh_06",
"serde",
# Enabled to get docs to compile
"android-native-activity",
]
default-target = "x86_64-unknown-linux-gnu"
# These are all tested in CI
targets = [
# Windows
"i686-pc-windows-msvc",
"x86_64-pc-windows-msvc",
# macOS
"x86_64-apple-darwin",
# Unix (X11 & Wayland)
"i686-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
# iOS
"x86_64-apple-ios",
# Android
"aarch64-linux-android",
# WebAssembly
"wasm32-unknown-unknown",
]
rustdoc-args = ["--cfg", "docsrs"]
[features]
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"]
wayland-dlopen = ["wayland-backend/dlopen"]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
android-native-activity = ["android-activity/native-activity"]
android-game-activity = ["android-activity/game-activity"]
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"]
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
[build-dependencies]
cfg_aliases = "0.1.1"
[dependencies]
bitflags = "2"
cursor-icon = "1.1.0"
log = "0.4"
mint = { version = "0.5.6", optional = true }
once_cell = "1.12"
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
serde = { version = "1", optional = true, features = ["serde_derive"] }
smol_str = "0.2.0"
[dev-dependencies]
image = { version = "0.24.0", default-features = false, features = ["png"] }
simple_logger = { version = "4.2.0", default_features = false }
winit = { path = ".", features = ["rwh_05"] }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
softbuffer = "0.3.0"
[target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.5.0"
ndk = { version = "0.8.0", default-features = false }
ndk-sys = "0.5.0"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
core-foundation = "0.9.3"
objc2 = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.23.1"
[target.'cfg(target_os = "macos")'.dependencies.icrate]
version = "0.0.4"
features = [
"dispatch",
"Foundation",
"Foundation_NSArray",
"Foundation_NSAttributedString",
"Foundation_NSMutableAttributedString",
"Foundation_NSData",
"Foundation_NSDictionary",
"Foundation_NSString",
"Foundation_NSProcessInfo",
"Foundation_NSThread",
"Foundation_NSNumber",
]
[target.'cfg(target_os = "ios")'.dependencies.icrate]
version = "0.0.4"
features = [
"dispatch",
"Foundation",
"Foundation_NSArray",
"Foundation_NSString",
"Foundation_NSProcessInfo",
"Foundation_NSThread",
"Foundation_NSSet",
]
[target.'cfg(target_os = "windows")'.dependencies]
unicode-segmentation = "1.7.1"
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.48"
features = [
"Win32_Devices_HumanInterfaceDevice",
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_Media",
"Win32_System_Com_StructuredStorage",
"Win32_System_Com",
"Win32_System_LibraryLoader",
"Win32_System_Ole",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_System_WindowsProgramming",
"Win32_UI_Accessibility",
"Win32_UI_Controls",
"Win32_UI_HiDpi",
"Win32_UI_Input_Ime",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Input_Pointer",
"Win32_UI_Input_Touch",
"Win32_UI_Shell",
"Win32_UI_TextServices",
"Win32_UI_WindowsAndMessaging",
]
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
ahash = { version = "0.8.3", features = ["no-rng"], optional = true }
bytemuck = { version = "1.13.1", default-features = false, optional = true }
calloop = "0.12.3"
libc = "0.2.64"
memmap2 = { version = "0.9.0", optional = true }
percent-encoding = { version = "2.0", optional = true }
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
sctk-adwaita = { version = "0.8.0", default_features = false, optional = true }
wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
wayland-client = { version = "0.31.1", optional = true }
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
x11-dl = { version = "2.18.5", optional = true }
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
xkbcommon-dl = "0.4.0"
[target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.42", default-features = false }
redox_syscall = "0.3"
[target.'cfg(target_family = "wasm")'.dependencies.web_sys]
package = "web-sys"
version = "0.3.64"
features = [
'AbortController',
'AbortSignal',
'console',
'CssStyleDeclaration',
'Document',
'DomRect',
'DomRectReadOnly',
'Element',
'Event',
'EventTarget',
'FocusEvent',
'HtmlCanvasElement',
'HtmlElement',
'IntersectionObserver',
'IntersectionObserverEntry',
'KeyboardEvent',
'MediaQueryList',
'MessageChannel',
'MessagePort',
'Node',
'PageTransitionEvent',
'PointerEvent',
'ResizeObserver',
'ResizeObserverBoxOptions',
'ResizeObserverEntry',
'ResizeObserverOptions',
'ResizeObserverSize',
'VisibilityState',
'Window',
'WheelEvent'
]
[target.'cfg(target_family = "wasm")'.dependencies]
atomic-waker = "1"
js-sys = "0.3.64"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-time = "0.2"
[target.'cfg(target_family = "wasm")'.dev-dependencies]
console_log = "1"
web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
[workspace]
members = [
"run-wasm",
"winit",
"winit-core"
]
resolver = "2"
[workspace.dependencies]
bitflags = "2"
cfg_aliases = "0.2.0"
cursor-icon = "1.1.0"
serde = { version = "1", features = ["serde_derive"] }
smol_str = "0.2.0"
web-time = "1"
winit-core = { path = "./winit-core", default-features = false, features = ["std"] }

View File

@@ -106,7 +106,6 @@ If your PR makes notable changes to Winit's features, please update this section
- **Cursor locking**: Locking the cursor inside the window so it cannot move.
- **Cursor confining**: Confining the cursor to the window bounds so it cannot leave them.
- **Cursor icon**: Changing the cursor icon or hiding the cursor.
- **Cursor image**: Changing the cursor to your own image.
- **Cursor hittest**: Handle or ignore mouse events for a window.
- **Touch events**: Single-touch events.
- **Touch pressure**: Touch events contain information about the amount of force being applied.
@@ -126,11 +125,6 @@ If your PR makes notable changes to Winit's features, please update this section
* Setting a menu bar
* `WS_EX_NOREDIRECTIONBITMAP` support
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme
* Changing a system-drawn backdrop
* Setting the window border color
* Setting the title bar background color
* Setting the title color
* Setting the corner rounding preference
### macOS
* Window activation policy
@@ -212,7 +206,6 @@ Legend:
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |❌ |
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|Cursor image |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|Cursor hittest |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A** |
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |

View File

@@ -2,8 +2,6 @@
[![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit)
[![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit)
[![Master Docs](https://img.shields.io/github/actions/workflow/status/rust-windowing/winit/docs.yml?branch=master&label=master%20docs
)](https://rust-windowing.github.io/winit/winit/index.html)
[![CI Status](https://github.com/rust-windowing/winit/workflows/CI/badge.svg)](https://github.com/rust-windowing/winit/actions)
```toml
@@ -19,9 +17,10 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
## 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 any of these:
The maintainers have a meeting every friday at UTC 14. The meeting notes can be found [here](https://hackmd.io/@winit-meetings).
[![Matrix](https://img.shields.io/badge/Matrix-%23rust--windowing%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#rust-windowing:matrix.org)
[![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit)
## Usage
@@ -43,7 +42,7 @@ Winit provides the following features, which can be enabled in your `Cargo.toml`
## 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.65**. Changes to
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
@@ -75,7 +74,7 @@ same MSRV policy.
Note that windows don't appear on Wayland until you draw/present to them.
#### Web
#### WebAssembly
To run the web example: `cargo run-wasm --example web`
@@ -86,7 +85,7 @@ either [provide Winit with a `<canvas>` element][web with_canvas], or [let Winit
create a `<canvas>` element which you can then retrieve][web canvas getter] and
insert it into the DOM yourself.
For the example code using Winit on Web, check out the [web example]. For
For the example code using Winit with WebAssembly, check out the [web example]. For
information on using Rust on WebAssembly, check out the [Rust and WebAssembly
book].
@@ -151,7 +150,7 @@ class. Your application _must_ specify the base class it needs via a feature fla
[agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
[Gradle]: https://developer.android.com/studio/build
For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples).
For more details, refer to these `android-activity` [example applications](https://github.com/rib/android-activity/tree/main/examples).
##### Converting from `ndk-glue` to `android-activity`

View File

@@ -8,7 +8,7 @@ fn main() {
cfg_aliases! {
// Systems.
android_platform: { target_os = "android" },
web_platform: { all(target_family = "wasm", target_os = "unknown") },
wasm_platform: { all(target_family = "wasm", not(target_os = "emscripten")) },
macos_platform: { target_os = "macos" },
ios_platform: { target_os = "ios" },
windows_platform: { target_os = "windows" },
@@ -17,8 +17,8 @@ fn main() {
redox: { target_os = "redox" },
// Native displays.
x11_platform: { all(feature = "x11", free_unix, not(redox)) },
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
x11_platform: { all(feature = "x11", free_unix, not(wasm), not(redox)) },
wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
orbital_platform: { redox },
}
}

View File

@@ -6,10 +6,8 @@ disallowed-methods = [
{ 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::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
{ path = "icrate::AppKit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
{ path = "icrate::AppKit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
]

View File

@@ -34,10 +34,9 @@ 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
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
]
skip-tree = [
{ name = "run-wasm", version = "0.1.0" }
]
skip-tree = []
[licenses]

View File

@@ -18,16 +18,16 @@ fn main() -> Result<(), impl std::error::Error> {
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget},
raw_window_handle::HasRawWindowHandle,
window::{Window, WindowId},
window::{Window, WindowBuilder, WindowId},
};
fn spawn_child_window(
parent: &Window,
event_loop: &EventLoopWindowTarget,
event_loop: &EventLoopWindowTarget<()>,
windows: &mut HashMap<WindowId, Window>,
) {
let parent = parent.raw_window_handle().unwrap();
let mut builder = Window::builder()
let mut builder = WindowBuilder::new()
.with_title("child window")
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
@@ -44,7 +44,7 @@ fn main() -> Result<(), impl std::error::Error> {
let mut windows = HashMap::new();
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
let parent_window = Window::builder()
let parent_window = WindowBuilder::new()
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32))

View File

@@ -1,9 +1,9 @@
#![allow(clippy::single_match)]
use std::thread;
#[cfg(not(web_platform))]
#[cfg(not(wasm_platform))]
use std::time;
#[cfg(web_platform)]
#[cfg(wasm_platform)]
use web_time as time;
use simple_logger::SimpleLogger;
@@ -11,7 +11,7 @@ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{ControlFlow, EventLoop},
keyboard::{Key, NamedKey},
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -37,7 +37,7 @@ fn main() -> Result<(), impl std::error::Error> {
println!("Press 'Esc' to close the window.");
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.")
.build(&event_loop)
.unwrap();

View File

@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
window::{CursorIcon, Window},
window::{CursorIcon, WindowBuilder},
};
#[path = "util/fill.rs"]
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder().build(&event_loop).unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
window.set_title("A fantastic window!");
let mut cursor_idx = 0;
@@ -31,7 +31,7 @@ fn main() -> Result<(), impl std::error::Error> {
..
} => {
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
window.set_cursor(CURSORS[cursor_idx]);
window.set_cursor_icon(CURSORS[cursor_idx]);
if cursor_idx < CURSORS.len() - 1 {
cursor_idx += 1;
} else {

View File

@@ -5,7 +5,7 @@ use winit::{
event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
keyboard::{Key, ModifiersState, NamedKey},
window::{CursorGrabMode, Window},
window::{CursorGrabMode, WindowBuilder},
};
#[path = "util/fill.rs"]
@@ -15,7 +15,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
.build(&event_loop)
.unwrap();

View File

@@ -1,12 +1,12 @@
#![allow(clippy::single_match)]
#[cfg(not(web_platform))]
#[cfg(not(wasm_platform))]
fn main() -> Result<(), impl std::error::Error> {
use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
window::Window,
event_loop::EventLoopBuilder,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -18,9 +18,11 @@ fn main() -> Result<(), impl std::error::Error> {
}
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::<CustomEvent>::with_user_event().build().unwrap();
let event_loop = EventLoopBuilder::<CustomEvent>::with_user_event()
.build()
.unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
@@ -54,7 +56,7 @@ fn main() -> Result<(), impl std::error::Error> {
})
}
#[cfg(web_platform)]
#[cfg(wasm_platform)]
fn main() {
panic!("This example is not supported on web.");
}

View File

@@ -5,7 +5,7 @@ use winit::{
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
event_loop::EventLoop,
keyboard::Key,
window::{Window, WindowId},
window::{Window, WindowBuilder, WindowId},
};
#[path = "util/fill.rs"]
@@ -15,8 +15,8 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window_1 = Window::builder().build(&event_loop).unwrap();
let window_2 = Window::builder().build(&event_loop).unwrap();
let window_1 = WindowBuilder::new().build(&event_loop).unwrap();
let window_2 = WindowBuilder::new().build(&event_loop).unwrap();
let mut switched = false;
let mut entered_id = window_2.id();

View File

@@ -3,14 +3,14 @@
//! Example for focusing a window.
use simple_logger::SimpleLogger;
#[cfg(not(web_platform))]
#[cfg(not(wasm_platform))]
use std::time;
#[cfg(web_platform)]
#[cfg(wasm_platform)]
use web_time as time;
use winit::{
event::{Event, StartCause, WindowEvent},
event_loop::EventLoop,
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(&event_loop)

View File

@@ -1,11 +1,11 @@
#![allow(clippy::single_match)]
use simple_logger::SimpleLogger;
use winit::dpi::LogicalSize;
use winit::dpi::PhysicalSize;
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::EventLoop;
use winit::keyboard::{Key, NamedKey};
use winit::window::{Fullscreen, Window};
use winit::window::{Fullscreen, WindowBuilder};
#[cfg(target_os = "macos")]
use winit::platform::macos::WindowExtMacOS;
@@ -22,7 +22,7 @@ fn main() -> Result<(), impl std::error::Error> {
let mut with_min_size = false;
let mut with_max_size = false;
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("Hello world!")
.build(&event_loop)
.unwrap();
@@ -126,7 +126,7 @@ fn main() -> Result<(), impl std::error::Error> {
"i" => {
with_min_size = !with_min_size;
let min_size = if with_min_size {
Some(LogicalSize::new(100, 100))
Some(PhysicalSize::new(100, 100))
} else {
None
};
@@ -139,7 +139,7 @@ fn main() -> Result<(), impl std::error::Error> {
"a" => {
with_max_size = !with_max_size;
let max_size = if with_max_size {
Some(LogicalSize::new(200, 200))
Some(PhysicalSize::new(200, 200))
} else {
None
};

View File

@@ -5,7 +5,7 @@ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
keyboard::Key,
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -15,7 +15,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("Your faithful window")
.build(&event_loop)
.unwrap();

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -7,7 +7,7 @@ use winit::{
event::{ElementState, Event, Ime, WindowEvent},
event_loop::EventLoop,
keyboard::NamedKey,
window::{ImePurpose, Window},
window::{ImePurpose, WindowBuilder},
};
#[path = "util/fill.rs"]
@@ -26,7 +26,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64))
.build(&event_loop)
.unwrap();

View File

@@ -8,7 +8,7 @@ use winit::{
keyboard::{Key, ModifiersState},
// WARNING: This is not available on all platforms (for example on the web).
platform::modifier_supplement::KeyEventExtModifierSupplement,
window::Window,
window::WindowBuilder,
};
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
@@ -24,7 +24,7 @@ fn main() -> Result<(), impl std::error::Error> {
simple_logger::SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_inner_size(LogicalSize::new(400.0, 200.0))
.build(&event_loop)
.unwrap();

View File

@@ -3,12 +3,12 @@
use simple_logger::SimpleLogger;
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::monitor::MonitorHandle;
use winit::{event_loop::EventLoop, window::Window};
use winit::{event_loop::EventLoop, window::WindowBuilder};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder().build(&event_loop).unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
if let Some(mon) = window.primary_monitor() {
print_info("Primary output", mon);

View File

@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("Mouse Wheel events")
.build(&event_loop)
.unwrap();

View File

@@ -1,6 +1,6 @@
#![allow(clippy::single_match)]
#[cfg(not(web_platform))]
#[cfg(not(wasm_platform))]
fn main() -> Result<(), impl std::error::Error> {
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
@@ -10,7 +10,7 @@ fn main() -> Result<(), impl std::error::Error> {
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
keyboard::{Key, ModifiersState, NamedKey},
window::{CursorGrabMode, CursorIcon, Fullscreen, Window, WindowLevel},
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel},
};
const WINDOW_COUNT: usize = 3;
@@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
for _ in 0..WINDOW_COUNT {
let window = Window::builder()
let window = WindowBuilder::new()
.with_inner_size(WINDOW_SIZE)
.build(&event_loop)
.unwrap();
@@ -84,7 +84,7 @@ fn main() -> Result<(), impl std::error::Error> {
"1" => window.set_window_level(WindowLevel::AlwaysOnTop),
"2" => window.set_window_level(WindowLevel::AlwaysOnBottom),
"3" => window.set_window_level(WindowLevel::Normal),
"c" => window.set_cursor(match state {
"c" => window.set_cursor_icon(match state {
true => CursorIcon::Progress,
false => CursorIcon::Default,
}),
@@ -96,13 +96,21 @@ fn main() -> Result<(), impl std::error::Error> {
)),
(false, _) => None,
}),
ch @ ("g" | "l") => {
let mode = match (ch, state) {
("l", true) => CursorGrabMode::Locked,
("g", true) => CursorGrabMode::Confined,
(_, _) => CursorGrabMode::None,
};
if let Err(err) = window.set_cursor_grab(mode) {
"l" if state => {
if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked)
{
println!("error: {err}");
}
}
"g" if state => {
if let Err(err) =
window.set_cursor_grab(CursorGrabMode::Confined)
{
println!("error: {err}");
}
}
"g" | "l" if !state => {
if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
println!("error: {err}");
}
}
@@ -115,6 +123,10 @@ fn main() -> Result<(), impl std::error::Error> {
println!("-> inner_size : {:?}", window.inner_size());
println!("-> fullscreen : {:?}", window.fullscreen());
}
"l" => window.set_min_inner_size(match state {
true => Some(WINDOW_SIZE),
false => None,
}),
"m" => window.set_maximized(state),
"p" => window.set_outer_position({
let mut position = window.outer_position().unwrap();
@@ -128,26 +140,12 @@ fn main() -> Result<(), impl std::error::Error> {
"s" => {
let _ = window.request_inner_size(match state {
true => PhysicalSize::new(
WINDOW_SIZE.width + 50,
WINDOW_SIZE.height + 50,
WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 100,
),
false => WINDOW_SIZE,
});
}
"k" => window.set_min_inner_size(match state {
true => Some(PhysicalSize::new(
WINDOW_SIZE.width - 100,
WINDOW_SIZE.height - 100,
)),
false => None,
}),
"o" => window.set_max_inner_size(match state {
true => Some(PhysicalSize::new(
WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 100,
)),
false => None,
}),
"w" => {
if let Size::Physical(size) = WINDOW_SIZE.into() {
window
@@ -205,7 +203,7 @@ fn main() -> Result<(), impl std::error::Error> {
})
}
#[cfg(web_platform)]
#[cfg(wasm_platform)]
fn main() {
panic!("Example not supported on Web");
panic!("Example not supported on Wasm");
}

View File

@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
use winit::{
event::{ElementState, Event, WindowEvent},
event_loop::EventLoop,
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();

View File

@@ -1,6 +1,6 @@
#![allow(clippy::single_match)]
#[cfg(not(web_platform))]
#[cfg(not(wasm_platform))]
fn main() -> Result<(), impl std::error::Error> {
use std::{sync::Arc, thread, time};
@@ -8,7 +8,7 @@ fn main() -> Result<(), impl std::error::Error> {
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -18,7 +18,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
let window = {
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
@@ -53,7 +53,7 @@ fn main() -> Result<(), impl std::error::Error> {
})
}
#[cfg(web_platform)]
#[cfg(wasm_platform)]
fn main() {
unimplemented!() // `Window` can't be sent between threads
}

View File

@@ -6,7 +6,7 @@ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -18,7 +18,7 @@ fn main() -> Result<(), impl std::error::Error> {
let mut resizable = false;
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("Hit space to toggle resizability.")
.with_inner_size(LogicalSize::new(600.0, 300.0))
.with_min_inner_size(LogicalSize::new(400.0, 200.0))

View File

@@ -14,7 +14,7 @@ mod example {
use winit::platform::startup_notify::{
EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify,
};
use winit::window::{Window, WindowId};
use winit::window::{Window, WindowBuilder, WindowId};
pub(super) fn main() -> Result<(), impl std::error::Error> {
// Create the event loop and get the activation token.
@@ -84,7 +84,8 @@ mod example {
if current_token.is_some() || create_first_window {
// Create the initial window.
let window = {
let mut builder = Window::builder().with_title(format!("Window {}", counter));
let mut builder =
WindowBuilder::new().with_title(format!("Window {}", counter));
if let Some(token) = current_token.take() {
println!("Creating a window with token {token:?}");

View File

@@ -5,7 +5,7 @@ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
keyboard::Key,
window::{Theme, Window},
window::{Theme, WindowBuilder},
};
#[path = "util/fill.rs"]
@@ -15,7 +15,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_theme(Some(Theme::Dark))
.build(&event_loop)

View File

@@ -1,16 +1,16 @@
#![allow(clippy::single_match)]
use std::time::Duration;
#[cfg(not(web_platform))]
#[cfg(not(wasm_platform))]
use std::time::Instant;
#[cfg(web_platform)]
#[cfg(wasm_platform)]
use web_time::Instant;
use simple_logger::SimpleLogger;
use winit::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();

View File

@@ -2,7 +2,7 @@ use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -12,44 +12,32 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("Touchpad gestures")
.build(&event_loop)
.unwrap();
#[cfg(target_os = "ios")]
{
use winit::platform::ios::WindowExtIOS;
window.recognize_doubletap_gesture(true);
window.recognize_pinch_gesture(true);
window.recognize_rotation_gesture(true);
}
println!("Only supported on macOS/iOS at the moment.");
let mut zoom = 0.0;
let mut rotated = 0.0;
println!("Only supported on macOS at the moment.");
event_loop.run(move |event, elwt| {
if let Event::WindowEvent { event, .. } = event {
match event {
WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::PinchGesture { delta, .. } => {
zoom += delta;
WindowEvent::TouchpadMagnify { delta, .. } => {
if delta > 0.0 {
println!("Zoomed in {delta:.5} (now: {zoom:.5})");
println!("Zoomed in {delta}");
} else {
println!("Zoomed out {delta:.5} (now: {zoom:.5})");
println!("Zoomed out {delta}");
}
}
WindowEvent::DoubleTapGesture { .. } => {
WindowEvent::SmartMagnify { .. } => {
println!("Smart zoom");
}
WindowEvent::RotationGesture { delta, .. } => {
rotated += delta;
WindowEvent::TouchpadRotate { delta, .. } => {
if delta > 0.0 {
println!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
println!("Rotated counterclockwise {delta}");
} else {
println!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
println!("Rotated clockwise {delta}");
}
}
WindowEvent::RedrawRequested => {

View File

@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_decorations(false)
.with_transparent(true)
.build(&event_loop)

View File

@@ -4,13 +4,13 @@ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
keyboard::Key,
window::{Fullscreen, Window},
window::{Fullscreen, WindowBuilder},
};
pub fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
let builder = Window::builder().with_title("A fantastic window!");
let builder = WindowBuilder::new().with_title("A fantastic window!");
#[cfg(wasm_platform)]
let builder = {
use winit::platform::web::WindowBuilderExtWebSys;
@@ -18,11 +18,11 @@ pub fn main() -> Result<(), impl std::error::Error> {
};
let window = builder.build(&event_loop).unwrap();
#[cfg(web_platform)]
#[cfg(wasm_platform)]
let log_list = wasm::insert_canvas_and_create_log_list(&window);
event_loop.run(move |event, elwt| {
#[cfg(web_platform)]
#[cfg(wasm_platform)]
wasm::log_event(&log_list, &event);
match event {
@@ -57,7 +57,7 @@ pub fn main() -> Result<(), impl std::error::Error> {
})
}
#[cfg(web_platform)]
#[cfg(wasm_platform)]
mod wasm {
use std::num::NonZeroU32;

View File

@@ -4,7 +4,7 @@ pub fn main() {
println!("This example must be run with cargo run-wasm --example web_aspect_ratio")
}
#[cfg(web_platform)]
#[cfg(wasm_platform)]
mod wasm {
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
@@ -14,7 +14,7 @@ mod wasm {
event::{Event, WindowEvent},
event_loop::EventLoop,
platform::web::WindowBuilderExtWebSys,
window::Window,
window::{Window, WindowBuilder},
};
const EXPLANATION: &str = "
@@ -33,7 +33,7 @@ This example demonstrates the desired future functionality which will possibly b
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
// When running in a non-wasm environment this would set the window size to 100x100.
// However in this example it just sets a default initial size of 100x100 that is immediately overwritten due to the layout + styling of the page.

View File

@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(&event_loop)

View File

@@ -8,7 +8,7 @@ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{DeviceEvents, EventLoop},
keyboard::Key,
window::{Window, WindowButtons},
window::{WindowBuilder, WindowButtons},
};
#[path = "util/fill.rs"]
@@ -18,7 +18,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(LogicalSize::new(300.0, 300.0))
.build(&event_loop)

View File

@@ -8,7 +8,7 @@ use winit::{
event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent},
event_loop::{DeviceEvents, EventLoop},
keyboard::{Key, KeyCode, PhysicalKey},
window::{Fullscreen, Window},
window::{Fullscreen, WindowBuilder},
};
#[path = "util/fill.rs"]
@@ -18,7 +18,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(LogicalSize::new(100.0, 100.0))
.build(&event_loop)

View File

@@ -5,7 +5,7 @@ use winit::{
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
event_loop::EventLoop,
keyboard::Key,
window::{CursorIcon, ResizeDirection, Window},
window::{CursorIcon, ResizeDirection, WindowBuilder},
};
const BORDER: f64 = 8.0;
@@ -17,7 +17,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0))
.with_min_inner_size(winit::dpi::LogicalSize::new(400.0, 200.0))
.with_decorations(false)
@@ -40,7 +40,7 @@ fn main() -> Result<(), impl std::error::Error> {
if new_location != cursor_location {
cursor_location = new_location;
window.set_cursor(cursor_direction_icon(cursor_location))
window.set_cursor_icon(cursor_direction_icon(cursor_location))
}
}
}

View File

@@ -6,7 +6,7 @@ use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
window::{Icon, Window},
window::{Icon, WindowBuilder},
};
#[path = "util/fill.rs"]
@@ -25,7 +25,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("An iconic window!")
// At present, this only does anything on Windows and X11, so if you want to save load
// time, you can put icon loading behind a function that returns `None` on other platforms.

View File

@@ -12,7 +12,7 @@ fn main() -> Result<(), impl std::error::Error> {
event::{Event, WindowEvent},
event_loop::EventLoop,
platform::run_on_demand::EventLoopExtRunOnDemand,
window::{Window, WindowId},
window::{Window, WindowBuilder, WindowId},
};
#[path = "util/fill.rs"]
@@ -65,7 +65,7 @@ fn main() -> Result<(), impl std::error::Error> {
_ => (),
}
} else if let Event::Resumed = event {
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(elwt)

View File

@@ -8,7 +8,7 @@ use winit::{
event::ElementState,
event::{Event, MouseButton, WindowEvent},
event_loop::EventLoop,
window::Window,
window::WindowBuilder,
};
#[cfg(target_os = "macos")]
@@ -21,7 +21,7 @@ mod fill;
fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(&event_loop)

View File

@@ -16,7 +16,7 @@ fn main() -> std::process::ExitCode {
event::{Event, WindowEvent},
event_loop::EventLoop,
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -25,7 +25,7 @@ fn main() -> std::process::ExitCode {
let mut event_loop = EventLoop::new().unwrap();
SimpleLogger::new().init().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
@@ -68,7 +68,7 @@ fn main() -> std::process::ExitCode {
}
}
#[cfg(any(ios_platform, web_platform, orbital_platform))]
#[cfg(any(ios_platform, wasm_platform, orbital_platform))]
fn main() {
println!("This platform doesn't support pump_events.");
}

View File

@@ -5,7 +5,7 @@ use winit::{
event::{ElementState, Event, WindowEvent},
event_loop::EventLoop,
keyboard::NamedKey,
window::Window,
window::WindowBuilder,
};
#[path = "util/fill.rs"]
@@ -15,7 +15,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(LogicalSize::new(128.0, 128.0))
.with_resize_increments(LogicalSize::new(25.0, 25.0))

View File

@@ -11,7 +11,7 @@ use winit::{
event_loop::EventLoop,
keyboard::{Key, NamedKey},
platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS},
window::Window,
window::{Window, WindowBuilder},
};
#[cfg(target_os = "macos")]
@@ -60,7 +60,7 @@ fn main() -> Result<(), impl std::error::Error> {
} => match logical_key.as_ref() {
Key::Character("t") => {
let tabbing_id = windows.get(&window_id).unwrap().tabbing_identifier();
let window = Window::builder()
let window = WindowBuilder::new()
.with_tabbing_identifier(&tabbing_id)
.build(elwt)
.unwrap();

View File

@@ -12,7 +12,7 @@ mod imple {
event::{Event, WindowEvent},
event_loop::EventLoop,
platform::x11::WindowBuilderExtX11,
window::Window,
window::WindowBuilder,
};
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
@@ -25,7 +25,7 @@ mod imple {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new()?;
let window = Window::builder()
let window = WindowBuilder::new()
.with_title("An embedded window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(parent_window_id)

View File

@@ -2,7 +2,8 @@
name = "run-wasm"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cargo-run-wasm = "0.2.0"

View File

@@ -97,16 +97,13 @@
//! [points]: https://en.wikipedia.org/wiki/Point_(typography)
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
//! [`ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged
//! [`window.scale_factor()`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.scale_factor
//! [`window.scale_factor()`]: crate::window::Window::scale_factor
//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html
//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/
//! [android_1]: https://developer.android.com/training/multiscreen/screendensities
//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub trait Pixel: Copy + Into<f64> {
fn from_f64(f: f64) -> Self;
fn cast<P: Pixel>(self) -> P {

View File

@@ -1,8 +1,6 @@
use crate::platform_impl;
use std::{error, fmt};
#[doc(inline)]
pub use winit_core::error::NotSupportedError;
use crate::platform_impl;
// TODO: Rename
/// An error that may be generated when requesting Winit state
@@ -16,10 +14,10 @@ pub enum ExternalError {
Os(OsError),
}
impl From<winit_core::event::InnerSizeIgnored> for ExternalError {
fn from(_: winit_core::event::InnerSizeIgnored) -> Self {
Self::Ignored
}
/// The error type for when the requested operation is not supported by the backend.
#[derive(Clone)]
pub struct NotSupportedError {
_marker: (),
}
/// The error type for when the OS cannot perform the requested operation.
@@ -37,6 +35,8 @@ pub enum EventLoopError {
NotSupported(NotSupportedError),
/// The OS cannot perform the operation.
Os(OsError),
/// The event loop can't be re-run while it's already running
AlreadyRunning,
/// The event loop can't be re-created.
RecreationAttempt,
/// Application has exit with an error status.
@@ -49,6 +49,14 @@ impl From<OsError> for EventLoopError {
}
}
impl NotSupportedError {
#[inline]
#[allow(dead_code)]
pub(crate) fn new() -> NotSupportedError {
NotSupportedError { _marker: () }
}
}
impl OsError {
#[allow(dead_code)]
pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError {
@@ -82,9 +90,22 @@ impl fmt::Display for ExternalError {
}
}
impl fmt::Debug for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("NotSupportedError").finish()
}
}
impl fmt::Display for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.pad("the requested operation is not supported by Winit")
}
}
impl fmt::Display for EventLoopError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
EventLoopError::AlreadyRunning => write!(f, "EventLoop is already running"),
EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
EventLoopError::NotSupported(e) => e.fmt(f),
EventLoopError::Os(e) => e.fmt(f),
@@ -95,6 +116,7 @@ impl fmt::Display for EventLoopError {
impl error::Error for OsError {}
impl error::Error for ExternalError {}
impl error::Error for NotSupportedError {}
impl error::Error for EventLoopError {}
#[cfg(test)]
@@ -106,6 +128,11 @@ mod tests {
// Eat attributes for testing
#[test]
fn ensure_fmt_does_not_panic() {
let _ = format!(
"{:?}, {}",
NotSupportedError::new(),
NotSupportedError::new().clone()
);
let _ = format!(
"{:?}, {}",
ExternalError::NotSupported(NotSupportedError::new()),

View File

@@ -1,64 +1,62 @@
//! Incoming notifications from the GUI system.
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::event_loop::AsyncRequestSerial;
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState};
use crate::window::{ActivationToken, Theme, WindowId};
use std::fmt;
//! The [`Event`] enum and assorted supporting types.
//!
//! These are sent to the closure given to [`EventLoop::run(...)`], where they get
//! 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
//! approximate the basic ordering loop of [`EventLoop::run(...)`] like this:
//!
//! ```rust,ignore
//! let mut start_cause = StartCause::Init;
//!
//! while !elwt.exiting() {
//! event_handler(NewEvents(start_cause), elwt);
//!
//! for e in (window events, user events, device events) {
//! event_handler(e, elwt);
//! }
//!
//! for w in (redraw windows) {
//! event_handler(RedrawRequested(w), elwt);
//! }
//!
//! event_handler(AboutToWait, elwt);
//! start_cause = wait_if_necessary();
//! }
//!
//! event_handler(LoopExiting, elwt);
//! ```
//!
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
//! describes what happens in what order.
//!
//! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use std::path::PathBuf;
use std::sync::{Mutex, Weak};
#[cfg(not(web_platform))]
#[cfg(not(wasm_platform))]
use std::time::Instant;
#[cfg(web_platform)]
use web_time::Instant;
#[cfg(feature = "serde")]
pub use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
#[cfg(wasm_platform)]
use web_time::Instant;
/// Identifier of an input device.
///
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
/// physical. Virtual devices typically aggregate inputs from multiple physical devices.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(u64);
impl DeviceId {
/// Returns a dummy id, useful for unit testing.
///
/// # Safety
///
/// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `DeviceId`.
///
/// **Passing this into a winit function will result in undefined behavior.**
pub const unsafe fn dummy() -> Self {
DeviceId(0)
}
}
impl From<u64> for DeviceId {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<DeviceId> for u64 {
fn from(value: DeviceId) -> Self {
value.0
}
}
use crate::error::ExternalError;
#[cfg(doc)]
use crate::window::Window;
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
event_loop::AsyncRequestSerial,
keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState},
platform_impl,
window::{ActivationToken, Theme, WindowId},
};
/// Describes a generic event.
///
/// See the module-level docs for more information on the event loop manages each event.
#[derive(Debug, Clone, PartialEq)]
pub enum Event<T: 'static, KeyExtra> {
pub enum Event<T: 'static> {
/// Emitted when new events arrive from the OS to be processed.
///
/// This event type is useful as a place to put code that should be done before you start
@@ -70,7 +68,7 @@ pub enum Event<T: 'static, KeyExtra> {
/// Emitted when the OS sends an event to a winit window.
WindowEvent {
window_id: WindowId,
event: WindowEvent<KeyExtra>,
event: WindowEvent,
},
/// Emitted when the OS sends an event to a device.
@@ -79,7 +77,7 @@ pub enum Event<T: 'static, KeyExtra> {
event: DeviceEvent,
},
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](https://docs.rs/winit/latest/winit/event_loop/struct.EventLoopProxy.html#method.send_event)
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event)
UserEvent(T),
/// Emitted when the application has been suspended.
@@ -255,9 +253,9 @@ pub enum Event<T: 'static, KeyExtra> {
MemoryWarning,
}
impl<T, Extra> Event<T, Extra> {
impl<T> Event<T> {
#[allow(clippy::result_large_err)]
pub fn map_nonuser_event<U>(self) -> Result<Event<U, Extra>, Event<T, Extra>> {
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
use self::Event::*;
match self {
UserEvent(_) => Err(self),
@@ -304,10 +302,8 @@ pub enum StartCause {
}
/// Describes an event from a [`Window`].
///
/// [`Window`]: https://docs.rs/winit/latest/winit/window/struct.Window.html
#[derive(Debug, Clone, PartialEq)]
pub enum WindowEvent<KeyExtra> {
pub enum WindowEvent {
/// The activation token was delivered back and now could be used.
///
#[cfg_attr(
@@ -369,7 +365,7 @@ pub enum WindowEvent<KeyExtra> {
/// events which are not marked as `is_synthetic`.
KeyboardInput {
device_id: DeviceId,
event: KeyEvent<KeyExtra>,
event: KeyEvent,
/// If `true`, the event was generated synthetically by winit
/// in one of the following circumstances:
@@ -393,8 +389,6 @@ pub enum WindowEvent<KeyExtra> {
/// ## Platform-specific
///
/// - **iOS / Android / Web / Orbital:** Unsupported.
///
/// [`Window::set_ime_allowed`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_allowed
Ime(Ime),
/// The cursor has moved on the window.
@@ -451,23 +445,21 @@ pub enum WindowEvent<KeyExtra> {
button: MouseButton,
},
/// Two-finger pinch gesture, often used for magnification.
/// Touchpad magnification event with two-finger pinch gesture.
///
/// Positive delta values indicate magnification (zooming in) and
/// negative delta values indicate shrinking (zooming out).
///
/// ## Platform-specific
///
/// - Only available on **macOS** and **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
PinchGesture {
/// - Only available on **macOS**.
TouchpadMagnify {
device_id: DeviceId,
/// Positive values indicate magnification (zooming in) and negative
/// values indicate shrinking (zooming out).
///
/// This value may be NaN.
delta: f64,
phase: TouchPhase,
},
/// Double tap gesture.
/// Smart magnification event.
///
/// On a Mac, smart magnification is triggered by a double tap with two fingers
/// on the trackpad and is commonly used to zoom on a certain object
@@ -483,20 +475,18 @@ pub enum WindowEvent<KeyExtra> {
///
/// ## Platform-specific
///
/// - Only available on **macOS 10.8** and later, and **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
DoubleTapGesture { device_id: DeviceId },
/// - Only available on **macOS 10.8** and later.
SmartMagnify { device_id: DeviceId },
/// Two-finger rotation gesture.
/// Touchpad rotation event with two-finger rotation gesture.
///
/// Positive delta values indicate rotation counterclockwise and
/// negative delta values indicate rotation clockwise.
///
/// ## Platform-specific
///
/// - Only available on **macOS** and **iOS**.
/// - On iOS, not recognized by default. It must be enabled when needed.
RotationGesture {
/// - Only available on **macOS**.
TouchpadRotate {
device_id: DeviceId,
delta: f32,
phase: TouchPhase,
@@ -584,7 +574,7 @@ pub enum WindowEvent<KeyExtra> {
/// ### Others
///
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
/// - **Android / Wayland / Windows / Orbital:** Unsupported.
/// - **Android / Windows / Orbital:** Unsupported.
///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
@@ -600,11 +590,33 @@ pub enum WindowEvent<KeyExtra> {
///
/// Winit will aggregate duplicate redraw requests into a single event, to
/// help avoid duplicating rendering work.
///
/// [`Window::request_redraw`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.request_redraw
RedrawRequested,
}
/// Identifier of an input device.
///
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
/// physical. Virtual devices typically aggregate inputs from multiple physical devices.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(pub(crate) platform_impl::DeviceId);
impl DeviceId {
/// Returns a dummy id, useful for unit testing.
///
/// # Safety
///
/// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `DeviceId`.
///
/// **Passing this into a winit function will result in undefined behavior.**
pub const unsafe fn dummy() -> Self {
#[allow(unused_unsafe)]
DeviceId(unsafe { platform_impl::DeviceId::dummy() })
}
}
/// Represents raw hardware events that are not associated with any particular window.
///
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person
@@ -665,7 +677,7 @@ pub struct RawKeyEvent {
/// Describes a keyboard input targeting a window.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeyEvent<Extra> {
pub struct KeyEvent {
/// Represents the position of a key independent of the currently active layout.
///
/// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode).
@@ -715,7 +727,7 @@ pub struct KeyEvent<Extra> {
/// - **Web:** Dead keys might be reported as the real key instead
/// of `Dead` depending on the browser/OS.
///
/// [`key_without_modifiers`]: https://docs.rs/winit/latest/winit/platform/modifier_supplement/trait.KeyEventExtModifierSupplement.html#tymethod.key_without_modifiers
/// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
pub logical_key: keyboard::Key,
/// Contains the text produced by this keypress.
@@ -772,7 +784,7 @@ pub struct KeyEvent<Extra> {
/// modifiers applied.
///
/// On Android, iOS, Redox and Web, this type is a no-op.
pub extra: Extra,
pub(crate) platform_specific: platform_impl::KeyEventExtra,
}
/// Describes keyboard modifiers event.
@@ -787,15 +799,6 @@ pub struct Modifiers {
}
impl Modifiers {
/// Only `winit` should instantiate this!
#[doc(hidden)]
pub fn new(state: ModifiersState, pressed_mods: ModifiersKeys) -> Self {
Self {
state,
pressed_mods,
}
}
/// The state of the modifiers.
pub fn state(&self) -> ModifiersState {
self.state
@@ -895,8 +898,6 @@ impl From<ModifiersState> for Modifiers {
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
/// Ime::Commit("啊不")
/// ```
///
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Ime {
@@ -905,8 +906,6 @@ pub enum Ime {
/// After getting this event you could receive [`Preedit`](Self::Preedit) and
/// [`Commit`](Self::Commit) events. You should also start performing IME related requests
/// like [`Window::set_ime_cursor_area`].
///
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
Enabled,
/// Notifies when a new composing text should be set at the cursor position.
@@ -929,8 +928,6 @@ pub enum Ime {
/// [`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
/// preedit text.
///
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
Disabled,
}
@@ -1119,7 +1116,7 @@ pub struct InnerSizeWriter {
impl InnerSizeWriter {
#[cfg(not(orbital_platform))]
pub fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
pub(crate) fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
Self { new_inner_size }
}
@@ -1127,19 +1124,14 @@ impl InnerSizeWriter {
pub fn request_inner_size(
&mut self,
new_inner_size: PhysicalSize<u32>,
) -> Result<(), InnerSizeIgnored> {
) -> Result<(), ExternalError> {
if let Some(inner) = self.new_inner_size.upgrade() {
*inner.lock().unwrap() = new_inner_size;
Ok(())
} else {
Err(InnerSizeIgnored { _private: () })
Err(ExternalError::Ignored)
}
}
/// Get the underlying size.
pub fn get(&self) -> PhysicalSize<u32> {
*self.new_inner_size.upgrade().unwrap().lock().unwrap()
}
}
impl PartialEq for InnerSizeWriter {
@@ -1148,25 +1140,6 @@ impl PartialEq for InnerSizeWriter {
}
}
/// Could not write the inner size.
pub struct InnerSizeIgnored {
_private: (),
}
impl fmt::Debug for InnerSizeIgnored {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InnerSizeIgnored").finish_non_exhaustive()
}
}
impl fmt::Display for InnerSizeIgnored {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("tried to write inner size after")
}
}
impl std::error::Error for InnerSizeIgnored {}
#[cfg(test)]
mod tests {
use crate::event;
@@ -1226,13 +1199,13 @@ mod tests {
state: event::ElementState::Pressed,
button: event::MouseButton::Other(0),
});
with_window_event(PinchGesture {
with_window_event(TouchpadMagnify {
device_id: did,
delta: 0.0,
phase: event::TouchPhase::Started,
});
with_window_event(DoubleTapGesture { device_id: did });
with_window_event(RotationGesture {
with_window_event(SmartMagnify { device_id: did });
with_window_event(TouchpadRotate {
device_id: did,
delta: 0.0,
phase: event::TouchPhase::Started,
@@ -1292,7 +1265,7 @@ mod tests {
#[allow(clippy::redundant_clone)]
#[test]
fn test_event_clone() {
foreach_event!(|event: event::Event<(), ()>| {
foreach_event!(|event: event::Event<()>| {
let event2 = event.clone();
assert_eq!(event, event2);
})
@@ -1300,7 +1273,7 @@ mod tests {
#[test]
fn test_map_nonuser_event() {
foreach_event!(|event: event::Event<(), ()>| {
foreach_event!(|event: event::Event<()>| {
let is_user = matches!(event, event::Event::UserEvent(()));
let event2 = event.map_nonuser_event::<()>();
if is_user {
@@ -1334,7 +1307,7 @@ mod tests {
#[allow(clippy::clone_on_copy)]
#[test]
fn ensure_attrs_do_not_panic() {
foreach_event!(|event: event::Event<(), ()>| {
foreach_event!(|event: event::Event<()>| {
let _ = format!("{:?}", event);
});
let _ = event::StartCause::Init.clone();

View File

@@ -11,15 +11,17 @@ use std::marker::PhantomData;
use std::ops::Deref;
#[cfg(any(x11_platform, wayland_platform))]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::{error, fmt};
#[cfg(not(wasm_platform))]
use std::time::{Duration, Instant};
#[cfg(wasm_platform)]
use web_time::{Duration, Instant};
use crate::error::EventLoopError;
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
#[doc(inline)]
pub use winit_core::event_loop::{AsyncRequestSerial, ControlFlow};
/// Provides a way to retrieve events from the system and from the windows that were registered to
/// the events loop.
///
@@ -46,8 +48,8 @@ pub struct EventLoop<T: 'static> {
/// your callback. [`EventLoop`] will coerce into this type (`impl<T> Deref for
/// EventLoop<T>`), so functions that take this as a parameter can also take
/// `&EventLoop`.
pub struct EventLoopWindowTarget {
pub(crate) p: platform_impl::EventLoopWindowTarget,
pub struct EventLoopWindowTarget<T: 'static> {
pub(crate) p: platform_impl::EventLoopWindowTarget<T>,
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
}
@@ -55,26 +57,33 @@ pub struct EventLoopWindowTarget {
///
/// This is used to make specifying options that affect the whole application
/// easier. But note that constructing multiple event loops is not supported.
///
/// This can be created using [`EventLoop::new`] or [`EventLoop::with_user_event`].
#[derive(Default)]
pub struct EventLoopBuilder<T: 'static> {
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
_p: PhantomData<T>,
}
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl EventLoopBuilder<()> {
/// Start building a new event loop.
#[inline]
#[deprecated = "use `EventLoop::builder` instead"]
pub fn new() -> Self {
EventLoop::builder()
Self::with_user_event()
}
}
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl<T> EventLoopBuilder<T> {
/// Start building a new event loop, with the given type as the user event
/// type.
#[inline]
pub fn with_user_event() -> Self {
Self {
platform_specific: Default::default(),
_p: PhantomData,
}
}
/// Builds a new event loop.
///
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
@@ -121,7 +130,7 @@ impl<T> EventLoopBuilder<T> {
})
}
#[cfg(web_platform)]
#[cfg(wasm_platform)]
pub(crate) fn allow_event_loop_recreation() {
EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
}
@@ -133,45 +142,78 @@ impl<T> fmt::Debug for EventLoop<T> {
}
}
impl fmt::Debug for EventLoopWindowTarget {
impl<T> fmt::Debug for EventLoopWindowTarget<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EventLoopWindowTarget { .. }")
}
}
impl EventLoop<()> {
/// Create the event loop.
/// Set through [`EventLoopWindowTarget::set_control_flow()`].
///
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
///
/// Defaults to [`Wait`].
///
/// [`Wait`]: Self::Wait
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum ControlFlow {
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process.
Poll,
/// When the current loop iteration finishes, suspend the thread until another event arrives.
#[default]
Wait,
/// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the given time is reached.
///
/// This is an alias of `EventLoop::builder().build()`.
/// Useful for implementing efficient timers. Applications which want to render at the display's
/// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
/// to reduce odds of missed frames.
///
/// [`Poll`]: Self::Poll
WaitUntil(Instant),
}
impl ControlFlow {
/// Creates a [`ControlFlow`] that waits until a timeout has expired.
///
/// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
/// instead set to [`Wait`].
///
/// [`WaitUntil`]: Self::WaitUntil
/// [`Wait`]: Self::Wait
pub fn wait_duration(timeout: Duration) -> Self {
match Instant::now().checked_add(timeout) {
Some(instant) => Self::WaitUntil(instant),
None => Self::Wait,
}
}
}
impl EventLoop<()> {
/// Alias for [`EventLoopBuilder::new().build()`].
///
/// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build
#[inline]
pub fn new() -> Result<EventLoop<()>, EventLoopError> {
Self::builder().build()
}
/// Start building a new event loop.
///
/// This returns an [`EventLoopBuilder`], to allow configuring the event loop before creation.
///
/// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that.
#[inline]
pub fn builder() -> EventLoopBuilder<()> {
Self::with_user_event()
EventLoopBuilder::new().build()
}
}
impl<T> EventLoop<T> {
/// 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,
}
#[deprecated = "Use `EventLoopBuilder::<T>::with_user_event().build()` instead."]
pub fn with_user_event() -> Result<EventLoop<T>, EventLoopError> {
EventLoopBuilder::<T>::with_user_event().build()
}
/// Runs the event loop in the calling thread and calls the given `event_handler` closure
/// to dispatch any pending events.
///
/// Since the closure is `'static`, it must be a `move` closure if it needs to
/// access any data from the calling context.
///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
///
/// ## Platform-specific
@@ -184,10 +226,10 @@ impl<T> EventLoop<T> {
///
/// Web applications are recommended to use
#[cfg_attr(
web_platform,
wasm_platform,
doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]"
)]
#[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")]
#[cfg_attr(not(wasm_platform), doc = "`EventLoopExtWebSys::spawn()`")]
/// [^1] instead of [`run()`] to avoid the need
/// for the Javascript exception trick, and to make it clearer that the event loop runs
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the
@@ -197,12 +239,12 @@ impl<T> EventLoop<T> {
///
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow()
/// [`run()`]: Self::run()
/// [^1]: `EventLoopExtWebSys::spawn()` is only available on Web.
/// [^1]: `EventLoopExtWebSys::spawn()` is only available on WASM.
#[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
#[cfg(not(all(wasm_platform, target_feature = "exception-handling")))]
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &EventLoopWindowTarget),
F: FnMut(Event<T>, &EventLoopWindowTarget<T>),
{
self.event_loop.run(event_handler)
}
@@ -259,13 +301,13 @@ impl<T> AsRawFd for EventLoop<T> {
}
impl<T> Deref for EventLoop<T> {
type Target = EventLoopWindowTarget;
fn deref(&self) -> &EventLoopWindowTarget {
type Target = EventLoopWindowTarget<T>;
fn deref(&self) -> &EventLoopWindowTarget<T> {
self.event_loop.window_target()
}
}
impl EventLoopWindowTarget {
impl<T> EventLoopWindowTarget<T> {
/// Returns the list of all the monitors available on the system.
#[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
@@ -328,19 +370,10 @@ impl EventLoopWindowTarget {
pub fn exiting(&self) -> bool {
self.p.exiting()
}
/// Gets a persistent reference to the underlying platform display.
///
/// See the [`OwnedDisplayHandle`] type for more information.
pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle {
platform: self.p.owned_display_handle(),
}
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for EventLoopWindowTarget {
impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.p.raw_display_handle_rwh_06()?;
// SAFETY: The display will never be deallocated while the event loop is alive.
@@ -349,59 +382,13 @@ impl rwh_06::HasDisplayHandle for EventLoopWindowTarget {
}
#[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawDisplayHandle for EventLoopWindowTarget {
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoopWindowTarget<T> {
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.p.raw_display_handle_rwh_05()
}
}
/// A proxy for the underlying display handle.
///
/// The purpose of this type is to provide a cheaply clonable handle to the underlying
/// display handle. This is often used by graphics APIs to connect to the underlying APIs.
/// It is difficult to keep a handle to the [`EventLoop`] type or the [`EventLoopWindowTarget`]
/// type. In contrast, this type involves no lifetimes and can be persisted for as long as
/// needed.
///
/// For all platforms, this is one of the following:
///
/// - A zero-sized type that is likely optimized out.
/// - A reference-counted pointer to the underlying type.
#[derive(Clone)]
pub struct OwnedDisplayHandle {
#[cfg_attr(not(any(feature = "rwh_05", feature = "rwh_06")), allow(dead_code))]
platform: platform_impl::OwnedDisplayHandle,
}
impl fmt::Debug for OwnedDisplayHandle {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
#[inline]
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.platform.raw_display_handle_rwh_06()?;
// SAFETY: The underlying display handle should be safe.
let handle = unsafe { rwh_06::DisplayHandle::borrow_raw(raw) };
Ok(handle)
}
}
#[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawDisplayHandle for OwnedDisplayHandle {
#[inline]
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.platform.raw_display_handle_rwh_05()
}
}
/// Used to send custom events to [`EventLoop`].
pub struct EventLoopProxy<T: 'static> {
event_loop_proxy: platform_impl::EventLoopProxy<T>,
@@ -460,3 +447,29 @@ pub enum DeviceEvents {
/// Never capture device events.
Never,
}
/// A unique identifier of the winit's async request.
///
/// This could be used to identify the async request once it's done
/// and a specific action must be taken.
///
/// One of the handling scenarious could be to maintain a working list
/// containing [`AsyncRequestSerial`] and some closure associated with it.
/// Then once event is arriving the working list is being traversed and a job
/// executed and removed from the list.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AsyncRequestSerial {
serial: u64,
}
impl AsyncRequestSerial {
// TODO(kchibisov) remove `cfg` when the clipboard will be added.
#[allow(dead_code)]
pub(crate) fn get() -> Self {
static CURRENT_SERIAL: AtomicU64 = AtomicU64::new(0);
// NOTE: we rely on wrap around here, while the user may just request
// in the loop u64::MAX times that's issue is considered on them.
let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
Self { serial }
}
}

View File

@@ -69,9 +69,6 @@
//
// --------- END OF W3C SHORT NOTICE ---------------------------------------------------------------
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub use smol_str::SmolStr;
/// Contains the platform-native physical key identifier
@@ -1454,29 +1451,6 @@ pub enum NamedKey {
F35,
}
// NOTE: the exact modifier key is not used to represent modifiers state in the
// first place due to a fact that modifiers state could be changed without any
// key being pressed and on some platforms like Wayland/X11 which key resulted
// in modifiers change is hidden, also, not that it really matters.
//
// The reason this API is even exposed is mostly to provide a way for users
// to treat modifiers differently based on their position, which is required
// on macOS due to their AltGr/Option situation.
bitflags! {
#[doc(hidden)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModifiersKeys: u8 {
const LSHIFT = 0b0000_0001;
const RSHIFT = 0b0000_0010;
const LCONTROL = 0b0000_0100;
const RCONTROL = 0b0000_1000;
const LALT = 0b0001_0000;
const RALT = 0b0010_0000;
const LSUPER = 0b0100_0000;
const RSUPER = 0b1000_0000;
}
}
/// Key represents the meaning of a keypress.
///
/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with
@@ -1588,7 +1562,7 @@ impl NamedKey {
/// # Examples
///
/// ```
/// use winit_core::keyboard::NamedKey;
/// use winit::keyboard::NamedKey;
///
/// assert_eq!(NamedKey::Enter.to_text(), Some("\r"));
/// assert_eq!(NamedKey::F20.to_text(), None);
@@ -1611,7 +1585,7 @@ impl Key {
/// # Examples
///
/// ```
/// use winit_core::keyboard::{NamedKey, Key};
/// use winit::keyboard::{NamedKey, Key};
///
/// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
/// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
@@ -1750,6 +1724,28 @@ pub enum ModifiersKeyState {
Unknown,
}
// NOTE: the exact modifier key is not used to represent modifiers state in the
// first place due to a fact that modifiers state could be changed without any
// key being pressed and on some platforms like Wayland/X11 which key resulted
// in modifiers change is hidden, also, not that it really matters.
//
// The reason this API is even exposed is mostly to provide a way for users
// to treat modifiers differently based on their position, which is required
// on macOS due to their AltGr/Option situation.
bitflags! {
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct ModifiersKeys: u8 {
const LSHIFT = 0b0000_0001;
const RSHIFT = 0b0000_0010;
const LCONTROL = 0b0000_0100;
const RCONTROL = 0b0000_1000;
const LALT = 0b0001_0000;
const RALT = 0b0010_0000;
const LSUPER = 0b0100_0000;
const RSUPER = 0b1000_0000;
}
}
#[cfg(feature = "serde")]
mod modifiers_serde {
use super::ModifiersState;

View File

@@ -13,7 +13,7 @@
//! Once this is done, there are two ways to create a [`Window`]:
//!
//! - Calling [`Window::new(&event_loop)`][window_new].
//! - Calling [`let builder = Window::builder()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build].
//! - Calling [`let builder = WindowBuilder::new()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build].
//!
//! The first method is the simplest and will give you default values for everything. The second
//! method allows you to customize the way your [`Window`] will look and behave by modifying the
@@ -63,11 +63,11 @@
//! use winit::{
//! event::{Event, WindowEvent},
//! event_loop::{ControlFlow, EventLoop},
//! window::Window,
//! window::WindowBuilder,
//! };
//!
//! let event_loop = EventLoop::new().unwrap();
//! let window = Window::builder().build(&event_loop).unwrap();
//! let window = WindowBuilder::new().build(&event_loop).unwrap();
//!
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications.
@@ -137,7 +137,7 @@
//! [`WindowId`]: window::WindowId
//! [`WindowBuilder`]: window::WindowBuilder
//! [window_new]: window::Window::new
//! [window_builder_new]: window::Window::builder
//! [window_builder_new]: window::WindowBuilder::new
//! [window_builder_build]: window::WindowBuilder::build
//! [`Window::id()`]: window::Window::id
//! [`WindowEvent`]: event::WindowEvent
@@ -154,27 +154,45 @@
#![deny(unsafe_op_in_unsafe_fn)]
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
#![cfg_attr(
docsrs,
feature(doc_auto_cfg, doc_cfg_hide),
doc(cfg_hide(doc, docsrs))
)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![allow(clippy::missing_safety_doc)]
#[cfg(feature = "rwh_06")]
pub use rwh_06 as raw_window_handle;
#[doc(inline)]
pub use winit_core::{dpi, keyboard};
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[macro_use]
extern crate bitflags;
pub mod dpi;
#[macro_use]
pub mod error;
mod cursor;
pub mod event;
pub mod event_loop;
mod icon;
pub mod keyboard;
pub mod monitor;
mod platform_impl;
pub mod window;
pub mod platform;
/// Wrapper for objects which winit will access on the main thread so they are effectively `Send`
/// and `Sync`, since they always execute on a single thread.
///
/// # Safety
///
/// Winit can run only one event loop at a time, and the event loop itself is tied to some thread.
/// The objects could be sent across the threads, but once passed to winit, they execute on the
/// main thread if the platform demands it. Thus, marking such objects as `Send + Sync` is safe.
#[doc(hidden)]
#[derive(Clone, Debug)]
pub(crate) struct SendSyncWrapper<T>(pub(crate) T);
unsafe impl<T> Send for SendSyncWrapper<T> {}
unsafe impl<T> Sync for SendSyncWrapper<T> {}

View File

@@ -10,32 +10,28 @@ use crate::{
platform_impl,
};
/// Deprecated! Use `VideoModeHandle` instead.
#[deprecated = "Renamed to `VideoModeHandle`"]
pub type VideoMode = VideoModeHandle;
/// Describes a fullscreen video mode of a monitor.
///
/// Can be acquired with [`MonitorHandle::video_modes`].
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct VideoModeHandle {
pub(crate) video_mode: platform_impl::VideoModeHandle,
pub struct VideoMode {
pub(crate) video_mode: platform_impl::VideoMode,
}
impl std::fmt::Debug for VideoModeHandle {
impl std::fmt::Debug for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.video_mode.fmt(f)
}
}
impl PartialOrd for VideoModeHandle {
fn partial_cmp(&self, other: &VideoModeHandle) -> Option<std::cmp::Ordering> {
impl PartialOrd for VideoMode {
fn partial_cmp(&self, other: &VideoMode) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VideoModeHandle {
fn cmp(&self, other: &VideoModeHandle) -> std::cmp::Ordering {
impl Ord for VideoMode {
fn cmp(&self, other: &VideoMode) -> std::cmp::Ordering {
self.monitor().cmp(&other.monitor()).then(
self.size()
.cmp(&other.size())
@@ -49,7 +45,7 @@ impl Ord for VideoModeHandle {
}
}
impl VideoModeHandle {
impl VideoMode {
/// Returns the resolution of this video mode.
#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
@@ -85,7 +81,7 @@ impl VideoModeHandle {
}
}
impl std::fmt::Display for VideoModeHandle {
impl std::fmt::Display for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
@@ -135,8 +131,8 @@ impl MonitorHandle {
/// 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.
/// When using exclusive fullscreen, the refresh rate of the [`VideoMode`] that was used to
/// enter fullscreen should be used instead.
#[inline]
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
self.inner.refresh_rate_millihertz()
@@ -165,9 +161,9 @@ impl MonitorHandle {
///
/// - **Web:** Always returns an empty iterator
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.inner
.video_modes()
.map(|video_mode| VideoModeHandle { video_mode })
.map(|video_mode| VideoMode { video_mode })
}
}

View File

@@ -3,7 +3,7 @@ use crate::{
window::{Window, WindowBuilder},
};
use self::activity::{AndroidApp, ConfigurationRef, Rect};
use android_activity::{AndroidApp, ConfigurationRef, Rect};
/// Additional methods on [`EventLoop`] that are specific to Android.
pub trait EventLoopExtAndroid {}
@@ -30,7 +30,7 @@ impl WindowExtAndroid for Window {
}
}
impl EventLoopWindowTargetExtAndroid for EventLoopWindowTarget {}
impl<T> EventLoopWindowTargetExtAndroid for EventLoopWindowTarget<T> {}
/// Additional methods on [`WindowBuilder`] that are specific to Android.
pub trait WindowBuilderExtAndroid {}
@@ -89,16 +89,5 @@ pub mod activity {
// feature enabled, so we avoid inlining it so that they're forced to view
// it on the crate's own docs.rs page.
#[doc(no_inline)]
#[cfg(android_platform)]
pub use android_activity::*;
#[cfg(not(android_platform))]
#[doc(hidden)]
pub struct Rect;
#[cfg(not(android_platform))]
#[doc(hidden)]
pub struct ConfigurationRef;
#[cfg(not(android_platform))]
#[doc(hidden)]
pub struct AndroidApp;
}

View File

@@ -1,8 +1,11 @@
use std::os::raw::c_void;
use icrate::Foundation::MainThreadMarker;
use objc2::rc::Id;
use crate::{
event_loop::EventLoop,
monitor::{MonitorHandle, VideoModeHandle},
monitor::{MonitorHandle, VideoMode},
window::{Window, WindowBuilder},
};
@@ -85,21 +88,6 @@ pub trait WindowExtIOS {
/// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc)
/// is also called for you.
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle);
/// Sets whether the [`Window`] should recognize pinch gestures.
///
/// The default is to not recognize gestures.
fn recognize_pinch_gesture(&self, should_recognize: bool);
/// Sets whether the [`Window`] should recognize double tap gestures.
///
/// The default is to not recognize gestures.
fn recognize_doubletap_gesture(&self, should_recognize: bool);
/// Sets whether the [`Window`] should recognize rotation gestures.
///
/// The default is to not recognize gestures.
fn recognize_rotation_gesture(&self, should_recognize: bool);
}
impl WindowExtIOS for Window {
@@ -139,24 +127,6 @@ impl WindowExtIOS for Window {
self.window
.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
}
#[inline]
fn recognize_pinch_gesture(&self, should_recognize: bool) {
self.window
.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
}
#[inline]
fn recognize_doubletap_gesture(&self, should_recognize: bool) {
self.window
.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
}
#[inline]
fn recognize_rotation_gesture(&self, should_recognize: bool) {
self.window
.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
}
}
/// Additional methods on [`WindowBuilder`] that are specific to iOS.
@@ -217,39 +187,38 @@ pub trait WindowBuilderExtIOS {
impl WindowBuilderExtIOS for WindowBuilder {
#[inline]
fn with_scale_factor(mut self, scale_factor: f64) -> Self {
self.window.platform_specific.scale_factor = Some(scale_factor);
self.platform_specific.scale_factor = Some(scale_factor);
self
}
#[inline]
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self {
self.window.platform_specific.valid_orientations = valid_orientations;
self.platform_specific.valid_orientations = valid_orientations;
self
}
#[inline]
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self {
self.window.platform_specific.prefers_home_indicator_hidden = hidden;
self.platform_specific.prefers_home_indicator_hidden = hidden;
self
}
#[inline]
fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
self.window
.platform_specific
self.platform_specific
.preferred_screen_edges_deferring_system_gestures = edges;
self
}
#[inline]
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self {
self.window.platform_specific.prefers_status_bar_hidden = hidden;
self.platform_specific.prefers_status_bar_hidden = hidden;
self
}
#[inline]
fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self {
self.window.platform_specific.preferred_status_bar_style = status_bar_style;
self.platform_specific.preferred_status_bar_style = status_bar_style;
self
}
}
@@ -261,23 +230,23 @@ pub trait MonitorHandleExtIOS {
/// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc
fn ui_screen(&self) -> *mut c_void;
/// Returns the preferred [`VideoModeHandle`] for this monitor.
/// Returns the preferred [`VideoMode`] for this monitor.
///
/// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc).
fn preferred_video_mode(&self) -> VideoModeHandle;
fn preferred_video_mode(&self) -> VideoMode;
}
impl MonitorHandleExtIOS for MonitorHandle {
#[inline]
fn ui_screen(&self) -> *mut c_void {
// SAFETY: The marker is only used to get the pointer of the screen
let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() };
objc2::rc::Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
let mtm = unsafe { MainThreadMarker::new_unchecked() };
Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
}
#[inline]
fn preferred_video_mode(&self) -> VideoModeHandle {
VideoModeHandle {
fn preferred_video_mode(&self) -> VideoMode {
VideoMode {
video_mode: self.inner.preferred_video_mode(),
}
}
@@ -314,7 +283,7 @@ pub enum Idiom {
CarPlay,
}
bitflags::bitflags! {
bitflags! {
/// The [edges] of a screen.
///
/// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc

View File

@@ -1,7 +1,6 @@
use std::os::raw::c_void;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use objc2::rc::Id;
use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
@@ -212,62 +211,61 @@ pub trait WindowBuilderExtMacOS {
impl WindowBuilderExtMacOS for WindowBuilder {
#[inline]
fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self {
self.window.platform_specific.movable_by_window_background = movable_by_window_background;
self.platform_specific.movable_by_window_background = movable_by_window_background;
self
}
#[inline]
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self {
self.window.platform_specific.titlebar_transparent = titlebar_transparent;
self.platform_specific.titlebar_transparent = titlebar_transparent;
self
}
#[inline]
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self {
self.window.platform_specific.titlebar_hidden = titlebar_hidden;
self.platform_specific.titlebar_hidden = titlebar_hidden;
self
}
#[inline]
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self {
self.window.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
self
}
#[inline]
fn with_title_hidden(mut self, title_hidden: bool) -> Self {
self.window.platform_specific.title_hidden = title_hidden;
self.platform_specific.title_hidden = title_hidden;
self
}
#[inline]
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self {
self.window.platform_specific.fullsize_content_view = fullsize_content_view;
self.platform_specific.fullsize_content_view = fullsize_content_view;
self
}
#[inline]
fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self {
self.window.platform_specific.disallow_hidpi = disallow_hidpi;
self.platform_specific.disallow_hidpi = disallow_hidpi;
self
}
#[inline]
fn with_has_shadow(mut self, has_shadow: bool) -> Self {
self.window.platform_specific.has_shadow = has_shadow;
self.platform_specific.has_shadow = has_shadow;
self
}
#[inline]
fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self {
self.window.platform_specific.accepts_first_mouse = accepts_first_mouse;
self.platform_specific.accepts_first_mouse = accepts_first_mouse;
self
}
#[inline]
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
self.window
.platform_specific
self.platform_specific
.tabbing_identifier
.replace(tabbing_identifier.to_string());
self
@@ -275,7 +273,7 @@ impl WindowBuilderExtMacOS for WindowBuilder {
#[inline]
fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self {
self.window.platform_specific.option_as_alt = option_as_alt;
self.platform_specific.option_as_alt = option_as_alt;
self
}
}
@@ -367,11 +365,7 @@ impl MonitorHandleExtMacOS for MonitorHandle {
}
fn ns_screen(&self) -> Option<*mut c_void> {
// SAFETY: We only use the marker to get a pointer
let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() };
self.inner
.ns_screen(mtm)
.map(|s| objc2::rc::Id::as_ptr(&s) as _)
self.inner.ns_screen().map(|s| Id::as_ptr(&s) as _)
}
}
@@ -389,7 +383,7 @@ pub trait EventLoopWindowTargetExtMacOS {
fn allows_automatic_window_tabbing(&self) -> bool;
}
impl EventLoopWindowTargetExtMacOS for EventLoopWindowTarget {
impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
fn hide_application(&self) {
self.p.hide_application()
}

57
src/platform/mod.rs Normal file
View File

@@ -0,0 +1,57 @@
//! Contains traits with platform-specific methods in them.
//!
//! Contains the follow OS-specific modules:
//!
//! - `android`
//! - `ios`
//! - `macos`
//! - `unix`
//! - `windows`
//! - `web`
//!
//! And the following platform-specific modules:
//!
//! - `run_on_demand` (available on `windows`, `unix`, `macos`, `android`)
//! - `pump_events` (available on `windows`, `unix`, `macos`, `android`)
//!
//! However only the module corresponding to the platform you're compiling to will be available.
#[cfg(android_platform)]
pub mod android;
#[cfg(ios_platform)]
pub mod ios;
#[cfg(macos_platform)]
pub mod macos;
#[cfg(orbital_platform)]
pub mod orbital;
#[cfg(any(x11_platform, wayland_platform))]
pub mod startup_notify;
#[cfg(wayland_platform)]
pub mod wayland;
#[cfg(wasm_platform)]
pub mod web;
#[cfg(windows_platform)]
pub mod windows;
#[cfg(x11_platform)]
pub mod x11;
#[cfg(any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
))]
pub mod run_on_demand;
#[cfg(any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
))]
pub mod pump_events;
pub mod modifier_supplement;
pub mod scancode;

View File

@@ -1,4 +1,5 @@
use crate::event::KeyEvent;
#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
use crate::keyboard::Key;
/// Additional methods for the `KeyEvent` which cannot be implemented on all
@@ -21,19 +22,3 @@ pub trait KeyEventExtModifierSupplement {
/// cannot be `Dead`.
fn key_without_modifiers(&self) -> Key;
}
impl KeyEventExtModifierSupplement for KeyEvent {
#[inline]
fn text_with_all_modifiers(&self) -> Option<&str> {
self.extra
.extra
.text_with_all_modifiers
.as_ref()
.map(|s| s.as_str())
}
#[inline]
fn key_without_modifiers(&self) -> Key {
self.extra.extra.key_without_modifiers.clone()
}
}

View File

@@ -51,12 +51,12 @@ pub trait EventLoopExtPumpEvents {
/// # event::{Event, WindowEvent},
/// # event_loop::EventLoop,
/// # platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
/// # window::Window,
/// # window::WindowBuilder,
/// # };
/// let mut event_loop = EventLoop::new().unwrap();
/// #
/// # SimpleLogger::new().init().unwrap();
/// let window = Window::builder()
/// let window = WindowBuilder::new()
/// .with_title("A fantastic window!")
/// .build(&event_loop)
/// .unwrap();
@@ -155,26 +155,26 @@ pub trait EventLoopExtPumpEvents {
/// - **Windows**: The implementation will use `PeekMessage` when checking for
/// window messages 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 `NSApp`
/// whenever the application `RunLoop` indicates that it is preparing to block
/// and wait for new events.
///
/// This is very different to the polling APIs that are available on other
/// platforms (the lower level polling primitives on MacOS are private
/// implementation details for `NSApplication` which aren't accessible to
/// application developers)
/// implementation details for `NSApp` which aren't accessible to application
/// developers)
///
/// It's likely this will be less efficient than polling on other OSs and
/// it also means the `NSApplication` is stopped while outside of the Winit
/// it also means the `NSApp` is stopped while outside of the Winit
/// event loop - and that's observable (for example to crates like `rfd`)
/// because the `NSApplication` is global state.
/// because the `NSApp` is global state.
///
/// If you render outside of Winit you are likely to see window resizing artifacts
/// since MacOS expects applications to render synchronously during any `drawRect`
/// callback.
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget);
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
}
impl<T> EventLoopExtPumpEvents for EventLoop<T> {
@@ -182,7 +182,7 @@ impl<T> EventLoopExtPumpEvents for EventLoop<T> {
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget),
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{
self.event_loop.pump_events(timeout, event_handler)
}

View File

@@ -55,10 +55,10 @@ pub trait EventLoopExtRunOnDemand {
/// loop that would block the browser and there is nothing that can be
/// polled to ask for new events. Events are delivered via callbacks based
/// on an event loop that is internal to the browser itself.
/// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS.
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS.
///
#[cfg_attr(
not(web_platform),
not(wasm_platform),
doc = "[^1]: `spawn()` is only available on `wasm` platforms."
)]
///
@@ -66,7 +66,7 @@ pub trait EventLoopExtRunOnDemand {
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow()
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget);
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
}
impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
@@ -74,29 +74,16 @@ impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget),
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{
self.event_loop.window_target().clear_exit();
self.event_loop.run_on_demand(event_handler)
}
}
impl EventLoopWindowTarget {
impl<T> EventLoopWindowTarget<T> {
/// Clear exit status.
pub(crate) fn clear_exit(&self) {
self.p.clear_exit()
}
}
/// ```compile_fail
/// use winit::event_loop::EventLoop;
/// use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
///
/// let mut event_loop = EventLoop::new().unwrap();
/// event_loop.run_on_demand(|_, _| {
/// // Attempt to run the event loop re-entrantly; this must fail.
/// event_loop.run_on_demand(|_, _| {});
/// });
/// ```
#[allow(dead_code)]
fn test_run_on_demand_cannot_access_event_loop() {}

View File

@@ -1,3 +1,5 @@
#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
use crate::keyboard::{KeyCode, PhysicalKey};
// TODO: Describe what this value contains for each platform
@@ -27,24 +29,17 @@ pub trait PhysicalKeyExtScancode {
fn from_scancode(scancode: u32) -> PhysicalKey;
}
impl PhysicalKeyExtScancode for PhysicalKey {
fn to_scancode(self) -> Option<u32> {
crate::platform_impl::physicalkey_to_scancode(self)
}
fn from_scancode(scancode: u32) -> PhysicalKey {
crate::platform_impl::scancode_to_physicalkey(scancode)
}
}
impl PhysicalKeyExtScancode for KeyCode {
#[inline]
fn to_scancode(self) -> Option<u32> {
<PhysicalKey as PhysicalKeyExtScancode>::to_scancode(PhysicalKey::Code(self))
}
impl PhysicalKeyExtScancode for KeyCode
where
PhysicalKey: PhysicalKeyExtScancode,
{
#[inline]
fn from_scancode(scancode: u32) -> PhysicalKey {
<PhysicalKey as PhysicalKeyExtScancode>::from_scancode(scancode)
}
#[inline]
fn to_scancode(self) -> Option<u32> {
<PhysicalKey as PhysicalKeyExtScancode>::to_scancode(PhysicalKey::Code(self))
}
}

View File

@@ -55,7 +55,7 @@ pub trait WindowBuilderExtStartupNotify {
fn with_activation_token(self, token: ActivationToken) -> Self;
}
impl EventLoopExtStartupNotify for EventLoopWindowTarget {
impl<T> EventLoopExtStartupNotify for EventLoopWindowTarget<T> {
fn read_token_from_env(&self) -> Option<ActivationToken> {
match self.p {
#[cfg(wayland_platform)]
@@ -64,7 +64,7 @@ impl EventLoopExtStartupNotify for EventLoopWindowTarget {
crate::platform_impl::EventLoopWindowTarget::X(_) => env::var(X11_VAR),
}
.ok()
.map(ActivationToken::new)
.map(ActivationToken::_new)
}
}
@@ -76,7 +76,7 @@ impl WindowExtStartupNotify for Window {
impl WindowBuilderExtStartupNotify for WindowBuilder {
fn with_activation_token(mut self, token: ActivationToken) -> Self {
self.window.platform_specific.activation_token = Some(token);
self.platform_specific.activation_token = Some(token);
self
}
}
@@ -94,6 +94,6 @@ pub fn reset_activation_token_env() {
///
/// This could be used before running daemon processes.
pub fn set_activation_token_env(token: ActivationToken) {
env::set_var(X11_VAR, token.token());
env::set_var(WAYLAND_VAR, token.token());
env::set_var(X11_VAR, &token._token);
env::set_var(WAYLAND_VAR, token._token);
}

View File

@@ -4,6 +4,8 @@ use crate::{
window::{Window, WindowBuilder},
};
use crate::platform_impl::{ApplicationName, Backend};
pub use crate::window::Theme;
/// Additional methods on [`EventLoopWindowTarget`] that are specific to Wayland.
@@ -12,7 +14,7 @@ pub trait EventLoopWindowTargetExtWayland {
fn is_wayland(&self) -> bool;
}
impl EventLoopWindowTargetExtWayland for EventLoopWindowTarget {
impl<T> EventLoopWindowTargetExtWayland for EventLoopWindowTarget<T> {
#[inline]
fn is_wayland(&self) -> bool {
self.p.is_wayland()
@@ -34,7 +36,7 @@ pub trait EventLoopBuilderExtWayland {
impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> {
#[inline]
fn with_wayland(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland);
self.platform_specific.forced_backend = Some(Backend::Wayland);
self
}
@@ -65,10 +67,7 @@ pub trait WindowBuilderExtWayland {
impl WindowBuilderExtWayland for WindowBuilder {
#[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.window.platform_specific.name = Some(crate::platform_impl::ApplicationName::new(
general.into(),
instance.into(),
));
self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into()));
self
}
}

View File

@@ -27,47 +27,17 @@
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use crate::event::Event;
use crate::event_loop::EventLoop;
use crate::event_loop::EventLoopWindowTarget;
use crate::window::{Window, WindowBuilder};
use crate::SendSyncWrapper;
#[cfg(web_platform)]
use web_sys::HtmlCanvasElement;
use crate::cursor::CustomCursorBuilder;
use crate::event::Event;
use crate::event_loop::{EventLoop, EventLoopWindowTarget};
#[cfg(web_platform)]
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
use crate::platform_impl::{PlatformCustomCursor, PlatformCustomCursorBuilder};
use crate::window::{CustomCursor, Window, WindowBuilder};
#[cfg(not(web_platform))]
#[doc(hidden)]
pub struct HtmlCanvasElement;
pub trait WindowExtWebSys {
/// Only returns the canvas if called from inside the window context (the
/// main thread).
/// Only returns the canvas if called from inside the window.
fn canvas(&self) -> Option<HtmlCanvasElement>;
/// Returns [`true`] if calling `event.preventDefault()` is enabled.
///
/// See [`Window::set_prevent_default()`] for more details.
fn prevent_default(&self) -> bool;
/// Sets whether `event.preventDefault()` should be called on events on the
/// canvas that have side effects.
///
/// For example, by default using the mouse wheel would cause the page to scroll, enabling this
/// would prevent that.
///
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
/// context menu with Shift+Rightclick.
fn set_prevent_default(&self, prevent_default: bool);
}
impl WindowExtWebSys for Window {
@@ -75,14 +45,6 @@ impl WindowExtWebSys for Window {
fn canvas(&self) -> Option<HtmlCanvasElement> {
self.window.canvas()
}
fn prevent_default(&self) -> bool {
self.window.prevent_default()
}
fn set_prevent_default(&self, prevent_default: bool) {
self.window.set_prevent_default(prevent_default)
}
}
pub trait WindowBuilderExtWebSys {
@@ -92,17 +54,16 @@ pub trait WindowBuilderExtWebSys {
/// In any case, the canvas won't be automatically inserted into the web page.
///
/// [`None`] by default.
#[cfg_attr(
not(web_platform),
doc = "",
doc = "[`HtmlCanvasElement`]: #only-available-on-wasm"
)]
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
/// Sets whether `event.preventDefault()` should be called on events on the
/// canvas that have side effects.
/// Whether `event.preventDefault` should be automatically called to prevent event propagation
/// when appropriate.
///
/// See [`Window::set_prevent_default()`] for more details.
/// For example, mouse wheel events are only handled by the canvas by default. This avoids
/// the default behavior of scrolling the page.
///
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
/// context menu with Shift+Rightclick.
///
/// Enabled by default.
fn with_prevent_default(self, prevent_default: bool) -> Self;
@@ -121,22 +82,22 @@ pub trait WindowBuilderExtWebSys {
impl WindowBuilderExtWebSys for WindowBuilder {
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
self.window.platform_specific.set_canvas(canvas);
self.platform_specific.canvas = SendSyncWrapper(canvas);
self
}
fn with_prevent_default(mut self, prevent_default: bool) -> Self {
self.window.platform_specific.prevent_default = prevent_default;
self.platform_specific.prevent_default = prevent_default;
self
}
fn with_focusable(mut self, focusable: bool) -> Self {
self.window.platform_specific.focusable = focusable;
self.platform_specific.focusable = focusable;
self
}
fn with_append(mut self, append: bool) -> Self {
self.window.platform_specific.append = append;
self.platform_specific.append = append;
self
}
}
@@ -150,11 +111,11 @@ pub trait EventLoopExtWebSys {
///
/// Unlike
#[cfg_attr(
all(web_platform, target_feature = "exception-handling"),
all(wasm_platform, target_feature = "exception-handling"),
doc = "`run()`"
)]
#[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")),
not(all(wasm_platform, target_feature = "exception-handling")),
doc = "[`run()`]"
)]
/// [^1], this returns immediately, and doesn't throw an exception in order to
@@ -166,13 +127,13 @@ pub trait EventLoopExtWebSys {
/// event loop when switching between tabs on a single page application.
///
#[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")),
not(all(wasm_platform, target_feature = "exception-handling")),
doc = "[`run()`]: EventLoop::run()"
)]
/// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`.
fn spawn<F>(self, event_handler: F)
where
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget);
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
}
impl<T> EventLoopExtWebSys for EventLoop<T> {
@@ -180,7 +141,7 @@ impl<T> EventLoopExtWebSys for EventLoop<T> {
fn spawn<F>(self, event_handler: F)
where
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget),
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{
self.event_loop.spawn(event_handler)
}
@@ -202,7 +163,7 @@ pub trait EventLoopWindowTargetExtWebSys {
fn poll_strategy(&self) -> PollStrategy;
}
impl EventLoopWindowTargetExtWebSys for EventLoopWindowTarget {
impl<T> EventLoopWindowTargetExtWebSys for EventLoopWindowTarget<T> {
#[inline]
fn set_poll_strategy(&self, strategy: PollStrategy) {
self.p.set_poll_strategy(strategy);
@@ -239,126 +200,3 @@ pub enum PollStrategy {
#[default]
Scheduler,
}
pub trait CustomCursorExtWebSys {
/// Returns if this cursor is an animation.
fn is_animation(&self) -> bool;
/// Creates a new cursor from a URL pointing to an image.
/// It uses the [url css function](https://developer.mozilla.org/en-US/docs/Web/CSS/url),
/// but browser support for image formats is inconsistent. Using [PNG] is recommended.
///
/// [PNG]: https://en.wikipedia.org/wiki/PNG
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder;
/// Crates a new animated cursor from multiple [`CustomCursor`]s.
/// Supplied `cursors` can't be empty or other animations.
fn from_animation(
duration: Duration,
cursors: Vec<CustomCursor>,
) -> Result<CustomCursorBuilder, BadAnimation>;
}
impl CustomCursorExtWebSys for CustomCursor {
fn is_animation(&self) -> bool {
self.inner.animation
}
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder {
CustomCursorBuilder {
inner: PlatformCustomCursorBuilder::Url {
url,
hotspot_x,
hotspot_y,
},
}
}
fn from_animation(
duration: Duration,
cursors: Vec<CustomCursor>,
) -> Result<CustomCursorBuilder, BadAnimation> {
if cursors.is_empty() {
return Err(BadAnimation::Empty);
}
if cursors.iter().any(CustomCursor::is_animation) {
return Err(BadAnimation::Animation);
}
Ok(CustomCursorBuilder {
inner: PlatformCustomCursorBuilder::Animation { duration, cursors },
})
}
}
/// An error produced when using [`CustomCursor::from_animation`] with invalid arguments.
#[derive(Debug, Clone)]
pub enum BadAnimation {
/// Produced when no cursors were supplied.
Empty,
/// Produced when a supplied cursor is an animation.
Animation,
}
impl fmt::Display for BadAnimation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => write!(f, "No cursors supplied"),
Self::Animation => write!(f, "A supplied cursor is an animtion"),
}
}
}
impl Error for BadAnimation {}
pub trait CustomCursorBuilderExtWebSys {
/// Async version of [`CustomCursorBuilder::build()`] which waits until the
/// cursor has completely finished loading.
fn build_async(self, window_target: &EventLoopWindowTarget) -> CustomCursorFuture;
}
impl CustomCursorBuilderExtWebSys for CustomCursorBuilder {
fn build_async(self, window_target: &EventLoopWindowTarget) -> CustomCursorFuture {
CustomCursorFuture(PlatformCustomCursor::build_async(
self.inner,
&window_target.p,
))
}
}
#[cfg(not(web_platform))]
struct PlatformCustomCursorFuture;
#[derive(Debug)]
pub struct CustomCursorFuture(PlatformCustomCursorFuture);
impl Future for CustomCursorFuture {
type Output = Result<CustomCursor, CustomCursorError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0)
.poll(cx)
.map_ok(|cursor| CustomCursor { inner: cursor })
}
}
#[derive(Clone, Debug)]
pub enum CustomCursorError {
Blob,
Decode(String),
Animation,
}
impl Display for CustomCursorError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Blob => write!(f, "failed to create `Blob`"),
Self::Decode(error) => write!(f, "failed to decode image: {error}"),
Self::Animation => write!(
f,
"found `CustomCursor` that is an animation when building an animation"
),
}
}
}

View File

@@ -2,9 +2,12 @@ use std::{ffi::c_void, path::Path};
use crate::{
dpi::PhysicalSize,
event::DeviceId,
event::{DeviceId, KeyEvent},
event_loop::EventLoopBuilder,
keyboard::Key,
monitor::MonitorHandle,
platform::modifier_supplement::KeyEventExtModifierSupplement,
platform_impl::WinIcon,
window::{BadIcon, Icon, Window, WindowBuilder},
};
@@ -15,92 +18,6 @@ pub type HMENU = isize;
/// Monitor Handle type used by Win32 API
pub type HMONITOR = isize;
/// Describes a system-drawn backdrop material of a window.
///
/// For a detailed explanation, see [`DWM_SYSTEMBACKDROP_TYPE docs`].
///
/// [`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)]
pub enum BackdropType {
/// Corresponds to `DWMSBT_AUTO`.
///
/// Usually draws a default backdrop effect on the title bar.
#[default]
Auto = 0,
/// Corresponds to `DWMSBT_NONE`.
None = 1,
/// Corresponds to `DWMSBT_MAINWINDOW`.
///
/// Draws the Mica backdrop material.
MainWindow = 2,
/// Corresponds to `DWMSBT_TRANSIENTWINDOW`.
///
/// Draws the Background Acrylic backdrop material.
TransientWindow = 3,
/// Corresponds to `DWMSBT_TABBEDWINDOW`.
///
/// Draws the Alt Mica backdrop material.
TabbedWindow = 4,
}
/// Describes a color used by Windows
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Color(u32);
impl Color {
/// Use the system's default color
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
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self((r as u32) | ((g as u32) << 8) | ((b as u32) << 16))
}
}
impl Default for Color {
fn default() -> Self {
Self::SYSTEM_DEFAULT
}
}
/// Describes how the corners of a window should look like.
///
/// For a detailed explanation, see [`DWM_WINDOW_CORNER_PREFERENCE docs`].
///
/// [`DWM_WINDOW_CORNER_PREFERENCE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference
#[repr(i32)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CornerPreference {
/// Corresponds to `DWMWCP_DEFAULT`.
///
/// Let the system decide when to round window corners.
#[default]
Default = 0,
/// Corresponds to `DWMWCP_DONOTROUND`.
///
/// Never round window corners.
DoNotRound = 1,
/// Corresponds to `DWMWCP_ROUND`.
///
/// Round the corners, if appropriate.
Round = 2,
/// Corresponds to `DWMWCP_ROUNDSMALL`.
///
/// Round the corners if appropriate, with a small radius.
RoundSmall = 3,
}
/// Additional methods on `EventLoop` that are specific to Windows.
pub trait EventLoopBuilderExtWindows {
/// Whether to allow the event loop to be created off of the main thread.
@@ -219,31 +136,6 @@ pub trait WindowExtWindows {
///
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn set_undecorated_shadow(&self, shadow: bool);
/// Sets system-drawn backdrop type.
///
/// Requires Windows 11 build 22523+.
fn set_system_backdrop(&self, backdrop_type: BackdropType);
/// Sets the color of the window border.
///
/// Supported starting with Windows 11 Build 22000.
fn set_border_color(&self, color: Option<Color>);
/// Sets the background color of the title bar.
///
/// Supported starting with Windows 11 Build 22000.
fn set_title_background_color(&self, color: Option<Color>);
/// Sets the color of the window title.
///
/// Supported starting with Windows 11 Build 22000.
fn set_title_text_color(&self, color: Color);
/// Sets the preferred style of the window corners.
///
/// Supported starting with Windows 11 Build 22000.
fn set_corner_preference(&self, preference: CornerPreference);
}
impl WindowExtWindows for Window {
@@ -266,34 +158,6 @@ impl WindowExtWindows for Window {
fn set_undecorated_shadow(&self, shadow: bool) {
self.window.set_undecorated_shadow(shadow)
}
#[inline]
fn set_system_backdrop(&self, backdrop_type: BackdropType) {
self.window.set_system_backdrop(backdrop_type)
}
#[inline]
fn set_border_color(&self, color: Option<Color>) {
self.window.set_border_color(color.unwrap_or(Color::NONE))
}
#[inline]
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
// to circumvent the Windows option "Show accent color on title bars and window borders"
self.window
.set_title_background_color(color.unwrap_or(Color::NONE))
}
#[inline]
fn set_title_text_color(&self, color: Color) {
self.window.set_title_text_color(color)
}
#[inline]
fn set_corner_preference(&self, preference: CornerPreference) {
self.window.set_corner_preference(preference)
}
}
/// Additional methods on `WindowBuilder` that are specific to Windows.
@@ -321,14 +185,7 @@ pub trait WindowBuilderExtWindows {
/// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how 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(
platform_windows,
doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
)]
#[cfg_attr(
not(platform_windows),
doc = "[`CreateMenu`]: #only-available-on-windows"
)]
/// [`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu
fn with_menu(self, menu: HMENU) -> Self;
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
@@ -356,118 +213,54 @@ pub trait WindowBuilderExtWindows {
/// The shadow is hidden by default.
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn with_undecorated_shadow(self, shadow: bool) -> Self;
/// Sets system-drawn backdrop type.
///
/// Requires Windows 11 build 22523+.
fn with_system_backdrop(self, backdrop_type: BackdropType) -> Self;
/// This sets or removes `WS_CLIPCHILDREN` style.
fn with_clip_children(self, flag: bool) -> Self;
/// Sets the color of the window border.
///
/// Supported starting with Windows 11 Build 22000.
fn with_border_color(self, color: Option<Color>) -> Self;
/// Sets the background color of the title bar.
///
/// Supported starting with Windows 11 Build 22000.
fn with_title_background_color(self, color: Option<Color>) -> Self;
/// Sets the color of the window title.
///
/// Supported starting with Windows 11 Build 22000.
fn with_title_text_color(self, color: Color) -> Self;
/// Sets the preferred style of the window corners.
///
/// Supported starting with Windows 11 Build 22000.
fn with_corner_preference(self, corners: CornerPreference) -> Self;
}
impl WindowBuilderExtWindows for WindowBuilder {
#[inline]
fn with_owner_window(mut self, parent: HWND) -> Self {
self.window.platform_specific.owner = Some(parent);
self.platform_specific.owner = Some(parent);
self
}
#[inline]
fn with_menu(mut self, menu: HMENU) -> Self {
self.window.platform_specific.menu = Some(menu);
self.platform_specific.menu = Some(menu);
self
}
#[inline]
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self {
self.window.platform_specific.taskbar_icon = taskbar_icon;
self.platform_specific.taskbar_icon = taskbar_icon;
self
}
#[inline]
fn with_no_redirection_bitmap(mut self, flag: bool) -> Self {
self.window.platform_specific.no_redirection_bitmap = flag;
self.platform_specific.no_redirection_bitmap = flag;
self
}
#[inline]
fn with_drag_and_drop(mut self, flag: bool) -> Self {
self.window.platform_specific.drag_and_drop = flag;
self.platform_specific.drag_and_drop = flag;
self
}
#[inline]
fn with_skip_taskbar(mut self, skip: bool) -> Self {
self.window.platform_specific.skip_taskbar = skip;
self.platform_specific.skip_taskbar = skip;
self
}
#[inline]
fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self {
self.window.platform_specific.class_name = class_name.into();
self.platform_specific.class_name = class_name.into();
self
}
#[inline]
fn with_undecorated_shadow(mut self, shadow: bool) -> Self {
self.window.platform_specific.decoration_shadow = shadow;
self
}
#[inline]
fn with_system_backdrop(mut self, backdrop_type: BackdropType) -> Self {
self.window.platform_specific.backdrop_type = backdrop_type;
self
}
#[inline]
fn with_clip_children(mut self, flag: bool) -> Self {
self.window.platform_specific.clip_children = flag;
self
}
#[inline]
fn with_border_color(mut self, color: Option<Color>) -> Self {
self.window.platform_specific.border_color = Some(color.unwrap_or(Color::NONE));
self
}
#[inline]
fn with_title_background_color(mut self, color: Option<Color>) -> Self {
self.window.platform_specific.title_background_color = Some(color.unwrap_or(Color::NONE));
self
}
#[inline]
fn with_title_text_color(mut self, color: Color) -> Self {
self.window.platform_specific.title_text_color = Some(color);
self
}
#[inline]
fn with_corner_preference(mut self, corners: CornerPreference) -> Self {
self.window.platform_specific.corner_preference = Some(corners);
self.platform_specific.decoration_shadow = shadow;
self
}
}
@@ -504,7 +297,7 @@ pub trait DeviceIdExtWindows {
impl DeviceIdExtWindows for DeviceId {
#[inline]
fn persistent_identifier(&self) -> Option<String> {
crate::platform_impl::persistent_identifier(*self)
self.0.persistent_identifier()
}
}
@@ -535,12 +328,27 @@ impl IconExtWindows for Icon {
path: P,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
let win_icon = crate::platform_impl::WinIcon::from_path(path, size)?;
let win_icon = WinIcon::from_path(path, size)?;
Ok(Icon { inner: win_icon })
}
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
let win_icon = crate::platform_impl::WinIcon::from_resource(ordinal, size)?;
let win_icon = WinIcon::from_resource(ordinal, size)?;
Ok(Icon { inner: win_icon })
}
}
impl KeyEventExtModifierSupplement for KeyEvent {
#[inline]
fn text_with_all_modifiers(&self) -> Option<&str> {
self.platform_specific
.text_with_all_modifers
.as_ref()
.map(|s| s.as_str())
}
#[inline]
fn key_without_modifiers(&self) -> Key {
self.platform_specific.key_without_modifiers.clone()
}
}

View File

@@ -1,6 +1,3 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
monitor::MonitorHandle,
@@ -8,50 +5,9 @@ use crate::{
};
use crate::dpi::Size;
use crate::platform_impl::{ApplicationName, Backend, XLIB_ERROR_HOOKS};
/// X window type. Maps directly to
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum WindowType {
/// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
/// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
/// root window clicks.
Desktop,
/// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
Dock,
/// Toolbar windows. "Torn off" from the main application.
Toolbar,
/// Pinnable menu windows. "Torn off" from the main application.
Menu,
/// A small persistent utility window, such as a palette or toolbox.
Utility,
/// The window is a splash screen displayed as an application is starting up.
Splash,
/// This is a dialog window.
Dialog,
/// A dropdown menu that usually appears when the user clicks on an item in a menu bar.
/// This property is typically used on override-redirect windows.
DropdownMenu,
/// A popup menu that usually appears when the user right clicks on an object.
/// This property is typically used on override-redirect windows.
PopupMenu,
/// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.
/// This property is typically used on override-redirect windows.
Tooltip,
/// The window is a notification.
/// This property is typically used on override-redirect windows.
Notification,
/// This should be used on the windows that are popped up by combo boxes.
/// This property is typically used on override-redirect windows.
Combo,
/// This indicates the the window is being dragged.
/// This property is typically used on override-redirect windows.
Dnd,
/// This is a normal, top-level window.
#[default]
Normal,
}
pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported};
/// The first argument in the provided hook will be the pointer to `XDisplay`
/// and the second one the pointer to [`XErrorEvent`]. The returned `bool` is an
@@ -82,10 +38,7 @@ pub type XWindow = u32;
pub fn register_xlib_error_hook(hook: XlibErrorHook) {
// Append new hook.
unsafe {
crate::platform_impl::XLIB_ERROR_HOOKS
.lock()
.unwrap()
.push(hook);
XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
}
}
@@ -95,7 +48,7 @@ pub trait EventLoopWindowTargetExtX11 {
fn is_x11(&self) -> bool;
}
impl EventLoopWindowTargetExtX11 for EventLoopWindowTarget {
impl<T> EventLoopWindowTargetExtX11 for EventLoopWindowTarget<T> {
#[inline]
fn is_x11(&self) -> bool {
!self.p.is_wayland()
@@ -117,7 +70,7 @@ pub trait EventLoopBuilderExtX11 {
impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> {
#[inline]
fn with_x11(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X);
self.platform_specific.forced_backend = Some(Backend::X);
self
}
@@ -143,29 +96,29 @@ pub trait WindowBuilderExtX11 {
/// Build window with the given `general` and `instance` names.
///
/// 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) = "general", "instance"`.
///
/// 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)
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;
/// Build window with override-redirect flag; defaults to false.
/// Build window with override-redirect flag; defaults to false. Only relevant on X11.
fn with_override_redirect(self, override_redirect: bool) -> Self;
/// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`.
fn with_x11_window_type(self, x11_window_type: Vec<WindowType>) -> Self;
/// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. Only relevant on X11.
fn with_x11_window_type(self, x11_window_type: Vec<XWindowType>) -> Self;
/// Build window with base size hint.
/// Build window with base size hint. Only implemented on X11.
///
/// ```
/// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::window::Window;
/// # use winit::window::WindowBuilder;
/// # use winit::platform::x11::WindowBuilderExtX11;
/// // Specify the size in logical dimensions like this:
/// Window::builder().with_base_size(LogicalSize::new(400.0, 200.0));
/// WindowBuilder::new().with_base_size(LogicalSize::new(400.0, 200.0));
///
/// // Or specify the size in physical dimensions like this:
/// Window::builder().with_base_size(PhysicalSize::new(400, 200));
/// WindowBuilder::new().with_base_size(PhysicalSize::new(400, 200));
/// ```
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
@@ -174,12 +127,12 @@ pub trait WindowBuilderExtX11 {
/// # Example
///
/// ```no_run
/// use winit::window::Window;
/// use winit::window::WindowBuilder;
/// use winit::platform::x11::{XWindow, WindowBuilderExtX11};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let event_loop = winit::event_loop::EventLoop::new().unwrap();
/// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?;
/// let window = Window::builder()
/// let window = WindowBuilder::new()
/// .with_embed_parent_window(parent_window_id)
/// .build(&event_loop)?;
/// # Ok(()) }
@@ -190,46 +143,43 @@ pub trait WindowBuilderExtX11 {
impl WindowBuilderExtX11 for WindowBuilder {
#[inline]
fn with_x11_visual(mut self, visual_id: XVisualID) -> Self {
self.window.platform_specific.x11.visual_id = Some(visual_id);
self.platform_specific.x11.visual_id = Some(visual_id);
self
}
#[inline]
fn with_x11_screen(mut self, screen_id: i32) -> Self {
self.window.platform_specific.x11.screen_id = Some(screen_id);
self.platform_specific.x11.screen_id = Some(screen_id);
self
}
#[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.window.platform_specific.name = Some(crate::platform_impl::ApplicationName::new(
general.into(),
instance.into(),
));
self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into()));
self
}
#[inline]
fn with_override_redirect(mut self, override_redirect: bool) -> Self {
self.window.platform_specific.x11.override_redirect = override_redirect;
self.platform_specific.x11.override_redirect = override_redirect;
self
}
#[inline]
fn with_x11_window_type(mut self, x11_window_types: Vec<WindowType>) -> Self {
self.window.platform_specific.x11.x11_window_types = x11_window_types;
fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self {
self.platform_specific.x11.x11_window_types = x11_window_types;
self
}
#[inline]
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
self.window.platform_specific.x11.base_size = Some(base_size.into());
self.platform_specific.x11.base_size = Some(base_size.into());
self
}
#[inline]
fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self {
self.window.platform_specific.x11.embed_window = Some(parent_window_id);
self.platform_specific.x11.embed_window = Some(parent_window_id);
self
}
}

View File

@@ -4,7 +4,6 @@ use std::{
cell::Cell,
collections::VecDeque,
hash::Hash,
marker::PhantomData,
sync::{
atomic::{AtomicBool, Ordering},
mpsc, Arc, Mutex, RwLock,
@@ -16,19 +15,16 @@ use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
use android_activity::{
AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect,
};
use log::{debug, trace, warn};
use once_cell::sync::Lazy;
use crate::{
cursor::Cursor,
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error,
event::{self, Force, InnerSizeWriter, StartCause},
event_loop::{self, ControlFlow, DeviceEvents, EventLoopWindowTarget as RootELW},
platform::pump_events::PumpStatus,
window::{
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowId,
WindowLevel,
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
},
};
use crate::{error::EventLoopError, platform_impl::Fullscreen};
@@ -36,7 +32,6 @@ use crate::{error::EventLoopError, platform_impl::Fullscreen};
mod keycodes;
static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));
const WINDOW_ID: u64 = 0;
/// Returns the minimum `Option<Duration>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use
@@ -143,7 +138,7 @@ pub struct KeyEventExtra {}
pub struct EventLoop<T: 'static> {
android_app: AndroidApp,
window_target: event_loop::EventLoopWindowTarget,
window_target: event_loop::EventLoopWindowTarget<T>,
redraw_flag: SharedFlag,
user_events_sender: mpsc::Sender<T>,
user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent
@@ -190,8 +185,9 @@ impl<T: 'static> EventLoop<T> {
&redraw_flag,
android_app.create_waker(),
),
_marker: std::marker::PhantomData,
},
_marker: PhantomData,
_marker: std::marker::PhantomData,
},
redraw_flag,
user_events_sender,
@@ -207,7 +203,7 @@ impl<T: 'static> EventLoop<T> {
fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
where
F: FnMut(event::Event<T>, &RootELW),
F: FnMut(event::Event<T>, &RootELW<T>),
{
trace!("Mainloop iteration");
@@ -236,7 +232,7 @@ impl<T: 'static> EventLoop<T> {
*HAS_FOCUS.write().unwrap() = true;
callback(
event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID),
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(true),
},
self.window_target(),
@@ -246,7 +242,7 @@ impl<T: 'static> EventLoop<T> {
*HAS_FOCUS.write().unwrap() = false;
callback(
event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID),
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(false),
},
self.window_target(),
@@ -261,7 +257,7 @@ impl<T: 'static> EventLoop<T> {
MonitorHandle::new(self.android_app.clone()).size(),
));
let event = event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID),
window_id: window::WindowId(WindowId),
event: event::WindowEvent::ScaleFactorChanged {
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
&new_inner_size,
@@ -349,7 +345,7 @@ impl<T: 'static> EventLoop<T> {
PhysicalSize::new(0, 0)
};
let event = event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID),
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Resized(size),
};
callback(event, self.window_target());
@@ -359,7 +355,7 @@ impl<T: 'static> EventLoop<T> {
if pending_redraw {
pending_redraw = false;
let event = event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID),
window_id: window::WindowId(WindowId),
event: event::WindowEvent::RedrawRequested,
};
callback(event, self.window_target());
@@ -379,13 +375,13 @@ impl<T: 'static> EventLoop<T> {
callback: &mut F,
) -> InputStatus
where
F: FnMut(event::Event<T>, &RootELW),
F: FnMut(event::Event<T>, &RootELW<T>),
{
let mut input_status = InputStatus::Handled;
match event {
InputEvent::MotionEvent(motion_event) => {
let window_id = WindowId::from(WINDOW_ID);
let device_id = event::DeviceId::from(motion_event.device_id() as u64);
let window_id = window::WindowId(WindowId);
let device_id = event::DeviceId(DeviceId(motion_event.device_id()));
let phase = match motion_event.action() {
MotionAction::Down | MotionAction::PointerDown => {
@@ -455,9 +451,9 @@ impl<T: 'static> EventLoop<T> {
);
let event = event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID),
window_id: window::WindowId(WindowId),
event: event::WindowEvent::KeyboardInput {
device_id: event::DeviceId::from(key.device_id() as u64),
device_id: event::DeviceId(DeviceId(key.device_id())),
event: event::KeyEvent {
state,
physical_key: keycodes::to_physical_key(keycode),
@@ -465,9 +461,7 @@ impl<T: 'static> EventLoop<T> {
location: keycodes::to_location(keycode),
repeat: key.repeat_count() > 0,
text: None,
extra: event::KeyExtra {
extra: KeyEventExtra {},
},
platform_specific: KeyEventExtra {},
},
is_synthetic: false,
},
@@ -486,15 +480,19 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget),
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>),
{
self.run_on_demand(event_handler)
}
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget),
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>),
{
if self.loop_running {
return Err(EventLoopError::AlreadyRunning);
}
loop {
match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => {
@@ -512,7 +510,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where
F: FnMut(event::Event<T>, &RootELW),
F: FnMut(event::Event<T>, &RootELW<T>),
{
if !self.loop_running {
self.loop_running = true;
@@ -545,7 +543,7 @@ impl<T: 'static> EventLoop<T> {
fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where
F: FnMut(event::Event<T>, &RootELW),
F: FnMut(event::Event<T>, &RootELW<T>),
{
let start = Instant::now();
@@ -621,7 +619,7 @@ impl<T: 'static> EventLoop<T> {
});
}
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget {
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {
&self.window_target
}
@@ -665,14 +663,15 @@ impl<T> EventLoopProxy<T> {
}
}
pub struct EventLoopWindowTarget {
pub struct EventLoopWindowTarget<T: 'static> {
app: AndroidApp,
control_flow: Cell<ControlFlow>,
exit: Cell<bool>,
redraw_requester: RedrawRequester,
_marker: std::marker::PhantomData<T>,
}
impl EventLoopWindowTarget {
impl<T: 'static> EventLoopWindowTarget<T> {
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle::new(self.app.clone()))
}
@@ -721,28 +720,35 @@ impl EventLoopWindowTarget {
pub(crate) fn exiting(&self) -> bool {
self.exit.get()
}
}
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) struct WindowId;
impl WindowId {
pub const fn dummy() -> Self {
WindowId
}
}
#[derive(Clone)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_05")]
#[inline]
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
rwh_05::AndroidDisplayHandle::empty().into()
impl From<WindowId> for u64 {
fn from(_: WindowId) -> Self {
0
}
}
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::AndroidDisplayHandle::new().into())
impl From<u64> for WindowId {
fn from(_: u64) -> Self {
Self
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DeviceId(i32);
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId(0)
}
}
@@ -755,9 +761,10 @@ pub(crate) struct Window {
}
impl Window {
pub(crate) fn new(
el: &EventLoopWindowTarget,
pub(crate) fn new<T: 'static>(
el: &EventLoopWindowTarget<T>,
_window_attrs: window::WindowAttributes,
_: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, error::OsError> {
// FIXME this ignores requested window attributes
@@ -776,7 +783,7 @@ impl Window {
}
pub fn id(&self) -> WindowId {
WindowId::from(WINDOW_ID)
WindowId
}
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
@@ -901,7 +908,7 @@ impl Window {
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
pub fn set_cursor(&self, _: Cursor) {}
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
@@ -1028,8 +1035,6 @@ impl Display for OsError {
}
}
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
@@ -1081,11 +1086,11 @@ impl MonitorHandle {
None
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let size = self.size().into();
// FIXME this is not the real refresh rate
// (it is guaranteed to support 32 bit color though)
std::iter::once(VideoModeHandle {
std::iter::once(VideoMode {
size,
bit_depth: 32,
refresh_rate_millihertz: 60000,
@@ -1095,14 +1100,14 @@ impl MonitorHandle {
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct VideoModeHandle {
pub struct VideoMode {
size: (u32, u32),
bit_depth: u16,
refresh_rate_millihertz: u32,
monitor: MonitorHandle,
}
impl VideoModeHandle {
impl VideoMode {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}

View File

@@ -3,7 +3,7 @@
use std::{
cell::{RefCell, RefMut},
collections::HashSet,
fmt, mem,
mem,
os::raw::c_void,
ptr,
sync::{Arc, Mutex},
@@ -24,12 +24,14 @@ use objc2::runtime::AnyObject;
use objc2::{msg_send, sel};
use once_cell::sync::Lazy;
use super::event_loop::{EventHandler, Never};
use super::uikit::UIView;
use super::view::WinitUIWindow;
use crate::{
dpi::PhysicalSize,
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget},
event_loop::ControlFlow,
window::WindowId as RootWindowId,
};
macro_rules! bug {
@@ -45,32 +47,8 @@ macro_rules! bug_assert {
}
#[derive(Debug)]
pub(crate) struct HandlePendingUserEvents;
pub(crate) struct EventLoopHandler {
#[allow(clippy::type_complexity)]
pub(crate) handler: Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>,
pub(crate) event_loop: RootEventLoopWindowTarget,
}
impl fmt::Debug for EventLoopHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopHandler")
.field("handler", &"...")
.field("event_loop", &self.event_loop)
.finish()
}
}
impl EventLoopHandler {
fn handle_event(&mut self, event: Event<HandlePendingUserEvents>) {
(self.handler)(event, &self.event_loop)
}
}
#[derive(Debug)]
pub(crate) enum EventWrapper {
StaticEvent(Event<HandlePendingUserEvents>),
pub enum EventWrapper {
StaticEvent(Event<Never>),
ScaleFactorChanged(ScaleFactorChanged),
}
@@ -83,7 +61,7 @@ pub struct ScaleFactorChanged {
enum UserCallbackTransitionResult<'a> {
Success {
handler: EventLoopHandler,
event_handler: Box<dyn EventHandler>,
active_control_flow: ControlFlow,
processing_redraws: bool,
},
@@ -92,14 +70,16 @@ enum UserCallbackTransitionResult<'a> {
},
}
fn is_redraw(event: &Event<HandlePendingUserEvents>) -> bool {
matches!(
event,
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
}
)
impl Event<Never> {
fn is_redraw(&self) -> bool {
matches!(
self,
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
}
)
}
}
// this is the state machine for the app lifecycle
@@ -114,11 +94,11 @@ enum AppStateImpl {
Launching {
queued_windows: Vec<Id<WinitUIWindow>>,
queued_events: Vec<EventWrapper>,
queued_handler: EventLoopHandler,
queued_event_handler: Box<dyn EventHandler>,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
},
ProcessingEvents {
handler: EventLoopHandler,
event_handler: Box<dyn EventHandler>,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
active_control_flow: ControlFlow,
},
@@ -128,15 +108,15 @@ enum AppStateImpl {
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
},
ProcessingRedraws {
handler: EventLoopHandler,
event_handler: Box<dyn EventHandler>,
active_control_flow: ControlFlow,
},
Waiting {
waiting_handler: EventLoopHandler,
waiting_event_handler: Box<dyn EventHandler>,
start: Instant,
},
PollFinished {
waiting_handler: EventLoopHandler,
waiting_event_handler: Box<dyn EventHandler>,
},
Terminated,
}
@@ -224,7 +204,7 @@ impl AppState {
matches!(self.state(), AppStateImpl::Terminated)
}
fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) {
fn will_launch_transition(&mut self, queued_event_handler: Box<dyn EventHandler>) {
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::NotLaunched {
queued_windows,
@@ -236,28 +216,28 @@ impl AppState {
self.set_state(AppStateImpl::Launching {
queued_windows,
queued_events,
queued_handler,
queued_event_handler,
queued_gpu_redraws,
});
}
fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) {
let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
let (windows, events, event_handler, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Launching {
queued_windows,
queued_events,
queued_handler,
queued_event_handler,
queued_gpu_redraws,
} => (
queued_windows,
queued_events,
queued_handler,
queued_event_handler,
queued_gpu_redraws,
),
s => bug!("unexpected state {:?}", s),
};
self.set_state(AppStateImpl::ProcessingEvents {
handler,
event_handler,
active_control_flow: self.control_flow,
queued_gpu_redraws,
});
@@ -271,19 +251,24 @@ impl AppState {
return None;
}
let (handler, event) = match (self.control_flow, self.take_state()) {
(ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => (
waiting_handler,
let (event_handler, event) = match (self.control_flow, self.take_state()) {
(
ControlFlow::Poll,
AppStateImpl::PollFinished {
waiting_event_handler,
},
) => (
waiting_event_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)),
),
(
ControlFlow::Wait,
AppStateImpl::Waiting {
waiting_handler,
waiting_event_handler,
start,
},
) => (
waiting_handler,
waiting_event_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: None,
@@ -292,7 +277,7 @@ impl AppState {
(
ControlFlow::WaitUntil(requested_resume),
AppStateImpl::Waiting {
waiting_handler,
waiting_event_handler,
start,
},
) => {
@@ -307,13 +292,13 @@ impl AppState {
requested_resume: Some(requested_resume),
}))
};
(waiting_handler, event)
(waiting_event_handler, event)
}
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
};
self.set_state(AppStateImpl::ProcessingEvents {
handler,
event_handler,
queued_gpu_redraws: Default::default(),
active_control_flow: self.control_flow,
});
@@ -358,20 +343,25 @@ impl AppState {
}
}
let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) =
let (event_handler, queued_gpu_redraws, active_control_flow, processing_redraws) =
match self.take_state() {
AppStateImpl::Launching { .. }
| AppStateImpl::NotLaunched { .. }
| AppStateImpl::InUserCallback { .. } => unreachable!(),
AppStateImpl::ProcessingEvents {
handler,
event_handler,
queued_gpu_redraws,
active_control_flow,
} => (handler, queued_gpu_redraws, active_control_flow, false),
AppStateImpl::ProcessingRedraws {
handler,
} => (
event_handler,
queued_gpu_redraws,
active_control_flow,
} => (handler, Default::default(), active_control_flow, true),
false,
),
AppStateImpl::ProcessingRedraws {
event_handler,
active_control_flow,
} => (event_handler, Default::default(), active_control_flow, true),
AppStateImpl::PollFinished { .. }
| AppStateImpl::Waiting { .. }
| AppStateImpl::Terminated => unreachable!(),
@@ -381,23 +371,23 @@ impl AppState {
queued_gpu_redraws,
});
UserCallbackTransitionResult::Success {
handler,
event_handler,
active_control_flow,
processing_redraws,
}
}
fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> {
let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
let (event_handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
AppStateImpl::ProcessingEvents {
handler,
event_handler,
queued_gpu_redraws,
active_control_flow,
} => (handler, queued_gpu_redraws, active_control_flow),
} => (event_handler, queued_gpu_redraws, active_control_flow),
s => bug!("unexpected state {:?}", s),
};
self.set_state(AppStateImpl::ProcessingRedraws {
handler,
event_handler,
active_control_flow,
});
queued_gpu_redraws
@@ -407,11 +397,11 @@ impl AppState {
if !self.has_launched() || self.has_terminated() {
return;
}
let (waiting_handler, old) = match self.take_state() {
let (waiting_event_handler, old) = match self.take_state() {
AppStateImpl::ProcessingRedraws {
handler,
event_handler,
active_control_flow,
} => (handler, active_control_flow),
} => (event_handler, active_control_flow),
s => bug!("unexpected state {:?}", s),
};
@@ -420,7 +410,7 @@ impl AppState {
(ControlFlow::Wait, ControlFlow::Wait) => {
let start = Instant::now();
self.set_state(AppStateImpl::Waiting {
waiting_handler,
waiting_event_handler,
start,
});
}
@@ -429,14 +419,14 @@ impl AppState {
{
let start = Instant::now();
self.set_state(AppStateImpl::Waiting {
waiting_handler,
waiting_event_handler,
start,
});
}
(_, ControlFlow::Wait) => {
let start = Instant::now();
self.set_state(AppStateImpl::Waiting {
waiting_handler,
waiting_event_handler,
start,
});
self.waker.stop()
@@ -444,22 +434,24 @@ impl AppState {
(_, ControlFlow::WaitUntil(new_instant)) => {
let start = Instant::now();
self.set_state(AppStateImpl::Waiting {
waiting_handler,
waiting_event_handler,
start,
});
self.waker.start_at(new_instant)
}
// Unlike on macOS, handle Poll to Poll transition here to call the waker
(_, ControlFlow::Poll) => {
self.set_state(AppStateImpl::PollFinished { waiting_handler });
self.set_state(AppStateImpl::PollFinished {
waiting_event_handler,
});
self.waker.start()
}
}
}
fn terminated_transition(&mut self) -> EventLoopHandler {
fn terminated_transition(&mut self) -> Box<dyn EventHandler> {
match self.replace_state(AppStateImpl::Terminated) {
AppStateImpl::ProcessingEvents { handler, .. } => handler,
AppStateImpl::ProcessingEvents { event_handler, .. } => event_handler,
s => bug!("`LoopExiting` happened while not processing events {:?}", s),
}
}
@@ -524,8 +516,8 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUI
}
}
pub(crate) fn will_launch(mtm: MainThreadMarker, queued_handler: EventLoopHandler) {
AppState::get_mut(mtm).will_launch_transition(queued_handler)
pub fn will_launch(mtm: MainThreadMarker, queued_event_handler: Box<dyn EventHandler>) {
AppState::get_mut(mtm).will_launch_transition(queued_event_handler)
}
pub fn did_finish_launching(mtm: MainThreadMarker) {
@@ -602,34 +594,36 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
return;
}
let (mut handler, active_control_flow, processing_redraws) =
let (mut event_handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
queued_events.extend(events);
return;
}
UserCallbackTransitionResult::Success {
handler,
event_handler,
active_control_flow,
processing_redraws,
} => (handler, active_control_flow, processing_redraws),
} => (event_handler, active_control_flow, processing_redraws),
};
drop(this);
for wrapper in events {
match wrapper {
EventWrapper::StaticEvent(event) => {
if !processing_redraws && is_redraw(&event) {
if !processing_redraws && event.is_redraw() {
log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !is_redraw(&event) {
} else if processing_redraws && !event.is_redraw() {
log::warn!(
"processing non `RedrawRequested` event after the main event loop: {:#?}",
event
);
}
handler.handle_event(event)
event_handler.handle_nonuser_event(event)
}
EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
}
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
}
}
@@ -656,12 +650,12 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
"redraw queued while processing redraws"
);
AppStateImpl::ProcessingRedraws {
handler,
event_handler,
active_control_flow,
}
} else {
AppStateImpl::ProcessingEvents {
handler,
event_handler,
queued_gpu_redraws,
active_control_flow,
}
@@ -673,17 +667,19 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
for wrapper in queued_events {
match wrapper {
EventWrapper::StaticEvent(event) => {
if !processing_redraws && is_redraw(&event) {
if !processing_redraws && event.is_redraw() {
log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !is_redraw(&event) {
} else if processing_redraws && !event.is_redraw() {
log::warn!(
"processing non-`RedrawRequested` event after the main event loop: {:#?}",
event
);
}
handler.handle_event(event)
event_handler.handle_nonuser_event(event)
}
EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
}
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
}
}
}
@@ -691,23 +687,23 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
fn handle_user_events(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm);
let (mut handler, active_control_flow, processing_redraws) =
let (mut event_handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
bug!("unexpected attempted to process an event")
}
UserCallbackTransitionResult::Success {
handler,
event_handler,
active_control_flow,
processing_redraws,
} => (handler, active_control_flow, processing_redraws),
} => (event_handler, active_control_flow, processing_redraws),
};
if processing_redraws {
bug!("user events attempted to be sent out while `ProcessingRedraws`");
}
drop(this);
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
event_handler.handle_user_events();
loop {
let mut this = AppState::get_mut(mtm);
@@ -727,7 +723,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
_ => unreachable!(),
};
this.app_state = Some(AppStateImpl::ProcessingEvents {
handler,
event_handler,
queued_gpu_redraws,
active_control_flow,
});
@@ -737,12 +733,13 @@ fn handle_user_events(mtm: MainThreadMarker) {
for wrapper in queued_events {
match wrapper {
EventWrapper::StaticEvent(event) => handler.handle_event(event),
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
EventWrapper::StaticEvent(event) => event_handler.handle_nonuser_event(event),
EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
}
}
}
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
event_handler.handle_user_events();
}
}
@@ -765,7 +762,7 @@ pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
.into_iter()
.map(|window| {
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::RedrawRequested,
})
})
@@ -782,13 +779,13 @@ pub fn handle_events_cleared(mtm: MainThreadMarker) {
pub fn terminated(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm);
let mut handler = this.terminated_transition();
let mut event_handler = this.terminated_transition();
drop(this);
handler.handle_event(Event::LoopExiting)
event_handler.handle_nonuser_event(Event::LoopExiting)
}
fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged) {
fn handle_hidpi_proxy(event_handler: &mut Box<dyn EventHandler>, event: ScaleFactorChanged) {
let ScaleFactorChanged {
suggested_size,
scale_factor,
@@ -796,13 +793,13 @@ fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged)
} = event;
let new_inner_size = Arc::new(Mutex::new(suggested_size));
let event = Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::ScaleFactorChanged {
scale_factor,
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
},
};
handler.handle_event(event);
event_handler.handle_nonuser_event(event);
let (view, screen_frame) = get_view_and_screen_frame(&window);
let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size);

View File

@@ -1,6 +1,7 @@
use std::{
collections::VecDeque,
ffi::c_void,
fmt::{self, Debug},
marker::PhantomData,
ptr,
sync::mpsc::{self, Receiver, Sender},
@@ -24,7 +25,6 @@ use crate::{
EventLoopWindowTarget as RootEventLoopWindowTarget,
},
platform::ios::Idiom,
platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents},
};
use super::{app_state, monitor, view, MonitorHandle};
@@ -34,11 +34,12 @@ use super::{
};
#[derive(Debug)]
pub struct EventLoopWindowTarget {
pub struct EventLoopWindowTarget<T: 'static> {
pub(super) mtm: MainThreadMarker,
p: PhantomData<T>,
}
impl EventLoopWindowTarget {
impl<T: 'static> EventLoopWindowTarget<T> {
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
monitor::uiscreens(self.mtm)
}
@@ -77,56 +78,19 @@ impl EventLoopWindowTarget {
pub(crate) fn exit(&self) {
// https://developer.apple.com/library/archive/qa/qa1561/_index.html
// it is not possible to quit an iOS app gracefully and programatically
log::warn!("`ControlFlow::Exit` ignored on iOS");
warn!("`ControlFlow::Exit` ignored on iOS");
}
pub(crate) fn exiting(&self) -> bool {
false
}
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle
}
}
#[derive(Clone)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_05")]
#[inline]
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
rwh_05::UiKitDisplayHandle::empty().into()
}
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::UiKitDisplayHandle::new().into())
}
}
fn map_user_event<T: 'static>(
mut handler: impl FnMut(Event<T>, &RootEventLoopWindowTarget),
receiver: mpsc::Receiver<T>,
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget) {
move |event, window_target| match event.map_nonuser_event() {
Ok(event) => (handler)(event, window_target),
Err(_) => {
for event in receiver.try_iter() {
(handler)(Event::UserEvent(event), window_target);
}
}
}
}
pub struct EventLoop<T: 'static> {
mtm: MainThreadMarker,
sender: Sender<T>,
receiver: Receiver<T>,
window_target: RootEventLoopWindowTarget,
window_target: RootEventLoopWindowTarget<T>,
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -159,59 +123,59 @@ impl<T: 'static> EventLoop<T> {
sender,
receiver,
window_target: RootEventLoopWindowTarget {
p: EventLoopWindowTarget { mtm },
p: EventLoopWindowTarget {
mtm,
p: PhantomData,
},
_marker: PhantomData,
},
})
}
pub fn run<F>(self, handler: F) -> !
pub fn run<F>(self, event_handler: F) -> !
where
F: FnMut(Event<T>, &RootEventLoopWindowTarget),
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{
let application = UIApplication::shared(self.mtm);
assert!(
application.is_none(),
"\
unsafe {
let application = UIApplication::shared(self.mtm);
assert!(
application.is_none(),
"\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS",
);
);
let handler = map_user_event(handler, self.receiver);
let event_handler = std::mem::transmute::<
Box<dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>)>,
Box<EventHandlerCallback<T>>,
>(Box::new(event_handler));
let handler = unsafe {
std::mem::transmute::<
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>,
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>,
>(Box::new(handler))
};
let handler = EventLoopHandler {
f: event_handler,
receiver: self.receiver,
event_loop: self.window_target,
};
let handler = EventLoopHandler {
handler,
event_loop: self.window_target,
};
app_state::will_launch(self.mtm, Box::new(handler));
app_state::will_launch(self.mtm, handler);
// Ensure application delegate is initialized
view::WinitApplicationDelegate::class();
// Ensure application delegate is initialized
view::WinitApplicationDelegate::class();
unsafe {
UIApplicationMain(
0,
ptr::null(),
None,
Some(&NSString::from_str("WinitApplicationDelegate")),
)
};
unreachable!()
);
unreachable!()
}
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.sender.clone())
}
pub fn window_target(&self) -> &RootEventLoopWindowTarget {
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
&self.window_target
}
}
@@ -378,3 +342,39 @@ fn setup_control_flow_observers() {
CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode);
}
}
#[derive(Debug)]
pub enum Never {}
type EventHandlerCallback<T> = dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>) + 'static;
pub trait EventHandler: Debug {
fn handle_nonuser_event(&mut self, event: Event<Never>);
fn handle_user_events(&mut self);
}
struct EventLoopHandler<T: 'static> {
f: Box<EventHandlerCallback<T>>,
receiver: Receiver<T>,
event_loop: RootEventLoopWindowTarget<T>,
}
impl<T: 'static> Debug for EventLoopHandler<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopHandler")
.field("event_loop", &self.event_loop)
.finish()
}
}
impl<T: 'static> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>) {
(self.f)(event.map_nonuser_event().unwrap(), &self.event_loop);
}
fn handle_user_events(&mut self) {
for event in self.receiver.try_iter() {
(self.f)(Event::UserEvent(event), &self.event_loop);
}
}
}

View File

@@ -68,22 +68,33 @@ mod window;
use std::fmt;
use crate::event::DeviceId;
pub(crate) use self::{
event_loop::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
},
monitor::{MonitorHandle, VideoModeHandle},
window::{PlatformSpecificWindowBuilderAttributes, Window},
monitor::{MonitorHandle, VideoMode},
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
};
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
use self::uikit::UIScreen;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
pub(crate) const DEVICE_ID: DeviceId = unsafe { DeviceId::dummy() };
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId {
uiscreen: *const UIScreen,
}
impl DeviceId {
pub const unsafe fn dummy() -> Self {
DeviceId {
uiscreen: std::ptr::null(),
}
}
}
unsafe impl Send for DeviceId {}
unsafe impl Sync for DeviceId {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyEventExtra {}

View File

@@ -8,12 +8,11 @@ use std::{
use icrate::Foundation::{MainThreadBound, MainThreadMarker, NSInteger};
use objc2::mutability::IsRetainable;
use objc2::rc::Id;
use objc2::Message;
use super::uikit::{UIScreen, UIScreenMode};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoModeHandle as RootVideoModeHandle,
monitor::VideoMode as RootVideoMode,
platform_impl::platform::app_state,
};
@@ -21,15 +20,16 @@ use crate::{
#[derive(Debug)]
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Id<T>>);
impl<T: IsRetainable + Message> Clone for MainThreadBoundDelegateImpls<T> {
impl<T: IsRetainable> Clone for MainThreadBoundDelegateImpls<T> {
fn clone(&self) -> Self {
Self(MainThreadMarker::run_on_main(|mtm| {
MainThreadBound::new(Id::clone(self.0.get(mtm)), mtm)
}))
Self(
self.0
.get_on_main(|inner, mtm| MainThreadBound::new(Id::clone(inner), mtm)),
)
}
}
impl<T: IsRetainable + Message> hash::Hash for MainThreadBoundDelegateImpls<T> {
impl<T: IsRetainable> hash::Hash for MainThreadBoundDelegateImpls<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
// SAFETY: Marker only used to get the pointer
let mtm = unsafe { MainThreadMarker::new_unchecked() };
@@ -37,7 +37,7 @@ impl<T: IsRetainable + Message> hash::Hash for MainThreadBoundDelegateImpls<T> {
}
}
impl<T: IsRetainable + Message> PartialEq for MainThreadBoundDelegateImpls<T> {
impl<T: IsRetainable> PartialEq for MainThreadBoundDelegateImpls<T> {
fn eq(&self, other: &Self) -> bool {
// SAFETY: Marker only used to get the pointer
let mtm = unsafe { MainThreadMarker::new_unchecked() };
@@ -45,10 +45,10 @@ impl<T: IsRetainable + Message> PartialEq for MainThreadBoundDelegateImpls<T> {
}
}
impl<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {}
impl<T: IsRetainable> Eq for MainThreadBoundDelegateImpls<T> {}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct VideoModeHandle {
pub struct VideoMode {
pub(crate) size: (u32, u32),
pub(crate) bit_depth: u16,
pub(crate) refresh_rate_millihertz: u32,
@@ -56,15 +56,15 @@ pub struct VideoModeHandle {
pub(crate) monitor: MonitorHandle,
}
impl VideoModeHandle {
impl VideoMode {
fn new(
uiscreen: Id<UIScreen>,
screen_mode: Id<UIScreenMode>,
mtm: MainThreadMarker,
) -> VideoModeHandle {
) -> VideoMode {
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
let size = screen_mode.size();
VideoModeHandle {
VideoMode {
size: (size.width as u32, size.height as u32),
bit_depth: 32,
refresh_rate_millihertz,
@@ -100,9 +100,11 @@ pub struct MonitorHandle {
impl Clone for MonitorHandle {
fn clone(&self) -> Self {
MainThreadMarker::run_on_main(|mtm| Self {
ui_screen: MainThreadBound::new(self.ui_screen.get(mtm).clone(), mtm),
})
Self {
ui_screen: self
.ui_screen
.get_on_main(|inner, mtm| MainThreadBound::new(inner.clone(), mtm)),
}
}
}
@@ -135,13 +137,24 @@ impl Ord for MonitorHandle {
impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MonitorHandle")
.field("name", &self.name())
.field("size", &self.size())
.field("position", &self.position())
.field("scale_factor", &self.scale_factor())
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz())
.finish_non_exhaustive()
// TODO: Do this using the proper fmt API
#[derive(Debug)]
#[allow(dead_code)]
struct MonitorHandle {
name: Option<String>,
size: PhysicalSize<u32>,
position: PhysicalPosition<i32>,
scale_factor: f64,
}
let monitor_id_proxy = MonitorHandle {
name: self.name(),
size: self.size(),
position: self.position(),
scale_factor: self.scale_factor(),
};
monitor_id_proxy.fmt(f)
}
}
@@ -155,16 +168,16 @@ impl MonitorHandle {
}
pub fn name(&self) -> Option<String> {
MainThreadMarker::run_on_main(|mtm| {
self.ui_screen.get_on_main(|ui_screen, mtm| {
let main = UIScreen::main(mtm);
if *self.ui_screen(mtm) == main {
if *ui_screen == main {
Some("Primary".to_string())
} else if *self.ui_screen(mtm) == main.mirroredScreen() {
} else if *ui_screen == main.mirroredScreen() {
Some("Mirrored".to_string())
} else {
UIScreen::screens(mtm)
.iter()
.position(|rhs| rhs == &**self.ui_screen(mtm))
.position(|rhs| rhs == &**ui_screen)
.map(|idx| idx.to_string())
}
})
@@ -173,39 +186,38 @@ impl MonitorHandle {
pub fn size(&self) -> PhysicalSize<u32> {
let bounds = self
.ui_screen
.get_on_main(|ui_screen| ui_screen.nativeBounds());
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
}
pub fn position(&self) -> PhysicalPosition<i32> {
let bounds = self
.ui_screen
.get_on_main(|ui_screen| ui_screen.nativeBounds());
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
(bounds.origin.x as f64, bounds.origin.y as f64).into()
}
pub fn scale_factor(&self) -> f64 {
self.ui_screen
.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64
.get_on_main(|ui_screen, _| ui_screen.nativeScale()) as f64
}
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
Some(
self.ui_screen
.get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen)),
.get_on_main(|ui_screen, _| refresh_rate_millihertz(ui_screen)),
)
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
MainThreadMarker::run_on_main(|mtm| {
let ui_screen = self.ui_screen(mtm);
// Use Ord impl of RootVideoModeHandle
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.ui_screen.get_on_main(|ui_screen, mtm| {
// Use Ord impl of RootVideoMode
let modes: BTreeSet<_> = ui_screen
.availableModes()
.into_iter()
.map(|mode| RootVideoModeHandle {
video_mode: VideoModeHandle::new(ui_screen.clone(), mode, mtm),
.map(|mode| RootVideoMode {
video_mode: VideoMode::new(ui_screen.clone(), mode, mtm),
})
.collect();
@@ -217,13 +229,9 @@ impl MonitorHandle {
self.ui_screen.get(mtm)
}
pub fn preferred_video_mode(&self) -> VideoModeHandle {
MainThreadMarker::run_on_main(|mtm| {
VideoModeHandle::new(
self.ui_screen(mtm).clone(),
self.ui_screen(mtm).preferredMode().unwrap(),
mtm,
)
pub fn preferred_video_mode(&self) -> VideoMode {
self.ui_screen.get_on_main(|ui_screen, mtm| {
VideoMode::new(ui_screen.clone(), ui_screen.preferredMode().unwrap(), mtm)
})
}
}

View File

@@ -9,7 +9,6 @@ mod application;
mod coordinate_space;
mod device;
mod event;
mod gesture_recognizer;
mod responder;
mod screen;
mod screen_mode;
@@ -24,10 +23,6 @@ pub(crate) use self::application::UIApplication;
pub(crate) use self::coordinate_space::UICoordinateSpace;
pub(crate) use self::device::UIDevice;
pub(crate) use self::event::UIEvent;
pub(crate) use self::gesture_recognizer::{
UIGestureRecognizer, UIGestureRecognizerState, UIPinchGestureRecognizer,
UIRotationGestureRecognizer, UITapGestureRecognizer,
};
pub(crate) use self::responder::UIResponder;
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};
pub(crate) use self::screen_mode::UIScreenMode;

View File

@@ -3,7 +3,7 @@ use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use super::{UICoordinateSpace, UIGestureRecognizer, UIResponder, UIViewController};
use super::{UICoordinateSpace, UIResponder, UIViewController};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
@@ -65,12 +65,6 @@ extern_methods!(
#[method(setNeedsDisplay)]
pub fn setNeedsDisplay(&self);
#[method(addGestureRecognizer:)]
pub fn addGestureRecognizer(&self, gestureRecognizer: &UIGestureRecognizer);
#[method(removeGestureRecognizer:)]
pub fn removeGestureRecognizer(&self, gestureRecognizer: &UIGestureRecognizer);
}
);

View File

@@ -39,7 +39,7 @@ extern_methods!(
}
);
bitflags::bitflags! {
bitflags! {
#[derive(Clone, Copy)]
pub struct UIInterfaceOrientationMask: NSUInteger {
const Portrait = 1 << 1;

View File

@@ -1,37 +1,32 @@
#![allow(clippy::unnecessary_cast)]
use std::cell::{Cell, RefCell};
use std::cell::Cell;
use std::ptr::NonNull;
use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet};
use objc2::declare::{Ivar, IvarDrop};
use objc2::rc::Id;
use objc2::runtime::AnyClass;
use objc2::{
declare_class, extern_methods, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
};
use objc2::{declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType};
use super::app_state::{self, EventWrapper};
use super::uikit::{
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIGestureRecognizerState,
UIInterfaceOrientationMask, UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer,
UIStatusBarStyle, UITapGestureRecognizer, UITouch, UITouchPhase, UITouchType,
UITraitCollection, UIView, UIViewController, UIWindow,
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIInterfaceOrientationMask,
UIResponder, UIStatusBarStyle, UITouch, UITouchPhase, UITouchType, UITraitCollection, UIView,
UIViewController, UIWindow,
};
use super::window::WindowId;
use crate::{
dpi::PhysicalPosition,
event::{Event, Force, Touch, TouchPhase, WindowEvent},
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
platform::ios::ValidOrientations,
platform_impl::platform::{
ffi::{UIRectEdge, UIUserInterfaceIdiom},
Fullscreen, DEVICE_ID,
window::PlatformSpecificWindowBuilderAttributes,
DeviceId, Fullscreen,
},
window::{WindowAttributes, WindowId},
window::{WindowAttributes, WindowId as RootWindowId},
};
pub struct WinitViewState {
pinch_gesture_recognizer: RefCell<Option<Id<UIPinchGestureRecognizer>>>,
doubletap_gesture_recognizer: RefCell<Option<Id<UITapGestureRecognizer>>>,
rotation_gesture_recognizer: RefCell<Option<Id<UIRotationGestureRecognizer>>>,
}
declare_class!(
pub(crate) struct WinitView;
@@ -42,10 +37,6 @@ declare_class!(
const NAME: &'static str = "WinitUIView";
}
impl DeclaredClass for WinitView {
type Ivars = WinitViewState;
}
unsafe impl WinitView {
#[method(drawRect:)]
fn draw_rect(&self, rect: CGRect) {
@@ -54,7 +45,7 @@ declare_class!(
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::RedrawRequested,
}),
);
@@ -89,7 +80,7 @@ declare_class!(
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::Resized(size),
}),
);
@@ -128,7 +119,7 @@ declare_class!(
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
};
let window_id = window.id();
let window_id = RootWindowId(window.id());
app_state::handle_nonuser_events(
mtm,
std::iter::once(EventWrapper::ScaleFactorChanged(
@@ -166,79 +157,6 @@ declare_class!(
fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
self.handle_touches(touches)
}
#[method(pinchGesture:)]
fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) {
let window = self.window().unwrap();
let phase = match recognizer.state() {
UIGestureRecognizerState::Began => TouchPhase::Started,
UIGestureRecognizerState::Changed => TouchPhase::Moved,
UIGestureRecognizerState::Ended => TouchPhase::Ended,
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
TouchPhase::Cancelled
}
state => panic!("unexpected recognizer state: {:?}", state),
};
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
event: WindowEvent::PinchGesture {
device_id: DEVICE_ID,
delta: recognizer.velocity() as _,
phase,
},
});
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event);
}
#[method(doubleTapGesture:)]
fn double_tap_gesture(&self, recognizer: &UITapGestureRecognizer) {
let window = self.window().unwrap();
if recognizer.state() == UIGestureRecognizerState::Ended {
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
event: WindowEvent::DoubleTapGesture {
device_id: DEVICE_ID,
},
});
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event);
}
}
#[method(rotationGesture:)]
fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) {
let window = self.window().unwrap();
let phase = match recognizer.state() {
UIGestureRecognizerState::Began => TouchPhase::Started,
UIGestureRecognizerState::Changed => TouchPhase::Moved,
UIGestureRecognizerState::Ended => TouchPhase::Ended,
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
TouchPhase::Cancelled
}
state => panic!("unexpected recognizer state: {:?}", state),
};
// Flip the velocity to match macOS.
let delta = -recognizer.velocity() as _;
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
event: WindowEvent::RotationGesture {
device_id: DEVICE_ID,
delta,
phase,
},
});
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event);
}
}
);
@@ -262,73 +180,24 @@ extern_methods!(
impl WinitView {
pub(crate) fn new(
_mtm: MainThreadMarker,
window_attributes: &WindowAttributes,
_window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
frame: CGRect,
) -> Id<Self> {
let this = Self::alloc().set_ivars(WinitViewState {
pinch_gesture_recognizer: RefCell::new(None),
doubletap_gesture_recognizer: RefCell::new(None),
rotation_gesture_recognizer: RefCell::new(None),
});
let this: Id<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] };
this.setMultipleTouchEnabled(true);
if let Some(scale_factor) = window_attributes.platform_specific.scale_factor {
if let Some(scale_factor) = platform_attributes.scale_factor {
this.setContentScaleFactor(scale_factor as _);
}
this
}
pub(crate) fn recognize_pinch_gesture(&self, should_recognize: bool) {
if should_recognize {
if self.ivars().pinch_gesture_recognizer.borrow().is_none() {
let pinch: Id<UIPinchGestureRecognizer> = unsafe {
msg_send_id![UIPinchGestureRecognizer::alloc(), initWithTarget: self, action: sel!(pinchGesture:)]
};
self.addGestureRecognizer(&pinch);
self.ivars().pinch_gesture_recognizer.replace(Some(pinch));
}
} else if let Some(recognizer) = self.ivars().pinch_gesture_recognizer.take() {
self.removeGestureRecognizer(&recognizer);
}
}
pub(crate) fn recognize_doubletap_gesture(&self, should_recognize: bool) {
if should_recognize {
if self.ivars().doubletap_gesture_recognizer.borrow().is_none() {
let tap: Id<UITapGestureRecognizer> = unsafe {
msg_send_id![UITapGestureRecognizer::alloc(), initWithTarget: self, action: sel!(doubleTapGesture:)]
};
tap.setNumberOfTapsRequired(2);
tap.setNumberOfTouchesRequired(1);
self.addGestureRecognizer(&tap);
self.ivars().doubletap_gesture_recognizer.replace(Some(tap));
}
} else if let Some(recognizer) = self.ivars().doubletap_gesture_recognizer.take() {
self.removeGestureRecognizer(&recognizer);
}
}
pub(crate) fn recognize_rotation_gesture(&self, should_recognize: bool) {
if should_recognize {
if self.ivars().rotation_gesture_recognizer.borrow().is_none() {
let rotation: Id<UIRotationGestureRecognizer> = unsafe {
msg_send_id![UIRotationGestureRecognizer::alloc(), initWithTarget: self, action: sel!(rotationGesture:)]
};
self.addGestureRecognizer(&rotation);
self.ivars()
.rotation_gesture_recognizer
.replace(Some(rotation));
}
} else if let Some(recognizer) = self.ivars().rotation_gesture_recognizer.take() {
self.removeGestureRecognizer(&recognizer);
}
}
fn handle_touches(&self, touches: &NSSet<UITouch>) {
let window = self.window().unwrap();
let uiscreen = window.screen();
let mut touch_events = Vec::new();
let os_supports_force = app_state::os_capabilities().force_touch;
for touch in touches {
@@ -379,9 +248,11 @@ impl WinitView {
)
};
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::Touch(Touch {
device_id: DEVICE_ID,
device_id: RootDeviceId(DeviceId {
uiscreen: Id::as_ptr(&uiscreen),
}),
id: touch_id,
location: physical_location,
force,
@@ -403,7 +274,11 @@ pub struct ViewControllerState {
}
declare_class!(
pub(crate) struct WinitViewController;
pub(crate) struct WinitViewController {
state: IvarDrop<Box<ViewControllerState>, "_state">,
}
mod view_controller_ivars;
unsafe impl ClassType for WinitViewController {
#[inherits(UIResponder, NSObject)]
@@ -412,8 +287,28 @@ declare_class!(
const NAME: &'static str = "WinitUIViewController";
}
impl DeclaredClass for WinitViewController {
type Ivars = ViewControllerState;
unsafe impl WinitViewController {
#[method(init)]
unsafe fn init(this: *mut Self) -> Option<NonNull<Self>> {
let this: Option<&mut Self> = msg_send![super(this), init];
this.map(|this| {
// These are set in WinitViewController::new, it's just to set them
// to _something_.
Ivar::write(
&mut this.state,
Box::new(ViewControllerState {
prefers_status_bar_hidden: Cell::new(false),
preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default),
prefers_home_indicator_auto_hidden: Cell::new(false),
supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
preferred_screen_edges_deferring_system_gestures: Cell::new(
UIRectEdge::NONE,
),
}),
);
NonNull::from(this)
})
}
}
unsafe impl WinitViewController {
@@ -424,27 +319,27 @@ declare_class!(
#[method(prefersStatusBarHidden)]
fn prefers_status_bar_hidden(&self) -> bool {
self.ivars().prefers_status_bar_hidden.get()
self.state.prefers_status_bar_hidden.get()
}
#[method(preferredStatusBarStyle)]
fn preferred_status_bar_style(&self) -> UIStatusBarStyle {
self.ivars().preferred_status_bar_style.get()
self.state.preferred_status_bar_style.get()
}
#[method(prefersHomeIndicatorAutoHidden)]
fn prefers_home_indicator_auto_hidden(&self) -> bool {
self.ivars().prefers_home_indicator_auto_hidden.get()
self.state.prefers_home_indicator_auto_hidden.get()
}
#[method(supportedInterfaceOrientations)]
fn supported_orientations(&self) -> UIInterfaceOrientationMask {
self.ivars().supported_orientations.get()
self.state.supported_orientations.get()
}
#[method(preferredScreenEdgesDeferringSystemGestures)]
fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
self.ivars()
self.state
.preferred_screen_edges_deferring_system_gestures
.get()
}
@@ -453,17 +348,17 @@ declare_class!(
impl WinitViewController {
pub(crate) fn set_prefers_status_bar_hidden(&self, val: bool) {
self.ivars().prefers_status_bar_hidden.set(val);
self.state.prefers_status_bar_hidden.set(val);
self.setNeedsStatusBarAppearanceUpdate();
}
pub(crate) fn set_preferred_status_bar_style(&self, val: UIStatusBarStyle) {
self.ivars().preferred_status_bar_style.set(val);
self.state.preferred_status_bar_style.set(val);
self.setNeedsStatusBarAppearanceUpdate();
}
pub(crate) fn set_prefers_home_indicator_auto_hidden(&self, val: bool) {
self.ivars().prefers_home_indicator_auto_hidden.set(val);
self.state.prefers_home_indicator_auto_hidden.set(val);
let os_capabilities = app_state::os_capabilities();
if os_capabilities.home_indicator_hidden {
self.setNeedsUpdateOfHomeIndicatorAutoHidden();
@@ -473,7 +368,7 @@ impl WinitViewController {
}
pub(crate) fn set_preferred_screen_edges_deferring_system_gestures(&self, val: UIRectEdge) {
self.ivars()
self.state
.preferred_screen_edges_deferring_system_gestures
.set(val);
let os_capabilities = app_state::os_capabilities();
@@ -506,52 +401,30 @@ impl WinitViewController {
| UIInterfaceOrientationMask::PortraitUpsideDown
}
};
self.ivars().supported_orientations.set(mask);
self.state.supported_orientations.set(mask);
UIViewController::attemptRotationToDeviceOrientation();
}
pub(crate) fn new(
mtm: MainThreadMarker,
window_attributes: &WindowAttributes,
_window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
view: &UIView,
) -> Id<Self> {
// These are set properly below, we just to set them to something in the meantime.
let this = Self::alloc().set_ivars(ViewControllerState {
prefers_status_bar_hidden: Cell::new(false),
preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default),
prefers_home_indicator_auto_hidden: Cell::new(false),
supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::NONE),
});
let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), init] };
this.set_prefers_status_bar_hidden(
window_attributes
.platform_specific
.prefers_status_bar_hidden,
);
this.set_prefers_status_bar_hidden(platform_attributes.prefers_status_bar_hidden);
this.set_preferred_status_bar_style(
window_attributes
.platform_specific
.preferred_status_bar_style
.into(),
);
this.set_preferred_status_bar_style(platform_attributes.preferred_status_bar_style.into());
this.set_supported_interface_orientations(
mtm,
window_attributes.platform_specific.valid_orientations,
);
this.set_supported_interface_orientations(mtm, platform_attributes.valid_orientations);
this.set_prefers_home_indicator_auto_hidden(
window_attributes
.platform_specific
.prefers_home_indicator_hidden,
platform_attributes.prefers_home_indicator_hidden,
);
this.set_preferred_screen_edges_deferring_system_gestures(
window_attributes
.platform_specific
platform_attributes
.preferred_screen_edges_deferring_system_gestures
.into(),
);
@@ -573,8 +446,6 @@ declare_class!(
const NAME: &'static str = "WinitUIWindow";
}
impl DeclaredClass for WinitUIWindow {}
unsafe impl WinitUIWindow {
#[method(becomeKeyWindow)]
fn become_key_window(&self) {
@@ -582,7 +453,7 @@ declare_class!(
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: self.id(),
window_id: RootWindowId(self.id()),
event: WindowEvent::Focused(true),
}),
);
@@ -595,7 +466,7 @@ declare_class!(
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
window_id: self.id(),
window_id: RootWindowId(self.id()),
event: WindowEvent::Focused(false),
}),
);
@@ -608,6 +479,7 @@ impl WinitUIWindow {
pub(crate) fn new(
mtm: MainThreadMarker,
window_attributes: &WindowAttributes,
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
frame: CGRect,
view_controller: &UIViewController,
) -> Id<Self> {
@@ -615,7 +487,7 @@ impl WinitUIWindow {
this.setRootViewController(Some(view_controller));
match window_attributes.fullscreen.clone().map(Into::into) {
match window_attributes.fullscreen.0.clone().map(Into::into) {
Some(Fullscreen::Exclusive(ref video_mode)) => {
let monitor = video_mode.monitor();
let screen = monitor.ui_screen(mtm);
@@ -646,8 +518,6 @@ declare_class!(
const NAME: &'static str = "WinitApplicationDelegate";
}
impl DeclaredClass for WinitApplicationDelegate {}
// UIApplicationDelegate protocol
unsafe impl WinitApplicationDelegate {
#[method(application:didFinishLaunchingWithOptions:)]
@@ -690,7 +560,7 @@ declare_class!(
&*ptr
};
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::Destroyed,
}));
}
@@ -720,7 +590,7 @@ impl WinitApplicationDelegate {
&*ptr
};
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(),
window_id: RootWindowId(window.id()),
event: WindowEvent::Occluded(occluded),
}));
}

View File

@@ -3,15 +3,14 @@
use std::collections::VecDeque;
use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
use log::{debug, warn};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{class, msg_send};
use super::app_state::EventWrapper;
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
use super::view::{WinitUIWindow, WinitView, WinitViewController};
use crate::{
cursor::Cursor,
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::{Event, WindowEvent},
@@ -21,8 +20,8 @@ use crate::{
app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
},
window::{
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowId, WindowLevel,
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel,
},
};
@@ -174,8 +173,8 @@ impl Inner {
self.view.contentScaleFactor() as _
}
pub fn set_cursor(&self, _cursor: Cursor) {
debug!("`Window::set_cursor` ignored on iOS")
pub fn set_cursor_icon(&self, _cursor: CursorIcon) {
debug!("`Window::set_cursor_icon` ignored on iOS")
}
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
@@ -356,14 +355,23 @@ impl Inner {
}
#[cfg(feature = "rwh_06")]
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
let mut window_handle = rwh_06::UiKitWindowHandle::new({
let ui_view = Id::as_ptr(&self.view) as _;
std::ptr::NonNull::new(ui_view).expect("Id<T> should never be null")
});
window_handle.ui_view_controller =
std::ptr::NonNull::new(Id::as_ptr(&self.view_controller) as _);
rwh_06::RawWindowHandle::UiKit(window_handle)
Ok(rwh_06::RawWindowHandle::UiKit(window_handle))
}
#[cfg(feature = "rwh_06")]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::UiKit(
rwh_06::UiKitDisplayHandle::new(),
))
}
pub fn theme(&self) -> Option<Theme> {
@@ -397,9 +405,10 @@ pub struct Window {
}
impl Window {
pub(crate) fn new(
event_loop: &EventLoopWindowTarget,
pub(crate) fn new<T>(
event_loop: &EventLoopWindowTarget<T>,
window_attributes: WindowAttributes,
platform_attributes: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, RootOsError> {
let mtm = event_loop.mtm;
@@ -413,7 +422,7 @@ impl Window {
// TODO: transparency, visible
let main_screen = UIScreen::main(mtm);
let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
let fullscreen = window_attributes.fullscreen.0.clone().map(Into::into);
let screen = match fullscreen {
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm),
Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm),
@@ -437,7 +446,7 @@ impl Window {
None => screen_bounds,
};
let view = WinitView::new(mtm, &window_attributes, frame);
let view = WinitView::new(mtm, &window_attributes, &platform_attributes, frame);
let gl_or_metal_backed = unsafe {
let layer_class = WinitView::layerClass();
@@ -446,8 +455,15 @@ impl Window {
is_metal || is_gl
};
let view_controller = WinitViewController::new(mtm, &window_attributes, &view);
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
let view_controller =
WinitViewController::new(mtm, &window_attributes, &platform_attributes, &view);
let window = WinitUIWindow::new(
mtm,
&window_attributes,
&platform_attributes,
frame,
&view_controller,
);
app_state::set_key_window(mtm, &window);
@@ -464,7 +480,7 @@ impl Window {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
};
let window_id = window.id();
let window_id = RootWindowId(window.id());
app_state::handle_nonuser_events(
mtm,
std::iter::once(EventWrapper::ScaleFactorChanged(
@@ -500,29 +516,7 @@ impl Window {
}
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Inner) -> R + Send) -> R {
self.inner.get_on_main(|inner| f(inner))
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_window_handle_rwh_06(
&self,
) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
if let Some(mtm) = MainThreadMarker::new() {
Ok(self.inner.get(mtm).raw_window_handle_rwh_06())
} else {
Err(rwh_06::HandleError::Unavailable)
}
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::UiKit(
rwh_06::UiKitDisplayHandle::new(),
))
self.inner.get_on_main(|inner, _mtm| f(inner))
}
}
@@ -562,18 +556,6 @@ impl Inner {
self.view_controller
.set_preferred_status_bar_style(status_bar_style.into());
}
pub fn recognize_pinch_gesture(&self, should_recognize: bool) {
self.view.recognize_pinch_gesture(should_recognize);
}
pub fn recognize_doubletap_gesture(&self, should_recognize: bool) {
self.view.recognize_doubletap_gesture(should_recognize);
}
pub fn recognize_rotation_gesture(&self, should_recognize: bool) {
self.view.recognize_rotation_gesture(should_recognize);
}
}
impl Inner {
@@ -638,7 +620,45 @@ impl Inner {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId {
window: *mut WinitUIWindow,
}
impl WindowId {
pub const unsafe fn dummy() -> Self {
WindowId {
window: std::ptr::null_mut(),
}
}
}
impl From<WindowId> for u64 {
fn from(window_id: WindowId) -> Self {
window_id.window as u64
}
}
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self {
window: raw_id as _,
}
}
}
unsafe impl Send for WindowId {}
unsafe impl Sync for WindowId {}
impl From<&AnyObject> for WindowId {
fn from(window: &AnyObject) -> WindowId {
WindowId {
window: window as *const _ as _,
}
}
}
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub scale_factor: Option<f64>,
pub valid_orientations: ValidOrientations,

View File

@@ -6,13 +6,13 @@ use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyC
///
/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses.
pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey {
scancode_to_physicalkey(keycode.saturating_sub(8))
scancode_to_keycode(keycode.saturating_sub(8))
}
/// Map the linux scancode to Keycode.
///
/// Both X11 and Wayland use keys with `+ 8` offset to linux scancode.
pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
pub fn scancode_to_keycode(scancode: u32) -> PhysicalKey {
// The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as
// libxkbcommon's documentation seems to suggest that the keycode values we're interested in
// are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes,

View File

@@ -398,8 +398,8 @@ impl KbdState {
let text_with_all_modifiers = event.text_with_all_modifiers();
let platform_specific = KeyEventExtra {
text_with_all_modifiers,
key_without_modifiers,
text_with_all_modifiers,
};
KeyEvent {
@@ -409,9 +409,7 @@ impl KbdState {
location,
state,
repeat,
extra: crate::event::KeyExtra {
extra: platform_specific,
},
platform_specific,
}
}
@@ -684,7 +682,7 @@ fn byte_slice_to_smol_str(bytes: &[u8]) -> Option<SmolStr> {
std::str::from_utf8(bytes)
.map(SmolStr::new)
.map_err(|e| {
log::warn!(
warn!(
"UTF-8 received from libxkbcommon ({:?}) was invalid: {e}",
bytes
)

View File

@@ -15,35 +15,39 @@ use once_cell::sync::Lazy;
use smol_str::SmolStr;
#[cfg(x11_platform)]
use self::x11::{X11Error, XConnection, XError, XNotSupported};
#[cfg(x11_platform)]
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
use crate::platform::x11::XlibErrorHook;
use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError},
event::KeyEvent,
event_loop::{
AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
EventLoopWindowTarget as RootELW,
},
icon::Icon,
keyboard::Key,
platform::pump_events::PumpStatus,
keyboard::{Key, PhysicalKey},
platform::{
modifier_supplement::KeyEventExtModifierSupplement, pump_events::PumpStatus,
scancode::PhysicalKeyExtScancode,
},
window::{
ActivationToken, Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme,
ActivationToken, CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme,
UserAttentionType, WindowAttributes, WindowButtons, WindowLevel,
},
};
#[cfg(x11_platform)]
pub use x11::XNotSupported;
#[cfg(x11_platform)]
use x11::{util::WindowType as XWindowType, X11Error, XConnection, XError};
pub(crate) use self::common::keymap::{physicalkey_to_scancode, scancode_to_physicalkey};
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
pub(crate) mod common;
pub mod common;
#[cfg(wayland_platform)]
pub(crate) mod wayland;
pub mod wayland;
#[cfg(x11_platform)]
pub(crate) mod x11;
pub mod x11;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum Backend {
@@ -71,7 +75,7 @@ impl ApplicationName {
}
}
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub name: Option<ApplicationName>,
pub activation_token: Option<ActivationToken>,
@@ -79,7 +83,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub x11: X11WindowBuilderAttributes,
}
#[derive(Clone, Debug)]
#[derive(Clone)]
#[cfg(x11_platform)]
pub struct X11WindowBuilderAttributes {
pub visual_id: Option<x11rb::protocol::xproto::Visualid>,
@@ -142,6 +146,44 @@ pub(crate) enum Window {
Wayland(wayland::Window),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(u64);
impl From<WindowId> for u64 {
fn from(window_id: WindowId) -> Self {
window_id.0
}
}
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self(raw_id)
}
}
impl WindowId {
pub const unsafe fn dummy() -> Self {
Self(0)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum DeviceId {
#[cfg(x11_platform)]
X(x11::DeviceId),
#[cfg(wayland_platform)]
Wayland(wayland::DeviceId),
}
impl DeviceId {
pub const unsafe fn dummy() -> Self {
#[cfg(wayland_platform)]
return DeviceId::Wayland(unsafe { wayland::DeviceId::dummy() });
#[cfg(all(not(wayland_platform), x11_platform))]
return DeviceId::X(unsafe { x11::DeviceId::dummy() });
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum MonitorHandle {
#[cfg(x11_platform)]
@@ -210,55 +252,56 @@ impl MonitorHandle {
}
#[inline]
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoModeHandle>> {
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes()))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum VideoModeHandle {
pub enum VideoMode {
#[cfg(x11_platform)]
X(x11::VideoModeHandle),
X(x11::VideoMode),
#[cfg(wayland_platform)]
Wayland(wayland::VideoModeHandle),
Wayland(wayland::VideoMode),
}
impl VideoModeHandle {
impl VideoMode {
#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
x11_or_wayland!(match self; VideoModeHandle(m) => m.size())
x11_or_wayland!(match self; VideoMode(m) => m.size())
}
#[inline]
pub fn bit_depth(&self) -> u16 {
x11_or_wayland!(match self; VideoModeHandle(m) => m.bit_depth())
x11_or_wayland!(match self; VideoMode(m) => m.bit_depth())
}
#[inline]
pub fn refresh_rate_millihertz(&self) -> u32 {
x11_or_wayland!(match self; VideoModeHandle(m) => m.refresh_rate_millihertz())
x11_or_wayland!(match self; VideoMode(m) => m.refresh_rate_millihertz())
}
#[inline]
pub fn monitor(&self) -> MonitorHandle {
x11_or_wayland!(match self; VideoModeHandle(m) => m.monitor(); as MonitorHandle)
x11_or_wayland!(match self; VideoMode(m) => m.monitor(); as MonitorHandle)
}
}
impl Window {
#[inline]
pub(crate) fn new(
window_target: &EventLoopWindowTarget,
pub(crate) fn new<T>(
window_target: &EventLoopWindowTarget<T>,
attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> {
match *window_target {
#[cfg(wayland_platform)]
EventLoopWindowTarget::Wayland(ref window_target) => {
wayland::Window::new(window_target, attribs).map(Window::Wayland)
wayland::Window::new(window_target, attribs, pl_attribs).map(Window::Wayland)
}
#[cfg(x11_platform)]
EventLoopWindowTarget::X(ref window_target) => {
x11::Window::new(window_target, attribs).map(Window::X)
x11::Window::new(window_target, attribs, pl_attribs).map(Window::X)
}
}
}
@@ -272,7 +315,7 @@ impl Window {
}
#[inline]
pub fn id(&self) -> crate::window::WindowId {
pub fn id(&self) -> WindowId {
x11_or_wayland!(match self; Window(w) => w.id())
}
@@ -377,8 +420,8 @@ impl Window {
}
#[inline]
pub fn set_cursor(&self, cursor: Cursor) {
x11_or_wayland!(match self; Window(w) => w.set_cursor(cursor))
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
x11_or_wayland!(match self; Window(w) => w.set_cursor_icon(cursor))
}
#[inline]
@@ -595,30 +638,32 @@ impl Window {
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeyEventExtra {
pub text_with_all_modifiers: Option<SmolStr>,
pub key_without_modifiers: Key,
pub text_with_all_modifiers: Option<SmolStr>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) enum PlatformCustomCursor {
#[cfg(wayland_platform)]
Wayland(wayland::CustomCursor),
#[cfg(x11_platform)]
X(x11::CustomCursor),
impl KeyEventExtModifierSupplement for KeyEvent {
#[inline]
fn text_with_all_modifiers(&self) -> Option<&str> {
self.platform_specific
.text_with_all_modifiers
.as_ref()
.map(|s| s.as_str())
}
#[inline]
fn key_without_modifiers(&self) -> Key {
self.platform_specific.key_without_modifiers.clone()
}
}
impl PlatformCustomCursor {
pub(crate) fn build(
builder: PlatformCustomCursorBuilder,
p: &EventLoopWindowTarget,
) -> PlatformCustomCursor {
match p {
#[cfg(wayland_platform)]
EventLoopWindowTarget::Wayland(_) => {
Self::Wayland(wayland::CustomCursor::build(builder, p))
}
#[cfg(x11_platform)]
EventLoopWindowTarget::X(p) => Self::X(x11::CustomCursor::build(builder, p)),
}
impl PhysicalKeyExtScancode for PhysicalKey {
fn from_scancode(scancode: u32) -> PhysicalKey {
common::keymap::scancode_to_keycode(scancode)
}
fn to_scancode(self) -> Option<u32> {
common::keymap::physicalkey_to_scancode(self)
}
}
@@ -665,7 +710,7 @@ unsafe extern "C" fn x_error_callback(
// Don't log error.
if !error_handled {
log::error!("X11 error: {:#?}", error);
error!("X11 error: {:#?}", error);
// XXX only update the error, if it wasn't handled by any of the hooks.
*xconn.latest_error.lock().unwrap() = Some(error);
}
@@ -712,11 +757,8 @@ impl<T: 'static> EventLoop<T> {
let backend = match (
attributes.forced_backend,
env::var("WAYLAND_DISPLAY")
.ok()
.filter(|var| !var.is_empty())
.or_else(|| env::var("WAYLAND_SOCKET").ok())
.filter(|var| !var.is_empty())
.is_some(),
.map(|var| !var.is_empty())
.unwrap_or(false),
env::var("DISPLAY")
.map(|var| !var.is_empty())
.unwrap_or(false),
@@ -730,15 +772,10 @@ impl<T: 'static> EventLoop<T> {
#[cfg(x11_platform)]
(None, _, true) => Backend::X,
// No backend is present.
(_, wayland_display, x11_display) => {
let msg = if wayland_display && !cfg!(wayland_platform) {
"DISPLAY is not set; note: enable the `winit/wayland` feature to support Wayland"
} else if x11_display && !cfg!(x11_platform) {
"neither WAYLAND_DISPLAY nor WAYLAND_SOCKET is set; note: enable the `winit/x11` feature to support X11"
} else {
"neither WAYLAND_DISPLAY nor WAYLAND_SOCKET nor DISPLAY is set."
};
return Err(EventLoopError::Os(os_error!(OsError::Misc(msg))));
_ => {
return Err(EventLoopError::Os(os_error!(OsError::Misc(
"neither WAYLAND_DISPLAY nor DISPLAY is set."
))));
}
};
@@ -772,26 +809,26 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
where
F: FnMut(crate::event::Event<T>, &RootELW),
F: FnMut(crate::event::Event<T>, &RootELW<T>),
{
self.run_on_demand(callback)
}
pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
where
F: FnMut(crate::event::Event<T>, &RootELW),
F: FnMut(crate::event::Event<T>, &RootELW<T>),
{
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(callback))
}
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
where
F: FnMut(crate::event::Event<T>, &RootELW),
F: FnMut(crate::event::Event<T>, &RootELW<T>),
{
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
}
pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget {
pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget<T> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
}
}
@@ -814,14 +851,14 @@ impl<T: 'static> EventLoopProxy<T> {
}
}
pub enum EventLoopWindowTarget {
pub enum EventLoopWindowTarget<T> {
#[cfg(wayland_platform)]
Wayland(wayland::EventLoopWindowTarget),
Wayland(wayland::EventLoopWindowTarget<T>),
#[cfg(x11_platform)]
X(x11::EventLoopWindowTarget),
X(x11::EventLoopWindowTarget<T>),
}
impl EventLoopWindowTarget {
impl<T> EventLoopWindowTarget<T> {
#[inline]
pub fn is_wayland(&self) -> bool {
match *self {
@@ -893,15 +930,6 @@ impl EventLoopWindowTarget {
x11_or_wayland!(match self; Self(evlp) => evlp.exiting())
}
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
match self {
#[cfg(x11_platform)]
Self::X(conn) => OwnedDisplayHandle::X(conn.x_connection().clone()),
#[cfg(wayland_platform)]
Self::Wayland(conn) => OwnedDisplayHandle::Wayland(conn.connection.clone()),
}
}
fn set_exit_code(&self, code: i32) {
x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code))
}
@@ -911,67 +939,6 @@ impl EventLoopWindowTarget {
}
}
#[derive(Clone)]
#[allow(dead_code)]
pub(crate) enum OwnedDisplayHandle {
#[cfg(x11_platform)]
X(Arc<XConnection>),
#[cfg(wayland_platform)]
Wayland(wayland_client::Connection),
}
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_05")]
#[inline]
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
match self {
#[cfg(x11_platform)]
Self::X(xconn) => {
let mut xlib_handle = rwh_05::XlibDisplayHandle::empty();
xlib_handle.display = xconn.display.cast();
xlib_handle.screen = xconn.default_screen_index() as _;
xlib_handle.into()
}
#[cfg(wayland_platform)]
Self::Wayland(conn) => {
use sctk::reexports::client::Proxy;
let mut wayland_handle = rwh_05::WaylandDisplayHandle::empty();
wayland_handle.display = conn.display().id().as_ptr() as *mut _;
wayland_handle.into()
}
}
}
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
use std::ptr::NonNull;
match self {
#[cfg(x11_platform)]
Self::X(xconn) => Ok(rwh_06::XlibDisplayHandle::new(
NonNull::new(xconn.display.cast()),
xconn.default_screen_index() as _,
)
.into()),
#[cfg(wayland_platform)]
Self::Wayland(conn) => {
use sctk::reexports::client::Proxy;
Ok(rwh_06::WaylandDisplayHandle::new(
NonNull::new(conn.display().id().as_ptr().cast()).unwrap(),
)
.into())
}
}
}
}
/// Returns the minimum `Option<Duration>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`)

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