Compare commits

..

101 Commits

Author SHA1 Message Date
Mads Marquart
1850131b97 Document that multiple windows are not supported on Android 2024-02-20 15:32:08 +01:00
Mads Marquart
2bc1943188 Pin ahash in CI (#3498)
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2024-02-16 17:51:48 +01:00
Kirill Chibisov
385c4b3c88 On Wayland, update title from AboutToWait
Fixes #3472.
2024-02-14 00:48:52 +04:00
Kirill Chibisov
ea70f773d3 On X11, don't require XIM to be present
In general, we may want to use xinput v2 for keyboard input in such
cases, so we have compose going, but for now just don't crash if
there's no XIM.
2024-02-13 07:49:58 +04:00
AryaveerSR
83012f4c1c Fix Documentation for ScaleFactorChanged in WindowEvent. (#3489) 2024-02-12 18:20:03 +01:00
Jeremy Soller
6825fed073 On Orbital, implement various Window methods
Implement the following methods on the `Window`:
  - `Window::set_cursor_grab`.
  - `Window::set_cursor_visible`.
  - `Window::drag_window`.
  - `Window::drag_resize_window`.
  - `Window::set_transparent`.
  - `Window::set_visible`.
  - `Window::is_visible`.
  - `Window::set_resizable`.
  - `Window::is_resizable`.
  - `Window::set_maximized`.
  - `Window::is_maximized`.
  - `Window::set_decorations`.
  - `Window::is_decorated`.
  - `Window::set_window_level`.

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

Fixes: df8805c0 (On X11, reload DPI on _XSETTINGS_SETTINGS)
2024-02-10 00:24:03 +04:00
Mads Marquart
4112fccc12 Update meeting time (#3475) 2024-02-09 17:20:46 +01:00
Jeremy Soller
bd6ce32860 On Orbital, map keys to NamedKey when possible 2024-02-09 11:28:30 +04:00
Kirill Chibisov
8936fe1acd Fix nightly CI dead_code warnings 2024-02-08 13:28:26 +04:00
Jeremy Soller
fedb86ea5a On Orbital, implement KeyEventExtModifiersSupplement
This also fixes `logical_key` and `text` not reported in `KeyEvent`.
2024-02-08 12:55:11 +04:00
Kirill Chibisov
20687fef1c Fix compatibility with platforms without AtomicU64
Fixes #3456.
2024-02-08 00:58:43 +04:00
Kirill Chibisov
56035e1f13 Account for WAYLAND_SOCKET when detecting Wayland
Fixes #3459.
2024-02-07 06:41:23 +04:00
Amr Bashir
08fc4099e8 On Windows, apply ScaleFactorChanged new size if different than OS (#3408)
This fixes an issue when setting the position of the window on a new monitor and immediately maximizing it

```rs
window.set_outer_position::<PhysicalPosition<u32>>((2000, 200).into());
window.set_maximized(true);
```

Due to the nature of the event loop, the requested position and maximization state will apply correctly but due to the fact that the new position is a different monitor, a `ScaleFactorChanged` is emitted afterwards to the evenloop and a new size is set while the window is still maximized which results in a window that has `WS_MAXIMZE` window style but doesn't cover the whole monitor.
2024-02-06 20:46:30 +01:00
Kirill Chibisov
4d4d6e5052 On Wayland, fix min/max inner size setting
The size is only applied on the next `wl_surface::commit` thus we
must trigger the redraw.
2024-02-01 00:11:31 +04:00
Kirill Chibisov
cf5f4de19e Specify that alpha channel is not premultiplied 2024-01-30 21:31:17 +04:00
Kirill Chibisov
21df84b7f4 On Wayland, pre-multiply alpha for custom cursor
Fixes: #3360
2024-01-30 21:31:17 +04:00
Kirill Chibisov
dd13ccda4c On Wayland, send Focused(false) once seats left
Given that we merge all the seats, we should consider that window
is not focused once all seats wl_keyboards are no longer present.

We use seats instead of keyboards to track focus to protect against
wl_keyboard::leave not being delivered when removing the seat
(usually it's not the case though).

Fixes: #3376
2024-01-30 18:28:13 +04:00
John Nunley
df8805c0d2 On X11, reload DPI on _XSETTINGS_SETTINGS
This also fixes the deadlock when such reload may happen.

Fixes: #3383
Signed-off-by: John Nunley <dev@notgull.net>
Signed-off-by: Kirill Chibisov <contact@kchibisov.com>
2024-01-30 16:52:29 +04:00
Kirill Chibisov
db1ca45a17 Move ::builder changes to the correct release
They were added to 0.29.2 release.

Fixes: 8862ce01 (Add EventLoop::builder)
Fixes: 569c44a6 (Add Window::builder)
2024-01-30 14:28:42 +04:00
Kirill Chibisov
ff731197dc On Wayland, disable Occluded handling
Change in state requires a redraw, however drawing when getting
`Occluded` with vsync will block indefinitely, thus the event in
it's current state is rather useless.

To solve this issue winit needs a way to determine whether the user
paused/continued their render loop, so it can commit on their behalf.

This commit also forces redraw when getting configure.

Links: https://github.com/rust-windowing/winit/issues/3442
2024-01-30 13:00:10 +04:00
Mads Marquart
f526a47152 Remove EventLoopError::AlreadyRunning
This is already prevented by the type-system, and as such it doesn't
make sense to have an error case for this.
2024-01-29 22:06:03 +04:00
Mads Marquart
f204467838 Add a note about winit team meetings 2024-01-29 21:41:05 +04:00
Bruce Mitchener
6641dfa412 Bump cfg_aliases to 0.2.0 2024-01-29 21:27:23 +04:00
John Nunley
c1168b4f58 Remove drm/kms features from softbuffer (#3439)
We use softbuffer as a dev-dependency for rendering into our windows in
examples. However, we do not support a DRM/KMS backend yet, while
softbuffer comes with a DRM/KMS backend by default. This commit removes
the DRM/KMS feature from softbuffer to save some build time during
testing
2024-01-28 22:40:01 +01:00
Ulrik de Muelenaere
f8b7c4b78f bugfix: Fix swapped instance and general class names on X11
This let statement swapped the two names, resulting in incorrect
behavior since commit d7ec899d. That commit did not actually introduce
the swap, but the previous code swapped it again before setting the
WM_CLASS property, so no issue was ever observed.

It also brings the documentation in line with the implementation since the
parent commit, and with the ICCCM standard, which states the following
about the WM_CLASS property [1]:

  The two strings, respectively, are:
  * A string that names the particular instance of the application [...]
  * A string that names the general class of applications [...]

[1] https://www.x.org/releases/current/doc/xorg-docs/icccm/icccm.html#WM_CLASS_Property
2024-01-27 18:40:28 -08:00
John Nunley
3830b492c4 Update new builders for latest master
Signed-off-by: John Nunley <dev@notgull.net>
2024-01-27 10:16:52 -08:00
Mads Marquart
8862ce0163 Add EventLoop::builder, which replaces EventLoopBuilder::new
Similarly for EventLoop::with_user_event and EventLoopBuilder::with_user_event
2024-01-27 10:16:52 -08:00
Mads Marquart
569c44a632 Add Window::builder, which replaces WindowBuilder::new 2024-01-27 10:16:52 -08:00
Mads Marquart
ef2ec904ce Fix iOS gesture deltas (#3426) 2024-01-25 23:46:48 +01:00
Dubzer
98d3391f2d Add DWMWA_SYSTEMBACKDROP_TYPE support on Windows (#3257) 2024-01-25 18:59:10 +01:00
Diggory Hardy
d0a1917603 Improve error when X11/Wayland is not present 2024-01-25 14:49:36 +04:00
Mads Marquart
b36d8d1e52 Refactor user event handling on macOS/iOS (#3422)
Move user event handling to inside main event loop
2024-01-25 05:26:50 +01:00
Nick
a5b08fc48c Send the event before waking up the message pump. (#3418) 2024-01-24 21:38:20 +01:00
Mads Marquart
0482d9cfce Fix Android examples link in README (#3420) 2024-01-24 20:41:33 +01:00
Amr Bashir
10a785019c Add option to enable/disable WS_CLIPCHILDREN window style (#3212) 2024-01-22 18:55:37 +01:00
Amr Bashir
0cc19716f3 On Windows, Remove WS_CAPTION, WS_BORDER and WS_EX_WINDOWEDGE styles for child windows (#3410) 2024-01-20 13:07:03 +01:00
白山風露
572d7ee77c On Windows, set fullscreen/maximized creating window 2024-01-19 21:43:08 +04:00
sidit77
b0c59c8416 On Windows, expose DWM attributes (#3409) 2024-01-19 12:43:39 +01:00
daxpedda
d7c7ba1d6c Move PlatformSpecificWindowBuilderAttributes (#3318) 2024-01-17 23:37:28 +01:00
daxpedda
aec608f93c Document Window Drop behavior (#3315) 2024-01-17 23:17:36 +01:00
daxpedda
d1717b6a01 X11: cache custom cursors (#3366) 2024-01-17 18:17:49 +01:00
François
6b29253797 Support pinch, double tap and rotation gestures on iOS (#3130)
This is off by default on iOS. Note that pinch delta may be NaN.

Co-authored-by: Mads Marquart <mads@marquart.dk>
2024-01-16 21:31:18 +01:00
Kirill Chibisov
30775f4982 Update platform and core maintainers 2024-01-16 21:45:29 +04:00
Mads Marquart
41070d7c67 Make DeviceId simpler on iOS (#3402)
This previously contained a UIScreen for some weird reason; perhaps
because `DeviceId` was confused for `UIDevice`?
2024-01-15 21:51:01 +01:00
John Nunley
6df972d108 feat: Add an owned display handle type
This was supposed to be rolled out with the rwh v0.6 update, but it
was left behind for some reason. I've added this type back.

Signed-off-by: John Nunley <dev@notgull.net>
2024-01-15 11:58:11 -08:00
Kirill Chibisov
73910d20cc Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-01-15 13:10:46 +04:00
daxpedda
16d860736b Platform is called Web not Wasm (#3393) 2024-01-14 18:54:52 +01:00
daxpedda
2ee44246ae Bump web-time to v1 (#3392) 2024-01-14 18:37:32 +01:00
Mads Marquart
14b418a3a7 macOS: Merge window and delegate state (#3391)
Previously we had a sort of artificial split between these, but both were accessing each other's state, since it's really the same state!
It was especially difficult to follow what happens to the fullscreen state.
So instead, we basically merge the window and the delegate files.

This does unfortunately screw a bit with the git history, apologies to whoever reads this in the future!
2024-01-14 05:19:23 +01:00
Mads Marquart
c86b0daf7f macOS: Remove unnecessary Mutex in window state (#3390) 2024-01-14 04:44:10 +01:00
Mads Marquart
40b61d2d92 macOS: Remove global HANDLER and AppState (#3389) 2024-01-14 03:37:53 +01:00
Mads Marquart
22311802b5 Remove generic parameter T from EventLoopWindowTarget (#3298) 2024-01-13 21:36:53 +01:00
daxpedda
169cd39f93 Web: improve custom cursor handling and add animated cursors (#3384) 2024-01-12 11:51:19 +01:00
Alexander Medvedev
bdeb2574dc Update Redox (#3368) 2024-01-10 20:05:52 +01:00
daxpedda
4fe38d8067 Web: increase cursor position accuracy (#3380) 2024-01-10 13:38:32 +01:00
daxpedda
816798bfd1 Web: support Firefox privacy.resistFingerprinting (#3371) 2024-01-06 23:05:51 +01:00
daxpedda
f99c810bec ci: Fix dead code error on nightly
See https://github.com/rust-lang/rust/pull/118297
2024-01-06 07:54:29 -08:00
daxpedda
d39528aa69 Web: account for canvas being focused already (#3369) 2024-01-06 16:14:27 +01:00
daxpedda
787b2d7362 Windows: cache custom cursors (#3293) 2024-01-05 17:02:08 +01:00
daxpedda
37b6243289 Deploy master docs to GitHub Pages (#3359) 2024-01-05 15:05:12 +01:00
Kirill Chibisov
8ea1da7879 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2024-01-05 15:18:55 +04:00
Emil Ernerfeldt
b1209bc253 On macOS, reported shifted key with shift+Ctrl/Cmd
Fixes #3078.
2024-01-05 14:53:47 +04:00
Kirill Chibisov
021fd23c34 On X11, fix error propagation in EventLoop::new
Fixes #3350.
2024-01-05 08:33:23 +04:00
nerditation
dd127463c5 Windows: Make EventLoopWindowTarget independent of UserEvent type (#3061)
* make `EventLoopWindowTarget` independent of UserEvent type

the `EventLoopWindowTarget` is needed for window creation. conceptually,
only `EventLoop` and `EventLoopProxy` need to be parameterized, and all
other parts of the backend should be agnostic about the user event type,
parallel to how `Event<T>` is parameterized, but `WindowEvent` is not.

this change removes the dependency on the type of user events from the
`EventLoopWindowTarget` for the Windows backend, but keep a phantom data
to keep the API intact. to achieve this, I moved the `Receiver` end of
the mpsc channel from `ThreadMsgTargetData` into `EventLoop` itself, so
the `UserEvent` is only passed between `EventLoop` and `EventLoopProxy`,
all other part of the backend just use unit type as a placeholder for
user events.

it's similar to the macos backend where an erased `EventHandler` trait
object is used so all component except `EventLoop` and `EventLoopProxy`
need to be parameterized. however `EventLoop` of the Windows backend
already use an `Box<dyn FnMut>` to wrap the user provided event handler
callback, so no need for an dedicated trait object, I just modified the
wrapper to replace the placeholder user event with real value pulled
from the channel. I find this is the approach which need minimum change
to be made to existing code. but it does the job and could serve as a
starting point to future Windows backend re-works.

* fix CI clippy failure.

* make UserEventPlaceholder a new type instead of alias

* invariance is maintained by top-level EventLoopWindowTarget<T>

this field is transitional and her to keep API compatibility only.
the correct variance and such is already ensured by the top-level
`EventLoopWindowTarget`, just use `PhantomData<T>` here.
2024-01-04 16:47:07 +01:00
daxpedda
ac247cd081 Fix missing target in docs.rs test (#3358) 2024-01-04 14:40:06 +01:00
daxpedda
ea1bfd254d Add Wasm atomic target to CI (#3357) 2024-01-04 14:21:19 +01:00
daxpedda
178f5fda05 Test all docs.rs deployments (#3356) 2024-01-04 13:59:31 +01:00
Mads Marquart
42dbc4748e Display all platform-specific documentation on docs.rs (#3076) 2024-01-04 12:54:35 +01:00
Kirill Chibisov
8b3de7cedf Issue resize due to scale change on Wayland
This is a regression from 8f6de4ef.

Links: https://github.com/alacritty/alacritty/issues/7559
2024-01-03 21:49:11 +04:00
Kirill Chibisov
8b0ffb7e7d On X11 and Wayland, fix numpad up being ArrowLeft
Links: https://github.com/alacritty/alacritty/issues/7533
2024-01-02 23:55:51 +04:00
Kirill Chibisov
c55a2c779b Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2023-12-31 20:25:19 +04:00
Kirill Chibisov
c5a422eed6 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 07:43:02 +04:00
John Nunley
1893b0ec42 On X11, cache the XRandR extension version 2023-12-30 10:04:27 +04:00
Kirill Chibisov
5e106b4dbb 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-30 09:05:03 +04:00
Kirill Chibisov
5a1d3e4656 On X11, update keymap on XkbMapNotify
This is required to handle xmodmap.

Fixes #3338.
2023-12-30 01:10:38 +04:00
Kirill Chibisov
8f6de4ef4b 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-29 21:28:06 +04:00
John Nunley
ad1843aea6 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-29 20:13:06 +04:00
Kirill Chibisov
ca1674519a Bump version on master (#3332)
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2023-12-27 10:30:59 +04:00
John Nunley
f78edc7ef1 bugfix: Change value sent to X server during minimize
Closes #3327

Signed-off-by: John Nunley <dev@notgull.net>
2023-12-26 21:25:27 -08:00
daxpedda
4f295e0c94 Add deprecated Window::set_cursor_icon() (#3329) 2023-12-26 22:40:43 +01:00
daxpedda
658f49b014 Rename VideoMode to VideoModeHandle (#3328) 2023-12-26 22:12:33 +01:00
daxpedda
34e42ff94d Remove unsound SendSyncWrapper (#3303) 2023-12-26 20:13:02 +01:00
daxpedda
ba654bb61e Add WindowBuilder::with_cursor() (#3319) 2023-12-26 19:50:58 +01:00
daxpedda
f5c691467b MacOS: check if cursor changed before applying (#3324) 2023-12-26 19:26:50 +01:00
John Nunley
a87cfb62c3 bugfix: Reload Xft database on DPI change
Closes #1228
2023-12-25 21:25:55 -08:00
daxpedda
25d6a1d46d Web: improve custom cursor loading (#3321) 2023-12-26 03:49:20 +01:00
daxpedda
e0fea25b06 Make canvas in WindowBuilder safe (#3320) 2023-12-26 01:22:10 +01:00
daxpedda
843d7904d6 On Web, add Window::(set_)prevent_default() (#3307) 2023-12-25 09:37:35 +01:00
daxpedda
28a811bbba Remove extern crate statements (#3310) 2023-12-25 09:25:09 +01:00
daxpedda
61a873d79a Remove wrong documentation on EventLoop::run() (#3314) 2023-12-25 08:27:34 +01:00
daxpedda
be4a660011 Merge Window::set_cursor_icon() and Window::set_custom_cursor() (#3308) 2023-12-25 07:20:52 +01:00
daxpedda
34dd2cdba9 Doc fixes (#3312) 2023-12-25 00:54:01 +01:00
Kirill Chibisov
775c8ece70 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2023-12-25 00:30:59 +04:00
Uli Schlachter
c12c7b82e8 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 22:27:02 +04:00
Kirill Chibisov
8cc5cb9d9b Fix run_on_demand exiting on consequent call
Fixes #3284.
2023-12-24 22:21:45 +04:00
Kirill Chibisov
9a28bb4b49 On Wayland, fix WindowEvent::Destroyed delivery 2023-12-24 22:21:45 +04:00
Mads Marquart
4f6fd44c6c macOS: Clean up coordinate system calculations (#3302)
* Clean up macOS and iOS monitor code a bit

* Clean up window size methods

Use `setContentSize`, `setContentMinSize`, `setContentMaxSize` and `contentRectForFrameRect` to let the windowing system figure out the required scaling, instead of us doing it manually.

* Use a flipped NSView coordinate system

* Clean up window position methods
2023-12-24 10:12:09 +01:00
Alex Butler
5a43ea8cd6 bugfix(rwh): Bump rwh_05 min version to 0.5.2
Correct min version to support "std" feature
2023-12-23 22:37:35 -08:00
160 changed files with 9769 additions and 7312 deletions

16
.github/CODEOWNERS vendored
View File

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

View File

@@ -48,6 +48,15 @@ jobs:
include: include:
- toolchain: '1.70.0' - toolchain: '1.70.0'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' } platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
- toolchain: 'nightly'
platform: {
name: 'web Atomic',
target: wasm32-unknown-unknown,
os: ubuntu-latest,
options: '-Zbuild-std=panic_abort,std',
rustflags: '-Ctarget-feature=+atomics,+bulk-memory',
components: rust-src,
}
env: env:
# Set more verbose terminal output # Set more verbose terminal output
@@ -55,8 +64,7 @@ jobs:
RUST_BACKTRACE: 1 RUST_BACKTRACE: 1
# Faster compilation and error on warnings # Faster compilation and error on warnings
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings' RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}'
RUSTDOCFLAGS: '--deny=warnings'
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }} OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
CMD: ${{ matrix.platform.cmd }} CMD: ${{ matrix.platform.cmd }}
@@ -81,7 +89,7 @@ jobs:
- name: Generate lockfile - name: Generate lockfile
# Also updates the crates.io index # Also updates the crates.io index
run: cargo generate-lockfile run: cargo generate-lockfile && cargo update -p ahash --precise 0.8.7
- name: Install GCC Multilib - name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686') if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
@@ -109,10 +117,12 @@ jobs:
with: with:
toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }} toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
targets: ${{ matrix.platform.target }} targets: ${{ matrix.platform.target }}
components: clippy components: clippy, ${{ matrix.platform.components }}
- name: Check documentation - name: Check documentation
run: cargo doc --no-deps $OPTIONS --document-private-items run: cargo doc --no-deps $OPTIONS --document-private-items
env:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }}'
- name: Build crate - name: Build crate
run: cargo $CMD build $OPTIONS run: cargo $CMD build $OPTIONS
@@ -151,6 +161,12 @@ jobs:
matrix.toolchain != '1.70.0' matrix.toolchain != '1.70.0'
run: cargo $CMD test $OPTIONS --features serde 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 # See restore step above
- name: Save cache of cargo folder - name: Save cache of cargo folder
uses: actions/cache/save@v3 uses: actions/cache/save@v3

50
.github/workflows/docs.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
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,18 +11,97 @@ Unreleased` header.
# Unreleased # Unreleased
- On X11, don't require XIM to run.
- Fix compatibility with 32-bit platforms without 64-bit atomics.
- 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. - On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example.
- Add `Window::set_custom_cursor` - **Breaking:** Remove `Window::set_cursor_icon`
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a `CursorIcon` or `CustomCursor`
- Add `CustomCursor` - Add `CustomCursor`
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data. - 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_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. - On macOS, add services menu.
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation. - **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
- On Web, fix setting cursor icon overriding cursor visibility. - On Web, fix setting cursor icon overriding cursor visibility.
- On Web, fix context menu not being disabled by `with_prevent_default(true)`.
- **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`. - **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:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available.
- **Breaking:** Bump MSRV from `1.65` to `1.70`. - **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.
- On Orbital, fix `logical_key` and `text` not reported in `KeyEvent`.
- On Orbital, implement `KeyEventExtModifierSupplement`.
- On Orbital, map keys to `NamedKey` when possible.
- On Orbital, implement `set_cursor_grab`.
- On Orbital, implement `set_cursor_visible`.
- On Orbital, implement `drag_window`.
- On Orbital, implement `drag_resize_window`.
- On Orbital, implement `set_transparent`.
- On Orbital, implement `set_visible`.
- On Orbital, implement `is_visible`.
- On Orbital, implement `set_resizable`.
- On Orbital, implement `is_resizable`.
- On Orbital, implement `set_maximized`.
- On Orbital, implement `is_maximized`.
- On Orbital, implement `set_decorations`.
- On Orbital, implement `is_decorated`.
- On Orbital, implement `set_window_level`.
- On Orbital, emit `DeviceEvent::MouseMotion`.
- On Wayland, fix title in CSD not updated from `AboutToWait`.
# 0.29.10
- On Web, account for canvas being focused already before event loop starts.
- On Web, increase cursor position accuracy.
# 0.29.9
- On X11, fix `NotSupported` error not propagated when creating event loop.
- On Wayland, fix resize not issued when scale changes
- On X11 and Wayland, fix arrow up on keypad reported as `ArrowLeft`.
- On macOS, report correct logical key when Ctrl or Cmd is pressed.
# 0.29.8
- On X11, fix IME input lagging behind.
- On X11, fix `ModifiersChanged` not sent from xdotool-like input
- On X11, fix keymap not updated from xmodmap.
- On X11, reduce the amount of time spent fetching screen resources.
- On Wayland, fix `Window::request_inner_size` being overwritten by resize.
- On Wayland, fix `Window::inner_size` not using the correct rounding.
# 0.29.7
- On X11, fix `Xft.dpi` reload during runtime.
- On X11, fix window minimize.
# 0.29.6
- On Web, fix context menu not being disabled by `with_prevent_default(true)`.
- On Wayland, fix `WindowEvent::Destroyed` not being delivered after destroying window.
- Fix `EventLoopExtRunOnDemand::run_on_demand` not working for consequent invocation
# 0.29.5 # 0.29.5
@@ -47,6 +126,7 @@ Unreleased` header.
- On macOS, send a `Resized` event after each `ScaleFactorChanged` event. - On macOS, send a `Resized` event after each `ScaleFactorChanged` event.
- On Wayland, fix `wl_surface` being destroyed before associated objects. - On Wayland, fix `wl_surface` being destroyed before associated objects.
- On macOS, fix assertion when pressing `Fn` key. - On macOS, fix assertion when pressing `Fn` key.
- On Windows, add `WindowBuilderExtWindows::with_clip_children` to control `WS_CLIPCHILDREN` style.
# 0.29.3 # 0.29.3

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "winit" name = "winit"
version = "0.29.5" version = "0.29.10"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"] authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library." description = "Cross-platform window creation library."
edition = "2021" edition = "2021"
@@ -18,6 +18,7 @@ features = [
"rwh_05", "rwh_05",
"rwh_06", "rwh_06",
"serde", "serde",
"mint",
# Enabled to get docs to compile # Enabled to get docs to compile
"android-native-activity", "android-native-activity",
] ]
@@ -36,7 +37,7 @@ targets = [
"x86_64-apple-ios", "x86_64-apple-ios",
# Android # Android
"aarch64-linux-android", "aarch64-linux-android",
# WebAssembly # Web
"wasm32-unknown-unknown", "wasm32-unknown-unknown",
] ]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
@@ -57,7 +58,7 @@ rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"] rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
[build-dependencies] [build-dependencies]
cfg_aliases = "0.1.1" cfg_aliases = "0.2.0"
[dependencies] [dependencies]
bitflags = "2" bitflags = "2"
@@ -66,7 +67,7 @@ log = "0.4"
mint = { version = "0.5.6", optional = true } mint = { version = "0.5.6", optional = true }
once_cell = "1.12" once_cell = "1.12"
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true } rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
rwh_05 = { package = "raw-window-handle", version = "0.5", features = ["std"], optional = true } rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
serde = { version = "1", optional = true, features = ["serde_derive"] } serde = { version = "1", optional = true, features = ["serde_derive"] }
smol_str = "0.2.0" smol_str = "0.2.0"
@@ -77,7 +78,7 @@ simple_logger = { version = "4.2.0", default_features = false }
winit = { path = ".", features = ["rwh_05"] } winit = { path = ".", features = ["rwh_05"] }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
softbuffer = "0.3.0" softbuffer = { version = "0.3.0", default-features = false, features = ["x11", "x11-dlopen", "wayland", "wayland-dlopen"] }
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.5.0" android-activity = "0.5.0"
@@ -173,7 +174,7 @@ features = [
] ]
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies] [target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
ahash = { version = "0.8.3", features = ["no-rng"], optional = true } ahash = { version = "0.8.7", features = ["no-rng"], optional = true }
bytemuck = { version = "1.13.1", default-features = false, optional = true } bytemuck = { version = "1.13.1", default-features = false, optional = true }
calloop = "0.12.3" calloop = "0.12.3"
libc = "0.2.64" libc = "0.2.64"
@@ -191,8 +192,8 @@ x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsaf
xkbcommon-dl = "0.4.0" xkbcommon-dl = "0.4.0"
[target.'cfg(target_os = "redox")'.dependencies] [target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.42", default-features = false } orbclient = { version = "0.3.47", default-features = false }
redox_syscall = "0.3" redox_syscall = "0.4.1"
[target.'cfg(target_family = "wasm")'.dependencies.web_sys] [target.'cfg(target_family = "wasm")'.dependencies.web_sys]
package = "web-sys" package = "web-sys"
@@ -204,6 +205,7 @@ features = [
'console', 'console',
'CssStyleDeclaration', 'CssStyleDeclaration',
'Document', 'Document',
'DomException',
'DomRect', 'DomRect',
'DomRectReadOnly', 'DomRectReadOnly',
'Element', 'Element',
@@ -239,11 +241,15 @@ features = [
] ]
[target.'cfg(target_family = "wasm")'.dependencies] [target.'cfg(target_family = "wasm")'.dependencies]
atomic-waker = "1"
js-sys = "0.3.64" js-sys = "0.3.64"
pin-project = "1"
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4" wasm-bindgen-futures = "0.4"
web-time = "0.2" web-time = "1"
[target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies]
atomic-waker = "1"
concurrent-queue = { version = "2", default-features = false }
[target.'cfg(target_family = "wasm")'.dev-dependencies] [target.'cfg(target_family = "wasm")'.dev-dependencies]
console_log = "1" console_log = "1"
@@ -253,3 +259,7 @@ web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
members = [ members = [
"run-wasm", "run-wasm",
] ]
[[example]]
doc-scrape-examples = true
name = "window"

View File

@@ -126,6 +126,11 @@ If your PR makes notable changes to Winit's features, please update this section
* Setting a menu bar * Setting a menu bar
* `WS_EX_NOREDIRECTIONBITMAP` support * `WS_EX_NOREDIRECTIONBITMAP` support
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme * 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 ### macOS
* Window activation policy * Window activation policy

View File

@@ -2,11 +2,13 @@
[![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit) [![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) [![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) [![CI Status](https://github.com/rust-windowing/winit/workflows/CI/badge.svg)](https://github.com/rust-windowing/winit/actions)
```toml ```toml
[dependencies] [dependencies]
winit = "0.29.5" winit = "0.29.10"
``` ```
## [Documentation](https://docs.rs/winit) ## [Documentation](https://docs.rs/winit)
@@ -17,10 +19,9 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
## Contact Us ## Contact Us
Join us in any of these: 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).
[![Matrix](https://img.shields.io/badge/Matrix-%23rust--windowing%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#rust-windowing:matrix.org) The maintainers have a meeting every friday at UTC 15. The meeting notes can be found [here](https://hackmd.io/@winit-meetings).
[![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit)
## Usage ## Usage
@@ -74,7 +75,7 @@ same MSRV policy.
Note that windows don't appear on Wayland until you draw/present to them. Note that windows don't appear on Wayland until you draw/present to them.
#### WebAssembly #### Web
To run the web example: `cargo run-wasm --example web` To run the web example: `cargo run-wasm --example web`
@@ -85,7 +86,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 create a `<canvas>` element which you can then retrieve][web canvas getter] and
insert it into the DOM yourself. insert it into the DOM yourself.
For the example code using Winit with WebAssembly, check out the [web example]. For For the example code using Winit on Web, check out the [web example]. For
information on using Rust on WebAssembly, check out the [Rust and WebAssembly information on using Rust on WebAssembly, check out the [Rust and WebAssembly
book]. book].
@@ -150,13 +151,13 @@ 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 [agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
[Gradle]: https://developer.android.com/studio/build [Gradle]: https://developer.android.com/studio/build
For more details, refer to these `android-activity` [example applications](https://github.com/rib/android-activity/tree/main/examples). For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples).
##### Converting from `ndk-glue` to `android-activity` ##### Converting from `ndk-glue` to `android-activity`
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be: If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
1. Remove `ndk-glue` from your `Cargo.toml` 1. Remove `ndk-glue` from your `Cargo.toml`
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.5", features = [ "android-native-activity" ] }` 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.10", features = [ "android-native-activity" ] }`
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above). 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above). 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).

View File

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

View File

@@ -11,4 +11,5 @@ disallowed-methods = [
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" }, { path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" }, { path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
{ path = "icrate::AppKit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." }, { path = "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,7 +34,6 @@ skip = [
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this { name = "raw-window-handle" }, # we intentionally have multiple versions of this
{ name = "bitflags" }, # the ecosystem is in the process of migrating. { name = "bitflags" }, # the ecosystem is in the process of migrating.
{ name = "libloading" }, # x11rb uses a different version until the next update { name = "libloading" }, # x11rb uses a different version until the next update
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
] ]
skip-tree = [] skip-tree = []

View File

@@ -17,22 +17,23 @@ fn main() -> Result<(), impl std::error::Error> {
dpi::{LogicalPosition, LogicalSize, Position}, dpi::{LogicalPosition, LogicalSize, Position},
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget}, event_loop::{EventLoop, EventLoopWindowTarget},
raw_window_handle::HasWindowHandle, raw_window_handle::HasRawWindowHandle,
window::{Window, WindowBuilder, WindowId}, window::{Window, WindowId},
}; };
fn spawn_child_window( fn spawn_child_window(
parent: &Window, parent: &Window,
event_loop: &EventLoopWindowTarget<()>, event_loop: &EventLoopWindowTarget,
windows: &mut HashMap<WindowId, Window>, windows: &mut HashMap<WindowId, Window>,
) { ) {
let parent = parent.window_handle().unwrap(); let parent = parent.raw_window_handle().unwrap();
let mut builder = WindowBuilder::new() let mut builder = Window::builder()
.with_title("child window") .with_title("child window")
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32)) .with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_visible(true); .with_visible(true);
builder = builder.with_parent_window(Some(parent)); // `with_parent_window` is unsafe. Parent window must be a valid window.
builder = unsafe { builder.with_parent_window(Some(parent)) };
let child_window = builder.build(event_loop).unwrap(); let child_window = builder.build(event_loop).unwrap();
let id = child_window.id(); let id = child_window.id();
@@ -43,7 +44,7 @@ fn main() -> Result<(), impl std::error::Error> {
let mut windows = HashMap::new(); let mut windows = HashMap::new();
let event_loop: EventLoop<()> = EventLoop::new().unwrap(); let event_loop: EventLoop<()> = EventLoop::new().unwrap();
let parent_window = WindowBuilder::new() let parent_window = Window::builder()
.with_title("parent window") .with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32)) .with_inner_size(LogicalSize::new(640.0f32, 480.0f32))

View File

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

View File

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

View File

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

View File

@@ -1,15 +1,24 @@
#![allow(clippy::single_match, clippy::disallowed_methods)] #![allow(clippy::single_match, clippy::disallowed_methods)]
#[cfg(not(wasm_platform))] #[cfg(not(web_platform))]
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget}, event_loop::{EventLoop, EventLoopWindowTarget},
keyboard::Key, keyboard::Key,
window::{CustomCursor, WindowBuilder}, window::{CursorIcon, CustomCursor, Window},
};
#[cfg(web_platform)]
use {
std::sync::atomic::{AtomicU64, Ordering},
std::time::Duration,
winit::platform::web::CustomCursorExtWebSys,
}; };
fn decode_cursor<T>(bytes: &[u8], window_target: &EventLoopWindowTarget<T>) -> CustomCursor { #[cfg(web_platform)]
static COUNTER: AtomicU64 = AtomicU64::new(0);
fn decode_cursor(bytes: &[u8], window_target: &EventLoopWindowTarget) -> CustomCursor {
let img = image::load_from_memory(bytes).unwrap().to_rgba8(); let img = image::load_from_memory(bytes).unwrap().to_rgba8();
let samples = img.into_flat_samples(); let samples = img.into_flat_samples();
let (_, w, h) = samples.extents(); let (_, w, h) = samples.extents();
@@ -19,22 +28,22 @@ fn decode_cursor<T>(bytes: &[u8], window_target: &EventLoopWindowTarget<T>) -> C
builder.build(window_target) builder.build(window_target)
} }
#[cfg(not(wasm_platform))] #[cfg(not(web_platform))]
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
#[cfg(not(wasm_platform))] #[cfg(not(web_platform))]
SimpleLogger::new() SimpleLogger::new()
.with_level(log::LevelFilter::Info) .with_level(log::LevelFilter::Info)
.init() .init()
.unwrap(); .unwrap();
#[cfg(wasm_platform)] #[cfg(web_platform)]
console_log::init_with_level(log::Level::Debug).unwrap(); console_log::init_with_level(log::Level::Debug).unwrap();
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let builder = WindowBuilder::new().with_title("A fantastic window!"); let builder = Window::builder().with_title("A fantastic window!");
#[cfg(wasm_platform)] #[cfg(web_platform)]
let builder = { let builder = {
use winit::platform::web::WindowBuilderExtWebSys; use winit::platform::web::WindowBuilderExtWebSys;
builder.with_append(true) builder.with_append(true)
@@ -47,6 +56,7 @@ fn main() -> Result<(), impl std::error::Error> {
let custom_cursors = [ let custom_cursors = [
decode_cursor(include_bytes!("data/cross.png"), &event_loop), decode_cursor(include_bytes!("data/cross.png"), &event_loop),
decode_cursor(include_bytes!("data/cross2.png"), &event_loop), decode_cursor(include_bytes!("data/cross2.png"), &event_loop),
decode_cursor(include_bytes!("data/gradient.png"), &event_loop),
]; ];
event_loop.run(move |event, _elwt| match event { event_loop.run(move |event, _elwt| match event {
@@ -62,26 +72,65 @@ fn main() -> Result<(), impl std::error::Error> {
} => match key.as_ref() { } => match key.as_ref() {
Key::Character("1") => { Key::Character("1") => {
log::debug!("Setting cursor to {:?}", cursor_idx); log::debug!("Setting cursor to {:?}", cursor_idx);
window.set_custom_cursor(&custom_cursors[cursor_idx]); window.set_cursor(custom_cursors[cursor_idx].clone());
cursor_idx = (cursor_idx + 1) % 2; cursor_idx = (cursor_idx + 1) % 3;
} }
Key::Character("2") => { Key::Character("2") => {
log::debug!("Setting cursor icon to default"); log::debug!("Setting cursor icon to default");
window.set_cursor_icon(Default::default()); window.set_cursor(CursorIcon::default());
} }
Key::Character("3") => { Key::Character("3") => {
cursor_visible = !cursor_visible; cursor_visible = !cursor_visible;
log::debug!("Setting cursor visibility to {:?}", cursor_visible); log::debug!("Setting cursor visibility to {:?}", cursor_visible);
window.set_cursor_visible(cursor_visible); window.set_cursor_visible(cursor_visible);
} }
#[cfg(web_platform)]
Key::Character("4") => {
log::debug!("Setting cursor to a random image from an URL");
window.set_cursor(
CustomCursor::from_url(
format!(
"https://picsum.photos/128?random={}",
COUNTER.fetch_add(1, Ordering::Relaxed)
),
64,
64,
)
.build(_elwt),
);
}
#[cfg(web_platform)]
Key::Character("5") => {
log::debug!("Setting cursor to an animation");
window.set_cursor(
CustomCursor::from_animation(
Duration::from_secs(3),
vec![
custom_cursors[0].clone(),
custom_cursors[1].clone(),
CustomCursor::from_url(
format!(
"https://picsum.photos/128?random={}",
COUNTER.fetch_add(1, Ordering::Relaxed)
),
64,
64,
)
.build(_elwt),
],
)
.unwrap()
.build(_elwt),
);
}
_ => {} _ => {}
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
#[cfg(not(wasm_platform))] #[cfg(not(web_platform))]
fill::fill_window(&window); fill::fill_window(&window);
} }
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
#[cfg(not(wasm_platform))] #[cfg(not(web_platform))]
_elwt.exit(); _elwt.exit();
} }
_ => (), _ => (),

View File

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

BIN
examples/data/gradient.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,17 +7,30 @@
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could //! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
//! also be used to fill the window buffer, but they are more complicated to use. //! also be used to fill the window buffer, but they are more complicated to use.
use winit::window::Window; #[allow(unused_imports)]
pub use platform::cleanup_window;
pub use platform::fill_window;
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))] #[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
pub(super) fn fill_window(window: &Window) { mod platform {
use softbuffer::{Context, Surface};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use softbuffer::{Context, Surface};
use winit::window::Window;
use winit::window::WindowId; use winit::window::WindowId;
thread_local! {
// NOTE: You should never do things like that, create context and drop it before
// you drop the event loop. We do this for brevity to not blow up examples. We use
// ManuallyDrop to prevent destructors from running.
//
// A static, thread-local map of graphics contexts to open windows.
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
}
/// The graphics context used to draw to a window. /// The graphics context used to draw to a window.
struct GraphicsContext { struct GraphicsContext {
/// The global softbuffer context. /// The global softbuffer context.
@@ -35,55 +48,69 @@ pub(super) fn fill_window(window: &Window) {
} }
} }
fn surface(&mut self, w: &Window) -> &mut Surface { fn create_surface(&mut self, window: &Window) -> &mut Surface {
self.surfaces.entry(w.id()).or_insert_with(|| { self.surfaces.entry(window.id()).or_insert_with(|| {
unsafe { Surface::new(&self.context, w) } unsafe { Surface::new(&self.context, window) }
.expect("Failed to create a softbuffer surface") .expect("Failed to create a softbuffer surface")
}) })
} }
fn destroy_surface(&mut self, window: &Window) {
self.surfaces.remove(&window.id());
}
} }
thread_local! { pub fn fill_window(window: &Window) {
// NOTE: You should never do things like that, create context and drop it before GC.with(|gc| {
// you drop the event loop. We do this for brevity to not blow up examples. We use let size = window.inner_size();
// ManuallyDrop to prevent destructors from running. let (Some(width), Some(height)) =
// (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
// A static, thread-local map of graphics contexts to open windows. else {
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None)); return;
};
// Either get the last context used or create a new one.
let mut gc = gc.borrow_mut();
let surface = gc
.get_or_insert_with(|| GraphicsContext::new(window))
.create_surface(window);
// Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xFF181818;
surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");
let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY);
buffer
.present()
.expect("Failed to present the softbuffer buffer");
})
} }
GC.with(|gc| { #[allow(dead_code)]
let size = window.inner_size(); pub fn cleanup_window(window: &Window) {
let (Some(width), Some(height)) = GC.with(|gc| {
(NonZeroU32::new(size.width), NonZeroU32::new(size.height)) let mut gc = gc.borrow_mut();
else { if let Some(context) = gc.as_mut() {
return; context.destroy_surface(window);
}; }
});
// Either get the last context used or create a new one. }
let mut gc = gc.borrow_mut();
let surface = gc
.get_or_insert_with(|| GraphicsContext::new(window))
.surface(window);
// Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xFF181818;
surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");
let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY);
buffer
.present()
.expect("Failed to present the softbuffer buffer");
})
} }
#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))] #[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
pub(super) fn fill_window(_window: &Window) { mod platform {
// No-op on mobile platforms. pub fn fill_window(_window: &winit::window::Window) {
// No-op on mobile platforms.
}
#[allow(dead_code)]
pub fn cleanup_window(_window: &winit::window::Window) {
// No-op on mobile platforms.
}
} }

View File

@@ -4,13 +4,13 @@ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
keyboard::Key, keyboard::Key,
window::{Fullscreen, WindowBuilder}, window::{Fullscreen, Window},
}; };
pub fn main() -> Result<(), impl std::error::Error> { pub fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let builder = WindowBuilder::new().with_title("A fantastic window!"); let builder = Window::builder().with_title("A fantastic window!");
#[cfg(wasm_platform)] #[cfg(wasm_platform)]
let builder = { let builder = {
use winit::platform::web::WindowBuilderExtWebSys; use winit::platform::web::WindowBuilderExtWebSys;
@@ -18,11 +18,11 @@ pub fn main() -> Result<(), impl std::error::Error> {
}; };
let window = builder.build(&event_loop).unwrap(); let window = builder.build(&event_loop).unwrap();
#[cfg(wasm_platform)] #[cfg(web_platform)]
let log_list = wasm::insert_canvas_and_create_log_list(&window); let log_list = wasm::insert_canvas_and_create_log_list(&window);
event_loop.run(move |event, elwt| { event_loop.run(move |event, elwt| {
#[cfg(wasm_platform)] #[cfg(web_platform)]
wasm::log_event(&log_list, &event); wasm::log_event(&log_list, &event);
match event { match event {
@@ -57,7 +57,7 @@ pub fn main() -> Result<(), impl std::error::Error> {
}) })
} }
#[cfg(wasm_platform)] #[cfg(web_platform)]
mod wasm { mod wasm {
use std::num::NonZeroU32; 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") println!("This example must be run with cargo run-wasm --example web_aspect_ratio")
} }
#[cfg(wasm_platform)] #[cfg(web_platform)]
mod wasm { mod wasm {
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
@@ -14,7 +14,7 @@ mod wasm {
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
platform::web::WindowBuilderExtWebSys, platform::web::WindowBuilderExtWebSys,
window::{Window, WindowBuilder}, window::Window,
}; };
const EXPLANATION: &str = " 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"); console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new() let window = Window::builder()
.with_title("A fantastic window!") .with_title("A fantastic window!")
// When running in a non-wasm environment this would set the window size to 100x100. // 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. // 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::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
window::WindowBuilder, window::Window,
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -14,7 +14,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new() let window = Window::builder()
.with_title("A fantastic window!") .with_title("A fantastic window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(&event_loop) .build(&event_loop)

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
window::{Icon, WindowBuilder}, window::{Icon, Window},
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -25,7 +25,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new() let window = Window::builder()
.with_title("An iconic window!") .with_title("An iconic window!")
// At present, this only does anything on Windows and X11, so if you want to save load // 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. // 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::{Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
platform::run_on_demand::EventLoopExtRunOnDemand, platform::run_on_demand::EventLoopExtRunOnDemand,
window::{Window, WindowBuilder, WindowId}, window::{Window, WindowId},
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -40,6 +40,7 @@ fn main() -> Result<(), impl std::error::Error> {
window_id, window_id,
} if window.id() == window_id => { } if window.id() == window_id => {
println!("--------------------------------------------------------- Window {idx} CloseRequested"); println!("--------------------------------------------------------- Window {idx} CloseRequested");
fill::cleanup_window(window);
app.window = None; app.window = None;
} }
Event::AboutToWait => window.request_redraw(), Event::AboutToWait => window.request_redraw(),
@@ -64,7 +65,7 @@ fn main() -> Result<(), impl std::error::Error> {
_ => (), _ => (),
} }
} else if let Event::Resumed = event { } else if let Event::Resumed = event {
let window = WindowBuilder::new() let window = Window::builder()
.with_title("Fantastic window number one!") .with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(elwt) .build(elwt)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,8 @@ use std::hash::Hasher;
use std::sync::Arc; use std::sync::Arc;
use std::{error::Error, hash::Hash}; use std::{error::Error, hash::Hash};
use cursor_icon::CursorIcon;
use crate::event_loop::EventLoopWindowTarget; use crate::event_loop::EventLoopWindowTarget;
use crate::platform_impl::{self, PlatformCustomCursor, PlatformCustomCursorBuilder}; use crate::platform_impl::{self, PlatformCustomCursor, PlatformCustomCursorBuilder};
@@ -11,8 +13,35 @@ pub const MAX_CURSOR_SIZE: u16 = 2048;
const PIXEL_SIZE: usize = 4; const PIXEL_SIZE: usize = 4;
/// See [`Window::set_cursor()`](crate::window::Window::set_cursor) for more details.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Cursor {
Icon(CursorIcon),
Custom(CustomCursor),
}
impl Default for Cursor {
fn default() -> Self {
Self::Icon(CursorIcon::default())
}
}
impl From<CursorIcon> for Cursor {
fn from(icon: CursorIcon) -> Self {
Self::Icon(icon)
}
}
impl From<CustomCursor> for Cursor {
fn from(custom: CustomCursor) -> Self {
Self::Custom(custom)
}
}
/// Use a custom image as a cursor (mouse pointer). /// Use a custom image as a cursor (mouse pointer).
/// ///
/// Is guaranteed to be cheap to clone.
///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// **Web**: Some browsers have limits on cursor sizes usually at 128x128. /// **Web**: Some browsers have limits on cursor sizes usually at 128x128.
@@ -44,7 +73,7 @@ const PIXEL_SIZE: usize = 4;
/// let custom_cursor = builder.build(&event_loop); /// let custom_cursor = builder.build(&event_loop);
/// ///
/// let window = Window::new(&event_loop).unwrap(); /// let window = Window::new(&event_loop).unwrap();
/// window.set_custom_cursor(&custom_cursor); /// window.set_cursor(custom_cursor.clone());
/// ``` /// ```
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct CustomCursor { pub struct CustomCursor {
@@ -55,10 +84,7 @@ pub struct CustomCursor {
impl CustomCursor { impl CustomCursor {
/// Creates a new cursor from an rgba buffer. /// Creates a new cursor from an rgba buffer.
/// ///
/// ## Platform-specific /// The alpha channel is assumed to be **not** premultiplied.
///
/// - **Web:** Setting cursor could be delayed due to the creation of `Blob` objects,
/// which are async by nature.
pub fn from_rgba( pub fn from_rgba(
rgba: impl Into<Vec<u8>>, rgba: impl Into<Vec<u8>>,
width: u16, width: u16,
@@ -87,7 +113,7 @@ pub struct CustomCursorBuilder {
} }
impl CustomCursorBuilder { impl CustomCursorBuilder {
pub fn build<T>(self, window_target: &EventLoopWindowTarget<T>) -> CustomCursor { pub fn build(self, window_target: &EventLoopWindowTarget) -> CustomCursor {
CustomCursor { CustomCursor {
inner: PlatformCustomCursor::build(self.inner, &window_target.p), inner: PlatformCustomCursor::build(self.inner, &window_target.p),
} }
@@ -189,9 +215,9 @@ impl Eq for OnlyCursorImage {}
#[allow(dead_code)] #[allow(dead_code)]
impl OnlyCursorImage { impl OnlyCursorImage {
fn build<T>( pub(crate) fn build(
builder: OnlyCursorImageBuilder, builder: OnlyCursorImageBuilder,
_: &platform_impl::EventLoopWindowTarget<T>, _: &platform_impl::EventLoopWindowTarget,
) -> Self { ) -> Self {
Self(Arc::new(builder.0)) Self(Arc::new(builder.0))
} }
@@ -272,7 +298,7 @@ impl NoCustomCursor {
Ok(Self) Ok(Self)
} }
fn build<T>(self, _: &platform_impl::EventLoopWindowTarget<T>) -> NoCustomCursor { fn build(self, _: &platform_impl::EventLoopWindowTarget) -> NoCustomCursor {
self self
} }
} }

View File

@@ -104,6 +104,9 @@
//! [android_1]: https://developer.android.com/training/multiscreen/screendensities //! [android_1]: https://developer.android.com/training/multiscreen/screendensities
//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio //! [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> { pub trait Pixel: Copy + Into<f64> {
fn from_f64(f: f64) -> Self; fn from_f64(f: f64) -> Self;
fn cast<P: Pixel>(self) -> P { fn cast<P: Pixel>(self) -> P {

View File

@@ -35,8 +35,6 @@ pub enum EventLoopError {
NotSupported(NotSupportedError), NotSupported(NotSupportedError),
/// The OS cannot perform the operation. /// The OS cannot perform the operation.
Os(OsError), Os(OsError),
/// The event loop can't be re-run while it's already running
AlreadyRunning,
/// The event loop can't be re-created. /// The event loop can't be re-created.
RecreationAttempt, RecreationAttempt,
/// Application has exit with an error status. /// Application has exit with an error status.
@@ -105,7 +103,6 @@ impl fmt::Display for NotSupportedError {
impl fmt::Display for EventLoopError { impl fmt::Display for EventLoopError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self { match self {
EventLoopError::AlreadyRunning => write!(f, "EventLoop is already running"),
EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"), EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
EventLoopError::NotSupported(e) => e.fmt(f), EventLoopError::NotSupported(e) => e.fmt(f),
EventLoopError::Os(e) => e.fmt(f), EventLoopError::Os(e) => e.fmt(f),

View File

@@ -34,11 +34,13 @@
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil //! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Mutex, Weak}; use std::sync::{Mutex, Weak};
#[cfg(not(wasm_platform))] #[cfg(not(web_platform))]
use std::time::Instant; use std::time::Instant;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use smol_str::SmolStr; use smol_str::SmolStr;
#[cfg(wasm_platform)] #[cfg(web_platform)]
use web_time::Instant; use web_time::Instant;
use crate::error::ExternalError; use crate::error::ExternalError;
@@ -445,21 +447,23 @@ pub enum WindowEvent {
button: MouseButton, button: MouseButton,
}, },
/// Touchpad magnification event with two-finger pinch gesture. /// Two-finger pinch gesture, often used for magnification.
///
/// Positive delta values indicate magnification (zooming in) and
/// negative delta values indicate shrinking (zooming out).
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - Only available on **macOS**. /// - Only available on **macOS** and **iOS**.
TouchpadMagnify { /// - On iOS, not recognized by default. It must be enabled when needed.
PinchGesture {
device_id: DeviceId, device_id: DeviceId,
/// Positive values indicate magnification (zooming in) and negative
/// values indicate shrinking (zooming out).
///
/// This value may be NaN.
delta: f64, delta: f64,
phase: TouchPhase, phase: TouchPhase,
}, },
/// Smart magnification event. /// Double tap gesture.
/// ///
/// On a Mac, smart magnification is triggered by a double tap with two fingers /// On a Mac, smart magnification is triggered by a double tap with two fingers
/// on the trackpad and is commonly used to zoom on a certain object /// on the trackpad and is commonly used to zoom on a certain object
@@ -475,18 +479,20 @@ pub enum WindowEvent {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - Only available on **macOS 10.8** and later. /// - Only available on **macOS 10.8** and later, and **iOS**.
SmartMagnify { device_id: DeviceId }, /// - On iOS, not recognized by default. It must be enabled when needed.
DoubleTapGesture { device_id: DeviceId },
/// Touchpad rotation event with two-finger rotation gesture. /// Two-finger rotation gesture.
/// ///
/// Positive delta values indicate rotation counterclockwise and /// Positive delta values indicate rotation counterclockwise and
/// negative delta values indicate rotation clockwise. /// negative delta values indicate rotation clockwise.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - Only available on **macOS**. /// - Only available on **macOS** and **iOS**.
TouchpadRotate { /// - On iOS, not recognized by default. It must be enabled when needed.
RotationGesture {
device_id: DeviceId, device_id: DeviceId,
delta: f32, delta: f32,
phase: TouchPhase, phase: TouchPhase,
@@ -530,9 +536,8 @@ pub enum WindowEvent {
/// * Changing the display's scale factor (e.g. in Control Panel on Windows). /// * Changing the display's scale factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different scale factor. /// * Moving the window to a display with a different scale factor.
/// ///
/// After this event callback has been processed, the window will be resized to whatever value /// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the window is
/// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested /// resized to the value suggested by the OS, but it can be changed to any value.
/// by the OS, but it can be changed to any value.
/// ///
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module. /// For more information about DPI in general, see the [`dpi`](crate::dpi) module.
ScaleFactorChanged { ScaleFactorChanged {
@@ -574,7 +579,7 @@ pub enum WindowEvent {
/// ### Others /// ### Others
/// ///
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
/// - **Android / Windows / Orbital:** Unsupported. /// - **Android / Wayland / Windows / Orbital:** Unsupported.
/// ///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
@@ -1199,13 +1204,13 @@ mod tests {
state: event::ElementState::Pressed, state: event::ElementState::Pressed,
button: event::MouseButton::Other(0), button: event::MouseButton::Other(0),
}); });
with_window_event(TouchpadMagnify { with_window_event(PinchGesture {
device_id: did, device_id: did,
delta: 0.0, delta: 0.0,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
}); });
with_window_event(SmartMagnify { device_id: did }); with_window_event(DoubleTapGesture { device_id: did });
with_window_event(TouchpadRotate { with_window_event(RotationGesture {
device_id: did, device_id: did,
delta: 0.0, delta: 0.0,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,

View File

@@ -11,12 +11,12 @@ use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::{error, fmt}; use std::{error, fmt};
#[cfg(not(wasm_platform))] #[cfg(not(web_platform))]
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
#[cfg(wasm_platform)] #[cfg(web_platform)]
use web_time::{Duration, Instant}; use web_time::{Duration, Instant};
use crate::error::EventLoopError; use crate::error::EventLoopError;
@@ -48,8 +48,8 @@ pub struct EventLoop<T: 'static> {
/// your callback. [`EventLoop`] will coerce into this type (`impl<T> Deref for /// 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<T>`), so functions that take this as a parameter can also take
/// `&EventLoop`. /// `&EventLoop`.
pub struct EventLoopWindowTarget<T: 'static> { pub struct EventLoopWindowTarget {
pub(crate) p: platform_impl::EventLoopWindowTarget<T>, pub(crate) p: platform_impl::EventLoopWindowTarget,
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
} }
@@ -57,33 +57,26 @@ pub struct EventLoopWindowTarget<T: 'static> {
/// ///
/// This is used to make specifying options that affect the whole application /// This is used to make specifying options that affect the whole application
/// easier. But note that constructing multiple event loops is not supported. /// easier. But note that constructing multiple event loops is not supported.
///
/// This can be created using [`EventLoop::new`] or [`EventLoop::with_user_event`].
#[derive(Default)] #[derive(Default)]
pub struct EventLoopBuilder<T: 'static> { pub struct EventLoopBuilder<T: 'static> {
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes, pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
_p: PhantomData<T>, _p: PhantomData<T>,
} }
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl EventLoopBuilder<()> { impl EventLoopBuilder<()> {
/// Start building a new event loop. /// Start building a new event loop.
#[inline] #[inline]
#[deprecated = "use `EventLoop::builder` instead"]
pub fn new() -> Self { pub fn new() -> Self {
Self::with_user_event() EventLoop::builder()
} }
} }
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl<T> EventLoopBuilder<T> { 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. /// Builds a new event loop.
/// ///
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread, /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
@@ -130,7 +123,7 @@ impl<T> EventLoopBuilder<T> {
}) })
} }
#[cfg(wasm_platform)] #[cfg(web_platform)]
pub(crate) fn allow_event_loop_recreation() { pub(crate) fn allow_event_loop_recreation() {
EVENT_LOOP_CREATED.store(false, Ordering::Relaxed); EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
} }
@@ -142,7 +135,7 @@ impl<T> fmt::Debug for EventLoop<T> {
} }
} }
impl<T> fmt::Debug for EventLoopWindowTarget<T> { impl fmt::Debug for EventLoopWindowTarget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EventLoopWindowTarget { .. }") f.pad("EventLoopWindowTarget { .. }")
} }
@@ -193,27 +186,38 @@ impl ControlFlow {
} }
impl EventLoop<()> { impl EventLoop<()> {
/// Alias for [`EventLoopBuilder::new().build()`]. /// Create the event loop.
/// ///
/// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build /// This is an alias of `EventLoop::builder().build()`.
#[inline] #[inline]
pub fn new() -> Result<EventLoop<()>, EventLoopError> { pub fn new() -> Result<EventLoop<()>, EventLoopError> {
EventLoopBuilder::new().build() 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()
} }
} }
impl<T> EventLoop<T> { impl<T> EventLoop<T> {
#[deprecated = "Use `EventLoopBuilder::<T>::with_user_event().build()` instead."] /// Start building a new event loop, with the given type as the user event
pub fn with_user_event() -> Result<EventLoop<T>, EventLoopError> { /// type.
EventLoopBuilder::<T>::with_user_event().build() pub fn with_user_event() -> EventLoopBuilder<T> {
EventLoopBuilder {
platform_specific: Default::default(),
_p: PhantomData,
}
} }
/// Runs the event loop in the calling thread and calls the given `event_handler` closure /// Runs the event loop in the calling thread and calls the given `event_handler` closure
/// to dispatch any pending events. /// 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. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
/// ## Platform-specific /// ## Platform-specific
@@ -226,10 +230,10 @@ impl<T> EventLoop<T> {
/// ///
/// Web applications are recommended to use /// Web applications are recommended to use
#[cfg_attr( #[cfg_attr(
wasm_platform, web_platform,
doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]" doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]"
)] )]
#[cfg_attr(not(wasm_platform), doc = "`EventLoopExtWebSys::spawn()`")] #[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")]
/// [^1] instead of [`run()`] to avoid the need /// [^1] instead of [`run()`] to avoid the need
/// for the Javascript exception trick, and to make it clearer that the event loop runs /// for the Javascript exception trick, and to make it clearer that the event loop runs
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the /// asynchronously (via the browser's own, internal, event loop) and doesn't block the
@@ -239,12 +243,12 @@ impl<T> EventLoop<T> {
/// ///
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow() /// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow()
/// [`run()`]: Self::run() /// [`run()`]: Self::run()
/// [^1]: `EventLoopExtWebSys::spawn()` is only available on WASM. /// [^1]: `EventLoopExtWebSys::spawn()` is only available on Web.
#[inline] #[inline]
#[cfg(not(all(wasm_platform, target_feature = "exception-handling")))] #[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError> pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &EventLoopWindowTarget<T>), F: FnMut(Event<T>, &EventLoopWindowTarget),
{ {
self.event_loop.run(event_handler) self.event_loop.run(event_handler)
} }
@@ -301,13 +305,13 @@ impl<T> AsRawFd for EventLoop<T> {
} }
impl<T> Deref for EventLoop<T> { impl<T> Deref for EventLoop<T> {
type Target = EventLoopWindowTarget<T>; type Target = EventLoopWindowTarget;
fn deref(&self) -> &EventLoopWindowTarget<T> { fn deref(&self) -> &EventLoopWindowTarget {
self.event_loop.window_target() self.event_loop.window_target()
} }
} }
impl<T> EventLoopWindowTarget<T> { impl EventLoopWindowTarget {
/// Returns the list of all the monitors available on the system. /// Returns the list of all the monitors available on the system.
#[inline] #[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
@@ -370,10 +374,19 @@ impl<T> EventLoopWindowTarget<T> {
pub fn exiting(&self) -> bool { pub fn exiting(&self) -> bool {
self.p.exiting() 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")] #[cfg(feature = "rwh_06")]
impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> { impl rwh_06::HasDisplayHandle for EventLoopWindowTarget {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.p.raw_display_handle_rwh_06()?; let raw = self.p.raw_display_handle_rwh_06()?;
// SAFETY: The display will never be deallocated while the event loop is alive. // SAFETY: The display will never be deallocated while the event loop is alive.
@@ -382,13 +395,59 @@ impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> {
} }
#[cfg(feature = "rwh_05")] #[cfg(feature = "rwh_05")]
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoopWindowTarget<T> { unsafe impl rwh_05::HasRawDisplayHandle for EventLoopWindowTarget {
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop. /// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle { fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.p.raw_display_handle_rwh_05() self.p.raw_display_handle_rwh_05()
} }
} }
/// 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`]. /// Used to send custom events to [`EventLoop`].
pub struct EventLoopProxy<T: 'static> { pub struct EventLoopProxy<T: 'static> {
event_loop_proxy: platform_impl::EventLoopProxy<T>, event_loop_proxy: platform_impl::EventLoopProxy<T>,
@@ -459,16 +518,16 @@ pub enum DeviceEvents {
/// executed and removed from the list. /// executed and removed from the list.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AsyncRequestSerial { pub struct AsyncRequestSerial {
serial: u64, serial: usize,
} }
impl AsyncRequestSerial { impl AsyncRequestSerial {
// TODO(kchibisov) remove `cfg` when the clipboard will be added. // TODO(kchibisov): Remove `cfg` when the clipboard will be added.
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn get() -> Self { pub(crate) fn get() -> Self {
static CURRENT_SERIAL: AtomicU64 = AtomicU64::new(0); static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0);
// NOTE: we rely on wrap around here, while the user may just request // 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. // in the loop usize::MAX times that's issue is considered on them.
let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed); let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
Self { serial } Self { serial }
} }

View File

@@ -69,6 +69,9 @@
// //
// --------- END OF W3C SHORT NOTICE --------------------------------------------------------------- // --------- END OF W3C SHORT NOTICE ---------------------------------------------------------------
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub use smol_str::SmolStr; pub use smol_str::SmolStr;
/// Contains the platform-native physical key identifier /// Contains the platform-native physical key identifier

View File

@@ -13,7 +13,7 @@
//! Once this is done, there are two ways to create a [`Window`]: //! Once this is done, there are two ways to create a [`Window`]:
//! //!
//! - Calling [`Window::new(&event_loop)`][window_new]. //! - Calling [`Window::new(&event_loop)`][window_new].
//! - Calling [`let builder = WindowBuilder::new()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build]. //! - Calling [`let builder = Window::builder()`][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 //! 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 //! method allows you to customize the way your [`Window`] will look and behave by modifying the
@@ -63,11 +63,11 @@
//! use winit::{ //! use winit::{
//! event::{Event, WindowEvent}, //! event::{Event, WindowEvent},
//! event_loop::{ControlFlow, EventLoop}, //! event_loop::{ControlFlow, EventLoop},
//! window::WindowBuilder, //! window::Window,
//! }; //! };
//! //!
//! let event_loop = EventLoop::new().unwrap(); //! let event_loop = EventLoop::new().unwrap();
//! let window = WindowBuilder::new().build(&event_loop).unwrap(); //! let window = Window::builder().build(&event_loop).unwrap();
//! //!
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't //! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications. //! // dispatched any events. This is ideal for games and similar applications.
@@ -137,7 +137,7 @@
//! [`WindowId`]: window::WindowId //! [`WindowId`]: window::WindowId
//! [`WindowBuilder`]: window::WindowBuilder //! [`WindowBuilder`]: window::WindowBuilder
//! [window_new]: window::Window::new //! [window_new]: window::Window::new
//! [window_builder_new]: window::WindowBuilder::new //! [window_builder_new]: window::Window::builder
//! [window_builder_build]: window::WindowBuilder::build //! [window_builder_build]: window::WindowBuilder::build
//! [`Window::id()`]: window::Window::id //! [`Window::id()`]: window::Window::id
//! [`WindowEvent`]: event::WindowEvent //! [`WindowEvent`]: event::WindowEvent
@@ -154,21 +154,16 @@
#![deny(unsafe_op_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)]
#![cfg_attr(feature = "cargo-clippy", deny(warnings))] #![cfg_attr(feature = "cargo-clippy", deny(warnings))]
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc // Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(
docsrs,
feature(doc_auto_cfg, doc_cfg_hide),
doc(cfg_hide(doc, docsrs))
)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
pub use rwh_06 as raw_window_handle; pub use rwh_06 as raw_window_handle;
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[macro_use]
extern crate bitflags;
pub mod dpi; pub mod dpi;
#[macro_use] #[macro_use]
pub mod error; pub mod error;
@@ -182,18 +177,3 @@ mod platform_impl;
pub mod window; pub mod window;
pub mod platform; 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,28 +10,32 @@ use crate::{
platform_impl, platform_impl,
}; };
/// Deprecated! Use `VideoModeHandle` instead.
#[deprecated = "Renamed to `VideoModeHandle`"]
pub type VideoMode = VideoModeHandle;
/// Describes a fullscreen video mode of a monitor. /// Describes a fullscreen video mode of a monitor.
/// ///
/// Can be acquired with [`MonitorHandle::video_modes`]. /// Can be acquired with [`MonitorHandle::video_modes`].
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub struct VideoMode { pub struct VideoModeHandle {
pub(crate) video_mode: platform_impl::VideoMode, pub(crate) video_mode: platform_impl::VideoModeHandle,
} }
impl std::fmt::Debug for VideoMode { impl std::fmt::Debug for VideoModeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.video_mode.fmt(f) self.video_mode.fmt(f)
} }
} }
impl PartialOrd for VideoMode { impl PartialOrd for VideoModeHandle {
fn partial_cmp(&self, other: &VideoMode) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &VideoModeHandle) -> Option<std::cmp::Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
impl Ord for VideoMode { impl Ord for VideoModeHandle {
fn cmp(&self, other: &VideoMode) -> std::cmp::Ordering { fn cmp(&self, other: &VideoModeHandle) -> std::cmp::Ordering {
self.monitor().cmp(&other.monitor()).then( self.monitor().cmp(&other.monitor()).then(
self.size() self.size()
.cmp(&other.size()) .cmp(&other.size())
@@ -45,7 +49,7 @@ impl Ord for VideoMode {
} }
} }
impl VideoMode { impl VideoModeHandle {
/// Returns the resolution of this video mode. /// Returns the resolution of this video mode.
#[inline] #[inline]
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
@@ -81,7 +85,7 @@ impl VideoMode {
} }
} }
impl std::fmt::Display for VideoMode { impl std::fmt::Display for VideoModeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
@@ -131,8 +135,8 @@ impl MonitorHandle {
/// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor /// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor
/// the window is on is removed. /// the window is on is removed.
/// ///
/// When using exclusive fullscreen, the refresh rate of the [`VideoMode`] that was used to /// When using exclusive fullscreen, the refresh rate of the [`VideoModeHandle`] that was
/// enter fullscreen should be used instead. /// used to enter fullscreen should be used instead.
#[inline] #[inline]
pub fn refresh_rate_millihertz(&self) -> Option<u32> { pub fn refresh_rate_millihertz(&self) -> Option<u32> {
self.inner.refresh_rate_millihertz() self.inner.refresh_rate_millihertz()
@@ -161,9 +165,9 @@ impl MonitorHandle {
/// ///
/// - **Web:** Always returns an empty iterator /// - **Web:** Always returns an empty iterator
#[inline] #[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> { pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
self.inner self.inner
.video_modes() .video_modes()
.map(|video_mode| VideoMode { video_mode }) .map(|video_mode| VideoModeHandle { video_mode })
} }
} }

View File

@@ -3,7 +3,7 @@ use crate::{
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use android_activity::{AndroidApp, ConfigurationRef, Rect}; use self::activity::{AndroidApp, ConfigurationRef, Rect};
/// Additional methods on [`EventLoop`] that are specific to Android. /// Additional methods on [`EventLoop`] that are specific to Android.
pub trait EventLoopExtAndroid {} pub trait EventLoopExtAndroid {}
@@ -30,7 +30,7 @@ impl WindowExtAndroid for Window {
} }
} }
impl<T> EventLoopWindowTargetExtAndroid for EventLoopWindowTarget<T> {} impl EventLoopWindowTargetExtAndroid for EventLoopWindowTarget {}
/// Additional methods on [`WindowBuilder`] that are specific to Android. /// Additional methods on [`WindowBuilder`] that are specific to Android.
pub trait WindowBuilderExtAndroid {} pub trait WindowBuilderExtAndroid {}
@@ -89,5 +89,16 @@ pub mod activity {
// feature enabled, so we avoid inlining it so that they're forced to view // feature enabled, so we avoid inlining it so that they're forced to view
// it on the crate's own docs.rs page. // it on the crate's own docs.rs page.
#[doc(no_inline)] #[doc(no_inline)]
#[cfg(android_platform)]
pub use android_activity::*; 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,11 +1,8 @@
use std::os::raw::c_void; use std::os::raw::c_void;
use icrate::Foundation::MainThreadMarker;
use objc2::rc::Id;
use crate::{ use crate::{
event_loop::EventLoop, event_loop::EventLoop,
monitor::{MonitorHandle, VideoMode}, monitor::{MonitorHandle, VideoModeHandle},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
@@ -88,6 +85,21 @@ pub trait WindowExtIOS {
/// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc) /// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc)
/// is also called for you. /// is also called for you.
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle); 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 { impl WindowExtIOS for Window {
@@ -127,6 +139,24 @@ impl WindowExtIOS for Window {
self.window self.window
.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style)) .maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
} }
#[inline]
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. /// Additional methods on [`WindowBuilder`] that are specific to iOS.
@@ -187,38 +217,39 @@ pub trait WindowBuilderExtIOS {
impl WindowBuilderExtIOS for WindowBuilder { impl WindowBuilderExtIOS for WindowBuilder {
#[inline] #[inline]
fn with_scale_factor(mut self, scale_factor: f64) -> Self { fn with_scale_factor(mut self, scale_factor: f64) -> Self {
self.platform_specific.scale_factor = Some(scale_factor); self.window.platform_specific.scale_factor = Some(scale_factor);
self self
} }
#[inline] #[inline]
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self { fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self {
self.platform_specific.valid_orientations = valid_orientations; self.window.platform_specific.valid_orientations = valid_orientations;
self self
} }
#[inline] #[inline]
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self { fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self {
self.platform_specific.prefers_home_indicator_hidden = hidden; self.window.platform_specific.prefers_home_indicator_hidden = hidden;
self self
} }
#[inline] #[inline]
fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self { fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
self.platform_specific self.window
.platform_specific
.preferred_screen_edges_deferring_system_gestures = edges; .preferred_screen_edges_deferring_system_gestures = edges;
self self
} }
#[inline] #[inline]
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self { fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self {
self.platform_specific.prefers_status_bar_hidden = hidden; self.window.platform_specific.prefers_status_bar_hidden = hidden;
self self
} }
#[inline] #[inline]
fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self { fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self {
self.platform_specific.preferred_status_bar_style = status_bar_style; self.window.platform_specific.preferred_status_bar_style = status_bar_style;
self self
} }
} }
@@ -230,23 +261,23 @@ pub trait MonitorHandleExtIOS {
/// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc /// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc
fn ui_screen(&self) -> *mut c_void; fn ui_screen(&self) -> *mut c_void;
/// Returns the preferred [`VideoMode`] for this monitor. /// Returns the preferred [`VideoModeHandle`] for this monitor.
/// ///
/// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc). /// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc).
fn preferred_video_mode(&self) -> VideoMode; fn preferred_video_mode(&self) -> VideoModeHandle;
} }
impl MonitorHandleExtIOS for MonitorHandle { impl MonitorHandleExtIOS for MonitorHandle {
#[inline] #[inline]
fn ui_screen(&self) -> *mut c_void { fn ui_screen(&self) -> *mut c_void {
// SAFETY: The marker is only used to get the pointer of the screen // SAFETY: The marker is only used to get the pointer of the screen
let mtm = unsafe { MainThreadMarker::new_unchecked() }; let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() };
Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void objc2::rc::Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
} }
#[inline] #[inline]
fn preferred_video_mode(&self) -> VideoMode { fn preferred_video_mode(&self) -> VideoModeHandle {
VideoMode { VideoModeHandle {
video_mode: self.inner.preferred_video_mode(), video_mode: self.inner.preferred_video_mode(),
} }
} }
@@ -283,7 +314,7 @@ pub enum Idiom {
CarPlay, CarPlay,
} }
bitflags! { bitflags::bitflags! {
/// The [edges] of a screen. /// The [edges] of a screen.
/// ///
/// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc /// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc

View File

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

View File

@@ -1,38 +1,24 @@
//! Contains traits with platform-specific methods in them. //! Contains traits with platform-specific methods in them.
//! //!
//! Contains the follow OS-specific modules: //! Only the modules corresponding to the platform you're compiling to will be available.
//!
//! - `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)] #[cfg(any(android_platform, docsrs))]
pub mod android; pub mod android;
#[cfg(ios_platform)] #[cfg(any(ios_platform, docsrs))]
pub mod ios; pub mod ios;
#[cfg(macos_platform)] #[cfg(any(macos_platform, docsrs))]
pub mod macos; pub mod macos;
#[cfg(orbital_platform)] #[cfg(any(orbital_platform, docsrs))]
pub mod orbital; pub mod orbital;
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform, docsrs))]
pub mod startup_notify; pub mod startup_notify;
#[cfg(wayland_platform)] #[cfg(any(wayland_platform, docsrs))]
pub mod wayland; pub mod wayland;
#[cfg(wasm_platform)] #[cfg(any(web_platform, docsrs))]
pub mod web; pub mod web;
#[cfg(windows_platform)] #[cfg(any(windows_platform, docsrs))]
pub mod windows; pub mod windows;
#[cfg(x11_platform)] #[cfg(any(x11_platform, docsrs))]
pub mod x11; pub mod x11;
#[cfg(any( #[cfg(any(
@@ -40,7 +26,8 @@ pub mod x11;
macos_platform, macos_platform,
android_platform, android_platform,
x11_platform, x11_platform,
wayland_platform wayland_platform,
docsrs,
))] ))]
pub mod run_on_demand; pub mod run_on_demand;
@@ -49,9 +36,26 @@ pub mod run_on_demand;
macos_platform, macos_platform,
android_platform, android_platform,
x11_platform, x11_platform,
wayland_platform wayland_platform,
docsrs,
))] ))]
pub mod pump_events; pub mod pump_events;
#[cfg(any(
windows_platform,
macos_platform,
x11_platform,
wayland_platform,
orbital_platform,
docsrs
))]
pub mod modifier_supplement; pub mod modifier_supplement;
#[cfg(any(
windows_platform,
macos_platform,
x11_platform,
wayland_platform,
docsrs
))]
pub mod scancode; pub mod scancode;

View File

@@ -1,5 +1,4 @@
#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))] use crate::event::KeyEvent;
use crate::keyboard::Key; use crate::keyboard::Key;
/// Additional methods for the `KeyEvent` which cannot be implemented on all /// Additional methods for the `KeyEvent` which cannot be implemented on all
@@ -22,3 +21,18 @@ pub trait KeyEventExtModifierSupplement {
/// cannot be `Dead`. /// cannot be `Dead`.
fn key_without_modifiers(&self) -> Key; fn key_without_modifiers(&self) -> Key;
} }
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()
}
}

View File

@@ -51,12 +51,12 @@ pub trait EventLoopExtPumpEvents {
/// # event::{Event, WindowEvent}, /// # event::{Event, WindowEvent},
/// # event_loop::EventLoop, /// # event_loop::EventLoop,
/// # platform::pump_events::{EventLoopExtPumpEvents, PumpStatus}, /// # platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
/// # window::WindowBuilder, /// # window::Window,
/// # }; /// # };
/// let mut event_loop = EventLoop::new().unwrap(); /// let mut event_loop = EventLoop::new().unwrap();
/// # /// #
/// # SimpleLogger::new().init().unwrap(); /// # SimpleLogger::new().init().unwrap();
/// let window = WindowBuilder::new() /// let window = Window::builder()
/// .with_title("A fantastic window!") /// .with_title("A fantastic window!")
/// .build(&event_loop) /// .build(&event_loop)
/// .unwrap(); /// .unwrap();
@@ -174,7 +174,7 @@ pub trait EventLoopExtPumpEvents {
/// callback. /// callback.
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>); F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget);
} }
impl<T> EventLoopExtPumpEvents for EventLoop<T> { 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 fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>), F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget),
{ {
self.event_loop.pump_events(timeout, event_handler) 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 /// 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 /// polled to ask for new events. Events are delivered via callbacks based
/// on an event loop that is internal to the browser itself. /// on an event loop that is internal to the browser itself.
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS. /// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS.
/// ///
#[cfg_attr( #[cfg_attr(
not(wasm_platform), not(web_platform),
doc = "[^1]: `spawn()` is only available on `wasm` platforms." doc = "[^1]: `spawn()` is only available on `wasm` platforms."
)] )]
/// ///
@@ -66,7 +66,7 @@ pub trait EventLoopExtRunOnDemand {
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow() /// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow()
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError> fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>); F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget);
} }
impl<T> EventLoopExtRunOnDemand for EventLoop<T> { impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
@@ -74,8 +74,29 @@ impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError> fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>), F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget),
{ {
self.event_loop.window_target().clear_exit();
self.event_loop.run_on_demand(event_handler) self.event_loop.run_on_demand(event_handler)
} }
} }
impl EventLoopWindowTarget {
/// 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,5 +1,3 @@
#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
use crate::keyboard::{KeyCode, PhysicalKey}; use crate::keyboard::{KeyCode, PhysicalKey};
// TODO: Describe what this value contains for each platform // TODO: Describe what this value contains for each platform
@@ -29,17 +27,24 @@ pub trait PhysicalKeyExtScancode {
fn from_scancode(scancode: u32) -> PhysicalKey; fn from_scancode(scancode: u32) -> PhysicalKey;
} }
impl PhysicalKeyExtScancode for KeyCode impl PhysicalKeyExtScancode for PhysicalKey {
where fn to_scancode(self) -> Option<u32> {
PhysicalKey: PhysicalKeyExtScancode, crate::platform_impl::physicalkey_to_scancode(self)
{
#[inline]
fn from_scancode(scancode: u32) -> PhysicalKey {
<PhysicalKey as PhysicalKeyExtScancode>::from_scancode(scancode)
} }
fn from_scancode(scancode: u32) -> PhysicalKey {
crate::platform_impl::scancode_to_physicalkey(scancode)
}
}
impl PhysicalKeyExtScancode for KeyCode {
#[inline] #[inline]
fn to_scancode(self) -> Option<u32> { fn to_scancode(self) -> Option<u32> {
<PhysicalKey as PhysicalKeyExtScancode>::to_scancode(PhysicalKey::Code(self)) <PhysicalKey as PhysicalKeyExtScancode>::to_scancode(PhysicalKey::Code(self))
} }
#[inline]
fn from_scancode(scancode: u32) -> PhysicalKey {
<PhysicalKey as PhysicalKeyExtScancode>::from_scancode(scancode)
}
} }

View File

@@ -55,7 +55,7 @@ pub trait WindowBuilderExtStartupNotify {
fn with_activation_token(self, token: ActivationToken) -> Self; fn with_activation_token(self, token: ActivationToken) -> Self;
} }
impl<T> EventLoopExtStartupNotify for EventLoopWindowTarget<T> { impl EventLoopExtStartupNotify for EventLoopWindowTarget {
fn read_token_from_env(&self) -> Option<ActivationToken> { fn read_token_from_env(&self) -> Option<ActivationToken> {
match self.p { match self.p {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
@@ -76,7 +76,7 @@ impl WindowExtStartupNotify for Window {
impl WindowBuilderExtStartupNotify for WindowBuilder { impl WindowBuilderExtStartupNotify for WindowBuilder {
fn with_activation_token(mut self, token: ActivationToken) -> Self { fn with_activation_token(mut self, token: ActivationToken) -> Self {
self.platform_specific.activation_token = Some(token); self.window.platform_specific.activation_token = Some(token);
self self
} }
} }

View File

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

View File

@@ -27,20 +27,47 @@
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border //! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding //! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
use crate::cursor::CustomCursorBuilder; use std::error::Error;
use crate::event::Event; use std::fmt::{self, Display, Formatter};
use crate::event_loop::EventLoop; use std::future::Future;
use crate::event_loop::EventLoopWindowTarget; use std::pin::Pin;
use crate::platform_impl::PlatformCustomCursorBuilder; use std::task::{Context, Poll};
use crate::window::CustomCursor; use std::time::Duration;
use crate::window::{Window, WindowBuilder};
use crate::SendSyncWrapper;
#[cfg(web_platform)]
use web_sys::HtmlCanvasElement; 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 { pub trait WindowExtWebSys {
/// Only returns the canvas if called from inside the window. /// Only returns the canvas if called from inside the window context (the
/// main thread).
fn canvas(&self) -> Option<HtmlCanvasElement>; 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 { impl WindowExtWebSys for Window {
@@ -48,6 +75,14 @@ impl WindowExtWebSys for Window {
fn canvas(&self) -> Option<HtmlCanvasElement> { fn canvas(&self) -> Option<HtmlCanvasElement> {
self.window.canvas() 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 { pub trait WindowBuilderExtWebSys {
@@ -57,16 +92,17 @@ pub trait WindowBuilderExtWebSys {
/// In any case, the canvas won't be automatically inserted into the web page. /// In any case, the canvas won't be automatically inserted into the web page.
/// ///
/// [`None`] by default. /// [`None`] by default.
#[cfg_attr(
not(web_platform),
doc = "",
doc = "[`HtmlCanvasElement`]: #only-available-on-wasm"
)]
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self; fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
/// Whether `event.preventDefault` should be automatically called to prevent event propagation /// Sets whether `event.preventDefault()` should be called on events on the
/// when appropriate. /// canvas that have side effects.
/// ///
/// For example, mouse wheel events are only handled by the canvas by default. This avoids /// See [`Window::set_prevent_default()`] for more details.
/// 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. /// Enabled by default.
fn with_prevent_default(self, prevent_default: bool) -> Self; fn with_prevent_default(self, prevent_default: bool) -> Self;
@@ -85,22 +121,22 @@ pub trait WindowBuilderExtWebSys {
impl WindowBuilderExtWebSys for WindowBuilder { impl WindowBuilderExtWebSys for WindowBuilder {
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self { fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
self.platform_specific.canvas = SendSyncWrapper(canvas); self.window.platform_specific.set_canvas(canvas);
self self
} }
fn with_prevent_default(mut self, prevent_default: bool) -> Self { fn with_prevent_default(mut self, prevent_default: bool) -> Self {
self.platform_specific.prevent_default = prevent_default; self.window.platform_specific.prevent_default = prevent_default;
self self
} }
fn with_focusable(mut self, focusable: bool) -> Self { fn with_focusable(mut self, focusable: bool) -> Self {
self.platform_specific.focusable = focusable; self.window.platform_specific.focusable = focusable;
self self
} }
fn with_append(mut self, append: bool) -> Self { fn with_append(mut self, append: bool) -> Self {
self.platform_specific.append = append; self.window.platform_specific.append = append;
self self
} }
} }
@@ -114,11 +150,11 @@ pub trait EventLoopExtWebSys {
/// ///
/// Unlike /// Unlike
#[cfg_attr( #[cfg_attr(
all(wasm_platform, target_feature = "exception-handling"), all(web_platform, target_feature = "exception-handling"),
doc = "`run()`" doc = "`run()`"
)] )]
#[cfg_attr( #[cfg_attr(
not(all(wasm_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run()`]" doc = "[`run()`]"
)] )]
/// [^1], this returns immediately, and doesn't throw an exception in order to /// [^1], this returns immediately, and doesn't throw an exception in order to
@@ -130,13 +166,13 @@ pub trait EventLoopExtWebSys {
/// event loop when switching between tabs on a single page application. /// event loop when switching between tabs on a single page application.
/// ///
#[cfg_attr( #[cfg_attr(
not(all(wasm_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run()`]: EventLoop::run()" doc = "[`run()`]: EventLoop::run()"
)] )]
/// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`. /// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`.
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>); F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget);
} }
impl<T> EventLoopExtWebSys for EventLoop<T> { impl<T> EventLoopExtWebSys for EventLoop<T> {
@@ -144,7 +180,7 @@ impl<T> EventLoopExtWebSys for EventLoop<T> {
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>), F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget),
{ {
self.event_loop.spawn(event_handler) self.event_loop.spawn(event_handler)
} }
@@ -166,7 +202,7 @@ pub trait EventLoopWindowTargetExtWebSys {
fn poll_strategy(&self) -> PollStrategy; fn poll_strategy(&self) -> PollStrategy;
} }
impl<T> EventLoopWindowTargetExtWebSys for EventLoopWindowTarget<T> { impl EventLoopWindowTargetExtWebSys for EventLoopWindowTarget {
#[inline] #[inline]
fn set_poll_strategy(&self, strategy: PollStrategy) { fn set_poll_strategy(&self, strategy: PollStrategy) {
self.p.set_poll_strategy(strategy); self.p.set_poll_strategy(strategy);
@@ -205,15 +241,29 @@ pub enum PollStrategy {
} }
pub trait CustomCursorExtWebSys { 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. /// 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), /// 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. /// but browser support for image formats is inconsistent. Using [PNG] is recommended.
/// ///
/// [PNG]: https://en.wikipedia.org/wiki/PNG /// [PNG]: https://en.wikipedia.org/wiki/PNG
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder; fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> 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 { impl CustomCursorExtWebSys for CustomCursor {
fn is_animation(&self) -> bool {
self.inner.animation
}
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder { fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder {
CustomCursorBuilder { CustomCursorBuilder {
inner: PlatformCustomCursorBuilder::Url { inner: PlatformCustomCursorBuilder::Url {
@@ -223,4 +273,92 @@ impl CustomCursorExtWebSys for CustomCursor {
}, },
} }
} }
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,12 +2,9 @@ use std::{ffi::c_void, path::Path};
use crate::{ use crate::{
dpi::PhysicalSize, dpi::PhysicalSize,
event::{DeviceId, KeyEvent}, event::DeviceId,
event_loop::EventLoopBuilder, event_loop::EventLoopBuilder,
keyboard::Key,
monitor::MonitorHandle, monitor::MonitorHandle,
platform::modifier_supplement::KeyEventExtModifierSupplement,
platform_impl::WinIcon,
window::{BadIcon, Icon, Window, WindowBuilder}, window::{BadIcon, Icon, Window, WindowBuilder},
}; };
@@ -18,6 +15,92 @@ pub type HMENU = isize;
/// Monitor Handle type used by Win32 API /// Monitor Handle type used by Win32 API
pub type HMONITOR = isize; 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. /// Additional methods on `EventLoop` that are specific to Windows.
pub trait EventLoopBuilderExtWindows { pub trait EventLoopBuilderExtWindows {
/// Whether to allow the event loop to be created off of the main thread. /// Whether to allow the event loop to be created off of the main thread.
@@ -136,6 +219,31 @@ pub trait WindowExtWindows {
/// ///
/// Enabling the shadow causes a thin 1px line to appear on the top of the window. /// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn set_undecorated_shadow(&self, shadow: bool); 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 { impl WindowExtWindows for Window {
@@ -158,6 +266,34 @@ impl WindowExtWindows for Window {
fn set_undecorated_shadow(&self, shadow: bool) { fn set_undecorated_shadow(&self, shadow: bool) {
self.window.set_undecorated_shadow(shadow) 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. /// Additional methods on `WindowBuilder` that are specific to Windows.
@@ -185,7 +321,14 @@ pub trait WindowBuilderExtWindows {
/// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look. /// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how 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. /// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect.
/// ///
/// [`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu #[cfg_attr(
platform_windows,
doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
)]
#[cfg_attr(
not(platform_windows),
doc = "[`CreateMenu`]: #only-available-on-windows"
)]
fn with_menu(self, menu: HMENU) -> Self; fn with_menu(self, menu: HMENU) -> Self;
/// This sets `ICON_BIG`. A good ceiling here is 256x256. /// This sets `ICON_BIG`. A good ceiling here is 256x256.
@@ -213,54 +356,118 @@ pub trait WindowBuilderExtWindows {
/// The shadow is hidden by default. /// The shadow is hidden by default.
/// Enabling the shadow causes a thin 1px line to appear on the top of the window. /// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn with_undecorated_shadow(self, shadow: bool) -> Self; 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 { impl WindowBuilderExtWindows for WindowBuilder {
#[inline] #[inline]
fn with_owner_window(mut self, parent: HWND) -> Self { fn with_owner_window(mut self, parent: HWND) -> Self {
self.platform_specific.owner = Some(parent); self.window.platform_specific.owner = Some(parent);
self self
} }
#[inline] #[inline]
fn with_menu(mut self, menu: HMENU) -> Self { fn with_menu(mut self, menu: HMENU) -> Self {
self.platform_specific.menu = Some(menu); self.window.platform_specific.menu = Some(menu);
self self
} }
#[inline] #[inline]
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self { fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self {
self.platform_specific.taskbar_icon = taskbar_icon; self.window.platform_specific.taskbar_icon = taskbar_icon;
self self
} }
#[inline] #[inline]
fn with_no_redirection_bitmap(mut self, flag: bool) -> Self { fn with_no_redirection_bitmap(mut self, flag: bool) -> Self {
self.platform_specific.no_redirection_bitmap = flag; self.window.platform_specific.no_redirection_bitmap = flag;
self self
} }
#[inline] #[inline]
fn with_drag_and_drop(mut self, flag: bool) -> Self { fn with_drag_and_drop(mut self, flag: bool) -> Self {
self.platform_specific.drag_and_drop = flag; self.window.platform_specific.drag_and_drop = flag;
self self
} }
#[inline] #[inline]
fn with_skip_taskbar(mut self, skip: bool) -> Self { fn with_skip_taskbar(mut self, skip: bool) -> Self {
self.platform_specific.skip_taskbar = skip; self.window.platform_specific.skip_taskbar = skip;
self self
} }
#[inline] #[inline]
fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self { fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self {
self.platform_specific.class_name = class_name.into(); self.window.platform_specific.class_name = class_name.into();
self self
} }
#[inline] #[inline]
fn with_undecorated_shadow(mut self, shadow: bool) -> Self { fn with_undecorated_shadow(mut self, shadow: bool) -> Self {
self.platform_specific.decoration_shadow = shadow; 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 self
} }
} }
@@ -328,27 +535,12 @@ impl IconExtWindows for Icon {
path: P, path: P,
size: Option<PhysicalSize<u32>>, size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> { ) -> Result<Self, BadIcon> {
let win_icon = WinIcon::from_path(path, size)?; let win_icon = crate::platform_impl::WinIcon::from_path(path, size)?;
Ok(Icon { inner: win_icon }) Ok(Icon { inner: win_icon })
} }
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> { fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
let win_icon = WinIcon::from_resource(ordinal, size)?; let win_icon = crate::platform_impl::WinIcon::from_resource(ordinal, size)?;
Ok(Icon { inner: win_icon }) 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,3 +1,6 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget}, event_loop::{EventLoopBuilder, EventLoopWindowTarget},
monitor::MonitorHandle, monitor::MonitorHandle,
@@ -5,9 +8,50 @@ use crate::{
}; };
use crate::dpi::Size; use crate::dpi::Size;
use crate::platform_impl::{ApplicationName, Backend, XLIB_ERROR_HOOKS};
pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported}; /// 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,
}
/// The first argument in the provided hook will be the pointer to `XDisplay` /// 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 /// and the second one the pointer to [`XErrorEvent`]. The returned `bool` is an
@@ -38,7 +82,10 @@ pub type XWindow = u32;
pub fn register_xlib_error_hook(hook: XlibErrorHook) { pub fn register_xlib_error_hook(hook: XlibErrorHook) {
// Append new hook. // Append new hook.
unsafe { unsafe {
XLIB_ERROR_HOOKS.lock().unwrap().push(hook); crate::platform_impl::XLIB_ERROR_HOOKS
.lock()
.unwrap()
.push(hook);
} }
} }
@@ -48,7 +95,7 @@ pub trait EventLoopWindowTargetExtX11 {
fn is_x11(&self) -> bool; fn is_x11(&self) -> bool;
} }
impl<T> EventLoopWindowTargetExtX11 for EventLoopWindowTarget<T> { impl EventLoopWindowTargetExtX11 for EventLoopWindowTarget {
#[inline] #[inline]
fn is_x11(&self) -> bool { fn is_x11(&self) -> bool {
!self.p.is_wayland() !self.p.is_wayland()
@@ -70,7 +117,7 @@ pub trait EventLoopBuilderExtX11 {
impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> { impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> {
#[inline] #[inline]
fn with_x11(&mut self) -> &mut Self { fn with_x11(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(Backend::X); self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X);
self self
} }
@@ -96,29 +143,29 @@ pub trait WindowBuilderExtX11 {
/// Build window with the given `general` and `instance` names. /// Build window with the given `general` and `instance` names.
/// ///
/// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the /// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the
/// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "general", "instance"`. /// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "instance", "general"`.
/// ///
/// For details about application ID conventions, see the /// For details about application ID conventions, see the
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id) /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self; fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;
/// Build window with override-redirect flag; defaults to false. Only relevant on X11. /// Build window with override-redirect flag; defaults to false.
fn with_override_redirect(self, override_redirect: bool) -> Self; fn with_override_redirect(self, override_redirect: bool) -> Self;
/// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. Only relevant on X11. /// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`.
fn with_x11_window_type(self, x11_window_type: Vec<XWindowType>) -> Self; fn with_x11_window_type(self, x11_window_type: Vec<WindowType>) -> Self;
/// Build window with base size hint. Only implemented on X11. /// Build window with base size hint.
/// ///
/// ``` /// ```
/// # use winit::dpi::{LogicalSize, PhysicalSize}; /// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::window::WindowBuilder; /// # use winit::window::Window;
/// # use winit::platform::x11::WindowBuilderExtX11; /// # use winit::platform::x11::WindowBuilderExtX11;
/// // Specify the size in logical dimensions like this: /// // Specify the size in logical dimensions like this:
/// WindowBuilder::new().with_base_size(LogicalSize::new(400.0, 200.0)); /// Window::builder().with_base_size(LogicalSize::new(400.0, 200.0));
/// ///
/// // Or specify the size in physical dimensions like this: /// // Or specify the size in physical dimensions like this:
/// WindowBuilder::new().with_base_size(PhysicalSize::new(400, 200)); /// Window::builder().with_base_size(PhysicalSize::new(400, 200));
/// ``` /// ```
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self; fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
@@ -127,12 +174,12 @@ pub trait WindowBuilderExtX11 {
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
/// use winit::window::WindowBuilder; /// use winit::window::Window;
/// use winit::platform::x11::{XWindow, WindowBuilderExtX11}; /// use winit::platform::x11::{XWindow, WindowBuilderExtX11};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> { /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let event_loop = winit::event_loop::EventLoop::new().unwrap(); /// let event_loop = winit::event_loop::EventLoop::new().unwrap();
/// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?; /// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?;
/// let window = WindowBuilder::new() /// let window = Window::builder()
/// .with_embed_parent_window(parent_window_id) /// .with_embed_parent_window(parent_window_id)
/// .build(&event_loop)?; /// .build(&event_loop)?;
/// # Ok(()) } /// # Ok(()) }
@@ -143,43 +190,46 @@ pub trait WindowBuilderExtX11 {
impl WindowBuilderExtX11 for WindowBuilder { impl WindowBuilderExtX11 for WindowBuilder {
#[inline] #[inline]
fn with_x11_visual(mut self, visual_id: XVisualID) -> Self { fn with_x11_visual(mut self, visual_id: XVisualID) -> Self {
self.platform_specific.x11.visual_id = Some(visual_id); self.window.platform_specific.x11.visual_id = Some(visual_id);
self self
} }
#[inline] #[inline]
fn with_x11_screen(mut self, screen_id: i32) -> Self { fn with_x11_screen(mut self, screen_id: i32) -> Self {
self.platform_specific.x11.screen_id = Some(screen_id); self.window.platform_specific.x11.screen_id = Some(screen_id);
self self
} }
#[inline] #[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self { fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into())); self.window.platform_specific.name = Some(crate::platform_impl::ApplicationName::new(
general.into(),
instance.into(),
));
self self
} }
#[inline] #[inline]
fn with_override_redirect(mut self, override_redirect: bool) -> Self { fn with_override_redirect(mut self, override_redirect: bool) -> Self {
self.platform_specific.x11.override_redirect = override_redirect; self.window.platform_specific.x11.override_redirect = override_redirect;
self self
} }
#[inline] #[inline]
fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self { fn with_x11_window_type(mut self, x11_window_types: Vec<WindowType>) -> Self {
self.platform_specific.x11.x11_window_types = x11_window_types; self.window.platform_specific.x11.x11_window_types = x11_window_types;
self self
} }
#[inline] #[inline]
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self { fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
self.platform_specific.x11.base_size = Some(base_size.into()); self.window.platform_specific.x11.base_size = Some(base_size.into());
self self
} }
#[inline] #[inline]
fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self { fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self {
self.platform_specific.x11.embed_window = Some(parent_window_id); self.window.platform_specific.x11.embed_window = Some(parent_window_id);
self self
} }
} }

View File

@@ -4,6 +4,7 @@ use std::{
cell::Cell, cell::Cell,
collections::VecDeque, collections::VecDeque,
hash::Hash, hash::Hash,
marker::PhantomData,
sync::{ sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
mpsc, Arc, Mutex, RwLock, mpsc, Arc, Mutex, RwLock,
@@ -15,9 +16,11 @@ use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
use android_activity::{ use android_activity::{
AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect, AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect,
}; };
use log::{debug, trace, warn};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::{ use crate::{
cursor::Cursor,
dpi::{PhysicalPosition, PhysicalSize, Position, Size}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error, error,
event::{self, Force, InnerSizeWriter, StartCause}, event::{self, Force, InnerSizeWriter, StartCause},
@@ -138,7 +141,7 @@ pub struct KeyEventExtra {}
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
android_app: AndroidApp, android_app: AndroidApp,
window_target: event_loop::EventLoopWindowTarget<T>, window_target: event_loop::EventLoopWindowTarget,
redraw_flag: SharedFlag, redraw_flag: SharedFlag,
user_events_sender: mpsc::Sender<T>, user_events_sender: mpsc::Sender<T>,
user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent
@@ -185,9 +188,8 @@ impl<T: 'static> EventLoop<T> {
&redraw_flag, &redraw_flag,
android_app.create_waker(), android_app.create_waker(),
), ),
_marker: std::marker::PhantomData,
}, },
_marker: std::marker::PhantomData, _marker: PhantomData,
}, },
redraw_flag, redraw_flag,
user_events_sender, user_events_sender,
@@ -203,7 +205,7 @@ impl<T: 'static> EventLoop<T> {
fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F) fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
where where
F: FnMut(event::Event<T>, &RootELW<T>), F: FnMut(event::Event<T>, &RootELW),
{ {
trace!("Mainloop iteration"); trace!("Mainloop iteration");
@@ -375,7 +377,7 @@ impl<T: 'static> EventLoop<T> {
callback: &mut F, callback: &mut F,
) -> InputStatus ) -> InputStatus
where where
F: FnMut(event::Event<T>, &RootELW<T>), F: FnMut(event::Event<T>, &RootELW),
{ {
let mut input_status = InputStatus::Handled; let mut input_status = InputStatus::Handled;
match event { match event {
@@ -480,19 +482,15 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError> pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>), F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget),
{ {
self.run_on_demand(event_handler) self.run_on_demand(event_handler)
} }
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>), F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget),
{ {
if self.loop_running {
return Err(EventLoopError::AlreadyRunning);
}
loop { loop {
match self.pump_events(None, &mut event_handler) { match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
@@ -510,7 +508,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where where
F: FnMut(event::Event<T>, &RootELW<T>), F: FnMut(event::Event<T>, &RootELW),
{ {
if !self.loop_running { if !self.loop_running {
self.loop_running = true; self.loop_running = true;
@@ -543,7 +541,7 @@ impl<T: 'static> EventLoop<T> {
fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where where
F: FnMut(event::Event<T>, &RootELW<T>), F: FnMut(event::Event<T>, &RootELW),
{ {
let start = Instant::now(); let start = Instant::now();
@@ -619,7 +617,7 @@ impl<T: 'static> EventLoop<T> {
}); });
} }
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> { pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget {
&self.window_target &self.window_target
} }
@@ -663,15 +661,14 @@ impl<T> EventLoopProxy<T> {
} }
} }
pub struct EventLoopWindowTarget<T: 'static> { pub struct EventLoopWindowTarget {
app: AndroidApp, app: AndroidApp,
control_flow: Cell<ControlFlow>, control_flow: Cell<ControlFlow>,
exit: Cell<bool>, exit: Cell<bool>,
redraw_requester: RedrawRequester, redraw_requester: RedrawRequester,
_marker: std::marker::PhantomData<T>,
} }
impl<T: 'static> EventLoopWindowTarget<T> { impl EventLoopWindowTarget {
pub fn primary_monitor(&self) -> Option<MonitorHandle> { pub fn primary_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle::new(self.app.clone())) Some(MonitorHandle::new(self.app.clone()))
} }
@@ -713,9 +710,36 @@ impl<T: 'static> EventLoopWindowTarget<T> {
self.exit.set(true) self.exit.set(true)
} }
pub(crate) fn clear_exit(&self) {
self.exit.set(false)
}
pub(crate) fn exiting(&self) -> bool { pub(crate) fn exiting(&self) -> bool {
self.exit.get() self.exit.get()
} }
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::AndroidDisplayHandle::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::AndroidDisplayHandle::new().into())
}
} }
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
@@ -751,28 +775,15 @@ impl DeviceId {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PlatformSpecificWindowBuilderAttributes; pub struct PlatformSpecificWindowBuilderAttributes;
#[derive(Debug, Clone)]
pub(crate) struct OwnedWindowHandle {}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
// Parent windows are currently unsupported, though owned window
// handles would be implementable.
Self {}
}
}
pub(crate) struct Window { pub(crate) struct Window {
app: AndroidApp, app: AndroidApp,
redraw_requester: RedrawRequester, redraw_requester: RedrawRequester,
} }
impl Window { impl Window {
pub(crate) fn new<T: 'static>( pub(crate) fn new(
el: &EventLoopWindowTarget<T>, el: &EventLoopWindowTarget,
_window_attrs: window::WindowAttributes, _window_attrs: window::WindowAttributes,
_: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, error::OsError> { ) -> Result<Self, error::OsError> {
// FIXME this ignores requested window attributes // FIXME this ignores requested window attributes
@@ -916,9 +927,7 @@ impl Window {
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {} pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
pub fn set_cursor_icon(&self, _: window::CursorIcon) {} pub fn set_cursor(&self, _: Cursor) {}
pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {}
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> { pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported( Err(error::ExternalError::NotSupported(
@@ -1098,11 +1107,11 @@ impl MonitorHandle {
None None
} }
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> { pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
let size = self.size().into(); let size = self.size().into();
// FIXME this is not the real refresh rate // FIXME this is not the real refresh rate
// (it is guaranteed to support 32 bit color though) // (it is guaranteed to support 32 bit color though)
std::iter::once(VideoMode { std::iter::once(VideoModeHandle {
size, size,
bit_depth: 32, bit_depth: 32,
refresh_rate_millihertz: 60000, refresh_rate_millihertz: 60000,
@@ -1112,14 +1121,14 @@ impl MonitorHandle {
} }
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct VideoMode { pub struct VideoModeHandle {
size: (u32, u32), size: (u32, u32),
bit_depth: u16, bit_depth: u16,
refresh_rate_millihertz: u32, refresh_rate_millihertz: u32,
monitor: MonitorHandle, monitor: MonitorHandle,
} }
impl VideoMode { impl VideoModeHandle {
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
self.size.into() self.size.into()
} }

View File

@@ -3,7 +3,7 @@
use std::{ use std::{
cell::{RefCell, RefMut}, cell::{RefCell, RefMut},
collections::HashSet, collections::HashSet,
mem, fmt, mem,
os::raw::c_void, os::raw::c_void,
ptr, ptr,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@@ -24,13 +24,12 @@ use objc2::runtime::AnyObject;
use objc2::{msg_send, sel}; use objc2::{msg_send, sel};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use super::event_loop::{EventHandler, Never};
use super::uikit::UIView; use super::uikit::UIView;
use super::view::WinitUIWindow; use super::view::WinitUIWindow;
use crate::{ use crate::{
dpi::PhysicalSize, dpi::PhysicalSize,
event::{Event, InnerSizeWriter, StartCause, WindowEvent}, event::{Event, InnerSizeWriter, StartCause, WindowEvent},
event_loop::ControlFlow, event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget},
window::WindowId as RootWindowId, window::WindowId as RootWindowId,
}; };
@@ -47,8 +46,32 @@ macro_rules! bug_assert {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum EventWrapper { pub(crate) struct HandlePendingUserEvents;
StaticEvent(Event<Never>),
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>),
ScaleFactorChanged(ScaleFactorChanged), ScaleFactorChanged(ScaleFactorChanged),
} }
@@ -61,7 +84,7 @@ pub struct ScaleFactorChanged {
enum UserCallbackTransitionResult<'a> { enum UserCallbackTransitionResult<'a> {
Success { Success {
event_handler: Box<dyn EventHandler>, handler: EventLoopHandler,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
processing_redraws: bool, processing_redraws: bool,
}, },
@@ -70,7 +93,7 @@ enum UserCallbackTransitionResult<'a> {
}, },
} }
impl Event<Never> { impl Event<HandlePendingUserEvents> {
fn is_redraw(&self) -> bool { fn is_redraw(&self) -> bool {
matches!( matches!(
self, self,
@@ -94,11 +117,11 @@ enum AppStateImpl {
Launching { Launching {
queued_windows: Vec<Id<WinitUIWindow>>, queued_windows: Vec<Id<WinitUIWindow>>,
queued_events: Vec<EventWrapper>, queued_events: Vec<EventWrapper>,
queued_event_handler: Box<dyn EventHandler>, queued_handler: EventLoopHandler,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
}, },
ProcessingEvents { ProcessingEvents {
event_handler: Box<dyn EventHandler>, handler: EventLoopHandler,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
@@ -108,15 +131,15 @@ enum AppStateImpl {
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
}, },
ProcessingRedraws { ProcessingRedraws {
event_handler: Box<dyn EventHandler>, handler: EventLoopHandler,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
Waiting { Waiting {
waiting_event_handler: Box<dyn EventHandler>, waiting_handler: EventLoopHandler,
start: Instant, start: Instant,
}, },
PollFinished { PollFinished {
waiting_event_handler: Box<dyn EventHandler>, waiting_handler: EventLoopHandler,
}, },
Terminated, Terminated,
} }
@@ -204,7 +227,7 @@ impl AppState {
matches!(self.state(), AppStateImpl::Terminated) matches!(self.state(), AppStateImpl::Terminated)
} }
fn will_launch_transition(&mut self, queued_event_handler: Box<dyn EventHandler>) { fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) {
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() { let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::NotLaunched { AppStateImpl::NotLaunched {
queued_windows, queued_windows,
@@ -216,28 +239,28 @@ impl AppState {
self.set_state(AppStateImpl::Launching { self.set_state(AppStateImpl::Launching {
queued_windows, queued_windows,
queued_events, queued_events,
queued_event_handler, queued_handler,
queued_gpu_redraws, queued_gpu_redraws,
}); });
} }
fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) { fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) {
let (windows, events, event_handler, queued_gpu_redraws) = match self.take_state() { let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Launching { AppStateImpl::Launching {
queued_windows, queued_windows,
queued_events, queued_events,
queued_event_handler, queued_handler,
queued_gpu_redraws, queued_gpu_redraws,
} => ( } => (
queued_windows, queued_windows,
queued_events, queued_events,
queued_event_handler, queued_handler,
queued_gpu_redraws, queued_gpu_redraws,
), ),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingEvents { self.set_state(AppStateImpl::ProcessingEvents {
event_handler, handler,
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
queued_gpu_redraws, queued_gpu_redraws,
}); });
@@ -251,24 +274,19 @@ impl AppState {
return None; return None;
} }
let (event_handler, event) = match (self.control_flow, self.take_state()) { let (handler, event) = match (self.control_flow, self.take_state()) {
( (ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => (
ControlFlow::Poll, waiting_handler,
AppStateImpl::PollFinished {
waiting_event_handler,
},
) => (
waiting_event_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)), EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)),
), ),
( (
ControlFlow::Wait, ControlFlow::Wait,
AppStateImpl::Waiting { AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}, },
) => ( ) => (
waiting_event_handler, waiting_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled { EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
start, start,
requested_resume: None, requested_resume: None,
@@ -277,7 +295,7 @@ impl AppState {
( (
ControlFlow::WaitUntil(requested_resume), ControlFlow::WaitUntil(requested_resume),
AppStateImpl::Waiting { AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}, },
) => { ) => {
@@ -292,13 +310,13 @@ impl AppState {
requested_resume: Some(requested_resume), requested_resume: Some(requested_resume),
})) }))
}; };
(waiting_event_handler, event) (waiting_handler, event)
} }
s => bug!("`EventHandler` unexpectedly woke up {:?}", s), s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingEvents { self.set_state(AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws: Default::default(), queued_gpu_redraws: Default::default(),
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
}); });
@@ -343,25 +361,20 @@ impl AppState {
} }
} }
let (event_handler, queued_gpu_redraws, active_control_flow, processing_redraws) = let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) =
match self.take_state() { match self.take_state() {
AppStateImpl::Launching { .. } AppStateImpl::Launching { .. }
| AppStateImpl::NotLaunched { .. } | AppStateImpl::NotLaunched { .. }
| AppStateImpl::InUserCallback { .. } => unreachable!(), | AppStateImpl::InUserCallback { .. } => unreachable!(),
AppStateImpl::ProcessingEvents { AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
} => ( } => (handler, queued_gpu_redraws, active_control_flow, false),
event_handler,
queued_gpu_redraws,
active_control_flow,
false,
),
AppStateImpl::ProcessingRedraws { AppStateImpl::ProcessingRedraws {
event_handler, handler,
active_control_flow, active_control_flow,
} => (event_handler, Default::default(), active_control_flow, true), } => (handler, Default::default(), active_control_flow, true),
AppStateImpl::PollFinished { .. } AppStateImpl::PollFinished { .. }
| AppStateImpl::Waiting { .. } | AppStateImpl::Waiting { .. }
| AppStateImpl::Terminated => unreachable!(), | AppStateImpl::Terminated => unreachable!(),
@@ -371,23 +384,23 @@ impl AppState {
queued_gpu_redraws, queued_gpu_redraws,
}); });
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
event_handler, handler,
active_control_flow, active_control_flow,
processing_redraws, processing_redraws,
} }
} }
fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> { fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> {
let (event_handler, queued_gpu_redraws, active_control_flow) = match self.take_state() { let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
AppStateImpl::ProcessingEvents { AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
} => (event_handler, queued_gpu_redraws, active_control_flow), } => (handler, queued_gpu_redraws, active_control_flow),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingRedraws { self.set_state(AppStateImpl::ProcessingRedraws {
event_handler, handler,
active_control_flow, active_control_flow,
}); });
queued_gpu_redraws queued_gpu_redraws
@@ -397,11 +410,11 @@ impl AppState {
if !self.has_launched() || self.has_terminated() { if !self.has_launched() || self.has_terminated() {
return; return;
} }
let (waiting_event_handler, old) = match self.take_state() { let (waiting_handler, old) = match self.take_state() {
AppStateImpl::ProcessingRedraws { AppStateImpl::ProcessingRedraws {
event_handler, handler,
active_control_flow, active_control_flow,
} => (event_handler, active_control_flow), } => (handler, active_control_flow),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
@@ -410,7 +423,7 @@ impl AppState {
(ControlFlow::Wait, ControlFlow::Wait) => { (ControlFlow::Wait, ControlFlow::Wait) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}); });
} }
@@ -419,14 +432,14 @@ impl AppState {
{ {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}); });
} }
(_, ControlFlow::Wait) => { (_, ControlFlow::Wait) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}); });
self.waker.stop() self.waker.stop()
@@ -434,24 +447,22 @@ impl AppState {
(_, ControlFlow::WaitUntil(new_instant)) => { (_, ControlFlow::WaitUntil(new_instant)) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}); });
self.waker.start_at(new_instant) self.waker.start_at(new_instant)
} }
// Unlike on macOS, handle Poll to Poll transition here to call the waker // Unlike on macOS, handle Poll to Poll transition here to call the waker
(_, ControlFlow::Poll) => { (_, ControlFlow::Poll) => {
self.set_state(AppStateImpl::PollFinished { self.set_state(AppStateImpl::PollFinished { waiting_handler });
waiting_event_handler,
});
self.waker.start() self.waker.start()
} }
} }
} }
fn terminated_transition(&mut self) -> Box<dyn EventHandler> { fn terminated_transition(&mut self) -> EventLoopHandler {
match self.replace_state(AppStateImpl::Terminated) { match self.replace_state(AppStateImpl::Terminated) {
AppStateImpl::ProcessingEvents { event_handler, .. } => event_handler, AppStateImpl::ProcessingEvents { handler, .. } => handler,
s => bug!("`LoopExiting` happened while not processing events {:?}", s), s => bug!("`LoopExiting` happened while not processing events {:?}", s),
} }
} }
@@ -516,8 +527,8 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUI
} }
} }
pub fn will_launch(mtm: MainThreadMarker, queued_event_handler: Box<dyn EventHandler>) { pub(crate) fn will_launch(mtm: MainThreadMarker, queued_handler: EventLoopHandler) {
AppState::get_mut(mtm).will_launch_transition(queued_event_handler) AppState::get_mut(mtm).will_launch_transition(queued_handler)
} }
pub fn did_finish_launching(mtm: MainThreadMarker) { pub fn did_finish_launching(mtm: MainThreadMarker) {
@@ -594,17 +605,17 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
return; return;
} }
let (mut event_handler, active_control_flow, processing_redraws) = let (mut handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() { match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => { UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
queued_events.extend(events); queued_events.extend(events);
return; return;
} }
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
event_handler, handler,
active_control_flow, active_control_flow,
processing_redraws, processing_redraws,
} => (event_handler, active_control_flow, processing_redraws), } => (handler, active_control_flow, processing_redraws),
}; };
drop(this); drop(this);
@@ -619,11 +630,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
event event
); );
} }
event_handler.handle_nonuser_event(event) handler.handle_event(event)
}
EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
} }
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
} }
} }
@@ -650,12 +659,12 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
"redraw queued while processing redraws" "redraw queued while processing redraws"
); );
AppStateImpl::ProcessingRedraws { AppStateImpl::ProcessingRedraws {
event_handler, handler,
active_control_flow, active_control_flow,
} }
} else { } else {
AppStateImpl::ProcessingEvents { AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
} }
@@ -675,11 +684,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
event event
); );
} }
event_handler.handle_nonuser_event(event) handler.handle_event(event)
}
EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
} }
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
} }
} }
} }
@@ -687,23 +694,23 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
fn handle_user_events(mtm: MainThreadMarker) { fn handle_user_events(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let (mut event_handler, active_control_flow, processing_redraws) = let (mut handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() { match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { .. } => { UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
bug!("unexpected attempted to process an event") bug!("unexpected attempted to process an event")
} }
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
event_handler, handler,
active_control_flow, active_control_flow,
processing_redraws, processing_redraws,
} => (event_handler, active_control_flow, processing_redraws), } => (handler, active_control_flow, processing_redraws),
}; };
if processing_redraws { if processing_redraws {
bug!("user events attempted to be sent out while `ProcessingRedraws`"); bug!("user events attempted to be sent out while `ProcessingRedraws`");
} }
drop(this); drop(this);
event_handler.handle_user_events(); handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
@@ -723,7 +730,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
_ => unreachable!(), _ => unreachable!(),
}; };
this.app_state = Some(AppStateImpl::ProcessingEvents { this.app_state = Some(AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
}); });
@@ -733,13 +740,12 @@ fn handle_user_events(mtm: MainThreadMarker) {
for wrapper in queued_events { for wrapper in queued_events {
match wrapper { match wrapper {
EventWrapper::StaticEvent(event) => event_handler.handle_nonuser_event(event), EventWrapper::StaticEvent(event) => handler.handle_event(event),
EventWrapper::ScaleFactorChanged(event) => { EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
handle_hidpi_proxy(&mut event_handler, event)
}
} }
} }
event_handler.handle_user_events();
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
} }
} }
@@ -779,13 +785,13 @@ pub fn handle_events_cleared(mtm: MainThreadMarker) {
pub fn terminated(mtm: MainThreadMarker) { pub fn terminated(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let mut event_handler = this.terminated_transition(); let mut handler = this.terminated_transition();
drop(this); drop(this);
event_handler.handle_nonuser_event(Event::LoopExiting) handler.handle_event(Event::LoopExiting)
} }
fn handle_hidpi_proxy(event_handler: &mut Box<dyn EventHandler>, event: ScaleFactorChanged) { fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged) {
let ScaleFactorChanged { let ScaleFactorChanged {
suggested_size, suggested_size,
scale_factor, scale_factor,
@@ -799,7 +805,7 @@ fn handle_hidpi_proxy(event_handler: &mut Box<dyn EventHandler>, event: ScaleFac
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)), inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
}, },
}; };
event_handler.handle_nonuser_event(event); handler.handle_event(event);
let (view, screen_frame) = get_view_and_screen_frame(&window); let (view, screen_frame) = get_view_and_screen_frame(&window);
let physical_size = *new_inner_size.lock().unwrap(); let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size); drop(new_inner_size);

View File

@@ -1,7 +1,6 @@
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
ffi::c_void, ffi::c_void,
fmt::{self, Debug},
marker::PhantomData, marker::PhantomData,
ptr, ptr,
sync::mpsc::{self, Receiver, Sender}, sync::mpsc::{self, Receiver, Sender},
@@ -25,6 +24,7 @@ use crate::{
EventLoopWindowTarget as RootEventLoopWindowTarget, EventLoopWindowTarget as RootEventLoopWindowTarget,
}, },
platform::ios::Idiom, platform::ios::Idiom,
platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents},
}; };
use super::{app_state, monitor, view, MonitorHandle}; use super::{app_state, monitor, view, MonitorHandle};
@@ -34,12 +34,11 @@ use super::{
}; };
#[derive(Debug)] #[derive(Debug)]
pub struct EventLoopWindowTarget<T: 'static> { pub struct EventLoopWindowTarget {
pub(super) mtm: MainThreadMarker, pub(super) mtm: MainThreadMarker,
p: PhantomData<T>,
} }
impl<T: 'static> EventLoopWindowTarget<T> { impl EventLoopWindowTarget {
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> { pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
monitor::uiscreens(self.mtm) monitor::uiscreens(self.mtm)
} }
@@ -78,19 +77,56 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub(crate) fn exit(&self) { pub(crate) fn exit(&self) {
// https://developer.apple.com/library/archive/qa/qa1561/_index.html // https://developer.apple.com/library/archive/qa/qa1561/_index.html
// it is not possible to quit an iOS app gracefully and programatically // it is not possible to quit an iOS app gracefully and programatically
warn!("`ControlFlow::Exit` ignored on iOS"); log::warn!("`ControlFlow::Exit` ignored on iOS");
} }
pub(crate) fn exiting(&self) -> bool { pub(crate) fn exiting(&self) -> bool {
false 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> { pub struct EventLoop<T: 'static> {
mtm: MainThreadMarker, mtm: MainThreadMarker,
sender: Sender<T>, sender: Sender<T>,
receiver: Receiver<T>, receiver: Receiver<T>,
window_target: RootEventLoopWindowTarget<T>, window_target: RootEventLoopWindowTarget,
} }
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -123,59 +159,59 @@ impl<T: 'static> EventLoop<T> {
sender, sender,
receiver, receiver,
window_target: RootEventLoopWindowTarget { window_target: RootEventLoopWindowTarget {
p: EventLoopWindowTarget { p: EventLoopWindowTarget { mtm },
mtm,
p: PhantomData,
},
_marker: PhantomData, _marker: PhantomData,
}, },
}) })
} }
pub fn run<F>(self, event_handler: F) -> ! pub fn run<F>(self, handler: F) -> !
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>), F: FnMut(Event<T>, &RootEventLoopWindowTarget),
{ {
unsafe { let application = UIApplication::shared(self.mtm);
let application = UIApplication::shared(self.mtm); assert!(
assert!( application.is_none(),
application.is_none(), "\
"\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\ `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS", Note: `EventLoop::run` calls `UIApplicationMain` on iOS",
); );
let event_handler = std::mem::transmute::< let handler = map_user_event(handler, self.receiver);
Box<dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>)>,
Box<EventHandlerCallback<T>>,
>(Box::new(event_handler));
let handler = EventLoopHandler { let handler = unsafe {
f: event_handler, std::mem::transmute::<
receiver: self.receiver, Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>,
event_loop: self.window_target, Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>,
}; >(Box::new(handler))
};
app_state::will_launch(self.mtm, Box::new(handler)); let handler = EventLoopHandler {
handler,
event_loop: self.window_target,
};
// Ensure application delegate is initialized app_state::will_launch(self.mtm, handler);
view::WinitApplicationDelegate::class();
// Ensure application delegate is initialized
view::WinitApplicationDelegate::class();
unsafe {
UIApplicationMain( UIApplicationMain(
0, 0,
ptr::null(), ptr::null(),
None, None,
Some(&NSString::from_str("WinitApplicationDelegate")), Some(&NSString::from_str("WinitApplicationDelegate")),
); )
unreachable!() };
} unreachable!()
} }
pub fn create_proxy(&self) -> EventLoopProxy<T> { pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.sender.clone()) EventLoopProxy::new(self.sender.clone())
} }
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> { pub fn window_target(&self) -> &RootEventLoopWindowTarget {
&self.window_target &self.window_target
} }
} }
@@ -342,39 +378,3 @@ fn setup_control_flow_observers() {
CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode); 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,35 +68,35 @@ mod window;
use std::fmt; use std::fmt;
use crate::event::DeviceId as RootDeviceId;
pub(crate) use self::{ pub(crate) use self::{
event_loop::{ event_loop::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes, EventLoop, EventLoopProxy, EventLoopWindowTarget, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
}, },
monitor::{MonitorHandle, VideoMode}, monitor::{MonitorHandle, VideoModeHandle},
window::{OwnedWindowHandle, PlatformSpecificWindowBuilderAttributes, Window, WindowId}, window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
}; };
use self::uikit::UIScreen;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor; pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder; pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::NoIcon as PlatformIcon; pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; pub(crate) use crate::platform_impl::Fullscreen;
/// There is no way to detect which device that performed a certain event in
/// UIKit (i.e. you can't differentiate between different external keyboards,
/// or wether it was the main touchscreen, assistive technologies, or some
/// other pointer device that caused a touch event).
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId { pub struct DeviceId;
uiscreen: *const UIScreen,
}
impl DeviceId { impl DeviceId {
pub const unsafe fn dummy() -> Self { pub const unsafe fn dummy() -> Self {
DeviceId { DeviceId
uiscreen: std::ptr::null(),
}
} }
} }
unsafe impl Send for DeviceId {} pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId);
unsafe impl Sync for DeviceId {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyEventExtra {} pub struct KeyEventExtra {}

View File

@@ -13,7 +13,7 @@ use objc2::Message;
use super::uikit::{UIScreen, UIScreenMode}; use super::uikit::{UIScreen, UIScreenMode};
use crate::{ use crate::{
dpi::{PhysicalPosition, PhysicalSize}, dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoMode as RootVideoMode, monitor::VideoModeHandle as RootVideoModeHandle,
platform_impl::platform::app_state, platform_impl::platform::app_state,
}; };
@@ -48,7 +48,7 @@ impl<T: IsRetainable + Message> PartialEq for MainThreadBoundDelegateImpls<T> {
impl<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {} impl<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {}
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct VideoMode { pub struct VideoModeHandle {
pub(crate) size: (u32, u32), pub(crate) size: (u32, u32),
pub(crate) bit_depth: u16, pub(crate) bit_depth: u16,
pub(crate) refresh_rate_millihertz: u32, pub(crate) refresh_rate_millihertz: u32,
@@ -56,15 +56,15 @@ pub struct VideoMode {
pub(crate) monitor: MonitorHandle, pub(crate) monitor: MonitorHandle,
} }
impl VideoMode { impl VideoModeHandle {
fn new( fn new(
uiscreen: Id<UIScreen>, uiscreen: Id<UIScreen>,
screen_mode: Id<UIScreenMode>, screen_mode: Id<UIScreenMode>,
mtm: MainThreadMarker, mtm: MainThreadMarker,
) -> VideoMode { ) -> VideoModeHandle {
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen); let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
let size = screen_mode.size(); let size = screen_mode.size();
VideoMode { VideoModeHandle {
size: (size.width as u32, size.height as u32), size: (size.width as u32, size.height as u32),
bit_depth: 32, bit_depth: 32,
refresh_rate_millihertz, refresh_rate_millihertz,
@@ -135,24 +135,13 @@ impl Ord for MonitorHandle {
impl fmt::Debug for MonitorHandle { impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Do this using the proper fmt API f.debug_struct("MonitorHandle")
#[derive(Debug)] .field("name", &self.name())
#[allow(dead_code)] .field("size", &self.size())
struct MonitorHandle { .field("position", &self.position())
name: Option<String>, .field("scale_factor", &self.scale_factor())
size: PhysicalSize<u32>, .field("refresh_rate_millihertz", &self.refresh_rate_millihertz())
position: PhysicalPosition<i32>, .finish_non_exhaustive()
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)
} }
} }
@@ -207,16 +196,16 @@ impl MonitorHandle {
) )
} }
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> { pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
MainThreadMarker::run_on_main(|mtm| { MainThreadMarker::run_on_main(|mtm| {
let ui_screen = self.ui_screen(mtm); let ui_screen = self.ui_screen(mtm);
// Use Ord impl of RootVideoMode // Use Ord impl of RootVideoModeHandle
let modes: BTreeSet<_> = ui_screen let modes: BTreeSet<_> = ui_screen
.availableModes() .availableModes()
.into_iter() .into_iter()
.map(|mode| RootVideoMode { .map(|mode| RootVideoModeHandle {
video_mode: VideoMode::new(ui_screen.clone(), mode, mtm), video_mode: VideoModeHandle::new(ui_screen.clone(), mode, mtm),
}) })
.collect(); .collect();
@@ -228,9 +217,9 @@ impl MonitorHandle {
self.ui_screen.get(mtm) self.ui_screen.get(mtm)
} }
pub fn preferred_video_mode(&self) -> VideoMode { pub fn preferred_video_mode(&self) -> VideoModeHandle {
MainThreadMarker::run_on_main(|mtm| { MainThreadMarker::run_on_main(|mtm| {
VideoMode::new( VideoModeHandle::new(
self.ui_screen(mtm).clone(), self.ui_screen(mtm).clone(),
self.ui_screen(mtm).preferredMode().unwrap(), self.ui_screen(mtm).preferredMode().unwrap(),
mtm, mtm,

View File

@@ -0,0 +1,121 @@
use icrate::Foundation::{CGFloat, NSInteger, NSObject, NSUInteger};
use objc2::{
encode::{Encode, Encoding},
extern_class, extern_methods, mutability, ClassType,
};
// https://developer.apple.com/documentation/uikit/uigesturerecognizer
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIGestureRecognizer;
unsafe impl ClassType for UIGestureRecognizer {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIGestureRecognizer {
#[method(state)]
pub fn state(&self) -> UIGestureRecognizerState;
}
);
unsafe impl Encode for UIGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
// https://developer.apple.com/documentation/uikit/uigesturerecognizer/state
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIGestureRecognizerState(NSInteger);
unsafe impl Encode for UIGestureRecognizerState {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[allow(dead_code)]
impl UIGestureRecognizerState {
pub const Possible: Self = Self(0);
pub const Began: Self = Self(1);
pub const Changed: Self = Self(2);
pub const Ended: Self = Self(3);
pub const Cancelled: Self = Self(4);
pub const Failed: Self = Self(5);
}
// https://developer.apple.com/documentation/uikit/uipinchgesturerecognizer
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIPinchGestureRecognizer;
unsafe impl ClassType for UIPinchGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIPinchGestureRecognizer {
#[method(scale)]
pub fn scale(&self) -> CGFloat;
#[method(velocity)]
pub fn velocity(&self) -> CGFloat;
}
);
unsafe impl Encode for UIPinchGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
// https://developer.apple.com/documentation/uikit/uirotationgesturerecognizer
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIRotationGestureRecognizer;
unsafe impl ClassType for UIRotationGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIRotationGestureRecognizer {
#[method(rotation)]
pub fn rotation(&self) -> CGFloat;
#[method(velocity)]
pub fn velocity(&self) -> CGFloat;
}
);
unsafe impl Encode for UIRotationGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
// https://developer.apple.com/documentation/uikit/uitapgesturerecognizer
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UITapGestureRecognizer;
unsafe impl ClassType for UITapGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UITapGestureRecognizer {
#[method(setNumberOfTapsRequired:)]
pub fn setNumberOfTapsRequired(&self, number_of_taps_required: NSUInteger);
#[method(setNumberOfTouchesRequired:)]
pub fn setNumberOfTouchesRequired(&self, number_of_touches_required: NSUInteger);
}
);
unsafe impl Encode for UITapGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}

View File

@@ -9,6 +9,7 @@ mod application;
mod coordinate_space; mod coordinate_space;
mod device; mod device;
mod event; mod event;
mod gesture_recognizer;
mod responder; mod responder;
mod screen; mod screen;
mod screen_mode; mod screen_mode;
@@ -23,6 +24,10 @@ pub(crate) use self::application::UIApplication;
pub(crate) use self::coordinate_space::UICoordinateSpace; pub(crate) use self::coordinate_space::UICoordinateSpace;
pub(crate) use self::device::UIDevice; pub(crate) use self::device::UIDevice;
pub(crate) use self::event::UIEvent; 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::responder::UIResponder;
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation}; pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};
pub(crate) use self::screen_mode::UIScreenMode; pub(crate) use self::screen_mode::UIScreenMode;

View File

@@ -3,7 +3,7 @@ use objc2::encode::{Encode, Encoding};
use objc2::rc::Id; use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType}; use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use super::{UICoordinateSpace, UIResponder, UIViewController}; use super::{UICoordinateSpace, UIGestureRecognizer, UIResponder, UIViewController};
extern_class!( extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
@@ -65,6 +65,12 @@ extern_methods!(
#[method(setNeedsDisplay)] #[method(setNeedsDisplay)]
pub fn setNeedsDisplay(&self); 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)] #[derive(Clone, Copy)]
pub struct UIInterfaceOrientationMask: NSUInteger { pub struct UIInterfaceOrientationMask: NSUInteger {
const Portrait = 1 << 1; const Portrait = 1 << 1;

View File

@@ -1,32 +1,38 @@
#![allow(clippy::unnecessary_cast)] #![allow(clippy::unnecessary_cast)]
use std::cell::Cell; use std::cell::{Cell, RefCell};
use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet}; use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet};
use objc2::rc::Id; use objc2::rc::Id;
use objc2::runtime::AnyClass; use objc2::runtime::AnyClass;
use objc2::{ use objc2::{
declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType, DeclaredClass, declare_class, extern_methods, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
}; };
use super::app_state::{self, EventWrapper}; use super::app_state::{self, EventWrapper};
use super::uikit::{ use super::uikit::{
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIInterfaceOrientationMask, UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIGestureRecognizerState,
UIResponder, UIStatusBarStyle, UITouch, UITouchPhase, UITouchType, UITraitCollection, UIView, UIInterfaceOrientationMask, UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer,
UIViewController, UIWindow, UIStatusBarStyle, UITapGestureRecognizer, UITouch, UITouchPhase, UITouchType,
UITraitCollection, UIView, UIViewController, UIWindow,
}; };
use super::window::WindowId; use super::window::WindowId;
use crate::{ use crate::{
dpi::PhysicalPosition, dpi::PhysicalPosition,
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent}, event::{Event, Force, Touch, TouchPhase, WindowEvent},
platform::ios::ValidOrientations, platform::ios::ValidOrientations,
platform_impl::platform::{ platform_impl::platform::{
ffi::{UIRectEdge, UIUserInterfaceIdiom}, ffi::{UIRectEdge, UIUserInterfaceIdiom},
window::PlatformSpecificWindowBuilderAttributes, Fullscreen, DEVICE_ID,
DeviceId, Fullscreen,
}, },
window::{WindowAttributes, WindowId as RootWindowId}, 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!( declare_class!(
pub(crate) struct WinitView; pub(crate) struct WinitView;
@@ -37,7 +43,9 @@ declare_class!(
const NAME: &'static str = "WinitUIView"; const NAME: &'static str = "WinitUIView";
} }
impl DeclaredClass for WinitView {} impl DeclaredClass for WinitView {
type Ivars = WinitViewState;
}
unsafe impl WinitView { unsafe impl WinitView {
#[method(drawRect:)] #[method(drawRect:)]
@@ -159,6 +167,79 @@ declare_class!(
fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) { fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
self.handle_touches(touches) 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: RootWindowId(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: RootWindowId(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: RootWindowId(window.id()),
event: WindowEvent::RotationGesture {
device_id: DEVICE_ID,
delta,
phase,
},
});
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event);
}
} }
); );
@@ -182,24 +263,73 @@ extern_methods!(
impl WinitView { impl WinitView {
pub(crate) fn new( pub(crate) fn new(
_mtm: MainThreadMarker, _mtm: MainThreadMarker,
_window_attributes: &WindowAttributes, window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
frame: CGRect, frame: CGRect,
) -> Id<Self> { ) -> Id<Self> {
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] }; 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] };
this.setMultipleTouchEnabled(true); this.setMultipleTouchEnabled(true);
if let Some(scale_factor) = platform_attributes.scale_factor { if let Some(scale_factor) = window_attributes.platform_specific.scale_factor {
this.setContentScaleFactor(scale_factor as _); this.setContentScaleFactor(scale_factor as _);
} }
this 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>) { fn handle_touches(&self, touches: &NSSet<UITouch>) {
let window = self.window().unwrap(); let window = self.window().unwrap();
let uiscreen = window.screen();
let mut touch_events = Vec::new(); let mut touch_events = Vec::new();
let os_supports_force = app_state::os_capabilities().force_touch; let os_supports_force = app_state::os_capabilities().force_touch;
for touch in touches { for touch in touches {
@@ -252,9 +382,7 @@ impl WinitView {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()), window_id: RootWindowId(window.id()),
event: WindowEvent::Touch(Touch { event: WindowEvent::Touch(Touch {
device_id: RootDeviceId(DeviceId { device_id: DEVICE_ID,
uiscreen: Id::as_ptr(&uiscreen),
}),
id: touch_id, id: touch_id,
location: physical_location, location: physical_location,
force, force,
@@ -385,8 +513,7 @@ impl WinitViewController {
pub(crate) fn new( pub(crate) fn new(
mtm: MainThreadMarker, mtm: MainThreadMarker,
_window_attributes: &WindowAttributes, window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
view: &UIView, view: &UIView,
) -> Id<Self> { ) -> Id<Self> {
// These are set properly below, we just to set them to something in the meantime. // These are set properly below, we just to set them to something in the meantime.
@@ -399,18 +526,33 @@ impl WinitViewController {
}); });
let this: Id<Self> = unsafe { msg_send_id![super(this), init] }; let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
this.set_prefers_status_bar_hidden(platform_attributes.prefers_status_bar_hidden); this.set_prefers_status_bar_hidden(
window_attributes
.platform_specific
.prefers_status_bar_hidden,
);
this.set_preferred_status_bar_style(platform_attributes.preferred_status_bar_style.into()); this.set_preferred_status_bar_style(
window_attributes
.platform_specific
.preferred_status_bar_style
.into(),
);
this.set_supported_interface_orientations(mtm, platform_attributes.valid_orientations); this.set_supported_interface_orientations(
mtm,
window_attributes.platform_specific.valid_orientations,
);
this.set_prefers_home_indicator_auto_hidden( this.set_prefers_home_indicator_auto_hidden(
platform_attributes.prefers_home_indicator_hidden, window_attributes
.platform_specific
.prefers_home_indicator_hidden,
); );
this.set_preferred_screen_edges_deferring_system_gestures( this.set_preferred_screen_edges_deferring_system_gestures(
platform_attributes window_attributes
.platform_specific
.preferred_screen_edges_deferring_system_gestures .preferred_screen_edges_deferring_system_gestures
.into(), .into(),
); );
@@ -467,7 +609,6 @@ impl WinitUIWindow {
pub(crate) fn new( pub(crate) fn new(
mtm: MainThreadMarker, mtm: MainThreadMarker,
window_attributes: &WindowAttributes, window_attributes: &WindowAttributes,
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
frame: CGRect, frame: CGRect,
view_controller: &UIViewController, view_controller: &UIViewController,
) -> Id<Self> { ) -> Id<Self> {
@@ -475,7 +616,7 @@ impl WinitUIWindow {
this.setRootViewController(Some(view_controller)); this.setRootViewController(Some(view_controller));
match window_attributes.fullscreen.0.clone().map(Into::into) { match window_attributes.fullscreen.clone().map(Into::into) {
Some(Fullscreen::Exclusive(ref video_mode)) => { Some(Fullscreen::Exclusive(ref video_mode)) => {
let monitor = video_mode.monitor(); let monitor = video_mode.monitor();
let screen = monitor.ui_screen(mtm); let screen = monitor.ui_screen(mtm);

View File

@@ -3,6 +3,7 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker}; use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
use log::{debug, warn};
use objc2::rc::Id; use objc2::rc::Id;
use objc2::runtime::AnyObject; use objc2::runtime::AnyObject;
use objc2::{class, msg_send}; use objc2::{class, msg_send};
@@ -11,33 +12,21 @@ use super::app_state::EventWrapper;
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation}; use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
use super::view::{WinitUIWindow, WinitView, WinitViewController}; use super::view::{WinitUIWindow, WinitView, WinitViewController};
use crate::{ use crate::{
cursor::Cursor,
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError}, error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::{Event, WindowEvent}, event::{Event, WindowEvent},
icon::Icon, icon::Icon,
platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations}, platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations},
platform_impl::platform::{ platform_impl::platform::{
app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle, PlatformCustomCursor, app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
}, },
window::{ window::{
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel, WindowButtons, WindowId as RootWindowId, WindowLevel,
}, },
}; };
#[derive(Debug, Clone)]
pub(crate) struct OwnedWindowHandle {}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
// Parent windows are currently unsupported, though owned window
// handles would be implementable (would work similar to macOS).
warn!("parent windows are unsupported on iOS");
Self {}
}
}
pub struct Inner { pub struct Inner {
window: Id<WinitUIWindow>, window: Id<WinitUIWindow>,
view_controller: Id<WinitViewController>, view_controller: Id<WinitViewController>,
@@ -186,12 +175,8 @@ impl Inner {
self.view.contentScaleFactor() as _ self.view.contentScaleFactor() as _
} }
pub fn set_cursor_icon(&self, _cursor: CursorIcon) { pub fn set_cursor(&self, _cursor: Cursor) {
debug!("`Window::set_cursor_icon` ignored on iOS") debug!("`Window::set_cursor` ignored on iOS")
}
pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {
debug!("`Window::set_custom_cursor` ignored on iOS")
} }
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> { pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
@@ -382,15 +367,6 @@ impl Inner {
rwh_06::RawWindowHandle::UiKit(window_handle) 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> { pub fn theme(&self) -> Option<Theme> {
warn!("`Window::theme` is ignored on iOS"); warn!("`Window::theme` is ignored on iOS");
None None
@@ -422,10 +398,9 @@ pub struct Window {
} }
impl Window { impl Window {
pub(crate) fn new<T>( pub(crate) fn new(
event_loop: &EventLoopWindowTarget<T>, event_loop: &EventLoopWindowTarget,
window_attributes: WindowAttributes, window_attributes: WindowAttributes,
platform_attributes: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, RootOsError> { ) -> Result<Window, RootOsError> {
let mtm = event_loop.mtm; let mtm = event_loop.mtm;
@@ -439,7 +414,7 @@ impl Window {
// TODO: transparency, visible // TODO: transparency, visible
let main_screen = UIScreen::main(mtm); let main_screen = UIScreen::main(mtm);
let fullscreen = window_attributes.fullscreen.0.clone().map(Into::into); let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
let screen = match fullscreen { let screen = match fullscreen {
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm), Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm),
Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm), Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm),
@@ -463,7 +438,7 @@ impl Window {
None => screen_bounds, None => screen_bounds,
}; };
let view = WinitView::new(mtm, &window_attributes, &platform_attributes, frame); let view = WinitView::new(mtm, &window_attributes, frame);
let gl_or_metal_backed = unsafe { let gl_or_metal_backed = unsafe {
let layer_class = WinitView::layerClass(); let layer_class = WinitView::layerClass();
@@ -472,15 +447,8 @@ impl Window {
is_metal || is_gl is_metal || is_gl
}; };
let view_controller = let view_controller = WinitViewController::new(mtm, &window_attributes, &view);
WinitViewController::new(mtm, &window_attributes, &platform_attributes, &view); let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
let window = WinitUIWindow::new(
mtm,
&window_attributes,
&platform_attributes,
frame,
&view_controller,
);
app_state::set_key_window(mtm, &window); app_state::set_key_window(mtm, &window);
@@ -547,6 +515,16 @@ impl Window {
Err(rwh_06::HandleError::Unavailable) 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(),
))
}
} }
// WindowExtIOS // WindowExtIOS
@@ -585,6 +563,18 @@ impl Inner {
self.view_controller self.view_controller
.set_preferred_status_bar_style(status_bar_style.into()); .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 { impl Inner {
@@ -687,7 +677,7 @@ impl From<&AnyObject> for WindowId {
} }
} }
#[derive(Clone, Default)] #[derive(Clone, Debug, Default)]
pub struct PlatformSpecificWindowBuilderAttributes { pub struct PlatformSpecificWindowBuilderAttributes {
pub scale_factor: Option<f64>, pub scale_factor: Option<f64>,
pub valid_orientations: ValidOrientations, 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. /// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses.
pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey { pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey {
scancode_to_keycode(keycode.saturating_sub(8)) scancode_to_physicalkey(keycode.saturating_sub(8))
} }
/// Map the linux scancode to Keycode. /// Map the linux scancode to Keycode.
/// ///
/// Both X11 and Wayland use keys with `+ 8` offset to linux scancode. /// Both X11 and Wayland use keys with `+ 8` offset to linux scancode.
pub fn scancode_to_keycode(scancode: u32) -> PhysicalKey { pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as // 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 // 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, // are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes,
@@ -504,7 +504,7 @@ pub fn keysym_to_key(keysym: u32) -> Key {
keysyms::KP_F4 => NamedKey::F4, keysyms::KP_F4 => NamedKey::F4,
keysyms::KP_Home => NamedKey::Home, keysyms::KP_Home => NamedKey::Home,
keysyms::KP_Left => NamedKey::ArrowLeft, keysyms::KP_Left => NamedKey::ArrowLeft,
keysyms::KP_Up => NamedKey::ArrowLeft, keysyms::KP_Up => NamedKey::ArrowUp,
keysyms::KP_Right => NamedKey::ArrowRight, keysyms::KP_Right => NamedKey::ArrowRight,
keysyms::KP_Down => NamedKey::ArrowDown, keysyms::KP_Down => NamedKey::ArrowDown,
// keysyms::KP_Prior => NamedKey::PageUp, // keysyms::KP_Prior => NamedKey::PageUp,

View File

@@ -398,8 +398,8 @@ impl KbdState {
let text_with_all_modifiers = event.text_with_all_modifiers(); let text_with_all_modifiers = event.text_with_all_modifiers();
let platform_specific = KeyEventExtra { let platform_specific = KeyEventExtra {
key_without_modifiers,
text_with_all_modifiers, text_with_all_modifiers,
key_without_modifiers,
}; };
KeyEvent { KeyEvent {
@@ -498,7 +498,7 @@ impl<'a> KeyEventResults<'a> {
} }
} }
fn physical_key(&mut self) -> PhysicalKey { fn physical_key(&self) -> PhysicalKey {
keymap::raw_keycode_to_physicalkey(self.keycode) keymap::raw_keycode_to_physicalkey(self.keycode)
} }
@@ -553,6 +553,7 @@ impl<'a> KeyEventResults<'a> {
} else { } else {
0 0
}; };
self.keysym_to_key(keysym) self.keysym_to_key(keysym)
.unwrap_or_else(|(key, location)| { .unwrap_or_else(|(key, location)| {
( (
@@ -565,7 +566,7 @@ impl<'a> KeyEventResults<'a> {
}) })
} }
fn keysym_to_key(&mut self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> { fn keysym_to_key(&self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> {
let location = super::keymap::keysym_location(keysym); let location = super::keymap::keysym_location(keysym);
let key = super::keymap::keysym_to_key(keysym); let key = super::keymap::keysym_to_key(keysym);
if matches!(key, Key::Unidentified(_)) { if matches!(key, Key::Unidentified(_)) {
@@ -682,7 +683,7 @@ fn byte_slice_to_smol_str(bytes: &[u8]) -> Option<SmolStr> {
std::str::from_utf8(bytes) std::str::from_utf8(bytes)
.map(SmolStr::new) .map(SmolStr::new)
.map_err(|e| { .map_err(|e| {
warn!( log::warn!(
"UTF-8 received from libxkbcommon ({:?}) was invalid: {e}", "UTF-8 received from libxkbcommon ({:?}) was invalid: {e}",
bytes bytes
) )

View File

@@ -15,41 +15,35 @@ use once_cell::sync::Lazy;
use smol_str::SmolStr; use smol_str::SmolStr;
#[cfg(x11_platform)] #[cfg(x11_platform)]
use crate::platform::x11::XlibErrorHook; use self::x11::{X11Error, XConnection, XError, XNotSupported};
#[cfg(x11_platform)]
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
use crate::{ use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError}, error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError},
event::KeyEvent,
event_loop::{ event_loop::{
AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
EventLoopWindowTarget as RootELW, EventLoopWindowTarget as RootELW,
}, },
icon::Icon, icon::Icon,
keyboard::{Key, PhysicalKey}, keyboard::Key,
platform::{ platform::pump_events::PumpStatus,
modifier_supplement::KeyEventExtModifierSupplement, pump_events::PumpStatus,
scancode::PhysicalKeyExtScancode,
},
window::{ window::{
ActivationToken, CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, ActivationToken, Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme,
UserAttentionType, WindowAttributes, WindowButtons, WindowLevel, 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 crate::cursor::OnlyCursorImage as PlatformCustomCursor; pub(crate) use self::common::keymap::{physicalkey_to_scancode, scancode_to_physicalkey};
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder; pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::RgbaIcon as PlatformIcon; pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; pub(crate) use crate::platform_impl::Fullscreen;
pub mod common; pub(crate) mod common;
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
pub mod wayland; pub(crate) mod wayland;
#[cfg(x11_platform)] #[cfg(x11_platform)]
pub mod x11; pub(crate) mod x11;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum Backend { pub(crate) enum Backend {
@@ -77,7 +71,7 @@ impl ApplicationName {
} }
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct PlatformSpecificWindowBuilderAttributes { pub struct PlatformSpecificWindowBuilderAttributes {
pub name: Option<ApplicationName>, pub name: Option<ApplicationName>,
pub activation_token: Option<ActivationToken>, pub activation_token: Option<ActivationToken>,
@@ -85,7 +79,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub x11: X11WindowBuilderAttributes, pub x11: X11WindowBuilderAttributes,
} }
#[derive(Clone)] #[derive(Clone, Debug)]
#[cfg(x11_platform)] #[cfg(x11_platform)]
pub struct X11WindowBuilderAttributes { pub struct X11WindowBuilderAttributes {
pub visual_id: Option<x11rb::protocol::xproto::Visualid>, pub visual_id: Option<x11rb::protocol::xproto::Visualid>,
@@ -141,43 +135,6 @@ impl fmt::Display for OsError {
} }
} }
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub(crate) enum OwnedWindowHandle {
#[cfg(x11_platform)]
X(x11rb::protocol::xproto::Window),
#[cfg(wayland_platform)]
Wayland,
}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(handle: rwh_06::WindowHandle<'_>) -> Self {
// TODO: Do we need to do something extra to extend the lifetime of
// the window lives beyond the passed-in handle?
match handle.as_raw() {
#[cfg(x11_platform)]
rwh_06::RawWindowHandle::Xlib(handle) => {
Self::X(handle.window as x11rb::protocol::xproto::Window)
}
#[cfg(x11_platform)]
rwh_06::RawWindowHandle::Xcb(handle) => Self::X(handle.window.get()),
#[cfg(wayland_platform)]
rwh_06::RawWindowHandle::Wayland(_handle) => {
// Wayland does not currently support parent windows, but it
// could support owned handles.
Self::Wayland
}
#[cfg(not(x11_platform))]
handle => panic!("invalid window handle {handle:?} on Wayland"),
#[cfg(not(wayland_platform))]
handle => panic!("invalid window handle {handle:?} on X11"),
#[cfg(all(x11_platform, wayland_platform))]
handle => panic!("invalid window handle {handle:?} on X11 or Wayland"),
}
}
}
pub(crate) enum Window { pub(crate) enum Window {
#[cfg(x11_platform)] #[cfg(x11_platform)]
X(x11::Window), X(x11::Window),
@@ -291,56 +248,55 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> { pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoModeHandle>> {
x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes())) x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes()))
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum VideoMode { pub enum VideoModeHandle {
#[cfg(x11_platform)] #[cfg(x11_platform)]
X(x11::VideoMode), X(x11::VideoModeHandle),
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
Wayland(wayland::VideoMode), Wayland(wayland::VideoModeHandle),
} }
impl VideoMode { impl VideoModeHandle {
#[inline] #[inline]
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
x11_or_wayland!(match self; VideoMode(m) => m.size()) x11_or_wayland!(match self; VideoModeHandle(m) => m.size())
} }
#[inline] #[inline]
pub fn bit_depth(&self) -> u16 { pub fn bit_depth(&self) -> u16 {
x11_or_wayland!(match self; VideoMode(m) => m.bit_depth()) x11_or_wayland!(match self; VideoModeHandle(m) => m.bit_depth())
} }
#[inline] #[inline]
pub fn refresh_rate_millihertz(&self) -> u32 { pub fn refresh_rate_millihertz(&self) -> u32 {
x11_or_wayland!(match self; VideoMode(m) => m.refresh_rate_millihertz()) x11_or_wayland!(match self; VideoModeHandle(m) => m.refresh_rate_millihertz())
} }
#[inline] #[inline]
pub fn monitor(&self) -> MonitorHandle { pub fn monitor(&self) -> MonitorHandle {
x11_or_wayland!(match self; VideoMode(m) => m.monitor(); as MonitorHandle) x11_or_wayland!(match self; VideoModeHandle(m) => m.monitor(); as MonitorHandle)
} }
} }
impl Window { impl Window {
#[inline] #[inline]
pub(crate) fn new<T>( pub(crate) fn new(
window_target: &EventLoopWindowTarget<T>, window_target: &EventLoopWindowTarget,
attribs: WindowAttributes, attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> { ) -> Result<Self, RootOsError> {
match *window_target { match *window_target {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
EventLoopWindowTarget::Wayland(ref window_target) => { EventLoopWindowTarget::Wayland(ref window_target) => {
wayland::Window::new(window_target, attribs, pl_attribs).map(Window::Wayland) wayland::Window::new(window_target, attribs).map(Window::Wayland)
} }
#[cfg(x11_platform)] #[cfg(x11_platform)]
EventLoopWindowTarget::X(ref window_target) => { EventLoopWindowTarget::X(ref window_target) => {
x11::Window::new(window_target, attribs, pl_attribs).map(Window::X) x11::Window::new(window_target, attribs).map(Window::X)
} }
} }
} }
@@ -459,13 +415,8 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) { pub fn set_cursor(&self, cursor: Cursor) {
x11_or_wayland!(match self; Window(w) => w.set_cursor_icon(cursor)) x11_or_wayland!(match self; Window(w) => w.set_cursor(cursor))
}
#[inline]
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
x11_or_wayland!(match self; Window(w) => w.set_custom_cursor(cursor))
} }
#[inline] #[inline]
@@ -682,32 +633,30 @@ impl Window {
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeyEventExtra { pub struct KeyEventExtra {
pub key_without_modifiers: Key,
pub text_with_all_modifiers: Option<SmolStr>, pub text_with_all_modifiers: Option<SmolStr>,
pub key_without_modifiers: Key,
} }
impl KeyEventExtModifierSupplement for KeyEvent { #[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[inline] pub(crate) enum PlatformCustomCursor {
fn text_with_all_modifiers(&self) -> Option<&str> { #[cfg(wayland_platform)]
self.platform_specific Wayland(wayland::CustomCursor),
.text_with_all_modifiers #[cfg(x11_platform)]
.as_ref() X(x11::CustomCursor),
.map(|s| s.as_str())
}
#[inline]
fn key_without_modifiers(&self) -> Key {
self.platform_specific.key_without_modifiers.clone()
}
} }
impl PlatformCustomCursor {
impl PhysicalKeyExtScancode for PhysicalKey { pub(crate) fn build(
fn from_scancode(scancode: u32) -> PhysicalKey { builder: PlatformCustomCursorBuilder,
common::keymap::scancode_to_keycode(scancode) p: &EventLoopWindowTarget,
} ) -> PlatformCustomCursor {
match p {
fn to_scancode(self) -> Option<u32> { #[cfg(wayland_platform)]
common::keymap::physicalkey_to_scancode(self) EventLoopWindowTarget::Wayland(_) => {
Self::Wayland(wayland::CustomCursor::build(builder, p))
}
#[cfg(x11_platform)]
EventLoopWindowTarget::X(p) => Self::X(x11::CustomCursor::build(builder, p)),
}
} }
} }
@@ -754,7 +703,7 @@ unsafe extern "C" fn x_error_callback(
// Don't log error. // Don't log error.
if !error_handled { if !error_handled {
error!("X11 error: {:#?}", error); log::error!("X11 error: {:#?}", error);
// XXX only update the error, if it wasn't handled by any of the hooks. // XXX only update the error, if it wasn't handled by any of the hooks.
*xconn.latest_error.lock().unwrap() = Some(error); *xconn.latest_error.lock().unwrap() = Some(error);
} }
@@ -801,8 +750,11 @@ impl<T: 'static> EventLoop<T> {
let backend = match ( let backend = match (
attributes.forced_backend, attributes.forced_backend,
env::var("WAYLAND_DISPLAY") env::var("WAYLAND_DISPLAY")
.map(|var| !var.is_empty()) .ok()
.unwrap_or(false), .filter(|var| !var.is_empty())
.or_else(|| env::var("WAYLAND_SOCKET").ok())
.filter(|var| !var.is_empty())
.is_some(),
env::var("DISPLAY") env::var("DISPLAY")
.map(|var| !var.is_empty()) .map(|var| !var.is_empty())
.unwrap_or(false), .unwrap_or(false),
@@ -816,10 +768,15 @@ impl<T: 'static> EventLoop<T> {
#[cfg(x11_platform)] #[cfg(x11_platform)]
(None, _, true) => Backend::X, (None, _, true) => Backend::X,
// No backend is present. // No backend is present.
_ => { (_, wayland_display, x11_display) => {
return Err(EventLoopError::Os(os_error!(OsError::Misc( let msg = if wayland_display && !cfg!(wayland_platform) {
"neither WAYLAND_DISPLAY nor DISPLAY is set." "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))));
} }
}; };
@@ -828,7 +785,7 @@ impl<T: 'static> EventLoop<T> {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
Backend::Wayland => EventLoop::new_wayland_any_thread().map_err(Into::into), Backend::Wayland => EventLoop::new_wayland_any_thread().map_err(Into::into),
#[cfg(x11_platform)] #[cfg(x11_platform)]
Backend::X => Ok(EventLoop::new_x11_any_thread().unwrap()), Backend::X => EventLoop::new_x11_any_thread().map_err(Into::into),
} }
} }
@@ -838,10 +795,10 @@ impl<T: 'static> EventLoop<T> {
} }
#[cfg(x11_platform)] #[cfg(x11_platform)]
fn new_x11_any_thread() -> Result<EventLoop<T>, XNotSupported> { fn new_x11_any_thread() -> Result<EventLoop<T>, EventLoopError> {
let xconn = match X11_BACKEND.lock().unwrap().as_ref() { let xconn = match X11_BACKEND.lock().unwrap().as_ref() {
Ok(xconn) => xconn.clone(), Ok(xconn) => xconn.clone(),
Err(err) => return Err(err.clone()), Err(_) => return Err(EventLoopError::NotSupported(NotSupportedError::new())),
}; };
Ok(EventLoop::X(x11::EventLoop::new(xconn))) Ok(EventLoop::X(x11::EventLoop::new(xconn)))
@@ -853,26 +810,26 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError> pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
where where
F: FnMut(crate::event::Event<T>, &RootELW<T>), F: FnMut(crate::event::Event<T>, &RootELW),
{ {
self.run_on_demand(callback) self.run_on_demand(callback)
} }
pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError> pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
where where
F: FnMut(crate::event::Event<T>, &RootELW<T>), F: FnMut(crate::event::Event<T>, &RootELW),
{ {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(callback)) 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 pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
where where
F: FnMut(crate::event::Event<T>, &RootELW<T>), F: FnMut(crate::event::Event<T>, &RootELW),
{ {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback)) x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
} }
pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget<T> { pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target()) x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
} }
} }
@@ -895,14 +852,14 @@ impl<T: 'static> EventLoopProxy<T> {
} }
} }
pub enum EventLoopWindowTarget<T> { pub enum EventLoopWindowTarget {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
Wayland(wayland::EventLoopWindowTarget<T>), Wayland(wayland::EventLoopWindowTarget),
#[cfg(x11_platform)] #[cfg(x11_platform)]
X(x11::EventLoopWindowTarget<T>), X(x11::EventLoopWindowTarget),
} }
impl<T> EventLoopWindowTarget<T> { impl EventLoopWindowTarget {
#[inline] #[inline]
pub fn is_wayland(&self) -> bool { pub fn is_wayland(&self) -> bool {
match *self { match *self {
@@ -962,6 +919,10 @@ impl<T> EventLoopWindowTarget<T> {
x11_or_wayland!(match self; Self(evlp) => evlp.control_flow()) x11_or_wayland!(match self; Self(evlp) => evlp.control_flow())
} }
pub(crate) fn clear_exit(&self) {
x11_or_wayland!(match self; Self(evlp) => evlp.clear_exit())
}
pub(crate) fn exit(&self) { pub(crate) fn exit(&self) {
x11_or_wayland!(match self; Self(evlp) => evlp.exit()) x11_or_wayland!(match self; Self(evlp) => evlp.exit())
} }
@@ -970,15 +931,87 @@ impl<T> EventLoopWindowTarget<T> {
x11_or_wayland!(match self; Self(evlp) => evlp.exiting()) 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()),
}
}
#[allow(dead_code)]
fn set_exit_code(&self, code: i32) { fn set_exit_code(&self, code: i32) {
x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code)) x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code))
} }
#[allow(dead_code)]
fn exit_code(&self) -> Option<i32> { fn exit_code(&self) -> Option<i32> {
x11_or_wayland!(match self; Self(evlp) => evlp.exit_code()) x11_or_wayland!(match self; Self(evlp) => evlp.exit_code())
} }
} }
#[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` /// Returns the minimum `Option<Duration>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use /// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`) /// `Option::min`)

View File

@@ -16,7 +16,7 @@ use sctk::reexports::calloop_wayland_source::WaylandSource;
use sctk::reexports::client::globals; use sctk::reexports::client::globals;
use sctk::reexports::client::{Connection, QueueHandle}; use sctk::reexports::client::{Connection, QueueHandle};
use crate::dpi::{LogicalSize, PhysicalSize}; use crate::dpi::LogicalSize;
use crate::error::{EventLoopError, OsError as RootOsError}; use crate::error::{EventLoopError, OsError as RootOsError};
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent}; use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
use crate::event_loop::{ use crate::event_loop::{
@@ -34,7 +34,7 @@ use sink::EventSink;
use super::state::{WindowCompositorUpdate, WinitState}; use super::state::{WindowCompositorUpdate, WinitState};
use super::window::state::FrameCallbackState; use super::window::state::FrameCallbackState;
use super::{DeviceId, WaylandError, WindowId}; use super::{logical_to_physical_rounded, DeviceId, WaylandError, WindowId};
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>; type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
@@ -63,7 +63,7 @@ pub struct EventLoop<T: 'static> {
connection: Connection, connection: Connection,
/// Event loop window target. /// Event loop window target.
window_target: RootEventLoopWindowTarget<T>, window_target: RootEventLoopWindowTarget,
// XXX drop after everything else, just to be safe. // XXX drop after everything else, just to be safe.
/// Calloop's event loop. /// Calloop's event loop.
@@ -167,7 +167,6 @@ impl<T: 'static> EventLoop<T> {
control_flow: Cell::new(ControlFlow::default()), control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None), exit: Cell::new(None),
state: RefCell::new(winit_state), state: RefCell::new(winit_state),
_marker: PhantomData,
}; };
let event_loop = Self { let event_loop = Self {
@@ -191,12 +190,8 @@ impl<T: 'static> EventLoop<T> {
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>), F: FnMut(Event<T>, &RootEventLoopWindowTarget),
{ {
if self.loop_running {
return Err(EventLoopError::AlreadyRunning);
}
let exit = loop { let exit = loop {
match self.pump_events(None, &mut event_handler) { match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
@@ -222,7 +217,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>), F: FnMut(Event<T>, &RootEventLoopWindowTarget),
{ {
if !self.loop_running { if !self.loop_running {
self.loop_running = true; self.loop_running = true;
@@ -249,7 +244,7 @@ impl<T: 'static> EventLoop<T> {
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>), F: FnMut(Event<T>, &RootEventLoopWindowTarget),
{ {
let cause = loop { let cause = loop {
let start = Instant::now(); let start = Instant::now();
@@ -325,7 +320,7 @@ impl<T: 'static> EventLoop<T> {
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause) fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>), F: FnMut(Event<T>, &RootEventLoopWindowTarget),
{ {
// NOTE currently just indented to simplify the diff // NOTE currently just indented to simplify the diff
@@ -356,15 +351,13 @@ impl<T: 'static> EventLoop<T> {
for mut compositor_update in compositor_updates.drain(..) { for mut compositor_update in compositor_updates.drain(..) {
let window_id = compositor_update.window_id; let window_id = compositor_update.window_id;
if let Some(scale_factor) = compositor_update.scale_factor { if compositor_update.scale_changed {
let physical_size = self.with_state(|state| { let (physical_size, scale_factor) = self.with_state(|state| {
let windows = state.windows.get_mut(); let windows = state.windows.get_mut();
let mut window = windows.get(&window_id).unwrap().lock().unwrap(); let window = windows.get(&window_id).unwrap().lock().unwrap();
let scale_factor = window.scale_factor();
// Set the new scale factor. let size = logical_to_physical_rounded(window.inner_size(), scale_factor);
window.set_scale_factor(scale_factor); (size, scale_factor)
let window_size = compositor_update.size.unwrap_or(window.inner_size());
logical_to_physical_rounded(window_size, scale_factor)
}); });
// Stash the old window size. // Stash the old window size.
@@ -386,30 +379,32 @@ impl<T: 'static> EventLoop<T> {
let physical_size = *new_inner_size.lock().unwrap(); let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size); drop(new_inner_size);
let new_logical_size = physical_size.to_logical(scale_factor);
// Resize the window when user altered the size. // Resize the window when user altered the size.
if old_physical_size != physical_size { if old_physical_size != physical_size {
self.with_state(|state| { self.with_state(|state| {
let windows = state.windows.get_mut(); let windows = state.windows.get_mut();
let mut window = windows.get(&window_id).unwrap().lock().unwrap(); let mut window = windows.get(&window_id).unwrap().lock().unwrap();
let new_logical_size: LogicalSize<f64> =
physical_size.to_logical(scale_factor);
window.request_inner_size(new_logical_size.into()); window.request_inner_size(new_logical_size.into());
}); });
}
// Make it queue resize. // Make it queue resize.
compositor_update.size = Some(new_logical_size); compositor_update.resized = true;
}
} }
if let Some(size) = compositor_update.size.take() { // NOTE: Rescale changed the physical size which winit operates in, thus we should
// resize.
if compositor_update.resized || compositor_update.scale_changed {
let physical_size = self.with_state(|state| { let physical_size = self.with_state(|state| {
let windows = state.windows.get_mut(); let windows = state.windows.get_mut();
let window = windows.get(&window_id).unwrap().lock().unwrap(); let window = windows.get(&window_id).unwrap().lock().unwrap();
let scale_factor = window.scale_factor(); let scale_factor = window.scale_factor();
let physical_size = logical_to_physical_rounded(size, scale_factor); let size = logical_to_physical_rounded(window.inner_size(), scale_factor);
// TODO could probably bring back size reporting optimization.
// Mark the window as needed a redraw. // Mark the window as needed a redraw.
state state
@@ -420,7 +415,7 @@ impl<T: 'static> EventLoop<T> {
.redraw_requested .redraw_requested
.store(true, Ordering::Relaxed); .store(true, Ordering::Relaxed);
physical_size size
}); });
callback( callback(
@@ -466,45 +461,45 @@ impl<T: 'static> EventLoop<T> {
window_ids.extend(state.window_requests.get_mut().keys()); window_ids.extend(state.window_requests.get_mut().keys());
}); });
for window_id in window_ids.drain(..) { for window_id in window_ids.iter() {
let request_redraw = self.with_state(|state| { let event = self.with_state(|state| {
let window_requests = state.window_requests.get_mut(); let window_requests = state.window_requests.get_mut();
if window_requests.get(&window_id).unwrap().take_closed() { if window_requests.get(window_id).unwrap().take_closed() {
mem::drop(window_requests.remove(&window_id)); mem::drop(window_requests.remove(window_id));
mem::drop(state.windows.get_mut().remove(&window_id)); mem::drop(state.windows.get_mut().remove(window_id));
false return Some(WindowEvent::Destroyed);
} else {
let mut window = state
.windows
.get_mut()
.get_mut(&window_id)
.unwrap()
.lock()
.unwrap();
if window.frame_callback_state() == FrameCallbackState::Requested {
false
} else {
// Reset the frame callbacks state.
window.frame_callback_reset();
let mut redraw_requested = window_requests
.get(&window_id)
.unwrap()
.take_redraw_requested();
// Redraw the frame while at it.
redraw_requested |= window.refresh_frame();
redraw_requested
}
} }
let mut window = state
.windows
.get_mut()
.get_mut(window_id)
.unwrap()
.lock()
.unwrap();
if window.frame_callback_state() == FrameCallbackState::Requested {
return None;
}
// Reset the frame callbacks state.
window.frame_callback_reset();
let mut redraw_requested = window_requests
.get(window_id)
.unwrap()
.take_redraw_requested();
// Redraw the frame while at it.
redraw_requested |= window.refresh_frame();
redraw_requested.then_some(WindowEvent::RedrawRequested)
}); });
if request_redraw { if let Some(event) = event {
callback( callback(
Event::WindowEvent { Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(*window_id),
event: WindowEvent::RedrawRequested, event,
}, },
&self.window_target, &self.window_target,
); );
@@ -519,6 +514,42 @@ impl<T: 'static> EventLoop<T> {
// This is always the last event we dispatch before poll again // This is always the last event we dispatch before poll again
callback(Event::AboutToWait, &self.window_target); callback(Event::AboutToWait, &self.window_target);
// Update the window frames and schedule redraws.
let mut wake_up = false;
for window_id in window_ids.drain(..) {
wake_up |= self.with_state(|state| match state.windows.get_mut().get_mut(&window_id) {
Some(window) => {
let refresh = window.lock().unwrap().refresh_frame();
if refresh {
state
.window_requests
.get_mut()
.get_mut(&window_id)
.unwrap()
.redraw_requested
.store(true, Ordering::Relaxed);
}
refresh
}
None => false,
});
}
// Wakeup event loop if needed.
//
// If the user draws from the `AboutToWait` this is likely not required, however
// we can't do much about it.
if wake_up {
match &self.window_target.p {
PlatformEventLoopWindowTarget::Wayland(window_target) => {
window_target.event_loop_awakener.ping();
}
#[cfg(x11_platform)]
PlatformEventLoopWindowTarget::X(_) => unreachable!(),
}
}
std::mem::swap(&mut self.compositor_updates, &mut compositor_updates); std::mem::swap(&mut self.compositor_updates, &mut compositor_updates);
std::mem::swap(&mut self.buffer_sink, &mut buffer_sink); std::mem::swap(&mut self.buffer_sink, &mut buffer_sink);
std::mem::swap(&mut self.window_ids, &mut window_ids); std::mem::swap(&mut self.window_ids, &mut window_ids);
@@ -530,7 +561,7 @@ impl<T: 'static> EventLoop<T> {
} }
#[inline] #[inline]
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> { pub fn window_target(&self) -> &RootEventLoopWindowTarget {
&self.window_target &self.window_target
} }
@@ -552,7 +583,7 @@ impl<T: 'static> EventLoop<T> {
}; };
self.event_loop.dispatch(timeout, state).map_err(|error| { self.event_loop.dispatch(timeout, state).map_err(|error| {
error!("Error dispatching event loop: {}", error); log::error!("Error dispatching event loop: {}", error);
error.into() error.into()
}) })
} }
@@ -602,7 +633,7 @@ impl<T> AsRawFd for EventLoop<T> {
} }
} }
pub struct EventLoopWindowTarget<T> { pub struct EventLoopWindowTarget {
/// The event loop wakeup source. /// The event loop wakeup source.
pub event_loop_awakener: calloop::ping::Ping, pub event_loop_awakener: calloop::ping::Ping,
@@ -624,11 +655,37 @@ pub struct EventLoopWindowTarget<T> {
/// Connection to the wayland server. /// Connection to the wayland server.
pub connection: Connection, pub connection: Connection,
_marker: std::marker::PhantomData<T>,
} }
impl<T> EventLoopWindowTarget<T> { impl EventLoopWindowTarget {
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow)
}
pub(crate) fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}
pub(crate) fn exit(&self) {
self.exit.set(Some(0))
}
pub(crate) fn clear_exit(&self) {
self.exit.set(None)
}
pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}
pub(crate) fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}
pub(crate) fn exit_code(&self) -> Option<i32> {
self.exit.get()
}
#[inline] #[inline]
pub fn listen_device_events(&self, _allowed: DeviceEvents) {} pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
@@ -656,10 +713,3 @@ impl<T> EventLoopWindowTarget<T> {
.into()) .into())
} }
} }
// The default routine does floor, but we need round on Wayland.
fn logical_to_physical_rounded(size: LogicalSize<u32>, scale_factor: f64) -> PhysicalSize<u32> {
let width = size.width as f64 * scale_factor;
let height = size.height as f64 * scale_factor;
(width.round(), height.round()).into()
}

View File

@@ -9,9 +9,11 @@ use sctk::reexports::client::globals::{BindError, GlobalError};
use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{self, ConnectError, DispatchError, Proxy}; use sctk::reexports::client::{self, ConnectError, DispatchError, Proxy};
pub(super) use crate::cursor::OnlyCursorImage as CustomCursor;
use crate::dpi::{LogicalSize, PhysicalSize};
pub use crate::platform_impl::platform::{OsError, WindowId}; pub use crate::platform_impl::platform::{OsError, WindowId};
pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
pub use output::{MonitorHandle, VideoMode}; pub use output::{MonitorHandle, VideoModeHandle};
pub use window::Window; pub use window::Window;
mod event_loop; mod event_loop;
@@ -76,3 +78,10 @@ impl DeviceId {
fn make_wid(surface: &WlSurface) -> WindowId { fn make_wid(surface: &WlSurface) -> WindowId {
WindowId(surface.id().as_ptr() as u64) WindowId(surface.id().as_ptr() as u64)
} }
/// The default routine does floor, but we need round on Wayland.
fn logical_to_physical_rounded(size: LogicalSize<u32>, scale_factor: f64) -> PhysicalSize<u32> {
let width = size.width as f64 * scale_factor;
let height = size.height as f64 * scale_factor;
(width.round(), height.round()).into()
}

View File

@@ -4,12 +4,11 @@ use sctk::reexports::client::Proxy;
use sctk::output::OutputData; use sctk::output::OutputData;
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::event_loop::ControlFlow; use crate::platform_impl::platform::VideoModeHandle as PlatformVideoModeHandle;
use crate::platform_impl::platform::VideoMode as PlatformVideoMode;
use super::event_loop::EventLoopWindowTarget; use super::event_loop::EventLoopWindowTarget;
impl<T> EventLoopWindowTarget<T> { impl EventLoopWindowTarget {
#[inline] #[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
self.state self.state
@@ -24,30 +23,6 @@ impl<T> EventLoopWindowTarget<T> {
// There's no primary monitor on Wayland. // There's no primary monitor on Wayland.
None None
} }
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow)
}
pub(crate) fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}
pub(crate) fn exit(&self) {
self.exit.set(Some(0))
}
pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}
pub(crate) fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}
pub(crate) fn exit_code(&self) -> Option<i32> {
self.exit.get()
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -123,14 +98,14 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn video_modes(&self) -> impl Iterator<Item = PlatformVideoMode> { pub fn video_modes(&self) -> impl Iterator<Item = PlatformVideoModeHandle> {
let output_data = self.proxy.data::<OutputData>().unwrap(); let output_data = self.proxy.data::<OutputData>().unwrap();
let modes = output_data.with_output_info(|info| info.modes.clone()); let modes = output_data.with_output_info(|info| info.modes.clone());
let monitor = self.clone(); let monitor = self.clone();
modes.into_iter().map(move |mode| { modes.into_iter().map(move |mode| {
PlatformVideoMode::Wayland(VideoMode { PlatformVideoModeHandle::Wayland(VideoModeHandle {
size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(), size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(),
refresh_rate_millihertz: mode.refresh_rate as u32, refresh_rate_millihertz: mode.refresh_rate as u32,
bit_depth: 32, bit_depth: 32,
@@ -167,14 +142,14 @@ impl std::hash::Hash for MonitorHandle {
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VideoMode { pub struct VideoModeHandle {
pub(crate) size: PhysicalSize<u32>, pub(crate) size: PhysicalSize<u32>,
pub(crate) bit_depth: u16, pub(crate) bit_depth: u16,
pub(crate) refresh_rate_millihertz: u32, pub(crate) refresh_rate_millihertz: u32,
pub(crate) monitor: MonitorHandle, pub(crate) monitor: MonitorHandle,
} }
impl VideoMode { impl VideoModeHandle {
#[inline] #[inline]
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
self.size self.size

View File

@@ -61,8 +61,13 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
let window_id = wayland::make_wid(&surface); let window_id = wayland::make_wid(&surface);
// Mark the window as focused. // Mark the window as focused.
match state.windows.get_mut().get(&window_id) { let was_unfocused = match state.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().set_has_focus(true), Some(window) => {
let mut window = window.lock().unwrap();
let was_unfocused = !window.has_focus();
window.add_seat_focus(data.seat.id());
was_unfocused
}
None => return, None => return,
}; };
@@ -73,13 +78,15 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
keyboard_state.loop_handle.remove(token); keyboard_state.loop_handle.remove(token);
} }
// The keyboard focus is considered as general focus.
state
.events_sink
.push_window_event(WindowEvent::Focused(true), window_id);
*data.window_id.lock().unwrap() = Some(window_id); *data.window_id.lock().unwrap() = Some(window_id);
// The keyboard focus is considered as general focus.
if was_unfocused {
state
.events_sink
.push_window_event(WindowEvent::Focused(true), window_id);
}
// HACK: this is just for GNOME not fixing their ordering issue of modifiers. // HACK: this is just for GNOME not fixing their ordering issue of modifiers.
if std::mem::take(&mut seat_state.modifiers_pending) { if std::mem::take(&mut seat_state.modifiers_pending) {
state.events_sink.push_window_event( state.events_sink.push_window_event(
@@ -101,24 +108,30 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
// NOTE: The check whether the window exists is essential as we might get a // NOTE: The check whether the window exists is essential as we might get a
// nil surface, regardless of what protocol says. // nil surface, regardless of what protocol says.
match state.windows.get_mut().get(&window_id) { let focused = match state.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().set_has_focus(false), Some(window) => {
let mut window = window.lock().unwrap();
window.remove_seat_focus(&data.seat.id());
window.has_focus()
}
None => return, None => return,
}; };
// Notify that no modifiers are being pressed.
state.events_sink.push_window_event(
WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
window_id,
);
// We don't need to update it above, because the next `Enter` will overwrite // We don't need to update it above, because the next `Enter` will overwrite
// anyway. // anyway.
*data.window_id.lock().unwrap() = None; *data.window_id.lock().unwrap() = None;
state if !focused {
.events_sink // Notify that no modifiers are being pressed.
.push_window_event(WindowEvent::Focused(false), window_id); state.events_sink.push_window_event(
WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
window_id,
);
state
.events_sink
.push_window_event(WindowEvent::Focused(false), window_id);
}
} }
WlKeyboardEvent::Key { WlKeyboardEvent::Key {
key, key,

View File

@@ -4,6 +4,7 @@ use std::sync::Arc;
use ahash::AHashMap; use ahash::AHashMap;
use sctk::reexports::client::backend::ObjectId;
use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_touch::WlTouch; use sctk::reexports::client::protocol::wl_touch::WlTouch;
use sctk::reexports::client::{Connection, Proxy, QueueHandle}; use sctk::reexports::client::{Connection, Proxy, QueueHandle};
@@ -13,6 +14,7 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
use sctk::seat::pointer::{ThemeSpec, ThemedPointer}; use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState}; use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
use crate::event::WindowEvent;
use crate::keyboard::ModifiersState; use crate::keyboard::ModifiersState;
use crate::platform_impl::wayland::state::WinitState; use crate::platform_impl::wayland::state::WinitState;
@@ -143,6 +145,10 @@ impl SeatHandler for WinitState {
) { ) {
let seat_state = self.seats.get_mut(&seat.id()).unwrap(); let seat_state = self.seats.get_mut(&seat.id()).unwrap();
if let Some(text_input) = seat_state.text_input.take() {
text_input.destroy();
}
match capability { match capability {
SeatCapability::Touch => { SeatCapability::Touch => {
if let Some(touch) = seat_state.touch.take() { if let Some(touch) = seat_state.touch.take() {
@@ -174,13 +180,10 @@ impl SeatHandler for WinitState {
} }
SeatCapability::Keyboard => { SeatCapability::Keyboard => {
seat_state.keyboard_state = None; seat_state.keyboard_state = None;
self.on_keyboard_destroy(&seat.id());
} }
_ => (), _ => (),
} }
if let Some(text_input) = seat_state.text_input.take() {
text_input.destroy();
}
} }
fn new_seat( fn new_seat(
@@ -199,6 +202,21 @@ impl SeatHandler for WinitState {
seat: WlSeat, seat: WlSeat,
) { ) {
let _ = self.seats.remove(&seat.id()); let _ = self.seats.remove(&seat.id());
self.on_keyboard_destroy(&seat.id());
}
}
impl WinitState {
fn on_keyboard_destroy(&mut self, seat: &ObjectId) {
for (window_id, window) in self.windows.get_mut() {
let mut window = window.lock().unwrap();
let had_focus = window.has_focus();
window.remove_seat_focus(seat);
if had_focus != window.has_focus() {
self.events_sink
.push_window_event(WindowEvent::Focused(false), *window_id);
}
}
} }
} }

View File

@@ -23,21 +23,19 @@ use sctk::shm::slot::SlotPool;
use sctk::shm::{Shm, ShmHandler}; use sctk::shm::{Shm, ShmHandler};
use sctk::subcompositor::SubcompositorState; use sctk::subcompositor::SubcompositorState;
use crate::dpi::LogicalSize; use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::OsError; use crate::platform_impl::wayland::output::MonitorHandle;
use crate::platform_impl::wayland::seat::{
use super::event_loop::sink::EventSink;
use super::output::MonitorHandle;
use super::seat::{
PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData, PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData,
WinitPointerDataExt, WinitSeatState, WinitPointerDataExt, WinitSeatState,
}; };
use super::types::kwin_blur::KWinBlurManager; use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use super::types::wp_fractional_scaling::FractionalScalingManager; use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScalingManager;
use super::types::wp_viewporter::ViewporterState; use crate::platform_impl::wayland::types::wp_viewporter::ViewporterState;
use super::types::xdg_activation::XdgActivationState; use crate::platform_impl::wayland::types::xdg_activation::XdgActivationState;
use super::window::{WindowRequests, WindowState}; use crate::platform_impl::wayland::window::{WindowRequests, WindowState};
use super::{WaylandError, WindowId}; use crate::platform_impl::wayland::{WaylandError, WindowId};
use crate::platform_impl::OsError;
/// Winit's Wayland state. /// Winit's Wayland state.
pub struct WinitState { pub struct WinitState {
@@ -135,7 +133,7 @@ impl WinitState {
) { ) {
Ok(c) => Some(c), Ok(c) => Some(c),
Err(e) => { Err(e) => {
warn!("Subcompositor protocol not available, ignoring CSD: {e:?}"); log::warn!("Subcompositor protocol not available, ignoring CSD: {e:?}");
None None
} }
}; };
@@ -227,7 +225,7 @@ impl WinitState {
// Update the scale factor right away. // Update the scale factor right away.
window.lock().unwrap().set_scale_factor(scale_factor); window.lock().unwrap().set_scale_factor(scale_factor);
self.window_compositor_updates[pos].scale_factor = Some(scale_factor); self.window_compositor_updates[pos].scale_changed = true;
} else if let Some(pointer) = self.pointer_surfaces.get(&surface.id()) { } else if let Some(pointer) = self.pointer_surfaces.get(&surface.id()) {
// Get the window, where the pointer resides right now. // Get the window, where the pointer resides right now.
let focused_window = match pointer.pointer().winit_data().focused_window() { let focused_window = match pointer.pointer().winit_data().focused_window() {
@@ -291,27 +289,26 @@ impl WindowHandler for WinitState {
}; };
// Populate the configure to the window. // Populate the configure to the window.
// self.window_compositor_updates[pos].resized |= self
// XXX the size on the window will be updated right before dispatching the size to the user.
let new_size = self
.windows .windows
.get_mut() .get_mut()
.get_mut(&window_id) .get_mut(&window_id)
.expect("got configure for dead window.") .expect("got configure for dead window.")
.lock() .lock()
.unwrap() .unwrap()
.configure( .configure(configure, &self.shm, &self.subcompositor_state);
configure,
&self.shm,
&self.subcompositor_state,
&mut self.events_sink,
);
// NOTE: Only update when the value is `Some` to not override consequent configures with // NOTE: configure demands wl_surface::commit, however winit doesn't commit on behalf of the
// the same sizes. // users, since it can break a lot of things, thus it'll ask users to redraw instead.
if new_size.is_some() { self.window_requests
self.window_compositor_updates[pos].size = new_size; .get_mut()
} .get(&window_id)
.unwrap()
.redraw_requested
.store(true, Ordering::Relaxed);
// Manually mark that we've got an event, since configure may not generate a resize.
self.dispatched_events = true;
} }
} }
@@ -405,10 +402,10 @@ pub struct WindowCompositorUpdate {
pub window_id: WindowId, pub window_id: WindowId,
/// New window size. /// New window size.
pub size: Option<LogicalSize<u32>>, pub resized: bool,
/// New scale factor. /// New scale factor.
pub scale_factor: Option<f64>, pub scale_changed: bool,
/// Close the window. /// Close the window.
pub close_window: bool, pub close_window: bool,
@@ -418,8 +415,8 @@ impl WindowCompositorUpdate {
fn new(window_id: WindowId) -> Self { fn new(window_id: WindowId) -> Self {
Self { Self {
window_id, window_id,
size: None, resized: false,
scale_factor: None, scale_changed: false,
close_window: false, close_window: false,
} }
} }

View File

@@ -37,12 +37,15 @@ impl CustomCursor {
) )
.unwrap(); .unwrap();
for (canvas_chunk, rgba_chunk) in canvas.chunks_exact_mut(4).zip(image.rgba.chunks_exact(4)) for (canvas_chunk, rgba) in canvas.chunks_exact_mut(4).zip(image.rgba.chunks_exact(4)) {
{ // Alpha in buffer is premultiplied.
canvas_chunk[0] = rgba_chunk[2]; let alpha = rgba[3] as f32 / 255.;
canvas_chunk[1] = rgba_chunk[1]; let r = (rgba[0] as f32 * alpha) as u32;
canvas_chunk[2] = rgba_chunk[0]; let g = (rgba[1] as f32 * alpha) as u32;
canvas_chunk[3] = rgba_chunk[3]; let b = (rgba[2] as f32 * alpha) as u32;
let color = ((rgba[3] as u32) << 24) + (r << 16) + (g << 8) + b;
let array: &mut [u8; 4] = canvas_chunk.try_into().unwrap();
*array = color.to_le_bytes();
} }
CustomCursor { CustomCursor {

View File

@@ -15,16 +15,17 @@ use sctk::shell::xdg::window::Window as SctkWindow;
use sctk::shell::xdg::window::WindowDecorations; use sctk::shell::xdg::window::WindowDecorations;
use sctk::shell::WaylandSurface; use sctk::shell::WaylandSurface;
use log::warn;
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError}; use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
use crate::event::{Ime, WindowEvent}; use crate::event::{Ime, WindowEvent};
use crate::event_loop::AsyncRequestSerial; use crate::event_loop::AsyncRequestSerial;
use crate::platform_impl::{ use crate::platform_impl::{
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor, Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
PlatformIcon, PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
}; };
use crate::window::{ use crate::window::{
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType, Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowLevel, WindowAttributes, WindowButtons, WindowLevel,
}; };
@@ -79,10 +80,9 @@ pub struct Window {
} }
impl Window { impl Window {
pub(crate) fn new<T>( pub(crate) fn new(
event_loop_window_target: &EventLoopWindowTarget<T>, event_loop_window_target: &EventLoopWindowTarget,
attributes: WindowAttributes, attributes: WindowAttributes,
platform_attributes: PlatformAttributes,
) -> Result<Self, RootOsError> { ) -> Result<Self, RootOsError> {
let queue_handle = event_loop_window_target.queue_handle.clone(); let queue_handle = event_loop_window_target.queue_handle.clone();
let mut state = event_loop_window_target.state.borrow_mut(); let mut state = event_loop_window_target.state.borrow_mut();
@@ -132,7 +132,7 @@ impl Window {
window_state.set_decorate(attributes.decorations); window_state.set_decorate(attributes.decorations);
// Set the app_id. // Set the app_id.
if let Some(name) = platform_attributes.name.map(|name| name.general) { if let Some(name) = attributes.platform_specific.name.map(|name| name.general) {
window.set_app_id(name); window.set_app_id(name);
} }
@@ -150,7 +150,7 @@ impl Window {
window_state.set_resizable(attributes.resizable); window_state.set_resizable(attributes.resizable);
// Set startup mode. // Set startup mode.
match attributes.fullscreen.0.map(Into::into) { match attributes.fullscreen.map(Into::into) {
Some(Fullscreen::Exclusive(_)) => { Some(Fullscreen::Exclusive(_)) => {
warn!("`Fullscreen::Exclusive` is ignored on Wayland"); warn!("`Fullscreen::Exclusive` is ignored on Wayland");
} }
@@ -167,10 +167,15 @@ impl Window {
_ => (), _ => (),
}; };
match attributes.cursor {
Cursor::Icon(icon) => window_state.set_cursor(icon),
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
}
// Activate the window when the token is passed. // Activate the window when the token is passed.
if let (Some(xdg_activation), Some(token)) = ( if let (Some(xdg_activation), Some(token)) = (
xdg_activation.as_ref(), xdg_activation.as_ref(),
platform_attributes.activation_token, attributes.platform_specific.activation_token,
) { ) {
xdg_activation.activate(token._token, &surface); xdg_activation.activate(token._token, &surface);
} }
@@ -280,7 +285,7 @@ impl Window {
pub fn inner_size(&self) -> PhysicalSize<u32> { pub fn inner_size(&self) -> PhysicalSize<u32> {
let window_state = self.window_state.lock().unwrap(); let window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor(); let scale_factor = window_state.scale_factor();
window_state.inner_size().to_physical(scale_factor) super::logical_to_physical_rounded(window_state.inner_size(), scale_factor)
} }
#[inline] #[inline]
@@ -308,7 +313,7 @@ impl Window {
pub fn outer_size(&self) -> PhysicalSize<u32> { pub fn outer_size(&self) -> PhysicalSize<u32> {
let window_state = self.window_state.lock().unwrap(); let window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor(); let scale_factor = window_state.scale_factor();
window_state.outer_size().to_physical(scale_factor) super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
} }
#[inline] #[inline]
@@ -327,7 +332,9 @@ impl Window {
self.window_state self.window_state
.lock() .lock()
.unwrap() .unwrap()
.set_min_inner_size(min_size) .set_min_inner_size(min_size);
// NOTE: Requires commit to be applied.
self.request_redraw();
} }
/// Set the maximum inner size for the window. /// Set the maximum inner size for the window.
@@ -338,7 +345,9 @@ impl Window {
self.window_state self.window_state
.lock() .lock()
.unwrap() .unwrap()
.set_max_inner_size(max_size) .set_max_inner_size(max_size);
// NOTE: Requires commit to be applied.
self.request_redraw();
} }
#[inline] #[inline]
@@ -387,7 +396,10 @@ impl Window {
#[inline] #[inline]
pub fn set_resizable(&self, resizable: bool) { pub fn set_resizable(&self, resizable: bool) {
self.window_state.lock().unwrap().set_resizable(resizable); if self.window_state.lock().unwrap().set_resizable(resizable) {
// NOTE: Requires commit to be applied.
self.request_redraw();
}
} }
#[inline] #[inline]
@@ -502,16 +514,13 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) { pub fn set_cursor(&self, cursor: Cursor) {
self.window_state.lock().unwrap().set_cursor(cursor); let window_state = &mut self.window_state.lock().unwrap();
}
#[inline] match cursor {
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) { Cursor::Icon(icon) => window_state.set_cursor(icon),
self.window_state Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
.lock() }
.unwrap()
.set_custom_cursor(&cursor.0);
} }
#[inline] #[inline]

View File

@@ -4,8 +4,10 @@ use std::num::NonZeroU32;
use std::sync::{Arc, Mutex, Weak}; use std::sync::{Arc, Mutex, Weak};
use std::time::Duration; use std::time::Duration;
use ahash::HashSet;
use log::{info, warn}; use log::{info, warn};
use sctk::reexports::client::backend::ObjectId;
use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_shm::WlShm; use sctk::reexports::client::protocol::wl_shm::WlShm;
use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::protocol::wl_surface::WlSurface;
@@ -28,15 +30,13 @@ use sctk::shm::Shm;
use sctk::subcompositor::SubcompositorState; use sctk::subcompositor::SubcompositorState;
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
use crate::cursor::CursorImage; use crate::cursor::CustomCursor as RootCustomCursor;
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size}; use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
use crate::error::{ExternalError, NotSupportedError}; use crate::error::{ExternalError, NotSupportedError};
use crate::event::WindowEvent; use crate::platform_impl::wayland::logical_to_physical_rounded;
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::make_wid;
use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor}; use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor};
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::WindowId; use crate::platform_impl::{PlatformCustomCursor, WindowId};
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme}; use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};
use crate::platform_impl::wayland::seat::{ use crate::platform_impl::wayland::seat::{
@@ -92,8 +92,10 @@ pub struct WindowState {
/// Whether the frame is resizable. /// Whether the frame is resizable.
resizable: bool, resizable: bool,
/// Whether the window has focus. // NOTE: we can't use simple counter, since it's racy when seat getting destroyed and new
has_focus: bool, // is created, since add/removed stuff could be delivered a bit out of order.
/// Seats that has keyboard focus on that window.
seat_focus: HashSet<ObjectId>,
/// The scale factor of the window. /// The scale factor of the window.
scale_factor: f64, scale_factor: f64,
@@ -189,7 +191,7 @@ impl WindowState {
fractional_scale, fractional_scale,
frame: None, frame: None,
frame_callback_state: FrameCallbackState::None, frame_callback_state: FrameCallbackState::None,
has_focus: false, seat_focus: Default::default(),
has_pending_move: None, has_pending_move: None,
ime_allowed: false, ime_allowed: false,
ime_purpose: ImePurpose::Normal, ime_purpose: ImePurpose::Normal,
@@ -261,8 +263,7 @@ impl WindowState {
configure: WindowConfigure, configure: WindowConfigure,
shm: &Shm, shm: &Shm,
subcompositor: &Option<Arc<SubcompositorState>>, subcompositor: &Option<Arc<SubcompositorState>>,
event_sink: &mut EventSink, ) -> bool {
) -> Option<LogicalSize<u32>> {
// NOTE: when using fractional scaling or wl_compositor@v6 the scaling // NOTE: when using fractional scaling or wl_compositor@v6 the scaling
// should be delivered before the first configure, thus apply it to // should be delivered before the first configure, thus apply it to
// properly scale the physical sizes provided by the users. // properly scale the physical sizes provided by the users.
@@ -305,19 +306,6 @@ impl WindowState {
let stateless = Self::is_stateless(&configure); let stateless = Self::is_stateless(&configure);
// Emit `Occluded` event on suspension change.
let occluded = configure.state.contains(XdgWindowState::SUSPENDED);
if self
.last_configure
.as_ref()
.map(|c| c.state.contains(XdgWindowState::SUSPENDED))
.unwrap_or(false)
!= occluded
{
let window_id = make_wid(self.window.wl_surface());
event_sink.push_window_event(WindowEvent::Occluded(occluded), window_id);
}
let (mut new_size, constrain) = if let Some(frame) = self.frame.as_mut() { let (mut new_size, constrain) = if let Some(frame) = self.frame.as_mut() {
// Configure the window states. // Configure the window states.
frame.update_state(configure.state); frame.update_state(configure.state);
@@ -374,9 +362,9 @@ impl WindowState {
if state_change_requires_resize || new_size != self.inner_size() { if state_change_requires_resize || new_size != self.inner_size() {
self.resize(new_size); self.resize(new_size);
Some(new_size) true
} else { } else {
None false
} }
} }
@@ -516,10 +504,12 @@ impl WindowState {
} }
/// Set the resizable state on the window. /// Set the resizable state on the window.
///
/// Returns `true` when the state was applied.
#[inline] #[inline]
pub fn set_resizable(&mut self, resizable: bool) { pub fn set_resizable(&mut self, resizable: bool) -> bool {
if self.resizable == resizable { if self.resizable == resizable {
return; return false;
} }
self.resizable = resizable; self.resizable = resizable;
@@ -535,12 +525,14 @@ impl WindowState {
if let Some(frame) = self.frame.as_mut() { if let Some(frame) = self.frame.as_mut() {
frame.set_resizable(resizable); frame.set_resizable(resizable);
} }
true
} }
/// Whether the window is focused. /// Whether the window is focused by any seat.
#[inline] #[inline]
pub fn has_focus(&self) -> bool { pub fn has_focus(&self) -> bool {
self.has_focus !self.seat_focus.is_empty()
} }
/// Whether the IME is allowed. /// Whether the IME is allowed.
@@ -656,7 +648,7 @@ impl WindowState {
self.resize(inner_size.to_logical(self.scale_factor())) self.resize(inner_size.to_logical(self.scale_factor()))
} }
self.inner_size().to_physical(self.scale_factor()) logical_to_physical_rounded(self.inner_size(), self.scale_factor())
} }
/// Resize the window to the new inner size. /// Resize the window to the new inner size.
@@ -726,10 +718,23 @@ impl WindowState {
} }
/// Set the custom cursor icon. /// Set the custom cursor icon.
pub(crate) fn set_custom_cursor(&mut self, cursor: &CursorImage) { pub(crate) fn set_custom_cursor(&mut self, cursor: RootCustomCursor) {
let cursor = match cursor {
RootCustomCursor {
inner: PlatformCustomCursor::Wayland(cursor),
} => cursor.0,
#[cfg(x11_platform)]
RootCustomCursor {
inner: PlatformCustomCursor::X(_),
} => {
log::error!("passed a X11 cursor to Wayland backend");
return;
}
};
let cursor = { let cursor = {
let mut pool = self.custom_cursor_pool.lock().unwrap(); let mut pool = self.custom_cursor_pool.lock().unwrap();
CustomCursor::new(&mut pool, cursor) CustomCursor::new(&mut pool, &cursor)
}; };
if self.cursor_visible { if self.cursor_visible {
@@ -954,12 +959,16 @@ impl WindowState {
} }
} }
/// Mark that the window has focus. /// Add seat focus for the window.
///
/// Should be used from routine that sends focused event.
#[inline] #[inline]
pub fn set_has_focus(&mut self, has_focus: bool) { pub fn add_seat_focus(&mut self, seat: ObjectId) {
self.has_focus = has_focus; self.seat_focus.insert(seat);
}
/// Remove seat focus from the window.
#[inline]
pub fn remove_seat_focus(&mut self, seat: &ObjectId) {
self.seat_focus.remove(seat);
} }
/// Returns `true` if the requested state was applied. /// Returns `true` if the requested state was applied.

View File

@@ -6,7 +6,7 @@ macro_rules! atom_manager {
($($name:ident $(:$lit:literal)?),*) => { ($($name:ident $(:$lit:literal)?),*) => {
x11rb::atom_manager! { x11rb::atom_manager! {
/// The atoms used by `winit` /// The atoms used by `winit`
pub(crate) Atoms: AtomsCookie { pub Atoms: AtomsCookie {
$($name $(:$lit)?,)* $($name $(:$lit)?,)*
} }
} }
@@ -14,7 +14,7 @@ macro_rules! atom_manager {
/// Indices into the `Atoms` struct. /// Indices into the `Atoms` struct.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub(crate) enum AtomName { pub enum AtomName {
$($name,)* $($name,)*
} }
@@ -100,7 +100,8 @@ atom_manager! {
_NET_FRAME_EXTENTS, _NET_FRAME_EXTENTS,
_NET_SUPPORTED, _NET_SUPPORTED,
_NET_SUPPORTING_WM_CHECK, _NET_SUPPORTING_WM_CHECK,
_XEMBED _XEMBED,
_XSETTINGS_SETTINGS
} }
impl Index<AtomName> for Atoms { impl Index<AtomName> for Atoms {

View File

@@ -41,7 +41,7 @@ impl From<io::Error> for DndDataParseError {
} }
} }
pub(crate) struct Dnd { pub struct Dnd {
xconn: Arc<XConnection>, xconn: Arc<XConnection>,
// Populated by XdndEnter event handler // Populated by XdndEnter event handler
pub version: Option<c_long>, pub version: Option<c_long>,

File diff suppressed because it is too large Load Diff

View File

@@ -79,9 +79,9 @@ pub(crate) unsafe fn set_destroy_callback(
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
enum ReplaceImError { enum ReplaceImError {
// Boxed to prevent large error type // Boxed to prevent large error type
MethodOpenFailed(Box<PotentialInputMethods>), MethodOpenFailed(#[allow(dead_code)] Box<PotentialInputMethods>),
ContextCreationFailed(ImeContextCreationError), ContextCreationFailed(#[allow(dead_code)] ImeContextCreationError),
SetDestroyCallbackFailed(XError), SetDestroyCallbackFailed(#[allow(dead_code)] XError),
} }
// Attempt to replace current IM (which may or may not be presently valid) with a new one. This // Attempt to replace current IM (which may or may not be presently valid) with a new one. This

View File

@@ -86,7 +86,7 @@ extern "C" fn preedit_draw_callback(
let chg_range = let chg_range =
call_data.chg_first as usize..(call_data.chg_first + call_data.chg_length) as usize; call_data.chg_first as usize..(call_data.chg_first + call_data.chg_length) as usize;
if chg_range.start > client_data.text.len() || chg_range.end > client_data.text.len() { if chg_range.start > client_data.text.len() || chg_range.end > client_data.text.len() {
warn!( log::warn!(
"invalid chg range: buffer length={}, but chg_first={} chg_lengthg={}", "invalid chg range: buffer length={}, but chg_first={} chg_lengthg={}",
client_data.text.len(), client_data.text.len(),
call_data.chg_first, call_data.chg_first,

View File

@@ -159,9 +159,9 @@ impl InputMethodResult {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum GetXimServersError { enum GetXimServersError {
XError(XError), XError(#[allow(dead_code)] XError),
GetPropertyError(util::GetPropertyError), GetPropertyError(#[allow(dead_code)] util::GetPropertyError),
InvalidUtf8(IntoStringError), InvalidUtf8(#[allow(dead_code)] IntoStringError),
} }
impl From<util::GetPropertyError> for GetXimServersError { impl From<util::GetPropertyError> for GetXimServersError {

View File

@@ -10,6 +10,10 @@ use std::sync::{
Arc, Arc,
}; };
use log::debug;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::{ffi, util, XConnection, XError}; use super::{ffi, util, XConnection, XError};
pub use self::context::ImeContextCreationError; pub use self::context::ImeContextCreationError;
@@ -48,7 +52,7 @@ pub enum ImeRequest {
pub(crate) enum ImeCreationError { pub(crate) enum ImeCreationError {
// Boxed to prevent large error type // Boxed to prevent large error type
OpenFailure(Box<PotentialInputMethods>), OpenFailure(Box<PotentialInputMethods>),
SetDestroyCallbackFailed(XError), SetDestroyCallbackFailed(#[allow(dead_code)] XError),
} }
pub(crate) struct Ime { pub(crate) struct Ime {

View File

@@ -1,5 +1,43 @@
#![cfg(x11_platform)] #![cfg(x11_platform)]
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, HashSet};
use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::os::raw::*;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
use std::sync::{Arc, Weak};
use std::time::{Duration, Instant};
use std::{ptr, slice, str};
use calloop::generic::Generic;
use calloop::EventLoop as Loop;
use calloop::{ping::Ping, Readiness};
use libc::{self, setlocale, LC_CTYPE};
use log::warn;
use x11rb::connection::RequestConnection;
use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
use x11rb::protocol::xinput::{self, ConnectionExt as _};
use x11rb::protocol::xkb;
use x11rb::protocol::xproto::{self, ConnectionExt as _};
use x11rb::x11_utils::X11Error as LogicalError;
use x11rb::xcb_ffi::ReplyOrIdError;
use super::{common::xkb_state::KbdState, ControlFlow, OsError};
use crate::{
error::{EventLoopError, OsError as RootOsError},
event::{Event, StartCause, WindowEvent},
event_loop::{DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
platform::pump_events::PumpStatus,
platform_impl::platform::{min_timeout, WindowId},
window::WindowAttributes,
};
mod activation; mod activation;
mod atoms; mod atoms;
mod dnd; mod dnd;
@@ -7,76 +45,20 @@ mod event_processor;
pub mod ffi; pub mod ffi;
mod ime; mod ime;
mod monitor; mod monitor;
pub mod util; mod util;
mod window; mod window;
mod xdisplay; mod xdisplay;
mod xsettings;
pub(crate) use self::{
monitor::{MonitorHandle, VideoMode},
window::UnownedWindow,
xdisplay::XConnection,
};
pub use self::xdisplay::{XError, XNotSupported};
use calloop::generic::Generic;
use calloop::EventLoop as Loop;
use calloop::{ping::Ping, Readiness};
use std::{
cell::{Cell, RefCell},
collections::{HashMap, HashSet},
ffi::CStr,
fmt,
mem::MaybeUninit,
ops::Deref,
os::{
raw::*,
unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd},
},
ptr,
rc::Rc,
slice, str,
sync::mpsc::{Receiver, Sender, TryRecvError},
sync::{mpsc, Arc, Weak},
time::{Duration, Instant},
};
use libc::{self, setlocale, LC_CTYPE};
use atoms::*; use atoms::*;
use dnd::{Dnd, DndState};
use event_processor::EventProcessor;
use ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender};
pub(crate) use monitor::{MonitorHandle, VideoModeHandle};
use window::UnownedWindow;
pub(crate) use xdisplay::{XConnection, XError, XNotSupported};
use x11rb::x11_utils::X11Error as LogicalError; pub use util::CustomCursor;
use x11rb::{
connection::RequestConnection,
protocol::{
xinput::{self, ConnectionExt as _},
xkb,
xproto::{self, ConnectionExt as _},
},
};
use x11rb::{
errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError},
xcb_ffi::ReplyOrIdError,
};
use self::{
dnd::{Dnd, DndState},
event_processor::EventProcessor,
ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender},
};
use super::{common::xkb_state::KbdState, ControlFlow, OsError};
use crate::{
error::{EventLoopError, OsError as RootOsError},
event::{Event, StartCause, WindowEvent},
event_loop::{DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
platform::pump_events::PumpStatus,
platform_impl::{
platform::{min_timeout, WindowId},
PlatformSpecificWindowBuilderAttributes,
},
window::WindowAttributes,
};
// Xinput constants not defined in x11rb // Xinput constants not defined in x11rb
const ALL_DEVICES: u16 = 0; const ALL_DEVICES: u16 = 0;
@@ -143,7 +125,7 @@ impl<T> PeekableReceiver<T> {
} }
} }
pub struct EventLoopWindowTarget<T> { pub struct EventLoopWindowTarget {
xconn: Arc<XConnection>, xconn: Arc<XConnection>,
wm_delete_window: xproto::Atom, wm_delete_window: xproto::Atom,
net_wm_ping: xproto::Atom, net_wm_ping: xproto::Atom,
@@ -151,24 +133,22 @@ pub struct EventLoopWindowTarget<T> {
control_flow: Cell<ControlFlow>, control_flow: Cell<ControlFlow>,
exit: Cell<Option<i32>>, exit: Cell<Option<i32>>,
root: xproto::Window, root: xproto::Window,
ime: RefCell<Ime>, ime: Option<RefCell<Ime>>,
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>, windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
redraw_sender: WakeSender<WindowId>, redraw_sender: WakeSender<WindowId>,
activation_sender: WakeSender<ActivationToken>, activation_sender: WakeSender<ActivationToken>,
device_events: Cell<DeviceEvents>, device_events: Cell<DeviceEvents>,
_marker: ::std::marker::PhantomData<T>,
} }
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
loop_running: bool, loop_running: bool,
event_loop: Loop<'static, EventLoopState>, event_loop: Loop<'static, EventLoopState>,
waker: calloop::ping::Ping, waker: calloop::ping::Ping,
event_processor: EventProcessor<T>, event_processor: EventProcessor,
redraw_receiver: PeekableReceiver<WindowId>, redraw_receiver: PeekableReceiver<WindowId>,
user_receiver: PeekableReceiver<T>, user_receiver: PeekableReceiver<T>,
activation_receiver: PeekableReceiver<ActivationToken>, activation_receiver: PeekableReceiver<ActivationToken>,
user_sender: Sender<T>, user_sender: Sender<T>,
target: Rc<RootELW<T>>,
/// The current state of the event loop. /// The current state of the event loop.
state: EventLoopState, state: EventLoopState,
@@ -229,13 +209,15 @@ impl<T: 'static> EventLoop<T> {
setlocale(LC_CTYPE, default_locale); setlocale(LC_CTYPE, default_locale);
} }
} }
let ime = RefCell::new({
let result = Ime::new(Arc::clone(&xconn), ime_event_sender); let ime = Ime::new(Arc::clone(&xconn), ime_event_sender);
if let Err(ImeCreationError::OpenFailure(ref state)) = result { if let Err(ImeCreationError::OpenFailure(state)) = ime.as_ref() {
panic!("Failed to open input method: {state:#?}"); warn!("Failed to open input method: {state:#?}");
} } else if let Err(err) = ime.as_ref() {
result.expect("Failed to set input method destruction callback") warn!("Failed to set input method destruction callback: {err:?}");
}); }
let ime = ime.ok().map(RefCell::new);
let randr_event_offset = xconn let randr_event_offset = xconn
.select_xrandr_input(root) .select_xrandr_input(root)
@@ -308,7 +290,6 @@ impl<T: 'static> EventLoop<T> {
control_flow: Cell::new(ControlFlow::default()), control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None), exit: Cell::new(None),
windows: Default::default(), windows: Default::default(),
_marker: ::std::marker::PhantomData,
ime_sender, ime_sender,
xconn, xconn,
wm_delete_window, wm_delete_window,
@@ -327,13 +308,13 @@ impl<T: 'static> EventLoop<T> {
// Set initial device event filter. // Set initial device event filter.
window_target.update_listen_device_events(true); window_target.update_listen_device_events(true);
let target = Rc::new(RootELW { let root_window_target = RootELW {
p: super::EventLoopWindowTarget::X(window_target), p: super::EventLoopWindowTarget::X(window_target),
_marker: ::std::marker::PhantomData, _marker: PhantomData,
}); };
let event_processor = EventProcessor { let event_processor = EventProcessor {
target: target.clone(), target: root_window_target,
dnd, dnd,
devices: Default::default(), devices: Default::default(),
randr_event_offset, randr_event_offset,
@@ -346,13 +327,15 @@ impl<T: 'static> EventLoop<T> {
held_key_press: None, held_key_press: None,
first_touch: None, first_touch: None,
active_window: None, active_window: None,
modifiers: Default::default(),
is_composing: false, is_composing: false,
}; };
// Register for device hotplug events // Register for device hotplug events
// (The request buffer is flushed during `init_device`) // (The request buffer is flushed during `init_device`)
get_xtarget(&target) let xconn = &EventProcessor::window_target(&event_processor.target).xconn;
.xconn
xconn
.select_xinput_events( .select_xinput_events(
root, root,
ALL_DEVICES, ALL_DEVICES,
@@ -360,11 +343,12 @@ impl<T: 'static> EventLoop<T> {
) )
.expect_then_ignore_error("Failed to register for XInput2 device hotplug events"); .expect_then_ignore_error("Failed to register for XInput2 device hotplug events");
get_xtarget(&target) xconn
.xconn
.select_xkb_events( .select_xkb_events(
0x100, // Use the "core keyboard device" 0x100, // Use the "core keyboard device"
xkb::EventType::NEW_KEYBOARD_NOTIFY | xkb::EventType::STATE_NOTIFY, xkb::EventType::NEW_KEYBOARD_NOTIFY
| xkb::EventType::MAP_NOTIFY
| xkb::EventType::STATE_NOTIFY,
) )
.unwrap(); .unwrap();
@@ -379,7 +363,6 @@ impl<T: 'static> EventLoop<T> {
activation_receiver: PeekableReceiver::from_recv(activation_token_channel), activation_receiver: PeekableReceiver::from_recv(activation_token_channel),
user_receiver: PeekableReceiver::from_recv(user_channel), user_receiver: PeekableReceiver::from_recv(user_channel),
user_sender, user_sender,
target,
state: EventLoopState { state: EventLoopState {
x11_readiness: Readiness::EMPTY, x11_readiness: Readiness::EMPTY,
}, },
@@ -395,18 +378,14 @@ impl<T: 'static> EventLoop<T> {
} }
} }
pub(crate) fn window_target(&self) -> &RootELW<T> { pub(crate) fn window_target(&self) -> &RootELW {
&self.target &self.event_processor.target
} }
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootELW<T>), F: FnMut(Event<T>, &RootELW),
{ {
if self.loop_running {
return Err(EventLoopError::AlreadyRunning);
}
let exit = loop { let exit = loop {
match self.pump_events(None, &mut event_handler) { match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
@@ -425,7 +404,7 @@ impl<T: 'static> EventLoop<T> {
// `run_on_demand` calls but if they have only just dropped their // `run_on_demand` calls but if they have only just dropped their
// windows we need to make sure those last requests are sent to the // windows we need to make sure those last requests are sent to the
// X Server. // X Server.
let wt = get_xtarget(&self.target); let wt = EventProcessor::window_target(&self.event_processor.target);
wt.x_connection().sync_with_server().map_err(|x_err| { wt.x_connection().sync_with_server().map_err(|x_err| {
EventLoopError::Os(os_error!(OsError::XError(Arc::new(X11Error::Xlib(x_err))))) EventLoopError::Os(os_error!(OsError::XError(Arc::new(X11Error::Xlib(x_err)))))
})?; })?;
@@ -435,7 +414,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where where
F: FnMut(Event<T>, &RootELW<T>), F: FnMut(Event<T>, &RootELW),
{ {
if !self.loop_running { if !self.loop_running {
self.loop_running = true; self.loop_running = true;
@@ -468,7 +447,7 @@ impl<T: 'static> EventLoop<T> {
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where where
F: FnMut(Event<T>, &RootELW<T>), F: FnMut(Event<T>, &RootELW),
{ {
let start = Instant::now(); let start = Instant::now();
@@ -546,14 +525,14 @@ impl<T: 'static> EventLoop<T> {
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause) fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
where where
F: FnMut(Event<T>, &RootELW<T>), F: FnMut(Event<T>, &RootELW),
{ {
callback(crate::event::Event::NewEvents(cause), &self.target); callback(Event::NewEvents(cause), &self.event_processor.target);
// NB: For consistency all platforms must emit a 'resumed' event even though X11 // NB: For consistency all platforms must emit a 'resumed' event even though X11
// applications don't themselves have a formal suspend/resume lifecycle. // applications don't themselves have a formal suspend/resume lifecycle.
if cause == StartCause::Init { if cause == StartCause::Init {
callback(crate::event::Event::Resumed, &self.target); callback(Event::Resumed, &self.event_processor.target);
} }
// Process all pending events // Process all pending events
@@ -568,16 +547,16 @@ impl<T: 'static> EventLoop<T> {
}); });
match token { match token {
Some(Ok(token)) => callback( Some(Ok(token)) => {
crate::event::Event::WindowEvent { let event = Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(window_id),
event: crate::event::WindowEvent::ActivationTokenDone { event: WindowEvent::ActivationTokenDone {
serial, serial,
token: crate::window::ActivationToken::_new(token), token: crate::window::ActivationToken::_new(token),
}, },
}, };
&self.target, callback(event, &self.event_processor.target)
), }
Some(Err(e)) => { Some(Err(e)) => {
log::error!("Failed to get activation token: {}", e); log::error!("Failed to get activation token: {}", e);
} }
@@ -588,7 +567,7 @@ impl<T: 'static> EventLoop<T> {
// Empty the user event buffer // Empty the user event buffer
{ {
while let Ok(event) = self.user_receiver.try_recv() { while let Ok(event) = self.user_receiver.try_recv() {
callback(crate::event::Event::UserEvent(event), &self.target); callback(Event::UserEvent(event), &self.event_processor.target);
} }
} }
@@ -607,55 +586,59 @@ impl<T: 'static> EventLoop<T> {
window_id, window_id,
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}, },
&self.target, &self.event_processor.target,
); );
} }
} }
// This is always the last event we dispatch before poll again // This is always the last event we dispatch before poll again
{ {
callback(crate::event::Event::AboutToWait, &self.target); callback(Event::AboutToWait, &self.event_processor.target);
} }
} }
fn drain_events<F>(&mut self, callback: &mut F) fn drain_events<F>(&mut self, callback: &mut F)
where where
F: FnMut(Event<T>, &RootELW<T>), F: FnMut(Event<T>, &RootELW),
{ {
let target = &self.target;
let mut xev = MaybeUninit::uninit(); let mut xev = MaybeUninit::uninit();
let wt = get_xtarget(&self.target);
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
let mut xev = unsafe { xev.assume_init() }; let mut xev = unsafe { xev.assume_init() };
self.event_processor.process_event(&mut xev, |event| { self.event_processor
if let Event::WindowEvent { .process_event(&mut xev, |window_target, event| {
window_id: crate::window::WindowId(wid), if let Event::WindowEvent {
event: WindowEvent::RedrawRequested, window_id: crate::window::WindowId(wid),
} = event event: WindowEvent::RedrawRequested,
{ } = event
wt.redraw_sender.send(wid).unwrap(); {
} else { let window_target = EventProcessor::window_target(window_target);
callback(event, target); window_target.redraw_sender.send(wid).unwrap();
} } else {
}); callback(event, window_target);
}
});
} }
} }
fn control_flow(&self) -> ControlFlow { fn control_flow(&self) -> ControlFlow {
self.target.p.control_flow() let window_target = EventProcessor::window_target(&self.event_processor.target);
window_target.control_flow()
} }
fn exiting(&self) -> bool { fn exiting(&self) -> bool {
self.target.p.exiting() let window_target = EventProcessor::window_target(&self.event_processor.target);
window_target.exiting()
} }
fn set_exit_code(&self, code: i32) { fn set_exit_code(&self, code: i32) {
self.target.p.set_exit_code(code) let window_target = EventProcessor::window_target(&self.event_processor.target);
window_target.set_exit_code(code);
} }
fn exit_code(&self) -> Option<i32> { fn exit_code(&self) -> Option<i32> {
self.target.p.exit_code() let window_target = EventProcessor::window_target(&self.event_processor.target);
window_target.exit_code()
} }
} }
@@ -671,15 +654,7 @@ impl<T> AsRawFd for EventLoop<T> {
} }
} }
pub(crate) fn get_xtarget<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> { impl EventLoopWindowTarget {
match target.p {
super::EventLoopWindowTarget::X(ref target) => target,
#[cfg(wayland_platform)]
_ => unreachable!(),
}
}
impl<T> EventLoopWindowTarget<T> {
/// Returns the `XConnection` of this events loop. /// Returns the `XConnection` of this events loop.
#[inline] #[inline]
pub(crate) fn x_connection(&self) -> &Arc<XConnection> { pub(crate) fn x_connection(&self) -> &Arc<XConnection> {
@@ -752,6 +727,10 @@ impl<T> EventLoopWindowTarget<T> {
self.exit.set(Some(0)) self.exit.set(Some(0))
} }
pub(crate) fn clear_exit(&self) {
self.exit.set(None)
}
pub(crate) fn exiting(&self) -> bool { pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some() self.exit.get().is_some()
} }
@@ -834,12 +813,11 @@ impl Deref for Window {
} }
impl Window { impl Window {
pub(crate) fn new<T>( pub(crate) fn new(
event_loop: &EventLoopWindowTarget<T>, event_loop: &EventLoopWindowTarget,
attribs: WindowAttributes, attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> { ) -> Result<Self, RootOsError> {
let window = Arc::new(UnownedWindow::new(event_loop, attribs, pl_attribs)?); let window = Arc::new(UnownedWindow::new(event_loop, attribs)?);
event_loop event_loop
.windows .windows
.borrow_mut() .borrow_mut()
@@ -891,6 +869,9 @@ pub enum X11Error {
/// Could not find a matching X11 visual for this visualid /// Could not find a matching X11 visual for this visualid
NoSuchVisual(xproto::Visualid), NoSuchVisual(xproto::Visualid),
/// Unable to parse xsettings.
XsettingsParse(xsettings::ParserError),
} }
impl fmt::Display for X11Error { impl fmt::Display for X11Error {
@@ -915,6 +896,9 @@ impl fmt::Display for X11Error {
visualid visualid
) )
} }
X11Error::XsettingsParse(err) => {
write!(f, "Failed to parse xsettings: {:?}", err)
}
} }
} }
} }
@@ -983,6 +967,12 @@ impl From<ReplyOrIdError> for X11Error {
} }
} }
impl From<xsettings::ParserError> for X11Error {
fn from(value: xsettings::ParserError) -> Self {
Self::XsettingsParse(value)
}
}
/// The underlying x11rb connection that we are using. /// The underlying x11rb connection that we are using.
type X11rbConnection = x11rb::xcb_ffi::XCBConnection; type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
@@ -1037,7 +1027,7 @@ fn mkdid(w: xinput::DeviceId) -> crate::event::DeviceId {
} }
#[derive(Debug)] #[derive(Debug)]
struct Device { pub struct Device {
_name: String, _name: String,
scroll_axes: Vec<(i32, ScrollAxis)>, scroll_axes: Vec<(i32, ScrollAxis)>,
// For master devices, this is the paired device (pointer <-> keyboard). // For master devices, this is the paired device (pointer <-> keyboard).

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