Compare commits

..

189 Commits

Author SHA1 Message Date
John Nunley
d28c867b61 chore: Rebase on latest master
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:02:41 -08:00
John Nunley
cfd35d2708 ci: Missed a spot
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:02:11 -08:00
John Nunley
1b9c576f53 ci: Use -p option in CI
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:02:10 -08:00
John Nunley
52532d2579 chore: Fix failing CI
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:02:10 -08:00
John Nunley
4b28f10470 chore: fmt and doc fixes
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:02:10 -08:00
John Nunley
daf1304879 chore: Housekeeping
- Update CHANGELOG
- Add documentation to items I forgot to document
- Trait implementations

Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:02:08 -08:00
John Nunley
c6e57430bb chore: Move over ControlFlow
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:01:49 -08:00
John Nunley
284f4c0558 feat: Move Event to winit-core
This requires a lot of restructuring on the backend side, as we need to
accommodate for backends not being able to construct these types
implicitly. In addition, we also need to expose some new APIs on these
types to make them usable.

Several breaking changes occurred in this commit.

Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:01:49 -08:00
John Nunley
859a6f109c feat: Move DeviceId to winit-core
This requires a similar restructuring as with WindowId.

Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:01:49 -08:00
John Nunley
a232f7bc77 feat: Move WindowId to winit-core
This commit moves WindowId to winit-core and replaces its inner type
with a platform-agnostic `u64`. This required some changes to many of
the backend in order to accommodate this new system.

Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 19:01:46 -08:00
John Nunley
48831f7910 feat: Re-export cursor types in winit-core/window
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 18:59:06 -08:00
John Nunley
114e6999d3 chore: Move parts of window.rs to winit-core
As winit no longer owns some of the types this requires some adjustments
in the Wayland backend.

Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 18:59:06 -08:00
John Nunley
2f197315d9 chore: Move most of keyboard.rs to winit-core
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 18:59:06 -08:00
John Nunley
557464489e chore: Move dpi.rs to winit-core wholesale
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 18:59:06 -08:00
John Nunley
623b3e5070 chore: Move part of error.rs to winit-core
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 18:59:06 -08:00
John Nunley
a97255e0fc chore: Bring up winit-core crate
Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 18:59:05 -08:00
John Nunley
3a817647d5 chore: Move winit files to winit subfolder
This will allow us to split off crates from the main winit crate.

Signed-off-by: John Nunley <dev@notgull.net>
2024-02-06 18:58:48 -08: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
Mads Marquart
e9a25a4c91 Replace remaining AppKit bindings with icrate's (#3296)
* Use icrate's window structs and enums

* Properly implement protocols

* Use icrate's NSWindow

We were previously using undocumented methods on `NSWindowTabGroup`

* Use icrate's NSApplication

And clean up some doc comments regarding NSApplication
2023-12-23 23:07:55 +01:00
Mads Marquart
674657efb6 Partially replace custom AppKit bindings with icrate's autogenerated bindings (#2982)
* Refactor winit-specific cursor logic out of appkit module

* Add relevant AppKit features that we depend on

* Use icrate's NSImageRep and NSBitmapImageRep

* Use icrate's NSImage

* Use icrate's NSCursor

* Use icrate's NSAppearance

* Use icrate's NSScreen

* Use icrate's NSButton

* Use icrate's NSAppKitVersionNumber

* Use icrate's NSTextInputContext

* Use icrate's NSColor

* Use icrate's NSEvent

* Use icrate's NSMenu and NSMenuItem

* Use icrate's NSPasteboard

* Use icrate's NSResponder

* Use icrate's NSTextInputClient

* Use icrate's NSView
2023-12-23 20:58:38 +01:00
Mads Marquart
7d5bee767c Update objc2 and icrate versions (#3256) 2023-12-23 18:04:24 +01:00
Markus Siglreithmaier
745cfaab2c On Windows, remove internal WindowWrapper (#3294)
HWND in windows-sys doesn't require a newtype wrapper for Send/Sync.
2023-12-23 17:06:43 +01:00
daxpedda
a8f49dc8ef MacOS: cache custom cursors (#3291) 2023-12-23 16:34:32 +01:00
daxpedda
e5310ade08 Custom cursor improvements (#3292) 2023-12-23 16:12:29 +01:00
daxpedda
37946e0a3a Use std::cell::OnceCell (#3290) 2023-12-22 23:49:25 +01:00
daxpedda
86b737f5e7 Fix changelog (#3289) 2023-12-22 23:36:58 +01:00
daxpedda
e37585e5bc Bump MSRV to 1.70 (#3287) 2023-12-22 23:27:36 +01:00
Mads Marquart
4aeeb24745 Window handle: Return an error when not on main thread on macOS and iOS (#3288) 2023-12-22 23:18:35 +01:00
daxpedda
8cd3aaa8a2 On Web, use the new WebCanvasWindowHandle (#3270) 2023-12-22 22:33:50 +01:00
daxpedda
2c15de7cf9 Allow custom cursor caching (#3276) 2023-12-22 22:20:41 +01:00
daxpedda
0a7ea61834 Fix some doc nits (#3274) 2023-12-22 21:46:00 +01:00
Markus Siglreithmaier
4ee11018c2 On Windows, refactor dynamic function definitions and raw input keyboard handling (#3286) 2023-12-22 18:42:17 +01:00
daxpedda
4f669ebbd2 On Web, fix context menu not being disabled (#3282) 2023-12-22 00:11:36 +01:00
Kirill Chibisov
7761b2b16c Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2023-12-22 00:55:52 +04:00
daxpedda
ae41e3265f On Web, correctly mark breaking changes in the changelog 2023-12-22 00:39:06 +04:00
wjian23
8702a09333 On Windows, fix IME area not working 2023-12-21 23:44:30 +04:00
Kirill Chibisov
8b5c84f404 On Wayland, ensure initial resize delivery
While we correctly configure the sizes, we also need to actually resize
the frame on initial configure and send geometry.

Fixes #3277.
2023-12-21 22:29:36 +04:00
Kirill Chibisov
a676d0018b On windows, remove empty file 2023-12-20 19:12:44 +04:00
Kirill Chibisov
04ca85a909 On Wayland, fix resize being sent on focus change
Fixes #3263.
2023-12-20 18:48:50 +04:00
daxpedda
cc33212479 On Web, fix setting cursor icon overriding cursor visibility (#3269) 2023-12-17 18:49:45 +01:00
daxpedda
f2c5127f27 Web: remove queuing fullscreen request (#3242) 2023-12-17 13:31:48 +01:00
Eero Lehtinen
af93167237 feat(all): Custom cursor images for all desktop platforms
There seems to be many PRs relating to this issue, but they don't include all
platforms and for some reason lost steam. This PR again tries to make this
feature happen, and does it for all desktop platforms (x11, wayland, macos,
windows, web).

I think the best user of this feature and the reason I'm doing this is Bevy and
game engines in general. There non laggy hardware cursors with custom images are
very important. Game devs also like their PNGs so supporting platform native
cursor files is not that important, but I guess could be added too.

Co-authored-by: daxpedda <daxpedda@gmail.com>
Co-authored-by: Mads Marquart <mads@marquart.dk>
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2023-12-16 12:02:17 -08:00
Amr Bashir
7f6b16a6af On Windows, fix set_fullscreen early return for Fullscreen::Borderless(None) 2023-12-16 20:34:18 +04:00
Friz64
bf5806a9b2 Update sctk-adwaita to 0.8 2023-12-16 20:16:39 +04:00
Héctor Ramón
2a9c593e01 Fix typo in get_xft_dpi 2023-12-15 16:12:50 +04:00
Marijn Suijten
becdd0dbd2 On Wayland, make wl_subcompositor protocol optional
This protocol is only used for (optional) Client Side Decorations
(where) the compositor still takes the burden of compositing various
window parts together, via subsurfaces that all belong to a single
window.

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

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

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

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

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

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

Signed-off-by: Uli Schlachter <psychon@znc.in>
2023-12-09 07:02:30 -08:00
Fredrik Fornwall
b863283c38 On Windows, avoid panic in video_modes() 2023-12-06 20:47:33 +04:00
Leon
73718c9f2f Changes and improvements to the documentation (#3253)
* FEATURES.md improvements

* docs improvements

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

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

---------

Co-authored-by: daxpedda <daxpedda@gmail.com>
2023-12-03 18:39:08 +01:00
Xiaopeng Li
f735f028a1 fix refresh_rate_millihertz on macOS (#3254)
* fix refresh_rate_millihertz on macOS

* round after conversion to mHz

* add changelog entry
2023-12-01 15:52:16 +01:00
John Nunley
da947992ac bugfix(x11): Use the right atom type in focus_window()
Closes #3248 by removing an Xlibism I forgot about

Signed-off-by: John Nunley <dev@notgull.net>
2023-11-28 16:20:36 -08:00
John Nunley
e9784127df bugfix(x11): Properly interpret float data in drag ops
Closes #3245

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

Signed-off-by: John Nunley <dev@notgull.net>
2023-11-28 15:08:14 -08:00
Mads Marquart
0be2bb0a8c Remove unused .gitmodules 2023-11-29 00:56:11 +04:00
OG
075996b1fa feat: macos services menu added (#3231) 2023-11-28 21:39:12 +01:00
Emil Ernerfeldt
a7241b3db3 On macOS, remove spurious error logging when handling Fn
Fixes #3246.
2023-11-28 23:19:16 +04:00
Kirill Chibisov
17296e9878 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2023-11-24 18:32:53 +04:00
John Nunley
b3c87caa7c On X11, reload DPI on PropertyChange
Signed-off-by: John Nunley <dev@notgull.net>
Fixes #1228.
2023-11-24 12:14:06 +04:00
Kirill Chibisov
81a1d9c396 Fix infinite recursion in BadIcon reporting (#3237) 2023-11-23 19:15:17 +01:00
Mads Marquart
5612626944 Make Android docs build on docs.rs (#3236) 2023-11-23 08:18:05 +01:00
Arend van Beelen jr
d3ca685b77 Fix crash when running iPad build on macOS 2023-11-22 16:14:51 +04:00
Kirill Chibisov
7bed5eecfd On macOS, fix assertion when pressing Fn key 2023-11-17 15:56:03 +04:00
Kirill Chibisov
14140607d1 On Wayland, fix wl_surface being dropped first
The surface was automatically dropped due to new RAII type in SCTK
when dropping the Window, which was not the case at some point with
SCTK.

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

Links: https://github.com/neovide/neovide/issues/2109
2023-11-11 20:35:30 +04:00
daxpedda
eab982c402 Web: forbid additional functions in favor of caching them (#3219) 2023-11-10 22:46:51 +01:00
Olivier Goffart
21701a33de On Windows, fix set_control_flow from `AboutToWait
In case the AboutToWait event sets the control flow to another value
it's not being used on this iteration.

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

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

Links: https://github.com/rust-mobile/ndk/pull/434#issuecomment-1752089087
2023-11-04 15:18:55 +04:00
DevJac
bcce5134e1 Fix typo in pre_present_notify docs
Fix typo and other small grammar corrections.
2023-11-02 01:07:35 +04:00
Linda_pp
d333dd8664 Fix crash when minimizing example on Windows 2023-10-31 19:21:36 +04:00
Jasper Bekkers
52af1b4a77 On Windows, fix MT safety when starting drag 2023-10-31 19:20:34 +04:00
Kirill Chibisov
3c9f9da19e Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2023-10-28 21:09:28 +04:00
Kirill Chibisov
075dfcea19 Clarify scale_factor docs
Wayland scales each window individually, thus make it clear. Also
recommend against using the `MonitorHandle::scale_factor`.

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

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

Fixes #3187.
2023-10-27 00:56:23 +04:00
Kirill Chibisov
53ca5af48f On Wayland, fix RedrawRequsted loop
The `dirty` is never cleared when decorations are hidden without
`sctk-adwaita`.

Fixes #3177.
2023-10-25 20:59:39 +04:00
Marijn Suijten
c235bd154a On wasm, provide intradoc-link for spawn() function in EventLoop docs (#3178) 2023-10-25 17:42:51 +02:00
J-P Nurmi
f4e71a1d9c On macOS, fix deadlock during nested event loops (e.g. rfd) 2023-10-25 19:13:44 +04:00
Kirill Chibisov
62ed51a138 On Winows, Fix deedlock with WM_MOUSEMOVE
The lock was still present in `None` path.

Fixes: d37d1a03b(On Windows, fix deadlock during `Cursor{Enter,Leave}`)
2023-10-25 18:32:16 +04:00
Kirill Chibisov
b2a2ec91ae Fix unused import warnings on nightly 2023-10-25 15:58:31 +04:00
Kirill Chibisov
d37d1a03b2 On Windows, fix deadlock during Cursor{Enter,Leave}
The lock was not released when calling back to the user.

Fixes #3171.
2023-10-22 19:38:54 +04:00
Kirill Chibisov
772b21ce09 Bump version on master
This commit does not represent a release and only synchronizes CHANGELOG
from the latest release.
2023-10-21 11:58:17 +04:00
Kirill Chibisov
2edcd09704 On X11, fix cursor_hittest not reloaded on Resize
The cursor hittest was not reloaded on window size changes, only
when `Window::request_inner_size` was called leading to regions
of the window being not clickable.

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

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

Mouse button device events are now using the same order as the MouseButton enum, and I also added hwheel device events for Windows.
2023-10-20 10:03:05 -07:00
Kirill Chibisov
36d4907da8 On Windows, fix IME APIs MT-safety
Execute the calls to the IME from the main thread.

Fixes #3123.
2023-10-20 15:46:57 +04:00
Kirill Chibisov
98b3508aca On Windows, fix RedrawRequested delivery
When calling `Window::request_redraw` from the `RedrawRequested`
handler the `RedrawWindow` won't result in `WM_PAINT` being delivered
due since user callback is run before `DefWindowProcW` is called.

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

Fixes #3150.
2023-10-20 14:52:01 +04:00
Diggory Hardy
c0db53a516 Implement Ord/PartialOrd for ModifiersState 2023-10-20 14:51:42 +04:00
Xiaopeng Li
52b7205b75 On Windows, fix invalid hmonitor panic
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2023-10-20 14:51:04 +04:00
Arend van Beelen jr
41dbbc27a0 On iOS, add configuration for status bar style
Co-authored-by: Mads Marquart <mads@marquart.dk>
2023-10-20 14:26:10 +04:00
Kirill Chibisov
c346fb7e61 On macOS, fix tabGroup misuse
The property is marked as `Weak`, however we used strong `Id`.

Links: https://github.com/alacritty/alacritty/issues/7249
2023-10-20 14:05:57 +04:00
Kirill Chibisov
6a041f84ba Remove garbage from README
The docs are in the src/lib.rs anyway and are present on docs.rs.
2023-10-19 19:25:30 +04:00
Diggory Hardy
acfeff5327 Revise Key and KeyCode enums
Split `Key` into clear categories, like `Named`, `Dead`, Character`, `Unidentified`
removing the `#[non_exhaustive]` from the `Key` itself.

Similar action was done for the `KeyCode`.

Fixes: #2995
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2023-10-19 18:27:49 +04:00
Kirill Chibisov
b9e1e96eaa Ensure that DISPLAY vars are non-empty before using
It's common to disable Wayland by `WAYLAND_DISPLAY= <application>`.
2023-10-19 17:03:15 +04:00
Kirill Chibisov
880238a24f Fix examples not render on Wayland
The `rwh_05` feature was not enabled.

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

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

Fixes #2872.
2023-10-17 05:59:48 +04:00
Kirill Chibisov
801fddbfcf Make WindowBuilder Send + Sync
Window builder is always accessed by winit on the thread event loop
is on, thus it's safe to mark the data it gets as `Send + Sync`.
Each unsafe object is marked individually as `Send + Sync` instead
of just implementing `Send` and `Sync` for the whole builder.
2023-10-17 04:54:12 +04:00
Kirill Chibisov
3ad64fb811 Remove resolved deny.toml entries 2023-10-17 04:35:14 +04:00
Marijn Suijten
9bf4493a21 Upgrade to ndk 0.8, ndk-sys 0.5 + android-activity 0.5 releases
Fixes #2905.
Co-authored-by: Robert Bragg <robert@sixbynine.org>
2023-10-17 04:08:33 +04:00
daxpedda
48f6582eb4 Web Async Rework (#3082) 2023-10-16 15:50:22 +02:00
Kirill Chibisov
ef34692148 Add a note on Window::request_redraw on Windows
Fixing this could require a massive rework to how redraw is handled
on windows to the point of removing `WM_PAINT`, since it's not reliable
by any means for our use case.

For now at least document that the API is broken. It was broken like
that for a long while.
2023-10-15 23:48:37 +04:00
Kirill Chibisov
c48116a8fd Implement AsFd/AsRawFd for EventLoop<T>
This should help other crates to integrate winit's event loop into
their bigger event loop without adding an extra thread.
2023-10-15 20:31:29 +04:00
283 changed files with 15639 additions and 12586 deletions

16
.github/CODEOWNERS vendored
View File

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

View File

@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
toolchain: [stable, nightly, '1.65.0']
toolchain: [stable, nightly, '1.70.0']
platform:
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
- { name: 'Windows 64bit MSVC', target: x86_64-pc-windows-msvc, os: windows-latest, }
@@ -35,20 +35,28 @@ jobs:
- { name: 'Linux 64bit', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
- { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--features=android-native-activity', cmd: 'apk --' }
- { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, }
- { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, }
- { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, }
- { name: 'iOS Aarch64', target: aarch64-apple-ios, os: macos-latest, }
- { name: 'web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
include:
exclude:
# Android is tested on stable-3
- toolchain: '1.69.0'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity' }
- toolchain: 'stable'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity' }
- toolchain: '1.70.0'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
include:
- toolchain: '1.70.0'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
- toolchain: 'nightly'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity' }
platform: {
name: 'web Atomic',
target: wasm32-unknown-unknown,
os: ubuntu-latest,
options: '-Zbuild-std=panic_abort,std',
rustflags: '-Ctarget-feature=+atomics,+bulk-memory',
components: rust-src,
}
env:
# Set more verbose terminal output
@@ -56,10 +64,10 @@ jobs:
RUST_BACKTRACE: 1
# Faster compilation and error on warnings
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings'
RUSTDOCFLAGS: '--deny=warnings'
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}'
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
CMD: ${{ matrix.platform.cmd }}
steps:
- uses: actions/checkout@v3
@@ -87,42 +95,43 @@ jobs:
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
run: sudo apt-get update && sudo apt-get install gcc-multilib
- name: Install xbuild
uses: taiki-e/install-action@v2
if: contains(matrix.platform.target, 'android') || contains(matrix.platform.target, 'ios')
- name: Cache cargo-apk
if: contains(matrix.platform.target, 'android')
id: cargo-apk-cache
uses: actions/cache@v3
with:
tool: xbuild@0.2.0
path: ~/.cargo/bin/cargo-apk
# Change this key if we update the required cargo-apk version
key: cargo-apk-v0-9-7
- uses: dtolnay/rust-toolchain@master
if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
with:
toolchain: stable
- name: Install cargo-apk
if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
run: cargo install cargo-apk --version=^0.9.7 --locked
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
targets: ${{ matrix.platform.target }}
components: clippy
- name: Install LLVM tools for Android
if: contains(matrix.platform.target, 'android')
run: sudo apt-get update && sudo apt-get install lld llvm
components: clippy, ${{ matrix.platform.components }}
- name: Check documentation
run: cargo doc --no-deps $OPTIONS --document-private-items
env:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }}'
- name: Build crate
run: cargo build $OPTIONS
- name: Build and package crate for Android
if: contains(matrix.platform.target, 'android')
run: x build -p android-xbuild-target --platform android --arch arm64
- name: Build and package crate for iOS
if: contains(matrix.platform.target, 'ios')
run: x build -p ios-xbuild-target --platform ios --arch arm64
- name: Build crate
run: cargo $CMD build -p winit $OPTIONS
- name: Build tests
if: >
!contains(matrix.platform.target, 'redox') &&
!contains(matrix.platform.target, 'android') &&
matrix.toolchain != '1.65.0'
run: cargo test --no-run $OPTIONS
matrix.toolchain != '1.70.0'
run: cargo $CMD test -p winit --no-run $OPTIONS
- name: Run tests
if: >
@@ -130,8 +139,8 @@ jobs:
!contains(matrix.platform.target, 'ios') &&
!contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.65.0'
run: cargo test $OPTIONS
matrix.toolchain != '1.70.0'
run: cargo $CMD test $OPTIONS
- name: Lint with clippy
if: (matrix.toolchain == 'stable') && !contains(matrix.platform.options, '--no-default-features')
@@ -140,9 +149,8 @@ jobs:
- name: Build tests with serde enabled
if: >
!contains(matrix.platform.target, 'redox') &&
!contains(matrix.platform.target, 'android') &&
matrix.toolchain != '1.65.0'
run: cargo test --no-run $OPTIONS --features serde
matrix.toolchain != '1.70.0'
run: cargo $CMD test --no-run $OPTIONS -p winit --features serde
- name: Run tests with serde enabled
if: >
@@ -150,8 +158,14 @@ jobs:
!contains(matrix.platform.target, 'ios') &&
!contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.65.0'
run: cargo test $OPTIONS --features serde
matrix.toolchain != '1.70.0'
run: cargo $CMD test $OPTIONS --features serde
- name: Check docs.rs documentation
if: matrix.toolchain == 'nightly'
run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
env:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs'
# See restore step above
- name: Save cache of cargo folder

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

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "deps/apk-builder"]
path = deps/apk-builder
url = https://github.com/rust-windowing/android-rs-glue

View File

@@ -2,49 +2,197 @@
All notable changes to this project will be documented in this file.
Please keep one empty line before and after all headers. (This is required for `git` to produce a conflict when a release is made while a PR is open and the PR's changelog entry would go into the wrong section).
Please keep one empty line before and after all headers. (This is required for
`git` to produce a conflict when a release is made while a PR is open and the
PR's changelog entry would go into the wrong section).
And please only add new entries to the top of this list, right below the `# Unreleased` header.
And please only add new entries to the top of this list, right below the `#
Unreleased` header.
# Unreleased
- Renamed `EventLoopExtRunOnDemand` / `run_ondemand` to `EventLoopExtRunOnDemand` / `run_on_demand`.
- Make iOS `MonitorHandle` and `VideoMode` usable from other threads.
- On Web, `ControlFlow::WaitUntil` now uses the Prioritized Task Scheduling API. `setTimeout()`, with a trick to circumvent throttling to 4ms, is used as a fallback.
- On Web, never return a `MonitorHandle`.
- **Breaking:** Move `Event::RedrawRequested` to `WindowEvent::RedrawRequested`.
- On macOS, fix crash in `window.set_minimized(false)`.
- On Web, enable event propagation and let `DeviceEvent`s appear after `WindowEvent`s.
- On Web, take all transient activations on the canvas and window into account to queue a fullscreen request.
- On Web, remove any fullscreen requests from the queue when an external fullscreen activation was detected.
- On Wayland, fix `TouchPhase::Canceled` being sent for moved events.
- Mark `startup_notify` unsafe functions as safe.
- Fix a bug where Wayland would be chosen on Linux even if the user specified `with_x11`. (#3058)
- **Breaking:** Moved `ControlFlow` to `EventLoopWindowTarget::set_control_flow()` and `EventLoopWindowTarget::control_flow()`.
- **Breaking:** Moved `ControlFlow::Exit` to `EventLoopWindowTarget::exit()` and `EventLoopWindowTarget::exiting()` and removed `ControlFlow::ExitWithCode(_)` entirely.
- On Web, add `EventLoopWindowTargetExtWebSys` and `PollStrategy`, which allows to set different strategies for `ControlFlow::Poll`. By default the Prioritized Task Scheduling API is used, but an option to use `Window.requestIdleCallback` is available as well. Both use `setTimeout()`, with a trick to circumvent throttling to 4ms, as a fallback.
- Implement `PartialOrd` and `Ord` for `MouseButton`.
- On X11, fix event loop not waking up on `ControlFlow::Poll` and `ControlFlow::WaitUntil`.
- On X11, fix swapped instance and general class names.
- **Breaking:** Removed unnecessary generic parameter `T` from `EventLoopWindowTarget`.
- On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example.
- **Breaking:** Remove `Window::set_cursor_icon`
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a `CursorIcon` or `CustomCursor`
- Add `CustomCursor`
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated cursors from other `CustomCursor`s.
- On macOS, add services menu.
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
- On Web, fix setting cursor icon overriding cursor visibility.
- **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
- **Breaking:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available.
- **Breaking:** Bump MSRV from `1.65` to `1.70`.
- On Web, add the ability to toggle calling `Event.preventDefault()` on `Window`.
- **Breaking:** Remove `WindowAttributes::fullscreen()` and expose as field directly.
- **Breaking:** Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold static data.
- **Breaking:** No longer export `platform::x11::XNotSupported`.
- **Breaking:** Renamed `platform::x11::XWindowType` to `platform::x11::WindowType`.
- Add the `OwnedDisplayHandle` type for allowing safe display handle usage outside of trivial cases.
- **Breaking:** Rename `TouchpadMagnify` to `PinchGesture`, `SmartMagnify` to `DoubleTapGesture` and `TouchpadRotate` to `RotationGesture` to represent the action rather than the intent.
- on iOS, add detection support for `PinchGesture`, `DoubleTapGesture` and `RotationGesture`.
- on Windows: add `with_system_backdrop`, `with_border_color`, `with_title_background_color`, `with_title_text_color` and `with_corner_preference`
- On Windows, Remove `WS_CAPTION`, `WS_BORDER` and `WS_EX_WINDOWEDGE` styles for child windows without decorations.
- On Windows, fixed a race condition when sending an event through the loop proxy.
- **Breaking:** Removed `EventLoopError::AlreadyRunning`, which can't happen as it is already prevented by the type system.
- On Wayland, disable `Occluded` event handling.
- Added `EventLoop::builder`, which is intended to replace the (now deprecated) `EventLoopBuilder::new`.
- **Breaking:** Changed the signature of `EventLoop::with_user_event` to return a builder.
- **Breaking:** Removed `EventLoopBuilder::with_user_event`, the functionality is now available in `EventLoop::with_user_event`.
- Add `Window::builder`, which is intended to replace the (now deprecated) `WindowBuilder::new`.
- On X11, reload dpi on `_XSETTINGS_SETTINGS` update.
- On X11, fix deadlock when adjusting DPI and resizing at the same time.
- On Wayland, fix `Focused(false)` being send when other seats still have window focused.
- On Wayland, fix `Window::set_{min,max}_inner_size` not always applied.
- On Windows, fix inconsistent resizing behavior with multi-monitor setups when repositioning outside the event loop.
- On Wayland, fix `WAYLAND_SOCKET` not used when detecting platform.
- **Breaking:** Move some types to the `winit-core` crate. Most types are
re-exported verbatim; however:
- `Event`, `WindowEvent` and `KeyEvent` now take a generic that `winit`
implements.
- `ActivationToken` and `InnerSizeWriter` expose new methods to make them
useful.
- `InnerSizeWriter::request_inner_size` returns an `InnerSizeIgnored` error
instead of an `ExternalError`.
# 0.29.10
- On Web, account for canvas being focused already before event loop starts.
- On Web, increase cursor position accuracy.
# 0.29.9
- On X11, fix `NotSupported` error not propagated when creating event loop.
- On Wayland, fix resize not issued when scale changes
- On X11 and Wayland, fix arrow up on keypad reported as `ArrowLeft`.
- On macOS, report correct logical key when Ctrl or Cmd is pressed.
# 0.29.8
- On X11, fix IME input lagging behind.
- On X11, fix `ModifiersChanged` not sent from xdotool-like input
- On X11, fix keymap not updated from xmodmap.
- On X11, reduce the amount of time spent fetching screen resources.
- On Wayland, fix `Window::request_inner_size` being overwritten by resize.
- On Wayland, fix `Window::inner_size` not using the correct rounding.
# 0.29.7
- On X11, fix `Xft.dpi` reload during runtime.
- On X11, fix window minimize.
# 0.29.6
- On Web, fix context menu not being disabled by `with_prevent_default(true)`.
- On Wayland, fix `WindowEvent::Destroyed` not being delivered after destroying window.
- Fix `EventLoopExtRunOnDemand::run_on_demand` not working for consequent invocation
# 0.29.5
- On macOS, remove spurious error logging when handling `Fn`.
- On X11, fix an issue where floating point data from the server is
misinterpreted during a drag and drop operation.
- On X11, fix a bug where focusing the window would panic.
- On macOS, fix `refresh_rate_millihertz`.
- On Wayland, disable Client Side Decorations when `wl_subcompositor` is not supported.
- On X11, fix `Xft.dpi` detection from Xresources.
- On Windows, fix consecutive calls to `window.set_fullscreen(Some(Fullscreen::Borderless(None)))` resulting in losing previous window state when eventually exiting fullscreen using `window.set_fullscreen(None)`.
- On Wayland, fix resize being sent on focus change.
- On Windows, fix `set_ime_cursor_area`.
# 0.29.4
- Fix crash when running iOS app on macOS.
- On X11, check common alternative cursor names when loading cursor.
- On X11, reload the DPI after a property change event.
- On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread.
- On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account.
- On macOS, send a `Resized` event after each `ScaleFactorChanged` event.
- On Wayland, fix `wl_surface` being destroyed before associated objects.
- On macOS, fix assertion when pressing `Fn` key.
- On Windows, add `WindowBuilderExtWindows::with_clip_children` to control `WS_CLIPCHILDREN` style.
# 0.29.3
- On Wayland, apply correct scale to `PhysicalSize` passed in `WindowBuilder::with_inner_size` when possible.
- On Wayland, fix `RedrawRequsted` being always sent without decorations and `sctk-adwaita` feature.
- On Wayland, ignore resize requests when the window is fully tiled.
- On Wayland, use `configure_bounds` to constrain `with_inner_size` when compositor wants users to pick size.
- On Windows, fix deadlock when accessing the state during `Cursor{Enter,Leave}`.
- On Windows, add support for `Window::set_transparent`.
- On macOS, fix deadlock when entering a nested event loop from an event handler.
- On macOS, add support for `Window::set_blur`.
# 0.29.2
- **Breaking:** Bump MSRV from `1.60` to `1.65`.
- **Breaking:** Add `Event::MemoryWarning`; implemented on iOS/Android.
- **Breaking:** Bump `ndk` version to `0.8.0`, ndk-sys to `0.5.0`, `android-activity` to `0.5.0`.
- **Breaking:** Change default `ControlFlow` from `Poll` to `Wait`.
- **Breaking:** remove `DeviceEvent::Text`.
- On Android, fix `DeviceId` to contain device id's.
- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now.
- On Web, fix `ControlFlow::WaitUntil` to never wake up **before** the given time.
- Add `Window::show_window_menu` to request a titlebar/system menu; implemented on Wayland/Windows for now.
- On iOS, send events `WindowEvent::Occluded(false)`, `WindowEvent::Occluded(true)` when application enters/leaves foreground.
- **Breaking** add `Event::MemoryWarning`; implemented on iOS/Android.
- On Wayland, support `Occluded` event with xdg-shell v6
# 0.29.1-beta
- **Breaking:** Bump `ndk` version to `0.8.0-beta.0`, ndk-sys to `v0.5.0-beta.0`, `android-activity` to `0.5.0-beta.1`.
- **Breaking:** Bump MSRV from `1.64` to `1.65`.
- Make iOS windows usable from other threads.
- Reexport `raw-window-handle` in `window` module.
- **Breaking:** `WINIT_UNIX_BACKEND` was removed in favor of standard `WAYLAND_DISPLAY` and `DISPLAY` variables.
- **Breaking:** Move `Event::RedrawRequested` to `WindowEvent::RedrawRequested`.
- **Breaking:** Moved `ControlFlow::Exit` to `EventLoopWindowTarget::exit()` and `EventLoopWindowTarget::exiting()` and removed `ControlFlow::ExitWithCode(_)` entirely.
- **Breaking:** Moved `ControlFlow` to `EventLoopWindowTarget::set_control_flow()` and `EventLoopWindowTarget::control_flow()`.
- **Breaking:** `EventLoop::new` and `EventLoopBuilder::build` now return `Result<Self, EventLoopError>`
- On X11, set `visual_id` in returned `raw-window-handle`.
- **Breaking:** on Wayland, dispatching user created wayland queue won't wake up the loop unless winit has event to send back.
- **Breaking:** `WINIT_UNIX_BACKEND` was removed in favor of standard `WAYLAND_DISPLAY` and `DISPLAY` variables.
- **Breaking:** on Wayland, dispatching user created Wayland queue won't wake up the loop unless winit has event to send back.
- **Breaking:** remove `DeviceEvent::Text`.
- **Breaking:** Remove lifetime parameter from `Event` and `WindowEvent`.
- **Breaking:** Rename `Window::set_inner_size` to `Window::request_inner_size` and indicate if the size was applied immediately.
- **Breaking:** `ActivationTokenDone` event which could be requested with the new `startup_notify` module, see its docs for more.
- **Breaking:** `ScaleFactorChanged` now contains a writer instead of a reference to update inner size.
- **Breaking** `run() -> !` has been replaced by `run() -> Result<(), EventLoopError>` for returning errors without calling `std::process::exit()` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
- **Breaking** Removed `EventLoopExtRunReturn` / `run_return` in favor of `EventLoopExtPumpEvents` / `pump_events` and `EventLoopExtRunOnDemand` / `run_on_demand` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
- `RedrawRequested` is no longer guaranteed to be emitted after `MainEventsCleared`, it is now platform-specific when the event is emitted after being requested via `redraw_request()`.
- On Windows, `RedrawRequested` is now driven by `WM_PAINT` messages which are requested via `redraw_request()`
- **Breaking** `LoopDestroyed` renamed to `LoopExiting` ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking** `RedrawEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking** `MainEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking:** Remove all deprecated `modifiers` fields.
- **Breaking:** Rename `DeviceEventFilter` to `DeviceEvents` reversing the behavior of variants.
- **Breaking** Add `AboutToWait` event which is emitted when the event loop is about to block and wait for new events ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking:** Rename `EventLoopWindowTarget::set_device_event_filter` to `listen_device_events`.
- **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone.
- **Breaking:** `with_x11_visual` now takes the visual ID instead of the bare pointer.
- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events on Wayland, X11, Windows, macOS and Web.
- **Breaking:** On Web, `instant` is now replaced by `web_time`.
- **Breaking:** On Web, dropped support for Safari versions below 13.1.
- **Breaking:** On Web, the canvas output bitmap size is no longer adjusted.
- **Breaking:** On Web, the canvas size is not controlled by Winit anymore and external changes to the canvas size will be reported through `WindowEvent::Resized`.
- **Breaking:** Updated `bitflags` crate version to `2`, which changes the API on exposed types.
- **Breaking:** `CursorIcon::Arrow` was removed.
- **Breaking:** `CursorIcon::Hand` is now named `CursorIcon::Pointer`.
- **Breaking:** `CursorIcon` is now used from the `cursor-icon` crate.
- **Breaking:** `WindowExtWebSys::canvas()` now returns an `Option`.
- **Breaking:** Overhaul keyboard input handling.
- Replace `KeyboardInput` with `KeyEvent` and `RawKeyEvent`.
- Change `WindowEvent::KeyboardInput` to contain a `KeyEvent`.
- Change `Event::Key` to contain a `RawKeyEvent`.
- Remove `Event::ReceivedCharacter`. In its place, you should use
`KeyEvent.text` in combination with `WindowEvent::Ime`.
- Replace `VirtualKeyCode` with the `Key` enum.
- Replace `ScanCode` with the `KeyCode` enum.
- Rename `ModifiersState::LOGO` to `SUPER` and `ModifiersState::CTRL` to `CONTROL`.
- Add `PhysicalKey` wrapping `KeyCode` and `NativeKeyCode`.
- Add `KeyCode` to refer to keys (roughly) by their physical location.
- Add `NativeKeyCode` to represent raw `KeyCode`s which Winit doesn't
understand.
- Add `Key` to represent the keys after they've been interpreted by the
active (software) keyboard layout.
- Add `NamedKey` to represent the categorized keys.
- Add `NativeKey` to represent raw `Key`s which Winit doesn't understand.
- Add `KeyLocation` to tell apart `Key`s which usually "mean" the same thing,
but can appear simultaneously in different spots on the same keyboard
layout.
- Add `Window::reset_dead_keys` to enable application-controlled cancellation
of dead key sequences.
- Add `KeyEventExtModifierSupplement` to expose additional (and less
portable) interpretations of a given key-press.
- Add `PhysicalKeyExtScancode`, which lets you convert between scancodes and
`PhysicalKey`.
- `ModifiersChanged` now uses dedicated `Modifiers` struct.
- Removed platform-specific extensions that should be retrieved through `raw-window-handle` trait implementations instead:
- `platform::windows::HINSTANCE`.
- `WindowExtWindows::hinstance`.
@@ -61,126 +209,89 @@ And please only add new entries to the top of this list, right below the `# Unre
- `WindowExtX11::xlib_display`.
- `WindowExtX11::xlib_screen_id`.
- `WindowExtX11::xcb_connection`.
- On Web, use `Window.requestAnimationFrame()` to throttle `RedrawRequested` events.
- On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor.
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.
- **Breaking:** Remove lifetime parameter from `Event` and `WindowEvent`.
- **Breaking:** `ScaleFactorChanged` now contains a writer instead of a reference to update inner size.
- On iOS, always wake the event loop when transitioning from `ControlFlow::Poll` to `ControlFlow::Poll`.
- **Breaking:** `ActivationTokenDone` event which could be requested with the new `startup_notify` module, see its docs for more.
- On Wayland, make double clicking and moving the CSD frame more reliable.
- On macOS, add tabbing APIs on `WindowExtMacOS` and `EventLoopWindowTargetExtMacOS`.
- **Breaking:** Rename `Window::set_inner_size` to `Window::request_inner_size` and indicate if the size was applied immediately.
- On X11, fix false positive flagging of key repeats when pressing different keys with no release between presses.
- Implement `PartialOrd` and `Ord` for `Key`, `KeyCode`, `NativeKey`, and `NativeKeyCode`.
- Reexport `raw-window-handle` in `window` module.
- Add `ElementState::is_pressed`.
- On Web, implement `WindowEvent::Occluded`.
- On Web, fix touch location to be as accurate as mouse position.
- On Web, account for CSS `padding`, `border`, and `margin` when getting or setting the canvas position.
- On Web, add Fullscreen API compatibility for Safari.
- On Web, implement `Window::set_(min|max)_inner_size()`.
- On Web, fix some `Window` methods using incorrect HTML attributes instead of CSS properties.
- On Web, fix some `WindowBuilder` methods doing nothing.
- On Web, implement `Window::focus_window()`.
- On Web, remove unnecessary `Window::is_dark_mode()`, which was replaced with `Window::theme()`.
- On Web, add `WindowBuilderExtWebSys::with_append()` to append the canvas element to the web page on creation.
- On Windows, add `drag_resize_window` method support.
- **Breaking** `run() ->!` has been replaced by `run() -> Result<(), EventLoopError>` for returning errors without calling `std::process::exit()` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
- **Breaking** Removed `EventLoopExtRunReturn` / `run_return` in favor of `EventLoopExtPumpEvents` / `pump_events` and `EventLoopExtRunOnDemand` / `run_ondemand` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
- `RedrawRequested` is no longer guaranteed to be emitted after `MainEventsCleared`, it is now platform-specific when the event is emitted after being requested via `redraw_request()`.
- On Windows, `RedrawRequested` is now driven by `WM_PAINT` messages which are requested via `redraw_request()`
- **Breaking** `LoopDestroyed` renamed to `LoopExiting` ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking** `RedrawEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking** `MainEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- Added `AboutToWait` event which is emitted when the event loop is about to block and wait for new events ([#2900](https://github.com/rust-windowing/winit/issues/2900))
- **Breaking:** `with_x11_visual` now takes the visual ID instead of the bare pointer.
- On X11, add a `with_embedded_parent_window` function to the window builder to allow embedding a window into another window.
- On iOS, add force data to touch events when using the Apple Pencil.
- On Android, add force data to touch events.
# 0.29.0-beta.0
- On Web, allow event loops to be recreated with `spawn`.
- **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone.
- On Android, changed default behavior of Android to ignore volume keys letting the operating system handle them.
- On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually.
- **Breaking:** Rename `DeviceEventFilter` to `DeviceEvents` reversing the behavior of variants.
- **Breaking:** Rename `EventLoopWindowTarget::set_device_event_filter` to `listen_device_events`.
- On X11, fix `EventLoopWindowTarget::listen_device_events` effect being reversed.
- **Breaking:** Remove all deprecated `modifiers` fields.
- **Breaking:** Overhaul keyboard input handling.
- Replace `KeyboardInput` with `KeyEvent` and `RawKeyEvent`.
- Change `WindowEvent::KeyboardInput` to contain a `KeyEvent`.
- Change `Event::Key` to contain a `RawKeyEvent`.
- Remove `Event::ReceivedCharacter`. In its place, you should use
`KeyEvent.text` in combination with `WindowEvent::Ime`.
- Replace `VirtualKeyCode` with the `Key` enum.
- Replace `ScanCode` with the `KeyCode` enum.
- Rename `ModifiersState::LOGO` to `SUPER` and `ModifiersState::CTRL` to `CONTROL`.
- Add `KeyCode` to refer to keys (roughly) by their physical location.
- Add `NativeKeyCode` to represent raw `KeyCode`s which Winit doesn't
understand.
- Add `Key` to represent the keys after they've been interpreted by the
active (software) keyboard layout.
- Add `NativeKey` to represent raw `Key`s which Winit doesn't understand.
- Add `KeyLocation` to tell apart `Key`s which usually "mean" the same thing,
but can appear simultaneously in different spots on the same keyboard
layout.
- Add `Window::reset_dead_keys` to enable application-controlled cancellation
of dead key sequences.
- Add `KeyEventExtModifierSupplement` to expose additional (and less
portable) interpretations of a given key-press.
- Add `KeyCodeExtScancode`, which lets you convert between raw keycodes and
`KeyCode`.
- `ModifiersChanged` now uses dedicated `Modifiers` struct.
- On Orbital, fix `ModifiersChanged` not being sent.
- **Breaking:** `CursorIcon` is now used from the `cursor-icon` crate.
- **Breaking:** `CursorIcon::Hand` is now named `CursorIcon::Pointer`.
- **Breaking:** `CursorIcon::Arrow` was removed.
- On Wayland, fix maximized startup not taking full size on GNOME.
- On Wayland, fix initial window size not restored for maximized/fullscreened on startup window.
- On Wayland, `Window::outer_size` now accounts for **client side** decorations.
- On Wayland, fix window not checking that it actually got initial configure event.
- On Wayland, fix maximized window creation and window geometry handling.
- On Wayland, fix forward compatibility issues.
- On Wayland, add `Window::drag_resize_window` method.
- On Wayland, drop `WINIT_WAYLAND_CSD_THEME` variable.
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now.
- Add `Window::show_window_menu` to request a titlebar/system menu; implemented on Wayland/Windows for now.
- Implement `AsFd`/`AsRawFd` for `EventLoop<T>` on X11 and Wayland.
- Implement `PartialOrd` and `Ord` for `MouseButton`.
- Implement `PartialOrd` and `Ord` on types in the `dpi` module.
- **Breaking:** Bump MSRV from `1.60` to `1.64`.
- **Breaking:** On Web, the canvas output bitmap size is no longer adjusted.
- On Web: fix `Window::request_redraw` not waking the event loop when called from outside the loop.
- On Web: fix position of touch events to be relative to the canvas.
- On Web, fix `Window:::set_fullscreen` doing nothing when called outside the event loop but during
a transient activation.
- On Web, fix pointer button events not being processed when a buttons is already pressed.
- **Breaking:** Updated `bitflags` crate version to `2`, which changes the API on exposed types.
- On Web, handle coalesced pointer events, which increases the resolution of pointer inputs.
- **Breaking:** On Web, `instant` is now replaced by `web_time`.
- On Windows, port to `windows-sys` version 0.48.0.
- On Web, fix pen treated as mouse input.
- On Web, send mouse position on button release as well.
- On Web, fix touch input not gaining or loosing focus.
- **Breaking:** On Web, dropped support for Safari versions below 13.1.
- On Web, prevent clicks on the canvas to select text.
- Make `WindowBuilder` `Send + Sync`.
- Make iOS `MonitorHandle` and `VideoMode` usable from other threads.
- Make iOS windows usable from other threads.
- On Android, add force data to touch events.
- On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually.
- On Android, fix `DeviceId` to contain device id's.
- On Orbital, fix `ModifiersChanged` not being sent.
- On Wayland, `Window::outer_size` now accounts for **client side** decorations.
- On Wayland, add `Window::drag_resize_window` method.
- On Wayland, remove `WINIT_WAYLAND_CSD_THEME` variable.
- On Wayland, fix `TouchPhase::Canceled` being sent for moved events.
- On Wayland, fix forward compatibility issues.
- On Wayland, fix initial window size not restored for maximized/fullscreened on startup window.
- On Wayland, fix maximized startup not taking full size on GNOME.
- On Wayland, fix maximized window creation and window geometry handling.
- On Wayland, fix window not checking that it actually got initial configure event.
- On Wayland, make double clicking and moving the CSD frame more reliable.
- On Wayland, support `Occluded` event with xdg-shell v6
- On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor.
- On Web, `ControlFlow::WaitUntil` now uses the Prioritized Task Scheduling API. `setTimeout()`, with a trick to circumvent throttling to 4ms, is used as a fallback.
- On Web, `EventLoopProxy` now implements `Send`.
- On Web, `Window` now implements `Send` and `Sync`.
- **Breaking:** `WindowExtWebSys::canvas()` now returns an `Option`.
- On Web, use the correct canvas size when calculating the new size during scale factor change,
instead of using the output bitmap size.
- On Web, scale factor and dark mode detection are now more robust.
- On Web, fix the bfcache by not using the `beforeunload` event and map bfcache loading/unloading to `Suspended`/`Resumed` events.
- On Web, fix scale factor resize suggestion always overwriting the canvas size.
- On macOS, fix crash when dropping `Window`.
- On Web, use `Window.requestIdleCallback()` for `ControlFlow::Poll` when available.
- **Breaking:** On Web, the canvas size is not controlled by Winit anymore and external changes to
the canvas size will be reported through `WindowEvent::Resized`.
- On Web, respect `EventLoopWindowTarget::listen_device_events()` settings.
- On Web, account for CSS `padding`, `border`, and `margin` when getting or setting the canvas position.
- On Web, add Fullscreen API compatibility for Safari.
- On Web, add `DeviceEvent::Motion`, `DeviceEvent::MouseWheel`, `DeviceEvent::Button` and `DeviceEvent::Key` support.
- On Web, add `EventLoopWindowTargetExtWebSys` and `PollStrategy`, which allows to set different strategies for `ControlFlow::Poll`. By default the Prioritized Task Scheduling API is used, but an option to use `Window.requestIdleCallback` is available as well. Both use `setTimeout()`, with a trick to circumvent throttling to 4ms, as a fallback.
- On Web, add `WindowBuilderExtWebSys::with_append()` to append the canvas element to the web page on creation.
- On Web, allow event loops to be recreated with `spawn`.
- On Web, enable event propagation.
- On Web, fix `ControlFlow::WaitUntil` to never wake up **before** the given time.
- On Web, fix `DeviceEvent::MouseMotion` only being emitted for each canvas instead of the whole window.
- On Web, add `DeviceEvent::Motion`, `DeviceEvent::MouseWheel`, `DeviceEvent::Button` and
`DeviceEvent::Key` support.
- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events
on Wayland, X11, Windows, macOS and Web.
- On Web, fix `Window:::set_fullscreen` doing nothing when called outside the event loop but during transient activation.
- On Web, fix pen treated as mouse input.
- On Web, fix pointer button events not being processed when a buttons is already pressed.
- On Web, fix scale factor resize suggestion always overwriting the canvas size.
- On Web, fix some `WindowBuilder` methods doing nothing.
- On Web, fix some `Window` methods using incorrect HTML attributes instead of CSS properties.
- On Web, fix the bfcache by not using the `beforeunload` event and map bfcache loading/unloading to `Suspended`/`Resumed` events.
- On Web, fix touch input not gaining or loosing focus.
- On Web, fix touch location to be as accurate as mouse position.
- On Web, handle coalesced pointer events, which increases the resolution of pointer inputs.
- On Web, implement `Window::focus_window()`.
- On Web, implement `Window::set_(min|max)_inner_size()`.
- On Web, implement `WindowEvent::Occluded`.
- On Web, never return a `MonitorHandle`.
- On Web, prevent clicks on the canvas to select text.
- On Web, remove any fullscreen requests from the queue when an external fullscreen activation was detected.
- On Web, remove unnecessary `Window::is_dark_mode()`, which was replaced with `Window::theme()`.
- On Web, respect `EventLoopWindowTarget::listen_device_events()` settings.
- On Web, scale factor and dark mode detection are now more robust.
- On Web, send mouse position on button release as well.
- On Web, take all transient activations on the canvas and window into account to queue a fullscreen request.
- On Web, use `Window.requestAnimationFrame()` to throttle `RedrawRequested` events.
- On Web, use the correct canvas size when calculating the new size during scale factor change, instead of using the output bitmap size.
- On Web: fix `Window::request_redraw` not waking the event loop when called from outside the loop.
- On Web: fix position of touch events to be relative to the canvas.
- On Windows, add `drag_resize_window` method support.
- On Windows, add horizontal MouseWheel `DeviceEvent`.
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.
- On Windows, fix IME APIs not working when from non event loop thread.
- On Windows, fix `CursorEnter/Left` not being sent when grabbing the mouse.
- On Windows, fix `RedrawRequested` not being delivered when calling `Window::request_redraw` from `RedrawRequested`.
- On Windows, port to `windows-sys` version 0.48.0.
- On X11, add a `with_embedded_parent_window` function to the window builder to allow embedding a window into another window.
- On X11, fix event loop not waking up on `ControlFlow::Poll` and `ControlFlow::WaitUntil`.
- On X11, fix false positive flagging of key repeats when pressing different keys with no release between presses.
- On X11, set `visual_id` in returned `raw-window-handle`.
- On iOS, add ability to change the status bar style.
- On iOS, add force data to touch events when using the Apple Pencil.
- On iOS, always wake the event loop when transitioning from `ControlFlow::Poll` to `ControlFlow::Poll`.
- On iOS, send events `WindowEvent::Occluded(false)`, `WindowEvent::Occluded(true)` when application enters/leaves foreground.
- On macOS, add tabbing APIs on `WindowExtMacOS` and `EventLoopWindowTargetExtMacOS`.
- On macOS, fix assertion when pressing `Globe` key.
- On macOS, fix crash in `window.set_minimized(false)`.
- On macOS, fix crash when dropping `Window`.
# 0.28.7

View File

@@ -11,7 +11,7 @@ may be worth creating a separate crate that extends Winit's API to add that func
When reporting an issue, in order to help the maintainers understand what the problem is, please make
your description of the issue as detailed as possible:
- if it is a bug, please provide clear explanation of what happens, what should happen, and how to
- if it is a bug, please provide a clear explanation of what happens, what should happen, and how to
reproduce the issue, ideally by providing a minimal program exhibiting the problem
- if it is a feature request, please provide a clear argumentation about why you believe this feature
should be supported by winit
@@ -20,8 +20,8 @@ your description of the issue as detailed as possible:
When making a code contribution to winit, before opening your pull request, please make sure that:
- your patch builds with Winit's minimal supported rust version - Rust 1.65.
- you tested your modifications on all the platforms impacted, or if not possible detail which platforms
- your patch builds with Winit's minimal supported rust version - Rust 1.70.
- you tested your modifications on all the platforms impacted, or if not possible, detail which platforms
were not tested, and what should be tested, so that a maintainer or another contributor can test them
- you updated any relevant documentation in winit
- you left comments in your code explaining any part that is not straightforward, so that the
@@ -34,7 +34,7 @@ When making a code contribution to winit, before opening your pull request, plea
relevant sections in [`FEATURES.md`](https://github.com/rust-windowing/winit/blob/master/FEATURES.md#features)
should be updated.
Once your PR is open, you can ask for review by a maintainer of your platform. Winit's merging policy
Once your PR is open, you can ask for a review by a maintainer of your platform. Winit's merging policy
is that a PR must be approved by at least two maintainers of winit before being merged, including
at least a maintainer of the platform (a maintainer making a PR themselves counts as approving it).
@@ -46,27 +46,26 @@ Once your PR is deemed ready, the merging maintainer will take care of resolving
The current maintainers are listed in the [CODEOWNERS](.github/CODEOWNERS) file.
If you are interested in being pinged when testing is needed for a certain platform, please add yourself to the [Testers and Contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors) table!
If you are interested in being pinged when testing is needed for a specific platform, please add yourself to the [Testers and Contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors) table!
## Release process
Given that winit is a widely used library we should be able to make a patch
Given that winit is a widely used library, we should be able to make a patch
releases at any time we want without blocking the development of new features.
To achieve these goals, a new branch is created for every new release. Releases
and later patch releases are committed and tagged in this branch.
To achieve these goals, a new branch is created for every new release. Releases and later patch releases are committed and tagged in this branch.
The exact steps for an exemplary `0.2.0` release might look like this:
1. Initially the version on the latest master is `0.1.0`
1. Initially, the version on the latest master is `0.1.0`
2. A new `v0.2.x` branch is created for the release
3. In the branch, the version is bumped to `v0.2.0`
4. The new commit in the branch is tagged `v0.2.0`
5. The version is pushed to crates.io
6. A GitHub release is created for the `v0.2.0` tag
7. On master, the version is bumped to `0.2.0` and the CHANGELOG is updated
7. On master, the version is bumped to `0.2.0`, and the CHANGELOG is updated
When doing a patch release the process is similar:
1. Initially the version of the latest release is `0.2.0`
When doing a patch release, the process is similar:
1. Initially, the version of the latest release is `0.2.0`
2. Checkout the `v0.2.x` branch
3. Cherry-pick the required non-breaking changes into the `v0.2.x`
4. Follow steps 3-7 of the regular release example

View File

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

View File

@@ -1,6 +1,6 @@
# Winit Scope
Winit aims to expose an interface that abstracts over window creation and input handling, and can
Winit aims to expose an interface that abstracts over window creation and input handling and can
be used to create both games and applications. It supports the following main graphical platforms:
- Desktop
- Windows 7+ (10+ is tested regularly)
@@ -45,10 +45,10 @@ be released and the library will enter maintenance mode. For the most part, new
be added past this point. New platform features may be accepted and exposed through point releases.
### Tier upgrades
Some platform features could in theory be exposed across multiple platforms, but have not gone
Some platform features could, in theory, be exposed across multiple platforms, but have not gone
through the implementation work necessary to function on all platforms. When one of these features
gets implemented across all platforms, a PR can be opened to upgrade the feature to a core feature.
If that gets accepted, the platform-specific functions gets deprecated and become permanently
If that gets accepted, the platform-specific functions get deprecated and become permanently
exposed through the core, cross-platform API.
# Features
@@ -88,7 +88,7 @@ If your PR makes notable changes to Winit's features, please update this section
- **Fullscreen toggle**: The windows created by winit can be switched to and from fullscreen after
creation.
- **Exclusive fullscreen**: Winit allows changing the video mode of the monitor
for fullscreen windows, and if applicable, captures the monitor for exclusive
for fullscreen windows and, if applicable, captures the monitor for exclusive
use by this application.
- **HiDPI support**: Winit assists developers in appropriately scaling HiDPI content.
- **Popup / modal windows**: Windows can be created relative to the client area of other windows, and parent
@@ -105,7 +105,8 @@ If your PR makes notable changes to Winit's features, please update this section
- **Mouse set location**: Forcibly changing the location of the pointer.
- **Cursor locking**: Locking the cursor inside the window so it cannot move.
- **Cursor confining**: Confining the cursor to the window bounds so it cannot leave them.
- **Cursor icon**: Changing the cursor icon, or hiding the cursor.
- **Cursor icon**: Changing the cursor icon or hiding the cursor.
- **Cursor image**: Changing the cursor to your own image.
- **Cursor hittest**: Handle or ignore mouse events for a window.
- **Touch events**: Single-touch events.
- **Touch pressure**: Touch events contain information about the amount of force being applied.
@@ -125,6 +126,11 @@ If your PR makes notable changes to Winit's features, please update this section
* Setting a menu bar
* `WS_EX_NOREDIRECTIONBITMAP` support
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme
* Changing a system-drawn backdrop
* Setting the window border color
* Setting the title bar background color
* Setting the title color
* Setting the corner rounding preference
### macOS
* Window activation policy
@@ -150,13 +156,13 @@ If your PR makes notable changes to Winit's features, please update this section
* Setting the `UIView` hidpi factor
* Valid orientations
* Home indicator visibility
* Status bar visibility
* Deferrring system gestures
* Status bar visibility and style
* Deferring system gestures
* Getting the device idiom
* Getting the preferred video mode
### Web
* Get if systems preferred color scheme is "dark"
* Get if the systems preferred color scheme is "dark"
## Usability
* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial)
@@ -166,7 +172,7 @@ If your PR makes notable changes to Winit's features, please update this section
Legend:
- ✔️: Works as intended
- ▢: Mostly works but some bugs are known
- ▢: Mostly works, but some bugs are known
- ❌: Missing feature or large bugs making it unusable
- **N/A**: Not applicable for this platform
- ❓: Unknown status
@@ -206,6 +212,7 @@ Legend:
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |❌ |
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|Cursor image |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|Cursor hittest |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A** |
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |

View File

@@ -2,21 +2,20 @@
The winit maintainers would like to recognize the following former winit
contributors, without whom winit would not exist in its current form. We thank
them deeply for their time and efforts, and wish them best of luck in their
them deeply for their time and efforts and wish them the best of luck in their
future endeavors:
* [@tomaka]: For creating the winit project and guiding it through its early
years of existence.
* [@vberger]: For diligently creating the Wayland backend, and being its
* [@vberger]: For diligently creating the Wayland backend and being its
extremely helpful and benevolent maintainer for years.
* [@francesca64]: For taking over the responsibility of maintaining almost every
winit backend, and standardizing HiDPI support across all of them.
* [@Osspial]: For heroically landing EventLoop 2.0, and valiantly ushering in a
winit backend and standardizing HiDPI support across all of them.
* [@Osspial]: For heroically landing EventLoop 2.0 and valiantly ushering in a
vastly more sustainable era of winit.
* [@goddessfreya]: For selflessly taking over maintainership of glutin, and her
* [@goddessfreya]: For selflessly taking over maintainership of glutin and her
stellar dedication to improving both winit and glutin.
* [@ArturKovacs]: For consistently maintaining the macOS backend, and his
immense involvement in designing and implementing the new keyboard API.
* [@ArturKovacs]: For consistently maintaining the macOS backend and for his immense involvement in designing and implementing the new keyboard API.
[@tomaka]: https://github.com/tomaka
[@vberger]: https://github.com/vberger

View File

@@ -2,11 +2,13 @@
[![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit)
[![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit)
[![Master Docs](https://img.shields.io/github/actions/workflow/status/rust-windowing/winit/docs.yml?branch=master&label=master%20docs
)](https://rust-windowing.github.io/winit/winit/index.html)
[![CI Status](https://github.com/rust-windowing/winit/workflows/CI/badge.svg)](https://github.com/rust-windowing/winit/actions)
```toml
[dependencies]
winit = "0.29.1-beta"
winit = "0.29.10"
```
## [Documentation](https://docs.rs/winit)
@@ -17,47 +19,20 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
## 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)
[![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit)
The maintainers have a meeting every friday at UTC 14. The meeting notes can be found [here](https://hackmd.io/@winit-meetings).
## Usage
Winit is a window creation and management library. It can create windows and lets you handle
events (for example: the window being resized, a key being pressed, a mouse movement, etc.)
produced by window.
produced by the window.
Winit is designed to be a low-level brick in a hierarchy of libraries. Consequently, in order to
show something on the window you need to use the platform-specific getters provided by winit, or
another library.
```rust
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
window::WindowBuilder,
};
fn main() {
let event_loop = EventLoop::new();
event_loop.set_control_flow(ControlFlow::Wait);
let window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(move |event, elwt| {
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => elwt.exit(),
_ => (),
}
});
}
```
Winit is only officially supported on the latest stable version of the Rust compiler.
### Cargo Features
Winit provides the following features, which can be enabled in your `Cargo.toml` file:
@@ -68,7 +43,7 @@ Winit provides the following features, which can be enabled in your `Cargo.toml`
## MSRV Policy
The Minimum Supported Rust Version (MSRV) of this crate is **1.65**. Changes to
This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to
the MSRV will be accompanied by a minor version bump.
As a **tentative** policy, the upper bound of the MSRV is given by the following
@@ -79,12 +54,11 @@ min(sid, stable - 3)
```
Where `sid` is the current version of `rustc` provided by [Debian Sid], and
`stable` is the latest stable version of Rust. This bound may be broken in the
event of a major ecosystem shift or a security vulnerability.
`stable` is the latest stable version of Rust. This bound may be broken in case of a major ecosystem shift or a security vulnerability.
[Debian Sid]: https://packages.debian.org/sid/rustc
The exception to this is for the Android platform, where a higher Rust version
The exception is for the Android platform, where a higher Rust version
must be used for certain Android features. In this case, the MSRV will be
capped at the latest stable version of Rust minus three. This inconsistency is
not reflected in Cargo metadata, as it is not powerful enough to expose this
@@ -101,7 +75,7 @@ same MSRV policy.
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`
@@ -112,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
insert it into the DOM yourself.
For 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
book].
@@ -135,14 +109,14 @@ glue crate (prior to `0.28` it used
The version of the glue crate that your application depends on _must_ match the
version that Winit depends on because the glue crate is responsible for your
application's main entrypoint. If Cargo resolves multiple versions they will
application's main entry point. If Cargo resolves multiple versions, they will
clash.
`winit` glue compatibility table:
| winit | ndk-glue |
| :---: | :--------------------------: |
| 0.29.1-beta | `android-activity = "0.5.0-beta.1"` |
| 0.29 | `android-activity = "0.5"` |
| 0.28 | `android-activity = "0.4"` |
| 0.27 | `ndk-glue = "0.7"` |
| 0.26 | `ndk-glue = "0.5"` |
@@ -153,7 +127,7 @@ The recommended way to avoid a conflict with the glue version is to avoid explic
depending on the `android-activity` crate, and instead consume the API that
is re-exported by Winit under `winit::platform::android::activity::*`
Running on an Android device needs a dynamic system library, add this to Cargo.toml:
Running on an Android device needs a dynamic system library. Add this to Cargo.toml:
```toml
[lib]
@@ -161,14 +135,14 @@ name = "main"
crate-type = ["cdylib"]
```
All Android applications are based on an `Activity` subclass and the
All Android applications are based on an `Activity` subclass, and the
`android-activity` crate is designed to support different choices for this base
class. Your application _must_ specify the base class it needs via a feature flag:
| Base Class | Feature Flag | Notes |
| :--------------: | :---------------: | :-----: |
| `NativeActivity` | `android-native-activity` | Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass `NativeActivity` to access some platform features. It does not derive from the [`AndroidAppCompat`] base class.|
| [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`] which is a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
| [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`], a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
[`GameActivity`]: https://developer.android.com/games/agdk/game-activity
[`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input
@@ -177,40 +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
[Gradle]: https://developer.android.com/studio/build
For example, add this to Cargo.toml:
```toml
winit = { version = "0.29.1-beta", features = [ "android-native-activity" ] }
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.11.0"
```
And, for example, define an entry point for your library like this:
```rust
#[cfg(target_os = "android")]
use winit::platform::android::activity::AndroidApp;
#[cfg(target_os = "android")]
#[no_mangle]
fn android_main(app: AndroidApp) {
use winit::platform::android::EventLoopBuilderExtAndroid;
android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Trace));
let event_loop = EventLoopBuilder::with_user_event()
.with_android_app(app)
.build();
_main(event_loop);
}
```
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`
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`
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.1-beta", 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).
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
@@ -221,13 +168,13 @@ doing anything; this includes creating windows, fetching monitors, drawing,
and so on, see issues [#2238], [#2051] and [#2087].
If you encounter problems, you should try doing your initialization inside
`Event::NewEvents(StartCause::Init)`.
`Event::Resumed`.
#### iOS
Similar to macOS, iOS's main `UIApplicationMain` does some init work that's required
by all UI related code, see issue [#1705]. You should consider creating your windows
inside `Event::NewEvents(StartCause::Init)`.
by all UI-related code (see issue [#1705]). It would be best to consider creating your windows
inside `Event::Resumed`.
[#2238]: https://github.com/rust-windowing/winit/issues/2238
@@ -237,5 +184,5 @@ inside `Event::NewEvents(StartCause::Init)`.
#### Redox OS
Redox OS has some functionality not present yet, that will be implemented when
Redox OS has some functionality not yet present that will be implemented when
its orbital display server provides it.

View File

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

View File

@@ -34,15 +34,10 @@ skip = [
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
{ name = "libloading" }, # x11rb uses a different version until the next update
{ name = "syn" }, # https://github.com/rust-mobile/ndk/issues/392
{ name = "num_enum"}, # See above ^, waiting for release
{ name = "num_enum_derive"}, # See above ^, waiting for release
{ name = "miniz_oxide"}, # https://github.com/rust-lang/flate2-rs/issues/340
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
{ name = "foreign-types-shared" }, # https://github.com/servo/core-foundation-rs/issues/634
{ name = "foreign-types" }, # See above ^, waiting for release
]
skip-tree = []
skip-tree = [
{ name = "run-wasm", version = "0.1.0" }
]
[licenses]

View File

@@ -5,7 +5,7 @@ These images are used in the documentation of `winit`.
## keyboard_*.svg
These files are a modified version of "[ANSI US QWERTY (Windows)](https://commons.wikimedia.org/wiki/File:ANSI_US_QWERTY_(Windows).svg)"
by [Tomiĉo] (https://commons.wikimedia.org/wiki/User:Tomi%C4%89o). It is
by [Tomiĉo] (https://commons.wikimedia.org/wiki/User:Tomi%C4%89o). It was
originally released under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)
License. Minor modifications have been made by [John Nunley](https://github.com/notgull),
which have been released under the same license as a derivative work.

View File

@@ -1,86 +0,0 @@
//! Fill the window buffer with a solid color.
//!
//! Launching a window without drawing to it has unpredictable results varying from platform to
//! platform. In order to have well-defined examples, this module provides an easy way to
//! fill the window buffer with a solid color.
//!
//! 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.
use winit::window::Window;
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
pub(super) fn fill_window(window: &Window) {
use softbuffer::{Context, Surface};
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::ManuallyDrop;
use std::num::NonZeroU32;
use winit::window::WindowId;
/// The graphics context used to draw to a window.
struct GraphicsContext {
/// The global softbuffer context.
context: Context,
/// The hash map of window IDs to surfaces.
surfaces: HashMap<WindowId, Surface>,
}
impl GraphicsContext {
fn new(w: &Window) -> Self {
Self {
context: unsafe { Context::new(w) }.expect("Failed to create a softbuffer context"),
surfaces: HashMap::new(),
}
}
fn surface(&mut self, w: &Window) -> &mut Surface {
self.surfaces.entry(w.id()).or_insert_with(|| {
unsafe { Surface::new(&self.context, w) }
.expect("Failed to create a softbuffer surface")
})
}
}
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));
}
GC.with(|gc| {
// 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;
let size = window.inner_size();
surface
.resize(
NonZeroU32::new(size.width).expect("Width must be greater than zero"),
NonZeroU32::new(size.height).expect("Height must be greater than zero"),
)
.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")))))]
pub(super) fn fill_window(_window: &Window) {
// No-op on mobile platforms.
}

View File

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

View File

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

View File

@@ -1,30 +0,0 @@
#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
use crate::keyboard::KeyCode;
// TODO: Describe what this value contains for each platform
/// Additional methods for the [`KeyCode`] type that allow the user to access the platform-specific
/// scancode.
///
/// [`KeyCode`]: crate::keyboard::KeyCode
pub trait KeyCodeExtScancode {
/// The raw value of the platform-specific physical key identifier.
///
/// Returns `Some(key_id)` if the conversion was succesful; returns `None` otherwise.
///
/// ## Platform-specific
/// - **Windows:** A 16bit extended scancode
/// - **Wayland/X11**: A 32-bit linux scancode, which is X11/Wayland keycode subtracted by 8.
fn to_scancode(self) -> Option<u32>;
/// Constructs a `KeyCode` from a platform-specific physical key identifier.
///
/// Note that this conversion may be lossy, i.e. converting the returned `KeyCode` back
/// using `to_scancode` might not yield the original value.
///
/// ## Platform-specific
/// - **Wayland/X11**: A 32-bit linux scancode. When building from X11/Wayland keycode subtract
/// `8` to get the value you wanted.
fn from_scancode(scancode: u32) -> KeyCode;
}

View File

@@ -1,8 +0,0 @@
use x11_dl::xmd::CARD32;
pub use x11_dl::{
error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*,
};
// Isn't defined by x11_dl
#[allow(non_upper_case_globals)]
pub const IconicState: CARD32 = 3;

View File

@@ -1,76 +0,0 @@
use std::ffi::CString;
use x11rb::connection::Connection;
use crate::window::CursorIcon;
use super::*;
impl XConnection {
pub fn set_cursor_icon(&self, window: xproto::Window, cursor: Option<CursorIcon>) {
let cursor = *self
.cursor_cache
.lock()
.unwrap()
.entry(cursor)
.or_insert_with(|| self.get_cursor(cursor));
self.update_cursor(window, cursor)
.expect("Failed to set cursor");
}
fn create_empty_cursor(&self) -> ffi::Cursor {
let data = 0;
let pixmap = unsafe {
let screen = (self.xlib.XDefaultScreen)(self.display);
let window = (self.xlib.XRootWindow)(self.display, screen);
(self.xlib.XCreateBitmapFromData)(self.display, window, &data, 1, 1)
};
if pixmap == 0 {
panic!("failed to allocate pixmap for cursor");
}
unsafe {
// We don't care about this color, since it only fills bytes
// in the pixmap which are not 0 in the mask.
let mut dummy_color = MaybeUninit::uninit();
let cursor = (self.xlib.XCreatePixmapCursor)(
self.display,
pixmap,
pixmap,
dummy_color.as_mut_ptr(),
dummy_color.as_mut_ptr(),
0,
0,
);
(self.xlib.XFreePixmap)(self.display, pixmap);
cursor
}
}
fn get_cursor(&self, cursor: Option<CursorIcon>) -> ffi::Cursor {
let cursor = match cursor {
Some(cursor) => cursor,
None => return self.create_empty_cursor(),
};
let name = CString::new(cursor.name()).unwrap();
unsafe {
(self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char)
}
}
fn update_cursor(&self, window: xproto::Window, cursor: ffi::Cursor) -> Result<(), X11Error> {
self.xcb_connection()
.change_window_attributes(
window,
&xproto::ChangeWindowAttributesAux::new().cursor(cursor as xproto::Cursor),
)?
.ignore_error();
self.xcb_connection().flush()?;
Ok(())
}
}

View File

@@ -1,100 +0,0 @@
#![allow(clippy::unnecessary_cast)]
use icrate::Foundation::NSObject;
use objc2::{declare_class, msg_send, mutability, ClassType};
use super::appkit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
use super::{app_state::AppState, DEVICE_ID};
use crate::event::{DeviceEvent, ElementState, Event};
declare_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(super) struct WinitApplication;
unsafe impl ClassType for WinitApplication {
#[inherits(NSResponder, NSObject)]
type Super = NSApplication;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "WinitApplication";
}
unsafe impl WinitApplication {
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
#[method(sendEvent:)]
fn send_event(&self, event: &NSEvent) {
// For posterity, there are some undocumented event types
// (https://github.com/servo/cocoa-rs/issues/155)
// but that doesn't really matter here.
let event_type = event.type_();
let modifier_flags = event.modifierFlags();
if event_type == NSEventType::NSKeyUp
&& modifier_flags.contains(NSEventModifierFlags::NSCommandKeyMask)
{
if let Some(key_window) = self.keyWindow() {
unsafe { key_window.sendEvent(event) };
}
} else {
maybe_dispatch_device_event(event);
unsafe { msg_send![super(self), sendEvent: event] }
}
}
}
);
fn maybe_dispatch_device_event(event: &NSEvent) {
let event_type = event.type_();
match event_type {
NSEventType::NSMouseMoved
| NSEventType::NSLeftMouseDragged
| NSEventType::NSOtherMouseDragged
| NSEventType::NSRightMouseDragged => {
let delta_x = event.deltaX() as f64;
let delta_y = event.deltaY() as f64;
if delta_x != 0.0 {
queue_device_event(DeviceEvent::Motion {
axis: 0,
value: delta_x,
});
}
if delta_y != 0.0 {
queue_device_event(DeviceEvent::Motion {
axis: 1,
value: delta_y,
})
}
if delta_x != 0.0 || delta_y != 0.0 {
queue_device_event(DeviceEvent::MouseMotion {
delta: (delta_x, delta_y),
});
}
}
NSEventType::NSLeftMouseDown
| NSEventType::NSRightMouseDown
| NSEventType::NSOtherMouseDown => {
queue_device_event(DeviceEvent::Button {
button: event.buttonNumber() as u32,
state: ElementState::Pressed,
});
}
NSEventType::NSLeftMouseUp | NSEventType::NSRightMouseUp | NSEventType::NSOtherMouseUp => {
queue_device_event(DeviceEvent::Button {
button: event.buttonNumber() as u32,
state: ElementState::Released,
});
}
_ => (),
}
}
fn queue_device_event(event: DeviceEvent) {
let event = Event::DeviceEvent {
device_id: DEVICE_ID,
event,
};
AppState::queue_event(event);
}

View File

@@ -1,79 +0,0 @@
use std::ptr::NonNull;
use icrate::Foundation::NSObject;
use objc2::declare::{IvarBool, IvarEncode};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType};
use super::app_state::AppState;
use super::appkit::NSApplicationActivationPolicy;
declare_class!(
#[derive(Debug)]
pub(super) struct ApplicationDelegate {
activation_policy: IvarEncode<NSApplicationActivationPolicy, "_activation_policy">,
default_menu: IvarBool<"_default_menu">,
activate_ignoring_other_apps: IvarBool<"_activate_ignoring_other_apps">,
}
mod ivars;
unsafe impl ClassType for ApplicationDelegate {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "WinitApplicationDelegate";
}
unsafe impl ApplicationDelegate {
#[method(initWithActivationPolicy:defaultMenu:activateIgnoringOtherApps:)]
unsafe fn init(
this: *mut Self,
activation_policy: NSApplicationActivationPolicy,
default_menu: bool,
activate_ignoring_other_apps: bool,
) -> Option<NonNull<Self>> {
let this: Option<&mut Self> = unsafe { msg_send![super(this), init] };
this.map(|this| {
*this.activation_policy = activation_policy;
*this.default_menu = default_menu;
*this.activate_ignoring_other_apps = activate_ignoring_other_apps;
NonNull::from(this)
})
}
#[method(applicationDidFinishLaunching:)]
fn did_finish_launching(&self, _sender: Option<&AnyObject>) {
trace_scope!("applicationDidFinishLaunching:");
AppState::launched(
*self.activation_policy,
*self.default_menu,
*self.activate_ignoring_other_apps,
);
}
#[method(applicationWillTerminate:)]
fn will_terminate(&self, _sender: Option<&AnyObject>) {
trace_scope!("applicationWillTerminate:");
// TODO: Notify every window that it will be destroyed, like done in iOS?
AppState::internal_exit();
}
}
);
impl ApplicationDelegate {
pub(super) fn new(
activation_policy: NSApplicationActivationPolicy,
default_menu: bool,
activate_ignoring_other_apps: bool,
) -> Id<Self> {
unsafe {
msg_send_id![
Self::alloc(),
initWithActivationPolicy: activation_policy,
defaultMenu: default_menu,
activateIgnoringOtherApps: activate_ignoring_other_apps,
]
}
}
}

View File

@@ -1,692 +0,0 @@
use std::{
cell::{RefCell, RefMut},
collections::VecDeque,
fmt::{self, Debug},
mem,
rc::{Rc, Weak},
sync::{
atomic::{AtomicBool, Ordering},
mpsc, Arc, Mutex, MutexGuard,
},
time::Instant,
};
use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp};
use icrate::Foundation::{is_main_thread, NSSize};
use objc2::rc::{autoreleasepool, Id};
use once_cell::sync::Lazy;
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent};
use super::{
event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never, window::WinitWindow,
};
use crate::{
dpi::PhysicalSize,
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
window::WindowId,
};
static HANDLER: Lazy<Handler> = Lazy::new(Default::default);
impl<Never> Event<Never> {
fn userify<T: 'static>(self) -> Event<T> {
self.map_nonuser_event()
// `Never` can't be constructed, so the `UserEvent` variant can't
// be present here.
.unwrap_or_else(|_| unreachable!())
}
}
pub trait EventHandler: Debug {
// Not sure probably it should accept Event<'static, Never>
fn handle_nonuser_event(&mut self, event: Event<Never>);
fn handle_user_events(&mut self);
}
pub(crate) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>;
struct EventLoopHandler<T: 'static> {
callback: Weak<Callback<T>>,
window_target: Rc<RootWindowTarget<T>>,
receiver: Rc<mpsc::Receiver<T>>,
}
impl<T> EventLoopHandler<T> {
fn with_callback<F>(&mut self, f: F)
where
F: FnOnce(&mut EventLoopHandler<T>, RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>)>),
{
// The `NSApp` and our `HANDLER` are global state and so it's possible that
// we could get a delegate callback after the application has exit an
// `EventLoop`. If the loop has been exit then our weak `self.callback`
// will fail to upgrade.
//
// We don't want to panic or output any verbose logging if we fail to
// upgrade the weak reference since it might be valid that the application
// re-starts the `NSApp` after exiting a Winit `EventLoop`
if let Some(callback) = self.callback.upgrade() {
let callback = callback.borrow_mut();
(f)(self, callback);
}
}
}
impl<T> Debug for EventLoopHandler<T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("EventLoopHandler")
.field("window_target", &self.window_target)
.finish()
}
}
impl<T> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>) {
self.with_callback(|this, mut callback| {
(callback)(event.userify(), &this.window_target);
});
}
fn handle_user_events(&mut self) {
self.with_callback(|this, mut callback| {
for event in this.receiver.try_iter() {
(callback)(Event::UserEvent(event), &this.window_target);
}
});
}
}
#[derive(Debug)]
enum EventWrapper {
StaticEvent(Event<Never>),
ScaleFactorChanged {
window: Id<WinitWindow>,
suggested_size: PhysicalSize<u32>,
scale_factor: f64,
},
}
#[derive(Default)]
struct Handler {
stop_app_on_launch: AtomicBool,
stop_app_before_wait: AtomicBool,
stop_app_after_wait: AtomicBool,
stop_app_on_redraw: AtomicBool,
launched: AtomicBool,
running: AtomicBool,
in_callback: AtomicBool,
control_flow: Mutex<ControlFlow>,
exit: AtomicBool,
start_time: Mutex<Option<Instant>>,
callback: Mutex<Option<Box<dyn EventHandler>>>,
pending_events: Mutex<VecDeque<EventWrapper>>,
pending_redraw: Mutex<Vec<WindowId>>,
wait_timeout: Mutex<Option<Instant>>,
waker: Mutex<EventLoopWaker>,
}
unsafe impl Send for Handler {}
unsafe impl Sync for Handler {}
impl Handler {
fn events(&self) -> MutexGuard<'_, VecDeque<EventWrapper>> {
self.pending_events.lock().unwrap()
}
fn redraw(&self) -> MutexGuard<'_, Vec<WindowId>> {
self.pending_redraw.lock().unwrap()
}
fn waker(&self) -> MutexGuard<'_, EventLoopWaker> {
self.waker.lock().unwrap()
}
/// `true` after `ApplicationDelegate::applicationDidFinishLaunching` called
///
/// NB: This is global / `NSApp` state and since the app will only be launched
/// once but an `EventLoop` may be run more than once then only the first
/// `EventLoop` will observe the `NSApp` before it is launched.
fn is_launched(&self) -> bool {
self.launched.load(Ordering::Acquire)
}
/// Set via `ApplicationDelegate::applicationDidFinishLaunching`
fn set_launched(&self) {
self.launched.store(true, Ordering::Release);
}
/// `true` if an `EventLoop` is currently running
///
/// NB: This is global / `NSApp` state and may persist beyond the lifetime of
/// a running `EventLoop`.
///
/// # Caveat
/// This is only intended to be called from the main thread
fn is_running(&self) -> bool {
self.running.load(Ordering::Relaxed)
}
/// Set when an `EventLoop` starts running, after the `NSApp` is launched
///
/// # Caveat
/// This is only intended to be called from the main thread
fn set_running(&self) {
self.running.store(true, Ordering::Relaxed);
}
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits
///
/// Since an `EventLoop` may be run more than once we need make sure to reset the
/// `control_flow` state back to `Poll` each time the loop exits.
///
/// Note: that if the `NSApp` has been launched then that state is preserved, and we won't
/// need to re-launch the app if subsequent EventLoops are run.
///
/// # Caveat
/// This is only intended to be called from the main thread
fn internal_exit(&self) {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interiour mutability
//
// XXX: As an aside; having each individual bit of state for `Handler` be atomic or wrapped in a
// `Mutex` for the sake of interior mutability seems a bit odd, and also a potential foot
// gun in case the state is unwittingly accessed across threads because the fine-grained locking
// wouldn't ensure that there's interior consistency.
//
// Maybe the whole thing should just be put in a static `Mutex<>` to make it clear
// the we can mutate more than one peice of state while maintaining consistency. (though it
// looks like there have been recuring re-entrancy issues with callback handling that might
// make that awkward)
self.running.store(false, Ordering::Relaxed);
self.set_stop_app_on_redraw_requested(false);
self.set_stop_app_before_wait(false);
self.set_stop_app_after_wait(false);
self.set_wait_timeout(None);
}
pub fn exit(&self) {
self.exit.store(true, Ordering::Relaxed)
}
pub fn exiting(&self) -> bool {
self.exit.load(Ordering::Relaxed)
}
pub fn request_stop_app_on_launch(&self) {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability
self.stop_app_on_launch.store(true, Ordering::Relaxed);
}
pub fn should_stop_app_on_launch(&self) -> bool {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability
self.stop_app_on_launch.load(Ordering::Relaxed)
}
pub fn set_stop_app_before_wait(&self, stop_before_wait: bool) {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability
self.stop_app_before_wait
.store(stop_before_wait, Ordering::Relaxed);
}
pub fn should_stop_app_before_wait(&self) -> bool {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability
self.stop_app_before_wait.load(Ordering::Relaxed)
}
pub fn set_stop_app_after_wait(&self, stop_after_wait: bool) {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability
self.stop_app_after_wait
.store(stop_after_wait, Ordering::Relaxed);
}
pub fn set_wait_timeout(&self, new_timeout: Option<Instant>) {
let mut timeout = self.wait_timeout.lock().unwrap();
*timeout = new_timeout;
}
pub fn wait_timeout(&self) -> Option<Instant> {
*self.wait_timeout.lock().unwrap()
}
pub fn should_stop_app_after_wait(&self) -> bool {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability
self.stop_app_after_wait.load(Ordering::Relaxed)
}
pub fn set_stop_app_on_redraw_requested(&self, stop_on_redraw: bool) {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability
self.stop_app_on_redraw
.store(stop_on_redraw, Ordering::Relaxed);
}
pub fn should_stop_app_on_redraw_requested(&self) -> bool {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability
self.stop_app_on_redraw.load(Ordering::Relaxed)
}
fn set_control_flow(&self, new_control_flow: ControlFlow) {
*self.control_flow.lock().unwrap() = new_control_flow
}
fn control_flow(&self) -> ControlFlow {
*self.control_flow.lock().unwrap()
}
fn get_start_time(&self) -> Option<Instant> {
*self.start_time.lock().unwrap()
}
fn update_start_time(&self) {
*self.start_time.lock().unwrap() = Some(Instant::now());
}
fn take_events(&self) -> VecDeque<EventWrapper> {
mem::take(&mut *self.events())
}
fn should_redraw(&self) -> Vec<WindowId> {
mem::take(&mut *self.redraw())
}
fn get_in_callback(&self) -> bool {
self.in_callback.load(Ordering::Acquire)
}
fn set_in_callback(&self, in_callback: bool) {
self.in_callback.store(in_callback, Ordering::Release);
}
fn have_callback(&self) -> bool {
self.callback.lock().unwrap().is_some()
}
fn handle_nonuser_event(&self, event: Event<Never>) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
callback.handle_nonuser_event(event)
}
}
fn handle_user_events(&self) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
callback.handle_user_events();
}
}
fn handle_scale_factor_changed_event(
&self,
window: &WinitWindow,
suggested_size: PhysicalSize<u32>,
scale_factor: f64,
) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
let new_inner_size = Arc::new(Mutex::new(suggested_size));
let event = Event::WindowEvent {
window_id: WindowId(window.id()),
event: WindowEvent::ScaleFactorChanged {
scale_factor,
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
},
};
callback.handle_nonuser_event(event);
let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size);
let logical_size = physical_size.to_logical(scale_factor);
let size = NSSize::new(logical_size.width, logical_size.height);
window.setContentSize(size);
}
}
}
pub(crate) enum AppState {}
impl AppState {
/// Associate the application's event callback with the (global static) Handler state
///
/// # Safety
/// This is ignoring the lifetime of the application callback (which may not be 'static)
/// and can lead to undefined behaviour if the callback is not cleared before the end of
/// its real lifetime.
///
/// All public APIs that take an event callback (`run`, `run_on_demand`,
/// `pump_events`) _must_ pair a call to `set_callback` with
/// a call to `clear_callback` before returning to avoid undefined behaviour.
pub unsafe fn set_callback<T>(
callback: Weak<Callback<T>>,
window_target: Rc<RootWindowTarget<T>>,
receiver: Rc<mpsc::Receiver<T>>,
) {
*HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler {
callback,
window_target,
receiver,
}));
}
pub fn clear_callback() {
HANDLER.callback.lock().unwrap().take();
}
pub fn is_launched() -> bool {
HANDLER.is_launched()
}
pub fn is_running() -> bool {
HANDLER.is_running()
}
// If `pump_events` is called to progress the event loop then we bootstrap the event
// loop via `[NSApp run]` but will use `CFRunLoopRunInMode` for subsequent calls to
// `pump_events`
pub fn request_stop_on_launch() {
HANDLER.request_stop_app_on_launch();
}
pub fn set_stop_app_before_wait(stop_before_wait: bool) {
HANDLER.set_stop_app_before_wait(stop_before_wait);
}
pub fn set_stop_app_after_wait(stop_after_wait: bool) {
HANDLER.set_stop_app_after_wait(stop_after_wait);
}
pub fn set_wait_timeout(timeout: Option<Instant>) {
HANDLER.set_wait_timeout(timeout);
}
pub fn set_stop_app_on_redraw_requested(stop_on_redraw: bool) {
HANDLER.set_stop_app_on_redraw_requested(stop_on_redraw);
}
pub fn set_control_flow(control_flow: ControlFlow) {
HANDLER.set_control_flow(control_flow)
}
pub fn control_flow() -> ControlFlow {
HANDLER.control_flow()
}
pub fn internal_exit() {
HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::LoopExiting);
HANDLER.set_in_callback(false);
HANDLER.internal_exit();
Self::clear_callback();
}
pub fn exit() {
HANDLER.exit()
}
pub fn exiting() -> bool {
HANDLER.exiting()
}
pub fn dispatch_init_events() {
HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init));
// NB: For consistency all platforms must emit a 'resumed' event even though macOS
// applications don't themselves have a formal suspend/resume lifecycle.
HANDLER.handle_nonuser_event(Event::Resumed);
HANDLER.set_in_callback(false);
}
pub fn start_running() {
debug_assert!(HANDLER.is_launched());
HANDLER.set_running();
Self::dispatch_init_events()
}
pub fn launched(
activation_policy: NSApplicationActivationPolicy,
create_default_menu: bool,
activate_ignoring_other_apps: bool,
) {
let app = NSApp();
// We need to delay setting the activation policy and activating the app
// until `applicationDidFinishLaunching` has been called. Otherwise the
// menu bar is initially unresponsive on macOS 10.15.
app.setActivationPolicy(activation_policy);
window_activation_hack(&app);
app.activateIgnoringOtherApps(activate_ignoring_other_apps);
HANDLER.set_launched();
HANDLER.waker().start();
if create_default_menu {
// The menubar initialization should be before the `NewEvents` event, to allow
// overriding of the default menu even if it's created
menu::initialize();
}
Self::start_running();
// If the `NSApp` is being launched via `EventLoop::pump_events()` then we'll
// want to stop the app once it is launched (and return to the external loop)
//
// In this case we still want to consider Winit's `EventLoop` to be "running",
// so we call `start_running()` above.
if HANDLER.should_stop_app_on_launch() {
// Note: the original idea had been to only stop the underlying `RunLoop`
// for the app but that didn't work as expected (`[NSApp run]` effectively
// ignored the attempt to stop the RunLoop and re-started it.). So we
// return from `pump_events` by stopping the `NSApp`
Self::stop();
}
}
// Called by RunLoopObserver after finishing waiting for new events
pub fn wakeup(panic_info: Weak<PanicInfo>) {
let panic_info = panic_info
.upgrade()
.expect("The panic info must exist here. This failure indicates a developer error.");
// Return when in callback due to https://github.com/rust-windowing/winit/issues/1779
if panic_info.is_panicking()
|| !HANDLER.have_callback()
|| !HANDLER.is_running()
|| HANDLER.get_in_callback()
{
return;
}
if HANDLER.should_stop_app_after_wait() {
Self::stop();
}
let start = HANDLER.get_start_time().unwrap();
let cause = match HANDLER.control_flow() {
ControlFlow::Poll => StartCause::Poll,
ControlFlow::Wait => StartCause::WaitCancelled {
start,
requested_resume: None,
},
ControlFlow::WaitUntil(requested_resume) => {
if Instant::now() >= requested_resume {
StartCause::ResumeTimeReached {
start,
requested_resume,
}
} else {
StartCause::WaitCancelled {
start,
requested_resume: Some(requested_resume),
}
}
}
};
HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::NewEvents(cause));
HANDLER.set_in_callback(false);
}
// This is called from multiple threads at present
pub fn queue_redraw(window_id: WindowId) {
let mut pending_redraw = HANDLER.redraw();
if !pending_redraw.contains(&window_id) {
pending_redraw.push(window_id);
}
unsafe {
let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl);
}
}
pub fn handle_redraw(window_id: WindowId) {
// Redraw request might come out of order from the OS.
// -> Don't go back into the callback when our callstack originates from there
if !HANDLER.in_callback.swap(true, Ordering::AcqRel) {
HANDLER.handle_nonuser_event(Event::WindowEvent {
window_id,
event: WindowEvent::RedrawRequested,
});
HANDLER.set_in_callback(false);
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested events
// as a way to ensure that `pump_events` can't block an external loop indefinitely
if HANDLER.should_stop_app_on_redraw_requested() {
AppState::stop();
}
}
}
pub fn queue_event(event: Event<Never>) {
if !is_main_thread() {
panic!("Event queued from different thread: {event:#?}");
}
HANDLER.events().push_back(EventWrapper::StaticEvent(event));
}
pub fn queue_static_scale_factor_changed_event(
window: Id<WinitWindow>,
suggested_size: PhysicalSize<u32>,
scale_factor: f64,
) {
HANDLER
.events()
.push_back(EventWrapper::ScaleFactorChanged {
window,
suggested_size,
scale_factor,
});
}
pub fn stop() {
let app = NSApp();
autoreleasepool(|_| {
app.stop(None);
// To stop event loop immediately, we need to post some event here.
app.postEvent_atStart(&NSEvent::dummy(), true);
});
}
// Called by RunLoopObserver before waiting for new events
pub fn cleared(panic_info: Weak<PanicInfo>) {
let panic_info = panic_info
.upgrade()
.expect("The panic info must exist here. This failure indicates a developer error.");
// Return when in callback due to https://github.com/rust-windowing/winit/issues/1779
// XXX: how does it make sense that `get_in_callback()` can ever return `true` here if we're
// about to return to the `CFRunLoop` to poll for new events?
if panic_info.is_panicking()
|| !HANDLER.have_callback()
|| !HANDLER.is_running()
|| HANDLER.get_in_callback()
{
return;
}
HANDLER.set_in_callback(true);
HANDLER.handle_user_events();
for event in HANDLER.take_events() {
match event {
EventWrapper::StaticEvent(event) => {
HANDLER.handle_nonuser_event(event);
}
EventWrapper::ScaleFactorChanged {
window,
suggested_size,
scale_factor,
} => {
HANDLER.handle_scale_factor_changed_event(
&window,
suggested_size,
scale_factor,
);
}
}
}
for window_id in HANDLER.should_redraw() {
HANDLER.handle_nonuser_event(Event::WindowEvent {
window_id,
event: WindowEvent::RedrawRequested,
});
}
HANDLER.handle_nonuser_event(Event::AboutToWait);
HANDLER.set_in_callback(false);
if HANDLER.exiting() {
Self::stop();
}
if HANDLER.should_stop_app_before_wait() {
Self::stop();
}
HANDLER.update_start_time();
let wait_timeout = HANDLER.wait_timeout(); // configured by pump_events
let app_timeout = match HANDLER.control_flow() {
ControlFlow::Wait => None,
ControlFlow::Poll => Some(Instant::now()),
ControlFlow::WaitUntil(instant) => Some(instant),
};
HANDLER
.waker()
.start_at(min_timeout(wait_timeout, app_timeout));
}
}
/// Returns the minimum `Option<Instant>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`)
fn min_timeout(a: Option<Instant>, b: Option<Instant>) -> Option<Instant> {
a.map_or(b, |a_timeout| {
b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout)))
})
}
/// A hack to make activation of multiple windows work when creating them before
/// `applicationDidFinishLaunching:` / `Event::Event::NewEvents(StartCause::Init)`.
///
/// Alternative to this would be the user calling `window.set_visible(true)` in
/// `StartCause::Init`.
///
/// If this becomes too bothersome to maintain, it can probably be removed
/// without too much damage.
fn window_activation_hack(app: &NSApplication) {
// TODO: Proper ordering of the windows
app.windows().into_iter().for_each(|window| {
// Call `makeKeyAndOrderFront` if it was called on the window in `WinitWindow::new`
// This way we preserve the user's desired initial visiblity status
// TODO: Also filter on the type/"level" of the window, and maybe other things?
if window.isVisible() {
trace!("Activating visible window");
window.makeKeyAndOrderFront(None);
} else {
trace!("Skipping activating invisible window");
}
})
}

View File

@@ -1,28 +0,0 @@
use icrate::Foundation::{NSArray, NSObject, NSString};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, mutability, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSAppearance;
unsafe impl ClassType for NSAppearance {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
type NSAppearanceName = NSString;
extern_methods!(
unsafe impl NSAppearance {
#[method_id(appearanceNamed:)]
pub fn appearanceNamed(name: &NSAppearanceName) -> Id<Self>;
#[method_id(bestMatchFromAppearancesWithNames:)]
pub fn bestMatchFromAppearancesWithNames(
&self,
appearances: &NSArray<NSAppearanceName>,
) -> Id<NSAppearanceName>;
}
);

View File

@@ -1,140 +0,0 @@
use icrate::Foundation::{MainThreadMarker, NSArray, NSInteger, NSObject, NSUInteger};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use objc2::{Encode, Encoding};
use super::{NSAppearance, NSEvent, NSMenu, NSResponder, NSWindow};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSApplication;
unsafe impl ClassType for NSApplication {
#[inherits(NSObject)]
type Super = NSResponder;
type Mutability = mutability::InteriorMutable;
}
);
pub(crate) fn NSApp() -> Id<NSApplication> {
// TODO: Only allow access from main thread
NSApplication::shared(unsafe { MainThreadMarker::new_unchecked() })
}
extern_methods!(
unsafe impl NSApplication {
/// This can only be called on the main thread since it may initialize
/// the application and since it's parameters may be changed by the main
/// thread at any time (hence it is only safe to access on the main thread).
pub fn shared(_mtm: MainThreadMarker) -> Id<Self> {
let app: Option<_> = unsafe { msg_send_id![Self::class(), sharedApplication] };
// SAFETY: `sharedApplication` always initializes the app if it isn't already
unsafe { app.unwrap_unchecked() }
}
#[method_id(currentEvent)]
pub fn currentEvent(&self) -> Option<Id<NSEvent>>;
#[method(postEvent:atStart:)]
pub fn postEvent_atStart(&self, event: &NSEvent, front_of_queue: bool);
#[method(presentationOptions)]
pub fn presentationOptions(&self) -> NSApplicationPresentationOptions;
#[method_id(windows)]
pub fn windows(&self) -> Id<NSArray<NSWindow>>;
#[method_id(keyWindow)]
pub fn keyWindow(&self) -> Option<Id<NSWindow>>;
// TODO: NSApplicationDelegate
#[method(setDelegate:)]
pub fn setDelegate(&self, delegate: &AnyObject);
#[method(setPresentationOptions:)]
pub fn setPresentationOptions(&self, options: NSApplicationPresentationOptions);
#[method(hide:)]
pub fn hide(&self, sender: Option<&AnyObject>);
#[method(orderFrontCharacterPalette:)]
#[allow(dead_code)]
pub fn orderFrontCharacterPalette(&self, sender: Option<&AnyObject>);
#[method(hideOtherApplications:)]
pub fn hideOtherApplications(&self, sender: Option<&AnyObject>);
#[method(stop:)]
pub fn stop(&self, sender: Option<&AnyObject>);
#[method(activateIgnoringOtherApps:)]
pub fn activateIgnoringOtherApps(&self, ignore: bool);
#[method(requestUserAttention:)]
pub fn requestUserAttention(&self, type_: NSRequestUserAttentionType) -> NSInteger;
#[method(setActivationPolicy:)]
pub fn setActivationPolicy(&self, policy: NSApplicationActivationPolicy) -> bool;
#[method(setMainMenu:)]
pub fn setMainMenu(&self, menu: &NSMenu);
#[method_id(effectiveAppearance)]
pub fn effectiveAppearance(&self) -> Id<NSAppearance>;
#[method(setAppearance:)]
pub fn setAppearance(&self, appearance: Option<&NSAppearance>);
#[method(run)]
pub unsafe fn run(&self);
}
);
#[allow(dead_code)]
#[repr(isize)] // NSInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSApplicationActivationPolicy {
NSApplicationActivationPolicyRegular = 0,
NSApplicationActivationPolicyAccessory = 1,
NSApplicationActivationPolicyProhibited = 2,
NSApplicationActivationPolicyERROR = -1,
}
unsafe impl Encode for NSApplicationActivationPolicy {
const ENCODING: Encoding = NSInteger::ENCODING;
}
bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct NSApplicationPresentationOptions: NSUInteger {
const NSApplicationPresentationDefault = 0;
const NSApplicationPresentationAutoHideDock = 1 << 0;
const NSApplicationPresentationHideDock = 1 << 1;
const NSApplicationPresentationAutoHideMenuBar = 1 << 2;
const NSApplicationPresentationHideMenuBar = 1 << 3;
const NSApplicationPresentationDisableAppleMenu = 1 << 4;
const NSApplicationPresentationDisableProcessSwitching = 1 << 5;
const NSApplicationPresentationDisableForceQuit = 1 << 6;
const NSApplicationPresentationDisableSessionTermination = 1 << 7;
const NSApplicationPresentationDisableHideApplication = 1 << 8;
const NSApplicationPresentationDisableMenuBarTransparency = 1 << 9;
const NSApplicationPresentationFullScreen = 1 << 10;
const NSApplicationPresentationAutoHideToolbar = 1 << 11;
}
}
unsafe impl Encode for NSApplicationPresentationOptions {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
#[repr(usize)] // NSUInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSRequestUserAttentionType {
NSCriticalRequest = 0,
NSInformationalRequest = 10,
}
unsafe impl Encode for NSRequestUserAttentionType {
const ENCODING: Encoding = NSUInteger::ENCODING;
}

View File

@@ -1,15 +0,0 @@
use icrate::Foundation::NSObject;
use objc2::{extern_class, mutability, ClassType};
use super::{NSControl, NSResponder, NSView};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSButton;
unsafe impl ClassType for NSButton {
#[inherits(NSView, NSResponder, NSObject)]
type Super = NSControl;
type Mutability = mutability::InteriorMutable;
}
);

View File

@@ -1,28 +0,0 @@
use icrate::Foundation::NSObject;
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, mutability, ClassType};
extern_class!(
/// An object that stores color data and sometimes opacity (alpha value).
///
/// <https://developer.apple.com/documentation/appkit/nscolor?language=objc>
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSColor;
unsafe impl ClassType for NSColor {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
// SAFETY: Documentation clearly states:
// > Color objects are immutable and thread-safe
unsafe impl Send for NSColor {}
unsafe impl Sync for NSColor {}
extern_methods!(
unsafe impl NSColor {
#[method_id(clearColor)]
pub fn clear() -> Id<Self>;
}
);

View File

@@ -1,25 +0,0 @@
use icrate::Foundation::NSObject;
use objc2::{extern_class, extern_methods, mutability, ClassType};
use super::{NSResponder, NSView};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSControl;
unsafe impl ClassType for NSControl {
#[inherits(NSResponder, NSObject)]
type Super = NSView;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl NSControl {
#[method(setEnabled:)]
pub fn setEnabled(&self, enabled: bool);
#[method(isEnabled)]
pub fn isEnabled(&self) -> bool;
}
);

View File

@@ -1,241 +0,0 @@
use once_cell::sync::Lazy;
use icrate::ns_string;
use icrate::Foundation::{
NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSString,
};
use objc2::rc::{DefaultId, Id};
use objc2::runtime::Sel;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, sel, ClassType};
use super::NSImage;
use crate::window::CursorIcon;
extern_class!(
/// <https://developer.apple.com/documentation/appkit/nscursor?language=objc>
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSCursor;
unsafe impl ClassType for NSCursor {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
// SAFETY: NSCursor is immutable, stated here:
// https://developer.apple.com/documentation/appkit/nscursor/1527062-image?language=objc
unsafe impl Send for NSCursor {}
unsafe impl Sync for NSCursor {}
macro_rules! def_cursor {
{$(
$(#[$($m:meta)*])*
pub fn $name:ident();
)*} => {$(
$(#[$($m)*])*
pub fn $name() -> Id<Self> {
unsafe { msg_send_id![Self::class(), $name] }
}
)*};
}
macro_rules! def_undocumented_cursor {
{$(
$(#[$($m:meta)*])*
pub fn $name:ident();
)*} => {$(
$(#[$($m)*])*
pub fn $name() -> Id<Self> {
unsafe { Self::from_selector(sel!($name)).unwrap_or_else(|| Default::default()) }
}
)*};
}
extern_methods!(
/// Documented cursors
unsafe impl NSCursor {
def_cursor!(
pub fn arrowCursor();
pub fn pointingHandCursor();
pub fn openHandCursor();
pub fn closedHandCursor();
pub fn IBeamCursor();
pub fn IBeamCursorForVerticalLayout();
pub fn dragCopyCursor();
pub fn dragLinkCursor();
pub fn operationNotAllowedCursor();
pub fn contextualMenuCursor();
pub fn crosshairCursor();
pub fn resizeRightCursor();
pub fn resizeUpCursor();
pub fn resizeLeftCursor();
pub fn resizeDownCursor();
pub fn resizeLeftRightCursor();
pub fn resizeUpDownCursor();
);
// Creating cursors should be thread-safe, though using them for anything probably isn't.
pub fn new(image: &NSImage, hotSpot: NSPoint) -> Id<Self> {
unsafe { msg_send_id![Self::alloc(), initWithImage: image, hotSpot: hotSpot] }
}
pub fn invisible() -> Id<Self> {
// 16x16 GIF data for invisible cursor
// You can reproduce this via ImageMagick.
// $ convert -size 16x16 xc:none cursor.gif
static CURSOR_BYTES: &[u8] = &[
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x84, 0x8F, 0xA9,
0xCB, 0xED, 0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B,
];
static CURSOR: Lazy<Id<NSCursor>> = Lazy::new(|| {
// TODO: Consider using `dataWithBytesNoCopy:`
let data = NSData::with_bytes(CURSOR_BYTES);
let image = NSImage::new_with_data(&data);
NSCursor::new(&image, NSPoint::new(0.0, 0.0))
});
CURSOR.clone()
}
}
/// Undocumented cursors
unsafe impl NSCursor {
#[method(respondsToSelector:)]
fn class_responds_to(sel: Sel) -> bool;
#[method_id(performSelector:)]
unsafe fn from_selector_unchecked(sel: Sel) -> Id<Self>;
unsafe fn from_selector(sel: Sel) -> Option<Id<Self>> {
if Self::class_responds_to(sel) {
Some(unsafe { Self::from_selector_unchecked(sel) })
} else {
warn!("Cursor `{:?}` appears to be invalid", sel);
None
}
}
def_undocumented_cursor!(
// Undocumented cursors: https://stackoverflow.com/a/46635398/5435443
pub fn _helpCursor();
pub fn _zoomInCursor();
pub fn _zoomOutCursor();
pub fn _windowResizeNorthEastCursor();
pub fn _windowResizeNorthWestCursor();
pub fn _windowResizeSouthEastCursor();
pub fn _windowResizeSouthWestCursor();
pub fn _windowResizeNorthEastSouthWestCursor();
pub fn _windowResizeNorthWestSouthEastCursor();
// While these two are available, the former just loads a white arrow,
// and the latter loads an ugly deflated beachball!
// pub fn _moveCursor();
// pub fn _waitCursor();
// An even more undocumented cursor...
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=522349
pub fn busyButClickableCursor();
);
}
/// Webkit cursors
unsafe impl NSCursor {
// Note that loading `busybutclickable` with this code won't animate
// the frames; instead you'll just get them all in a column.
unsafe fn load_webkit_cursor(name: &NSString) -> Id<Self> {
// Snatch a cursor from WebKit; They fit the style of the native
// cursors, and will seem completely standard to macOS users.
//
// https://stackoverflow.com/a/21786835/5435443
let root = ns_string!("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors");
let cursor_path = root.stringByAppendingPathComponent(name);
let pdf_path = cursor_path.stringByAppendingPathComponent(ns_string!("cursor.pdf"));
let image = NSImage::new_by_referencing_file(&pdf_path);
// TODO: Handle PLists better
let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist"));
let info: Id<NSDictionary<NSObject, NSObject>> = unsafe {
msg_send_id![
<NSDictionary<NSObject, NSObject>>::class(),
dictionaryWithContentsOfFile: &*info_path,
]
};
let mut x = 0.0;
if let Some(n) = info.get(&*ns_string!("hotx")) {
if n.is_kind_of::<NSNumber>() {
let ptr: *const NSObject = n;
let ptr: *const NSNumber = ptr.cast();
x = unsafe { &*ptr }.as_cgfloat()
}
}
let mut y = 0.0;
if let Some(n) = info.get(&*ns_string!("hotx")) {
if n.is_kind_of::<NSNumber>() {
let ptr: *const NSObject = n;
let ptr: *const NSNumber = ptr.cast();
y = unsafe { &*ptr }.as_cgfloat()
}
}
let hotspot = NSPoint::new(x, y);
Self::new(&image, hotspot)
}
pub fn moveCursor() -> Id<Self> {
unsafe { Self::load_webkit_cursor(ns_string!("move")) }
}
pub fn cellCursor() -> Id<Self> {
unsafe { Self::load_webkit_cursor(ns_string!("cell")) }
}
}
);
impl NSCursor {
pub fn from_icon(icon: CursorIcon) -> Id<Self> {
match icon {
CursorIcon::Default => Default::default(),
CursorIcon::Pointer => Self::pointingHandCursor(),
CursorIcon::Grab => Self::openHandCursor(),
CursorIcon::Grabbing => Self::closedHandCursor(),
CursorIcon::Text => Self::IBeamCursor(),
CursorIcon::VerticalText => Self::IBeamCursorForVerticalLayout(),
CursorIcon::Copy => Self::dragCopyCursor(),
CursorIcon::Alias => Self::dragLinkCursor(),
CursorIcon::NotAllowed | CursorIcon::NoDrop => Self::operationNotAllowedCursor(),
CursorIcon::ContextMenu => Self::contextualMenuCursor(),
CursorIcon::Crosshair => Self::crosshairCursor(),
CursorIcon::EResize => Self::resizeRightCursor(),
CursorIcon::NResize => Self::resizeUpCursor(),
CursorIcon::WResize => Self::resizeLeftCursor(),
CursorIcon::SResize => Self::resizeDownCursor(),
CursorIcon::EwResize | CursorIcon::ColResize => Self::resizeLeftRightCursor(),
CursorIcon::NsResize | CursorIcon::RowResize => Self::resizeUpDownCursor(),
CursorIcon::Help => Self::_helpCursor(),
CursorIcon::ZoomIn => Self::_zoomInCursor(),
CursorIcon::ZoomOut => Self::_zoomOutCursor(),
CursorIcon::NeResize => Self::_windowResizeNorthEastCursor(),
CursorIcon::NwResize => Self::_windowResizeNorthWestCursor(),
CursorIcon::SeResize => Self::_windowResizeSouthEastCursor(),
CursorIcon::SwResize => Self::_windowResizeSouthWestCursor(),
CursorIcon::NeswResize => Self::_windowResizeNorthEastSouthWestCursor(),
CursorIcon::NwseResize => Self::_windowResizeNorthWestSouthEastCursor(),
// This is the wrong semantics for `Wait`, but it's the same as
// what's used in Safari and Chrome.
CursorIcon::Wait | CursorIcon::Progress => Self::busyButClickableCursor(),
CursorIcon::Move | CursorIcon::AllScroll => Self::moveCursor(),
CursorIcon::Cell => Self::cellCursor(),
_ => Default::default(),
}
}
}
impl DefaultId for NSCursor {
fn default_id() -> Id<Self> {
Self::arrowCursor()
}
}

View File

@@ -1,308 +0,0 @@
use std::os::raw::c_ushort;
use icrate::Foundation::{
CGFloat, NSCopying, NSInteger, NSObject, NSPoint, NSString, NSTimeInterval, NSUInteger,
};
use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, mutability, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSEvent;
unsafe impl ClassType for NSEvent {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
// > Safely handled only on the same thread, whether that be the main thread
// > or a secondary thread; otherwise you run the risk of having events get
// > out of sequence.
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123383>
extern_methods!(
unsafe impl NSEvent {
#[method_id(
otherEventWithType:
location:
modifierFlags:
timestamp:
windowNumber:
context:
subtype:
data1:
data2:
)]
unsafe fn otherEventWithType(
type_: NSEventType,
location: NSPoint,
flags: NSEventModifierFlags,
time: NSTimeInterval,
window_num: NSInteger,
context: Option<&NSObject>, // NSGraphicsContext
subtype: NSEventSubtype,
data1: NSInteger,
data2: NSInteger,
) -> Id<Self>;
pub fn dummy() -> Id<Self> {
unsafe {
Self::otherEventWithType(
NSEventType::NSApplicationDefined,
NSPoint::new(0.0, 0.0),
NSEventModifierFlags::empty(),
0.0,
0,
None,
NSEventSubtype::NSWindowExposedEventType,
0,
0,
)
}
}
#[method_id(
keyEventWithType:
location:
modifierFlags:
timestamp:
windowNumber:
context:
characters:
charactersIgnoringModifiers:
isARepeat:
keyCode:
)]
pub fn keyEventWithType(
type_: NSEventType,
location: NSPoint,
modifier_flags: NSEventModifierFlags,
timestamp: NSTimeInterval,
window_num: NSInteger,
context: Option<&NSObject>,
characters: &NSString,
characters_ignoring_modifiers: &NSString,
is_a_repeat: bool,
scancode: c_ushort,
) -> Id<Self>;
#[method(locationInWindow)]
pub fn locationInWindow(&self) -> NSPoint;
// TODO: MainThreadMarker
#[method(pressedMouseButtons)]
pub fn pressedMouseButtons() -> NSUInteger;
#[method(modifierFlags)]
pub fn modifierFlags(&self) -> NSEventModifierFlags;
#[method(type)]
pub fn type_(&self) -> NSEventType;
#[method(keyCode)]
pub fn key_code(&self) -> c_ushort;
#[method(magnification)]
pub fn magnification(&self) -> CGFloat;
#[method(phase)]
pub fn phase(&self) -> NSEventPhase;
#[method(momentumPhase)]
pub fn momentumPhase(&self) -> NSEventPhase;
#[method(deltaX)]
pub fn deltaX(&self) -> CGFloat;
#[method(deltaY)]
pub fn deltaY(&self) -> CGFloat;
#[method(buttonNumber)]
pub fn buttonNumber(&self) -> NSInteger;
#[method(scrollingDeltaX)]
pub fn scrollingDeltaX(&self) -> CGFloat;
#[method(scrollingDeltaY)]
pub fn scrollingDeltaY(&self) -> CGFloat;
#[method(hasPreciseScrollingDeltas)]
pub fn hasPreciseScrollingDeltas(&self) -> bool;
#[method(rotation)]
pub fn rotation(&self) -> f32;
#[method(pressure)]
pub fn pressure(&self) -> f32;
#[method(stage)]
pub fn stage(&self) -> NSInteger;
#[method(isARepeat)]
pub fn is_a_repeat(&self) -> bool;
#[method(windowNumber)]
pub fn window_number(&self) -> NSInteger;
#[method(timestamp)]
pub fn timestamp(&self) -> NSTimeInterval;
#[method_id(characters)]
pub fn characters(&self) -> Option<Id<NSString>>;
#[method_id(charactersIgnoringModifiers)]
pub fn charactersIgnoringModifiers(&self) -> Option<Id<NSString>>;
pub fn lshift_pressed(&self) -> bool {
let raw_modifiers = self.modifierFlags().bits() as u32;
raw_modifiers & NX_DEVICELSHIFTKEYMASK != 0
}
pub fn rshift_pressed(&self) -> bool {
let raw_modifiers = self.modifierFlags().bits() as u32;
raw_modifiers & NX_DEVICERSHIFTKEYMASK != 0
}
pub fn lctrl_pressed(&self) -> bool {
let raw_modifiers = self.modifierFlags().bits() as u32;
raw_modifiers & NX_DEVICELCTLKEYMASK != 0
}
pub fn rctrl_pressed(&self) -> bool {
let raw_modifiers = self.modifierFlags().bits() as u32;
raw_modifiers & NX_DEVICERCTLKEYMASK != 0
}
pub fn lalt_pressed(&self) -> bool {
let raw_modifiers = self.modifierFlags().bits() as u32;
raw_modifiers & NX_DEVICELALTKEYMASK != 0
}
pub fn ralt_pressed(&self) -> bool {
let raw_modifiers = self.modifierFlags().bits() as u32;
raw_modifiers & NX_DEVICERALTKEYMASK != 0
}
pub fn lcmd_pressed(&self) -> bool {
let raw_modifiers = self.modifierFlags().bits() as u32;
raw_modifiers & NX_DEVICELCMDKEYMASK != 0
}
pub fn rcmd_pressed(&self) -> bool {
let raw_modifiers = self.modifierFlags().bits() as u32;
raw_modifiers & NX_DEVICERCMDKEYMASK != 0
}
}
);
unsafe impl NSCopying for NSEvent {}
// The values are from the https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h#L258-L259
const NX_DEVICELCTLKEYMASK: u32 = 0x00000001;
const NX_DEVICELSHIFTKEYMASK: u32 = 0x00000002;
const NX_DEVICERSHIFTKEYMASK: u32 = 0x00000004;
const NX_DEVICELCMDKEYMASK: u32 = 0x00000008;
const NX_DEVICERCMDKEYMASK: u32 = 0x00000010;
const NX_DEVICELALTKEYMASK: u32 = 0x00000020;
const NX_DEVICERALTKEYMASK: u32 = 0x00000040;
const NX_DEVICERCTLKEYMASK: u32 = 0x00002000;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NSEventModifierFlags: NSUInteger {
const NSAlphaShiftKeyMask = 1 << 16;
const NSShiftKeyMask = 1 << 17;
const NSControlKeyMask = 1 << 18;
const NSAlternateKeyMask = 1 << 19;
const NSCommandKeyMask = 1 << 20;
const NSNumericPadKeyMask = 1 << 21;
const NSHelpKeyMask = 1 << 22;
const NSFunctionKeyMask = 1 << 23;
const NSDeviceIndependentModifierFlagsMask = 0xffff0000;
}
}
unsafe impl Encode for NSEventModifierFlags {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NSEventPhase: NSUInteger {
const NSEventPhaseNone = 0;
const NSEventPhaseBegan = 0x1 << 0;
const NSEventPhaseStationary = 0x1 << 1;
const NSEventPhaseChanged = 0x1 << 2;
const NSEventPhaseEnded = 0x1 << 3;
const NSEventPhaseCancelled = 0x1 << 4;
const NSEventPhaseMayBegin = 0x1 << 5;
}
}
unsafe impl Encode for NSEventPhase {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
#[allow(dead_code)]
#[repr(i16)] // short
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSEventSubtype {
// TODO: Not sure what these values are
// NSMouseEventSubtype = NX_SUBTYPE_DEFAULT,
// NSTabletPointEventSubtype = NX_SUBTYPE_TABLET_POINT,
// NSTabletProximityEventSubtype = NX_SUBTYPE_TABLET_PROXIMITY
// NSTouchEventSubtype = NX_SUBTYPE_MOUSE_TOUCH,
NSWindowExposedEventType = 0,
NSApplicationActivatedEventType = 1,
NSApplicationDeactivatedEventType = 2,
NSWindowMovedEventType = 4,
NSScreenChangedEventType = 8,
NSAWTEventType = 16,
}
unsafe impl Encode for NSEventSubtype {
const ENCODING: Encoding = i16::ENCODING;
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(usize)] // NSUInteger
pub enum NSEventType {
NSLeftMouseDown = 1,
NSLeftMouseUp = 2,
NSRightMouseDown = 3,
NSRightMouseUp = 4,
NSMouseMoved = 5,
NSLeftMouseDragged = 6,
NSRightMouseDragged = 7,
NSMouseEntered = 8,
NSMouseExited = 9,
NSKeyDown = 10,
NSKeyUp = 11,
NSFlagsChanged = 12,
NSAppKitDefined = 13,
NSSystemDefined = 14,
NSApplicationDefined = 15,
NSPeriodic = 16,
NSCursorUpdate = 17,
NSScrollWheel = 22,
NSTabletPoint = 23,
NSTabletProximity = 24,
NSOtherMouseDown = 25,
NSOtherMouseUp = 26,
NSOtherMouseDragged = 27,
NSEventTypeGesture = 29,
NSEventTypeMagnify = 30,
NSEventTypeSwipe = 31,
NSEventTypeRotate = 18,
NSEventTypeBeginGesture = 19,
NSEventTypeEndGesture = 20,
NSEventTypePressure = 34,
}
unsafe impl Encode for NSEventType {
const ENCODING: Encoding = NSUInteger::ENCODING;
}

View File

@@ -1,36 +0,0 @@
use icrate::Foundation::{NSData, NSObject, NSString};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
extern_class!(
// TODO: Can this be mutable?
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSImage;
unsafe impl ClassType for NSImage {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
// Documented Thread-Unsafe, but:
// > One thread can create an NSImage object, draw to the image buffer,
// > and pass it off to the main thread for drawing. The underlying image
// > cache is shared among all threads.
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-126728>
//
// So really only unsafe to mutate on several threads.
unsafe impl Send for NSImage {}
unsafe impl Sync for NSImage {}
extern_methods!(
unsafe impl NSImage {
pub fn new_by_referencing_file(path: &NSString) -> Id<Self> {
unsafe { msg_send_id![Self::alloc(), initByReferencingFile: path] }
}
pub fn new_with_data(data: &NSData) -> Id<Self> {
unsafe { msg_send_id![Self::alloc(), initWithData: data] }
}
}
);

View File

@@ -1,25 +0,0 @@
use icrate::Foundation::NSObject;
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, mutability, ClassType};
use super::NSMenuItem;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSMenu;
unsafe impl ClassType for NSMenu {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl NSMenu {
#[method_id(new)]
pub fn new() -> Id<Self>;
#[method(addItem:)]
pub fn addItem(&self, item: &NSMenuItem);
}
);

View File

@@ -1,43 +0,0 @@
use icrate::Foundation::{NSObject, NSString};
use objc2::rc::Id;
use objc2::runtime::Sel;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use super::{NSEventModifierFlags, NSMenu};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSMenuItem;
unsafe impl ClassType for NSMenuItem {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl NSMenuItem {
#[method_id(new)]
pub fn new() -> Id<Self>;
pub fn newWithTitle(title: &NSString, action: Sel, key_equivalent: &NSString) -> Id<Self> {
unsafe {
msg_send_id![
Self::alloc(),
initWithTitle: title,
action: action,
keyEquivalent: key_equivalent,
]
}
}
#[method_id(separatorItem)]
pub fn separatorItem() -> Id<Self>;
#[method(setKeyEquivalentModifierMask:)]
pub fn setKeyEquivalentModifierMask(&self, mask: NSEventModifierFlags);
#[method(setSubmenu:)]
pub fn setSubmenu(&self, submenu: &NSMenu);
}
);

View File

@@ -1,66 +0,0 @@
//! Safe bindings for the AppKit framework.
//!
//! These are split out from the rest of `winit` to make safety easier to review.
//! In the future, these should probably live in another crate like `cacao`.
//!
//! TODO: Main thread safety.
// Objective-C methods have different conventions, and it's much easier to
// understand if we just use the same names
#![allow(non_snake_case)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::enum_variant_names)]
#![allow(non_upper_case_globals)]
mod appearance;
mod application;
mod button;
mod color;
mod control;
mod cursor;
mod event;
mod image;
mod menu;
mod menu_item;
mod pasteboard;
mod responder;
mod screen;
mod tab_group;
mod text_input_client;
mod text_input_context;
mod version;
mod view;
mod window;
pub(crate) use self::appearance::NSAppearance;
pub(crate) use self::application::{
NSApp, NSApplication, NSApplicationActivationPolicy, NSApplicationPresentationOptions,
NSRequestUserAttentionType,
};
pub(crate) use self::button::NSButton;
pub(crate) use self::color::NSColor;
pub(crate) use self::control::NSControl;
pub(crate) use self::cursor::NSCursor;
#[allow(unused_imports)]
pub(crate) use self::event::{
NSEvent, NSEventModifierFlags, NSEventPhase, NSEventSubtype, NSEventType,
};
pub(crate) use self::image::NSImage;
pub(crate) use self::menu::NSMenu;
pub(crate) use self::menu_item::NSMenuItem;
pub(crate) use self::pasteboard::{NSFilenamesPboardType, NSPasteboard, NSPasteboardType};
pub(crate) use self::responder::NSResponder;
#[allow(unused_imports)]
pub(crate) use self::screen::{NSDeviceDescriptionKey, NSScreen};
pub(crate) use self::tab_group::NSWindowTabGroup;
pub(crate) use self::text_input_client::NSTextInputClient;
pub(crate) use self::text_input_context::NSTextInputContext;
pub(crate) use self::version::NSAppKitVersion;
pub(crate) use self::view::{NSTrackingRectTag, NSView};
pub(crate) use self::window::{
NSBackingStoreType, NSWindow, NSWindowButton, NSWindowLevel, NSWindowOcclusionState,
NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode,
NSWindowTitleVisibility,
};
#[link(name = "AppKit", kind = "framework")]
extern "C" {}

View File

@@ -1,26 +0,0 @@
use icrate::Foundation::{NSObject, NSString};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, mutability, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSPasteboard;
unsafe impl ClassType for NSPasteboard {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl NSPasteboard {
#[method_id(propertyListForType:)]
pub fn propertyListForType(&self, type_: &NSPasteboardType) -> Id<NSObject>;
}
);
pub type NSPasteboardType = NSString;
extern "C" {
pub static NSFilenamesPboardType: &'static NSPasteboardType;
}

View File

@@ -1,23 +0,0 @@
use icrate::Foundation::{NSArray, NSObject};
use objc2::{extern_class, extern_methods, mutability, ClassType};
use super::NSEvent;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct NSResponder;
unsafe impl ClassType for NSResponder {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
// Documented as "Thread-Unsafe".
extern_methods!(
unsafe impl NSResponder {
#[method(interpretKeyEvents:)]
pub(crate) unsafe fn interpretKeyEvents(&self, events: &NSArray<NSEvent>);
}
);

View File

@@ -1,65 +0,0 @@
use icrate::ns_string;
use icrate::Foundation::{CGFloat, NSArray, NSDictionary, NSNumber, NSObject, NSRect, NSString};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{extern_class, extern_methods, mutability, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSScreen;
unsafe impl ClassType for NSScreen {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
// TODO: Main thread marker!
extern_methods!(
unsafe impl NSScreen {
/// The application object must have been created.
#[method_id(mainScreen)]
pub fn main() -> Option<Id<Self>>;
/// The application object must have been created.
#[method_id(screens)]
pub fn screens() -> Id<NSArray<Self>>;
#[method(frame)]
pub fn frame(&self) -> NSRect;
#[method(visibleFrame)]
pub fn visibleFrame(&self) -> NSRect;
#[method_id(deviceDescription)]
pub fn deviceDescription(&self) -> Id<NSDictionary<NSDeviceDescriptionKey, AnyObject>>;
pub fn display_id(&self) -> u32 {
let key = ns_string!("NSScreenNumber");
objc2::rc::autoreleasepool(|_| {
let device_description = self.deviceDescription();
// Retrieve the CGDirectDisplayID associated with this screen
//
// SAFETY: The value from @"NSScreenNumber" in deviceDescription is guaranteed
// to be an NSNumber. See documentation for `deviceDescription` for details:
// <https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription?language=objc>
let obj = device_description
.get(key)
.expect("failed getting screen display id from device description");
let obj: *const AnyObject = obj;
let obj: *const NSNumber = obj.cast();
let obj: &NSNumber = unsafe { &*obj };
obj.as_u32()
})
}
#[method(backingScaleFactor)]
pub fn backingScaleFactor(&self) -> CGFloat;
}
);
pub type NSDeviceDescriptionKey = NSString;

View File

@@ -1,31 +0,0 @@
use icrate::Foundation::{NSArray, NSObject};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, mutability, ClassType};
use super::NSWindow;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSWindowTabGroup;
unsafe impl ClassType for NSWindowTabGroup {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl NSWindowTabGroup {
#[method(selectNextTab)]
pub fn selectNextTab(&self);
#[method(selectPreviousTab)]
pub fn selectPreviousTab(&self);
#[method_id(windows)]
pub fn tabbedWindows(&self) -> Id<NSArray<NSWindow>>;
#[method(setSelectedWindow:)]
pub fn setSelectedWindow(&self, window: &NSWindow);
}
);

View File

@@ -1,9 +0,0 @@
use objc2::{extern_protocol, ProtocolType};
extern_protocol!(
pub(crate) unsafe trait NSTextInputClient {
// TODO: Methods
}
unsafe impl ProtocolType for dyn NSTextInputClient {}
);

View File

@@ -1,29 +0,0 @@
use icrate::Foundation::{NSObject, NSString};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, mutability, ClassType};
type NSTextInputSourceIdentifier = NSString;
extern_class!(
/// Main-Thread-Only!
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSTextInputContext;
unsafe impl ClassType for NSTextInputContext {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl NSTextInputContext {
#[method(invalidateCharacterCoordinates)]
pub fn invalidateCharacterCoordinates(&self);
#[method(discardMarkedText)]
pub fn discardMarkedText(&self);
#[method_id(selectedKeyboardInputSource)]
pub fn selectedKeyboardInputSource(&self) -> Option<Id<NSTextInputSourceIdentifier>>;
}
);

View File

@@ -1,62 +0,0 @@
#[repr(transparent)]
#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
pub struct NSAppKitVersion(f64);
#[allow(dead_code)]
#[allow(non_upper_case_globals)]
impl NSAppKitVersion {
pub fn current() -> Self {
extern "C" {
static NSAppKitVersionNumber: NSAppKitVersion;
}
unsafe { NSAppKitVersionNumber }
}
pub fn floor(self) -> Self {
Self(self.0.floor())
}
pub const NSAppKitVersionNumber10_0: Self = Self(577.0);
pub const NSAppKitVersionNumber10_1: Self = Self(620.0);
pub const NSAppKitVersionNumber10_2: Self = Self(663.0);
pub const NSAppKitVersionNumber10_2_3: Self = Self(663.6);
pub const NSAppKitVersionNumber10_3: Self = Self(743.0);
pub const NSAppKitVersionNumber10_3_2: Self = Self(743.14);
pub const NSAppKitVersionNumber10_3_3: Self = Self(743.2);
pub const NSAppKitVersionNumber10_3_5: Self = Self(743.24);
pub const NSAppKitVersionNumber10_3_7: Self = Self(743.33);
pub const NSAppKitVersionNumber10_3_9: Self = Self(743.36);
pub const NSAppKitVersionNumber10_4: Self = Self(824.0);
pub const NSAppKitVersionNumber10_4_1: Self = Self(824.1);
pub const NSAppKitVersionNumber10_4_3: Self = Self(824.23);
pub const NSAppKitVersionNumber10_4_4: Self = Self(824.33);
pub const NSAppKitVersionNumber10_4_7: Self = Self(824.41);
pub const NSAppKitVersionNumber10_5: Self = Self(949.0);
pub const NSAppKitVersionNumber10_5_2: Self = Self(949.27);
pub const NSAppKitVersionNumber10_5_3: Self = Self(949.33);
pub const NSAppKitVersionNumber10_6: Self = Self(1038.0);
pub const NSAppKitVersionNumber10_7: Self = Self(1138.0);
pub const NSAppKitVersionNumber10_7_2: Self = Self(1138.23);
pub const NSAppKitVersionNumber10_7_3: Self = Self(1138.32);
pub const NSAppKitVersionNumber10_7_4: Self = Self(1138.47);
pub const NSAppKitVersionNumber10_8: Self = Self(1187.0);
pub const NSAppKitVersionNumber10_9: Self = Self(1265.0);
pub const NSAppKitVersionNumber10_10: Self = Self(1343.0);
pub const NSAppKitVersionNumber10_10_2: Self = Self(1344.0);
pub const NSAppKitVersionNumber10_10_3: Self = Self(1347.0);
pub const NSAppKitVersionNumber10_10_4: Self = Self(1348.0);
pub const NSAppKitVersionNumber10_10_5: Self = Self(1348.0);
pub const NSAppKitVersionNumber10_10_Max: Self = Self(1349.0);
pub const NSAppKitVersionNumber10_11: Self = Self(1404.0);
pub const NSAppKitVersionNumber10_11_1: Self = Self(1404.13);
pub const NSAppKitVersionNumber10_11_2: Self = Self(1404.34);
pub const NSAppKitVersionNumber10_11_3: Self = Self(1404.34);
pub const NSAppKitVersionNumber10_12: Self = Self(1504.0);
pub const NSAppKitVersionNumber10_12_1: Self = Self(1504.60);
pub const NSAppKitVersionNumber10_12_2: Self = Self(1504.76);
pub const NSAppKitVersionNumber10_13: Self = Self(1561.0);
pub const NSAppKitVersionNumber10_13_1: Self = Self(1561.1);
pub const NSAppKitVersionNumber10_13_2: Self = Self(1561.2);
pub const NSAppKitVersionNumber10_13_4: Self = Self(1561.4);
}

View File

@@ -1,95 +0,0 @@
use std::ffi::c_void;
use std::num::NonZeroIsize;
use std::ptr;
use icrate::Foundation::{NSObject, NSPoint, NSRect};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{extern_class, extern_methods, mutability, ClassType};
use super::{NSCursor, NSResponder, NSTextInputContext, NSWindow};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSView;
unsafe impl ClassType for NSView {
#[inherits(NSObject)]
type Super = NSResponder;
type Mutability = mutability::InteriorMutable;
}
);
// Documented as "Main Thread Only".
// > generally thread safe, although operations on views such as creating,
// > resizing, and moving should happen on the main thread.
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
//
// > If you want to use a thread to draw to a view, bracket all drawing code
// > between the lockFocusIfCanDraw and unlockFocus methods of NSView.
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123351-BBCFIIEB>
extern_methods!(
/// Getter methods
unsafe impl NSView {
#[method(frame)]
pub fn frame(&self) -> NSRect;
#[method(bounds)]
pub fn bounds(&self) -> NSRect;
#[method_id(inputContext)]
pub fn inputContext(
&self,
// _mtm: MainThreadMarker,
) -> Option<Id<NSTextInputContext>>;
#[method(hasMarkedText)]
pub fn hasMarkedText(&self) -> bool;
#[method(convertPoint:fromView:)]
pub fn convertPoint_fromView(&self, point: NSPoint, view: Option<&NSView>) -> NSPoint;
#[method_id(window)]
pub fn window(&self) -> Option<Id<NSWindow>>;
}
unsafe impl NSView {
#[method(setWantsBestResolutionOpenGLSurface:)]
pub fn setWantsBestResolutionOpenGLSurface(&self, value: bool);
#[method(setWantsLayer:)]
pub fn setWantsLayer(&self, wants_layer: bool);
#[method(setPostsFrameChangedNotifications:)]
pub fn setPostsFrameChangedNotifications(&self, value: bool);
#[method(removeTrackingRect:)]
pub fn removeTrackingRect(&self, tag: NSTrackingRectTag);
#[method(addTrackingRect:owner:userData:assumeInside:)]
unsafe fn inner_addTrackingRect(
&self,
rect: NSRect,
owner: &AnyObject,
user_data: *mut c_void,
assume_inside: bool,
) -> Option<NSTrackingRectTag>;
pub fn add_tracking_rect(&self, rect: NSRect, assume_inside: bool) -> NSTrackingRectTag {
// SAFETY: The user data is NULL, so it is valid
unsafe { self.inner_addTrackingRect(rect, self, ptr::null_mut(), assume_inside) }
.expect("failed creating tracking rect")
}
#[method(addCursorRect:cursor:)]
// NSCursor safe to take by shared reference since it is already immutable
pub fn addCursorRect(&self, rect: NSRect, cursor: &NSCursor);
#[method(setHidden:)]
pub fn setHidden(&self, hidden: bool);
}
);
/// <https://developer.apple.com/documentation/appkit/nstrackingrecttag?language=objc>
pub type NSTrackingRectTag = NonZeroIsize; // NSInteger, but non-zero!

View File

@@ -1,437 +0,0 @@
use icrate::Foundation::{
CGFloat, NSArray, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, NSUInteger,
};
use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{extern_class, extern_methods, mutability, ClassType};
use super::{
NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView, NSWindowTabGroup,
};
extern_class!(
/// Main-Thread-Only!
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct NSWindow;
unsafe impl ClassType for NSWindow {
#[inherits(NSObject)]
type Super = NSResponder;
type Mutability = mutability::InteriorMutable;
}
);
// Documented as "Main Thread Only", but:
// > Thread safe in that you can create and manage them on a secondary thread.
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123364>
//
// So could in theory be `Send`, and perhaps also `Sync` - but we would like
// interior mutability on windows, since that's just much easier, and in that
// case, they can't be!
extern_methods!(
unsafe impl NSWindow {
#[method(frame)]
pub(crate) fn frame(&self) -> NSRect;
#[method(backingScaleFactor)]
pub(crate) fn backingScaleFactor(&self) -> CGFloat;
#[method_id(contentView)]
pub(crate) fn contentView(&self) -> Id<NSView>;
#[method(setContentView:)]
pub(crate) fn setContentView(&self, view: &NSView);
#[method(setInitialFirstResponder:)]
pub(crate) fn setInitialFirstResponder(&self, view: &NSView);
#[method(makeFirstResponder:)]
#[must_use]
pub(crate) fn makeFirstResponder(&self, responder: Option<&NSResponder>) -> bool;
#[method(contentRectForFrameRect:)]
pub(crate) fn contentRectForFrameRect(&self, windowFrame: NSRect) -> NSRect;
#[method_id(screen)]
pub(crate) fn screen(&self) -> Option<Id<NSScreen>>;
#[method(setContentSize:)]
pub(crate) fn setContentSize(&self, contentSize: NSSize);
#[method(setFrameTopLeftPoint:)]
pub(crate) fn setFrameTopLeftPoint(&self, point: NSPoint);
#[method(setMinSize:)]
pub(crate) fn setMinSize(&self, minSize: NSSize);
#[method(setMaxSize:)]
pub(crate) fn setMaxSize(&self, maxSize: NSSize);
#[method(setResizeIncrements:)]
pub(crate) fn setResizeIncrements(&self, increments: NSSize);
#[method(contentResizeIncrements)]
pub(crate) fn contentResizeIncrements(&self) -> NSSize;
#[method(setContentResizeIncrements:)]
pub(crate) fn setContentResizeIncrements(&self, increments: NSSize);
#[method(setFrame:display:)]
pub(crate) fn setFrame_display(&self, frameRect: NSRect, flag: bool);
#[method(setMovable:)]
pub(crate) fn setMovable(&self, movable: bool);
#[method(setSharingType:)]
pub(crate) fn setSharingType(&self, sharingType: NSWindowSharingType);
#[method(setTabbingMode:)]
pub(crate) fn setTabbingMode(&self, tabbingMode: NSWindowTabbingMode);
#[method(setOpaque:)]
pub(crate) fn setOpaque(&self, opaque: bool);
#[method(hasShadow)]
pub(crate) fn hasShadow(&self) -> bool;
#[method(setHasShadow:)]
pub(crate) fn setHasShadow(&self, has_shadow: bool);
#[method(setIgnoresMouseEvents:)]
pub(crate) fn setIgnoresMouseEvents(&self, ignores: bool);
#[method(setBackgroundColor:)]
pub(crate) fn setBackgroundColor(&self, color: &NSColor);
#[method(styleMask)]
pub(crate) fn styleMask(&self) -> NSWindowStyleMask;
#[method(setStyleMask:)]
pub(crate) fn setStyleMask(&self, mask: NSWindowStyleMask);
#[method(registerForDraggedTypes:)]
pub(crate) fn registerForDraggedTypes(&self, types: &NSArray<NSPasteboardType>);
#[method(makeKeyAndOrderFront:)]
pub(crate) fn makeKeyAndOrderFront(&self, sender: Option<&AnyObject>);
#[method(orderFront:)]
pub(crate) fn orderFront(&self, sender: Option<&AnyObject>);
#[method(miniaturize:)]
pub(crate) fn miniaturize(&self, sender: Option<&AnyObject>);
#[method(deminiaturize:)]
pub(crate) fn deminiaturize(&self, sender: Option<&AnyObject>);
#[method(toggleFullScreen:)]
pub(crate) fn toggleFullScreen(&self, sender: Option<&AnyObject>);
#[method(orderOut:)]
pub(crate) fn orderOut(&self, sender: Option<&AnyObject>);
#[method(zoom:)]
pub(crate) fn zoom(&self, sender: Option<&AnyObject>);
#[method(selectNextKeyView:)]
pub(crate) fn selectNextKeyView(&self, sender: Option<&AnyObject>);
#[method(selectPreviousKeyView:)]
pub(crate) fn selectPreviousKeyView(&self, sender: Option<&AnyObject>);
#[method_id(firstResponder)]
pub(crate) fn firstResponder(&self) -> Option<Id<NSResponder>>;
#[method_id(standardWindowButton:)]
pub(crate) fn standardWindowButton(&self, kind: NSWindowButton) -> Option<Id<NSButton>>;
#[method(setTitle:)]
pub(crate) fn setTitle(&self, title: &NSString);
#[method_id(title)]
pub(crate) fn title_(&self) -> Id<NSString>;
#[method(setReleasedWhenClosed:)]
pub(crate) fn setReleasedWhenClosed(&self, val: bool);
#[method(setAcceptsMouseMovedEvents:)]
pub(crate) fn setAcceptsMouseMovedEvents(&self, val: bool);
#[method(setTitlebarAppearsTransparent:)]
pub(crate) fn setTitlebarAppearsTransparent(&self, val: bool);
#[method(setTitleVisibility:)]
pub(crate) fn setTitleVisibility(&self, visibility: NSWindowTitleVisibility);
#[method(setMovableByWindowBackground:)]
pub(crate) fn setMovableByWindowBackground(&self, val: bool);
#[method(setLevel:)]
pub(crate) fn setLevel(&self, level: NSWindowLevel);
#[method(setAllowsAutomaticWindowTabbing:)]
pub(crate) fn setAllowsAutomaticWindowTabbing(val: bool);
#[method(setTabbingIdentifier:)]
pub(crate) fn setTabbingIdentifier(&self, identifier: &NSString);
#[method(setDocumentEdited:)]
pub(crate) fn setDocumentEdited(&self, val: bool);
#[method(occlusionState)]
pub(crate) fn occlusionState(&self) -> NSWindowOcclusionState;
#[method(center)]
pub(crate) fn center(&self);
#[method(isResizable)]
pub(crate) fn isResizable(&self) -> bool;
#[method(isMiniaturizable)]
pub(crate) fn isMiniaturizable(&self) -> bool;
#[method(hasCloseBox)]
pub(crate) fn hasCloseBox(&self) -> bool;
#[method(isMiniaturized)]
pub(crate) fn isMiniaturized(&self) -> bool;
#[method(isVisible)]
pub(crate) fn isVisible(&self) -> bool;
#[method(isKeyWindow)]
pub(crate) fn isKeyWindow(&self) -> bool;
#[method(isZoomed)]
pub(crate) fn isZoomed(&self) -> bool;
#[method(allowsAutomaticWindowTabbing)]
pub(crate) fn allowsAutomaticWindowTabbing() -> bool;
#[method(selectNextTab)]
pub(crate) fn selectNextTab(&self);
#[method_id(tabbingIdentifier)]
pub(crate) fn tabbingIdentifier(&self) -> Id<NSString>;
#[method_id(tabGroup)]
pub(crate) fn tabGroup(&self) -> Id<NSWindowTabGroup>;
#[method(isDocumentEdited)]
pub(crate) fn isDocumentEdited(&self) -> bool;
#[method(close)]
pub(crate) fn close(&self);
#[method(performWindowDragWithEvent:)]
// TODO: Can this actually accept NULL?
pub(crate) fn performWindowDragWithEvent(&self, event: Option<&NSEvent>);
#[method(invalidateCursorRectsForView:)]
pub(crate) fn invalidateCursorRectsForView(&self, view: &NSView);
#[method(setDelegate:)]
pub(crate) fn setDelegate(&self, delegate: Option<&NSObject>);
#[method(sendEvent:)]
pub(crate) unsafe fn sendEvent(&self, event: &NSEvent);
#[method(addChildWindow:ordered:)]
pub(crate) unsafe fn addChildWindow(&self, child: &NSWindow, ordered: NSWindowOrderingMode);
}
);
#[allow(dead_code)]
#[repr(isize)] // NSInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSWindowTitleVisibility {
#[doc(alias = "NSWindowTitleVisible")]
Visible = 0,
#[doc(alias = "NSWindowTitleHidden")]
Hidden = 1,
}
unsafe impl Encode for NSWindowTitleVisibility {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[allow(dead_code)]
#[repr(usize)] // NSUInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSWindowButton {
#[doc(alias = "NSWindowCloseButton")]
Close = 0,
#[doc(alias = "NSWindowMiniaturizeButton")]
Miniaturize = 1,
#[doc(alias = "NSWindowZoomButton")]
Zoom = 2,
#[doc(alias = "NSWindowToolbarButton")]
Toolbar = 3,
#[doc(alias = "NSWindowDocumentIconButton")]
DocumentIcon = 4,
#[doc(alias = "NSWindowDocumentVersionsButton")]
DocumentVersions = 6,
#[doc(alias = "NSWindowFullScreenButton")]
#[deprecated = "Deprecated since macOS 10.12"]
FullScreen = 7,
}
unsafe impl Encode for NSWindowButton {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
// CGWindowLevel.h
//
// Note: There are two different things at play in this header:
// `CGWindowLevel` and `CGWindowLevelKey`.
//
// It seems like there was a push towards using "key" values instead of the
// raw window level values, and then you were supposed to use
// `CGWindowLevelForKey` to get the actual level.
//
// But the values that `NSWindowLevel` has are compiled in, and as such has
// to remain ABI compatible, so they're safe for us to hardcode as well.
#[allow(dead_code)]
mod window_level {
const kCGNumReservedWindowLevels: i32 = 16;
const kCGNumReservedBaseWindowLevels: i32 = 5;
pub const kCGBaseWindowLevel: i32 = i32::MIN;
pub const kCGMinimumWindowLevel: i32 = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels;
pub const kCGMaximumWindowLevel: i32 = i32::MAX - kCGNumReservedWindowLevels;
pub const kCGDesktopWindowLevel: i32 = kCGMinimumWindowLevel + 20;
pub const kCGDesktopIconWindowLevel: i32 = kCGDesktopWindowLevel + 20;
pub const kCGBackstopMenuLevel: i32 = -20;
pub const kCGNormalWindowLevel: i32 = 0;
pub const kCGFloatingWindowLevel: i32 = 3;
pub const kCGTornOffMenuWindowLevel: i32 = 3;
pub const kCGModalPanelWindowLevel: i32 = 8;
pub const kCGUtilityWindowLevel: i32 = 19;
pub const kCGDockWindowLevel: i32 = 20;
pub const kCGMainMenuWindowLevel: i32 = 24;
pub const kCGStatusWindowLevel: i32 = 25;
pub const kCGPopUpMenuWindowLevel: i32 = 101;
pub const kCGOverlayWindowLevel: i32 = 102;
pub const kCGHelpWindowLevel: i32 = 200;
pub const kCGDraggingWindowLevel: i32 = 500;
pub const kCGScreenSaverWindowLevel: i32 = 1000;
pub const kCGAssistiveTechHighWindowLevel: i32 = 1500;
pub const kCGCursorWindowLevel: i32 = kCGMaximumWindowLevel - 1;
}
use window_level::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct NSWindowLevel(pub NSInteger);
#[allow(dead_code)]
impl NSWindowLevel {
#[doc(alias = "BelowNormalWindowLevel")]
pub const BELOW_NORMAL: Self = Self((kCGNormalWindowLevel - 1) as _);
#[doc(alias = "NSNormalWindowLevel")]
pub const Normal: Self = Self(kCGNormalWindowLevel as _);
#[doc(alias = "NSFloatingWindowLevel")]
pub const Floating: Self = Self(kCGFloatingWindowLevel as _);
#[doc(alias = "NSTornOffMenuWindowLevel")]
pub const TornOffMenu: Self = Self(kCGTornOffMenuWindowLevel as _);
#[doc(alias = "NSModalPanelWindowLevel")]
pub const ModalPanel: Self = Self(kCGModalPanelWindowLevel as _);
#[doc(alias = "NSMainMenuWindowLevel")]
pub const MainMenu: Self = Self(kCGMainMenuWindowLevel as _);
#[doc(alias = "NSStatusWindowLevel")]
pub const Status: Self = Self(kCGStatusWindowLevel as _);
#[doc(alias = "NSPopUpMenuWindowLevel")]
pub const PopUpMenu: Self = Self(kCGPopUpMenuWindowLevel as _);
#[doc(alias = "NSScreenSaverWindowLevel")]
pub const ScreenSaver: Self = Self(kCGScreenSaverWindowLevel as _);
}
unsafe impl Encode for NSWindowLevel {
const ENCODING: Encoding = NSInteger::ENCODING;
}
bitflags! {
#[derive(Clone, Copy)]
pub struct NSWindowOcclusionState: NSUInteger {
const NSWindowOcclusionStateVisible = 1 << 1;
}
}
unsafe impl Encode for NSWindowOcclusionState {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct NSWindowStyleMask: NSUInteger {
const NSBorderlessWindowMask = 0;
const NSTitledWindowMask = 1 << 0;
const NSClosableWindowMask = 1 << 1;
const NSMiniaturizableWindowMask = 1 << 2;
const NSResizableWindowMask = 1 << 3;
const NSTexturedBackgroundWindowMask = 1 << 8;
const NSUnifiedTitleAndToolbarWindowMask = 1 << 12;
const NSFullScreenWindowMask = 1 << 14;
const NSFullSizeContentViewWindowMask = 1 << 15;
}
}
unsafe impl Encode for NSWindowStyleMask {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
#[allow(dead_code)]
#[repr(usize)] // NSUInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSBackingStoreType {
NSBackingStoreRetained = 0,
NSBackingStoreNonretained = 1,
NSBackingStoreBuffered = 2,
}
unsafe impl Encode for NSBackingStoreType {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
#[allow(dead_code)]
#[repr(usize)] // NSUInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSWindowSharingType {
NSWindowSharingNone = 0,
NSWindowSharingReadOnly = 1,
NSWindowSharingReadWrite = 2,
}
unsafe impl Encode for NSWindowSharingType {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
#[allow(dead_code)]
#[repr(isize)] // NSInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSWindowOrderingMode {
NSWindowAbove = 1,
NSWindowBelow = -1,
NSWindowOut = 0,
}
unsafe impl Encode for NSWindowOrderingMode {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[allow(dead_code)]
#[repr(isize)] // NSInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSWindowTabbingMode {
NSWindowTabbingModeAutomatic = 0,
NSWindowTabbingModeDisallowed = 2,
NSWindowTabbingModePreferred = 1,
}
unsafe impl Encode for NSWindowTabbingMode {
const ENCODING: Encoding = NSInteger::ENCODING;
}

View File

@@ -1,602 +0,0 @@
use std::ffi::c_void;
use core_foundation::{
base::CFRelease,
data::{CFDataGetBytePtr, CFDataRef},
};
use icrate::Foundation::MainThreadMarker;
use smol_str::SmolStr;
use super::appkit::{NSEvent, NSEventModifierFlags};
use crate::{
event::{ElementState, KeyEvent, Modifiers},
keyboard::{
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NativeKey, NativeKeyCode,
},
platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode},
platform_impl::platform::ffi,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyEventExtra {
pub text_with_all_modifiers: Option<SmolStr>,
pub key_without_modifiers: Key,
}
impl KeyEventExtModifierSupplement for KeyEvent {
fn text_with_all_modifiers(&self) -> Option<&str> {
self.platform_specific
.text_with_all_modifiers
.as_ref()
.map(|s| s.as_str())
}
fn key_without_modifiers(&self) -> Key {
self.platform_specific.key_without_modifiers.clone()
}
}
pub fn get_modifierless_char(scancode: u16) -> Key {
let mut string = [0; 16];
let input_source;
let layout;
unsafe {
input_source = ffi::TISCopyCurrentKeyboardLayoutInputSource();
if input_source.is_null() {
log::error!("`TISCopyCurrentKeyboardLayoutInputSource` returned null ptr");
return Key::Unidentified(NativeKey::MacOS(scancode));
}
let layout_data =
ffi::TISGetInputSourceProperty(input_source, ffi::kTISPropertyUnicodeKeyLayoutData);
if layout_data.is_null() {
CFRelease(input_source as *mut c_void);
log::error!("`TISGetInputSourceProperty` returned null ptr");
return Key::Unidentified(NativeKey::MacOS(scancode));
}
layout = CFDataGetBytePtr(layout_data as CFDataRef) as *const ffi::UCKeyboardLayout;
}
let keyboard_type = MainThreadMarker::run_on_main(|_mtm| unsafe { ffi::LMGetKbdType() });
let mut result_len = 0;
let mut dead_keys = 0;
let modifiers = 0;
let translate_result = unsafe {
ffi::UCKeyTranslate(
layout,
scancode,
ffi::kUCKeyActionDisplay,
modifiers,
keyboard_type as u32,
ffi::kUCKeyTranslateNoDeadKeysMask,
&mut dead_keys,
string.len() as ffi::UniCharCount,
&mut result_len,
string.as_mut_ptr(),
)
};
unsafe {
CFRelease(input_source as *mut c_void);
}
if translate_result != 0 {
log::error!(
"`UCKeyTranslate` returned with the non-zero value: {}",
translate_result
);
return Key::Unidentified(NativeKey::MacOS(scancode));
}
if result_len == 0 {
log::error!("`UCKeyTranslate` was succesful but gave a string of 0 length.");
return Key::Unidentified(NativeKey::MacOS(scancode));
}
let chars = String::from_utf16_lossy(&string[0..result_len as usize]);
Key::Character(SmolStr::new(chars))
}
fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
let string = ns_event
.charactersIgnoringModifiers()
.map(|s| s.to_string())
.unwrap_or_default();
if string.is_empty() {
// Probably a dead key
let first_char = modifierless_chars.chars().next();
return Key::Dead(first_char);
}
Key::Character(SmolStr::new(string))
}
/// Create `KeyEvent` for the given `NSEvent`.
///
/// This function shouldn't be called when the IME input is in process.
pub(crate) fn create_key_event(
ns_event: &NSEvent,
is_press: bool,
is_repeat: bool,
key_override: Option<KeyCode>,
) -> KeyEvent {
use ElementState::{Pressed, Released};
let state = if is_press { Pressed } else { Released };
let scancode = ns_event.key_code();
let mut physical_key = key_override.unwrap_or_else(|| KeyCode::from_scancode(scancode as u32));
let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() {
None
} else {
let characters = ns_event
.characters()
.map(|s| s.to_string())
.unwrap_or_default();
if characters.is_empty() {
None
} else {
if matches!(physical_key, KeyCode::Unidentified(_)) {
// The key may be one of the funky function keys
physical_key = extra_function_key_to_code(scancode, &characters);
}
Some(SmolStr::new(characters))
}
};
let key_from_code = code_to_key(physical_key, scancode);
let (logical_key, key_without_modifiers) = if matches!(key_from_code, Key::Unidentified(_)) {
let key_without_modifiers = get_modifierless_char(scancode);
let modifiers = NSEvent::modifierFlags(ns_event);
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
let logical_key = match text_with_all_modifiers.as_ref() {
// Only checking for ctrl here, not checking for alt because we DO want to
// include its effect in the key. For example if -on the Germay layout- one
// presses alt+8, the logical key should be "{"
// Also not checking if this is a release event because then this issue would
// still affect the key release.
Some(text) if !has_ctrl => Key::Character(text.clone()),
_ => {
let modifierless_chars = match key_without_modifiers.as_ref() {
Key::Character(ch) => ch,
_ => "",
};
get_logical_key_char(ns_event, modifierless_chars)
}
};
(logical_key, key_without_modifiers)
} else {
(key_from_code.clone(), key_from_code)
};
let text = if is_press {
logical_key.to_text().map(SmolStr::new)
} else {
None
};
let location = code_to_location(physical_key);
KeyEvent {
location,
logical_key,
physical_key,
repeat: is_repeat,
state,
text,
platform_specific: KeyEventExtra {
key_without_modifiers,
text_with_all_modifiers,
},
}
}
pub fn code_to_key(code: KeyCode, scancode: u16) -> Key {
match code {
KeyCode::Enter => Key::Enter,
KeyCode::Tab => Key::Tab,
KeyCode::Space => Key::Space,
KeyCode::Backspace => Key::Backspace,
KeyCode::Escape => Key::Escape,
KeyCode::SuperRight => Key::Super,
KeyCode::SuperLeft => Key::Super,
KeyCode::ShiftLeft => Key::Shift,
KeyCode::AltLeft => Key::Alt,
KeyCode::ControlLeft => Key::Control,
KeyCode::ShiftRight => Key::Shift,
KeyCode::AltRight => Key::Alt,
KeyCode::ControlRight => Key::Control,
KeyCode::NumLock => Key::NumLock,
KeyCode::AudioVolumeUp => Key::AudioVolumeUp,
KeyCode::AudioVolumeDown => Key::AudioVolumeDown,
// Other numpad keys all generate text on macOS (if I understand correctly)
KeyCode::NumpadEnter => Key::Enter,
KeyCode::F1 => Key::F1,
KeyCode::F2 => Key::F2,
KeyCode::F3 => Key::F3,
KeyCode::F4 => Key::F4,
KeyCode::F5 => Key::F5,
KeyCode::F6 => Key::F6,
KeyCode::F7 => Key::F7,
KeyCode::F8 => Key::F8,
KeyCode::F9 => Key::F9,
KeyCode::F10 => Key::F10,
KeyCode::F11 => Key::F11,
KeyCode::F12 => Key::F12,
KeyCode::F13 => Key::F13,
KeyCode::F14 => Key::F14,
KeyCode::F15 => Key::F15,
KeyCode::F16 => Key::F16,
KeyCode::F17 => Key::F17,
KeyCode::F18 => Key::F18,
KeyCode::F19 => Key::F19,
KeyCode::F20 => Key::F20,
KeyCode::Insert => Key::Insert,
KeyCode::Home => Key::Home,
KeyCode::PageUp => Key::PageUp,
KeyCode::Delete => Key::Delete,
KeyCode::End => Key::End,
KeyCode::PageDown => Key::PageDown,
KeyCode::ArrowLeft => Key::ArrowLeft,
KeyCode::ArrowRight => Key::ArrowRight,
KeyCode::ArrowDown => Key::ArrowDown,
KeyCode::ArrowUp => Key::ArrowUp,
_ => Key::Unidentified(NativeKey::MacOS(scancode)),
}
}
pub fn code_to_location(code: KeyCode) -> KeyLocation {
match code {
KeyCode::SuperRight => KeyLocation::Right,
KeyCode::SuperLeft => KeyLocation::Left,
KeyCode::ShiftLeft => KeyLocation::Left,
KeyCode::AltLeft => KeyLocation::Left,
KeyCode::ControlLeft => KeyLocation::Left,
KeyCode::ShiftRight => KeyLocation::Right,
KeyCode::AltRight => KeyLocation::Right,
KeyCode::ControlRight => KeyLocation::Right,
KeyCode::NumLock => KeyLocation::Numpad,
KeyCode::NumpadDecimal => KeyLocation::Numpad,
KeyCode::NumpadMultiply => KeyLocation::Numpad,
KeyCode::NumpadAdd => KeyLocation::Numpad,
KeyCode::NumpadDivide => KeyLocation::Numpad,
KeyCode::NumpadEnter => KeyLocation::Numpad,
KeyCode::NumpadSubtract => KeyLocation::Numpad,
KeyCode::NumpadEqual => KeyLocation::Numpad,
KeyCode::Numpad0 => KeyLocation::Numpad,
KeyCode::Numpad1 => KeyLocation::Numpad,
KeyCode::Numpad2 => KeyLocation::Numpad,
KeyCode::Numpad3 => KeyLocation::Numpad,
KeyCode::Numpad4 => KeyLocation::Numpad,
KeyCode::Numpad5 => KeyLocation::Numpad,
KeyCode::Numpad6 => KeyLocation::Numpad,
KeyCode::Numpad7 => KeyLocation::Numpad,
KeyCode::Numpad8 => KeyLocation::Numpad,
KeyCode::Numpad9 => KeyLocation::Numpad,
_ => KeyLocation::Standard,
}
}
// While F1-F20 have scancodes we can match on, we have to check against UTF-16
// constants for the rest.
// https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?preferredLanguage=occ
pub fn extra_function_key_to_code(scancode: u16, string: &str) -> KeyCode {
if let Some(ch) = string.encode_utf16().next() {
match ch {
0xf718 => KeyCode::F21,
0xf719 => KeyCode::F22,
0xf71a => KeyCode::F23,
0xf71b => KeyCode::F24,
_ => KeyCode::Unidentified(NativeKeyCode::MacOS(scancode)),
}
} else {
KeyCode::Unidentified(NativeKeyCode::MacOS(scancode))
}
}
pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
let flags = event.modifierFlags();
let mut state = ModifiersState::empty();
let mut pressed_mods = ModifiersKeys::empty();
state.set(
ModifiersState::SHIFT,
flags.contains(NSEventModifierFlags::NSShiftKeyMask),
);
pressed_mods.set(ModifiersKeys::LSHIFT, event.lshift_pressed());
pressed_mods.set(ModifiersKeys::RSHIFT, event.rshift_pressed());
state.set(
ModifiersState::CONTROL,
flags.contains(NSEventModifierFlags::NSControlKeyMask),
);
pressed_mods.set(ModifiersKeys::LCONTROL, event.lctrl_pressed());
pressed_mods.set(ModifiersKeys::RCONTROL, event.rctrl_pressed());
state.set(
ModifiersState::ALT,
flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
);
pressed_mods.set(ModifiersKeys::LALT, event.lalt_pressed());
pressed_mods.set(ModifiersKeys::RALT, event.ralt_pressed());
state.set(
ModifiersState::SUPER,
flags.contains(NSEventModifierFlags::NSCommandKeyMask),
);
pressed_mods.set(ModifiersKeys::LSUPER, event.lcmd_pressed());
pressed_mods.set(ModifiersKeys::RSUPER, event.rcmd_pressed());
Modifiers {
state,
pressed_mods,
}
}
impl KeyCodeExtScancode for KeyCode {
fn to_scancode(self) -> Option<u32> {
match self {
KeyCode::KeyA => Some(0x00),
KeyCode::KeyS => Some(0x01),
KeyCode::KeyD => Some(0x02),
KeyCode::KeyF => Some(0x03),
KeyCode::KeyH => Some(0x04),
KeyCode::KeyG => Some(0x05),
KeyCode::KeyZ => Some(0x06),
KeyCode::KeyX => Some(0x07),
KeyCode::KeyC => Some(0x08),
KeyCode::KeyV => Some(0x09),
KeyCode::KeyB => Some(0x0b),
KeyCode::KeyQ => Some(0x0c),
KeyCode::KeyW => Some(0x0d),
KeyCode::KeyE => Some(0x0e),
KeyCode::KeyR => Some(0x0f),
KeyCode::KeyY => Some(0x10),
KeyCode::KeyT => Some(0x11),
KeyCode::Digit1 => Some(0x12),
KeyCode::Digit2 => Some(0x13),
KeyCode::Digit3 => Some(0x14),
KeyCode::Digit4 => Some(0x15),
KeyCode::Digit6 => Some(0x16),
KeyCode::Digit5 => Some(0x17),
KeyCode::Equal => Some(0x18),
KeyCode::Digit9 => Some(0x19),
KeyCode::Digit7 => Some(0x1a),
KeyCode::Minus => Some(0x1b),
KeyCode::Digit8 => Some(0x1c),
KeyCode::Digit0 => Some(0x1d),
KeyCode::BracketRight => Some(0x1e),
KeyCode::KeyO => Some(0x1f),
KeyCode::KeyU => Some(0x20),
KeyCode::BracketLeft => Some(0x21),
KeyCode::KeyI => Some(0x22),
KeyCode::KeyP => Some(0x23),
KeyCode::Enter => Some(0x24),
KeyCode::KeyL => Some(0x25),
KeyCode::KeyJ => Some(0x26),
KeyCode::Quote => Some(0x27),
KeyCode::KeyK => Some(0x28),
KeyCode::Semicolon => Some(0x29),
KeyCode::Backslash => Some(0x2a),
KeyCode::Comma => Some(0x2b),
KeyCode::Slash => Some(0x2c),
KeyCode::KeyN => Some(0x2d),
KeyCode::KeyM => Some(0x2e),
KeyCode::Period => Some(0x2f),
KeyCode::Tab => Some(0x30),
KeyCode::Space => Some(0x31),
KeyCode::Backquote => Some(0x32),
KeyCode::Backspace => Some(0x33),
KeyCode::Escape => Some(0x35),
KeyCode::SuperRight => Some(0x36),
KeyCode::SuperLeft => Some(0x37),
KeyCode::ShiftLeft => Some(0x38),
KeyCode::AltLeft => Some(0x3a),
KeyCode::ControlLeft => Some(0x3b),
KeyCode::ShiftRight => Some(0x3c),
KeyCode::AltRight => Some(0x3d),
KeyCode::ControlRight => Some(0x3e),
KeyCode::F17 => Some(0x40),
KeyCode::NumpadDecimal => Some(0x41),
KeyCode::NumpadMultiply => Some(0x43),
KeyCode::NumpadAdd => Some(0x45),
KeyCode::NumLock => Some(0x47),
KeyCode::AudioVolumeUp => Some(0x49),
KeyCode::AudioVolumeDown => Some(0x4a),
KeyCode::NumpadDivide => Some(0x4b),
KeyCode::NumpadEnter => Some(0x4c),
KeyCode::NumpadSubtract => Some(0x4e),
KeyCode::F18 => Some(0x4f),
KeyCode::F19 => Some(0x50),
KeyCode::NumpadEqual => Some(0x51),
KeyCode::Numpad0 => Some(0x52),
KeyCode::Numpad1 => Some(0x53),
KeyCode::Numpad2 => Some(0x54),
KeyCode::Numpad3 => Some(0x55),
KeyCode::Numpad4 => Some(0x56),
KeyCode::Numpad5 => Some(0x57),
KeyCode::Numpad6 => Some(0x58),
KeyCode::Numpad7 => Some(0x59),
KeyCode::F20 => Some(0x5a),
KeyCode::Numpad8 => Some(0x5b),
KeyCode::Numpad9 => Some(0x5c),
KeyCode::IntlYen => Some(0x5d),
KeyCode::F5 => Some(0x60),
KeyCode::F6 => Some(0x61),
KeyCode::F7 => Some(0x62),
KeyCode::F3 => Some(0x63),
KeyCode::F8 => Some(0x64),
KeyCode::F9 => Some(0x65),
KeyCode::F11 => Some(0x67),
KeyCode::F13 => Some(0x69),
KeyCode::F16 => Some(0x6a),
KeyCode::F14 => Some(0x6b),
KeyCode::F10 => Some(0x6d),
KeyCode::F12 => Some(0x6f),
KeyCode::F15 => Some(0x71),
KeyCode::Insert => Some(0x72),
KeyCode::Home => Some(0x73),
KeyCode::PageUp => Some(0x74),
KeyCode::Delete => Some(0x75),
KeyCode::F4 => Some(0x76),
KeyCode::End => Some(0x77),
KeyCode::F2 => Some(0x78),
KeyCode::PageDown => Some(0x79),
KeyCode::F1 => Some(0x7a),
KeyCode::ArrowLeft => Some(0x7b),
KeyCode::ArrowRight => Some(0x7c),
KeyCode::ArrowDown => Some(0x7d),
KeyCode::ArrowUp => Some(0x7e),
_ => None,
}
}
fn from_scancode(scancode: u32) -> KeyCode {
match scancode {
0x00 => KeyCode::KeyA,
0x01 => KeyCode::KeyS,
0x02 => KeyCode::KeyD,
0x03 => KeyCode::KeyF,
0x04 => KeyCode::KeyH,
0x05 => KeyCode::KeyG,
0x06 => KeyCode::KeyZ,
0x07 => KeyCode::KeyX,
0x08 => KeyCode::KeyC,
0x09 => KeyCode::KeyV,
//0x0a => World 1,
0x0b => KeyCode::KeyB,
0x0c => KeyCode::KeyQ,
0x0d => KeyCode::KeyW,
0x0e => KeyCode::KeyE,
0x0f => KeyCode::KeyR,
0x10 => KeyCode::KeyY,
0x11 => KeyCode::KeyT,
0x12 => KeyCode::Digit1,
0x13 => KeyCode::Digit2,
0x14 => KeyCode::Digit3,
0x15 => KeyCode::Digit4,
0x16 => KeyCode::Digit6,
0x17 => KeyCode::Digit5,
0x18 => KeyCode::Equal,
0x19 => KeyCode::Digit9,
0x1a => KeyCode::Digit7,
0x1b => KeyCode::Minus,
0x1c => KeyCode::Digit8,
0x1d => KeyCode::Digit0,
0x1e => KeyCode::BracketRight,
0x1f => KeyCode::KeyO,
0x20 => KeyCode::KeyU,
0x21 => KeyCode::BracketLeft,
0x22 => KeyCode::KeyI,
0x23 => KeyCode::KeyP,
0x24 => KeyCode::Enter,
0x25 => KeyCode::KeyL,
0x26 => KeyCode::KeyJ,
0x27 => KeyCode::Quote,
0x28 => KeyCode::KeyK,
0x29 => KeyCode::Semicolon,
0x2a => KeyCode::Backslash,
0x2b => KeyCode::Comma,
0x2c => KeyCode::Slash,
0x2d => KeyCode::KeyN,
0x2e => KeyCode::KeyM,
0x2f => KeyCode::Period,
0x30 => KeyCode::Tab,
0x31 => KeyCode::Space,
0x32 => KeyCode::Backquote,
0x33 => KeyCode::Backspace,
//0x34 => unknown,
0x35 => KeyCode::Escape,
0x36 => KeyCode::SuperRight,
0x37 => KeyCode::SuperLeft,
0x38 => KeyCode::ShiftLeft,
0x39 => KeyCode::CapsLock,
0x3a => KeyCode::AltLeft,
0x3b => KeyCode::ControlLeft,
0x3c => KeyCode::ShiftRight,
0x3d => KeyCode::AltRight,
0x3e => KeyCode::ControlRight,
0x3f => KeyCode::Fn,
0x40 => KeyCode::F17,
0x41 => KeyCode::NumpadDecimal,
//0x42 -> unknown,
0x43 => KeyCode::NumpadMultiply,
//0x44 => unknown,
0x45 => KeyCode::NumpadAdd,
//0x46 => unknown,
0x47 => KeyCode::NumLock,
//0x48 => KeyCode::NumpadClear,
// TODO: (Artur) for me, kVK_VolumeUp is 0x48
// macOS 10.11
// /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
0x49 => KeyCode::AudioVolumeUp,
0x4a => KeyCode::AudioVolumeDown,
0x4b => KeyCode::NumpadDivide,
0x4c => KeyCode::NumpadEnter,
//0x4d => unknown,
0x4e => KeyCode::NumpadSubtract,
0x4f => KeyCode::F18,
0x50 => KeyCode::F19,
0x51 => KeyCode::NumpadEqual,
0x52 => KeyCode::Numpad0,
0x53 => KeyCode::Numpad1,
0x54 => KeyCode::Numpad2,
0x55 => KeyCode::Numpad3,
0x56 => KeyCode::Numpad4,
0x57 => KeyCode::Numpad5,
0x58 => KeyCode::Numpad6,
0x59 => KeyCode::Numpad7,
0x5a => KeyCode::F20,
0x5b => KeyCode::Numpad8,
0x5c => KeyCode::Numpad9,
0x5d => KeyCode::IntlYen,
//0x5e => JIS Ro,
//0x5f => unknown,
0x60 => KeyCode::F5,
0x61 => KeyCode::F6,
0x62 => KeyCode::F7,
0x63 => KeyCode::F3,
0x64 => KeyCode::F8,
0x65 => KeyCode::F9,
//0x66 => JIS Eisuu (macOS),
0x67 => KeyCode::F11,
//0x68 => JIS Kanna (macOS),
0x69 => KeyCode::F13,
0x6a => KeyCode::F16,
0x6b => KeyCode::F14,
//0x6c => unknown,
0x6d => KeyCode::F10,
//0x6e => unknown,
0x6f => KeyCode::F12,
//0x70 => unknown,
0x71 => KeyCode::F15,
0x72 => KeyCode::Insert,
0x73 => KeyCode::Home,
0x74 => KeyCode::PageUp,
0x75 => KeyCode::Delete,
0x76 => KeyCode::F4,
0x77 => KeyCode::End,
0x78 => KeyCode::F2,
0x79 => KeyCode::PageDown,
0x7a => KeyCode::F1,
0x7b => KeyCode::ArrowLeft,
0x7c => KeyCode::ArrowRight,
0x7d => KeyCode::ArrowDown,
0x7e => KeyCode::ArrowUp,
//0x7f => unknown,
// 0xA is the caret (^) an macOS's German QERTZ layout. This key is at the same location as
// backquote (`) on Windows' US layout.
0xa => KeyCode::Backquote,
_ => KeyCode::Unidentified(NativeKeyCode::MacOS(scancode as u16)),
}
}
}

View File

@@ -1,60 +0,0 @@
use core_graphics::display::CGDisplay;
use icrate::Foundation::{CGFloat, NSNotFound, NSPoint, NSRange, NSRect, NSUInteger};
use crate::dpi::LogicalPosition;
// Replace with `!` once stable
#[derive(Debug)]
pub enum Never {}
pub const EMPTY_RANGE: NSRange = NSRange {
location: NSNotFound as NSUInteger,
length: 0,
};
macro_rules! trace_scope {
($s:literal) => {
let _crate = $crate::platform_impl::platform::util::TraceGuard::new(module_path!(), $s);
};
}
pub(crate) struct TraceGuard {
module_path: &'static str,
called_from_fn: &'static str,
}
impl TraceGuard {
#[inline]
pub(crate) fn new(module_path: &'static str, called_from_fn: &'static str) -> Self {
trace!(target: module_path, "Triggered `{}`", called_from_fn);
Self {
module_path,
called_from_fn,
}
}
}
impl Drop for TraceGuard {
#[inline]
fn drop(&mut self) {
trace!(target: self.module_path, "Completed `{}`", self.called_from_fn);
}
}
// For consistency with other platforms, this will...
// 1. translate the bottom-left window corner into the top-left window corner
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
#[allow(clippy::unnecessary_cast)]
pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height) as f64
}
/// Converts from winit screen-coordinates to macOS screen-coordinates.
/// Winit: top-left is (0, 0) and y increasing downwards
/// macOS: bottom-left is (0, 0) and y increasing upwards
pub fn window_position(position: LogicalPosition<f64>) -> NSPoint {
NSPoint::new(
position.x as CGFloat,
CGDisplay::main().pixels_high() as CGFloat - position.y as CGFloat,
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,483 +0,0 @@
#![allow(clippy::unnecessary_cast)]
use std::cell::Cell;
use std::ptr::{self, NonNull};
use icrate::Foundation::{NSArray, NSObject, NSSize, NSString};
use objc2::declare::{Ivar, IvarDrop};
use objc2::rc::{autoreleasepool, Id};
use objc2::runtime::AnyObject;
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType};
use super::appkit::{
NSApplicationPresentationOptions, NSFilenamesPboardType, NSPasteboard, NSWindowOcclusionState,
};
use super::{
app_state::AppState,
util,
window::{get_ns_theme, WinitWindow},
Fullscreen,
};
use crate::{
dpi::{LogicalPosition, LogicalSize},
event::{Event, WindowEvent},
window::WindowId,
};
#[derive(Debug)]
pub struct State {
// This is set when WindowBuilder::with_fullscreen was set,
// see comments of `window_did_fail_to_enter_fullscreen`
initial_fullscreen: Cell<bool>,
// During `windowDidResize`, we use this to only send Moved if the position changed.
previous_position: Cell<Option<(f64, f64)>>,
// Used to prevent redundant events.
previous_scale_factor: Cell<f64>,
}
declare_class!(
#[derive(Debug)]
pub(crate) struct WinitWindowDelegate {
window: IvarDrop<Id<WinitWindow>, "_window">,
// TODO: It may be possible for delegate methods to be called
// asynchronously, causing data races panics?
// TODO: Remove unnecessary boxing here
state: IvarDrop<Box<State>, "_state">,
}
mod ivars;
unsafe impl ClassType for WinitWindowDelegate {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "WinitWindowDelegate";
}
unsafe impl WinitWindowDelegate {
#[method(initWithWindow:initialFullscreen:)]
unsafe fn init_with_winit(
this: *mut Self,
window: &WinitWindow,
initial_fullscreen: bool,
) -> Option<NonNull<Self>> {
let this: Option<&mut Self> = unsafe { msg_send![super(this), init] };
this.map(|this| {
let scale_factor = window.scale_factor();
Ivar::write(&mut this.window, window.retain());
Ivar::write(
&mut this.state,
Box::new(State {
initial_fullscreen: Cell::new(initial_fullscreen),
previous_position: Cell::new(None),
previous_scale_factor: Cell::new(scale_factor),
}),
);
if scale_factor != 1.0 {
this.queue_static_scale_factor_changed_event();
}
this.window.setDelegate(Some(this));
// Enable theme change event
let notification_center: Id<AnyObject> =
unsafe { msg_send_id![class!(NSDistributedNotificationCenter), defaultCenter] };
let notification_name =
NSString::from_str("AppleInterfaceThemeChangedNotification");
let _: () = unsafe {
msg_send![
&notification_center,
addObserver: &*this
selector: sel!(effectiveAppearanceDidChange:)
name: &*notification_name
object: ptr::null::<AnyObject>()
]
};
NonNull::from(this)
})
}
}
// NSWindowDelegate + NSDraggingDestination protocols
unsafe impl WinitWindowDelegate {
#[method(windowShouldClose:)]
fn window_should_close(&self, _: Option<&AnyObject>) -> bool {
trace_scope!("windowShouldClose:");
self.queue_event(WindowEvent::CloseRequested);
false
}
#[method(windowWillClose:)]
fn window_will_close(&self, _: Option<&AnyObject>) {
trace_scope!("windowWillClose:");
// `setDelegate:` retains the previous value and then autoreleases it
autoreleasepool(|_| {
// Since El Capitan, we need to be careful that delegate methods can't
// be called after the window closes.
self.window.setDelegate(None);
});
self.queue_event(WindowEvent::Destroyed);
}
#[method(windowDidResize:)]
fn window_did_resize(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidResize:");
// NOTE: WindowEvent::Resized is reported in frameDidChange.
self.emit_move_event();
}
#[method(windowWillStartLiveResize:)]
fn window_will_start_live_resize(&self, _: Option<&AnyObject>) {
trace_scope!("windowWillStartLiveResize:");
let increments = self
.window
.lock_shared_state("window_will_enter_fullscreen")
.resize_increments;
self.window.set_resize_increments_inner(increments);
}
#[method(windowDidEndLiveResize:)]
fn window_did_end_live_resize(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidEndLiveResize:");
self.window.set_resize_increments_inner(NSSize::new(1., 1.));
}
// This won't be triggered if the move was part of a resize.
#[method(windowDidMove:)]
fn window_did_move(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidMove:");
self.emit_move_event();
}
#[method(windowDidChangeBackingProperties:)]
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidChangeBackingProperties:");
self.queue_static_scale_factor_changed_event();
}
#[method(windowDidBecomeKey:)]
fn window_did_become_key(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidBecomeKey:");
// TODO: center the cursor if the window had mouse grab when it
// lost focus
self.queue_event(WindowEvent::Focused(true));
}
#[method(windowDidResignKey:)]
fn window_did_resign_key(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidResignKey:");
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
// NSWindowDelegate will receive a didResignKey event despite no event
// being received when the modifiers are released. This is because
// flagsChanged events are received by the NSView instead of the
// NSWindowDelegate, and as a result a tracked modifiers state can quite
// easily fall out of synchrony with reality. This requires us to emit
// a synthetic ModifiersChanged event when we lose focus.
self.window.view().reset_modifiers();
self.queue_event(WindowEvent::Focused(false));
}
/// Invoked when the dragged image enters destination bounds or frame
#[method(draggingEntered:)]
fn dragging_entered(&self, sender: &NSObject) -> bool {
trace_scope!("draggingEntered:");
use std::path::PathBuf;
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType });
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
filenames.into_iter().for_each(|file| {
let path = PathBuf::from(file.to_string());
self.queue_event(WindowEvent::HoveredFile(path));
});
true
}
/// Invoked when the image is released
#[method(prepareForDragOperation:)]
fn prepare_for_drag_operation(&self, _sender: &NSObject) -> bool {
trace_scope!("prepareForDragOperation:");
true
}
/// Invoked after the released image has been removed from the screen
#[method(performDragOperation:)]
fn perform_drag_operation(&self, sender: &NSObject) -> bool {
trace_scope!("performDragOperation:");
use std::path::PathBuf;
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType });
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
filenames.into_iter().for_each(|file| {
let path = PathBuf::from(file.to_string());
self.queue_event(WindowEvent::DroppedFile(path));
});
true
}
/// Invoked when the dragging operation is complete
#[method(concludeDragOperation:)]
fn conclude_drag_operation(&self, _sender: Option<&NSObject>) {
trace_scope!("concludeDragOperation:");
}
/// Invoked when the dragging operation is cancelled
#[method(draggingExited:)]
fn dragging_exited(&self, _sender: Option<&NSObject>) {
trace_scope!("draggingExited:");
self.queue_event(WindowEvent::HoveredFileCancelled);
}
/// Invoked when before enter fullscreen
#[method(windowWillEnterFullScreen:)]
fn window_will_enter_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowWillEnterFullScreen:");
let mut shared_state = self
.window
.lock_shared_state("window_will_enter_fullscreen");
shared_state.maximized = self.window.is_zoomed();
let fullscreen = shared_state.fullscreen.as_ref();
match fullscreen {
// Exclusive mode sets the state in `set_fullscreen` as the user
// can't enter exclusive mode by other means (like the
// fullscreen button on the window decorations)
Some(Fullscreen::Exclusive(_)) => (),
// `window_will_enter_fullscreen` was triggered and we're already
// in fullscreen, so we must've reached here by `set_fullscreen`
// as it updates the state
Some(Fullscreen::Borderless(_)) => (),
// Otherwise, we must've reached fullscreen by the user clicking
// on the green fullscreen button. Update state!
None => {
let current_monitor = self.window.current_monitor_inner();
shared_state.fullscreen = Some(Fullscreen::Borderless(current_monitor))
}
}
shared_state.in_fullscreen_transition = true;
}
/// Invoked when before exit fullscreen
#[method(windowWillExitFullScreen:)]
fn window_will_exit_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowWillExitFullScreen:");
let mut shared_state = self.window.lock_shared_state("window_will_exit_fullscreen");
shared_state.in_fullscreen_transition = true;
}
#[method(window:willUseFullScreenPresentationOptions:)]
fn window_will_use_fullscreen_presentation_options(
&self,
_: Option<&AnyObject>,
proposed_options: NSApplicationPresentationOptions,
) -> NSApplicationPresentationOptions {
trace_scope!("window:willUseFullScreenPresentationOptions:");
// Generally, games will want to disable the menu bar and the dock. Ideally,
// this would be configurable by the user. Unfortunately because of our
// `CGShieldingWindowLevel() + 1` hack (see `set_fullscreen`), our window is
// placed on top of the menu bar in exclusive fullscreen mode. This looks
// broken so we always disable the menu bar in exclusive fullscreen. We may
// still want to make this configurable for borderless fullscreen. Right now
// we don't, for consistency. If we do, it should be documented that the
// user-provided options are ignored in exclusive fullscreen.
let mut options = proposed_options;
let shared_state = self
.window
.lock_shared_state("window_will_use_fullscreen_presentation_options");
if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen {
options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
}
options
}
/// Invoked when entered fullscreen
#[method(windowDidEnterFullScreen:)]
fn window_did_enter_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidEnterFullScreen:");
self.state.initial_fullscreen.set(false);
let mut shared_state = self.window.lock_shared_state("window_did_enter_fullscreen");
shared_state.in_fullscreen_transition = false;
let target_fullscreen = shared_state.target_fullscreen.take();
drop(shared_state);
if let Some(target_fullscreen) = target_fullscreen {
self.window.set_fullscreen(target_fullscreen);
}
}
/// Invoked when exited fullscreen
#[method(windowDidExitFullScreen:)]
fn window_did_exit_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidExitFullScreen:");
self.window.restore_state_from_fullscreen();
let mut shared_state = self.window.lock_shared_state("window_did_exit_fullscreen");
shared_state.in_fullscreen_transition = false;
let target_fullscreen = shared_state.target_fullscreen.take();
drop(shared_state);
if let Some(target_fullscreen) = target_fullscreen {
self.window.set_fullscreen(target_fullscreen);
}
}
/// Invoked when fail to enter fullscreen
///
/// When this window launch from a fullscreen app (e.g. launch from VS Code
/// terminal), it creates a new virtual destkop and a transition animation.
/// This animation takes one second and cannot be disable without
/// elevated privileges. In this animation time, all toggleFullscreen events
/// will be failed. In this implementation, we will try again by using
/// performSelector:withObject:afterDelay: until window_did_enter_fullscreen.
/// It should be fine as we only do this at initialzation (i.e with_fullscreen
/// was set).
///
/// From Apple doc:
/// In some cases, the transition to enter full-screen mode can fail,
/// due to being in the midst of handling some other animation or user gesture.
/// This method indicates that there was an error, and you should clean up any
/// work you may have done to prepare to enter full-screen mode.
#[method(windowDidFailToEnterFullScreen:)]
fn window_did_fail_to_enter_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidFailToEnterFullScreen:");
let mut shared_state = self
.window
.lock_shared_state("window_did_fail_to_enter_fullscreen");
shared_state.in_fullscreen_transition = false;
shared_state.target_fullscreen = None;
if self.state.initial_fullscreen.get() {
#[allow(clippy::let_unit_value)]
unsafe {
let _: () = msg_send![
&*self.window,
performSelector: sel!(toggleFullScreen:),
withObject: ptr::null::<AnyObject>(),
afterDelay: 0.5,
];
};
} else {
self.window.restore_state_from_fullscreen();
}
}
// Invoked when the occlusion state of the window changes
#[method(windowDidChangeOcclusionState:)]
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidChangeOcclusionState:");
self.queue_event(WindowEvent::Occluded(
!self
.window
.occlusionState()
.contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible),
))
}
// Observe theme change
#[method(effectiveAppearanceDidChange:)]
fn effective_appearance_did_change(&self, sender: Option<&AnyObject>) {
trace_scope!("Triggered `effectiveAppearanceDidChange:`");
unsafe {
msg_send![
self,
performSelectorOnMainThread: sel!(effectiveAppearanceDidChangedOnMainThread:),
withObject: sender,
waitUntilDone: false,
]
}
}
#[method(effectiveAppearanceDidChangedOnMainThread:)]
fn effective_appearance_did_changed_on_main_thread(&self, _: Option<&AnyObject>) {
let theme = get_ns_theme();
let mut shared_state = self
.window
.lock_shared_state("effective_appearance_did_change");
let current_theme = shared_state.current_theme;
shared_state.current_theme = Some(theme);
drop(shared_state);
if current_theme != Some(theme) {
self.queue_event(WindowEvent::ThemeChanged(theme));
}
}
#[method(windowDidChangeScreen:)]
fn window_did_change_screen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidChangeScreen:");
let is_simple_fullscreen = self
.window
.lock_shared_state("window_did_change_screen")
.is_simple_fullscreen;
if is_simple_fullscreen {
if let Some(screen) = self.window.screen() {
self.window.setFrame_display(screen.frame(), true);
}
}
}
}
);
impl WinitWindowDelegate {
pub fn new(window: &WinitWindow, initial_fullscreen: bool) -> Id<Self> {
unsafe {
msg_send_id![
Self::alloc(),
initWithWindow: window,
initialFullscreen: initial_fullscreen,
]
}
}
pub(crate) fn queue_event(&self, event: WindowEvent) {
let event = Event::WindowEvent {
window_id: WindowId(self.window.id()),
event,
};
AppState::queue_event(event);
}
fn queue_static_scale_factor_changed_event(&self) {
let scale_factor = self.window.scale_factor();
if scale_factor == self.state.previous_scale_factor.get() {
return;
};
self.state.previous_scale_factor.set(scale_factor);
let suggested_size = self.view_size();
AppState::queue_static_scale_factor_changed_event(
self.window.clone(),
suggested_size.to_physical(scale_factor),
scale_factor,
);
}
fn emit_move_event(&self) {
let rect = self.window.frame();
let x = rect.origin.x as f64;
let y = util::bottom_left_to_top_left(rect);
if self.state.previous_position.get() != Some((x, y)) {
self.state.previous_position.set(Some((x, y)));
let scale_factor = self.window.scale_factor();
let physical_pos = LogicalPosition::<f64>::from((x, y)).to_physical(scale_factor);
self.queue_event(WindowEvent::Moved(physical_pos));
}
}
fn view_size(&self) -> LogicalSize<f64> {
let size = self.window.contentView().frame().size;
LogicalSize::new(size.width as f64, size.height as f64)
}
}

View File

@@ -1,297 +0,0 @@
use atomic_waker::AtomicWaker;
use std::future;
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, RecvError, SendError, Sender, TryRecvError};
use std::sync::{Arc, Condvar, Mutex, RwLock};
use std::task::Poll;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};
// Unsafe wrapper type that allows us to use `T` when it's not `Send` from other threads.
// `value` **must** only be accessed on the main thread.
pub struct MainThreadSafe<const SYNC: bool, T: 'static, E: 'static> {
// We wrap this in an `Arc` to allow it to be safely cloned without accessing the value.
// The `RwLock` lets us safely drop in any thread.
// The `Option` lets us safely drop `T` only in the main thread, while letting other threads drop `None`.
value: Arc<RwLock<Option<T>>>,
handler: fn(&RwLock<Option<T>>, E),
sender: AsyncSender<E>,
// Prevent's `Send` or `Sync` to be automatically implemented.
local: PhantomData<*const ()>,
}
impl<const SYNC: bool, T, E> MainThreadSafe<SYNC, T, E> {
thread_local! {
static MAIN_THREAD: bool = {
#[wasm_bindgen]
extern "C" {
#[derive(Clone)]
type Global;
#[wasm_bindgen(method, getter, js_name = Window)]
fn window(this: &Global) -> JsValue;
}
let global: Global = js_sys::global().unchecked_into();
!global.window().is_undefined()
};
}
#[track_caller]
fn new(value: T, handler: fn(&RwLock<Option<T>>, E)) -> Option<Self> {
Self::MAIN_THREAD.with(|safe| {
if !safe {
panic!("only callable from inside the `Window`")
}
});
let value = Arc::new(RwLock::new(Some(value)));
let (sender, receiver) = channel::<E>();
wasm_bindgen_futures::spawn_local({
let value = Arc::clone(&value);
async move {
while let Ok(event) = receiver.next().await {
handler(&value, event)
}
// An error was returned because the channel was closed, which
// happens when all senders are dropped.
value.write().unwrap().take().unwrap();
}
});
Some(Self {
value,
handler,
sender,
local: PhantomData,
})
}
pub fn send(&self, event: E) {
Self::MAIN_THREAD.with(|is_main_thread| {
if *is_main_thread {
(self.handler)(&self.value, event)
} else {
self.sender.send(event).unwrap()
}
})
}
fn is_main_thread(&self) -> bool {
Self::MAIN_THREAD.with(|is_main_thread| *is_main_thread)
}
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
Self::MAIN_THREAD.with(|is_main_thread| {
if *is_main_thread {
Some(f(self.value.read().unwrap().as_ref().unwrap()))
} else {
None
}
})
}
}
impl<const SYNC: bool, T, E> Clone for MainThreadSafe<SYNC, T, E> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
handler: self.handler,
sender: self.sender.clone(),
local: PhantomData,
}
}
}
unsafe impl<const SYNC: bool, T, E> Send for MainThreadSafe<SYNC, T, E> {}
unsafe impl<T, E> Sync for MainThreadSafe<true, T, E> {}
fn channel<T>() -> (AsyncSender<T>, AsyncReceiver<T>) {
let (sender, receiver) = mpsc::channel();
let sender = Arc::new(Mutex::new(sender));
let waker = Arc::new(AtomicWaker::new());
let closed = Arc::new(AtomicBool::new(false));
let sender = AsyncSender {
sender,
closed: closed.clone(),
waker: Arc::clone(&waker),
};
let receiver = AsyncReceiver {
receiver,
closed,
waker,
};
(sender, receiver)
}
struct AsyncSender<T> {
// We need to wrap it into a `Mutex` to make it `Sync`. So the sender can't
// be accessed on the main thread, as it could block. Additionally we need
// to wrap it in an `Arc` to make it clonable on the main thread without
// having to block.
sender: Arc<Mutex<Sender<T>>>,
closed: Arc<AtomicBool>,
waker: Arc<AtomicWaker>,
}
impl<T> AsyncSender<T> {
pub fn send(&self, event: T) -> Result<(), SendError<T>> {
self.sender.lock().unwrap().send(event)?;
self.waker.wake();
Ok(())
}
}
impl<T> Clone for AsyncSender<T> {
fn clone(&self) -> Self {
Self {
sender: self.sender.clone(),
waker: self.waker.clone(),
closed: self.closed.clone(),
}
}
}
impl<T> Drop for AsyncSender<T> {
fn drop(&mut self) {
// If it's the last + the one held by the receiver make sure to wake it
// up and tell it that all receiver have dropped.
if Arc::strong_count(&self.closed) == 2 {
self.closed.store(true, Ordering::Relaxed);
self.waker.wake()
}
}
}
struct AsyncReceiver<T> {
receiver: Receiver<T>,
closed: Arc<AtomicBool>,
waker: Arc<AtomicWaker>,
}
impl<T> AsyncReceiver<T> {
pub async fn next(&self) -> Result<T, RecvError> {
future::poll_fn(|cx| match self.receiver.try_recv() {
Ok(event) => Poll::Ready(Ok(event)),
Err(TryRecvError::Empty) => {
if self.closed.load(Ordering::Relaxed) {
return Poll::Ready(Err(RecvError));
}
self.waker.register(cx.waker());
match self.receiver.try_recv() {
Ok(event) => Poll::Ready(Ok(event)),
Err(TryRecvError::Empty) => {
if self.closed.load(Ordering::Relaxed) {
Poll::Ready(Err(RecvError))
} else {
Poll::Pending
}
}
Err(TryRecvError::Disconnected) => Poll::Ready(Err(RecvError)),
}
}
Err(TryRecvError::Disconnected) => Poll::Ready(Err(RecvError)),
})
.await
}
}
pub struct Dispatcher<T: 'static>(MainThreadSafe<true, T, Closure<T>>);
pub struct Closure<T>(Box<dyn FnOnce(&T) + Send>);
impl<T> Dispatcher<T> {
#[track_caller]
pub fn new(value: T) -> Option<Self> {
MainThreadSafe::new(value, |value, Closure(closure)| {
// SAFETY: The given `Closure` here isn't really `'static`, so we shouldn't do anything
// funny with it here. See `Self::queue()`.
closure(value.read().unwrap().as_ref().unwrap())
})
.map(Self)
}
pub fn dispatch(&self, f: impl 'static + FnOnce(&T) + Send) {
if self.is_main_thread() {
self.0.with(f).unwrap()
} else {
self.0.send(Closure(Box::new(f)))
}
}
pub fn queue<R: Send>(&self, f: impl FnOnce(&T) -> R + Send) -> R {
if self.is_main_thread() {
self.0.with(f).unwrap()
} else {
let pair = Arc::new((Mutex::new(None), Condvar::new()));
let closure = Box::new({
let pair = pair.clone();
move |value: &T| {
*pair.0.lock().unwrap() = Some(f(value));
pair.1.notify_one();
}
}) as Box<dyn FnOnce(&T) + Send>;
// SAFETY: The `transmute` is necessary because `Closure` requires `'static`. This is
// safe because this function won't return until `f` has finished executing. See
// `Self::new()`.
let closure = Closure(unsafe { std::mem::transmute(closure) });
self.0.send(closure);
let mut started = pair.0.lock().unwrap();
while started.is_none() {
started = pair.1.wait(started).unwrap();
}
started.take().unwrap()
}
}
}
impl<T> Deref for Dispatcher<T> {
type Target = MainThreadSafe<true, T, Closure<T>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
type ChannelValue<T, E> = MainThreadSafe<false, (T, fn(&T, E)), E>;
pub struct Channel<T: 'static, E: 'static>(ChannelValue<T, E>);
impl<T, E> Channel<T, E> {
pub fn new(value: T, handler: fn(&T, E)) -> Option<Self> {
MainThreadSafe::new((value, handler), |runner, event| {
let lock = runner.read().unwrap();
let (value, handler) = lock.as_ref().unwrap();
handler(value, event);
})
.map(Self)
}
}
impl<T, E> Clone for Channel<T, E> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T, E> Deref for Channel<T, E> {
type Target = ChannelValue<T, E>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@@ -1,8 +0,0 @@
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(pub i32);
impl DeviceId {
pub const unsafe fn dummy() -> Self {
Self(0)
}
}

View File

@@ -1,39 +0,0 @@
use std::sync::mpsc::Sender;
use super::runner;
use crate::event::Event;
use crate::event_loop::EventLoopClosed;
use crate::platform_impl::platform::r#async::Channel;
pub struct EventLoopProxy<T: 'static> {
// used to wake the event loop handler, not to actually pass data
runner: Channel<runner::Shared, ()>,
sender: Sender<T>,
}
impl<T: 'static> EventLoopProxy<T> {
pub fn new(runner: runner::Shared, sender: Sender<T>) -> Self {
Self {
runner: Channel::new(runner, |runner, event| {
runner.send_event(Event::UserEvent(event))
})
.unwrap(),
sender,
}
}
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender.send(event).unwrap();
self.runner.send(());
Ok(())
}
}
impl<T: 'static> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
Self {
runner: self.runner.clone(),
sender: self.sender.clone(),
}
}
}

View File

@@ -1,522 +0,0 @@
use smol_str::SmolStr;
use crate::keyboard::{Key, KeyCode, NativeKey, NativeKeyCode};
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub(crate) struct KeyEventExtra;
impl Key {
pub(crate) fn from_key_attribute_value(kav: &str) -> Self {
match kav {
"Unidentified" => Key::Unidentified(NativeKey::Web(SmolStr::new(kav))),
"Dead" => Key::Dead(None),
"Alt" => Key::Alt,
"AltGraph" => Key::AltGraph,
"CapsLock" => Key::CapsLock,
"Control" => Key::Control,
"Fn" => Key::Fn,
"FnLock" => Key::FnLock,
"NumLock" => Key::NumLock,
"ScrollLock" => Key::ScrollLock,
"Shift" => Key::Shift,
"Symbol" => Key::Symbol,
"SymbolLock" => Key::SymbolLock,
"Hyper" => Key::Hyper,
"Meta" => Key::Super,
"Enter" => Key::Enter,
"Tab" => Key::Tab,
" " => Key::Space,
"ArrowDown" => Key::ArrowDown,
"ArrowLeft" => Key::ArrowLeft,
"ArrowRight" => Key::ArrowRight,
"ArrowUp" => Key::ArrowUp,
"End" => Key::End,
"Home" => Key::Home,
"PageDown" => Key::PageDown,
"PageUp" => Key::PageUp,
"Backspace" => Key::Backspace,
"Clear" => Key::Clear,
"Copy" => Key::Copy,
"CrSel" => Key::CrSel,
"Cut" => Key::Cut,
"Delete" => Key::Delete,
"EraseEof" => Key::EraseEof,
"ExSel" => Key::ExSel,
"Insert" => Key::Insert,
"Paste" => Key::Paste,
"Redo" => Key::Redo,
"Undo" => Key::Undo,
"Accept" => Key::Accept,
"Again" => Key::Again,
"Attn" => Key::Attn,
"Cancel" => Key::Cancel,
"ContextMenu" => Key::ContextMenu,
"Escape" => Key::Escape,
"Execute" => Key::Execute,
"Find" => Key::Find,
"Help" => Key::Help,
"Pause" => Key::Pause,
"Play" => Key::Play,
"Props" => Key::Props,
"Select" => Key::Select,
"ZoomIn" => Key::ZoomIn,
"ZoomOut" => Key::ZoomOut,
"BrightnessDown" => Key::BrightnessDown,
"BrightnessUp" => Key::BrightnessUp,
"Eject" => Key::Eject,
"LogOff" => Key::LogOff,
"Power" => Key::Power,
"PowerOff" => Key::PowerOff,
"PrintScreen" => Key::PrintScreen,
"Hibernate" => Key::Hibernate,
"Standby" => Key::Standby,
"WakeUp" => Key::WakeUp,
"AllCandidates" => Key::AllCandidates,
"Alphanumeric" => Key::Alphanumeric,
"CodeInput" => Key::CodeInput,
"Compose" => Key::Compose,
"Convert" => Key::Convert,
"FinalMode" => Key::FinalMode,
"GroupFirst" => Key::GroupFirst,
"GroupLast" => Key::GroupLast,
"GroupNext" => Key::GroupNext,
"GroupPrevious" => Key::GroupPrevious,
"ModeChange" => Key::ModeChange,
"NextCandidate" => Key::NextCandidate,
"NonConvert" => Key::NonConvert,
"PreviousCandidate" => Key::PreviousCandidate,
"Process" => Key::Process,
"SingleCandidate" => Key::SingleCandidate,
"HangulMode" => Key::HangulMode,
"HanjaMode" => Key::HanjaMode,
"JunjaMode" => Key::JunjaMode,
"Eisu" => Key::Eisu,
"Hankaku" => Key::Hankaku,
"Hiragana" => Key::Hiragana,
"HiraganaKatakana" => Key::HiraganaKatakana,
"KanaMode" => Key::KanaMode,
"KanjiMode" => Key::KanjiMode,
"Katakana" => Key::Katakana,
"Romaji" => Key::Romaji,
"Zenkaku" => Key::Zenkaku,
"ZenkakuHankaku" => Key::ZenkakuHankaku,
"Soft1" => Key::Soft1,
"Soft2" => Key::Soft2,
"Soft3" => Key::Soft3,
"Soft4" => Key::Soft4,
"ChannelDown" => Key::ChannelDown,
"ChannelUp" => Key::ChannelUp,
"Close" => Key::Close,
"MailForward" => Key::MailForward,
"MailReply" => Key::MailReply,
"MailSend" => Key::MailSend,
"MediaClose" => Key::MediaClose,
"MediaFastForward" => Key::MediaFastForward,
"MediaPause" => Key::MediaPause,
"MediaPlay" => Key::MediaPlay,
"MediaPlayPause" => Key::MediaPlayPause,
"MediaRecord" => Key::MediaRecord,
"MediaRewind" => Key::MediaRewind,
"MediaStop" => Key::MediaStop,
"MediaTrackNext" => Key::MediaTrackNext,
"MediaTrackPrevious" => Key::MediaTrackPrevious,
"New" => Key::New,
"Open" => Key::Open,
"Print" => Key::Print,
"Save" => Key::Save,
"SpellCheck" => Key::SpellCheck,
"Key11" => Key::Key11,
"Key12" => Key::Key12,
"AudioBalanceLeft" => Key::AudioBalanceLeft,
"AudioBalanceRight" => Key::AudioBalanceRight,
"AudioBassBoostDown" => Key::AudioBassBoostDown,
"AudioBassBoostToggle" => Key::AudioBassBoostToggle,
"AudioBassBoostUp" => Key::AudioBassBoostUp,
"AudioFaderFront" => Key::AudioFaderFront,
"AudioFaderRear" => Key::AudioFaderRear,
"AudioSurroundModeNext" => Key::AudioSurroundModeNext,
"AudioTrebleDown" => Key::AudioTrebleDown,
"AudioTrebleUp" => Key::AudioTrebleUp,
"AudioVolumeDown" => Key::AudioVolumeDown,
"AudioVolumeUp" => Key::AudioVolumeUp,
"AudioVolumeMute" => Key::AudioVolumeMute,
"MicrophoneToggle" => Key::MicrophoneToggle,
"MicrophoneVolumeDown" => Key::MicrophoneVolumeDown,
"MicrophoneVolumeUp" => Key::MicrophoneVolumeUp,
"MicrophoneVolumeMute" => Key::MicrophoneVolumeMute,
"SpeechCorrectionList" => Key::SpeechCorrectionList,
"SpeechInputToggle" => Key::SpeechInputToggle,
"LaunchApplication1" => Key::LaunchApplication1,
"LaunchApplication2" => Key::LaunchApplication2,
"LaunchCalendar" => Key::LaunchCalendar,
"LaunchContacts" => Key::LaunchContacts,
"LaunchMail" => Key::LaunchMail,
"LaunchMediaPlayer" => Key::LaunchMediaPlayer,
"LaunchMusicPlayer" => Key::LaunchMusicPlayer,
"LaunchPhone" => Key::LaunchPhone,
"LaunchScreenSaver" => Key::LaunchScreenSaver,
"LaunchSpreadsheet" => Key::LaunchSpreadsheet,
"LaunchWebBrowser" => Key::LaunchWebBrowser,
"LaunchWebCam" => Key::LaunchWebCam,
"LaunchWordProcessor" => Key::LaunchWordProcessor,
"BrowserBack" => Key::BrowserBack,
"BrowserFavorites" => Key::BrowserFavorites,
"BrowserForward" => Key::BrowserForward,
"BrowserHome" => Key::BrowserHome,
"BrowserRefresh" => Key::BrowserRefresh,
"BrowserSearch" => Key::BrowserSearch,
"BrowserStop" => Key::BrowserStop,
"AppSwitch" => Key::AppSwitch,
"Call" => Key::Call,
"Camera" => Key::Camera,
"CameraFocus" => Key::CameraFocus,
"EndCall" => Key::EndCall,
"GoBack" => Key::GoBack,
"GoHome" => Key::GoHome,
"HeadsetHook" => Key::HeadsetHook,
"LastNumberRedial" => Key::LastNumberRedial,
"Notification" => Key::Notification,
"MannerMode" => Key::MannerMode,
"VoiceDial" => Key::VoiceDial,
"TV" => Key::TV,
"TV3DMode" => Key::TV3DMode,
"TVAntennaCable" => Key::TVAntennaCable,
"TVAudioDescription" => Key::TVAudioDescription,
"TVAudioDescriptionMixDown" => Key::TVAudioDescriptionMixDown,
"TVAudioDescriptionMixUp" => Key::TVAudioDescriptionMixUp,
"TVContentsMenu" => Key::TVContentsMenu,
"TVDataService" => Key::TVDataService,
"TVInput" => Key::TVInput,
"TVInputComponent1" => Key::TVInputComponent1,
"TVInputComponent2" => Key::TVInputComponent2,
"TVInputComposite1" => Key::TVInputComposite1,
"TVInputComposite2" => Key::TVInputComposite2,
"TVInputHDMI1" => Key::TVInputHDMI1,
"TVInputHDMI2" => Key::TVInputHDMI2,
"TVInputHDMI3" => Key::TVInputHDMI3,
"TVInputHDMI4" => Key::TVInputHDMI4,
"TVInputVGA1" => Key::TVInputVGA1,
"TVMediaContext" => Key::TVMediaContext,
"TVNetwork" => Key::TVNetwork,
"TVNumberEntry" => Key::TVNumberEntry,
"TVPower" => Key::TVPower,
"TVRadioService" => Key::TVRadioService,
"TVSatellite" => Key::TVSatellite,
"TVSatelliteBS" => Key::TVSatelliteBS,
"TVSatelliteCS" => Key::TVSatelliteCS,
"TVSatelliteToggle" => Key::TVSatelliteToggle,
"TVTerrestrialAnalog" => Key::TVTerrestrialAnalog,
"TVTerrestrialDigital" => Key::TVTerrestrialDigital,
"TVTimer" => Key::TVTimer,
"AVRInput" => Key::AVRInput,
"AVRPower" => Key::AVRPower,
"ColorF0Red" => Key::ColorF0Red,
"ColorF1Green" => Key::ColorF1Green,
"ColorF2Yellow" => Key::ColorF2Yellow,
"ColorF3Blue" => Key::ColorF3Blue,
"ColorF4Grey" => Key::ColorF4Grey,
"ColorF5Brown" => Key::ColorF5Brown,
"ClosedCaptionToggle" => Key::ClosedCaptionToggle,
"Dimmer" => Key::Dimmer,
"DisplaySwap" => Key::DisplaySwap,
"DVR" => Key::DVR,
"Exit" => Key::Exit,
"FavoriteClear0" => Key::FavoriteClear0,
"FavoriteClear1" => Key::FavoriteClear1,
"FavoriteClear2" => Key::FavoriteClear2,
"FavoriteClear3" => Key::FavoriteClear3,
"FavoriteRecall0" => Key::FavoriteRecall0,
"FavoriteRecall1" => Key::FavoriteRecall1,
"FavoriteRecall2" => Key::FavoriteRecall2,
"FavoriteRecall3" => Key::FavoriteRecall3,
"FavoriteStore0" => Key::FavoriteStore0,
"FavoriteStore1" => Key::FavoriteStore1,
"FavoriteStore2" => Key::FavoriteStore2,
"FavoriteStore3" => Key::FavoriteStore3,
"Guide" => Key::Guide,
"GuideNextDay" => Key::GuideNextDay,
"GuidePreviousDay" => Key::GuidePreviousDay,
"Info" => Key::Info,
"InstantReplay" => Key::InstantReplay,
"Link" => Key::Link,
"ListProgram" => Key::ListProgram,
"LiveContent" => Key::LiveContent,
"Lock" => Key::Lock,
"MediaApps" => Key::MediaApps,
"MediaAudioTrack" => Key::MediaAudioTrack,
"MediaLast" => Key::MediaLast,
"MediaSkipBackward" => Key::MediaSkipBackward,
"MediaSkipForward" => Key::MediaSkipForward,
"MediaStepBackward" => Key::MediaStepBackward,
"MediaStepForward" => Key::MediaStepForward,
"MediaTopMenu" => Key::MediaTopMenu,
"NavigateIn" => Key::NavigateIn,
"NavigateNext" => Key::NavigateNext,
"NavigateOut" => Key::NavigateOut,
"NavigatePrevious" => Key::NavigatePrevious,
"NextFavoriteChannel" => Key::NextFavoriteChannel,
"NextUserProfile" => Key::NextUserProfile,
"OnDemand" => Key::OnDemand,
"Pairing" => Key::Pairing,
"PinPDown" => Key::PinPDown,
"PinPMove" => Key::PinPMove,
"PinPToggle" => Key::PinPToggle,
"PinPUp" => Key::PinPUp,
"PlaySpeedDown" => Key::PlaySpeedDown,
"PlaySpeedReset" => Key::PlaySpeedReset,
"PlaySpeedUp" => Key::PlaySpeedUp,
"RandomToggle" => Key::RandomToggle,
"RcLowBattery" => Key::RcLowBattery,
"RecordSpeedNext" => Key::RecordSpeedNext,
"RfBypass" => Key::RfBypass,
"ScanChannelsToggle" => Key::ScanChannelsToggle,
"ScreenModeNext" => Key::ScreenModeNext,
"Settings" => Key::Settings,
"SplitScreenToggle" => Key::SplitScreenToggle,
"STBInput" => Key::STBInput,
"STBPower" => Key::STBPower,
"Subtitle" => Key::Subtitle,
"Teletext" => Key::Teletext,
"VideoModeNext" => Key::VideoModeNext,
"Wink" => Key::Wink,
"ZoomToggle" => Key::ZoomToggle,
"F1" => Key::F1,
"F2" => Key::F2,
"F3" => Key::F3,
"F4" => Key::F4,
"F5" => Key::F5,
"F6" => Key::F6,
"F7" => Key::F7,
"F8" => Key::F8,
"F9" => Key::F9,
"F10" => Key::F10,
"F11" => Key::F11,
"F12" => Key::F12,
"F13" => Key::F13,
"F14" => Key::F14,
"F15" => Key::F15,
"F16" => Key::F16,
"F17" => Key::F17,
"F18" => Key::F18,
"F19" => Key::F19,
"F20" => Key::F20,
"F21" => Key::F21,
"F22" => Key::F22,
"F23" => Key::F23,
"F24" => Key::F24,
"F25" => Key::F25,
"F26" => Key::F26,
"F27" => Key::F27,
"F28" => Key::F28,
"F29" => Key::F29,
"F30" => Key::F30,
"F31" => Key::F31,
"F32" => Key::F32,
"F33" => Key::F33,
"F34" => Key::F34,
"F35" => Key::F35,
string => Key::Character(SmolStr::new(string)),
}
}
}
impl KeyCode {
pub fn from_key_code_attribute_value(kcav: &str) -> Self {
match kcav {
"Backquote" => KeyCode::Backquote,
"Backslash" => KeyCode::Backslash,
"BracketLeft" => KeyCode::BracketLeft,
"BracketRight" => KeyCode::BracketRight,
"Comma" => KeyCode::Comma,
"Digit0" => KeyCode::Digit0,
"Digit1" => KeyCode::Digit1,
"Digit2" => KeyCode::Digit2,
"Digit3" => KeyCode::Digit3,
"Digit4" => KeyCode::Digit4,
"Digit5" => KeyCode::Digit5,
"Digit6" => KeyCode::Digit6,
"Digit7" => KeyCode::Digit7,
"Digit8" => KeyCode::Digit8,
"Digit9" => KeyCode::Digit9,
"Equal" => KeyCode::Equal,
"IntlBackslash" => KeyCode::IntlBackslash,
"IntlRo" => KeyCode::IntlRo,
"IntlYen" => KeyCode::IntlYen,
"KeyA" => KeyCode::KeyA,
"KeyB" => KeyCode::KeyB,
"KeyC" => KeyCode::KeyC,
"KeyD" => KeyCode::KeyD,
"KeyE" => KeyCode::KeyE,
"KeyF" => KeyCode::KeyF,
"KeyG" => KeyCode::KeyG,
"KeyH" => KeyCode::KeyH,
"KeyI" => KeyCode::KeyI,
"KeyJ" => KeyCode::KeyJ,
"KeyK" => KeyCode::KeyK,
"KeyL" => KeyCode::KeyL,
"KeyM" => KeyCode::KeyM,
"KeyN" => KeyCode::KeyN,
"KeyO" => KeyCode::KeyO,
"KeyP" => KeyCode::KeyP,
"KeyQ" => KeyCode::KeyQ,
"KeyR" => KeyCode::KeyR,
"KeyS" => KeyCode::KeyS,
"KeyT" => KeyCode::KeyT,
"KeyU" => KeyCode::KeyU,
"KeyV" => KeyCode::KeyV,
"KeyW" => KeyCode::KeyW,
"KeyX" => KeyCode::KeyX,
"KeyY" => KeyCode::KeyY,
"KeyZ" => KeyCode::KeyZ,
"Minus" => KeyCode::Minus,
"Period" => KeyCode::Period,
"Quote" => KeyCode::Quote,
"Semicolon" => KeyCode::Semicolon,
"Slash" => KeyCode::Slash,
"AltLeft" => KeyCode::AltLeft,
"AltRight" => KeyCode::AltRight,
"Backspace" => KeyCode::Backspace,
"CapsLock" => KeyCode::CapsLock,
"ContextMenu" => KeyCode::ContextMenu,
"ControlLeft" => KeyCode::ControlLeft,
"ControlRight" => KeyCode::ControlRight,
"Enter" => KeyCode::Enter,
"MetaLeft" => KeyCode::SuperLeft,
"MetaRight" => KeyCode::SuperRight,
"ShiftLeft" => KeyCode::ShiftLeft,
"ShiftRight" => KeyCode::ShiftRight,
"Space" => KeyCode::Space,
"Tab" => KeyCode::Tab,
"Convert" => KeyCode::Convert,
"KanaMode" => KeyCode::KanaMode,
"Lang1" => KeyCode::Lang1,
"Lang2" => KeyCode::Lang2,
"Lang3" => KeyCode::Lang3,
"Lang4" => KeyCode::Lang4,
"Lang5" => KeyCode::Lang5,
"NonConvert" => KeyCode::NonConvert,
"Delete" => KeyCode::Delete,
"End" => KeyCode::End,
"Help" => KeyCode::Help,
"Home" => KeyCode::Home,
"Insert" => KeyCode::Insert,
"PageDown" => KeyCode::PageDown,
"PageUp" => KeyCode::PageUp,
"ArrowDown" => KeyCode::ArrowDown,
"ArrowLeft" => KeyCode::ArrowLeft,
"ArrowRight" => KeyCode::ArrowRight,
"ArrowUp" => KeyCode::ArrowUp,
"NumLock" => KeyCode::NumLock,
"Numpad0" => KeyCode::Numpad0,
"Numpad1" => KeyCode::Numpad1,
"Numpad2" => KeyCode::Numpad2,
"Numpad3" => KeyCode::Numpad3,
"Numpad4" => KeyCode::Numpad4,
"Numpad5" => KeyCode::Numpad5,
"Numpad6" => KeyCode::Numpad6,
"Numpad7" => KeyCode::Numpad7,
"Numpad8" => KeyCode::Numpad8,
"Numpad9" => KeyCode::Numpad9,
"NumpadAdd" => KeyCode::NumpadAdd,
"NumpadBackspace" => KeyCode::NumpadBackspace,
"NumpadClear" => KeyCode::NumpadClear,
"NumpadClearEntry" => KeyCode::NumpadClearEntry,
"NumpadComma" => KeyCode::NumpadComma,
"NumpadDecimal" => KeyCode::NumpadDecimal,
"NumpadDivide" => KeyCode::NumpadDivide,
"NumpadEnter" => KeyCode::NumpadEnter,
"NumpadEqual" => KeyCode::NumpadEqual,
"NumpadHash" => KeyCode::NumpadHash,
"NumpadMemoryAdd" => KeyCode::NumpadMemoryAdd,
"NumpadMemoryClear" => KeyCode::NumpadMemoryClear,
"NumpadMemoryRecall" => KeyCode::NumpadMemoryRecall,
"NumpadMemoryStore" => KeyCode::NumpadMemoryStore,
"NumpadMemorySubtract" => KeyCode::NumpadMemorySubtract,
"NumpadMultiply" => KeyCode::NumpadMultiply,
"NumpadParenLeft" => KeyCode::NumpadParenLeft,
"NumpadParenRight" => KeyCode::NumpadParenRight,
"NumpadStar" => KeyCode::NumpadStar,
"NumpadSubtract" => KeyCode::NumpadSubtract,
"Escape" => KeyCode::Escape,
"Fn" => KeyCode::Fn,
"FnLock" => KeyCode::FnLock,
"PrintScreen" => KeyCode::PrintScreen,
"ScrollLock" => KeyCode::ScrollLock,
"Pause" => KeyCode::Pause,
"BrowserBack" => KeyCode::BrowserBack,
"BrowserFavorites" => KeyCode::BrowserFavorites,
"BrowserForward" => KeyCode::BrowserForward,
"BrowserHome" => KeyCode::BrowserHome,
"BrowserRefresh" => KeyCode::BrowserRefresh,
"BrowserSearch" => KeyCode::BrowserSearch,
"BrowserStop" => KeyCode::BrowserStop,
"Eject" => KeyCode::Eject,
"LaunchApp1" => KeyCode::LaunchApp1,
"LaunchApp2" => KeyCode::LaunchApp2,
"LaunchMail" => KeyCode::LaunchMail,
"MediaPlayPause" => KeyCode::MediaPlayPause,
"MediaSelect" => KeyCode::MediaSelect,
"MediaStop" => KeyCode::MediaStop,
"MediaTrackNext" => KeyCode::MediaTrackNext,
"MediaTrackPrevious" => KeyCode::MediaTrackPrevious,
"Power" => KeyCode::Power,
"Sleep" => KeyCode::Sleep,
"AudioVolumeDown" => KeyCode::AudioVolumeDown,
"AudioVolumeMute" => KeyCode::AudioVolumeMute,
"AudioVolumeUp" => KeyCode::AudioVolumeUp,
"WakeUp" => KeyCode::WakeUp,
"Hyper" => KeyCode::Hyper,
"Turbo" => KeyCode::Turbo,
"Abort" => KeyCode::Abort,
"Resume" => KeyCode::Resume,
"Suspend" => KeyCode::Suspend,
"Again" => KeyCode::Again,
"Copy" => KeyCode::Copy,
"Cut" => KeyCode::Cut,
"Find" => KeyCode::Find,
"Open" => KeyCode::Open,
"Paste" => KeyCode::Paste,
"Props" => KeyCode::Props,
"Select" => KeyCode::Select,
"Undo" => KeyCode::Undo,
"Hiragana" => KeyCode::Hiragana,
"Katakana" => KeyCode::Katakana,
"F1" => KeyCode::F1,
"F2" => KeyCode::F2,
"F3" => KeyCode::F3,
"F4" => KeyCode::F4,
"F5" => KeyCode::F5,
"F6" => KeyCode::F6,
"F7" => KeyCode::F7,
"F8" => KeyCode::F8,
"F9" => KeyCode::F9,
"F10" => KeyCode::F10,
"F11" => KeyCode::F11,
"F12" => KeyCode::F12,
"F13" => KeyCode::F13,
"F14" => KeyCode::F14,
"F15" => KeyCode::F15,
"F16" => KeyCode::F16,
"F17" => KeyCode::F17,
"F18" => KeyCode::F18,
"F19" => KeyCode::F19,
"F20" => KeyCode::F20,
"F21" => KeyCode::F21,
"F22" => KeyCode::F22,
"F23" => KeyCode::F23,
"F24" => KeyCode::F24,
"F25" => KeyCode::F25,
"F26" => KeyCode::F26,
"F27" => KeyCode::F27,
"F28" => KeyCode::F28,
"F29" => KeyCode::F29,
"F30" => KeyCode::F30,
"F31" => KeyCode::F31,
"F32" => KeyCode::F32,
"F33" => KeyCode::F33,
"F34" => KeyCode::F34,
"F35" => KeyCode::F35,
_ => KeyCode::Unidentified(NativeKeyCode::Unidentified),
}
}
}

View File

@@ -1,157 +0,0 @@
use std::cell::Cell;
use std::rc::Rc;
use js_sys::Promise;
use once_cell::unsync::OnceCell;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{Document, Element, HtmlCanvasElement};
use super::EventListenerHandle;
thread_local! {
static FULLSCREEN_API_SUPPORT: OnceCell<bool> = OnceCell::new();
}
pub struct FullscreenHandler {
document: Document,
canvas: HtmlCanvasElement,
fullscreen_requested: Rc<Cell<bool>>,
_fullscreen_change: EventListenerHandle<dyn FnMut()>,
}
impl FullscreenHandler {
pub fn new(document: Document, canvas: HtmlCanvasElement) -> Self {
let fullscreen_requested = Rc::new(Cell::new(false));
let fullscreen_change = EventListenerHandle::new(
canvas.clone(),
if has_fullscreen_api_support(&canvas) {
"fullscreenchange"
} else {
"webkitfullscreenchange"
},
Closure::new({
let fullscreen_requested = fullscreen_requested.clone();
move || {
// It doesn't matter if the canvas entered or exitted fullscreen mode,
// we don't want to request it again later.
fullscreen_requested.set(false);
}
}),
);
Self {
document,
canvas,
fullscreen_requested,
_fullscreen_change: fullscreen_change,
}
}
fn internal_request_fullscreen(&self) {
#[wasm_bindgen]
extern "C" {
type RequestFullscreen;
#[wasm_bindgen(method, js_name = requestFullscreen)]
fn request_fullscreen(this: &RequestFullscreen) -> Promise;
#[wasm_bindgen(method, js_name = webkitRequestFullscreen)]
fn webkit_request_fullscreen(this: &RequestFullscreen);
}
let canvas: &RequestFullscreen = self.canvas.unchecked_ref();
if has_fullscreen_api_support(&self.canvas) {
thread_local! {
static REJECT_HANDLER: Closure<dyn FnMut(JsValue)> = Closure::new(|_| ());
}
REJECT_HANDLER.with(|handler| {
let _ = canvas.request_fullscreen().catch(handler);
});
} else {
canvas.webkit_request_fullscreen();
}
}
pub fn request_fullscreen(&self) {
if !self.is_fullscreen() {
self.internal_request_fullscreen();
self.fullscreen_requested.set(true);
}
}
pub fn transient_activation(&self) {
if self.fullscreen_requested.get() {
self.internal_request_fullscreen()
}
}
pub fn is_fullscreen(&self) -> bool {
#[wasm_bindgen]
extern "C" {
type FullscreenElement;
#[wasm_bindgen(method, getter, js_name = webkitFullscreenElement)]
fn webkit_fullscreen_element(this: &FullscreenElement) -> Option<Element>;
}
let element = if has_fullscreen_api_support(&self.canvas) {
#[allow(clippy::disallowed_methods)]
self.document.fullscreen_element()
} else {
let document: &FullscreenElement = self.document.unchecked_ref();
document.webkit_fullscreen_element()
};
match element {
Some(element) => {
let canvas: &Element = &self.canvas;
canvas == &element
}
None => false,
}
}
pub fn exit_fullscreen(&self) {
#[wasm_bindgen]
extern "C" {
type ExitFullscreen;
#[wasm_bindgen(method, js_name = webkitExitFullscreen)]
fn webkit_exit_fullscreen(this: &ExitFullscreen);
}
if has_fullscreen_api_support(&self.canvas) {
#[allow(clippy::disallowed_methods)]
self.document.exit_fullscreen()
} else {
let document: &ExitFullscreen = self.document.unchecked_ref();
document.webkit_exit_fullscreen()
}
self.fullscreen_requested.set(false);
}
pub fn cancel(&self) {
self.fullscreen_requested.set(false);
}
}
fn has_fullscreen_api_support(canvas: &HtmlCanvasElement) -> bool {
FULLSCREEN_API_SUPPORT.with(|support| {
*support.get_or_init(|| {
#[wasm_bindgen]
extern "C" {
type CanvasFullScreenApiSupport;
#[wasm_bindgen(method, getter, js_name = requestFullscreen)]
fn has_request_fullscreen(this: &CanvasFullScreenApiSupport) -> JsValue;
}
let support: &CanvasFullScreenApiSupport = canvas.unchecked_ref();
!support.has_request_fullscreen().is_undefined()
})
})
}

View File

@@ -1,230 +0,0 @@
use std::{
mem::{self, size_of},
ptr,
};
use windows_sys::Win32::{
Devices::HumanInterfaceDevice::{
HID_USAGE_GENERIC_KEYBOARD, HID_USAGE_GENERIC_MOUSE, HID_USAGE_PAGE_GENERIC,
},
Foundation::{HANDLE, HWND},
UI::{
Input::{
GetRawInputData, GetRawInputDeviceInfoW, GetRawInputDeviceList,
RegisterRawInputDevices, HRAWINPUT, RAWINPUT, RAWINPUTDEVICE, RAWINPUTDEVICELIST,
RAWINPUTHEADER, RIDEV_DEVNOTIFY, RIDEV_INPUTSINK, RIDEV_REMOVE, RIDI_DEVICEINFO,
RIDI_DEVICENAME, RID_DEVICE_INFO, RID_DEVICE_INFO_HID, RID_DEVICE_INFO_KEYBOARD,
RID_DEVICE_INFO_MOUSE, RID_INPUT, RIM_TYPEHID, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
},
WindowsAndMessaging::{
RI_MOUSE_LEFT_BUTTON_DOWN, RI_MOUSE_LEFT_BUTTON_UP, RI_MOUSE_MIDDLE_BUTTON_DOWN,
RI_MOUSE_MIDDLE_BUTTON_UP, RI_MOUSE_RIGHT_BUTTON_DOWN, RI_MOUSE_RIGHT_BUTTON_UP,
},
},
};
use crate::{event::ElementState, event_loop::DeviceEvents, platform_impl::platform::util};
#[allow(dead_code)]
pub fn get_raw_input_device_list() -> Option<Vec<RAWINPUTDEVICELIST>> {
let list_size = size_of::<RAWINPUTDEVICELIST>() as u32;
let mut num_devices = 0;
let status = unsafe { GetRawInputDeviceList(ptr::null_mut(), &mut num_devices, list_size) };
if status == u32::MAX {
return None;
}
let mut buffer = Vec::with_capacity(num_devices as _);
let num_stored =
unsafe { GetRawInputDeviceList(buffer.as_mut_ptr(), &mut num_devices, list_size) };
if num_stored == u32::MAX {
return None;
}
debug_assert_eq!(num_devices, num_stored);
unsafe { buffer.set_len(num_devices as _) };
Some(buffer)
}
#[allow(dead_code)]
pub enum RawDeviceInfo {
Mouse(RID_DEVICE_INFO_MOUSE),
Keyboard(RID_DEVICE_INFO_KEYBOARD),
Hid(RID_DEVICE_INFO_HID),
}
impl From<RID_DEVICE_INFO> for RawDeviceInfo {
fn from(info: RID_DEVICE_INFO) -> Self {
unsafe {
match info.dwType {
RIM_TYPEMOUSE => RawDeviceInfo::Mouse(info.Anonymous.mouse),
RIM_TYPEKEYBOARD => RawDeviceInfo::Keyboard(info.Anonymous.keyboard),
RIM_TYPEHID => RawDeviceInfo::Hid(info.Anonymous.hid),
_ => unreachable!(),
}
}
}
}
#[allow(dead_code)]
pub fn get_raw_input_device_info(handle: HANDLE) -> Option<RawDeviceInfo> {
let mut info: RID_DEVICE_INFO = unsafe { mem::zeroed() };
let info_size = size_of::<RID_DEVICE_INFO>() as u32;
info.cbSize = info_size;
let mut minimum_size = 0;
let status = unsafe {
GetRawInputDeviceInfoW(
handle,
RIDI_DEVICEINFO,
&mut info as *mut _ as _,
&mut minimum_size,
)
};
if status == u32::MAX || status == 0 {
return None;
}
debug_assert_eq!(info_size, status);
Some(info.into())
}
pub fn get_raw_input_device_name(handle: HANDLE) -> Option<String> {
let mut minimum_size = 0;
let status = unsafe {
GetRawInputDeviceInfoW(handle, RIDI_DEVICENAME, ptr::null_mut(), &mut minimum_size)
};
if status != 0 {
return None;
}
let mut name: Vec<u16> = Vec::with_capacity(minimum_size as _);
let status = unsafe {
GetRawInputDeviceInfoW(
handle,
RIDI_DEVICENAME,
name.as_ptr() as _,
&mut minimum_size,
)
};
if status == u32::MAX || status == 0 {
return None;
}
debug_assert_eq!(minimum_size, status);
unsafe { name.set_len(minimum_size as _) };
util::decode_wide(&name).into_string().ok()
}
pub fn register_raw_input_devices(devices: &[RAWINPUTDEVICE]) -> bool {
let device_size = size_of::<RAWINPUTDEVICE>() as u32;
unsafe {
RegisterRawInputDevices(devices.as_ptr(), devices.len() as u32, device_size) == true.into()
}
}
pub fn register_all_mice_and_keyboards_for_raw_input(
mut window_handle: HWND,
filter: DeviceEvents,
) -> bool {
// RIDEV_DEVNOTIFY: receive hotplug events
// RIDEV_INPUTSINK: receive events even if we're not in the foreground
// RIDEV_REMOVE: don't receive device events (requires NULL hwndTarget)
let flags = match filter {
DeviceEvents::Never => {
window_handle = 0;
RIDEV_REMOVE
}
DeviceEvents::WhenFocused => RIDEV_DEVNOTIFY,
DeviceEvents::Always => RIDEV_DEVNOTIFY | RIDEV_INPUTSINK,
};
let devices: [RAWINPUTDEVICE; 2] = [
RAWINPUTDEVICE {
usUsagePage: HID_USAGE_PAGE_GENERIC,
usUsage: HID_USAGE_GENERIC_MOUSE,
dwFlags: flags,
hwndTarget: window_handle,
},
RAWINPUTDEVICE {
usUsagePage: HID_USAGE_PAGE_GENERIC,
usUsage: HID_USAGE_GENERIC_KEYBOARD,
dwFlags: flags,
hwndTarget: window_handle,
},
];
register_raw_input_devices(&devices)
}
pub fn get_raw_input_data(handle: HRAWINPUT) -> Option<RAWINPUT> {
let mut data: RAWINPUT = unsafe { mem::zeroed() };
let mut data_size = size_of::<RAWINPUT>() as u32;
let header_size = size_of::<RAWINPUTHEADER>() as u32;
let status = unsafe {
GetRawInputData(
handle,
RID_INPUT,
&mut data as *mut _ as _,
&mut data_size,
header_size,
)
};
if status == u32::MAX || status == 0 {
return None;
}
Some(data)
}
fn button_flags_to_element_state(
button_flags: u32,
down_flag: u32,
up_flag: u32,
) -> Option<ElementState> {
// We assume the same button won't be simultaneously pressed and released.
if util::has_flag(button_flags, down_flag) {
Some(ElementState::Pressed)
} else if util::has_flag(button_flags, up_flag) {
Some(ElementState::Released)
} else {
None
}
}
pub fn get_raw_mouse_button_state(button_flags: u32) -> [Option<ElementState>; 3] {
[
button_flags_to_element_state(
button_flags,
RI_MOUSE_LEFT_BUTTON_DOWN,
RI_MOUSE_LEFT_BUTTON_UP,
),
button_flags_to_element_state(
button_flags,
RI_MOUSE_MIDDLE_BUTTON_DOWN,
RI_MOUSE_MIDDLE_BUTTON_UP,
),
button_flags_to_element_state(
button_flags,
RI_MOUSE_RIGHT_BUTTON_DOWN,
RI_MOUSE_RIGHT_BUTTON_UP,
),
]
}

View File

@@ -16,6 +16,11 @@ fn window_send() {
needs_send::<winit::window::Window>();
}
#[test]
fn window_builder_send() {
needs_send::<winit::window::WindowBuilder>();
}
#[test]
fn ids_send() {
// ensures that the various `..Id` types implement `Send`
@@ -23,3 +28,9 @@ fn ids_send() {
needs_send::<winit::event::DeviceId>();
needs_send::<winit::monitor::MonitorHandle>();
}
#[test]
fn custom_cursor_send() {
needs_send::<winit::window::CustomCursorBuilder>();
needs_send::<winit::window::CustomCursor>();
}

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use winit::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase},
keyboard::{Key, KeyCode, KeyLocation, ModifiersState},
keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey, PhysicalKey},
window::CursorIcon,
};
@@ -23,7 +23,9 @@ fn events_serde() {
needs_serde::<MouseButton>();
needs_serde::<MouseScrollDelta>();
needs_serde::<Key>();
needs_serde::<NamedKey>();
needs_serde::<KeyCode>();
needs_serde::<PhysicalKey>();
needs_serde::<KeyLocation>();
needs_serde::<ModifiersState>();
}

View File

@@ -6,3 +6,14 @@ fn window_sync() {
// ensures that `winit::Window` implements `Sync`
needs_sync::<winit::window::Window>();
}
#[test]
fn window_builder_sync() {
needs_sync::<winit::window::WindowBuilder>();
}
#[test]
fn custom_cursor_sync() {
needs_sync::<winit::window::CustomCursorBuilder>();
needs_sync::<winit::window::CustomCursor>();
}

32
winit-core/Cargo.toml Normal file
View File

@@ -0,0 +1,32 @@
[package]
name = "winit-core"
version = "0.1.0"
authors = ["The winit contributors"]
description = "Base types for windowing libraries"
edition = "2021"
keywords = ["windowing"]
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/rust-windowing/winit"
documentation = "https://docs.rs/winit"
categories = ["gui"]
rust-version = "1.70.0"
[features]
default = ["std"]
std = ["alloc"]
alloc = []
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"]
[dependencies]
bitflags.workspace = true
cursor-icon.workspace = true
mint = { version = "0.5.6", optional = true }
serde = { workspace = true, optional = true }
smol_str.workspace = true
[target.'cfg(target_family = "wasm")'.dependencies]
web-time.workspace = true
[build-dependencies]
cfg_aliases.workspace = true

3
winit-core/README.md Normal file
View File

@@ -0,0 +1,3 @@
# winit-core - Base types for a windowing system
TODO

View File

@@ -8,7 +8,7 @@ fn main() {
cfg_aliases! {
// Systems.
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" },
ios_platform: { target_os = "ios" },
windows_platform: { target_os = "windows" },
@@ -17,8 +17,8 @@ fn main() {
redox: { target_os = "redox" },
// Native displays.
x11_platform: { all(feature = "x11", free_unix, not(wasm), not(redox)) },
wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
x11_platform: { all(feature = "x11", free_unix, not(redox)) },
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
orbital_platform: { redox },
}
}

View File

@@ -4,13 +4,13 @@
//!
//! Modern computer screens don't have a consistent relationship between resolution and size.
//! 1920x1080 is a common resolution for both desktop and mobile screens, despite mobile screens
//! normally being less than a quarter the size of their desktop counterparts. What's more, neither
//! desktop nor mobile screens are consistent resolutions within their own size classes - common
//! typically being less than a quarter the size of their desktop counterparts. Moreover, neither
//! desktop nor mobile screens have consistent resolutions within their own size classes - common
//! mobile screens range from below 720p to above 1440p, and desktop screens range from 720p to 5K
//! and beyond.
//!
//! Given that, it's a mistake to assume that 2D content will only be displayed on screens with
//! a consistent pixel density. If you were to render a 96-pixel-square image on a 1080p screen,
//! a consistent pixel density. If you were to render a 96-pixel-square image on a 1080p screen and
//! then render the same image on a similarly-sized 4K screen, the 4K rendition would only take up
//! about a quarter of the physical space as it did on the 1080p screen. That issue is especially
//! problematic with text rendering, where quarter-sized text becomes a significant legibility
@@ -25,12 +25,12 @@
//!
//! The solution to this problem is to account for the device's *scale factor*. The scale factor is
//! the factor UI elements should be scaled by to be consistent with the rest of the user's system -
//! for example, a button that's normally 50 pixels across would be 100 pixels across on a device
//! for example, a button that's usually 50 pixels across would be 100 pixels across on a device
//! with a scale factor of `2.0`, or 75 pixels across with a scale factor of `1.5`.
//!
//! Many UI systems, such as CSS, expose DPI-dependent units like [points] or [picas]. That's
//! usually a mistake, since there's no consistent mapping between the scale factor and the screen's
//! actual DPI. Unless you're printing to a physical medium, you should work in scaled pixels rather
//! usually a mistake since there's no consistent mapping between the scale factor and the screen's
//! actual DPI. Unless printing to a physical medium, you should work in scaled pixels rather
//! than any DPI-dependent units.
//!
//! ### Position and Size types
@@ -42,11 +42,11 @@
//! coordinates as input, allowing you to use the most convenient coordinate system for your
//! particular application.
//!
//! Winit's position and size types types are generic over their exact pixel type, `P`, to allow the
//! Winit's position and size types are generic over their exact pixel type, `P`, to allow the
//! API to have integer precision where appropriate (e.g. most window manipulation functions) and
//! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch
//! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so
//! will truncate the fractional part of the float, rather than properly round to the nearest
//! will truncate the fractional part of the float rather than properly round to the nearest
//! integer. Use the provided `cast` function or [`From`]/[`Into`] conversions, which handle the
//! rounding properly. Note that precision loss will still occur when rounding from a float to an
//! int, although rounding lessens the problem.
@@ -55,34 +55,35 @@
//!
//! Winit will dispatch a [`ScaleFactorChanged`] event whenever a window's scale factor has changed.
//! This can happen if the user drags their window from a standard-resolution monitor to a high-DPI
//! monitor, or if the user changes their DPI settings. This gives you a chance to rescale your
//! application's UI elements and adjust how the platform changes the window's size to reflect the new
//! scale factor. If a window hasn't received a [`ScaleFactorChanged`] event, then its scale factor
//! monitor or if the user changes their DPI settings. This allows you to rescale your application's
//! UI elements and adjust how the platform changes the window's size to reflect the new scale
//! factor. If a window hasn't received a [`ScaleFactorChanged`] event, its scale factor
//! can be found by calling [`window.scale_factor()`].
//!
//! ## How is the scale factor calculated?
//!
//! Scale factor is calculated differently on different platforms:
//! The scale factor is calculated differently on different platforms:
//!
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the
//! display settings. While users are free to select any option they want, they're only given a
//! selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7, the scale factor is
//! selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7. The scale factor is
//! global and changing it requires logging out. See [this article][windows_1] for technical
//! details.
//! - **macOS:** Recent versions of macOS allow the user to change the scaling factor for certain
//! displays. When this is available, the user may pick a per-monitor scaling factor from a set
//! of pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default but
//! the specific value varies across devices.
//! - **macOS:** Recent macOS versions allow the user to change the scaling factor for specific
//! displays. When available, the user may pick a per-monitor scaling factor from a set of
//! pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default,
//! but the specific value varies across devices.
//! - **X11:** Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit
//! currently uses a three-pronged approach:
//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable, if present.
//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable if present.
//! + If not present, use the value set in `Xft.dpi` in Xresources.
//! + Otherwise, calculate the scale factor based on the millimeter monitor dimensions provided by XRandR.
//!
//! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the
//! XRandR scaling method. Generally speaking, you should try to configure the standard system
//! variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`.
//! - **Wayland:** Scale factor is suggested by the the compositor.
//! - **Wayland:** The scale factor is suggested by the compositor for each window individually. The
//! monitor scale factor may differ from the window scale factor.
//! - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range
//! from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more
//! information.
@@ -96,13 +97,16 @@
//! [points]: https://en.wikipedia.org/wiki/Point_(typography)
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
//! [`ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged
//! [`window.scale_factor()`]: crate::window::Window::scale_factor
//! [`window.scale_factor()`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.scale_factor
//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html
//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/
//! [android_1]: https://developer.android.com/training/multiscreen/screendensities
//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub trait Pixel: Copy + Into<f64> {
fn from_f64(f: f64) -> Self;
fn cast<P: Pixel>(self) -> P {

54
winit-core/src/error.rs Normal file
View File

@@ -0,0 +1,54 @@
//! Common error types.
use std::{error, fmt};
/// The error type for when the requested operation is not supported by the backend.
#[derive(Clone)]
pub struct NotSupportedError {
_marker: (),
}
impl Default for NotSupportedError {
fn default() -> Self {
Self::new()
}
}
impl NotSupportedError {
/// Create a new [`NotSupportedError`].
#[inline]
pub fn new() -> NotSupportedError {
NotSupportedError { _marker: () }
}
}
impl fmt::Debug for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("NotSupportedError").finish()
}
}
impl fmt::Display for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.pad("the requested operation is not supported by Winit")
}
}
impl error::Error for NotSupportedError {}
#[cfg(test)]
mod tests {
#![allow(clippy::redundant_clone)]
use super::*;
// Eat attributes for testing
#[test]
fn ensure_fmt_does_not_panic() {
let _ = format!(
"{:?}, {}",
NotSupportedError::new(),
NotSupportedError::new().clone()
);
}
}

View File

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

View File

@@ -0,0 +1,80 @@
//! Types needed to define the event loop.
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(not(web_platform))]
use std::time::{Duration, Instant};
#[cfg(web_platform)]
use web_time::{Duration, Instant};
/// A unique identifier of the winit's async request.
///
/// This could be used to identify the async request once it's done
/// and a specific action must be taken.
///
/// One of the handling scenarious could be to maintain a working list
/// containing [`AsyncRequestSerial`] and some closure associated with it.
/// Then once event is arriving the working list is being traversed and a job
/// executed and removed from the list.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AsyncRequestSerial {
serial: u64,
}
impl AsyncRequestSerial {
/// Get the next serial in the sequence.
pub fn get() -> Self {
static CURRENT_SERIAL: AtomicU64 = AtomicU64::new(0);
// NOTE: we rely on wrap around here, while the user may just request
// in the loop u64::MAX times that's issue is considered on them.
let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
Self { serial }
}
}
/// Set through [`EventLoopWindowTarget::set_control_flow()`].
///
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
///
/// Defaults to [`Wait`].
///
/// [`Wait`]: Self::Wait
///
/// [`EventLoopWindowTarget::set_control_flow()`]: https://docs.rs/winit/latest/winit/event_loop/struct.EventLoopWindowTarget.html#method.set_control_flow
/// [`Event::AboutToWait`]: crate::event::Event::AboutToWait
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum ControlFlow {
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process.
Poll,
/// When the current loop iteration finishes, suspend the thread until another event arrives.
#[default]
Wait,
/// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the given time is reached.
///
/// Useful for implementing efficient timers. Applications which want to render at the display's
/// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
/// to reduce odds of missed frames.
///
/// [`Poll`]: Self::Poll
WaitUntil(Instant),
}
impl ControlFlow {
/// Creates a [`ControlFlow`] that waits until a timeout has expired.
///
/// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
/// instead set to [`Wait`].
///
/// [`WaitUntil`]: Self::WaitUntil
/// [`Wait`]: Self::Wait
pub fn wait_duration(timeout: Duration) -> Self {
match Instant::now().checked_add(timeout) {
Some(instant) => Self::WaitUntil(instant),
None => Self::Wait,
}
}
}

View File

@@ -69,6 +69,9 @@
//
// --------- END OF W3C SHORT NOTICE ---------------------------------------------------------------
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub use smol_str::SmolStr;
/// Contains the platform-native physical key identifier
@@ -185,26 +188,112 @@ impl std::fmt::Debug for NativeKey {
}
}
impl From<NativeKeyCode> for NativeKey {
#[inline]
fn from(code: NativeKeyCode) -> Self {
match code {
NativeKeyCode::Unidentified => NativeKey::Unidentified,
NativeKeyCode::Android(x) => NativeKey::Android(x),
NativeKeyCode::MacOS(x) => NativeKey::MacOS(x),
NativeKeyCode::Windows(x) => NativeKey::Windows(x),
NativeKeyCode::Xkb(x) => NativeKey::Xkb(x),
}
}
}
impl PartialEq<NativeKey> for NativeKeyCode {
#[allow(clippy::cmp_owned)] // uses less code than direct match; target is stack allocated
#[inline]
fn eq(&self, rhs: &NativeKey) -> bool {
NativeKey::from(*self) == *rhs
}
}
impl PartialEq<NativeKeyCode> for NativeKey {
#[inline]
fn eq(&self, rhs: &NativeKeyCode) -> bool {
rhs == self
}
}
/// Represents the location of a physical key.
///
/// This type is a superset of [`KeyCode`], including an [`Unidentified`](Self::Unidentified)
/// variant.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PhysicalKey {
/// A known key code
Code(KeyCode),
/// This variant is used when the key cannot be translated to a [`KeyCode`]
///
/// The native keycode is provided (if available) so you're able to more reliably match
/// key-press and key-release events by hashing the [`PhysicalKey`]. It is also possible to use
/// this for keybinds for non-standard keys, but such keybinds are tied to a given platform.
Unidentified(NativeKeyCode),
}
impl From<KeyCode> for PhysicalKey {
#[inline]
fn from(code: KeyCode) -> Self {
PhysicalKey::Code(code)
}
}
impl From<NativeKeyCode> for PhysicalKey {
#[inline]
fn from(code: NativeKeyCode) -> Self {
PhysicalKey::Unidentified(code)
}
}
impl PartialEq<KeyCode> for PhysicalKey {
#[inline]
fn eq(&self, rhs: &KeyCode) -> bool {
match self {
PhysicalKey::Code(ref code) => code == rhs,
_ => false,
}
}
}
impl PartialEq<PhysicalKey> for KeyCode {
#[inline]
fn eq(&self, rhs: &PhysicalKey) -> bool {
rhs == self
}
}
impl PartialEq<NativeKeyCode> for PhysicalKey {
#[inline]
fn eq(&self, rhs: &NativeKeyCode) -> bool {
match self {
PhysicalKey::Unidentified(ref code) => code == rhs,
_ => false,
}
}
}
impl PartialEq<PhysicalKey> for NativeKeyCode {
#[inline]
fn eq(&self, rhs: &PhysicalKey) -> bool {
rhs == self
}
}
/// Code representing the location of a physical key
///
/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.code`] with a few
/// exceptions:
/// - The keys that the specification calls "MetaLeft" and "MetaRight" are named "SuperLeft" and
/// "SuperRight" here.
/// - The key that the specification calls "Super" is reported as `Unidentified` here.
/// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`.
///
/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum KeyCode {
/// This variant is used when the key cannot be translated to any other variant.
///
/// The native keycode is provided (if available) so you're able to more reliably match
/// key-press and key-release events by hashing the [`KeyCode`]. It is also possible to use
/// this for keybinds for non-standard keys, but such keybinds are tied to a given platform.
Unidentified(NativeKeyCode),
/// <kbd>`</kbd> on a US keyboard. This is also called a backtick or grave.
/// This is the <kbd>半角</kbd>/<kbd>全角</kbd>/<kbd>漢字</kbd>
/// (hankaku/zenkaku/kanji) key on Japanese keyboards
@@ -648,7 +737,7 @@ pub enum KeyCode {
F35,
}
/// Key represents the meaning of a keypress.
/// A [`Key::Named`] value
///
/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few
/// exceptions:
@@ -656,32 +745,12 @@ pub enum KeyCode {
/// another key which the specification calls `Super`. That does not exist here.)
/// - The `Space` variant here, can be identified by the character it generates in the
/// specificaiton.
/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`.
/// - The `Dead` variant here, can specify the character which is inserted when pressing the
/// dead-key twice.
///
/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Key<Str = SmolStr> {
/// A key string that corresponds to the character typed by the user, taking into account the
/// users current locale setting, and any system-level keyboard mapping overrides that are in
/// effect.
Character(Str),
/// This variant is used when the key cannot be translated to any other variant.
///
/// The native key is provided (if available) in order to allow the user to specify keybindings
/// for keys which are not defined by this API, mainly through some sort of UI.
Unidentified(NativeKey),
/// Contains the text representation of the dead-key when available.
///
/// ## Platform-specific
/// - **Web:** Always contains `None`
Dead(Option<char>),
pub enum NamedKey {
/// The `Alt` (Alternative) key.
///
/// This key enables the alternate modifier function for interpreting concurrent or subsequent
@@ -1385,83 +1454,154 @@ pub enum Key<Str = SmolStr> {
F35,
}
macro_rules! map_match {
(
$to_match:expr,
// Custom match arms
{ $( $from:pat => $to:expr ),* },
// The enum's name
$prefix:path,
// Trivial match arms for unit variants
{ $( $t:tt ),* }) => {
match $to_match {
$( $from => $to, )*
$( Key::$t => Key::$t, )*
// NOTE: the exact modifier key is not used to represent modifiers state in the
// first place due to a fact that modifiers state could be changed without any
// key being pressed and on some platforms like Wayland/X11 which key resulted
// in modifiers change is hidden, also, not that it really matters.
//
// The reason this API is even exposed is mostly to provide a way for users
// to treat modifiers differently based on their position, which is required
// on macOS due to their AltGr/Option situation.
bitflags! {
#[doc(hidden)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModifiersKeys: u8 {
const LSHIFT = 0b0000_0001;
const RSHIFT = 0b0000_0010;
const LCONTROL = 0b0000_0100;
const RCONTROL = 0b0000_1000;
const LALT = 0b0001_0000;
const RALT = 0b0010_0000;
const LSUPER = 0b0100_0000;
const RSUPER = 0b1000_0000;
}
}
/// Key represents the meaning of a keypress.
///
/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with
/// additions:
/// - All simple variants are wrapped under the `Named` variant
/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`.
/// - The `Dead` variant here, can specify the character which is inserted when pressing the
/// dead-key twice.
///
/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Key<Str = SmolStr> {
/// A simple (unparameterised) action
Named(NamedKey),
/// A key string that corresponds to the character typed by the user, taking into account the
/// users current locale setting, and any system-level keyboard mapping overrides that are in
/// effect.
Character(Str),
/// This variant is used when the key cannot be translated to any other variant.
///
/// The native key is provided (if available) in order to allow the user to specify keybindings
/// for keys which are not defined by this API, mainly through some sort of UI.
Unidentified(NativeKey),
/// Contains the text representation of the dead-key when available.
///
/// ## Platform-specific
/// - **Web:** Always contains `None`
Dead(Option<char>),
}
impl From<NamedKey> for Key {
#[inline]
fn from(action: NamedKey) -> Self {
Key::Named(action)
}
}
impl From<NativeKey> for Key {
#[inline]
fn from(code: NativeKey) -> Self {
Key::Unidentified(code)
}
}
impl<Str> PartialEq<NamedKey> for Key<Str> {
#[inline]
fn eq(&self, rhs: &NamedKey) -> bool {
match self {
Key::Named(ref a) => a == rhs,
_ => false,
}
};
}
}
impl<Str: PartialEq<str>> PartialEq<str> for Key<Str> {
#[inline]
fn eq(&self, rhs: &str) -> bool {
match self {
Key::Character(ref s) => s == rhs,
_ => false,
}
}
}
impl<Str: PartialEq<str>> PartialEq<&str> for Key<Str> {
#[inline]
fn eq(&self, rhs: &&str) -> bool {
self == *rhs
}
}
impl<Str> PartialEq<NativeKey> for Key<Str> {
#[inline]
fn eq(&self, rhs: &NativeKey) -> bool {
match self {
Key::Unidentified(ref code) => code == rhs,
_ => false,
}
}
}
impl<Str> PartialEq<Key<Str>> for NativeKey {
#[inline]
fn eq(&self, rhs: &Key<Str>) -> bool {
rhs == self
}
}
impl Key<SmolStr> {
/// Convert `Key::Character(SmolStr)` to `Key::Character(&str)` so you can more easily match on
/// `Key`. All other variants remain unchanged.
pub fn as_ref(&self) -> Key<&str> {
map_match!(
self,
{
Key::Character(ch) => Key::Character(ch.as_str()),
Key::Dead(d) => Key::Dead(*d),
Key::Unidentified(u) => Key::Unidentified(u.clone())
},
Key,
{
Alt, AltGraph, CapsLock, Control, Fn, FnLock, NumLock, ScrollLock, Shift, Symbol,
SymbolLock, Meta, Hyper, Super, Enter, Tab, Space, ArrowDown, ArrowLeft,
ArrowRight, ArrowUp, End, Home, PageDown, PageUp, Backspace, Clear, Copy, CrSel,
Cut, Delete, EraseEof, ExSel, Insert, Paste, Redo, Undo, Accept, Again, Attn,
Cancel, ContextMenu, Escape, Execute, Find, Help, Pause, Play, Props, Select,
ZoomIn, ZoomOut, BrightnessDown, BrightnessUp, Eject, LogOff, Power, PowerOff,
PrintScreen, Hibernate, Standby, WakeUp, AllCandidates, Alphanumeric, CodeInput,
Compose, Convert, FinalMode, GroupFirst, GroupLast, GroupNext, GroupPrevious,
ModeChange, NextCandidate, NonConvert, PreviousCandidate, Process, SingleCandidate,
HangulMode, HanjaMode, JunjaMode, Eisu, Hankaku, Hiragana, HiraganaKatakana,
KanaMode, KanjiMode, Katakana, Romaji, Zenkaku, ZenkakuHankaku, Soft1, Soft2,
Soft3, Soft4, ChannelDown, ChannelUp, Close, MailForward, MailReply, MailSend,
MediaClose, MediaFastForward, MediaPause, MediaPlay, MediaPlayPause, MediaRecord,
MediaRewind, MediaStop, MediaTrackNext, MediaTrackPrevious, New, Open, Print, Save,
SpellCheck, Key11, Key12, AudioBalanceLeft, AudioBalanceRight, AudioBassBoostDown,
AudioBassBoostToggle, AudioBassBoostUp, AudioFaderFront, AudioFaderRear,
AudioSurroundModeNext, AudioTrebleDown, AudioTrebleUp, AudioVolumeDown,
AudioVolumeUp, AudioVolumeMute, MicrophoneToggle, MicrophoneVolumeDown,
MicrophoneVolumeUp, MicrophoneVolumeMute, SpeechCorrectionList, SpeechInputToggle,
LaunchApplication1, LaunchApplication2, LaunchCalendar, LaunchContacts, LaunchMail,
LaunchMediaPlayer, LaunchMusicPlayer, LaunchPhone, LaunchScreenSaver,
LaunchSpreadsheet, LaunchWebBrowser, LaunchWebCam, LaunchWordProcessor,
BrowserBack, BrowserFavorites, BrowserForward, BrowserHome, BrowserRefresh,
BrowserSearch, BrowserStop, AppSwitch, Call, Camera, CameraFocus, EndCall, GoBack,
GoHome, HeadsetHook, LastNumberRedial, Notification, MannerMode, VoiceDial, TV,
TV3DMode, TVAntennaCable, TVAudioDescription, TVAudioDescriptionMixDown,
TVAudioDescriptionMixUp, TVContentsMenu, TVDataService, TVInput, TVInputComponent1,
TVInputComponent2, TVInputComposite1, TVInputComposite2, TVInputHDMI1,
TVInputHDMI2, TVInputHDMI3, TVInputHDMI4, TVInputVGA1, TVMediaContext, TVNetwork,
TVNumberEntry, TVPower, TVRadioService, TVSatellite, TVSatelliteBS, TVSatelliteCS,
TVSatelliteToggle, TVTerrestrialAnalog, TVTerrestrialDigital, TVTimer, AVRInput,
AVRPower, ColorF0Red, ColorF1Green, ColorF2Yellow, ColorF3Blue, ColorF4Grey,
ColorF5Brown, ClosedCaptionToggle, Dimmer, DisplaySwap, DVR, Exit, FavoriteClear0,
FavoriteClear1, FavoriteClear2, FavoriteClear3, FavoriteRecall0, FavoriteRecall1,
FavoriteRecall2, FavoriteRecall3, FavoriteStore0, FavoriteStore1, FavoriteStore2,
FavoriteStore3, Guide, GuideNextDay, GuidePreviousDay, Info, InstantReplay, Link,
ListProgram, LiveContent, Lock, MediaApps, MediaAudioTrack, MediaLast,
MediaSkipBackward, MediaSkipForward, MediaStepBackward, MediaStepForward,
MediaTopMenu, NavigateIn, NavigateNext, NavigateOut, NavigatePrevious,
NextFavoriteChannel, NextUserProfile, OnDemand, Pairing, PinPDown, PinPMove,
PinPToggle, PinPUp, PlaySpeedDown, PlaySpeedReset, PlaySpeedUp, RandomToggle,
RcLowBattery, RecordSpeedNext, RfBypass, ScanChannelsToggle, ScreenModeNext,
Settings, SplitScreenToggle, STBInput, STBPower, Subtitle, Teletext, VideoModeNext,
Wink, ZoomToggle, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,
F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31,
F32, F33, F34, F35
}
)
match self {
Key::Named(a) => Key::Named(*a),
Key::Character(ch) => Key::Character(ch.as_str()),
Key::Dead(d) => Key::Dead(*d),
Key::Unidentified(u) => Key::Unidentified(u.clone()),
}
}
}
impl NamedKey {
/// Convert an action to its approximate textual equivalent.
///
/// # Examples
///
/// ```
/// use winit_core::keyboard::NamedKey;
///
/// assert_eq!(NamedKey::Enter.to_text(), Some("\r"));
/// assert_eq!(NamedKey::F20.to_text(), None);
/// ```
pub fn to_text(&self) -> Option<&str> {
match self {
NamedKey::Enter => Some("\r"),
NamedKey::Backspace => Some("\x08"),
NamedKey::Tab => Some("\t"),
NamedKey::Space => Some(" "),
NamedKey::Escape => Some("\x1b"),
_ => None,
}
}
}
@@ -1471,20 +1611,16 @@ impl Key {
/// # Examples
///
/// ```
/// use winit::keyboard::Key;
/// use winit_core::keyboard::{NamedKey, Key};
///
/// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
/// assert_eq!(Key::Enter.to_text(), Some("\r"));
/// assert_eq!(Key::F20.to_text(), None);
/// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
/// assert_eq!(Key::Named(NamedKey::F20).to_text(), None);
/// ```
pub fn to_text(&self) -> Option<&str> {
match self {
Key::Named(action) => action.to_text(),
Key::Character(ch) => Some(ch.as_str()),
Key::Enter => Some("\r"),
Key::Backspace => Some("\x08"),
Key::Tab => Some("\t"),
Key::Space => Some(" "),
Key::Escape => Some("\x1b"),
_ => None,
}
}
@@ -1572,7 +1708,7 @@ bitflags! {
/// Represents the current state of the keyboard modifiers
///
/// Each flag represents a modifier and is set if this modifier is active.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ModifiersState: u32 {
/// The "shift" key.
const SHIFT = 0b100;
@@ -1614,28 +1750,6 @@ pub enum ModifiersKeyState {
Unknown,
}
// NOTE: the exact modifier key is not used to represent modifiers state in the
// first place due to a fact that modifiers state could be changed without any
// key being pressed and on some platforms like Wayland/X11 which key resulted
// in modifiers change is hidden, also, not that it really matters.
//
// The reason this API is even exposed is mostly to provide a way for users
// to treat modifiers differently based on their position, which is required
// on macOS due to their AltGr/Option situation.
bitflags! {
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct ModifiersKeys: u8 {
const LSHIFT = 0b0000_0001;
const RSHIFT = 0b0000_0010;
const LCONTROL = 0b0000_0100;
const RCONTROL = 0b0000_1000;
const LALT = 0b0001_0000;
const RALT = 0b0010_0000;
const LSUPER = 0b0100_0000;
const RSUPER = 0b1000_0000;
}
}
#[cfg(feature = "serde")]
mod modifiers_serde {
use super::ModifiersState;

18
winit-core/src/lib.rs Normal file
View File

@@ -0,0 +1,18 @@
//! Base types for a windowing library.
//!
//! This crate contains types, traits and basic functions from [`winit`] that are platform
//! independent. It is intended to allow for other crates to build abstractions around [`winit`]
//! without needing to pull in all of [`winit`]'s dependencies, as well as to provide an
//! interface for alternative backends for [`winit`] to be constructed.
//!
//! [`winit`]: https://docs.rs/winit
#[cfg(any(not(feature = "std"), not(feature = "alloc")))]
compile_error! { "no-std and no-alloc usage are not yet supported" }
pub mod dpi;
pub mod error;
pub mod event;
pub mod event_loop;
pub mod keyboard;
pub mod window;

229
winit-core/src/window.rs Normal file
View File

@@ -0,0 +1,229 @@
//! Types used in window construction.
#[doc(inline)]
pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError};
#[cfg(feature = "serde")]
pub use serde::{Deserialize, Serialize};
/// Identifier of a window. Unique for each window.
///
/// Can be obtained with [`window.id()`](https://docs.rs/winit/latest/winit/window/struct.Window.html#method.id).
///
/// Whenever you receive an event specific to a window, this event contains a `WindowId` which you
/// can then compare to the ids of your windows.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(u64);
impl WindowId {
/// Returns a dummy id, useful for unit testing.
///
/// # Safety
///
/// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real [`WindowId`].
///
/// **Passing this into a winit function will result in undefined behavior.**
pub const unsafe fn dummy() -> Self {
WindowId(0)
}
}
impl From<WindowId> for u64 {
fn from(window_id: WindowId) -> Self {
window_id.0
}
}
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self(raw_id)
}
}
/// The behavior of cursor grabbing.
///
/// Use this enum with [`Window::set_cursor_grab`] to grab the cursor.
///
/// [`Window::set_cursor_grab`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_cursor_grab
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CursorGrabMode {
/// No grabbing of the cursor is performed.
None,
/// The cursor is confined to the window area.
///
/// There's no guarantee that the cursor will be hidden. You should hide it by yourself if you
/// want to do so.
///
/// ## Platform-specific
///
/// - **macOS:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
/// - **iOS / Android / Web / Orbital:** Always returns an [`ExternalError::NotSupported`].
///
/// [`ExternalError::NotSupported`]: https://docs.rs/winit/latest/winit/error/enum.ExternalError.html#variant.NotSupported
Confined,
/// The cursor is locked inside the window area to the certain position.
///
/// There's no guarantee that the cursor will be hidden. You should hide it by yourself if you
/// want to do so.
///
/// ## Platform-specific
///
/// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
/// - **iOS / Android / Orbital:** Always returns an [`ExternalError::NotSupported`].
///
/// [`ExternalError::NotSupported`]: https://docs.rs/winit/latest/winit/error/enum.ExternalError.html#variant.NotSupported
Locked,
}
/// Defines the orientation that a window resize will be performed.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ResizeDirection {
East,
North,
NorthEast,
NorthWest,
South,
SouthEast,
SouthWest,
West,
}
impl From<ResizeDirection> for CursorIcon {
fn from(direction: ResizeDirection) -> Self {
use ResizeDirection::*;
match direction {
East => CursorIcon::EResize,
North => CursorIcon::NResize,
NorthEast => CursorIcon::NeResize,
NorthWest => CursorIcon::NwResize,
South => CursorIcon::SResize,
SouthEast => CursorIcon::SeResize,
SouthWest => CursorIcon::SwResize,
West => CursorIcon::WResize,
}
}
}
/// The theme variant to use.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Theme {
/// Use the light variant.
Light,
/// Use the dark variant.
Dark,
}
/// ## Platform-specific
///
/// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between [`Critical`] and [`Informational`].
///
/// [`Critical`]: Self::Critical
/// [`Informational`]: Self::Informational
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum UserAttentionType {
/// ## Platform-specific
///
/// - **macOS:** Bounces the dock icon until the application is in focus.
/// - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
Critical,
/// ## Platform-specific
///
/// - **macOS:** Bounces the dock icon once.
/// - **Windows:** Flashes the taskbar button until the application is in focus.
#[default]
Informational,
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WindowButtons: u32 {
const CLOSE = 1 << 0;
const MINIMIZE = 1 << 1;
const MAXIMIZE = 1 << 2;
}
}
/// A window level groups windows with respect to their z-position.
///
/// The relative ordering between windows in different window levels is fixed.
/// The z-order of a window within the same window level may change dynamically on user interaction.
///
/// ## Platform-specific
///
/// - **iOS / Android / Web / Wayland:** Unsupported.
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub enum WindowLevel {
/// The window will always be below normal windows.
///
/// This is useful for a widget-based app.
AlwaysOnBottom,
/// The default.
#[default]
Normal,
/// The window will always be on top of normal windows.
AlwaysOnTop,
}
/// Generic IME purposes for use in [`Window::set_ime_purpose`].
///
/// The purpose may improve UX by optimizing the IME for the specific use case,
/// if winit can express the purpose to the platform and the platform reacts accordingly.
///
/// ## Platform-specific
///
/// - **iOS / Android / Web / Windows / X11 / macOS / Orbital:** Unsupported.
///
/// [`Window::set_ime_purpose`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_purpose
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum ImePurpose {
/// No special hints for the IME (default).
Normal,
/// The IME is used for password input.
Password,
/// The IME is used to input into a terminal.
///
/// For example, that could alter OSK on Wayland to show extra buttons.
Terminal,
}
impl Default for ImePurpose {
fn default() -> Self {
Self::Normal
}
}
/// An stringly-typed token used to activate the [`Window`].
///
/// [`Window`]: https://docs.rs/winit/latest/winit/window/struct.Window.html
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ActivationToken {
pub(crate) token: String,
}
impl ActivationToken {
/// Create a new [`ActivationToken`].
pub fn new(token: String) -> Self {
Self { token }
}
/// Get the underlying token.
pub fn token(&self) -> &str {
&self.token
}
/// Convert into the underlying token.
pub fn into_token(self) -> String {
self.token
}
}

261
winit/Cargo.toml Normal file
View File

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

1
winit/README.md Symbolic link
View File

@@ -0,0 +1 @@
../README.md

24
winit/build.rs Normal file
View File

@@ -0,0 +1,24 @@
use cfg_aliases::cfg_aliases;
fn main() {
// The script doesn't depend on our code
println!("cargo:rerun-if-changed=build.rs");
// Setup cfg aliases
cfg_aliases! {
// Systems.
android_platform: { target_os = "android" },
web_platform: { all(target_family = "wasm", target_os = "unknown") },
macos_platform: { target_os = "macos" },
ios_platform: { target_os = "ios" },
windows_platform: { target_os = "windows" },
apple: { any(target_os = "ios", target_os = "macos") },
free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) },
redox: { target_os = "redox" },
// Native displays.
x11_platform: { all(feature = "x11", free_unix, not(redox)) },
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
orbital_platform: { redox },
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,143 @@
#![allow(clippy::single_match, clippy::disallowed_methods)]
#[cfg(not(web_platform))]
use simple_logger::SimpleLogger;
use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget},
keyboard::Key,
window::{CursorIcon, CustomCursor, Window},
};
#[cfg(web_platform)]
use {
std::sync::atomic::{AtomicU64, Ordering},
std::time::Duration,
winit::platform::web::CustomCursorExtWebSys,
};
#[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 samples = img.into_flat_samples();
let (_, w, h) = samples.extents();
let (w, h) = (w as u16, h as u16);
let builder = CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap();
builder.build(window_target)
}
#[cfg(not(web_platform))]
#[path = "util/fill.rs"]
mod fill;
fn main() -> Result<(), impl std::error::Error> {
#[cfg(not(web_platform))]
SimpleLogger::new()
.with_level(log::LevelFilter::Info)
.init()
.unwrap();
#[cfg(web_platform)]
console_log::init_with_level(log::Level::Debug).unwrap();
let event_loop = EventLoop::new().unwrap();
let builder = Window::builder().with_title("A fantastic window!");
#[cfg(web_platform)]
let builder = {
use winit::platform::web::WindowBuilderExtWebSys;
builder.with_append(true)
};
let window = builder.build(&event_loop).unwrap();
let mut cursor_idx = 0;
let mut cursor_visible = true;
let custom_cursors = [
decode_cursor(include_bytes!("data/cross.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::WindowEvent { event, .. } => match event {
WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
logical_key: key,
..
},
..
} => match key.as_ref() {
Key::Character("1") => {
log::debug!("Setting cursor to {:?}", cursor_idx);
window.set_cursor(custom_cursors[cursor_idx].clone());
cursor_idx = (cursor_idx + 1) % 3;
}
Key::Character("2") => {
log::debug!("Setting cursor icon to default");
window.set_cursor(CursorIcon::default());
}
Key::Character("3") => {
cursor_visible = !cursor_visible;
log::debug!("Setting cursor visibility to {:?}", 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 => {
#[cfg(not(web_platform))]
fill::fill_window(&window);
}
WindowEvent::CloseRequested => {
#[cfg(not(web_platform))]
_elwt.exit();
}
_ => (),
},
Event::AboutToWait => {
window.request_redraw();
}
_ => {}
})
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

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_loop::EventLoop,
keyboard::Key,
window::{Window, WindowBuilder, WindowId},
window::{Window, WindowId},
};
#[path = "util/fill.rs"]
@@ -15,8 +15,8 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window_1 = WindowBuilder::new().build(&event_loop).unwrap();
let window_2 = WindowBuilder::new().build(&event_loop).unwrap();
let window_1 = Window::builder().build(&event_loop).unwrap();
let window_2 = Window::builder().build(&event_loop).unwrap();
let mut switched = false;
let mut entered_id = window_2.id();

56
winit/examples/focus.rs Normal file
View File

@@ -0,0 +1,56 @@
#![allow(clippy::single_match)]
//! Example for focusing a window.
use simple_logger::SimpleLogger;
#[cfg(not(web_platform))]
use std::time;
#[cfg(web_platform)]
use web_time as time;
use winit::{
event::{Event, StartCause, WindowEvent},
event_loop::EventLoop,
window::Window,
};
#[path = "util/fill.rs"]
mod fill;
fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap();
let window = Window::builder()
.with_title("A fantastic window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(&event_loop)
.unwrap();
let mut deadline = time::Instant::now() + time::Duration::from_secs(3);
event_loop.run(move |event, elwt| {
match event {
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
// Timeout reached; focus the window.
println!("Re-focusing the window.");
deadline += time::Duration::from_secs(3);
window.focus_window();
}
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::RedrawRequested => {
// Notify the windowing system that we'll be presenting to the window.
window.pre_present_notify();
fill::fill_window(&window);
}
_ => (),
},
Event::AboutToWait => {
window.request_redraw();
}
_ => (),
}
elwt.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(deadline));
})
}

View File

@@ -1,11 +1,11 @@
#![allow(clippy::single_match)]
use simple_logger::SimpleLogger;
use winit::dpi::PhysicalSize;
use winit::dpi::LogicalSize;
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::EventLoop;
use winit::keyboard::Key;
use winit::window::{Fullscreen, WindowBuilder};
use winit::keyboard::{Key, NamedKey};
use winit::window::{Fullscreen, Window};
#[cfg(target_os = "macos")]
use winit::platform::macos::WindowExtMacOS;
@@ -22,7 +22,7 @@ fn main() -> Result<(), impl std::error::Error> {
let mut with_min_size = false;
let mut with_max_size = false;
let window = WindowBuilder::new()
let window = Window::builder()
.with_title("Hello world!")
.build(&event_loop)
.unwrap();
@@ -65,7 +65,7 @@ fn main() -> Result<(), impl std::error::Error> {
},
..
} => match key {
Key::Escape => elwt.exit(),
Key::Named(NamedKey::Escape) => elwt.exit(),
// WARNING: Consider using `key_without_modifers()` if available on your platform.
// See the `key_binding` example
Key::Character(ch) => match ch.to_lowercase().as_str() {
@@ -126,7 +126,7 @@ fn main() -> Result<(), impl std::error::Error> {
"i" => {
with_min_size = !with_min_size;
let min_size = if with_min_size {
Some(PhysicalSize::new(100, 100))
Some(LogicalSize::new(100, 100))
} else {
None
};
@@ -139,7 +139,7 @@ fn main() -> Result<(), impl std::error::Error> {
"a" => {
with_max_size = !with_max_size;
let max_size = if with_max_size {
Some(PhysicalSize::new(200, 200))
Some(LogicalSize::new(200, 200))
} else {
None
};

View File

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

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -6,8 +6,8 @@ use winit::{
dpi::{PhysicalPosition, PhysicalSize},
event::{ElementState, Event, Ime, WindowEvent},
event_loop::EventLoop,
keyboard::{Key, KeyCode},
window::{ImePurpose, WindowBuilder},
keyboard::NamedKey,
window::{ImePurpose, Window},
};
#[path = "util/fill.rs"]
@@ -26,7 +26,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new()
let window = Window::builder()
.with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64))
.build(&event_loop)
.unwrap();
@@ -69,12 +69,12 @@ fn main() -> Result<(), impl std::error::Error> {
WindowEvent::KeyboardInput { event, .. } => {
println!("key: {event:?}");
if event.state == ElementState::Pressed && event.physical_key == KeyCode::F2 {
if event.state == ElementState::Pressed && event.logical_key == NamedKey::F2 {
ime_allowed = !ime_allowed;
window.set_ime_allowed(ime_allowed);
println!("\nIME allowed: {ime_allowed}\n");
}
if event.state == ElementState::Pressed && event.logical_key == Key::F3 {
if event.state == ElementState::Pressed && event.logical_key == NamedKey::F3 {
ime_purpose = match ime_purpose {
ImePurpose::Normal => ImePurpose::Password,
ImePurpose::Password => ImePurpose::Terminal,

View File

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

View File

@@ -3,12 +3,12 @@
use simple_logger::SimpleLogger;
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::monitor::MonitorHandle;
use winit::{event_loop::EventLoop, window::WindowBuilder};
use winit::{event_loop::EventLoop, window::Window};
fn main() {
SimpleLogger::new().init().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() {
print_info("Primary output", mon);

View File

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

View File

@@ -1,6 +1,6 @@
#![allow(clippy::single_match)]
#[cfg(not(wasm_platform))]
#[cfg(not(web_platform))]
fn main() -> Result<(), impl std::error::Error> {
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
@@ -9,8 +9,8 @@ fn main() -> Result<(), impl std::error::Error> {
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
keyboard::{Key, ModifiersState},
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel},
keyboard::{Key, ModifiersState, NamedKey},
window::{CursorGrabMode, CursorIcon, Fullscreen, Window, WindowLevel},
};
const WINDOW_COUNT: usize = 3;
@@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
for _ in 0..WINDOW_COUNT {
let window = WindowBuilder::new()
let window = Window::builder()
.with_inner_size(WINDOW_SIZE)
.build(&event_loop)
.unwrap();
@@ -65,17 +65,17 @@ fn main() -> Result<(), impl std::error::Error> {
},
..
} => {
use Key::{ArrowLeft, ArrowRight};
use NamedKey::{ArrowLeft, ArrowRight};
window.set_title(&format!("{key:?}"));
let state = !modifiers.shift_key();
match key {
// Cycle through video modes
Key::ArrowRight | Key::ArrowLeft => {
video_mode_id = match key {
ArrowLeft => video_mode_id.saturating_sub(1),
ArrowRight => (video_modes.len() - 1).min(video_mode_id + 1),
_ => unreachable!(),
};
Key::Named(ArrowRight) | Key::Named(ArrowLeft) => {
if key == ArrowLeft {
video_mode_id = video_mode_id.saturating_sub(1);
} else if key == ArrowRight {
video_mode_id = (video_modes.len() - 1).min(video_mode_id + 1);
}
println!("Picking video mode: {}", video_modes[video_mode_id]);
}
// WARNING: Consider using `key_without_modifers()` if available on your platform.
@@ -84,7 +84,7 @@ fn main() -> Result<(), impl std::error::Error> {
"1" => window.set_window_level(WindowLevel::AlwaysOnTop),
"2" => window.set_window_level(WindowLevel::AlwaysOnBottom),
"3" => window.set_window_level(WindowLevel::Normal),
"c" => window.set_cursor_icon(match state {
"c" => window.set_cursor(match state {
true => CursorIcon::Progress,
false => CursorIcon::Default,
}),
@@ -96,21 +96,13 @@ fn main() -> Result<(), impl std::error::Error> {
)),
(false, _) => None,
}),
"l" if state => {
if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked)
{
println!("error: {err}");
}
}
"g" if state => {
if let Err(err) =
window.set_cursor_grab(CursorGrabMode::Confined)
{
println!("error: {err}");
}
}
"g" | "l" if !state => {
if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
ch @ ("g" | "l") => {
let mode = match (ch, state) {
("l", true) => CursorGrabMode::Locked,
("g", true) => CursorGrabMode::Confined,
(_, _) => CursorGrabMode::None,
};
if let Err(err) = window.set_cursor_grab(mode) {
println!("error: {err}");
}
}
@@ -123,10 +115,6 @@ fn main() -> Result<(), impl std::error::Error> {
println!("-> inner_size : {:?}", window.inner_size());
println!("-> fullscreen : {:?}", window.fullscreen());
}
"l" => window.set_min_inner_size(match state {
true => Some(WINDOW_SIZE),
false => None,
}),
"m" => window.set_maximized(state),
"p" => window.set_outer_position({
let mut position = window.outer_position().unwrap();
@@ -140,12 +128,26 @@ fn main() -> Result<(), impl std::error::Error> {
"s" => {
let _ = window.request_inner_size(match state {
true => PhysicalSize::new(
WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 100,
WINDOW_SIZE.width + 50,
WINDOW_SIZE.height + 50,
),
false => WINDOW_SIZE,
});
}
"k" => window.set_min_inner_size(match state {
true => Some(PhysicalSize::new(
WINDOW_SIZE.width - 100,
WINDOW_SIZE.height - 100,
)),
false => None,
}),
"o" => window.set_max_inner_size(match state {
true => Some(PhysicalSize::new(
WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 100,
)),
false => None,
}),
"w" => {
if let Size::Physical(size) = WINDOW_SIZE.into() {
window
@@ -185,7 +187,7 @@ fn main() -> Result<(), impl std::error::Error> {
event:
KeyEvent {
state: ElementState::Released,
logical_key: Key::Escape,
logical_key: Key::Named(NamedKey::Escape),
..
},
..
@@ -203,7 +205,7 @@ fn main() -> Result<(), impl std::error::Error> {
})
}
#[cfg(wasm_platform)]
#[cfg(web_platform)]
fn main() {
panic!("Example not supported on Wasm");
panic!("Example not supported on Web");
}

View File

@@ -4,9 +4,9 @@ use std::collections::HashMap;
use simple_logger::SimpleLogger;
use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event::{ElementState, Event, WindowEvent},
event_loop::EventLoop,
keyboard::Key,
keyboard::{Key, NamedKey},
window::Window,
};
@@ -40,19 +40,18 @@ fn main() -> Result<(), impl std::error::Error> {
}
}
WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
logical_key: Key::Character(c),
..
},
event,
is_synthetic: false,
..
} if matches!(c.as_ref(), "n" | "N") => {
let window = Window::new(elwt).unwrap();
println!("Opened a new window: {:?}", window.id());
windows.insert(window.id(), window);
}
} if event.state == ElementState::Pressed => match event.logical_key {
Key::Named(NamedKey::Escape) => elwt.exit(),
Key::Character(c) if c == "n" || c == "N" => {
let window = Window::new(elwt).unwrap();
println!("Opened a new window: {:?}", window.id());
windows.insert(window.id(), window);
}
_ => (),
},
WindowEvent::RedrawRequested => {
if let Some(window) = windows.get(&window_id) {
fill::fill_window(window);

View File

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

View File

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

View File

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

View File

@@ -11,11 +11,10 @@ mod example {
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::EventLoop;
use winit::keyboard::Key;
use winit::platform::startup_notify::{
EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify,
};
use winit::window::{Window, WindowBuilder, WindowId};
use winit::window::{Window, WindowId};
pub(super) fn main() -> Result<(), impl std::error::Error> {
// Create the event loop and get the activation token.
@@ -46,7 +45,7 @@ mod example {
},
..
} => {
if logical_key == Key::Character("n".into()) {
if logical_key == "n" {
if let Some(window) = windows.get(&window_id) {
// Request a new activation token on this window.
// Once we get it we will use it to create a window.
@@ -85,8 +84,7 @@ mod example {
if current_token.is_some() || create_first_window {
// Create the initial window.
let window = {
let mut builder =
WindowBuilder::new().with_title(format!("Window {}", counter));
let mut builder = Window::builder().with_title(format!("Window {}", counter));
if let Some(token) = current_token.take() {
println!("Creating a window with token {token:?}");

View File

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

View File

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

View File

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

View File

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

116
winit/examples/util/fill.rs Normal file
View File

@@ -0,0 +1,116 @@
//! Fill the window buffer with a solid color.
//!
//! Launching a window without drawing to it has unpredictable results varying from platform to
//! platform. In order to have well-defined examples, this module provides an easy way to
//! fill the window buffer with a solid color.
//!
//! 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.
#[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"))))]
mod platform {
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::ManuallyDrop;
use std::num::NonZeroU32;
use softbuffer::{Context, Surface};
use winit::window::Window;
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.
struct GraphicsContext {
/// The global softbuffer context.
context: Context,
/// The hash map of window IDs to surfaces.
surfaces: HashMap<WindowId, Surface>,
}
impl GraphicsContext {
fn new(w: &Window) -> Self {
Self {
context: unsafe { Context::new(w) }.expect("Failed to create a softbuffer context"),
surfaces: HashMap::new(),
}
}
fn create_surface(&mut self, window: &Window) -> &mut Surface {
self.surfaces.entry(window.id()).or_insert_with(|| {
unsafe { Surface::new(&self.context, window) }
.expect("Failed to create a softbuffer surface")
})
}
fn destroy_surface(&mut self, window: &Window) {
self.surfaces.remove(&window.id());
}
}
pub fn fill_window(window: &Window) {
GC.with(|gc| {
let size = window.inner_size();
let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
else {
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");
})
}
#[allow(dead_code)]
pub fn cleanup_window(window: &Window) {
GC.with(|gc| {
let mut gc = gc.borrow_mut();
if let Some(context) = gc.as_mut() {
context.destroy_surface(window);
}
});
}
}
#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
mod platform {
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

@@ -3,14 +3,14 @@
use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
keyboard::KeyCode,
window::{Fullscreen, WindowBuilder},
keyboard::Key,
window::{Fullscreen, Window},
};
pub fn main() -> Result<(), impl std::error::Error> {
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)]
let builder = {
use winit::platform::web::WindowBuilderExtWebSys;
@@ -18,11 +18,11 @@ pub fn main() -> Result<(), impl std::error::Error> {
};
let window = builder.build(&event_loop).unwrap();
#[cfg(wasm_platform)]
#[cfg(web_platform)]
let log_list = wasm::insert_canvas_and_create_log_list(&window);
event_loop.run(move |event, elwt| {
#[cfg(wasm_platform)]
#[cfg(web_platform)]
wasm::log_event(&log_list, &event);
match event {
@@ -39,13 +39,13 @@ pub fn main() -> Result<(), impl std::error::Error> {
WindowEvent::KeyboardInput {
event:
KeyEvent {
physical_key: KeyCode::KeyF,
logical_key: Key::Character(c),
state: ElementState::Released,
..
},
..
},
} if window_id == window.id() => {
} if window_id == window.id() && c == "f" => {
if window.fullscreen().is_some() {
window.set_fullscreen(None);
} else {
@@ -57,7 +57,7 @@ pub fn main() -> Result<(), impl std::error::Error> {
})
}
#[cfg(wasm_platform)]
#[cfg(web_platform)]
mod wasm {
use std::num::NonZeroU32;

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