Compare commits

..

24 Commits

Author SHA1 Message Date
Mads Marquart
fbc6fdc30c Add initialization closure and drop on exit 2024-12-03 13:28:52 +01:00
Mads Marquart
164bf85b5b Windows: Add scancode conversions from Chromium sources (#4020) 2024-12-03 11:05:22 +01:00
Mads Marquart
cfa8f027cc AppKit/UIKit: Do not emit default state events upon window creation (#3912)
Do not emit redundant ScaleFactorChanged, SurfaceResized and Focused.
2024-12-02 23:10:33 +01:00
Mads Marquart
2e5db75101 Linux: Align scancode conversions with Firefox (#4021) 2024-12-02 13:38:37 +01:00
Mads Marquart
19e5bee3d1 chore: fix clippy lints 2024-12-02 14:51:26 +03:00
Hamir Mahal
fc6cf89ac0 style: simplify string formatting for readability (#4001) 2024-11-22 22:14:11 +01:00
Mads Marquart
dbcdb6f1b4 Add safe area and document coordinate systems (#3890)
Added `Window::safe_area`, which describes the area of the surface that
is unobstructed by notches, bezels etc. The drawing code in the examples
have been updated to draw a star inside the safe area, and the plain
background outside of it.

Also renamed `Window::inner_position` to `Window::surface_position`, and
changed it to from screen coordinates to window coordinates, to better
align how these coordinate systems work together.

Finally, added some SVG images and documentation to describe how all of
this works.

This is fully implemented on macOS and iOS, and partially on the web.

Co-authored-by: daxpedda <daxpedda@gmail.com>
2024-11-21 17:37:03 +01:00
Rodrigo Rivas Costa
d0c6c34eaa x11: move up XInput2 event registration
It should be done before mapping the window, or we could
lose the firsst XInput2 events, such as the first FocusIn.

Fixes #2841.
2024-11-21 14:04:55 +03:00
Mads Marquart
f2688d94ea ci: fix cargo deny 2024-11-21 10:28:32 +03:00
Nico Burns
f6b20852bf macOS: set activation policy by default if app is not bundled (#3961) 2024-11-18 21:19:45 +01:00
Kirill Chibisov
59b1eb5410 api: make OwnedDisplayHandle wrap an opaque type
This will help in case we want to linger the event loop during drop
to prevent use after free in the consumers code.
2024-11-13 15:29:05 +03:00
Aaron Muir Hamilton
f781e13166 X11: Use bottom-right corner of IME cursor area as caret position
XIM servers currently do not support preedit area reporting from
clients and there may be no standard way to report it.

Fcitx and iBus both place the candidate window descending descending
from the caret, with the reported X font; but since winit does not
report a font, the height of the line is assumed 0, so when we report
the top left corner of the cursor area they will tend to obscure it.

Taking this into account, the best default option is to report the
bottom right corner of the cursor area, because it will tend not to
obscure the preedit area when using `Window::set_ime_cursor_area` in
the way suggested by documentation.
2024-11-13 15:13:09 +03:00
Piotr Podusowski
9f8ac8feb5 android: use show_soft_input to summon the keyboard
Route it via the `Window::set_ime_allowed` like on iOS.
2024-11-13 14:43:59 +03:00
Mads Marquart
74958ecc6f docs: add fn main to root examples
This is not strictly required, but makes the examples a bit easier to
read understand (especially since the `EventLoop` really _should_ be
created inside `fn main`, and not in some random function potentially
running on a random thread).
2024-11-12 16:52:48 +03:00
Kirill Chibisov
3a60cbaba5 api: make EventLoopProxy wrap an opaque type
The proxy is intended to be Clone, thus use `Arc` for it internally and
don't require backends for it to be `Clone`. Use `EventLoopProxyProvider`
to hide the backend's proxy implementation details.

Co-authored-by: daxpedda <daxpedda@gmail.com>
2024-11-12 10:56:20 +03:00
Matěj Laitl
ae4c449670 x11: fix WindowAttributesExtX11::with_x11_screen()
Based on https://github.com/rust-windowing/winit/pull/3973, which should be merged first.

There's an API to programmatically specify X11 screen id (override what is determined from the `DISPLAY` env variable), but it doesn't work.

Seeting up X Server with 2 screens and calling `DISPLAY=:0 X11_SCREEN_ID=1 cargo run --example window` should be equivalent to calling `DISPLAY=:0.1 cargo run --example window`

The latter works (and places the window on the correct screen), but the former yields

`failed to create initial window: Os(OsError { line: 620, file: "src/platform_impl/linux/x11/window.rs", error: X11Error(X11Error { error_kind: Match, error_code: 8, sequence: 219, bad_value: 1319, minor_opcode: 0, major_opcode: 1, extension_name: None, request_name: Some("CreateWindow") }) })`

_Here `1319` is the root window id for screen 0, which doesn't match the screen 1 that we request._

The problem is that we need to factor in the screen id when determining the parent (root) window when not explicitly set. This patch does that.

---

Also: Extend the window example with X11_{SCREEN,VISUAL}_ID env variables
2024-11-06 08:30:25 -08:00
Kirill Chibisov
b2896d7408 chore: always pull raw-window-handle
Winit is not useful without it and we don't provide older versions
anymore.
2024-11-03 00:39:01 +03:00
Kirill Chibisov
edfb4b03f4 chore: remove platform FingerId
The same as for `WindowId`.
2024-11-02 17:10:32 +03:00
Kirill Chibisov
c8c1eca3c7 api: move primary from FingerId to Pointer events
Whether the pointer event is primary or not generally matters for the
context where all input is done by the same event, so users can
_ignore_ non-primary events since they are likely from users clicking
something else with their other fingers.

Having it only on a FingerId made it useless, since it's usually used
to avoid multi-touch, but if you start mapping on touch event you
already can track things like that yourself.

Fixes #3943.

Co-authored-by: daxpedda <daxpedda@gmail.com>
2024-11-02 17:10:32 +03:00
Matěj Laitl
d3207a8d76 Apply @notgull's suggestion 2024-10-29 17:54:21 -07:00
Matěj Laitl
9d347b72d9 Add changelog entry 2024-10-29 17:54:21 -07:00
Matěj Laitl
13bb51c7e0 x11: iterate only visuals from the matching screen 2024-10-29 17:54:21 -07:00
AngelicosPhosphoros
bb4aa22cf9 Improve waiting for messages on Windows
Previous version used [`SetTimer`] with `GetMessageW` for waiting.
The downside of UI timers like ones created by `SetTimer`,
is that they may be late by up to 15-16 ms.

To fix this behaviour, I added use of high resolution timers created by [`CreateWaitableTimerExW`] with the flag `CREATE_WAITABLE_TIMER_HIGH_RESOLUTION`.
In my previous experience, waiting on such timers have precision of roundly 0.5 ms which is the best available on Windows at the moment.
I use [`MsgWaitForMultipleObjectsEx`] to wait simultaneously for both timer and newly arriving events.

Unfortunately, high resolution timers are available only since Windows 10 1803. However:

1. Win 10 is already getting to the end of support, like all previous versions, so it is OK to rely on APIs introduced in it;
2. I use `dwMilliseconds` parameter of `MsgWaitForMultipleObjectsEx` as a fallback. It should perform not worse compared to waiting for events from `SetTimer`.

I also refactored code to remove event dispatching from function responsible for waiting for events. This provides more clear separations of concern and avoids unnecessary duplication of dispatching logic.

After [review] from @rib, I also moved the waiting itself from `wait_for_messages` method to separate function, so it is clearly seen that `wait_for_messages` do 3 things: notify app that we about to wait, wait, notify that we have new events.

I have tested behaviour using a egui app with Vulkan rendering with `VK_PRESENT_MODE_IMMEDIATE_KHR`, and older version consistently have twice less FPS than requested (e.g. 30 FPS when limit is 60 and 60 FPS when limit is 120) while newer version works more correctly (almost always 60 FPS when limit is 60, and only 5-10 frames missing when FPS is set to 120 or more).

Fixes https://github.com/rust-windowing/winit/issues/1610

[`CreateWaitableTimerExW`]: https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createwaitabletimerexw
[`MsgWaitForMultipleObjectsEx`]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex
[`SetTimer`]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-settimer
[review]: https://github.com/rust-windowing/winit/pull/3950#discussion_r1800184479
2024-10-28 16:38:17 -07:00
Valentine Briese
3e9b80d47a macOS: add ability to make titlebar unified (#3960)
Adds `WindowExtMacOS::set_unified_titlebar` and
`WindowAttributesExtMacOS::with_unified_titlebar`,
which allow you to use a larger titlebar style on macOS.
2024-10-25 23:22:52 +02:00
84 changed files with 2392 additions and 1728 deletions

View File

@@ -25,7 +25,6 @@ version = "0.30.5"
[package.metadata.docs.rs]
features = [
"rwh_06",
"serde",
"mint",
# Enabled to get docs to compile
@@ -55,9 +54,8 @@ targets = [
[features]
android-game-activity = ["android-activity/game-activity"]
android-native-activity = ["android-activity/native-activity"]
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
mint = ["dpi/mint"]
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde", "bitflags/serde"]
wayland = [
"wayland-client",
@@ -81,7 +79,7 @@ cfg_aliases = "0.2.1"
bitflags = "2"
cursor-icon = "1.1.0"
dpi = { version = "0.1.1", path = "dpi" }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"] }
serde = { workspace = true, optional = true }
smol_str = "0.2.0"
tracing = { version = "0.1.40", default-features = false }
@@ -102,7 +100,7 @@ softbuffer = { version = "0.4.6", default-features = false, features = [
# Android
[target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.6.0"
ndk = { version = "0.9.0", default-features = false }
ndk = { version = "0.9.0", features = ["rwh_06"], default-features = false }
# AppKit or UIKit
[target.'cfg(target_vendor = "apple")'.dependencies]
@@ -136,6 +134,7 @@ objc2-app-kit = { version = "0.2.2", features = [
"NSScreen",
"NSTextInputClient",
"NSTextInputContext",
"NSToolbar",
"NSView",
"NSWindow",
"NSWindowScripting",
@@ -150,6 +149,7 @@ objc2-foundation = { version = "0.2.2", features = [
"NSDictionary",
"NSDistributedNotificationCenter",
"NSEnumerator",
"NSGeometry",
"NSKeyValueObserving",
"NSNotification",
"NSObjCRuntime",
@@ -214,6 +214,7 @@ windows-sys = { version = "0.52.0", features = [
"Win32_System_Com",
"Win32_System_LibraryLoader",
"Win32_System_Ole",
"Win32_Security",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
@@ -296,6 +297,7 @@ web_sys = { package = "web-sys", version = "0.3.70", features = [
"FocusEvent",
"HtmlCanvasElement",
"HtmlElement",
"HtmlHtmlElement",
"HtmlImageElement",
"ImageBitmap",
"ImageBitmapOptions",
@@ -343,11 +345,9 @@ wasm-bindgen-test = "0.3"
[[example]]
doc-scrape-examples = true
name = "window"
required-features = ["rwh_06"]
[[example]]
name = "child_window"
required-features = ["rwh_06"]
[workspace]
members = ["dpi"]

View File

@@ -26,12 +26,12 @@ targets = [
[licenses]
allow = [
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
"ISC", # https://tldrlegal.com/license/-isc-license
"MIT", # https://tldrlegal.com/license/mit-license
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
"ISC", # https://tldrlegal.com/license/isc-license
"MIT", # https://tldrlegal.com/license/mit-license
"Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html
]
confidence-threshold = 1.0
private = { ignore = true }

View File

@@ -9,3 +9,10 @@ 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.
## `coordinate-systems*`
These files are created by [Mads Marquart](https://github.com/madsmtm) using
[draw.io](https://draw.io/), and compressed using [svgomg.net](https://svgomg.net/).
They are licensed under the [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/) license.

View File

@@ -0,0 +1,130 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0" version="24.8.6" pages="2">
<diagram name="desktop" id="3DDum1nDijUk3y7wIDRm">
<mxGraphModel dx="1080" dy="707" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1000" pageHeight="500" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="cRYnzpdCW-J0f_YpP3mc-1" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#E8E8E8;fontColor=#333333;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="200" y="80" width="480" height="360" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-4" value="" style="rounded=1;whiteSpace=wrap;html=1;shadow=0;fillColor=#d5e8d4;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="260" y="340" width="360" height="40" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-2" value="" style="rounded=1;whiteSpace=wrap;html=1;shadow=0;fillColor=#dae8fc;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="260" y="140" width="360" height="80" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-3" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#DBDBDB;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
<mxGeometry x="200" y="60" width="480" height="20" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-5" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=none;fillColor=#d5e8d4;" parent="1" vertex="1">
<mxGeometry x="260" y="180" width="360" height="180" as="geometry" />
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-6" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#666666;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-4" target="cRYnzpdCW-J0f_YpP3mc-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="10" y="310" as="sourcePoint" />
<mxPoint x="60" y="260" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-7" value="" style="endArrow=none;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#666666;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-4" target="cRYnzpdCW-J0f_YpP3mc-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="770" y="570" as="sourcePoint" />
<mxPoint x="770" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-8" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;strokeColor=#666666;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-2" target="cRYnzpdCW-J0f_YpP3mc-5" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="220.00000000000023" y="179.69" as="sourcePoint" />
<mxPoint x="740.0000000000002" y="179.69" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-9" value="&lt;font&gt;outer_position&lt;/font&gt;" style="endArrow=blockThin;html=1;strokeWidth=3;rounded=0;exitX=0;exitY=0;exitDx=0;exitDy=0;dashed=1;align=right;fontSize=20;fontFamily=monospace;fontColor=#6C8EBF;labelBackgroundColor=none;spacingLeft=0;spacingRight=15;spacing=0;fillColor=#dae8fc;strokeColor=#6C8EBF;endFill=1;startArrow=oval;startFill=1;endSize=6;targetPerimeterSpacing=0;entryX=0;entryY=0;entryDx=0;entryDy=0;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-3" edge="1" target="cRYnzpdCW-J0f_YpP3mc-2">
<mxGeometry x="-0.36" y="-24" width="50" height="50" relative="1" as="geometry">
<mxPoint x="80" y="160" as="sourcePoint" />
<mxPoint x="240" y="160" as="targetPoint" />
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-10" value="&lt;font&gt;outer_size&lt;/font&gt;" style="endArrow=none;html=1;strokeWidth=3;rounded=0;dashed=1;align=left;fontSize=20;fontFamily=monospace;fontColor=#6C8EBF;labelBackgroundColor=none;spacingLeft=15;spacingRight=0;spacing=0;exitX=1;exitY=0;exitDx=0;exitDy=0;fillColor=#dae8fc;strokeColor=#6c8ebf;entryX=1;entryY=1;entryDx=0;entryDy=0;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-2" edge="1" target="cRYnzpdCW-J0f_YpP3mc-4">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="850" y="170" as="sourcePoint" />
<mxPoint x="760" y="420" as="targetPoint" />
<Array as="points">
<mxPoint x="860" y="140" />
<mxPoint x="860" y="380" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-11" value="&lt;font&gt;surface_size&lt;/font&gt;" style="endArrow=none;html=1;strokeWidth=3;rounded=0;dashed=1;align=left;fontSize=20;fontFamily=monospace;fontColor=#82B366;labelBackgroundColor=none;spacingLeft=15;spacingRight=0;spacing=0;entryX=1;entryY=1;entryDx=0;entryDy=0;fillColor=#d5e8d4;strokeColor=#82B366;" parent="1" target="cRYnzpdCW-J0f_YpP3mc-4" edge="1">
<mxGeometry x="0.0526" width="50" height="50" relative="1" as="geometry">
<mxPoint x="600" y="180" as="sourcePoint" />
<mxPoint x="760" y="420" as="targetPoint" />
<Array as="points">
<mxPoint x="700" y="180" />
<mxPoint x="700" y="380" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="cRYnzpdCW-J0f_YpP3mc-12" value="&lt;font&gt;surface_position&lt;/font&gt;" style="endArrow=blockThin;html=1;strokeWidth=3;rounded=0;dashed=1;align=right;fontSize=20;fontFamily=monospace;fontColor=#82B366;labelBackgroundColor=none;spacingLeft=0;spacingRight=15;spacing=0;fillColor=#d5e8d4;strokeColor=#82b366;exitX=0;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;curved=1;startArrow=oval;startFill=1;endFill=1;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-2" target="cRYnzpdCW-J0f_YpP3mc-5" edge="1">
<mxGeometry y="-50" width="50" height="50" relative="1" as="geometry">
<mxPoint x="140" y="140" as="sourcePoint" />
<mxPoint x="160" y="200" as="targetPoint" />
<Array as="points">
<mxPoint x="250" y="160" />
</Array>
<mxPoint x="-5" y="-22" as="offset" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram name="mobile" id="D5mAeJSS4Z33KEKjPCBt">
<mxGraphModel dx="1710" dy="1120" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="720" pageHeight="720" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-1" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#E8E8E8;fontColor=#333333;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="200" y="40" width="320" height="640" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-2" value="" style="rounded=1;whiteSpace=wrap;html=1;shadow=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="210" y="50" width="300" height="620" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-4" value="" style="rounded=0;whiteSpace=wrap;html=1;shadow=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="1" vertex="1">
<mxGeometry x="210" y="90" width="300" height="540" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-20" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#DBDBDB;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
<mxGeometry x="290" y="640" width="140" height="10" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-3" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#DBDBDB;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
<mxGeometry x="300" y="50" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="RxwCrVmIsQwV7z5iJ9nY-12" value="&lt;font&gt;surface_size&lt;/font&gt;" style="endArrow=none;html=1;strokeWidth=3;rounded=0;dashed=1;align=left;fontSize=20;fontFamily=monospace;fontColor=#82B366;labelBackgroundColor=none;spacingLeft=15;spacingRight=15;spacing=0;fillColor=#d5e8d4;strokeColor=#82b366;exitX=1;exitY=0;exitDx=0;exitDy=0;" parent="1" source="RxwCrVmIsQwV7z5iJ9nY-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="210" y="50" as="sourcePoint" />
<mxPoint x="510" y="670" as="targetPoint" />
<Array as="points">
<mxPoint x="560" y="50" />
<mxPoint x="560" y="670" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="NrHAzeOh65jb3hkBOxW9-1" value="&lt;div&gt;safe_area.top&lt;/div&gt;" style="endArrow=blockThin;html=1;strokeWidth=3;rounded=0;align=right;fontSize=20;fontFamily=monospace;fontColor=#D79B00;labelBackgroundColor=none;spacingLeft=0;spacingRight=15;spacing=0;fillColor=#ffe6cc;strokeColor=#d79b00;startArrow=blockThin;startFill=1;endFill=1;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="180" y="50" as="sourcePoint" />
<mxPoint x="180" y="90" as="targetPoint" />
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="NrHAzeOh65jb3hkBOxW9-5" value="&lt;div&gt;safe_area.bottom&lt;/div&gt;" style="endArrow=blockThin;html=1;strokeWidth=3;rounded=0;align=right;fontSize=20;fontFamily=monospace;fontColor=#D79B00;labelBackgroundColor=none;spacingLeft=0;spacingRight=15;spacing=0;fillColor=#ffe6cc;strokeColor=#d79b00;startArrow=blockThin;startFill=1;endFill=1;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="180" y="670" as="sourcePoint" />
<mxPoint x="180" y="630" as="targetPoint" />
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="720" viewBox="-0.5 -0.5 720 720" class="ge-export-svg-auto"><defs><style><![CDATA[@media (prefers-color-scheme:dark){svg.ge-export-svg-auto svg:not(mjx-container>svg),svg.ge-export-svg-auto:not(mjx-container>svg){filter:invert(100%) hue-rotate(180deg)}}]]></style></defs><g data-cell-id="0"><g data-cell-id="1"><rect x="200" y="40" width="320" height="640" rx="48" ry="48" fill="#e8e8e8" stroke="#666" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-1"/><rect x="210" y="50" width="300" height="620" rx="45" ry="45" fill="#d5e8d4" stroke="#82b366" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-2"/><path fill="#ffe6cc" stroke="#d79b00" pointer-events="all" d="M210 90h300v540H210z" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-4"/><rect x="290" y="640" width="140" height="10" rx="1.5" ry="1.5" fill="#dbdbdb" stroke="#666" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-20"/><rect x="300" y="50" width="120" height="30" rx="4.5" ry="4.5" fill="#dbdbdb" stroke="#666" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-3"/><g data-cell-id="RxwCrVmIsQwV7z5iJ9nY-12"><path d="M510 50h50v620h-50" fill="none" stroke="#82b366" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><switch transform="translate(-.5 -.5)"><foreignObject style="overflow:visible;text-align:left" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe flex-start;width:1px;height:1px;padding-top:360px;margin-left:575px"><div style="box-sizing:border-box;font-size:0;text-align:left" data-drawio-colors="color: #82B366;"><div style="display:inline-block;font-size:20px;font-family:&quot;monospace&quot;;color:#82b366;line-height:1.2;pointer-events:all;white-space:nowrap"><font>surface_size</font></div></div></div></foreignObject><text x="575" y="366" fill="#82B366" font-family="&quot;monospace&quot;" font-size="20">surfa...</text></switch></g><g data-cell-id="NrHAzeOh65jb3hkBOxW9-1"><path d="M180 62.35v15.3" fill="none" stroke="#d79b00" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/><path d="m180 53.35 3 9h-6ZM180 86.65l-3-9h6Z" fill="#d79b00" stroke="#d79b00" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><switch transform="translate(-.5 -.5)"><foreignObject style="overflow:visible;text-align:left" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe flex-end;width:1px;height:1px;padding-top:70px;margin-left:165px"><div style="box-sizing:border-box;font-size:0;text-align:right" data-drawio-colors="color: #D79B00;"><div style="display:inline-block;font-size:20px;font-family:&quot;monospace&quot;;color:#d79b00;line-height:1.2;pointer-events:all;white-space:nowrap"><div>safe_area.top</div></div></div></div></foreignObject><text x="165" y="76" fill="#D79B00" font-family="&quot;monospace&quot;" font-size="20" text-anchor="end">safe_...</text></switch></g><g data-cell-id="NrHAzeOh65jb3hkBOxW9-5"><path d="M180 657.65v-15.3" fill="none" stroke="#d79b00" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/><path d="m180 666.65-3-9h6ZM180 633.35l3 9h-6Z" fill="#d79b00" stroke="#d79b00" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><switch transform="translate(-.5 -.5)"><foreignObject style="overflow:visible;text-align:left" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe flex-end;width:1px;height:1px;padding-top:650px;margin-left:165px"><div style="box-sizing:border-box;font-size:0;text-align:right" data-drawio-colors="color: #D79B00;"><div style="display:inline-block;font-size:20px;font-family:&quot;monospace&quot;;color:#d79b00;line-height:1.2;pointer-events:all;white-space:nowrap"><div>safe_area.bottom</div></div></div></div></foreignObject><text x="165" y="656" fill="#D79B00" font-family="&quot;monospace&quot;" font-size="20" text-anchor="end">safe_...</text></switch></g></g></g></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -11,7 +11,7 @@ Unreleased` header.
## Unreleased
- Add `Rect`, `PhysicalRect` and `LogicalRect`.
- Added `Insets`, `LogicalInsets` and `PhysicalInsets` types.
## 0.1.1

View File

@@ -759,24 +759,30 @@ impl<P: Pixel> From<LogicalPosition<P>> for Position {
}
}
/// A rectangle represented in logical pixels.
/// The logical distance between the edges of two rectangles.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalRect<P> {
pub origin: LogicalPosition<P>,
pub size: LogicalSize<P>,
pub struct LogicalInsets<P> {
/// The distance to the top edge.
pub top: P,
/// The distance to the left edge.
pub left: P,
/// The distance to the bottom edge.
pub bottom: P,
/// The distance to the right edge.
pub right: P,
}
impl<P> LogicalRect<P> {
impl<P> LogicalInsets<P> {
#[inline]
pub const fn new(origin: LogicalPosition<P>, size: LogicalSize<P>) -> Self {
Self { origin, size }
pub const fn new(top: P, left: P, bottom: P, right: P) -> Self {
Self { top, left, bottom, right }
}
}
impl<P: Pixel> LogicalRect<P> {
impl<P: Pixel> LogicalInsets<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalRect<X>>, X: Pixel>(
pub fn from_physical<T: Into<PhysicalInsets<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
@@ -784,92 +790,116 @@ impl<P: Pixel> LogicalRect<P> {
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalRect<X> {
let origin = self.origin.to_physical(scale_factor);
let size = self.size.to_physical(scale_factor);
PhysicalRect::new(origin, size)
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalInsets<X> {
assert!(validate_scale_factor(scale_factor));
let top = self.top.into() * scale_factor;
let left = self.left.into() * scale_factor;
let bottom = self.bottom.into() * scale_factor;
let right = self.right.into() * scale_factor;
PhysicalInsets::new(top, left, bottom, right).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalRect<X> {
LogicalRect { origin: self.origin.cast(), size: self.size.cast() }
pub fn cast<X: Pixel>(&self) -> LogicalInsets<X> {
LogicalInsets {
top: self.top.cast(),
left: self.left.cast(),
bottom: self.bottom.cast(),
right: self.right.cast(),
}
}
}
/// A rectangle represented in physical pixels.
/// The physical distance between the edges of two rectangles.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalRect<P> {
pub origin: PhysicalPosition<P>,
pub size: PhysicalSize<P>,
pub struct PhysicalInsets<P> {
/// The distance to the top edge.
pub top: P,
/// The distance to the left edge.
pub left: P,
/// The distance to the bottom edge.
pub bottom: P,
/// The distance to the right edge.
pub right: P,
}
impl<P> PhysicalRect<P> {
impl<P> PhysicalInsets<P> {
#[inline]
pub const fn new(origin: PhysicalPosition<P>, size: PhysicalSize<P>) -> Self {
Self { origin, size }
pub const fn new(top: P, left: P, bottom: P, right: P) -> Self {
Self { top, left, bottom, right }
}
}
impl<P: Pixel> PhysicalRect<P> {
impl<P: Pixel> PhysicalInsets<P> {
#[inline]
pub fn from_logical<T: Into<LogicalRect<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
pub fn from_logical<T: Into<LogicalInsets<X>>, X: Pixel>(
logical: T,
scale_factor: f64,
) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalRect<X> {
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalInsets<X> {
assert!(validate_scale_factor(scale_factor));
let origin = self.origin.to_logical(scale_factor);
let size = self.size.to_logical(scale_factor);
LogicalRect::new(origin, size)
let top = self.top.into() / scale_factor;
let left = self.left.into() / scale_factor;
let bottom = self.bottom.into() / scale_factor;
let right = self.right.into() / scale_factor;
LogicalInsets::new(top, left, bottom, right).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalRect<X> {
PhysicalRect { origin: self.origin.cast(), size: self.size.cast() }
pub fn cast<X: Pixel>(&self) -> PhysicalInsets<X> {
PhysicalInsets {
top: self.top.cast(),
left: self.left.cast(),
bottom: self.bottom.cast(),
right: self.right.cast(),
}
}
}
/// A rectangle that's either physical or logical.
/// Insets that are either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Rect {
Physical(PhysicalRect<i32>),
Logical(LogicalRect<f64>),
pub enum Insets {
Physical(PhysicalInsets<u32>),
Logical(LogicalInsets<f64>),
}
impl Rect {
pub fn new<R: Into<Self>>(rect: R) -> Self {
rect.into()
impl Insets {
pub fn new<S: Into<Self>>(insets: S) -> Self {
insets.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalRect<P> {
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalInsets<P> {
match *self {
Self::Physical(rect) => rect.to_logical(scale_factor),
Self::Logical(rect) => rect.cast(),
Self::Physical(insets) => insets.to_logical(scale_factor),
Self::Logical(insets) => insets.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalRect<P> {
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalInsets<P> {
match *self {
Self::Physical(rect) => rect.cast(),
Self::Logical(rect) => rect.to_physical(scale_factor),
Self::Physical(insets) => insets.cast(),
Self::Logical(insets) => insets.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalRect<P>> for Rect {
impl<P: Pixel> From<PhysicalInsets<P>> for Insets {
#[inline]
fn from(rect: PhysicalRect<P>) -> Self {
Self::Physical(rect.cast())
fn from(insets: PhysicalInsets<P>) -> Self {
Self::Physical(insets.cast())
}
}
impl<P: Pixel> From<LogicalRect<P>> for Rect {
impl<P: Pixel> From<LogicalInsets<P>> for Insets {
#[inline]
fn from(rect: LogicalRect<P>) -> Self {
Self::Logical(rect.cast())
fn from(insets: LogicalInsets<P>) -> Self {
Self::Logical(insets.cast())
}
}

View File

@@ -13,26 +13,12 @@ fn main() -> Result<(), impl std::error::Error> {
#[path = "util/fill.rs"]
mod fill;
#[derive(Default)]
struct Application {
parent_window_id: Option<WindowId>,
parent_window_id: WindowId,
windows: HashMap<WindowId, Box<dyn Window>>,
}
impl ApplicationHandler for Application {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let attributes = WindowAttributes::default()
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
let window = event_loop.create_window(attributes).unwrap();
println!("Parent window id: {:?})", window.id());
self.parent_window_id = Some(window.id());
self.windows.insert(window.id(), window);
}
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
@@ -56,7 +42,7 @@ fn main() -> Result<(), impl std::error::Error> {
event: KeyEvent { state: ElementState::Pressed, .. },
..
} => {
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap();
let parent_window = self.windows.get(&self.parent_window_id).unwrap();
let child_window = spawn_child_window(parent_window.as_ref(), event_loop);
let child_id = child_window.id();
println!("Child window created with id: {child_id:?}");
@@ -72,6 +58,20 @@ fn main() -> Result<(), impl std::error::Error> {
}
}
fn init(event_loop: &dyn ActiveEventLoop) -> Application {
let attributes = WindowAttributes::default()
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
let window = event_loop.create_window(attributes).unwrap();
println!("Parent window id: {:?})", window.id());
let parent_window_id = window.id();
let windows = HashMap::from([(window.id(), window)]);
Application { parent_window_id, windows }
}
fn spawn_child_window(
parent: &dyn Window,
event_loop: &dyn ActiveEventLoop,
@@ -89,7 +89,7 @@ fn main() -> Result<(), impl std::error::Error> {
}
let event_loop = EventLoop::new().unwrap();
event_loop.run_app(Application::default())
event_loop.run(init)
}
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))]

View File

@@ -43,16 +43,31 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
event_loop.run_app(ControlFlowDemo::default())
event_loop.run(ControlFlowDemo::new)
}
#[derive(Default)]
struct ControlFlowDemo {
mode: Mode,
request_redraw: bool,
wait_cancelled: bool,
close_requested: bool,
window: Option<Box<dyn Window>>,
window: Box<dyn Window>,
}
impl ControlFlowDemo {
fn new(event_loop: &dyn ActiveEventLoop) -> Self {
let window_attributes = WindowAttributes::default().with_title(
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
);
let window = event_loop.create_window(window_attributes).unwrap();
Self {
mode: Mode::default(),
request_redraw: false,
wait_cancelled: false,
close_requested: false,
window,
}
}
}
impl ApplicationHandler for ControlFlowDemo {
@@ -65,13 +80,6 @@ impl ApplicationHandler for ControlFlowDemo {
}
}
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = WindowAttributes::default().with_title(
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
);
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event(
&mut self,
_event_loop: &dyn ActiveEventLoop,
@@ -112,9 +120,8 @@ impl ApplicationHandler for ControlFlowDemo {
_ => (),
},
WindowEvent::RedrawRequested => {
let window = self.window.as_ref().unwrap();
window.pre_present_notify();
fill::fill_window(window.as_ref());
self.window.pre_present_notify();
fill::fill_window(&*self.window);
},
_ => (),
}
@@ -122,7 +129,7 @@ impl ApplicationHandler for ControlFlowDemo {
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
if self.request_redraw && !self.wait_cancelled && !self.close_requested {
self.window.as_ref().unwrap().request_redraw();
self.window.request_redraw();
}
match self.mode {

View File

@@ -8,7 +8,7 @@ fn main() -> std::process::ExitCode {
use std::time::Duration;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event::{StartCause, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus};
use winit::window::{Window, WindowAttributes, WindowId};
@@ -22,9 +22,12 @@ fn main() -> std::process::ExitCode {
}
impl ApplicationHandler for PumpDemo {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = WindowAttributes::default().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap());
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
if matches!(cause, StartCause::Init) && self.window.is_none() {
let window_attributes =
WindowAttributes::default().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
}
fn window_event(

View File

@@ -21,14 +21,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
window: Option<Box<dyn Window>>,
}
impl ApplicationHandler for App {
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
}
}
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
impl App {
fn init_window(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = WindowAttributes::default()
.with_title("Fantastic window number one!")
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
@@ -36,6 +30,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
self.window_id = Some(window.id());
self.window = Some(window);
}
}
impl ApplicationHandler for App {
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
}
}
fn window_event(
&mut self,
@@ -81,14 +83,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut event_loop = EventLoop::new().unwrap();
let mut app = App { idx: 1, ..Default::default() };
event_loop.run_app_on_demand(&mut app)?;
event_loop.run_on_demand(|event_loop| {
app.init_window(event_loop);
&mut app
})?;
println!("--------------------------------------------------------- Finished first loop");
println!("--------------------------------------------------------- Waiting 5 seconds");
std::thread::sleep(Duration::from_secs(5));
app.idx += 1;
event_loop.run_app_on_demand(&mut app)?;
event_loop.run_on_demand(|event_loop| {
app.init_window(event_loop);
&mut app
})?;
println!("--------------------------------------------------------- Finished second loop");
Ok(())
}

View File

@@ -11,7 +11,7 @@
pub use platform::cleanup_window;
pub use platform::fill_window;
#[cfg(all(feature = "rwh_06", not(any(target_os = "android", target_os = "ios"))))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
mod platform {
use std::cell::RefCell;
use std::collections::HashMap;
@@ -106,7 +106,7 @@ mod platform {
}
}
#[cfg(not(all(feature = "rwh_06", not(any(target_os = "android", target_os = "ios")))))]
#[cfg(any(target_os = "android", target_os = "ios"))]
mod platform {
pub fn fill_window(_window: &dyn winit::window::Window) {
// No-op on mobile platforms.

View File

@@ -31,6 +31,8 @@ use winit::platform::startup_notify::{
};
#[cfg(web_platform)]
use winit::platform::web::{ActiveEventLoopExtWeb, CustomCursorExtWeb, WindowAttributesExtWeb};
#[cfg(x11_platform)]
use winit::platform::x11::WindowAttributesExtX11;
use winit::window::{
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection,
Theme, Window, WindowAttributes, WindowId,
@@ -68,8 +70,7 @@ fn main() -> Result<(), Box<dyn Error>> {
});
}
let app = Application::new(&event_loop, receiver, sender);
Ok(event_loop.run_app(app)?)
Ok(event_loop.run(|event_loop| Application::new(event_loop, receiver, sender))?)
}
/// Application state and event handling.
@@ -86,21 +87,24 @@ struct Application {
///
/// With OpenGL it could be EGLDisplay.
#[cfg(not(android_platform))]
context: Option<Context<DisplayHandle<'static>>>,
context: Context<DisplayHandle<'static>>,
}
impl Application {
fn new(event_loop: &EventLoop, receiver: Receiver<Action>, sender: Sender<Action>) -> Self {
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
fn new(
event_loop: &dyn ActiveEventLoop,
receiver: Receiver<Action>,
sender: Sender<Action>,
) -> Self {
// SAFETY: The context is stored in the application, which is dropped right before the event
// loop is stopped, thus making it safe.
#[cfg(not(android_platform))]
let context = Some(
Context::new(unsafe {
std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(
event_loop.display_handle().unwrap(),
)
})
.unwrap(),
);
let context = Context::new(unsafe {
std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(
event_loop.display_handle().unwrap(),
)
})
.unwrap();
// You'll have to choose an icon size at your own discretion. On X11, the desired size
// varies by WM, and on Windows, you still have to account for screen scaling. Here
@@ -118,7 +122,7 @@ impl Application {
.into_iter()
.collect();
Self {
let mut app = Self {
receiver,
sender,
#[cfg(not(android_platform))]
@@ -126,7 +130,16 @@ impl Application {
custom_cursors,
icon,
windows: Default::default(),
}
};
app.dump_monitors(event_loop);
// Create initial window.
app.create_window(event_loop, None).expect("failed to create initial window");
app.print_help();
app
}
fn create_window(
@@ -149,6 +162,28 @@ impl Application {
window_attributes = window_attributes.with_activation_token(token);
}
#[cfg(x11_platform)]
match std::env::var("X11_VISUAL_ID") {
Ok(visual_id_str) => {
info!("Using X11 visual id {visual_id_str}");
let visual_id = visual_id_str.parse()?;
window_attributes = window_attributes.with_x11_visual(visual_id);
},
Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"),
}
#[cfg(x11_platform)]
match std::env::var("X11_SCREEN_ID") {
Ok(screen_id_str) => {
info!("Placing the window on X11 screen {screen_id_str}");
let screen_id = screen_id_str.parse()?;
window_attributes = window_attributes.with_x11_screen(screen_id);
},
Err(_) => info!(
"Set the X11_SCREEN_ID env variable to place the window on non-default screen"
),
}
#[cfg(macos_platform)]
if let Some(tab_id) = _tab_id {
window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
@@ -218,6 +253,10 @@ impl Application {
Action::ToggleResizable => window.toggle_resizable(),
Action::ToggleDecorations => window.toggle_decorations(),
Action::ToggleFullscreen => window.toggle_fullscreen(),
#[cfg(macos_platform)]
Action::ToggleSimpleFullscreen => {
window.window.set_simple_fullscreen(!window.window.simple_fullscreen());
},
Action::ToggleMaximize => window.toggle_maximize(),
Action::ToggleImeInput => window.toggle_ime(),
Action::Minimize => window.minimize(),
@@ -532,14 +571,12 @@ impl ApplicationHandler for Application {
info!("Device {device_id:?} event: {event:?}");
}
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
info!("Ready to create surfaces");
self.dump_monitors(event_loop);
// Create initial window.
self.create_window(event_loop, None).expect("failed to create initial window");
self.print_help();
#[cfg(not(android_platform))]
fn can_create_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {
for window in self.windows.values_mut() {
window.surface = Some(Surface::new(&self.context, Arc::clone(&window.window)).unwrap());
window.resize(window.window.surface_size());
}
}
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
@@ -550,9 +587,10 @@ impl ApplicationHandler for Application {
}
#[cfg(not(android_platform))]
fn exiting(&mut self, _event_loop: &dyn ActiveEventLoop) {
// We must drop the context here.
self.context = None;
fn destroy_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {
for window in self.windows.values_mut() {
window.surface = None;
}
}
#[cfg(target_os = "macos")]
@@ -579,9 +617,9 @@ struct WindowState {
ime: bool,
/// Render surface.
///
/// NOTE: This surface must be dropped before the `Window`.
/// `None` when not between `can_create_surfaces` and `destroy_surfaces`.
#[cfg(not(android_platform))]
surface: Surface<DisplayHandle<'static>, Arc<dyn Window>>,
surface: Option<Surface<DisplayHandle<'static>, Arc<dyn Window>>>,
/// The actual winit Window.
window: Arc<dyn Window>,
/// The window theme we're drawing with.
@@ -614,11 +652,6 @@ impl WindowState {
fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> {
let window: Arc<dyn Window> = Arc::from(window);
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
// it doesn't outlive it.
#[cfg(not(android_platform))]
let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
let theme = window.theme().unwrap_or(Theme::Dark);
info!("Theme: {theme:?}");
let named_idx = 0;
@@ -628,15 +661,14 @@ impl WindowState {
let ime = true;
window.set_ime_allowed(ime);
let size = window.surface_size();
let mut state = Self {
Ok(Self {
#[cfg(macos_platform)]
option_as_alt: window.option_as_alt(),
custom_idx: app.custom_cursors.as_ref().map(Vec::len).unwrap_or(1) - 1,
cursor_grab: CursorGrabMode::None,
named_idx,
#[cfg(not(android_platform))]
surface,
surface: None,
window,
theme,
ime,
@@ -647,10 +679,7 @@ impl WindowState {
rotated: Default::default(),
panned: Default::default(),
zoom: Default::default(),
};
state.resize(size);
Ok(state)
})
}
pub fn toggle_ime(&mut self) {
@@ -824,7 +853,11 @@ impl WindowState {
(Some(width), Some(height)) => (width, height),
_ => return,
};
self.surface.resize(width, height).expect("failed to resize inner buffer");
self.surface
.as_mut()
.unwrap()
.resize(width, height)
.expect("failed to resize inner buffer");
}
self.window.request_redraw();
}
@@ -917,18 +950,38 @@ impl WindowState {
return Ok(());
}
const WHITE: u32 = 0xffffffff;
const DARK_GRAY: u32 = 0xff181818;
let mut buffer = self.surface.as_mut().unwrap().buffer_mut()?;
let color = match self.theme {
Theme::Light => WHITE,
Theme::Dark => DARK_GRAY,
};
// Draw a different color inside the safe area
let surface_size = self.window.surface_size();
let insets = self.window.safe_area();
for y in 0..surface_size.height {
for x in 0..surface_size.width {
let index = y as usize * surface_size.width as usize + x as usize;
if insets.left <= x
&& x <= (surface_size.width - insets.right)
&& insets.top <= y
&& y <= (surface_size.height - insets.bottom)
{
// In safe area
buffer[index] = match self.theme {
Theme::Light => 0xffe8e8e8, // Light gray
Theme::Dark => 0xff525252, // Medium gray
};
} else {
// Outside safe area
buffer[index] = match self.theme {
Theme::Light => 0xffffffff, // White
Theme::Dark => 0xff181818, // Dark gray
};
}
}
}
let mut buffer = self.surface.buffer_mut()?;
buffer.fill(color);
// Present the buffer
self.window.pre_present_notify();
buffer.present()?;
Ok(())
}
@@ -965,6 +1018,8 @@ enum Action {
ToggleDecorations,
ToggleResizable,
ToggleFullscreen,
#[cfg(macos_platform)]
ToggleSimpleFullscreen,
ToggleMaximize,
Minimize,
NextCursor,
@@ -998,6 +1053,8 @@ impl Action {
Action::ToggleDecorations => "Toggle decorations",
Action::ToggleResizable => "Toggle window resizable state",
Action::ToggleFullscreen => "Toggle fullscreen",
#[cfg(macos_platform)]
Action::ToggleSimpleFullscreen => "Toggle simple fullscreen",
Action::ToggleMaximize => "Maximize",
Action::Minimize => "Minimize",
Action::ToggleResizeIncrements => "Use resize increments when resizing window",
@@ -1140,6 +1197,8 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
Binding::new("Q", ModifiersState::CONTROL, Action::CloseWindow),
Binding::new("H", ModifiersState::CONTROL, Action::PrintHelp),
Binding::new("F", ModifiersState::CONTROL, Action::ToggleFullscreen),
#[cfg(macos_platform)]
Binding::new("F", ModifiersState::ALT, Action::ToggleSimpleFullscreen),
Binding::new("D", ModifiersState::CONTROL, Action::ToggleDecorations),
Binding::new("I", ModifiersState::CONTROL, Action::ToggleImeInput),
Binding::new("L", ModifiersState::CONTROL, Action::CycleCursorGrab),

View File

@@ -13,39 +13,28 @@ fn main() -> Result<(), Box<dyn Error>> {
mod fill;
pub struct XEmbedDemo {
parent_window_id: u32,
window: Option<Box<dyn Window>>,
window: Box<dyn Window>,
}
impl ApplicationHandler for XEmbedDemo {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = WindowAttributes::default()
.with_title("An embedded window!")
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(self.parent_window_id);
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let window = self.window.as_ref().unwrap();
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
window.pre_present_notify();
fill::fill_window(window.as_ref());
self.window.pre_present_notify();
fill::fill_window(&*self.window);
},
_ => (),
}
}
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
self.window.as_ref().unwrap().request_redraw();
self.window.request_redraw();
}
}
@@ -58,7 +47,15 @@ fn main() -> Result<(), Box<dyn Error>> {
tracing_subscriber::fmt::init();
let event_loop = EventLoop::new()?;
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?)
Ok(event_loop.run(|event_loop| {
let window_attributes = WindowAttributes::default()
.with_title("An embedded window!")
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(parent_window_id);
let window = event_loop.create_window(window_attributes).unwrap();
XEmbedDemo { window }
})?)
}
#[cfg(not(x11_platform))]

View File

@@ -7,6 +7,8 @@ use crate::platform::macos::ApplicationHandlerExtMacOS;
use crate::window::WindowId;
/// The handler of the application events.
///
/// This is [dropped][std::ops::Drop] when the event loop is being shut down.
pub trait ApplicationHandler {
/// Emitted when new events arrive from the OS to be processed.
///
@@ -101,7 +103,9 @@ pub trait ApplicationHandler {
///
/// [`can_create_surfaces()`]: Self::can_create_surfaces()
/// [`destroy_surfaces()`]: Self::destroy_surfaces()
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop);
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Called after a wake up is requested using [`EventLoopProxy::wake_up()`].
///
@@ -142,8 +146,6 @@ pub trait ApplicationHandler {
/// # ) {
/// # }
/// #
/// # fn can_create_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {}
/// #
/// fn proxy_wake_up(&mut self, _event_loop: &dyn ActiveEventLoop) {
/// // Iterate current events, since wake-ups may have been merged.
/// //
@@ -162,8 +164,6 @@ pub trait ApplicationHandler {
///
/// let (sender, receiver) = mpsc::channel();
///
/// let mut app = MyApp { receiver };
///
/// // Send an event in a loop
/// let proxy = event_loop.create_proxy();
/// let background_thread = thread::spawn(move || {
@@ -182,9 +182,8 @@ pub trait ApplicationHandler {
/// }
/// });
///
/// event_loop.run_app(&mut app)?;
/// event_loop.run(|_event_loop| MyApp { receiver })?;
///
/// drop(app);
/// background_thread.join().unwrap();
///
/// Ok(())
@@ -286,13 +285,13 @@ pub trait ApplicationHandler {
/// with the [`onStop`] lifecycle event which typically results in the surface to be destroyed
/// after the app becomes invisible.
///
/// Applications that need to run on Android should assume their [`NativeWindow`] has been
/// destroyed, which indirectly invalidates any existing render surfaces that may have been
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
/// Applications that need to run on Android must be able to handle their underlying
/// [`SurfaceView`] being destroyed, which in turn indirectly invalidates any existing
/// render surfaces that may have been created outside of Winit (such as an `EGLSurface`,
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]).
///
/// When receiving [`destroy_surfaces()`] Android applications should drop all render surfaces
/// before the event callback completes, which may be re-created when the application next
/// receives [`can_create_surfaces()`].
/// This means that in this method, you must drop all render surfaces before the event callback
/// completes, and only re-create them in or after [`can_create_surfaces()`] is next recieved.
///
/// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window
/// [`Surface`]: https://developer.android.com/reference/android/view/Surface
@@ -310,14 +309,6 @@ pub trait ApplicationHandler {
let _ = event_loop;
}
/// Emitted when the event loop is being shut down.
///
/// This is irreversible - if this method is called, it is guaranteed that the event loop
/// will exit right after.
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the application has received a memory warning.
///
/// ## Platform-specific
@@ -413,11 +404,6 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
(**self).destroy_surfaces(event_loop);
}
#[inline]
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop);
@@ -487,11 +473,6 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
(**self).destroy_surfaces(event_loop);
}
#[inline]
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop);

View File

@@ -59,15 +59,24 @@ changelog entry.
- Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd`
and `Serialize` on many types.
- Add `MonitorHandle::current_video_mode()`.
- On Android, the soft keyboard can now be shown using `Window::set_ime_allowed`.
- Add basic iOS IME support. The soft keyboard can now be shown using `Window::set_ime_allowed`.
- Add `ApplicationHandlerExtMacOS` trait, and a `macos_handler` method to `ApplicationHandler` which returns a `dyn ApplicationHandlerExtMacOS` which allows for macOS specific extensions to winit.
- Add a `standard_key_binding` method to the `ApplicationHandlerExtMacOS` trait. This allows handling of standard keybindings such as "go to end of line" on macOS.
- On macOS, add `WindowExtMacOS::set_borderless_game` and `WindowAttributesExtMacOS::with_borderless_game`
to fully disable the menu bar and dock in Borderless Fullscreen as commonly done in games.
- On macOS, add `WindowExtMacOS::set_unified_titlebar` and `WindowAttributesExtMacOS::with_unified_titlebar`
to use a larger style of titlebar.
- Add `WindowId::into_raw()` and `from_raw()`.
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId` and `position` to all pointer
events as part of the pointer event overhaul.
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId`, `primary` and `position` to all
pointer events as part of the pointer event overhaul.
- Add `DeviceId::into_raw()` and `from_raw()`.
- On X11, the `window` example now understands the `X11_VISUAL_ID` and `X11_SCREEN_ID` env
variables to test the respective modifiers of window creation.
- Added `Window::surface_position`, which is the position of the surface inside the window.
- Added `Window::safe_area`, which describes the area of the surface that is unobstructed.
- On X11 and Wayland, improved scancode conversions for more obscure key codes.
- On Windows, improved scancode conversions for more obscure key codes.
### Changed
@@ -136,6 +145,8 @@ changelog entry.
- Rename `CursorEntered` to `PointerEntered`.
- Rename `CursorLeft` to `PointerLeft`.
- Rename `MouseInput` to `PointerButton`.
- Add `primary` to every `PointerEvent` as a way to identify discard non-primary pointers in a
multi-touch interaction.
- Add `position` to every `PointerEvent`.
- `PointerMoved` is **not sent** after `PointerEntered` anymore.
- Remove `Touch`, which is folded into the `Pointer*` events.
@@ -148,10 +159,11 @@ changelog entry.
type to a generic mouse button.
- New `FingerId` added to `PointerKind::Touch` and `PointerSource::Touch` able to uniquely
identify a finger in a multi-touch interaction. Replaces the old `Touch::id`.
- On Web and Windows, add `FingerIdExt*::is_primary()`, exposing a way to determine
the primary finger in a multi-touch interaction.
- In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`.
- Remove `Force::Calibrated::altitude_angle`.
- On X11, use bottom-right corner for IME hotspot in `Window::set_ime_cursor_area`.
- On macOS and iOS, no longer emit `ScaleFactorChanged` upon window creation.
- On macOS, no longer emit `Focused` upon window creation.
### Removed
@@ -183,6 +195,7 @@ changelog entry.
- Remove `WindowEvent::Touch` and `Touch` in favor of the new `PointerKind`, `PointerSource` and
`ButtonSource` as part of the new pointer event overhaul.
- Remove `Force::altitude_angle`.
- Removed `Window::inner_position`, use the new `Window::surface_position` instead.
### Fixed
@@ -192,3 +205,8 @@ changelog entry.
default activation policy, unless explicitly provided during initialization.
- On macOS, fix crash when calling `drag_window()` without a left click present.
- On X11, key events forward to IME anyway, even when it's disabled.
- On Windows, make `ControlFlow::WaitUntil` work more precisely using `CREATE_WAITABLE_TIMER_HIGH_RESOLUTION`.
- On X11, creating windows on screen that is not the first one (e.g. `DISPLAY=:0.1`) works again.
- On X11, creating windows while passing `with_x11_screen(non_default_screen)` works again.
- On X11, fix XInput handling that prevented a new window from getting the focus in some cases.
- On iOS, fixed `SurfaceResized` and `Window::surface_size` not reporting the size of the actual surface.

View File

@@ -1,39 +1,4 @@
//! The event enums and assorted supporting types.
//!
//! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get
//! processed and used to modify the program state. For more details, see the root-level
//! documentation.
//!
//! Some of these events represent different "parts" of a traditional event-handling loop. You could
//! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this:
//!
//! ```rust,ignore
//! let mut start_cause = StartCause::Init;
//!
//! while !elwt.exiting() {
//! app.new_events(event_loop, start_cause);
//!
//! for event in (window events, user events, device events) {
//! // This will pick the right method on the application based on the event.
//! app.handle_event(event_loop, event);
//! }
//!
//! for window_id in (redraw windows) {
//! app.window_event(event_loop, window_id, RedrawRequested);
//! }
//!
//! app.about_to_wait(event_loop);
//! start_cause = wait_if_necessary();
//! }
//!
//! app.exiting(event_loop);
//! ```
//!
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
//! describes what happens in what order.
//!
//! [`EventLoop::run_app(...)`]: crate::event_loop::EventLoop::run_app
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use std::path::PathBuf;
use std::sync::{Mutex, Weak};
#[cfg(not(web_platform))]
@@ -85,11 +50,6 @@ pub(crate) enum Event {
/// [`ApplicationHandler::suspended()`]: crate::application::ApplicationHandler::suspended()
Suspended,
/// See [`ApplicationHandler::can_create_surfaces()`] for details.
///
/// [`ApplicationHandler::can_create_surfaces()`]: crate::application::ApplicationHandler::can_create_surfaces()
CreateSurfaces,
/// See [`ApplicationHandler::resumed()`] for details.
///
/// [`ApplicationHandler::resumed()`]: crate::application::ApplicationHandler::resumed()
@@ -100,11 +60,6 @@ pub(crate) enum Event {
/// [`ApplicationHandler::about_to_wait()`]: crate::application::ApplicationHandler::about_to_wait()
AboutToWait,
/// See [`ApplicationHandler::exiting()`] for details.
///
/// [`ApplicationHandler::exiting()`]: crate::application::ApplicationHandler::exiting()
LoopExiting,
/// See [`ApplicationHandler::memory_warning()`] for details.
///
/// [`ApplicationHandler::memory_warning()`]: crate::application::ApplicationHandler::memory_warning()
@@ -153,10 +108,16 @@ pub enum WindowEvent {
/// Contains the new dimensions of the surface (can also be retrieved with
/// [`Window::surface_size`]).
///
/// This event will not necessarily be emitted upon window creation, query
/// [`Window::surface_size`] if you need to determine the surface's initial size.
///
/// [`Window::surface_size`]: crate::window::Window::surface_size
SurfaceResized(PhysicalSize<u32>),
/// The position of the window has changed. Contains the window's new position.
/// The position of the window has changed.
///
/// Contains the window's new position in desktop coordinates (can also be retrieved with
/// [`Window::outer_position`]).
///
/// ## Platform-specific
///
@@ -190,6 +151,9 @@ pub enum WindowEvent {
/// The window gained or lost focus.
///
/// The parameter is true if the window has gained focus, and false if it has lost focus.
///
/// Windows are unfocused upon creation, but will usually be focused by the system soon
/// afterwards.
Focused(bool),
/// An event from the keyboard has been received.
@@ -245,6 +209,12 @@ pub enum WindowEvent {
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: PhysicalPosition<f64>,
/// Indicates whether the event is created by a primary pointer.
///
/// A pointer is considered primary when it's a mouse, the first finger in a multi-touch
/// interaction, or an unknown pointer source.
primary: bool,
source: PointerSource,
},
@@ -264,6 +234,12 @@ pub enum WindowEvent {
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: PhysicalPosition<f64>,
/// Indicates whether the event is created by a primary pointer.
///
/// A pointer is considered primary when it's a mouse, the first finger in a multi-touch
/// interaction, or an unknown pointer source.
primary: bool,
kind: PointerKind,
},
@@ -284,6 +260,12 @@ pub enum WindowEvent {
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: Option<PhysicalPosition<f64>>,
/// Indicates whether the event is created by a primary pointer.
///
/// A pointer is considered primary when it's a mouse, the first finger in a multi-touch
/// interaction, or an unknown pointer source.
primary: bool,
kind: PointerKind,
},
@@ -307,6 +289,12 @@ pub enum WindowEvent {
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: PhysicalPosition<f64>,
/// Indicates whether the event is created by a primary pointer.
///
/// A pointer is considered primary when it's a mouse, the first finger in a multi-touch
/// interaction, or an unknown pointer source.
primary: bool,
button: ButtonSource,
},
@@ -393,7 +381,12 @@ pub enum WindowEvent {
/// To update the window size, use the provided [`SurfaceSizeWriter`] handle. By default, the
/// window is resized to the value suggested by the OS, but it can be changed to any value.
///
/// This event will not necessarily be emitted upon window creation, query
/// [`Window::scale_factor`] if you need to determine the window's initial scale factor.
///
/// For more information about DPI in general, see the [`dpi`] crate.
///
/// [`Window::scale_factor`]: crate::window::Window::scale_factor
ScaleFactorChanged {
scale_factor: f64,
/// Handle to update surface size during scale changes.
@@ -445,13 +438,15 @@ pub enum WindowEvent {
/// Emitted when a window should be redrawn.
///
/// This gets triggered in two scenarios:
/// This gets triggered in a few scenarios:
/// - The OS has performed an operation that's invalidated the window's contents (such as
/// resizing the window).
/// resizing the window, or changing [the safe area]).
/// - The application has explicitly requested a redraw via [`Window::request_redraw`].
///
/// Winit will aggregate duplicate redraw requests into a single event, to
/// help avoid duplicating rendering work.
///
/// [the safe area]: crate::window::Window::safe_area
RedrawRequested,
}
@@ -610,12 +605,23 @@ impl DeviceId {
/// Whenever a touch event is received it contains a `FingerId` which uniquely identifies the finger
/// used for the current interaction.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId(pub(crate) platform_impl::FingerId);
pub struct FingerId(pub(crate) usize);
impl FingerId {
#[cfg(test)]
pub(crate) const fn dummy() -> Self {
FingerId(platform_impl::FingerId::dummy())
/// Convert the [`FingerId`] into the underlying integer.
///
/// This is useful if you need to pass the ID across an FFI boundary, or store it in an atomic.
#[allow(dead_code)]
pub(crate) const fn into_raw(self) -> usize {
self.0
}
/// Construct a [`FingerId`] from the underlying integer.
///
/// This should only be called with integers returned from [`FingerId::into_raw`].
#[allow(dead_code)]
pub(crate) const fn from_raw(id: usize) -> Self {
Self(id)
}
}
@@ -1130,7 +1136,7 @@ mod tests {
($closure:expr) => {{
#[allow(unused_mut)]
let mut x = $closure;
let fid = event::FingerId::dummy();
let fid = event::FingerId::from_raw(0);
#[allow(deprecated)]
{
@@ -1144,7 +1150,6 @@ mod tests {
let wid = WindowId::from_raw(0);
x(NewEvents(event::StartCause::Init));
x(AboutToWait);
x(LoopExiting);
x(Suspended);
x(Resumed);
@@ -1162,16 +1167,19 @@ mod tests {
with_window_event(Ime(Enabled));
with_window_event(PointerMoved {
device_id: None,
primary: true,
position: (0, 0).into(),
source: PointerSource::Mouse,
});
with_window_event(ModifiersChanged(event::Modifiers::default()));
with_window_event(PointerEntered {
device_id: None,
primary: true,
position: (0, 0).into(),
kind: PointerKind::Mouse,
});
with_window_event(PointerLeft {
primary: true,
device_id: None,
position: Some((0, 0).into()),
kind: PointerKind::Mouse,
@@ -1183,12 +1191,14 @@ mod tests {
});
with_window_event(PointerButton {
device_id: None,
primary: true,
state: event::ElementState::Pressed,
position: (0, 0).into(),
button: event::MouseButton::Other(0).into(),
});
with_window_event(PointerButton {
device_id: None,
primary: true,
state: event::ElementState::Released,
position: (0, 0).into(),
button: event::ButtonSource::Touch {
@@ -1258,11 +1268,11 @@ mod tests {
#[test]
fn ensure_attrs_do_not_panic() {
foreach_event!(|event: event::Event| {
let _ = format!("{:?}", event);
let _ = format!("{event:?}");
});
let _ = event::StartCause::Init.clone();
let fid = crate::event::FingerId::dummy().clone();
let fid = crate::event::FingerId::from_raw(0).clone();
HashSet::new().insert(fid);
let mut set = [fid, fid, fid];
set.sort_unstable();

View File

@@ -13,9 +13,11 @@ use std::marker::PhantomData;
#[cfg(any(x11_platform, wayland_platform))]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
#[cfg(not(web_platform))]
use std::time::{Duration, Instant};
use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
#[cfg(web_platform)]
use web_time::{Duration, Instant};
@@ -193,10 +195,48 @@ impl EventLoop {
}
impl EventLoop {
/// Run the application with the event loop on the calling thread.
/// Run the event loop on the current thread.
///
/// You pass in a closure that returns your application state. This closure has access to the
/// currently running event loop, allowing you to initialize your windows and surfaces in here.
///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
///
/// ## Event loop flow
///
/// This function internally handles the different parts of a traditional event-handling loop.
/// You could imagine this method being implemented like this:
///
/// ```rust,ignore
/// // Initialize.
/// let mut app = init_closure(event_loop);
/// let mut start_cause = StartCause::Init;
///
/// // Run loop.
/// while !elwt.exiting() {
/// // Wake up.
/// app.new_events(event_loop, start_cause);
///
/// // Handle events by the user.
/// for (device_id, event) in incoming_device_events {
/// app.device_event(event_loop, device_id, event);
/// }
/// for (window_id, event) in incoming_window_events {
/// app.window_event(event_loop, window_id, event);
/// }
///
/// // Done handling events, wait until we're woken up again.
/// app.about_to_wait(event_loop);
/// start_cause = wait_if_necessary();
/// }
///
/// // Finished running, drop application state.
/// drop(app);
/// ```
///
/// This is of course a very coarse-grained overview, and leaves out timing details like
/// [`ControlFlow::WaitUntil`] and life-cycle methods like [`ApplicationHandler::resumed`].
///
/// ## Platform-specific
///
/// - **iOS:** Will never return to the caller and so values not passed to this function will
@@ -211,7 +251,7 @@ impl EventLoop {
doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
)]
#[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")]
/// [^1] instead of [`run_app()`] to avoid the need for the Javascript exception trick, and to
/// [^1] instead of [`run()`] to avoid the need for the Javascript exception trick, and to
/// make it clearer that the event loop runs asynchronously (via the browser's own,
/// internal, event loop) and doesn't block the current thread of execution like it does
/// on other platforms.
@@ -221,11 +261,20 @@ impl EventLoop {
/// [^1]: `spawn_app()` is only available on the Web platform.
///
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`run_app()`]: Self::run_app()
/// [`run()`]: Self::run()
#[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run<A: ApplicationHandler>(
self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.event_loop.run(init_closure)
}
/// Run the event loop with the given application state.
#[deprecated = "less flexible version of `run`"]
pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
self.event_loop.run_app(app)
self.run(|_event_loop| app)
}
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
@@ -273,10 +322,9 @@ impl EventLoop {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for EventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
impl HasDisplayHandle for EventLoop {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
}
}
@@ -391,9 +439,7 @@ pub trait ActiveEventLoop: AsAny {
/// Gets the current [`ControlFlow`].
fn control_flow(&self) -> ControlFlow;
/// This exits the event loop.
///
/// See [`exiting`][crate::application::ApplicationHandler::exiting].
/// This stops the event loop.
fn exit(&self);
/// Returns if the [`EventLoop`] is about to stop.
@@ -407,13 +453,11 @@ pub trait ActiveEventLoop: AsAny {
fn owned_display_handle(&self) -> OwnedDisplayHandle;
/// Get the raw-window-handle handle.
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
fn rwh_06_handle(&self) -> &dyn HasDisplayHandle;
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
impl HasDisplayHandle for dyn ActiveEventLoop + '_ {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.rwh_06_handle().display_handle()
}
}
@@ -430,36 +474,55 @@ impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
///
/// - A zero-sized type that is likely optimized out.
/// - A reference-counted pointer to the underlying type.
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone)]
pub struct OwnedDisplayHandle {
#[cfg_attr(not(feature = "rwh_06"), allow(dead_code))]
pub(crate) platform: platform_impl::OwnedDisplayHandle,
pub(crate) handle: Arc<dyn HasDisplayHandle>,
}
impl OwnedDisplayHandle {
pub(crate) fn new(handle: Arc<dyn HasDisplayHandle>) -> Self {
Self { handle }
}
}
impl HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.handle.display_handle()
}
}
impl fmt::Debug for OwnedDisplayHandle {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
#[inline]
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.platform.raw_display_handle_rwh_06()?;
// SAFETY: The underlying display handle should be safe.
let handle = unsafe { rwh_06::DisplayHandle::borrow_raw(raw) };
Ok(handle)
impl PartialEq for OwnedDisplayHandle {
fn eq(&self, other: &Self) -> bool {
match (self.display_handle(), other.display_handle()) {
(Ok(lhs), Ok(rhs)) => lhs == rhs,
_ => false,
}
}
}
impl Eq for OwnedDisplayHandle {}
pub(crate) trait EventLoopProxyProvider: Send + Sync {
/// See [`EventLoopProxy::wake_up`] for details.
fn wake_up(&self);
}
/// Control the [`EventLoop`], possibly from a different thread, without referencing it directly.
#[derive(Clone)]
pub struct EventLoopProxy {
pub(crate) event_loop_proxy: platform_impl::EventLoopProxy,
pub(crate) proxy: Arc<dyn EventLoopProxyProvider>,
}
impl fmt::Debug for EventLoopProxy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopProxy").finish_non_exhaustive()
}
}
impl EventLoopProxy {
@@ -479,13 +542,11 @@ impl EventLoopProxy {
///
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
pub fn wake_up(&self) {
self.event_loop_proxy.wake_up();
self.proxy.wake_up();
}
}
impl fmt::Debug for EventLoopProxy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ActiveEventLoop").finish_non_exhaustive()
pub(crate) fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self {
Self { proxy }
}
}

View File

@@ -1,19 +1,14 @@
//! Winit is a cross-platform window creation and event loop management library.
//!
//! # Building windows
//!
//! Before you can create a [`Window`], you first need to build an [`EventLoop`]. This is done with
//! the [`EventLoop::new()`] function.
//!
//! ```no_run
//! use winit::event_loop::EventLoop;
//! let event_loop = EventLoop::new().unwrap();
//! ```
//!
//! Then you create a [`Window`] with [`create_window`].
//!
//! # Event handling
//!
//! Basically all of the functionality that Winit exposes requires an [`ActiveEventLoop`], which you
//! can get access to by running an [`EventLoop`] using [`EventLoop::new()`] and
//! [`EventLoop::run()`].
//!
//! Once it's running, you can create your [`Window`]s with [`ActiveEventLoop::create_window()`] by
//! passing in the desired [`WindowAttributes`].
//!
//! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can
//! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the
//! window or a key getting pressed while the window is focused. Devices can generate
@@ -21,9 +16,10 @@
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`].
//!
//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
//! will run until [`exit()`] is used, at which point [`exiting()`] is called.
//! You retrieve by implementing [`ApplicationHandler`] for a new type, which will be the state of
//! your application. The methods in this trait will continuously receive events until
//! [`ActiveEventLoop::exit()`] is used, at which point your application state will be dropped, and
//! the application shuts down.
//!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
//! model, since that can't be implemented properly on some platforms (e.g Web, iOS) and works
@@ -44,19 +40,20 @@
//! use winit::application::ApplicationHandler;
//! use winit::event::WindowEvent;
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
//! use winit::window::{Window, WindowId, WindowAttributes};
//! use winit::window::{Window, WindowId};
//!
//! #[derive(Default)]
//! struct App {
//! window: Option<Box<dyn Window>>,
//! window: Box<dyn Window>,
//! }
//!
//! impl ApplicationHandler for App {
//! fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
//! self.window = Some(event_loop.create_window(WindowAttributes::default()).unwrap());
//! }
//!
//! fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, id: WindowId, event: WindowEvent) {
//! fn window_event(
//! &mut self,
//! event_loop: &dyn ActiveEventLoop,
//! id: WindowId,
//! event: WindowEvent,
//! ) {
//! // Called by `EventLoop::run` when a new event happens on the window.
//! match event {
//! WindowEvent::CloseRequested => {
//! println!("The close button was pressed; stopping");
@@ -76,26 +73,45 @@
//! // You only need to call this if you've determined that you need to redraw in
//! // applications which do not always need to. Applications that redraw continuously
//! // can render here instead.
//! self.window.as_ref().unwrap().request_redraw();
//! }
//! self.window.request_redraw();
//! },
//! _ => (),
//! }
//! }
//! }
//!
//! let event_loop = EventLoop::new().unwrap();
//! # // Intentionally use `fn main` for clarity
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Create a new event loop.
//! let event_loop = EventLoop::new()?;
//!
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications.
//! event_loop.set_control_flow(ControlFlow::Poll);
//! // Configure settings before launching.
//!
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
//! // This is ideal for non-game applications that only update in response to user
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! event_loop.set_control_flow(ControlFlow::Wait);
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications.
//! event_loop.set_control_flow(ControlFlow::Poll);
//!
//! let mut app = App::default();
//! event_loop.run_app(&mut app);
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
//! // This is ideal for non-game applications that only update in response to user
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! event_loop.set_control_flow(ControlFlow::Wait);
//!
//! // Launch and begin running our event loop.
//! event_loop.run(|event_loop| {
//! // The event loop has launched, and we can initialize our UI state in this closure.
//!
//! // Create a simple window with default attributes.
//! let window = event_loop
//! .create_window(Window::default_attributes())
//! .expect("failed creating window");
//!
//! // Give our newly created application state to Winit, which will, when necessary, call
//! // the `ApplicationHandler` methods configured above.
//! App { window }
//! })?;
//!
//! Ok(())
//! }
//! ```
//!
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
@@ -115,6 +131,45 @@
//! [`visible` set to `false`][crate::window::WindowAttributes::with_visible] and explicitly make
//! the window visible only once you're ready to render into it.
//!
//! There is another important concept you need to know about when drawing: the "safe area". This
//! can be accessed with [`Window::safe_area`], and describes a rectangle in the surface that is not
//! obscured by notches, the status bar, and so on. You should be drawing your background and
//! non-important content on the entire surface, but restrict important content (such as
//! interactable UIs, text, etc.) to only being drawn inside the safe area.
//!
//! [`Window::safe_area`]: crate::window::Window::safe_area
//!
//! # Coordinate systems
//!
//! Windowing systems use many different coordinate systems, and this is reflected in Winit as well;
//! there are "desktop coordinates", which is the coordinates of a window or monitor relative to the
//! desktop at large, "window coordinates" which is the coordinates of the surface, relative to the
//! window, and finally "surface coordinates", which is the coordinates relative to the drawn
//! surface. All of these coordinates are relative to the top-left corner of their respective
//! origin.
//!
//! Most of the functionality in Winit works with surface coordinates, so usually you only need to
//! concern yourself with those. In case you need to convert to some other coordinate system, Winit
//! provides [`Window::surface_position`] and [`Window::surface_size`] to describe the surface's
//! location in window coordinates, and Winit provides [`Window::outer_position`] and
//! [`Window::outer_size`] to describe the window's location in desktop coordinates. Using these
//! methods, you should be able to convert a position in one coordinate system to another.
//!
//! An overview of how these four methods fit together can be seen in the image below:
#![doc = concat!("\n\n", include_str!("../docs/res/coordinate-systems-desktop.svg"), "\n\n")] // Rustfmt removes \n, adding them like this works around that.
//! On mobile, the situation is usually a bit different; because of the smaller screen space,
//! windows usually fill the whole screen at a time, and as such there is _rarely_ a difference
//! between these three coordinate systems, although you should still strive to handle this, as
//! they're still relevant in more niche area such as Mac Catalyst, or multi-tasking on tablets.
//!
//! This is illustrated in the image below, along with the safe area since it's often relevant on
//! mobile.
#![doc = concat!("\n\n", include_str!("../docs/res/coordinate-systems-mobile.svg"), "\n\n")] // Rustfmt removes \n, adding them like this works around that.
//! [`Window::surface_position`]: crate::window::Window::surface_position
//! [`Window::surface_size`]: crate::window::Window::surface_size
//! [`Window::outer_position`]: crate::window::Window::outer_position
//! [`Window::outer_size`]: crate::window::Window::outer_size
//!
//! # UI scaling
//!
//! UI scaling is important, go read the docs for the [`dpi`] crate for an
@@ -203,20 +258,21 @@
//! |32-bit ARM Android |`arm-linux-androideabi` |Android |
//! |64-bit SPARC Linux with glibc |`sparc64-unknown-linux-gnu` |X11, Wayland |
//!
//! [`ActiveEventLoop`]: event_loop::ActiveEventLoop
//! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app
//! [`exit()`]: event_loop::ActiveEventLoop::exit
//! [`EventLoop::run()`]: event_loop::EventLoop::run
//! [`ActiveEventLoop::exit()`]: event_loop::ActiveEventLoop::exit
//! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId
//! [`WindowAttributes`]: window::WindowAttributes
//! [window_new]: window::Window::new
//! [`create_window`]: event_loop::ActiveEventLoop::create_window
//! [`ActiveEventLoop::create_window()`]: event_loop::ActiveEventLoop::create_window
//! [`Window::id()`]: window::Window::id
//! [`WindowEvent`]: event::WindowEvent
//! [`DeviceEvent`]: event::DeviceEvent
//! [`ApplicationHandler`]: application::ApplicationHandler
//! [`Event::UserEvent`]: event::Event::UserEvent
//! [`exiting()`]: crate::application::ApplicationHandler::exiting
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland.
@@ -230,11 +286,11 @@
// doc
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
#![allow(clippy::missing_safety_doc)]
#![warn(clippy::uninlined_format_args)]
// Re-export DPI types so that users don't have to put it in Cargo.toml.
#[doc(inline)]
pub use dpi;
#[cfg(feature = "rwh_06")]
pub use rwh_06 as raw_window_handle;
pub mod application;

View File

@@ -141,8 +141,11 @@ impl MonitorHandle {
self.inner.name()
}
/// Returns the top-left corner position of the monitor relative to the larger full
/// screen area.
/// Returns the top-left corner position of the monitor in desktop coordinates.
///
/// This position is in the same coordinate system as [`Window::outer_position`].
///
/// [`Window::outer_position`]: crate::window::Window::outer_position
///
/// ## Platform-specific
///

View File

@@ -65,7 +65,7 @@
//! let app = NSApplication::sharedApplication(mtm);
//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
//!
//! // event_loop.run_app(&mut my_app);
//! // event_loop.run(|event_loop| { ... })?;
//! Ok(())
//! }
//! ```
@@ -92,6 +92,9 @@ pub trait WindowExtMacOS {
/// This is how fullscreen used to work on macOS in versions before Lion.
/// And allows the user to have a fullscreen window without using another
/// space or taking control over the entire monitor.
///
/// Make sure you only draw your important content inside the safe area so that it does not
/// overlap with the notch on newer devices, see [`Window::safe_area`] for details.
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool;
/// Returns whether or not the window has shadow.
@@ -157,6 +160,13 @@ pub trait WindowExtMacOS {
/// Getter for the [`WindowExtMacOS::set_borderless_game`].
fn is_borderless_game(&self) -> bool;
/// Makes the titlebar bigger, effectively adding more space around the
/// window controls if the titlebar is invisible.
fn set_unified_titlebar(&self, unified_titlebar: bool);
/// Getter for the [`WindowExtMacOS::set_unified_titlebar`].
fn unified_titlebar(&self) -> bool;
}
impl WindowExtMacOS for dyn Window + '_ {
@@ -255,6 +265,18 @@ impl WindowExtMacOS for dyn Window + '_ {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.is_borderless_game())
}
#[inline]
fn set_unified_titlebar(&self, unified_titlebar: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.set_unified_titlebar(unified_titlebar))
}
#[inline]
fn unified_titlebar(&self) -> bool {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.unified_titlebar())
}
}
/// Corresponds to `NSApplicationActivationPolicy`.
@@ -308,6 +330,8 @@ pub trait WindowAttributesExtMacOS {
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
/// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
fn with_borderless_game(self, borderless_game: bool) -> Self;
/// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set.
fn with_unified_titlebar(self, unified_titlebar: bool) -> Self;
}
impl WindowAttributesExtMacOS for WindowAttributes {
@@ -382,6 +406,12 @@ impl WindowAttributesExtMacOS for WindowAttributes {
self.platform_specific.borderless_game = borderless_game;
self
}
#[inline]
fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self {
self.platform_specific.unified_titlebar = unified_titlebar;
self
}
}
pub trait EventLoopBuilderExtMacOS {

View File

@@ -21,6 +21,7 @@ pub mod windows;
#[cfg(any(x11_platform, docsrs))]
pub mod x11;
#[allow(unused_imports)]
#[cfg(any(
windows_platform,
macos_platform,

View File

@@ -1,16 +1,14 @@
use crate::application::ApplicationHandler;
use crate::error::EventLoopError;
use crate::event_loop::EventLoop;
use crate::event_loop::{ActiveEventLoop, EventLoop};
#[cfg(doc)]
use crate::{
event_loop::ActiveEventLoop, platform::pump_events::EventLoopExtPumpEvents, window::Window,
};
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
/// Additional methods on [`EventLoop`] to return control flow to the caller.
pub trait EventLoopExtRunOnDemand {
/// Run the application with the event loop on the calling thread.
///
/// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`)
/// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`)
/// closures and it is possible to return control back to the caller without
/// consuming the `EventLoop` (by using [`exit()`]) and
/// so the event loop can be re-run after it has exit.
@@ -23,7 +21,7 @@ pub trait EventLoopExtRunOnDemand {
/// to while maintaining the full state of your application. (If you need something like this
/// you can look at the [`EventLoopExtPumpEvents::pump_app_events()`] API)
///
/// Each time `run_app_on_demand` is called the startup sequence of `init`, followed by
/// Each time `run_on_demand` is called the startup sequence of `init`, followed by
/// `resume` is being preserved.
///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
@@ -40,7 +38,7 @@ pub trait EventLoopExtRunOnDemand {
/// [^1] more than once instead).
/// - No [`Window`] state can be carried between separate runs of the event loop.
///
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you
/// You are strongly encouraged to use [`EventLoop::run()`] for portability, unless you
/// specifically need the ability to re-run a single event loop more than once
///
/// # Supported Platforms
@@ -60,12 +58,18 @@ pub trait EventLoopExtRunOnDemand {
///
/// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>;
fn run_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError>;
}
impl EventLoopExtRunOnDemand for EventLoop {
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError> {
self.event_loop.run_app_on_demand(app)
fn run_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.event_loop.run_on_demand(init_closure)
}
}

View File

@@ -57,7 +57,6 @@ use web_sys::HtmlCanvasElement;
use crate::application::ApplicationHandler;
use crate::cursor::CustomCursorSource;
use crate::error::NotSupportedError;
use crate::event::FingerId;
use crate::event_loop::{ActiveEventLoop, EventLoop};
use crate::monitor::MonitorHandle;
use crate::platform_impl::PlatformCustomCursorSource;
@@ -191,10 +190,10 @@ pub trait EventLoopExtWeb {
/// Initializes the winit event loop.
///
/// Unlike
#[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run_app()`")]
#[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run()`")]
#[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run_app()`]"
doc = "[`run()`]"
)]
/// [^1], this returns immediately, and doesn't throw an exception in order to
/// satisfy its [`!`] return type.
@@ -207,9 +206,9 @@ pub trait EventLoopExtWeb {
///
#[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run_app()`]: EventLoop::run_app()"
doc = "[`run()`]: EventLoop::run()"
)]
/// [^1]: `run_app()` is _not_ available on Wasm when the target supports `exception-handling`.
/// [^1]: `run()` is _not_ available on Wasm when the target supports `exception-handling`.
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A);
/// Sets the strategy for [`ControlFlow::Poll`].
@@ -780,16 +779,3 @@ impl Display for OrientationLockError {
}
impl Error for OrientationLockError {}
/// Additional methods on [`FingerId`] that are specific to Web.
pub trait FingerIdExtWeb {
/// Indicates if the finger represents the first contact in a multi-touch interaction.
#[allow(clippy::wrong_self_convention)]
fn is_primary(self) -> bool;
}
impl FingerIdExtWeb for FingerId {
fn is_primary(self) -> bool {
self.0.is_primary()
}
}

View File

@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
use windows_sys::Win32::Foundation::HANDLE;
use crate::dpi::PhysicalSize;
use crate::event::{DeviceId, FingerId};
use crate::event::DeviceId;
use crate::event_loop::EventLoopBuilder;
use crate::monitor::MonitorHandle;
use crate::window::{BadIcon, Icon, Window, WindowAttributes};
@@ -135,7 +135,6 @@ impl<W: Window> Deref for AnyThread<W> {
}
}
#[cfg(feature = "rwh_06")]
impl<W: Window> rwh_06::HasWindowHandle for AnyThread<W> {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
// SAFETY: The top level user has asserted this is only used safely.
@@ -337,7 +336,6 @@ pub trait WindowExtWindows {
/// });
/// # }
/// ```
#[cfg(feature = "rwh_06")]
unsafe fn window_handle_any_thread(
&self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>;
@@ -401,7 +399,6 @@ impl WindowExtWindows for dyn Window + '_ {
window.set_corner_preference(preference)
}
#[cfg(feature = "rwh_06")]
unsafe fn window_handle_any_thread(
&self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
@@ -435,16 +432,8 @@ pub trait WindowBorrowExtWindows: Borrow<dyn Window> + Sized {
/// Win32 APIs.
///
/// [`Window`]: crate::window::Window
#[cfg_attr(
feature = "rwh_06",
doc = "[`HasWindowHandle`]: rwh_06::HasWindowHandle",
doc = "[`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread"
)]
#[cfg_attr(
not(feature = "rwh_06"),
doc = "[`HasWindowHandle`]: #only-available-with-rwh_06",
doc = "[`window_handle_any_thread`]: #only-available-with-rwh_06"
)]
/// [`HasWindowHandle`]: rwh_06::HasWindowHandle
/// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread
unsafe fn any_thread(self) -> AnyThread<Self>
where
Self: Window,
@@ -670,20 +659,6 @@ impl DeviceIdExtWindows for DeviceId {
}
}
/// Additional methods on `FingerId` that are specific to Windows.
pub trait FingerIdExtWindows {
/// Indicates if the finger represents the first contact in a multi-touch interaction.
#[allow(clippy::wrong_self_convention)]
fn is_primary(self) -> bool;
}
impl FingerIdExtWindows for FingerId {
#[inline]
fn is_primary(self) -> bool {
self.0.is_primary()
}
}
/// Additional methods on `Icon` that are specific to Windows.
pub trait IconExtWindows: Sized {
/// Create an icon from a file path.

View File

@@ -13,12 +13,13 @@ use tracing::{debug, trace, warn};
use crate::application::ApplicationHandler;
use crate::cursor::Cursor;
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event::{self, DeviceId, Force, StartCause, SurfaceSizeWriter};
use crate::event::{self, DeviceId, FingerId, Force, StartCause, SurfaceSizeWriter};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform::pump_events::PumpStatus;
@@ -107,6 +108,7 @@ pub struct EventLoop {
running: bool,
pending_redraw: bool,
cause: StartCause,
primary_pointer: Option<FingerId>,
ignore_volume_keys: bool,
combining_accent: Option<char>,
}
@@ -130,22 +132,24 @@ impl EventLoop {
pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes,
) -> Result<Self, EventLoopError> {
let proxy_wake_up = Arc::new(AtomicBool::new(false));
let android_app = attributes.android_app.as_ref().expect(
"An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on \
Android",
);
let event_loop_proxy = Arc::new(EventLoopProxy::new(android_app.create_waker()));
let redraw_flag = SharedFlag::new();
Ok(Self {
android_app: android_app.clone(),
primary_pointer: None,
window_target: ActiveEventLoop {
app: android_app.clone(),
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(false),
redraw_requester: RedrawRequester::new(&redraw_flag, android_app.create_waker()),
proxy_wake_up,
event_loop_proxy,
},
redraw_flag,
loop_running: false,
@@ -274,7 +278,7 @@ impl EventLoop {
},
}
if self.window_target.proxy_wake_up.swap(false, Ordering::Relaxed) {
if self.window_target.event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
app.proxy_wake_up(&self.window_target);
}
@@ -333,36 +337,83 @@ impl EventLoop {
_ => None,
};
if let Some(pointers) = pointers {
for pointer in pointers {
let tool_type = pointer.tool_type();
let position =
PhysicalPosition { x: pointer.x() as _, y: pointer.y() as _ };
trace!(
"Input event {device_id:?}, {action:?}, loc={position:?}, \
pointer={pointer:?}, tool_type={tool_type:?}"
);
let finger_id = event::FingerId(FingerId(pointer.pointer_id()));
let force = Some(Force::Normalized(pointer.pressure() as f64));
for pointer in pointers.into_iter().flatten() {
let tool_type = pointer.tool_type();
let position = PhysicalPosition { x: pointer.x() as _, y: pointer.y() as _ };
trace!(
"Input event {device_id:?}, {action:?}, loc={position:?}, \
pointer={pointer:?}, tool_type={tool_type:?}"
);
let finger_id = FingerId::from_raw(pointer.pointer_id() as usize);
let force = Some(Force::Normalized(pointer.pressure() as f64));
match action {
MotionAction::Down | MotionAction::PointerDown => {
let event = event::WindowEvent::PointerEntered {
device_id,
position,
kind: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerKind::Touch(finger_id)
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerKind::Unknown,
match action {
MotionAction::Down | MotionAction::PointerDown => {
let primary = action == MotionAction::Down;
if primary {
self.primary_pointer = Some(finger_id);
}
let event = event::WindowEvent::PointerEntered {
device_id,
primary,
position,
kind: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerKind::Touch(finger_id)
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerKind::Unknown,
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
let event = event::WindowEvent::PointerButton {
device_id,
primary,
state: event::ElementState::Pressed,
position,
button: match tool_type {
android_activity::input::ToolType::Finger => {
event::ButtonSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::ButtonSource::Unknown(0),
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
MotionAction::Move => {
let primary = self.primary_pointer == Some(finger_id);
let event = event::WindowEvent::PointerMoved {
device_id,
primary,
position,
source: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerSource::Unknown,
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
MotionAction::Up | MotionAction::PointerUp | MotionAction::Cancel => {
let primary = action == MotionAction::Up
|| (action == MotionAction::Cancel
&& self.primary_pointer == Some(finger_id));
if primary {
self.primary_pointer = None;
}
if let MotionAction::Up | MotionAction::PointerUp = action {
let event = event::WindowEvent::PointerButton {
device_id,
state: event::ElementState::Pressed,
primary,
state: event::ElementState::Released,
position,
button: match tool_type {
android_activity::input::ToolType::Finger => {
@@ -374,56 +425,24 @@ impl EventLoop {
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
MotionAction::Move => {
let event = event::WindowEvent::PointerMoved {
device_id,
position,
source: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerSource::Unknown,
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
MotionAction::Up | MotionAction::PointerUp | MotionAction::Cancel => {
if let MotionAction::Up | MotionAction::PointerUp = action {
let event = event::WindowEvent::PointerButton {
device_id,
state: event::ElementState::Released,
position,
button: match tool_type {
android_activity::input::ToolType::Finger => {
event::ButtonSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::ButtonSource::Unknown(0),
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
}
}
let event = event::WindowEvent::PointerLeft {
device_id,
position: Some(position),
kind: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerKind::Touch(finger_id)
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerKind::Unknown,
let event = event::WindowEvent::PointerLeft {
device_id,
primary,
position: Some(position),
kind: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerKind::Touch(finger_id)
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
_ => unreachable!(),
}
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerKind::Unknown,
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
_ => unreachable!(),
}
}
},
@@ -477,15 +496,21 @@ impl EventLoop {
input_status
}
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
pub fn run<A: ApplicationHandler>(
mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
}
pub fn run_app_on_demand<A: ApplicationHandler>(
pub fn run_on_demand<A: ApplicationHandler>(
&mut self,
mut app: A,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.window_target.clear_exit();
let mut app = init_closure(&self.window_target);
loop {
match self.pump_app_events(None, &mut app) {
PumpStatus::Exit(0) => {
@@ -527,8 +552,6 @@ impl EventLoop {
if self.exiting() {
self.loop_running = false;
app.exiting(&self.window_target);
PumpStatus::Exit(0)
} else {
PumpStatus::Continue
@@ -545,7 +568,8 @@ impl EventLoop {
self.pending_redraw |= self.redraw_flag.get_and_reset();
timeout = if self.running
&& (self.pending_redraw || self.window_target.proxy_wake_up.load(Ordering::Relaxed))
&& (self.pending_redraw
|| self.window_target.event_loop_proxy.wake_up.load(Ordering::Relaxed))
{
// If we already have work to do then we don't want to block on the next poll
Some(Duration::ZERO)
@@ -578,7 +602,7 @@ impl EventLoop {
self.pending_redraw |= self.redraw_flag.get_and_reset();
if !self.running
|| (!self.pending_redraw
&& !self.window_target.proxy_wake_up.load(Ordering::Relaxed))
&& !self.window_target.event_loop_proxy.wake_up.load(Ordering::Relaxed))
{
return;
}
@@ -617,15 +641,20 @@ impl EventLoop {
}
}
#[derive(Clone)]
pub struct EventLoopProxy {
proxy_wake_up: Arc<AtomicBool>,
wake_up: AtomicBool,
waker: AndroidAppWaker,
}
impl EventLoopProxy {
pub fn wake_up(&self) {
self.proxy_wake_up.store(true, Ordering::Relaxed);
fn new(waker: AndroidAppWaker) -> Self {
Self { wake_up: AtomicBool::new(false), waker }
}
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.wake_up.store(true, Ordering::Relaxed);
self.waker.wake();
}
}
@@ -635,7 +664,7 @@ pub struct ActiveEventLoop {
control_flow: Cell<ControlFlow>,
exit: Cell<bool>,
redraw_requester: RedrawRequester,
proxy_wake_up: Arc<AtomicBool>,
event_loop_proxy: Arc<EventLoopProxy>,
}
impl ActiveEventLoop {
@@ -645,12 +674,8 @@ impl ActiveEventLoop {
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = EventLoopProxy {
proxy_wake_up: self.proxy_wake_up.clone(),
waker: self.app.create_waker(),
};
RootEventLoopProxy { event_loop_proxy }
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(self.event_loop_proxy.clone())
}
fn create_window(
@@ -697,17 +722,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.exit.get()
}
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
}
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::AndroidDisplayHandle::new();
@@ -718,23 +741,10 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::AndroidDisplayHandle::new().into())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FingerId(i32);
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId(0)
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::AndroidDisplayHandle::new();
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw.into()) })
}
}
@@ -764,7 +774,6 @@ impl Window {
self.app.content_rect()
}
#[cfg(feature = "rwh_06")]
// Allow the usage of HasRawWindowHandle inside this function
#[allow(deprecated)]
fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
@@ -782,13 +791,11 @@ impl Window {
}
}
#[cfg(feature = "rwh_06")]
fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::Android(rwh_06::AndroidDisplayHandle::new()))
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?;
@@ -796,7 +803,6 @@ impl rwh_06::HasDisplayHandle for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_window_handle_rwh_06()?;
@@ -831,8 +837,8 @@ impl CoreWindow for Window {
fn pre_present_notify(&self) {}
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Err(NotSupportedError::new("inner_position is not supported").into())
fn surface_position(&self) -> PhysicalPosition<i32> {
(0, 0).into()
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
@@ -855,6 +861,10 @@ impl CoreWindow for Window {
screen_size(&self.app)
}
fn safe_area(&self) -> PhysicalInsets<u32> {
PhysicalInsets::new(0, 0, 0, 0)
}
fn set_min_surface_size(&self, _: Option<Size>) {}
fn set_max_surface_size(&self, _: Option<Size>) {}
@@ -921,7 +931,13 @@ impl CoreWindow for Window {
fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
fn set_ime_allowed(&self, _allowed: bool) {}
fn set_ime_allowed(&self, allowed: bool) {
if allowed {
self.app.show_soft_input(true);
} else {
self.app.hide_soft_input(true);
}
}
fn set_ime_purpose(&self, _purpose: ImePurpose) {}
@@ -974,12 +990,10 @@ impl CoreWindow for Window {
fn reset_dead_keys(&self) {}
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self
}

View File

@@ -1,20 +1,20 @@
use std::cell::{Cell, OnceCell, RefCell};
use std::mem;
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
use std::sync::atomic::Ordering as AtomicOrdering;
use std::sync::Arc;
use std::time::Instant;
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy};
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningApplication};
use objc2_foundation::{MainThreadMarker, NSNotification};
use super::super::event_handler::EventHandler;
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
use super::event_loop::{stop_app_immediately, EventLoopProxy, PanicInfo};
use super::menu;
use super::observer::{EventLoopWaker, RunLoop};
use crate::application::ApplicationHandler;
use crate::event::{StartCause, WindowEvent};
use crate::event_loop::ControlFlow;
use crate::event_loop::{ActiveEventLoop, ControlFlow};
use crate::window::WindowId;
#[derive(Debug)]
@@ -24,7 +24,7 @@ pub(super) struct AppState {
default_menu: bool,
activate_ignoring_other_apps: bool,
run_loop: RunLoop,
proxy_wake_up: Arc<AtomicBool>,
event_loop_proxy: Arc<EventLoopProxy>,
event_handler: EventHandler,
stop_on_launch: Cell<bool>,
stop_before_wait: Cell<bool>,
@@ -72,7 +72,7 @@ impl AppState {
let this = Rc::new(AppState {
mtm,
activation_policy,
proxy_wake_up: Arc::new(AtomicBool::new(false)),
event_loop_proxy: Arc::new(EventLoopProxy::new()),
default_menu,
activate_ignoring_other_apps,
run_loop: RunLoop::main(mtm),
@@ -113,10 +113,21 @@ impl AppState {
// 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.
// If no activation policy is explicitly provided, do not set it at all
// to allow the package manifest to define behavior via LSUIElement.
if self.activation_policy.is_some() {
app.setActivationPolicy(self.activation_policy.unwrap());
if let Some(activation_policy) = self.activation_policy {
app.setActivationPolicy(activation_policy);
} else {
// If no activation policy is explicitly provided, and the application
// is bundled, do not set the activation policy at all, to allow the
// package manifest to define the behavior via LSUIElement.
//
// See:
// - https://github.com/rust-windowing/winit/issues/261
// - https://github.com/rust-windowing/winit/issues/3958
let is_bundled =
unsafe { NSRunningApplication::currentApplication().bundleIdentifier().is_some() };
if !is_bundled {
app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
}
}
#[allow(deprecated)]
@@ -152,21 +163,29 @@ impl AppState {
pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) {
trace_scope!("NSApplicationWillTerminateNotification");
// TODO: Notify every window that it will be destroyed, like done in iOS?
self.with_handler(|app, event_loop| {
app.exiting(event_loop);
});
self.internal_exit();
}
/// Place the event handler in the application state for the duration
/// of the given closure.
pub fn set_event_handler<R>(
pub fn set_init_closure<A: ApplicationHandler, R>(
&self,
handler: &mut dyn ApplicationHandler,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
closure: impl FnOnce() -> R,
) -> R {
self.event_handler.set(handler, closure)
let init_closure = Box::new(
|active_event_loop: &'_ dyn ActiveEventLoop| -> Box<dyn ApplicationHandler + '_> {
Box::new(init_closure(active_event_loop))
},
);
self.event_handler.set(init_closure, closure)
}
pub fn proxy_wake_up(&self) -> Arc<AtomicBool> {
self.proxy_wake_up.clone()
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.event_loop_proxy
}
/// If `pump_events` is called to progress the event loop then we
@@ -197,10 +216,6 @@ impl AppState {
/// NOTE: that if the `NSApplication` has been launched then that state is preserved,
/// and we won't need to re-launch the app if subsequent EventLoops are run.
pub fn internal_exit(self: &Rc<Self>) {
self.with_handler(|app, event_loop| {
app.exiting(event_loop);
});
self.set_is_running(false);
self.set_stop_on_redraw(false);
self.set_stop_before_wait(false);
@@ -350,7 +365,7 @@ impl AppState {
return;
}
if self.proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
if self.event_loop_proxy.wake_up.swap(false, AtomicOrdering::Relaxed) {
self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
}

View File

@@ -20,6 +20,7 @@ use objc2_app_kit::{
NSApplicationWillTerminateNotification, NSWindow,
};
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject, NSObjectProtocol};
use rwh_06::HasDisplayHandle;
use super::super::notification_center::create_observer;
use super::app::WinitApplication;
@@ -32,7 +33,8 @@ use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, RequestError};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform::macos::ActivationPolicy;
@@ -95,9 +97,8 @@ impl ActiveEventLoop {
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = EventLoopProxy::new(self.app_state.proxy_wake_up());
RootEventLoopProxy { event_loop_proxy }
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(self.app_state.event_loop_proxy().clone())
}
fn create_window(
@@ -151,17 +152,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.app_state.exiting()
}
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
}
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
@@ -275,20 +274,23 @@ impl EventLoop {
&self.window_target
}
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
pub fn run<A: ApplicationHandler>(
mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
}
// NB: we don't base this on `pump_events` because for `MacOs` we can't support
// `pump_events` elegantly (we just ask to run the loop for a "short" amount of
// time and so a layered implementation would end up using a lot of CPU due to
// redundant wake ups.
pub fn run_app_on_demand<A: ApplicationHandler>(
pub fn run_on_demand<A: ApplicationHandler>(
&mut self,
mut app: A,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.app_state.clear_exit();
self.app_state.set_event_handler(&mut app, || {
self.app_state.set_init_closure(init_closure, || {
autoreleasepool(|_| {
// clear / normalize pump_events state
self.app_state.set_wait_timeout(None);
@@ -391,16 +393,12 @@ impl EventLoop {
}
}
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::AppKitDisplayHandle::new().into())
impl HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}
@@ -439,8 +437,9 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
}
}
#[derive(Debug)]
pub struct EventLoopProxy {
proxy_wake_up: Arc<AtomicBool>,
pub(crate) wake_up: AtomicBool,
source: CFRunLoopSourceRef,
}
@@ -455,14 +454,8 @@ impl Drop for EventLoopProxy {
}
}
impl Clone for EventLoopProxy {
fn clone(&self) -> Self {
EventLoopProxy::new(self.proxy_wake_up.clone())
}
}
impl EventLoopProxy {
fn new(proxy_wake_up: Arc<AtomicBool>) -> Self {
pub(crate) fn new() -> Self {
unsafe {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
@@ -486,14 +479,16 @@ impl EventLoopProxy {
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);
EventLoopProxy { proxy_wake_up, source }
EventLoopProxy { wake_up: AtomicBool::new(false), source }
}
}
}
pub fn wake_up(&self) {
self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.wake_up.store(true, AtomicOrdering::Relaxed);
unsafe {
// let the main thread know there's a new event
// Let the main thread know there's a new event.
CFRunLoopSourceSignal(self.source);
let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl);

View File

@@ -17,8 +17,7 @@ mod window_delegate;
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey, KeyEventExtra};
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::Window;
@@ -26,13 +25,3 @@ pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId;
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId
}
}

View File

@@ -399,7 +399,7 @@ declare_class!(
unsafe { &*string }.to_string()
};
let is_control = string.chars().next().map_or(false, |c| c.is_control());
let is_control = string.chars().next().is_some_and(|c| c.is_control());
// Commit only if we have marked text.
if unsafe { self.hasMarkedText() } && self.is_ime_enabled() && !is_control {
@@ -657,6 +657,7 @@ declare_class!(
self.queue_event(WindowEvent::PointerEntered {
device_id: None,
primary: true,
position,
kind: PointerKind::Mouse,
});
@@ -670,6 +671,7 @@ declare_class!(
self.queue_event(WindowEvent::PointerLeft {
device_id: None,
primary: true,
position: Some(position),
kind: PointerKind::Mouse,
});
@@ -1061,6 +1063,7 @@ impl WinitView {
self.queue_event(WindowEvent::PointerButton {
device_id: None,
primary: true,
state: button_state,
position,
button: button.into(),
@@ -1087,6 +1090,7 @@ impl WinitView {
self.queue_event(WindowEvent::PointerMoved {
device_id: None,
primary: true,
position: view_point.to_physical(self.scale_factor()),
source: PointerSource::Mouse,
});

View File

@@ -42,7 +42,6 @@ impl Window {
self.delegate.get_on_main(|delegate| f(delegate))
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_window_handle_rwh_06(
&self,
@@ -54,7 +53,6 @@ impl Window {
}
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_display_handle_rwh_06(
&self,
@@ -74,7 +72,6 @@ impl Drop for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?;
@@ -82,7 +79,6 @@ impl rwh_06::HasDisplayHandle for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_window_handle_rwh_06()?;
@@ -111,12 +107,12 @@ impl CoreWindow for Window {
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
}
fn inner_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
fn surface_position(&self) -> dpi::PhysicalPosition<i32> {
self.maybe_wait_on_main(|delegate| delegate.surface_position())
}
fn outer_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
self.maybe_wait_on_main(|delegate| delegate.outer_position())
}
fn set_outer_position(&self, position: Position) {
@@ -135,6 +131,10 @@ impl CoreWindow for Window {
self.maybe_wait_on_main(|delegate| delegate.outer_size())
}
fn safe_area(&self) -> dpi::PhysicalInsets<u32> {
self.maybe_wait_on_main(|delegate| delegate.safe_area())
}
fn set_min_surface_size(&self, min_size: Option<Size>) {
self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size))
}
@@ -323,12 +323,10 @@ impl CoreWindow for Window {
})
}
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self
}

View File

@@ -6,7 +6,7 @@ use std::ptr;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use core_graphics::display::{CGDisplay, CGPoint};
use core_graphics::display::CGDisplay;
use monitor::VideoModeHandle;
use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::{AnyObject, ProtocolObject};
@@ -15,15 +15,16 @@ use objc2_app_kit::{
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization,
NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType,
NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard,
NSRequestUserAttentionType, NSScreen, NSView, NSWindowButton, NSWindowDelegate,
NSRequestUserAttentionType, NSScreen, NSToolbar, NSView, NSWindowButton, NSWindowDelegate,
NSWindowFullScreenButton, NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode,
NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility,
NSWindowToolbarStyle,
};
use objc2_foundation::{
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSKeyValueChangeKey,
NSKeyValueChangeNewKey, NSKeyValueChangeOldKey, NSKeyValueObservingOptions, NSObject,
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
NSRect, NSSize, NSString,
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSEdgeInsets,
NSKeyValueChangeKey, NSKeyValueChangeNewKey, NSKeyValueChangeOldKey,
NSKeyValueObservingOptions, NSObject, NSObjectNSDelayedPerforming,
NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint, NSRect, NSSize, NSString,
};
use tracing::{trace, warn};
@@ -34,7 +35,10 @@ use super::observer::RunLoop;
use super::view::WinitView;
use super::window::WinitWindow;
use super::{ffi, Fullscreen, MonitorHandle};
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
Position, Size,
};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{SurfaceSizeWriter, WindowEvent};
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
@@ -57,6 +61,7 @@ pub struct PlatformSpecificWindowAttributes {
pub tabbing_identifier: Option<String>,
pub option_as_alt: OptionAsAlt,
pub borderless_game: bool,
pub unified_titlebar: bool,
}
impl Default for PlatformSpecificWindowAttributes {
@@ -75,6 +80,7 @@ impl Default for PlatformSpecificWindowAttributes {
tabbing_identifier: None,
option_as_alt: Default::default(),
borderless_game: false,
unified_titlebar: false,
}
}
}
@@ -439,9 +445,15 @@ declare_class!(
// NOTE: We don't _really_ need to check the key path, as there should only be one, but
// in the future we might want to observe other key paths.
if key_path == Some(ns_string!("effectiveAppearance")) {
let change = change.expect("requested a change dictionary in `addObserver`, but none was provided");
let old = change.get(unsafe { NSKeyValueChangeOldKey }).expect("requested change dictionary did not contain `NSKeyValueChangeOldKey`");
let new = change.get(unsafe { NSKeyValueChangeNewKey }).expect("requested change dictionary did not contain `NSKeyValueChangeNewKey`");
let change = change.expect(
"requested a change dictionary in `addObserver`, but none was provided",
);
let old = change
.get(unsafe { NSKeyValueChangeOldKey })
.expect("requested change dictionary did not contain `NSKeyValueChangeOldKey`");
let new = change
.get(unsafe { NSKeyValueChangeNewKey })
.expect("requested change dictionary did not contain `NSKeyValueChangeNewKey`");
// SAFETY: The value of `effectiveAppearance` is `NSAppearance`
let old: *const AnyObject = old;
@@ -558,6 +570,12 @@ fn new_window(
}
if attrs.platform_specific.fullsize_content_view {
// NOTE: If we decide to add an option to change this at runtime, we must emit a
// `SurfaceResized` event to let applications know that the safe area changed.
//
// An alternative would be to add a `WindowEvent::SafeAreaChanged` event, this could be
// done with an observer on `safeAreaRect` / `contentLayoutRect`, see:
// <https://github.com/rust-windowing/winit/issues/3911>
masks |= NSWindowStyleMask::FullSizeContentView;
}
@@ -616,6 +634,14 @@ fn new_window(
if attrs.platform_specific.movable_by_window_background {
window.setMovableByWindowBackground(true);
}
if attrs.platform_specific.unified_titlebar {
unsafe {
// The toolbar style is ignored if there is no toolbar, so it is
// necessary to add one.
window.setToolbar(Some(&NSToolbar::new(mtm)));
window.setToolbarStyle(NSWindowToolbarStyle::Unified);
}
}
if !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) {
if let Some(button) = window.standardWindowButton(NSWindowButton::NSWindowZoomButton) {
@@ -681,7 +707,6 @@ impl WindowDelegate {
let window = new_window(app_state, &attrs, mtm)
.ok_or_else(|| os_error!("couldn't create `NSWindow`"))?;
#[cfg(feature = "rwh_06")]
match attrs.parent_window.map(|handle| handle.0) {
Some(rwh_06::RawWindowHandle::AppKit(handle)) => {
// SAFETY: Caller ensures the pointer is valid or NULL
@@ -740,12 +765,6 @@ impl WindowDelegate {
});
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
if scale_factor != 1.0 {
let delegate = delegate.clone();
RunLoop::main(mtm).queue_closure(move || {
delegate.handle_scale_factor_changed(scale_factor);
});
}
window.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
// Listen for theme change event.
@@ -776,10 +795,6 @@ impl WindowDelegate {
delegate.set_cursor(attrs.cursor);
// XXX Send `Focused(false)` right after creating the window delegate, so we won't
// obscure the real focused events on the startup.
delegate.queue_event(WindowEvent::Focused(false));
// Set fullscreen mode after we setup everything
delegate.set_fullscreen(attrs.fullscreen.map(Into::into));
@@ -924,15 +939,15 @@ impl WindowDelegate {
#[inline]
pub fn pre_present_notify(&self) {}
pub fn outer_position(&self) -> PhysicalPosition<i32> {
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
let position = flip_window_screen_coordinates(self.window().frame());
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
}
pub fn inner_position(&self) -> PhysicalPosition<i32> {
pub fn surface_position(&self) -> PhysicalPosition<i32> {
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
let position = flip_window_screen_coordinates(content_rect);
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
let logical = LogicalPosition::new(content_rect.origin.x, content_rect.origin.y);
logical.to_physical(self.scale_factor())
}
pub fn set_outer_position(&self, position: Position) {
@@ -958,6 +973,32 @@ impl WindowDelegate {
logical.to_physical(self.scale_factor())
}
pub fn safe_area(&self) -> PhysicalInsets<u32> {
// Only available on macOS 11.0
let insets = if self.view().respondsToSelector(sel!(safeAreaInsets)) {
// Includes NSWindowStyleMask::FullSizeContentView by default, and the notch because
// we've set it up with `additionalSafeAreaInsets`.
unsafe { self.view().safeAreaInsets() }
} else {
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
// Includes NSWindowStyleMask::FullSizeContentView
// Convert from window coordinates to view coordinates
let safe_rect = unsafe {
self.view().convertRect_fromView(self.window().contentLayoutRect(), None)
};
NSEdgeInsets {
top: safe_rect.origin.y - content_rect.origin.y,
left: safe_rect.origin.x - content_rect.origin.x,
bottom: (content_rect.size.height + content_rect.origin.x)
- (safe_rect.size.height + safe_rect.origin.x),
right: (content_rect.size.width + content_rect.origin.y)
- (safe_rect.size.width + safe_rect.origin.y),
}
};
let insets = LogicalInsets::new(insets.top, insets.left, insets.bottom, insets.right);
insets.to_physical(self.scale_factor())
}
#[inline]
pub fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
let scale_factor = self.scale_factor();
@@ -1154,13 +1195,12 @@ impl WindowDelegate {
#[inline]
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), RequestError> {
let physical_window_position = self.inner_position();
let scale_factor = self.scale_factor();
let window_position = physical_window_position.to_logical::<CGFloat>(scale_factor);
let logical_cursor_position = cursor_position.to_logical::<CGFloat>(scale_factor);
let point = CGPoint {
x: logical_cursor_position.x + window_position.x,
y: logical_cursor_position.y + window_position.y,
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
let window_position = flip_window_screen_coordinates(content_rect);
let cursor_position = cursor_position.to_logical::<CGFloat>(self.scale_factor());
let point = core_graphics::display::CGPoint {
x: window_position.x + cursor_position.x,
y: window_position.y + cursor_position.y,
};
CGDisplay::warp_mouse_cursor_position(point)
.map_err(|status| os_error!(format!("CGError {status}")))?;
@@ -1620,7 +1660,6 @@ impl WindowDelegate {
Some(monitor)
}
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
let window_handle = rwh_06::AppKitWindowHandle::new({
@@ -1743,12 +1782,15 @@ impl WindowExtMacOS for WindowDelegate {
let screen = self.window().screen().expect("expected screen to be available");
self.window().setFrame_display(screen.frame(), true);
// Configure the safe area rectangle, to ensure that we don't obscure the notch.
if NSScreen::class().responds_to(sel!(safeAreaInsets)) {
unsafe { self.view().setAdditionalSafeAreaInsets(screen.safeAreaInsets()) };
}
// Fullscreen windows can't be resized, minimized, or moved
self.toggle_style_mask(NSWindowStyleMask::Miniaturizable, false);
self.toggle_style_mask(NSWindowStyleMask::Resizable, false);
self.window().setMovable(false);
true
} else {
let new_mask = self.saved_style();
self.set_style_mask(new_mask);
@@ -1761,11 +1803,22 @@ impl WindowExtMacOS for WindowDelegate {
app.setPresentationOptions(presentation_opts);
}
if NSScreen::class().responds_to(sel!(safeAreaInsets)) {
unsafe {
self.view().setAdditionalSafeAreaInsets(NSEdgeInsets {
top: 0.0,
left: 0.0,
bottom: 0.0,
right: 0.0,
});
}
}
self.window().setFrame_display(frame, true);
self.window().setMovable(true);
true
}
true
}
#[inline]
@@ -1837,6 +1890,34 @@ impl WindowExtMacOS for WindowDelegate {
fn is_borderless_game(&self) -> bool {
self.ivars().is_borderless_game.get()
}
fn set_unified_titlebar(&self, unified_titlebar: bool) {
let window = self.window();
if unified_titlebar {
let mtm = MainThreadMarker::from(self);
unsafe {
// The toolbar style is ignored if there is no toolbar, so it is
// necessary to add one.
window.setToolbar(Some(&NSToolbar::new(mtm)));
window.setToolbarStyle(NSWindowToolbarStyle::Unified);
}
} else {
unsafe {
window.setToolbar(None);
window.setToolbarStyle(NSWindowToolbarStyle::Automatic);
}
}
}
fn unified_titlebar(&self) -> bool {
let window = self.window();
unsafe {
window.toolbar().is_some() && window.toolbarStyle() == NSWindowToolbarStyle::Unified
}
}
}
const DEFAULT_STANDARD_FRAME: NSRect =

View File

@@ -1,33 +1,55 @@
use std::cell::RefCell;
use std::cell::Cell;
use std::{fmt, mem};
use crate::application::ApplicationHandler;
use crate::event_loop::ActiveEventLoop;
/// A helper type for storing a reference to `ApplicationHandler`, allowing interior mutable access
/// to it within the execution of a closure.
#[derive(Default)]
pub(crate) struct EventHandler {
/// This can be in the following states:
/// - Not registered by the event loop (None).
/// - Present (Some(handler)).
/// - Currently executing the handler / in use (RefCell borrowed).
inner: RefCell<Option<&'static mut dyn ApplicationHandler>>,
state: Cell<State>,
}
type InitClosure<'handler> =
Box<dyn FnOnce(&dyn ActiveEventLoop) -> Box<dyn ApplicationHandler + 'handler> + 'handler>;
#[derive(Default)]
enum State {
/// Not registered by the event loop.
#[default]
NotRegistered,
/// The event is registered by the event loop.
Registered(InitClosure<'static>),
/// The application has been initialized, and we're ready to handle events.
Ready(Box<dyn ApplicationHandler + 'static>),
/// Currently executing the handler.
CurrentlyExecuting,
/// The application has been terminated.
Terminated,
// TODO: Invalid state?
}
impl fmt::Debug for EventHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state = match self.inner.try_borrow().as_deref() {
Ok(Some(_)) => "<available>",
Ok(None) => "<not set>",
Err(_) => "<in use>",
let state = self.state.replace(State::CurrentlyExecuting);
// NOTE: We're very careful not to panic inside the "critial" section here.
let string = match &state {
State::NotRegistered => "<not registered>",
State::Registered(_) => "<registered>",
State::Ready(_) => "<ready>",
State::CurrentlyExecuting => "<currently executing>",
State::Terminated => "<terminated>",
};
f.debug_struct("EventHandler").field("state", &state).finish_non_exhaustive()
self.state.set(state);
f.debug_struct("EventHandler").field("state", &string).finish_non_exhaustive()
}
}
impl EventHandler {
pub(crate) fn new() -> Self {
Self { inner: RefCell::new(None) }
pub(crate) const fn new() -> Self {
Self { state: Cell::new(State::NotRegistered) }
}
/// Set the event loop handler for the duration of the given closure.
@@ -37,7 +59,7 @@ impl EventHandler {
/// from within the closure.
pub(crate) fn set<'handler, R>(
&self,
app: &'handler mut dyn ApplicationHandler,
init_closure: InitClosure<'handler>,
closure: impl FnOnce() -> R,
) -> R {
// SAFETY: We extend the lifetime of the handler here so that we can
@@ -46,14 +68,9 @@ impl EventHandler {
// This is sound, since we make sure to unset the handler again at the
// end of this function, and as such the lifetime isn't actually
// extended beyond `'handler`.
let handler = unsafe {
mem::transmute::<
&'handler mut dyn ApplicationHandler,
&'static mut dyn ApplicationHandler,
>(app)
};
let handler = unsafe { mem::transmute::<InitClosure<'handler>, InitClosure<'static>>(app) };
match self.inner.try_borrow_mut().as_deref_mut() {
match self.state.try_borrow_mut().as_deref_mut() {
Ok(Some(_)) => {
unreachable!("tried to set handler while another was already set");
},
@@ -69,7 +86,7 @@ impl EventHandler {
impl Drop for ClearOnDrop<'_> {
fn drop(&mut self) {
match self.0.inner.try_borrow_mut().as_deref_mut() {
match self.0.state.try_borrow_mut().as_deref_mut() {
Ok(data @ Some(_)) => {
*data = None;
},
@@ -101,6 +118,10 @@ impl EventHandler {
// soundness.
}
fn init(&self) {}
fn terminate(&self) {}
#[cfg(target_os = "macos")]
pub(crate) fn in_use(&self) -> bool {
self.inner.try_borrow().is_err()
@@ -120,7 +141,7 @@ impl EventHandler {
//
// If the handler unwinds, the `RefMut` will ensure that the
// handler is no longer borrowed.
callback(*user_app);
callback(user_app);
},
Ok(None) => {
// `NSApplication`, our app state and this handler are all

View File

@@ -7,7 +7,9 @@ mod notification_center;
#[cfg(not(target_os = "macos"))]
mod uikit;
#[allow(unused_imports)]
#[cfg(target_os = "macos")]
pub use self::appkit::*;
#[allow(unused_imports)]
#[cfg(not(target_os = "macos"))]
pub use self::uikit::*;

View File

@@ -3,7 +3,7 @@
use std::cell::{OnceCell, RefCell, RefMut};
use std::collections::HashSet;
use std::os::raw::c_void;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, OnceLock};
use std::time::Instant;
use std::{mem, ptr};
@@ -24,7 +24,7 @@ use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
use super::super::event_handler::EventHandler;
use super::window::WinitUIWindow;
use super::ActiveEventLoop;
use super::{ActiveEventLoop, EventLoopProxy};
use crate::application::ApplicationHandler;
use crate::dpi::PhysicalSize;
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
@@ -139,7 +139,7 @@ pub(crate) struct AppState {
app_state: Option<AppStateImpl>,
control_flow: ControlFlow,
waker: EventLoopWaker,
proxy_wake_up: Arc<AtomicBool>,
event_loop_proxy: Arc<EventLoopProxy>,
}
impl AppState {
@@ -164,7 +164,7 @@ impl AppState {
}),
control_flow: ControlFlow::default(),
waker,
proxy_wake_up: Arc::new(AtomicBool::new(false)),
event_loop_proxy: Arc::new(EventLoopProxy::new()),
});
}
init_guard(&mut guard);
@@ -376,8 +376,8 @@ impl AppState {
}
}
pub(crate) fn proxy_wake_up(&self) -> Arc<AtomicBool> {
self.proxy_wake_up.clone()
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.event_loop_proxy
}
pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) {
@@ -543,10 +543,10 @@ fn handle_user_events(mtm: MainThreadMarker) {
if processing_redraws {
bug!("user events attempted to be sent out while `ProcessingRedraws`");
}
let proxy_wake_up = this.proxy_wake_up.clone();
let event_loop_proxy = this.event_loop_proxy().clone();
drop(this);
if proxy_wake_up.swap(false, Ordering::Relaxed) {
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
handle_event(mtm, Event::UserWakeUp);
}
@@ -578,7 +578,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
}
}
if proxy_wake_up.swap(false, Ordering::Relaxed) {
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
handle_event(mtm, Event::UserWakeUp);
}
}

View File

@@ -20,6 +20,7 @@ use objc2_ui_kit::{
UIApplicationWillEnterForegroundNotification, UIApplicationWillResignActiveNotification,
UIApplicationWillTerminateNotification, UIScreen,
};
use rwh_06::HasDisplayHandle;
use super::super::notification_center::create_observer;
use super::app_state::{send_occluded_event_for_all_windows, AppState, EventWrapper};
@@ -29,7 +30,8 @@ use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event::Event;
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform_impl::Window;
@@ -41,9 +43,8 @@ pub(crate) struct ActiveEventLoop {
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
let event_loop_proxy = EventLoopProxy::new(AppState::get_mut(self.mtm).proxy_wake_up());
RootEventLoopProxy { event_loop_proxy }
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(AppState::get_mut(self.mtm).event_loop_proxy().clone())
}
fn create_window(
@@ -94,17 +95,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
false
}
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
}
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::UiKit(rwh_06::UiKitDisplayHandle::new());
@@ -115,13 +114,10 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::UiKitDisplayHandle::new().into())
impl HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::UiKit(rwh_06::UiKitDisplayHandle::new());
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}
@@ -292,19 +288,13 @@ impl EventLoop {
}
pub struct EventLoopProxy {
proxy_wake_up: Arc<AtomicBool>,
pub(crate) wake_up: AtomicBool,
source: CFRunLoopSourceRef,
}
unsafe impl Send for EventLoopProxy {}
unsafe impl Sync for EventLoopProxy {}
impl Clone for EventLoopProxy {
fn clone(&self) -> EventLoopProxy {
EventLoopProxy::new(self.proxy_wake_up.clone())
}
}
impl Drop for EventLoopProxy {
fn drop(&mut self) {
unsafe {
@@ -315,7 +305,7 @@ impl Drop for EventLoopProxy {
}
impl EventLoopProxy {
fn new(proxy_wake_up: Arc<AtomicBool>) -> EventLoopProxy {
pub(crate) fn new() -> EventLoopProxy {
unsafe {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
@@ -339,12 +329,14 @@ impl EventLoopProxy {
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);
EventLoopProxy { proxy_wake_up, source }
EventLoopProxy { wake_up: AtomicBool::new(false), source }
}
}
}
pub fn wake_up(&self) {
self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.wake_up.store(true, AtomicOrdering::Relaxed);
unsafe {
// let the main thread know there's a new event
CFRunLoopSourceSignal(self.source);

View File

@@ -10,8 +10,7 @@ mod window;
use std::fmt;
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
@@ -21,16 +20,6 @@ pub(crate) use crate::cursor::{
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId(usize);
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId(0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyEventExtra {}

View File

@@ -6,19 +6,19 @@ use objc2::runtime::{NSObjectProtocol, ProtocolObject};
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet, NSString};
use objc2_ui_kit::{
UICoordinateSpace, UIEvent, UIForceTouchCapability, UIGestureRecognizer,
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer,
UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer,
UITextInputTraits, UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
UIEvent, UIForceTouchCapability, UIGestureRecognizer, UIGestureRecognizerDelegate,
UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer, UIPinchGestureRecognizer,
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITextInputTraits, UITouch,
UITouchPhase, UITouchType, UITraitEnvironment, UIView,
};
use tracing::debug;
use super::app_state::{self, EventWrapper};
use super::window::WinitUIWindow;
use super::FingerId;
use crate::dpi::PhysicalPosition;
use crate::event::{
ButtonSource, ElementState, Event, FingerId as RootFingerId, Force, KeyEvent, PointerKind,
PointerSource, TouchPhase, WindowEvent,
ButtonSource, ElementState, Event, FingerId, Force, KeyEvent, PointerKind, PointerSource,
TouchPhase, WindowEvent,
};
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
use crate::platform_impl::KeyEventExtra;
@@ -34,6 +34,9 @@ pub struct WinitViewState {
rotation_last_delta: Cell<CGFloat>,
pinch_last_delta: Cell<CGFloat>,
pan_last_delta: Cell<CGPoint>,
primary_finger: Cell<Option<FingerId>>,
fingers: Cell<u8>,
}
declare_class!(
@@ -70,26 +73,15 @@ declare_class!(
let mtm = MainThreadMarker::new().unwrap();
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
let window = self.window().unwrap();
let window_bounds = window.bounds();
let screen = window.screen();
let screen_space = screen.coordinateSpace();
let screen_frame = self.convertRect_toCoordinateSpace(window_bounds, &screen_space);
let scale_factor = screen.scale();
let frame = self.frame();
let scale_factor = self.contentScaleFactor() as f64;
let size = crate::dpi::LogicalSize {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
}
.to_physical(scale_factor as f64);
// If the app is started in landscape, the view frame and window bounds can be mismatched.
// The view frame will be in portrait and the window bounds in landscape. So apply the
// window bounds to the view frame to make it consistent.
let view_frame = self.frame();
if view_frame != window_bounds {
self.setFrame(window_bounds);
width: frame.size.width as f64,
height: frame.size.height as f64,
}
.to_physical(scale_factor);
let window = self.window().unwrap();
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
@@ -124,13 +116,10 @@ declare_class!(
"invalid scale_factor set on UIView",
);
let scale_factor = scale_factor as f64;
let bounds = self.bounds();
let screen = window.screen();
let screen_space = screen.coordinateSpace();
let screen_frame = self.convertRect_toCoordinateSpace(bounds, &screen_space);
let frame = self.frame();
let size = crate::dpi::LogicalSize {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
width: frame.size.width as f64,
height: frame.size.height as f64,
};
let window_id = window.id();
app_state::handle_nonuser_events(
@@ -151,6 +140,13 @@ declare_class!(
);
}
#[method(safeAreaInsetsDidChange)]
fn safe_area_changed(&self) {
debug!("safeAreaInsetsDidChange was called, requesting redraw");
// When the safe area changes we want to make sure to emit a redraw event
self.setNeedsDisplay();
}
#[method(touchesBegan:withEvent:)]
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
self.handle_touches(touches)
@@ -193,7 +189,7 @@ declare_class!(
// Pass -delta so that action is reversed
(TouchPhase::Cancelled, -recognizer.scale())
}
state => panic!("unexpected recognizer state: {:?}", state),
state => panic!("unexpected recognizer state: {state:?}"),
};
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
@@ -252,7 +248,7 @@ declare_class!(
// Pass -delta so that action is reversed
(TouchPhase::Cancelled, -recognizer.rotation())
}
state => panic!("unexpected recognizer state: {:?}", state),
state => panic!("unexpected recognizer state: {state:?}"),
};
// Make delta negative to match macos, convert to degrees
@@ -303,7 +299,7 @@ declare_class!(
// Pass -delta so that action is reversed
(TouchPhase::Cancelled, -last_pan.x, -last_pan.y)
}
state => panic!("unexpected recognizer state: {:?}", state),
state => panic!("unexpected recognizer state: {state:?}"),
};
@@ -371,6 +367,9 @@ impl WinitView {
rotation_last_delta: Cell::new(0.0),
pinch_last_delta: Cell::new(0.0),
pan_last_delta: Cell::new(CGPoint { x: 0.0, y: 0.0 }),
primary_finger: Cell::new(None),
fingers: Cell::new(0),
});
let this: Retained<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
@@ -513,14 +512,37 @@ impl WinitView {
)
};
let window_id = window.id();
let finger_id = RootFingerId(FingerId(touch_id));
let finger_id = FingerId::from_raw(touch_id);
let ivars = self.ivars();
match phase {
UITouchPhase::Began => {
let primary = if let UITouchType::Pencil = touch_type {
true
} else {
ivars.fingers.set(ivars.fingers.get() + 1);
// Keep the primary finger around until we clear all the fingers to
// recognize it when user briefly removes it.
match ivars.primary_finger.get() {
Some(primary_id) => primary_id == finger_id,
None => {
debug_assert_eq!(
ivars.fingers.get(),
1,
"number of fingers were not counted correctly"
);
ivars.primary_finger.set(Some(finger_id));
true
},
}
};
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id: None,
primary,
position,
kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown
@@ -533,6 +555,7 @@ impl WinitView {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
primary,
state: ElementState::Pressed,
position,
button: if let UITouchType::Pencil = touch_type {
@@ -544,26 +567,44 @@ impl WinitView {
}));
},
UITouchPhase::Moved => {
let (primary, source) = if let UITouchType::Pencil = touch_type {
(true, PointerSource::Unknown)
} else {
(ivars.primary_finger.get().unwrap() == finger_id, PointerSource::Touch {
finger_id,
force,
})
};
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
primary,
position,
source: if let UITouchType::Pencil = touch_type {
PointerSource::Unknown
} else {
PointerSource::Touch { finger_id, force }
},
source,
},
}));
},
// 2 is UITouchPhase::Stationary and is not expected here
UITouchPhase::Ended | UITouchPhase::Cancelled => {
let primary = if let UITouchType::Pencil = touch_type {
true
} else {
ivars.fingers.set(ivars.fingers.get() - 1);
let primary = ivars.primary_finger.get().unwrap() == finger_id;
if ivars.fingers.get() == 0 {
ivars.primary_finger.set(None);
}
primary
};
if let UITouchPhase::Ended = phase {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
primary,
state: ElementState::Released,
position,
button: if let UITouchType::Pencil = touch_type {
@@ -579,6 +620,7 @@ impl WinitView {
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
primary,
position: Some(position),
kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown

View File

@@ -8,8 +8,8 @@ use objc2_foundation::{
CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker, NSObject, NSObjectProtocol,
};
use objc2_ui_kit::{
UIApplication, UICoordinateSpace, UIResponder, UIScreen, UIScreenOverscanCompensation,
UIViewController, UIWindow,
UIApplication, UICoordinateSpace, UIEdgeInsets, UIResponder, UIScreen,
UIScreenOverscanCompensation, UIViewController, UIWindow,
};
use tracing::{debug, warn};
@@ -18,7 +18,10 @@ use super::view::WinitView;
use super::view_controller::WinitViewController;
use super::{app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle};
use crate::cursor::Cursor;
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
Position, Size,
};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{Event, WindowEvent};
use crate::icon::Icon;
@@ -158,20 +161,19 @@ impl Inner {
pub fn pre_present_notify(&self) {}
pub fn inner_position(&self) -> PhysicalPosition<i32> {
let safe_area = self.safe_area_screen_space();
pub fn surface_position(&self) -> PhysicalPosition<i32> {
let view_position = self.view.frame().origin;
let position =
LogicalPosition { x: safe_area.origin.x as f64, y: safe_area.origin.y as f64 };
let scale_factor = self.scale_factor();
position.to_physical(scale_factor)
unsafe { self.window.convertPoint_fromView(view_position, Some(&self.view)) };
let position = LogicalPosition::new(position.x, position.y);
position.to_physical(self.scale_factor())
}
pub fn outer_position(&self) -> PhysicalPosition<i32> {
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
let screen_frame = self.screen_frame();
let position =
LogicalPosition { x: screen_frame.origin.x as f64, y: screen_frame.origin.y as f64 };
let scale_factor = self.scale_factor();
position.to_physical(scale_factor)
Ok(position.to_physical(self.scale_factor()))
}
pub fn set_outer_position(&self, physical_position: Position) {
@@ -187,29 +189,36 @@ impl Inner {
}
pub fn surface_size(&self) -> PhysicalSize<u32> {
let scale_factor = self.scale_factor();
let safe_area = self.safe_area_screen_space();
let size = LogicalSize {
width: safe_area.size.width as f64,
height: safe_area.size.height as f64,
};
size.to_physical(scale_factor)
let frame = self.view.frame();
let size = LogicalSize::new(frame.size.width, frame.size.height);
size.to_physical(self.scale_factor())
}
pub fn outer_size(&self) -> PhysicalSize<u32> {
let scale_factor = self.scale_factor();
let screen_frame = self.screen_frame();
let size = LogicalSize {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
};
size.to_physical(scale_factor)
let frame = self.window.frame();
let size = LogicalSize::new(frame.size.width, frame.size.height);
size.to_physical(self.scale_factor())
}
pub fn request_surface_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
Some(self.surface_size())
}
pub fn safe_area(&self) -> PhysicalInsets<u32> {
// Only available on iOS 11.0
let insets = if app_state::os_capabilities().safe_area {
self.view.safeAreaInsets()
} else {
// Assume the status bar frame is the only thing that obscures the view
let app = UIApplication::sharedApplication(MainThreadMarker::new().unwrap());
#[allow(deprecated)]
let status_bar_frame = app.statusBarFrame();
UIEdgeInsets { top: status_bar_frame.size.height, left: 0.0, bottom: 0.0, right: 0.0 }
};
let insets = LogicalInsets::new(insets.top, insets.left, insets.bottom, insets.right);
insets.to_physical(self.scale_factor())
}
pub fn set_min_surface_size(&self, _dimensions: Option<Size>) {
warn!("`Window::set_min_surface_size` is ignored on iOS")
}
@@ -420,7 +429,6 @@ impl Inner {
self.window.id()
}
#[cfg(feature = "rwh_06")]
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
let mut window_handle = rwh_06::UiKitWindowHandle::new({
let ui_view = Retained::as_ptr(&self.view) as _;
@@ -509,35 +517,6 @@ impl Window {
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
window.makeKeyAndVisible();
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `SurfaceResized`
// event on window creation if the DPI factor != 1.0
let scale_factor = view.contentScaleFactor();
let scale_factor = scale_factor as f64;
if scale_factor != 1.0 {
let bounds = view.bounds();
let screen = window.screen();
let screen_space = screen.coordinateSpace();
let screen_frame = view.convertRect_toCoordinateSpace(bounds, &screen_space);
let size = LogicalSize {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
};
app_state::handle_nonuser_events(
mtm,
std::iter::once(EventWrapper::ScaleFactorChanged(app_state::ScaleFactorChanged {
window: window.clone(),
scale_factor,
suggested_size: size.to_physical(scale_factor),
}))
.chain(std::iter::once(EventWrapper::StaticEvent(
Event::WindowEvent {
window_id: window.id(),
event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)),
},
))),
);
}
let inner = Inner { window, view_controller, view, gl_or_metal_backed };
Ok(Window { inner: MainThreadBound::new(inner, mtm) })
}
@@ -546,7 +525,6 @@ impl Window {
self.inner.get_on_main(|inner| f(inner))
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_window_handle_rwh_06(
&self,
@@ -558,7 +536,6 @@ impl Window {
}
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_display_handle_rwh_06(
&self,
@@ -567,7 +544,6 @@ impl Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?;
@@ -575,7 +551,6 @@ impl rwh_06::HasDisplayHandle for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_window_handle_rwh_06()?;
@@ -604,12 +579,12 @@ impl CoreWindow for Window {
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
}
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
fn surface_position(&self) -> PhysicalPosition<i32> {
self.maybe_wait_on_main(|delegate| delegate.surface_position())
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
self.maybe_wait_on_main(|delegate| delegate.outer_position())
}
fn set_outer_position(&self, position: Position) {
@@ -628,6 +603,10 @@ impl CoreWindow for Window {
self.maybe_wait_on_main(|delegate| delegate.outer_size())
}
fn safe_area(&self) -> PhysicalInsets<u32> {
self.maybe_wait_on_main(|delegate| delegate.safe_area())
}
fn set_min_surface_size(&self, min_size: Option<Size>) {
self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size))
}
@@ -815,12 +794,10 @@ impl CoreWindow for Window {
})
}
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self
}
@@ -888,7 +865,7 @@ impl Inner {
impl Inner {
fn screen_frame(&self) -> CGRect {
self.rect_to_screen_space(self.window.bounds())
self.rect_to_screen_space(self.window.frame())
}
fn rect_to_screen_space(&self, rect: CGRect) -> CGRect {
@@ -900,43 +877,6 @@ impl Inner {
let screen_space = self.window.screen().coordinateSpace();
self.window.convertRect_fromCoordinateSpace(rect, &screen_space)
}
fn safe_area_screen_space(&self) -> CGRect {
let bounds = self.window.bounds();
if app_state::os_capabilities().safe_area {
let safe_area = self.window.safeAreaInsets();
let safe_bounds = CGRect {
origin: CGPoint {
x: bounds.origin.x + safe_area.left,
y: bounds.origin.y + safe_area.top,
},
size: CGSize {
width: bounds.size.width - safe_area.left - safe_area.right,
height: bounds.size.height - safe_area.top - safe_area.bottom,
},
};
self.rect_to_screen_space(safe_bounds)
} else {
let screen_frame = self.rect_to_screen_space(bounds);
let status_bar_frame = {
let app = UIApplication::sharedApplication(MainThreadMarker::new().unwrap());
#[allow(deprecated)]
app.statusBarFrame()
};
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
(screen_frame.origin.y, screen_frame.size.height)
} else {
let y = status_bar_frame.size.height;
let height = screen_frame.size.height
- (status_bar_frame.size.height - screen_frame.origin.y);
(y, height)
};
CGRect {
origin: CGPoint { x: screen_frame.origin.x, y },
size: CGSize { width: screen_frame.size.width, height },
}
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]

View File

@@ -35,6 +35,9 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes,
// I can only hope they agree on what the keycodes mean.
//
// The mapping here is heavily influenced by Firefox' source:
// https://searchfox.org/mozilla-central/rev/c597e9c789ad36af84a0370d395be066b7dc94f4/widget/NativeKeyToDOMCodeName.h
//
// Some of the keycodes are likely superfluous for our purposes, and some are ones which are
// difficult to test the correctness of, or discover the purpose of. Because of this, they've
// either been commented out here, or not included at all.
@@ -166,23 +169,23 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
125 => KeyCode::SuperLeft,
126 => KeyCode::SuperRight,
127 => KeyCode::ContextMenu,
// 128 => KeyCode::STOP,
// 129 => KeyCode::AGAIN,
// 130 => KeyCode::PROPS,
// 131 => KeyCode::UNDO,
// 132 => KeyCode::FRONT,
// 133 => KeyCode::COPY,
// 134 => KeyCode::OPEN,
// 135 => KeyCode::PASTE,
// 136 => KeyCode::FIND,
// 137 => KeyCode::CUT,
// 138 => KeyCode::HELP,
128 => KeyCode::BrowserStop,
129 => KeyCode::Again,
130 => KeyCode::Props,
131 => KeyCode::Undo,
132 => KeyCode::Select, // FRONT
133 => KeyCode::Copy,
134 => KeyCode::Open,
135 => KeyCode::Paste,
136 => KeyCode::Find,
137 => KeyCode::Cut,
138 => KeyCode::Help,
// 139 => KeyCode::MENU,
// 140 => KeyCode::CALC,
140 => KeyCode::LaunchApp2, // CALC
// 141 => KeyCode::SETUP,
// 142 => KeyCode::SLEEP,
// 143 => KeyCode::WAKEUP,
// 144 => KeyCode::FILE,
143 => KeyCode::WakeUp,
144 => KeyCode::LaunchApp1, // FILE
// 145 => KeyCode::SENDFILE,
// 146 => KeyCode::DELETEFILE,
// 147 => KeyCode::XFER,
@@ -193,13 +196,13 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 152 => KeyCode::COFFEE,
// 153 => KeyCode::ROTATE_DISPLAY,
// 154 => KeyCode::CYCLEWINDOWS,
// 155 => KeyCode::MAIL,
// 156 => KeyCode::BOOKMARKS,
155 => KeyCode::LaunchMail,
156 => KeyCode::BrowserFavorites, // BOOKMARKS
// 157 => KeyCode::COMPUTER,
// 158 => KeyCode::BACK,
// 159 => KeyCode::FORWARD,
158 => KeyCode::BrowserBack,
159 => KeyCode::BrowserForward,
// 160 => KeyCode::CLOSECD,
// 161 => KeyCode::EJECTCD,
161 => KeyCode::Eject, // EJECTCD
// 162 => KeyCode::EJECTCLOSECD,
163 => KeyCode::MediaTrackNext,
164 => KeyCode::MediaPlayPause,
@@ -209,9 +212,9 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 168 => KeyCode::REWIND,
// 169 => KeyCode::PHONE,
// 170 => KeyCode::ISO,
// 171 => KeyCode::CONFIG,
// 172 => KeyCode::HOMEPAGE,
// 173 => KeyCode::REFRESH,
171 => KeyCode::MediaSelect, // CONFIG
172 => KeyCode::BrowserHome,
173 => KeyCode::BrowserRefresh,
// 174 => KeyCode::EXIT,
// 175 => KeyCode::MOVE,
// 176 => KeyCode::EDIT,
@@ -250,7 +253,7 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 214 => KeyCode::QUESTION,
// 215 => KeyCode::EMAIL,
// 216 => KeyCode::CHAT,
// 217 => KeyCode::SEARCH,
217 => KeyCode::BrowserSearch,
// 218 => KeyCode::CONNECT,
// 219 => KeyCode::FINANCE,
// 220 => KeyCode::SPORT,
@@ -419,10 +422,32 @@ pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option<u32> {
KeyCode::SuperLeft => Some(125),
KeyCode::SuperRight => Some(126),
KeyCode::ContextMenu => Some(127),
KeyCode::BrowserStop => Some(128),
KeyCode::Again => Some(129),
KeyCode::Props => Some(130),
KeyCode::Undo => Some(131),
KeyCode::Select => Some(132),
KeyCode::Copy => Some(133),
KeyCode::Open => Some(134),
KeyCode::Paste => Some(135),
KeyCode::Find => Some(136),
KeyCode::Cut => Some(137),
KeyCode::Help => Some(138),
KeyCode::LaunchApp2 => Some(140),
KeyCode::WakeUp => Some(143),
KeyCode::LaunchApp1 => Some(144),
KeyCode::LaunchMail => Some(155),
KeyCode::BrowserFavorites => Some(156),
KeyCode::BrowserBack => Some(158),
KeyCode::BrowserForward => Some(159),
KeyCode::Eject => Some(161),
KeyCode::MediaTrackNext => Some(163),
KeyCode::MediaPlayPause => Some(164),
KeyCode::MediaTrackPrevious => Some(165),
KeyCode::MediaStop => Some(166),
KeyCode::MediaSelect => Some(171),
KeyCode::BrowserHome => Some(172),
KeyCode::BrowserRefresh => Some(173),
KeyCode::F13 => Some(183),
KeyCode::F14 => Some(184),
KeyCode::F15 => Some(185),
@@ -435,6 +460,7 @@ pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option<u32> {
KeyCode::F22 => Some(192),
KeyCode::F23 => Some(193),
KeyCode::F24 => Some(194),
KeyCode::BrowserSearch => Some(217),
_ => None,
}
}

View File

@@ -183,7 +183,7 @@ pub struct KeyContext<'a> {
scratch_buffer: &'a mut Vec<u8>,
}
impl<'a> KeyContext<'a> {
impl KeyContext<'_> {
pub fn process_key_event(
&mut self,
keycode: u32,

View File

@@ -107,24 +107,6 @@ impl Default for PlatformSpecificWindowAttributes {
pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported>>> =
Lazy::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)));
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FingerId {
#[cfg(x11_platform)]
X(x11::FingerId),
#[cfg(wayland_platform)]
Wayland(wayland::FingerId),
}
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
#[cfg(wayland_platform)]
return FingerId::Wayland(wayland::FingerId::dummy());
#[cfg(all(not(wayland_platform), x11_platform))]
return FingerId::X(x11::FingerId::dummy());
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum MonitorHandle {
#[cfg(x11_platform)]
@@ -295,14 +277,6 @@ pub enum EventLoop {
X(x11::EventLoop),
}
#[derive(Clone)]
pub enum EventLoopProxy {
#[cfg(x11_platform)]
X(x11::EventLoopProxy),
#[cfg(wayland_platform)]
Wayland(wayland::EventLoopProxy),
}
impl EventLoop {
pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes,
@@ -386,15 +360,18 @@ impl EventLoop {
}
}
pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app))
pub fn run<A: ApplicationHandler>(
self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run(init_closure))
}
pub fn run_app_on_demand<A: ApplicationHandler>(
pub fn run_on_demand<A: ApplicationHandler>(
&mut self,
app: A,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app_on_demand(app))
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(init_closure))
}
pub fn pump_app_events<A: ApplicationHandler>(
@@ -422,65 +399,6 @@ impl AsRawFd for EventLoop {
}
}
impl EventLoopProxy {
pub fn wake_up(&self) {
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.wake_up())
}
}
#[derive(Clone)]
#[allow(dead_code)]
pub(crate) enum OwnedDisplayHandle {
#[cfg(x11_platform)]
X(Arc<XConnection>),
#[cfg(wayland_platform)]
Wayland(wayland_client::Connection),
}
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
use std::ptr::NonNull;
match self {
#[cfg(x11_platform)]
Self::X(xconn) => Ok(rwh_06::XlibDisplayHandle::new(
NonNull::new(xconn.display.cast()),
xconn.default_screen_index() as _,
)
.into()),
#[cfg(wayland_platform)]
Self::Wayland(conn) => {
use sctk::reexports::client::Proxy;
Ok(rwh_06::WaylandDisplayHandle::new(
NonNull::new(conn.display().id().as_ptr().cast()).unwrap(),
)
.into())
},
}
}
}
impl PartialEq for OwnedDisplayHandle {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
#[cfg(x11_platform)]
(Self::X(this), Self::X(other)) => Arc::as_ptr(this).eq(&Arc::as_ptr(other)),
#[cfg(wayland_platform)]
(Self::Wayland(this), Self::Wayland(other)) => this.eq(other),
#[cfg(all(x11_platform, wayland_platform))]
_ => false,
}
}
}
impl Eq for OwnedDisplayHandle {}
/// Returns the minimum `Option<Duration>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`)

View File

@@ -16,7 +16,10 @@ use crate::cursor::OnlyCursorImage;
use crate::dpi::LogicalSize;
use crate::error::{EventLoopError, OsError, RequestError};
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::PlatformCustomCursor;
@@ -25,12 +28,13 @@ use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme}
mod proxy;
pub mod sink;
pub use proxy::EventLoopProxy;
use proxy::EventLoopProxy;
use sink::EventSink;
use super::state::{WindowCompositorUpdate, WinitState};
use super::window::state::FrameCallbackState;
use super::{logical_to_physical_rounded, WindowId};
pub use crate::event_loop::EventLoopProxy as CoreEventLoopProxy;
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
@@ -48,7 +52,7 @@ pub struct EventLoop {
wayland_dispatcher: WaylandDispatcher,
/// Connection to the wayland server.
connection: Connection,
handle: Arc<OwnedDisplayHandle>,
/// Event loop window target.
active_event_loop: ActiveEventLoop,
@@ -116,11 +120,12 @@ impl EventLoop {
})
.map_err(|err| os_error!(err))?;
let handle = Arc::new(OwnedDisplayHandle::new(connection));
let active_event_loop = ActiveEventLoop {
connection: connection.clone(),
handle: handle.clone(),
wayland_dispatcher: wayland_dispatcher.clone(),
event_loop_awakener,
event_loop_proxy: EventLoopProxy::new(ping),
event_loop_proxy: EventLoopProxy::new(ping).into(),
queue_handle,
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None),
@@ -132,7 +137,7 @@ impl EventLoop {
compositor_updates: Vec::new(),
buffer_sink: EventSink::default(),
window_ids: Vec::new(),
connection,
handle,
wayland_dispatcher,
event_loop,
active_event_loop,
@@ -141,15 +146,21 @@ impl EventLoop {
Ok(event_loop)
}
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
pub fn run<A: ApplicationHandler>(
mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
}
pub fn run_app_on_demand<A: ApplicationHandler>(
pub fn run_on_demand<A: ApplicationHandler>(
&mut self,
mut app: A,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.active_event_loop.clear_exit();
let mut app = init_closure(&self.active_event_loop);
let exit = loop {
match self.pump_app_events(None, &mut app) {
PumpStatus::Exit(0) => {
@@ -193,8 +204,6 @@ impl EventLoop {
if let Some(code) = self.exit_code() {
self.loop_running = false;
app.exiting(&self.active_event_loop);
PumpStatus::Exit(code)
} else {
PumpStatus::Continue
@@ -226,7 +235,7 @@ impl EventLoop {
//
// Checking for flush error is essential to perform an exit with error, since
// once we have a protocol error, we could get stuck retrying...
if self.connection.flush().is_err() {
if self.handle.connection.flush().is_err() {
self.set_exit_code(1);
return;
}
@@ -539,7 +548,7 @@ impl AsRawFd for EventLoop {
pub struct ActiveEventLoop {
/// Event loop proxy
event_loop_proxy: EventLoopProxy,
event_loop_proxy: CoreEventLoopProxy,
/// The event loop wakeup source.
pub event_loop_awakener: calloop::ping::Ping,
@@ -560,17 +569,13 @@ pub struct ActiveEventLoop {
/// Dispatcher of Wayland events.
pub wayland_dispatcher: WaylandDispatcher,
/// Connection to the wayland server.
pub connection: Connection,
/// Handle for the underlying event loop.
pub handle: Arc<OwnedDisplayHandle>,
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
crate::event_loop::EventLoopProxy {
event_loop_proxy: crate::platform_impl::EventLoopProxy::Wayland(
self.event_loop_proxy.clone(),
),
}
fn create_proxy(&self) -> CoreEventLoopProxy {
self.event_loop_proxy.clone()
}
fn set_control_flow(&self, control_flow: ControlFlow) {
@@ -631,13 +636,10 @@ impl RootActiveEventLoop for ActiveEventLoop {
None
}
fn owned_display_handle(&self) -> crate::event_loop::OwnedDisplayHandle {
crate::event_loop::OwnedDisplayHandle {
platform: crate::platform_impl::OwnedDisplayHandle::Wayland(self.connection.clone()),
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(self.handle.clone())
}
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
@@ -657,8 +659,23 @@ impl ActiveEventLoop {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
self.handle.display_handle()
}
}
pub struct OwnedDisplayHandle {
pub(crate) connection: Connection,
}
impl OwnedDisplayHandle {
fn new(connection: Connection) -> Self {
Self { connection }
}
}
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
use sctk::reexports::client::Proxy;

View File

@@ -1,19 +1,30 @@
//! An event loop proxy.
use std::sync::Arc;
use sctk::reexports::calloop::ping::Ping;
use crate::event_loop::{EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider};
/// A handle that can be sent across the threads and used to wake up the `EventLoop`.
#[derive(Clone)]
pub struct EventLoopProxy {
ping: Ping,
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.ping.ping();
}
}
impl EventLoopProxy {
pub fn new(ping: Ping) -> Self {
Self { ping }
}
}
pub fn wake_up(&self) {
self.ping.ping();
impl From<EventLoopProxy> for CoreEventLoopProxy {
fn from(value: EventLoopProxy) -> Self {
CoreEventLoopProxy::new(Arc::new(value))
}
}

View File

@@ -1,6 +1,6 @@
//! Winit's Wayland backend.
pub use event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
pub use event_loop::{ActiveEventLoop, EventLoop};
pub use output::{MonitorHandle, VideoModeHandle};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::Proxy;
@@ -17,16 +17,6 @@ mod state;
mod types;
mod window;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId(i32);
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId(0)
}
}
/// Get the WindowId out of the surface.
#[inline]
fn make_wid(surface: &WlSurface) -> WindowId {

View File

@@ -40,6 +40,9 @@ pub struct WinitSeatState {
/// The mapping from touched points to the surfaces they're present.
touch_map: AHashMap<i32, TouchPoint>,
/// Id of the first touch event.
first_touch_id: Option<i32>,
/// The text input bound on the seat.
text_input: Option<Arc<ZwpTextInputV3>>,

View File

@@ -124,6 +124,7 @@ impl PointerHandler for WinitState {
PointerEventKind::Enter { .. } => {
self.events_sink.push_window_event(
WindowEvent::PointerEntered {
primary: true,
device_id: None,
position,
kind: PointerKind::Mouse,
@@ -144,6 +145,7 @@ impl PointerHandler for WinitState {
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
primary: true,
device_id: None,
position: Some(position),
kind: PointerKind::Mouse,
@@ -154,6 +156,7 @@ impl PointerHandler for WinitState {
PointerEventKind::Motion { .. } => {
self.events_sink.push_window_event(
WindowEvent::PointerMoved {
primary: true,
device_id: None,
position,
source: PointerSource::Mouse,
@@ -174,6 +177,7 @@ impl PointerHandler for WinitState {
};
self.events_sink.push_window_event(
WindowEvent::PointerButton {
primary: true,
device_id: None,
state,
position,

View File

@@ -8,9 +8,9 @@ use sctk::seat::touch::{TouchData, TouchHandler};
use tracing::warn;
use crate::dpi::LogicalPosition;
use crate::event::{ButtonSource, ElementState, PointerKind, PointerSource, WindowEvent};
use crate::event::{ButtonSource, ElementState, FingerId, PointerKind, PointerSource, WindowEvent};
use crate::platform_impl::wayland;
use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, FingerId};
impl TouchHandler for WinitState {
fn down(
@@ -40,15 +40,20 @@ impl TouchHandler for WinitState {
// Update the state of the point.
let location = LogicalPosition::<f64>::from(position);
// Only update primary finger once we don't have any touch.
if seat_state.touch_map.is_empty() {
seat_state.first_touch_id = Some(id);
}
let primary = seat_state.first_touch_id == Some(id);
seat_state.touch_map.insert(id, TouchPoint { surface, location });
let position = location.to_physical(scale_factor);
let finger_id =
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
let finger_id = FingerId::from_raw(id as usize);
self.events_sink.push_window_event(
WindowEvent::PointerEntered {
device_id: None,
primary,
position,
kind: PointerKind::Touch(finger_id),
},
@@ -57,6 +62,7 @@ impl TouchHandler for WinitState {
self.events_sink.push_window_event(
WindowEvent::PointerButton {
device_id: None,
primary,
state: ElementState::Pressed,
position,
button: ButtonSource::Touch { finger_id, force: None },
@@ -88,6 +94,15 @@ impl TouchHandler for WinitState {
None => return,
};
// Update the primary touch point.
let primary = seat_state.first_touch_id == Some(id);
// Reset primary finger once all the other fingers are lifted to not transfer primary
// finger to some other finger and still accept it when it's briefly moved between the
// windows.
if seat_state.touch_map.is_empty() {
seat_state.first_touch_id = None;
}
let window_id = wayland::make_wid(&touch_point.surface);
let scale_factor = match self.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().scale_factor(),
@@ -95,12 +110,12 @@ impl TouchHandler for WinitState {
};
let position = touch_point.location.to_physical(scale_factor);
let finger_id =
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
let finger_id = FingerId::from_raw(id as usize);
self.events_sink.push_window_event(
WindowEvent::PointerButton {
device_id: None,
primary,
state: ElementState::Released,
position,
button: ButtonSource::Touch { finger_id, force: None },
@@ -110,6 +125,7 @@ impl TouchHandler for WinitState {
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
device_id: None,
primary,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
@@ -140,6 +156,8 @@ impl TouchHandler for WinitState {
None => return,
};
let primary = seat_state.first_touch_id == Some(id);
let window_id = wayland::make_wid(&touch_point.surface);
let scale_factor = match self.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().scale_factor(),
@@ -151,11 +169,10 @@ impl TouchHandler for WinitState {
self.events_sink.push_window_event(
WindowEvent::PointerMoved {
device_id: None,
primary,
position: touch_point.location.to_physical(scale_factor),
source: PointerSource::Touch {
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
FingerId(id),
)),
finger_id: FingerId::from_raw(id as usize),
force: None,
},
},
@@ -179,19 +196,21 @@ impl TouchHandler for WinitState {
None => return,
};
let primary = seat_state.first_touch_id == Some(id);
let position = touch_point.location.to_physical(scale_factor);
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
device_id: None,
primary,
position: Some(position),
kind: PointerKind::Touch(crate::event::FingerId(
crate::platform_impl::FingerId::Wayland(FingerId(id)),
)),
kind: PointerKind::Touch(FingerId::from_raw(id as usize)),
},
window_id,
);
}
seat_state.first_touch_id = None;
}
fn shape(

View File

@@ -17,7 +17,7 @@ use super::output::MonitorHandle;
use super::state::WinitState;
use super::types::xdg_activation::XdgActivationTokenData;
use super::ActiveEventLoop;
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{Ime, WindowEvent};
use crate::event_loop::AsyncRequestSerial;
@@ -87,7 +87,7 @@ impl Window {
let compositor = state.compositor_state.clone();
let xdg_activation =
state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone());
let display = event_loop_window_target.connection.display();
let display = event_loop_window_target.handle.connection.display();
let size: Size = attributes.surface_size.unwrap_or(LogicalSize::new(800., 600.).into());
@@ -103,7 +103,7 @@ impl Window {
state.xdg_shell.create_window(surface.clone(), default_decorations, &queue_handle);
let mut window_state = WindowState::new(
event_loop_window_target.connection.clone(),
event_loop_window_target.handle.clone(),
&event_loop_window_target.queue_handle,
&state,
size,
@@ -248,7 +248,6 @@ impl Drop for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::WaylandWindowHandle::new({
@@ -260,7 +259,6 @@ impl rwh_06::HasWindowHandle for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::WaylandDisplayHandle::new({
@@ -305,9 +303,8 @@ impl CoreWindow for Window {
crate::platform_impl::common::xkb::reset_dead_keys()
}
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Err(NotSupportedError::new("window position information is not available on Wayland")
.into())
fn surface_position(&self) -> PhysicalPosition<i32> {
(0, 0).into()
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
@@ -338,6 +335,10 @@ impl CoreWindow for Window {
super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
}
fn safe_area(&self) -> PhysicalInsets<u32> {
PhysicalInsets::new(0, 0, 0, 0)
}
fn set_min_surface_size(&self, min_size: Option<Size>) {
let scale_factor = self.scale_factor();
let min_size = min_size.map(|size| size.to_logical(scale_factor));
@@ -648,13 +649,11 @@ impl CoreWindow for Window {
}
/// Get the raw-window-handle v0.6 display handle.
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
/// Get the raw-window-handle v0.6 window handle.
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self
}

View File

@@ -10,7 +10,7 @@ use sctk::reexports::client::backend::ObjectId;
use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_shm::WlShm;
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
use sctk::reexports::client::{Proxy, QueueHandle};
use sctk::reexports::csd_frame::{
DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowState as XdgWindowState,
};
@@ -31,6 +31,7 @@ use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
use crate::cursor::CustomCursor as RootCustomCursor;
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::platform_impl::wayland::event_loop::OwnedDisplayHandle;
use crate::platform_impl::wayland::logical_to_physical_rounded;
use crate::platform_impl::wayland::seat::{
PointerConstraintsState, WinitPointerData, WinitPointerDataExt, ZwpTextInputV3Ext,
@@ -52,7 +53,7 @@ const MIN_WINDOW_SIZE: LogicalSize<u32> = LogicalSize::new(2, 1);
/// The state of the window which is being updated from the [`WinitState`].
pub struct WindowState {
/// The connection to Wayland server.
pub connection: Connection,
pub handle: Arc<OwnedDisplayHandle>,
/// The `Shm` to set cursor.
pub shm: WlShm,
@@ -161,7 +162,7 @@ pub struct WindowState {
impl WindowState {
/// Create new window state.
pub fn new(
connection: Connection,
handle: Arc<OwnedDisplayHandle>,
queue_handle: &QueueHandle<WinitState>,
winit_state: &WinitState,
initial_size: Size,
@@ -183,7 +184,7 @@ impl WindowState {
blur: None,
blur_manager: winit_state.kwin_blur_manager.clone(),
compositor,
connection,
handle,
csd_fails: false,
cursor_grab_mode: GrabState::new(),
selected_cursor: Default::default(),
@@ -693,7 +694,7 @@ impl WindowState {
}
self.apply_on_pointer(|pointer, _| {
if pointer.set_cursor(&self.connection, cursor_icon).is_err() {
if pointer.set_cursor(&self.handle.connection, cursor_icon).is_err() {
warn!("Failed to set cursor to {:?}", cursor_icon);
}
})

View File

@@ -165,14 +165,14 @@ fn push_display(buffer: &mut Vec<u8>, display: &impl std::fmt::Display) {
buffer: &'a mut Vec<u8>,
}
impl<'a> std::fmt::Write for Writer<'a> {
impl std::fmt::Write for Writer<'_> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.buffer.extend_from_slice(s.as_bytes());
Ok(())
}
}
write!(Writer { buffer }, "{}", display).unwrap();
write!(Writer { buffer }, "{display}").unwrap();
}
#[cfg(test)]

View File

@@ -22,8 +22,9 @@ use xkbcommon_dl::xkb_mod_mask_t;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::event::{
ButtonSource, DeviceEvent, DeviceId, ElementState, Event, Ime, MouseButton, MouseScrollDelta,
PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent,
ButtonSource, DeviceEvent, DeviceId, ElementState, Event, FingerId, Ime, MouseButton,
MouseScrollDelta, PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, TouchPhase,
WindowEvent,
};
use crate::keyboard::ModifiersState;
use crate::platform_impl::common::xkb::{self, XkbState};
@@ -33,7 +34,7 @@ use crate::platform_impl::platform::x11::ActiveEventLoop;
use crate::platform_impl::x11::atoms::*;
use crate::platform_impl::x11::util::cookie::GenericEventCookie;
use crate::platform_impl::x11::{
mkdid, mkfid, mkwid, util, CookieResultExt, Device, DeviceInfo, Dnd, DndState, ImeReceiver,
mkdid, mkwid, util, CookieResultExt, Device, DeviceInfo, Dnd, DndState, ImeReceiver,
ScrollOrientation, UnownedWindow, WindowId,
};
@@ -1034,12 +1035,14 @@ impl EventProcessor {
let event = match event.detail as u32 {
xlib::Button1 => WindowEvent::PointerButton {
device_id,
primary: true,
state,
position,
button: MouseButton::Left.into(),
},
xlib::Button2 => WindowEvent::PointerButton {
device_id,
primary: true,
state,
position,
button: MouseButton::Middle.into(),
@@ -1047,6 +1050,7 @@ impl EventProcessor {
xlib::Button3 => WindowEvent::PointerButton {
device_id,
primary: true,
state,
position,
button: MouseButton::Right.into(),
@@ -1069,6 +1073,7 @@ impl EventProcessor {
},
8 => WindowEvent::PointerButton {
device_id,
primary: true,
state,
position,
button: MouseButton::Back.into(),
@@ -1076,12 +1081,14 @@ impl EventProcessor {
9 => WindowEvent::PointerButton {
device_id,
primary: true,
state,
position,
button: MouseButton::Forward.into(),
},
x => WindowEvent::PointerButton {
device_id,
primary: true,
state,
position,
button: MouseButton::Other(x as u16).into(),
@@ -1116,6 +1123,7 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerMoved {
device_id,
primary: true,
position,
source: PointerSource::Mouse,
},
@@ -1205,6 +1213,7 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerEntered {
device_id,
primary: true,
position,
kind: PointerKind::Mouse,
},
@@ -1229,6 +1238,7 @@ impl EventProcessor {
window_id: mkwid(window),
event: WindowEvent::PointerLeft {
device_id: Some(mkdid(event.deviceid as xinput::DeviceId)),
primary: true,
position: Some(PhysicalPosition::new(event.event_x, event.event_y)),
kind: PointerKind::Mouse,
},
@@ -1289,7 +1299,12 @@ impl EventProcessor {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved { device_id, position, source: PointerSource::Mouse },
event: WindowEvent::PointerMoved {
device_id,
primary: true,
position,
source: PointerSource::Mouse,
},
};
callback(&self.target, event);
}
@@ -1360,11 +1375,14 @@ impl EventProcessor {
// Mouse cursor position changes when touch events are received.
// Only the first concurrently active touch ID moves the mouse cursor.
if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase) {
let is_first_touch =
is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase);
if is_first_touch {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
primary: true,
position: position.cast(),
source: PointerSource::Mouse,
},
@@ -1373,7 +1391,7 @@ impl EventProcessor {
}
let device_id = Some(mkdid(xev.deviceid as xinput::DeviceId));
let finger_id = mkfid(id);
let finger_id = FingerId::from_raw(id as usize);
match phase {
xinput2::XI_TouchBegin => {
@@ -1381,6 +1399,7 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerEntered {
device_id,
primary: is_first_touch,
position,
kind: PointerKind::Touch(finger_id),
},
@@ -1390,6 +1409,7 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerButton {
device_id,
primary: is_first_touch,
state: ElementState::Pressed,
position,
button: ButtonSource::Touch { finger_id, force: None },
@@ -1402,6 +1422,7 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerMoved {
device_id,
primary: is_first_touch,
position,
source: PointerSource::Touch { finger_id, force: None },
},
@@ -1413,6 +1434,7 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerButton {
device_id,
primary: is_first_touch,
state: ElementState::Released,
position,
button: ButtonSource::Touch { finger_id, force: None },
@@ -1423,6 +1445,7 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerLeft {
device_id,
primary: is_first_touch,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},

View File

@@ -27,13 +27,14 @@ use crate::error::{EventLoopError, RequestError};
use crate::event::{DeviceId, Event, StartCause, WindowEvent};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
OwnedDisplayHandle as RootOwnedDisplayHandle,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::common::xkb::Context;
use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::x11::window::Window;
use crate::platform_impl::{OwnedDisplayHandle, PlatformCustomCursor};
use crate::platform_impl::PlatformCustomCursor;
use crate::window::{
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
WindowAttributes, WindowId,
@@ -139,7 +140,7 @@ pub struct ActiveEventLoop {
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
redraw_sender: WakeSender<WindowId>,
activation_sender: WakeSender<ActivationToken>,
event_loop_proxy: EventLoopProxy,
event_loop_proxy: CoreEventLoopProxy,
device_events: Cell<DeviceEvents>,
}
@@ -306,7 +307,7 @@ impl EventLoop {
sender: activation_token_sender, // not used again so no clone
waker: waker.clone(),
},
event_loop_proxy,
event_loop_proxy: event_loop_proxy.into(),
device_events: Default::default(),
};
@@ -372,15 +373,21 @@ impl EventLoop {
&self.event_processor.target
}
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
pub fn run<A: ApplicationHandler>(
mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
}
pub fn run_app_on_demand<A: ApplicationHandler>(
pub fn run_on_demand<A: ApplicationHandler>(
&mut self,
mut app: A,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.event_processor.target.clear_exit();
let mut app = init_closure(&self.event_processor.target);
let exit = loop {
match self.pump_app_events(None, &mut app) {
PumpStatus::Exit(0) => {
@@ -428,8 +435,6 @@ impl EventLoop {
if let Some(code) = self.exit_code() {
self.loop_running = false;
app.exiting(self.window_target());
PumpStatus::Exit(code)
} else {
PumpStatus::Continue
@@ -647,21 +652,6 @@ impl ActiveEventLoop {
.expect_then_ignore_error("Failed to update device event filter");
}
#[cfg(feature = "rwh_06")]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
let display_handle = rwh_06::XlibDisplayHandle::new(
// SAFETY: display will never be null
Some(
std::ptr::NonNull::new(self.xconn.display as *mut _)
.expect("X11 display should never be null"),
),
self.xconn.default_screen_index() as c_int,
);
Ok(display_handle.into())
}
pub(crate) fn clear_exit(&self) {
self.exit.set(None)
}
@@ -676,12 +666,8 @@ impl ActiveEventLoop {
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
crate::event_loop::EventLoopProxy {
event_loop_proxy: crate::platform_impl::EventLoopProxy::X(
self.event_loop_proxy.clone(),
),
}
fn create_proxy(&self) -> CoreEventLoopProxy {
self.event_loop_proxy.clone()
}
fn create_window(
@@ -743,28 +729,18 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.exit.get().is_some()
}
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
let handle = OwnedDisplayHandle::X(self.x_connection().clone());
RootOwnedDisplayHandle { platform: handle }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(self.x_connection().clone())
}
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?;
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}
impl EventLoopProxy {
pub fn wake_up(&self) {
self.ping.ping();
self.xconn.display_handle()
}
}
@@ -790,14 +766,14 @@ impl<'a> DeviceInfo<'a> {
}
}
impl<'a> Drop for DeviceInfo<'a> {
impl Drop for DeviceInfo<'_> {
fn drop(&mut self) {
assert!(!self.info.is_null());
unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
}
}
impl<'a> Deref for DeviceInfo<'a> {
impl Deref for DeviceInfo<'_> {
type Target = [ffi::XIDeviceInfo];
fn deref(&self) -> &Self::Target {
@@ -805,28 +781,29 @@ impl<'a> Deref for DeviceInfo<'a> {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId(u32);
impl FingerId {
#[cfg(test)]
#[allow(unused)]
pub const fn dummy() -> Self {
FingerId(0)
}
}
#[derive(Clone)]
pub struct EventLoopProxy {
ping: Ping,
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.ping.ping();
}
}
impl EventLoopProxy {
fn new(ping: Ping) -> Self {
Self { ping }
}
}
impl From<EventLoopProxy> for CoreEventLoopProxy {
fn from(value: EventLoopProxy) -> Self {
CoreEventLoopProxy::new(Arc::new(value))
}
}
/// Generic sum error type for X11 errors.
#[derive(Debug)]
pub enum X11Error {
@@ -870,24 +847,24 @@ pub enum X11Error {
impl fmt::Display for X11Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X11Error::Xlib(e) => write!(f, "Xlib error: {}", e),
X11Error::Connect(e) => write!(f, "X11 connection error: {}", e),
X11Error::Connection(e) => write!(f, "X11 connection error: {}", e),
X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {}", e),
X11Error::GetProperty(e) => write!(f, "Failed to get X property {}", e),
X11Error::X11(e) => write!(f, "X11 error: {:?}", e),
X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {}", s),
X11Error::Xlib(e) => write!(f, "Xlib error: {e}"),
X11Error::Connect(e) => write!(f, "X11 connection error: {e}"),
X11Error::Connection(e) => write!(f, "X11 connection error: {e}"),
X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {e}"),
X11Error::GetProperty(e) => write!(f, "Failed to get X property {e}"),
X11Error::X11(e) => write!(f, "X11 error: {e:?}"),
X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {s}"),
X11Error::InvalidActivationToken(s) => write!(
f,
"Invalid activation token: {}",
std::str::from_utf8(s).unwrap_or("<invalid utf8>")
),
X11Error::MissingExtension(s) => write!(f, "Missing X11 extension: {}", s),
X11Error::MissingExtension(s) => write!(f, "Missing X11 extension: {s}"),
X11Error::NoSuchVisual(visualid) => {
write!(f, "Could not find a matching X11 visual for ID `{:x}`", visualid)
write!(f, "Could not find a matching X11 visual for ID `{visualid:x}`")
},
X11Error::XsettingsParse(err) => {
write!(f, "Failed to parse xsettings: {:?}", err)
write!(f, "Failed to parse xsettings: {err:?}")
},
X11Error::NoArgb32Format => {
f.write_str("winit only supports X11 displays with ARGB32 picture formats")
@@ -981,7 +958,7 @@ trait CookieResultExt {
fn expect_then_ignore_error(self, msg: &str);
}
impl<'a, E: fmt::Debug> CookieResultExt for Result<VoidCookie<'a>, E> {
impl<E: fmt::Debug> CookieResultExt for Result<VoidCookie<'_>, E> {
fn expect_then_ignore_error(self, msg: &str) {
self.expect(msg).ignore_error()
}
@@ -994,10 +971,6 @@ fn mkdid(w: xinput::DeviceId) -> DeviceId {
DeviceId::from_raw(w as i64)
}
fn mkfid(w: u32) -> crate::event::FingerId {
crate::event::FingerId(crate::platform_impl::FingerId::X(FingerId(w)))
}
#[derive(Debug)]
pub struct Device {
_name: String,

View File

@@ -280,7 +280,7 @@ impl XConnection {
let info = self
.xcb_connection()
.extension_information(randr::X11_EXTENSION_NAME)?
.ok_or_else(|| X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
.ok_or(X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
// Select input data.
let event_mask =

View File

@@ -67,15 +67,20 @@ pub struct FrameExtentsHeuristic {
}
impl FrameExtentsHeuristic {
pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) {
pub fn surface_position(&self) -> (i32, i32) {
use self::FrameExtentsHeuristicPath::*;
if self.heuristic_path != UnsupportedBordered {
(x - self.frame_extents.left as i32, y - self.frame_extents.top as i32)
(self.frame_extents.left as i32, self.frame_extents.top as i32)
} else {
(x, y)
(0, 0)
}
}
pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) {
let (left, top) = self.surface_position();
(x - left, y - top)
}
pub fn surface_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
(
width.saturating_add(

View File

@@ -19,7 +19,7 @@ impl<'a, T> XSmartPointer<'a, T> {
}
}
impl<'a, T> Deref for XSmartPointer<'a, T> {
impl<T> Deref for XSmartPointer<'_, T> {
type Target = T;
fn deref(&self) -> &T {
@@ -27,13 +27,13 @@ impl<'a, T> Deref for XSmartPointer<'a, T> {
}
}
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
impl<T> DerefMut for XSmartPointer<'_, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
impl<'a, T> Drop for XSmartPointer<'a, T> {
impl<T> Drop for XSmartPointer<'_, T> {
fn drop(&mut self) {
unsafe {
(self.xconn.xlib.XFree)(self.ptr as *mut _);

View File

@@ -21,7 +21,7 @@ use super::{
ffi, ActiveEventLoop, CookieResultExt, ImeRequest, ImeSender, VoidCookie, XConnection,
};
use crate::cursor::{Cursor, CustomCursor as RootCustomCursor};
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{Event, SurfaceSizeWriter, WindowEvent};
use crate::event_loop::AsyncRequestSerial;
@@ -82,8 +82,8 @@ impl CoreWindow for Window {
common::xkb::reset_dead_keys();
}
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
self.0.inner_position()
fn surface_position(&self) -> PhysicalPosition<i32> {
self.0.surface_position()
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
@@ -106,6 +106,10 @@ impl CoreWindow for Window {
self.0.outer_size()
}
fn safe_area(&self) -> PhysicalInsets<u32> {
self.0.safe_area()
}
fn set_min_surface_size(&self, min_size: Option<Size>) {
self.0.set_min_surface_size(min_size)
}
@@ -294,18 +298,15 @@ impl CoreWindow for Window {
.map(|inner| crate::monitor::MonitorHandle { inner })
}
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.0.raw_display_handle_rwh_06()?;
@@ -313,7 +314,6 @@ impl rwh_06::HasDisplayHandle for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.0.raw_window_handle_rwh_06()?;
@@ -443,15 +443,26 @@ impl UnownedWindow {
) -> Result<UnownedWindow, RequestError> {
let xconn = &event_loop.xconn;
let atoms = xconn.atoms();
#[cfg(feature = "rwh_06")]
let screen_id = match window_attrs.platform_specific.x11.screen_id {
Some(id) => id,
None => xconn.default_screen_index() as c_int,
};
let screen = {
let screen_id_usize = usize::try_from(screen_id)
.map_err(|_| NotSupportedError::new("screen id must be non-negative"))?;
xconn.xcb_connection().setup().roots.get(screen_id_usize).ok_or(
NotSupportedError::new("requested screen id not present in server's response"),
)?
};
let root = match window_attrs.parent_window.as_ref().map(|handle| handle.0) {
Some(rwh_06::RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window,
Some(rwh_06::RawWindowHandle::Xcb(handle)) => handle.window.get(),
Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"),
None => event_loop.root,
None => screen.root,
};
#[cfg(not(feature = "rwh_06"))]
let root = event_loop.root;
let mut monitors = leap!(xconn.available_monitors());
let guessed_monitor = if monitors.is_empty() {
@@ -506,18 +517,10 @@ impl UnownedWindow {
dimensions
};
let screen_id = match window_attrs.platform_specific.x11.screen_id {
Some(id) => id,
None => xconn.default_screen_index() as c_int,
};
// An iterator over all of the visuals combined with their depths.
let mut all_visuals = xconn
.xcb_connection()
.setup()
.roots
// An iterator over the visuals matching screen id combined with their depths.
let mut all_visuals = screen
.allowed_depths
.iter()
.flat_map(|root| &root.allowed_depths)
.flat_map(|depth| depth.visuals.iter().map(move |visual| (visual, depth.depth)));
// creating
@@ -806,6 +809,20 @@ impl UnownedWindow {
leap!(result).ignore_error();
}
// Select XInput2 events
let mask = xinput::XIEventMask::MOTION
| xinput::XIEventMask::BUTTON_PRESS
| xinput::XIEventMask::BUTTON_RELEASE
| xinput::XIEventMask::ENTER
| xinput::XIEventMask::LEAVE
| xinput::XIEventMask::FOCUS_IN
| xinput::XIEventMask::FOCUS_OUT
| xinput::XIEventMask::TOUCH_BEGIN
| xinput::XIEventMask::TOUCH_UPDATE
| xinput::XIEventMask::TOUCH_END;
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
.ignore_error();
// Set visibility (map window)
if window_attrs.visible {
leap!(xconn.xcb_connection().map_window(window.xwindow)).ignore_error();
@@ -829,20 +846,6 @@ impl UnownedWindow {
}
}
// Select XInput2 events
let mask = xinput::XIEventMask::MOTION
| xinput::XIEventMask::BUTTON_PRESS
| xinput::XIEventMask::BUTTON_RELEASE
| xinput::XIEventMask::ENTER
| xinput::XIEventMask::LEAVE
| xinput::XIEventMask::FOCUS_IN
| xinput::XIEventMask::FOCUS_OUT
| xinput::XIEventMask::TOUCH_BEGIN
| xinput::XIEventMask::TOUCH_UPDATE
| xinput::XIEventMask::TOUCH_END;
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
.ignore_error();
// Try to create input context for the window.
if let Some(ime) = event_loop.ime.as_ref() {
ime.borrow_mut()
@@ -1509,7 +1512,7 @@ impl UnownedWindow {
}
}
pub(crate) fn inner_position_physical(&self) -> (i32, i32) {
fn inner_position_physical(&self) -> (i32, i32) {
// This should be okay to unwrap since the only error XTranslateCoordinates can return
// is BadWindow, and if the window handle is bad we have bigger problems.
self.xconn
@@ -1519,8 +1522,14 @@ impl UnownedWindow {
}
#[inline]
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Ok(self.inner_position_physical().into())
pub fn surface_position(&self) -> PhysicalPosition<i32> {
let extents = self.shared_state_lock().frame_extents.clone();
if let Some(extents) = extents {
extents.surface_position().into()
} else {
self.update_cached_frame_extents();
self.surface_position()
}
}
pub(crate) fn set_position_inner(
@@ -1583,6 +1592,10 @@ impl UnownedWindow {
}
}
fn safe_area(&self) -> PhysicalInsets<u32> {
PhysicalInsets::new(0, 0, 0, 0)
}
pub(crate) fn request_surface_size_physical(&self, width: u32, height: u32) {
self.xconn
.xcb_connection()
@@ -1990,7 +2003,7 @@ impl UnownedWindow {
.query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER)
.map_err(|err| os_error!(err))?;
let window_position = self.inner_position()?;
let window_position = self.inner_position_physical();
let atoms = self.xconn.atoms();
let message = atoms[_NET_WM_MOVERESIZE];
@@ -2017,8 +2030,8 @@ impl UnownedWindow {
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
),
[
(window_position.x + xinput_fp1616_to_float(pointer.win_x) as i32) as u32,
(window_position.y + xinput_fp1616_to_float(pointer.win_y) as i32) as u32,
(window_position.0 + xinput_fp1616_to_float(pointer.win_x) as i32) as u32,
(window_position.1 + xinput_fp1616_to_float(pointer.win_y) as i32) as u32,
action.try_into().unwrap(),
1, // Button 1
1,
@@ -2032,12 +2045,19 @@ impl UnownedWindow {
}
#[inline]
pub fn set_ime_cursor_area(&self, spot: Position, _size: Size) {
let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
let PhysicalPosition { x, y } = spot.to_physical::<i16>(self.scale_factor());
let PhysicalSize { width, height } = size.to_physical::<i16>(self.scale_factor());
// We only currently support reporting a caret position via XIM.
// No IM servers currently process preedit area information from XIM clients
// and it is unclear this is even part of the standard protocol.
// Fcitx and iBus both assume that the position reported is at the insertion
// caret, and by default will place the candidate window under and to the
// right of the reported point.
let _ = self.ime_sender.lock().unwrap().send(ImeRequest::Position(
self.xwindow as ffi::Window,
x,
y,
x.saturating_add(width),
y.saturating_add(height),
));
}
@@ -2152,7 +2172,6 @@ impl UnownedWindow {
// TODO timer
}
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
let mut window_handle = rwh_06::XlibWindowHandle::new(self.xlib_window());
@@ -2160,7 +2179,6 @@ impl UnownedWindow {
Ok(window_handle.into())
}
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,

View File

@@ -1,9 +1,11 @@
use std::collections::HashMap;
use std::error::Error;
use std::ffi::c_int;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard};
use std::{fmt, ptr};
use rwh_06::HasDisplayHandle;
use x11rb::connection::Connection;
use x11rb::protocol::randr::ConnectionExt as _;
use x11rb::protocol::render;
@@ -62,6 +64,13 @@ pub struct XConnection {
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, xproto::Cursor>>,
}
impl HasDisplayHandle for XConnection {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle()?;
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}
unsafe impl Send for XConnection {}
unsafe impl Sync for XConnection {}
@@ -155,7 +164,7 @@ impl XConnection {
fn new_xsettings_screen(xcb: &XCBConnection, default_screen: usize) -> Option<xproto::Atom> {
// Fetch the _XSETTINGS_S[screen number] atom.
let xsettings_screen = xcb
.intern_atom(false, format!("_XSETTINGS_S{}", default_screen).as_bytes())
.intern_atom(false, format!("_XSETTINGS_S{default_screen}").as_bytes())
.ok()?
.reply()
.ok()?
@@ -284,6 +293,19 @@ impl XConnection {
self.xcb_connection().setup().image_byte_order != endian
}
pub fn raw_display_handle(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
let display_handle = rwh_06::XlibDisplayHandle::new(
// SAFETY: display will never be null
Some(
std::ptr::NonNull::new(self.display as *mut _)
.expect("X11 display should never be null"),
),
self.default_screen_index() as c_int,
);
Ok(display_handle.into())
}
}
impl fmt::Debug for XConnection {

View File

@@ -22,6 +22,7 @@ use self::apple as platform;
use self::linux as platform;
#[cfg(orbital_platform)]
use self::orbital as platform;
#[allow(unused_imports)]
pub use self::platform::*;
#[cfg(web_platform)]
use self::web as platform;

View File

@@ -18,7 +18,11 @@ use super::{
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event::{self, Ime, Modifiers, StartCause};
use crate::event_loop::{self, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::keyboard::{
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
PhysicalKey,
@@ -286,8 +290,7 @@ impl EventLoop {
let event_socket =
Arc::new(RedoxSocket::event().map_err(|error| os_error!(format!("{error}")))?);
let wake_socket =
Arc::new(TimeSocket::open().map_err(|error| os_error!(format!("{error}")))?);
let wake_socket = TimeSocket::open().map_err(|error| os_error!(format!("{error}")))?;
event_socket
.write(&syscall::Event {
@@ -306,8 +309,7 @@ impl EventLoop {
redraws: Arc::new(Mutex::new(VecDeque::new())),
destroys: Arc::new(Mutex::new(VecDeque::new())),
event_socket,
wake_socket,
user_events_sender,
event_loop_proxy: Arc::new(EventLoopProxy { wake_socket, user_events_sender }),
},
user_events_receiver,
})
@@ -404,6 +406,7 @@ impl EventLoop {
EventOption::Mouse(MouseEvent { x, y }) => {
app.window_event(window_target, window_id, event::WindowEvent::PointerMoved {
device_id: None,
primary: true,
position: (x, y).into(),
source: event::PointerSource::Mouse,
});
@@ -417,6 +420,7 @@ impl EventLoop {
while let Some((button, state)) = event_state.mouse(left, middle, right) {
app.window_event(window_target, window_id, event::WindowEvent::PointerButton {
device_id: None,
primary: true,
state,
position: dpi::PhysicalPosition::default(),
button: button.into(),
@@ -458,12 +462,14 @@ impl EventLoop {
let event = if entered {
event::WindowEvent::PointerEntered {
device_id: None,
primary: true,
position: dpi::PhysicalPosition::default(),
kind: event::PointerKind::Mouse,
}
} else {
event::WindowEvent::PointerLeft {
device_id: None,
primary: true,
position: None,
kind: event::PointerKind::Mouse,
}
@@ -477,7 +483,11 @@ impl EventLoop {
}
}
pub fn run_app<A: ApplicationHandler>(mut self, mut app: A) -> Result<(), EventLoopError> {
pub fn run<A: ApplicationHandler>(
mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
let mut app = init_closure(&self.window_target);
let mut start_cause = StartCause::Init;
loop {
app.new_events(&self.window_target, start_cause);
@@ -648,8 +658,6 @@ impl EventLoop {
}
}
app.exiting(&self.window_target);
Ok(())
}
@@ -660,11 +668,11 @@ impl EventLoop {
pub struct EventLoopProxy {
user_events_sender: mpsc::SyncSender<()>,
wake_socket: Arc<TimeSocket>,
pub(super) wake_socket: TimeSocket,
}
impl EventLoopProxy {
pub fn wake_up(&self) {
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
// When we fail to send the event it means that we haven't woken up to read the previous
// event.
if self.user_events_sender.try_send(()).is_ok() {
@@ -673,15 +681,6 @@ impl EventLoopProxy {
}
}
impl Clone for EventLoopProxy {
fn clone(&self) -> Self {
Self {
user_events_sender: self.user_events_sender.clone(),
wake_socket: self.wake_socket.clone(),
}
}
}
impl Unpin for EventLoopProxy {}
pub struct ActiveEventLoop {
@@ -691,18 +690,12 @@ pub struct ActiveEventLoop {
pub(super) redraws: Arc<Mutex<VecDeque<WindowId>>>,
pub(super) destroys: Arc<Mutex<VecDeque<WindowId>>>,
pub(super) event_socket: Arc<RedoxSocket>,
pub(super) wake_socket: Arc<TimeSocket>,
user_events_sender: mpsc::SyncSender<()>,
pub(super) event_loop_proxy: Arc<EventLoopProxy>,
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> event_loop::EventLoopProxy {
event_loop::EventLoopProxy {
event_loop_proxy: EventLoopProxy {
user_events_sender: self.user_events_sender.clone(),
wake_socket: self.wake_socket.clone(),
},
}
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(self.event_loop_proxy.clone())
}
fn create_window(
@@ -751,17 +744,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.exit.get()
}
fn owned_display_handle(&self) -> event_loop::OwnedDisplayHandle {
event_loop::OwnedDisplayHandle { platform: OwnedDisplayHandle }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
}
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new());
@@ -769,15 +760,12 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
}
}
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::OrbitalDisplayHandle::new().into())
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new());
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}

View File

@@ -5,7 +5,7 @@ use std::{fmt, str};
use smol_str::SmolStr;
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle};
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop};
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::keyboard::Key;
mod event_loop;
@@ -99,16 +99,6 @@ impl TimeSocket {
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct PlatformSpecificEventLoopAttributes {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FingerId;
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PlatformSpecificWindowAttributes;
@@ -135,7 +125,7 @@ impl<'a> WindowProperties<'a> {
}
}
impl<'a> fmt::Display for WindowProperties<'a> {
impl fmt::Display for WindowProperties<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,

View File

@@ -1,9 +1,10 @@
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, TimeSocket, WindowProperties};
use super::event_loop::EventLoopProxy;
use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, WindowProperties};
use crate::cursor::Cursor;
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::monitor::MonitorHandle as CoreMonitorHandle;
use crate::window::{self, Fullscreen, ImePurpose, Window as CoreWindow, WindowId};
@@ -23,7 +24,7 @@ pub struct Window {
window_socket: Arc<RedoxSocket>,
redraws: Arc<Mutex<VecDeque<WindowId>>>,
destroys: Arc<Mutex<VecDeque<WindowId>>>,
wake_socket: Arc<TimeSocket>,
event_loop_proxy: Arc<EventLoopProxy>,
}
impl Window {
@@ -113,13 +114,13 @@ impl Window {
creates.push_back(window_socket.clone());
}
el.wake_socket.wake().unwrap();
el.event_loop_proxy.wake_socket.wake().unwrap();
Ok(Self {
window_socket,
redraws: el.redraws.clone(),
destroys: el.destroys.clone(),
wake_socket: el.wake_socket.clone(),
event_loop_proxy: el.event_loop_proxy.clone(),
})
}
@@ -137,7 +138,6 @@ impl Window {
Ok(())
}
#[cfg(feature = "rwh_06")]
#[inline]
fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
let handle = rwh_06::OrbitalWindowHandle::new({
@@ -147,7 +147,6 @@ impl Window {
Ok(rwh_06::RawWindowHandle::Orbital(handle))
}
#[cfg(feature = "rwh_06")]
#[inline]
fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new()))
@@ -186,7 +185,7 @@ impl CoreWindow for Window {
if !redraws.contains(&window_id) {
redraws.push_back(window_id);
self.wake_socket.wake().unwrap();
self.event_loop_proxy.wake_socket.wake().unwrap();
}
}
@@ -199,17 +198,17 @@ impl CoreWindow for Window {
}
#[inline]
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
let mut buf: [u8; 4096] = [0; 4096];
let path = self.window_socket.fpath(&mut buf).expect("failed to read properties");
let properties = WindowProperties::new(path);
Ok((properties.x, properties.y).into())
fn surface_position(&self) -> PhysicalPosition<i32> {
// TODO: adjust for window decorations
(0, 0).into()
}
#[inline]
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
// TODO: adjust for window decorations
self.inner_position()
let mut buf: [u8; 4096] = [0; 4096];
let path = self.window_socket.fpath(&mut buf).expect("failed to read properties");
let properties = WindowProperties::new(path);
Ok((properties.x, properties.y).into())
}
#[inline]
@@ -240,6 +239,10 @@ impl CoreWindow for Window {
self.surface_size()
}
fn safe_area(&self) -> PhysicalInsets<u32> {
PhysicalInsets::new(0, 0, 0, 0)
}
#[inline]
fn set_min_surface_size(&self, _: Option<Size>) {}
@@ -411,7 +414,7 @@ impl CoreWindow for Window {
window::ResizeDirection::West => "L",
};
self.window_socket
.write(format!("D,{}", arg).as_bytes())
.write(format!("D,{arg}").as_bytes())
.map_err(|err| os_error!(format!("{err}")))?;
Ok(())
}
@@ -447,18 +450,15 @@ impl CoreWindow for Window {
fn set_content_protected(&self, _protected: bool) {}
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self
}
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_window_handle_rwh_06()?;
@@ -466,7 +466,6 @@ impl rwh_06::HasWindowHandle for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?;
@@ -481,6 +480,6 @@ impl Drop for Window {
destroys.push_back(self.id());
}
self.wake_socket.wake().unwrap();
self.event_loop_proxy.wake_socket.wake().unwrap();
}
}

View File

@@ -6,15 +6,13 @@ mod channel;
mod concurrent_queue;
mod dispatcher;
mod notifier;
mod waker;
mod wrapper;
use atomic_waker::AtomicWaker;
pub(crate) use atomic_waker::AtomicWaker;
use concurrent_queue::{ConcurrentQueue, PushError};
pub use self::abortable::{AbortHandle, Abortable, DropAbortHandle};
pub use self::channel::{channel, Receiver, Sender};
pub use self::dispatcher::{DispatchRunner, Dispatcher};
pub use self::notifier::{Notified, Notifier};
pub use self::waker::{Waker, WakerSpawner};
use self::wrapper::Wrapper;
pub(crate) use self::wrapper::Wrapper;

View File

@@ -1,117 +0,0 @@
use std::future;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::Poll;
use super::super::main_thread::MainThreadMarker;
use super::{AtomicWaker, Wrapper};
pub struct WakerSpawner<T: 'static>(Wrapper<Handler<T>, Sender, ()>);
pub struct Waker<T: 'static>(Wrapper<Handler<T>, Sender, ()>);
struct Handler<T> {
value: T,
handler: fn(&T, bool),
}
#[derive(Clone)]
struct Sender(Arc<Inner>);
impl<T> WakerSpawner<T> {
pub fn new(main_thread: MainThreadMarker, value: T, handler: fn(&T, bool)) -> Self {
let inner = Arc::new(Inner {
awoken: AtomicBool::new(false),
waker: AtomicWaker::new(),
closed: AtomicBool::new(false),
});
let handler = Handler { value, handler };
let sender = Sender(Arc::clone(&inner));
Self(Wrapper::new(
main_thread,
handler,
|handler, _| {
let handler = handler.borrow();
let handler = handler.as_ref().unwrap();
(handler.handler)(&handler.value, true);
},
{
let inner = Arc::clone(&inner);
move |handler| async move {
while future::poll_fn(|cx| {
if inner.awoken.swap(false, Ordering::Relaxed) {
Poll::Ready(true)
} else {
inner.waker.register(cx.waker());
if inner.awoken.swap(false, Ordering::Relaxed) {
Poll::Ready(true)
} else {
if inner.closed.load(Ordering::Relaxed) {
return Poll::Ready(false);
}
Poll::Pending
}
}
})
.await
{
let handler = handler.borrow();
let handler = handler.as_ref().unwrap();
(handler.handler)(&handler.value, false);
}
}
},
sender,
|inner, _| {
inner.0.awoken.store(true, Ordering::Relaxed);
inner.0.waker.wake();
},
))
}
pub fn waker(&self) -> Waker<T> {
Waker(self.0.clone())
}
pub fn take(&self) -> bool {
debug_assert!(
MainThreadMarker::new().is_some(),
"this should only be called from the main thread"
);
self.0.with_sender_data(|inner| inner.0.awoken.swap(false, Ordering::Relaxed))
}
}
impl<T> Drop for WakerSpawner<T> {
fn drop(&mut self) {
self.0.with_sender_data(|inner| {
inner.0.closed.store(true, Ordering::Relaxed);
inner.0.waker.wake();
});
}
}
impl<T> Waker<T> {
pub fn wake(&self) {
self.0.send(())
}
}
impl<T> Clone for Waker<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
struct Inner {
awoken: AtomicBool,
waker: AtomicWaker,
closed: AtomicBool,
}

View File

@@ -1,4 +1,4 @@
use crate::event::{DeviceId, FingerId as RootFingerId};
use crate::event::DeviceId;
pub(crate) fn mkdid(pointer_id: i32) -> Option<DeviceId> {
if let Ok(pointer_id) = u32::try_from(pointer_id) {
@@ -10,30 +10,3 @@ pub(crate) fn mkdid(pointer_id: i32) -> Option<DeviceId> {
None
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId {
pointer_id: i32,
primary: bool,
}
impl FingerId {
pub fn new(pointer_id: i32, primary: bool) -> Self {
Self { pointer_id, primary }
}
#[cfg(test)]
pub const fn dummy() -> Self {
Self { pointer_id: -1, primary: false }
}
pub fn is_primary(self) -> bool {
self.primary
}
}
impl From<FingerId> for RootFingerId {
fn from(id: FingerId) -> Self {
Self(id)
}
}

View File

@@ -10,8 +10,7 @@ pub(crate) mod runner;
mod state;
mod window_target;
pub(crate) use proxy::EventLoopProxy;
pub(crate) use window_target::{ActiveEventLoop, OwnedDisplayHandle};
pub(crate) use window_target::ActiveEventLoop;
pub struct EventLoop {
elw: ActiveEventLoop,
@@ -97,7 +96,6 @@ fn handle_event<A: ApplicationHandler>(app: &mut A, target: &ActiveEventLoop, ev
Event::Resumed => app.resumed(target),
Event::CreateSurfaces => app.can_create_surfaces(target),
Event::AboutToWait => app.about_to_wait(target),
Event::LoopExiting => app.exiting(target),
Event::MemoryWarning => app.memory_warning(target),
}
}

View File

@@ -1,17 +1,101 @@
use super::runner::WeakShared;
use crate::platform_impl::platform::r#async::Waker;
use std::future;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::Poll;
#[derive(Clone)]
pub struct EventLoopProxy {
runner: Waker<WeakShared>,
use super::super::main_thread::MainThreadMarker;
use crate::event_loop::EventLoopProxyProvider;
use crate::platform_impl::web::event_loop::runner::WeakShared;
use crate::platform_impl::web::r#async::{AtomicWaker, Wrapper};
pub struct EventLoopProxy(Wrapper<WeakShared, Arc<State>, ()>);
struct State {
awoken: AtomicBool,
waker: AtomicWaker,
closed: AtomicBool,
}
impl EventLoopProxy {
pub fn new(runner: Waker<WeakShared>) -> Self {
Self { runner }
pub fn new(main_thread: MainThreadMarker, runner: WeakShared) -> Self {
let state = Arc::new(State {
awoken: AtomicBool::new(false),
waker: AtomicWaker::new(),
closed: AtomicBool::new(false),
});
Self(Wrapper::new(
main_thread,
runner,
|runner, _| {
let runner = runner.borrow();
let runner = runner.as_ref().unwrap();
if let Some(runner) = runner.upgrade() {
runner.send_proxy_wake_up(true);
}
},
{
let state = Arc::clone(&state);
move |runner| async move {
while future::poll_fn(|cx| {
if state.awoken.swap(false, Ordering::Relaxed) {
Poll::Ready(true)
} else {
state.waker.register(cx.waker());
if state.awoken.swap(false, Ordering::Relaxed) {
Poll::Ready(true)
} else {
if state.closed.load(Ordering::Relaxed) {
return Poll::Ready(false);
}
Poll::Pending
}
}
})
.await
{
let runner = runner.borrow();
let runner = runner.as_ref().unwrap();
if let Some(runner) = runner.upgrade() {
runner.send_proxy_wake_up(false);
}
}
}
},
state,
|state, _| {
state.awoken.store(true, Ordering::Relaxed);
state.waker.wake();
},
))
}
pub fn wake_up(&self) {
self.runner.wake();
pub fn take(&self) -> bool {
debug_assert!(
MainThreadMarker::new().is_some(),
"this should only be called from the main thread"
);
self.0.with_sender_data(|state| state.awoken.swap(false, Ordering::Relaxed))
}
}
impl Drop for EventLoopProxy {
fn drop(&mut self) {
self.0.with_sender_data(|state| {
state.closed.store(true, Ordering::Relaxed);
state.waker.wake();
});
}
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.0.send(())
}
}

View File

@@ -3,6 +3,7 @@ use std::collections::{HashSet, VecDeque};
use std::iter;
use std::ops::Deref;
use std::rc::{Rc, Weak};
use std::sync::Arc;
use wasm_bindgen::prelude::Closure;
use wasm_bindgen::JsCast;
@@ -13,13 +14,14 @@ use super::super::event;
use super::super::main_thread::MainThreadMarker;
use super::super::monitor::MonitorHandler;
use super::backend;
use super::proxy::EventLoopProxy;
use super::state::State;
use crate::dpi::PhysicalSize;
use crate::event::{DeviceEvent, ElementState, Event, RawKeyEvent, StartCause, WindowEvent};
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::platform::web::{PollStrategy, WaitUntilStrategy};
use crate::platform_impl::platform::backend::EventListenerHandle;
use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
use crate::platform_impl::platform::backend::{EventListenerHandle, SafeAreaHandle};
use crate::platform_impl::platform::r#async::DispatchRunner;
use crate::platform_impl::platform::window::Inner;
use crate::window::WindowId;
@@ -37,7 +39,7 @@ type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
struct Execution {
main_thread: MainThreadMarker,
proxy_spawner: WakerSpawner<WeakShared>,
event_loop_proxy: Arc<EventLoopProxy>,
control_flow: Cell<ControlFlow>,
poll_strategy: Cell<PollStrategy>,
wait_until_strategy: Cell<WaitUntilStrategy>,
@@ -55,6 +57,7 @@ struct Execution {
redraw_pending: RefCell<HashSet<WindowId>>,
destroy_pending: RefCell<VecDeque<WindowId>>,
pub(crate) monitor: Rc<MonitorHandler>,
safe_area: Rc<SafeAreaHandle>,
page_transition_event_handle: RefCell<Option<backend::PageTransitionEventHandle>>,
device_events: Cell<DeviceEvents>,
on_mouse_move: OnEventHandle<PointerEvent>,
@@ -140,12 +143,7 @@ impl Shared {
let document = window.document().expect("Failed to obtain document");
Shared(Rc::<Execution>::new_cyclic(|weak| {
let proxy_spawner =
WakerSpawner::new(main_thread, WeakShared(weak.clone()), |runner, local| {
if let Some(runner) = runner.upgrade() {
runner.send_proxy_wake_up(local);
}
});
let proxy_spawner = EventLoopProxy::new(main_thread, WeakShared(weak.clone()));
let monitor = MonitorHandler::new(
main_thread,
@@ -154,9 +152,11 @@ impl Shared {
WeakShared(weak.clone()),
);
let safe_area = SafeAreaHandle::new(&window, &document);
Execution {
main_thread,
proxy_spawner,
event_loop_proxy: Arc::new(proxy_spawner),
control_flow: Cell::new(ControlFlow::default()),
poll_strategy: Cell::new(PollStrategy::default()),
wait_until_strategy: Cell::new(WaitUntilStrategy::default()),
@@ -173,6 +173,7 @@ impl Shared {
redraw_pending: RefCell::new(HashSet::new()),
destroy_pending: RefCell::new(VecDeque::new()),
monitor: Rc::new(monitor),
safe_area: Rc::new(safe_area),
page_transition_event_handle: RefCell::new(None),
device_events: Cell::default(),
on_mouse_move: RefCell::new(None),
@@ -608,7 +609,7 @@ impl Shared {
self.apply_control_flow();
// We don't call `handle_loop_destroyed` here because we don't need to
// perform cleanup when the Web browser is going to destroy the page.
self.handle_event(Event::LoopExiting);
todo!("drop the application handler");
}
// handle_event takes in events and either queues them or applies a callback
@@ -653,7 +654,7 @@ impl Shared {
// Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle.
events.extend(
self.0
.proxy_spawner
.event_loop_proxy
.take()
.then_some(Event::UserWakeUp)
.map(EventWrapper::from),
@@ -712,7 +713,7 @@ impl Shared {
}
fn handle_loop_destroyed(&self) {
self.handle_event(Event::LoopExiting);
todo!("drop the application handler");
let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut());
*self.0.page_transition_event_handle.borrow_mut() = None;
*self.0.on_mouse_move.borrow_mut() = None;
@@ -818,8 +819,8 @@ impl Shared {
self.0.wait_until_strategy.get()
}
pub(crate) fn waker(&self) -> Waker<WeakShared> {
self.0.proxy_spawner.waker()
pub(crate) fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.0.event_loop_proxy
}
pub(crate) fn weak(&self) -> WeakShared {
@@ -829,6 +830,10 @@ impl Shared {
pub(crate) fn monitor(&self) -> &Rc<MonitorHandler> {
&self.0.monitor
}
pub(crate) fn safe_area(&self) -> &Rc<SafeAreaHandle> {
&self.0.safe_area
}
}
#[derive(Clone, Debug)]

View File

@@ -2,24 +2,25 @@ use std::cell::Cell;
use std::clone::Clone;
use std::iter;
use std::rc::Rc;
use std::sync::Arc;
use web_sys::Element;
use super::super::monitor::MonitorPermissionFuture;
use super::super::{lock, KeyEventExtra};
use super::runner::{EventWrapper, WeakShared};
use super::{backend, runner, EventLoopProxy};
use super::runner::EventWrapper;
use super::{backend, runner};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{ElementState, Event, KeyEvent, TouchPhase, WindowEvent};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::keyboard::ModifiersState;
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform::web::{CustomCursorFuture, PollStrategy, WaitUntilStrategy};
use crate::platform_impl::platform::cursor::CustomCursor;
use crate::platform_impl::platform::r#async::Waker;
use crate::platform_impl::web::event_loop::proxy::EventLoopProxy;
use crate::platform_impl::Window;
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId};
@@ -197,7 +198,7 @@ impl ActiveEventLoop {
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, kind| {
move |active_modifiers, device_id, primary, position, kind| {
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
@@ -208,7 +209,12 @@ impl ActiveEventLoop {
runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft { device_id, position: Some(position), kind },
event: WindowEvent::PointerLeft {
device_id,
primary,
position: Some(position),
kind,
},
})))
}
});
@@ -218,7 +224,7 @@ impl ActiveEventLoop {
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, kind| {
move |active_modifiers, device_id, primary, position, kind| {
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
@@ -229,7 +235,7 @@ impl ActiveEventLoop {
runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered { device_id, position, kind },
event: WindowEvent::PointerEntered { device_id, primary, position, kind },
})))
}
});
@@ -241,21 +247,31 @@ impl ActiveEventLoop {
let modifiers = self.modifiers.clone();
move |device_id, events| {
runner.send_events(events.flat_map(|(active_modifiers, position, source)| {
let modifiers = (has_focus.get() && modifiers.get() != active_modifiers)
.then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(events.flat_map(
|(active_modifiers, primary, position, source)| {
let modifiers = (has_focus.get()
&& modifiers.get() != active_modifiers)
.then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id,
event: WindowEvent::ModifiersChanged(
active_modifiers.into(),
),
}
});
modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved { device_id, position, source },
}))
}));
modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id,
primary,
position,
source,
},
}))
},
));
}
},
{
@@ -263,7 +279,7 @@ impl ActiveEventLoop {
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, state, button| {
move |active_modifiers, device_id, primary, position, state, button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
@@ -275,7 +291,13 @@ impl ActiveEventLoop {
runner.send_events(modifiers.into_iter().chain([Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton { device_id, state, position, button },
event: WindowEvent::PointerButton {
device_id,
primary,
state,
position,
button,
},
}]));
}
},
@@ -285,7 +307,7 @@ impl ActiveEventLoop {
let runner = self.runner.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, button| {
move |active_modifiers, device_id, primary, position, button| {
let modifiers = (modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
@@ -298,6 +320,7 @@ impl ActiveEventLoop {
window_id,
event: WindowEvent::PointerButton {
device_id,
primary,
state: ElementState::Pressed,
position,
button,
@@ -311,7 +334,7 @@ impl ActiveEventLoop {
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, position, button| {
move |active_modifiers, device_id, primary, position, button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
@@ -325,6 +348,7 @@ impl ActiveEventLoop {
window_id,
event: WindowEvent::PointerButton {
device_id,
primary,
state: ElementState::Released,
position,
button,
@@ -453,15 +477,15 @@ impl ActiveEventLoop {
self.runner.monitor().has_detailed_monitor_permission()
}
pub(crate) fn waker(&self) -> Waker<WeakShared> {
self.runner.waker()
pub(crate) fn event_loop_proxy(&self) -> Arc<EventLoopProxy> {
self.runner.event_loop_proxy().clone()
}
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = EventLoopProxy::new(self.waker());
RootEventLoopProxy { event_loop_proxy }
let event_loop_proxy = self.event_loop_proxy();
RootEventLoopProxy::new(event_loop_proxy)
}
fn create_window(
@@ -523,17 +547,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.runner.exiting()
}
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
}
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::Web(rwh_06::WebDisplayHandle::new());
@@ -541,15 +563,12 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
}
}
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::WebDisplayHandle::new().into())
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::Web(rwh_06::WebDisplayHandle::new());
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}

View File

@@ -37,10 +37,8 @@ pub(crate) use cursor::{
CustomCursorSource as PlatformCustomCursorSource,
};
pub use self::event::FingerId;
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::keyboard::KeyEventExtra;
pub(crate) use self::monitor::{

View File

@@ -72,8 +72,8 @@ pub struct Common {
#[derive(Clone, Debug)]
pub struct Style {
read: CssStyleDeclaration,
write: CssStyleDeclaration,
pub(super) read: CssStyleDeclaration,
pub(super) write: CssStyleDeclaration,
}
impl Canvas {
@@ -331,28 +331,32 @@ impl Canvas {
pub fn on_pointer_leave<F>(&self, handler: F)
where
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
F: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_leave(&self.common, handler)
}
pub fn on_pointer_enter<F>(&self, handler: F)
where
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
F: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_enter(&self.common, handler)
}
pub fn on_pointer_release<C>(&self, handler: C)
where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
C: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_release(&self.common, handler)
}
pub fn on_pointer_press<C>(&self, handler: C)
where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
C: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_press(
&self.common,
@@ -366,12 +370,15 @@ impl Canvas {
C: 'static
+ FnMut(
Option<DeviceId>,
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
&mut dyn Iterator<
Item = (ModifiersState, bool, PhysicalPosition<f64>, PointerSource),
>,
),
B: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
bool,
PhysicalPosition<f64>,
ElementState,
ButtonSource,

View File

@@ -6,9 +6,8 @@ use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{KeyboardEvent, MouseEvent, Navigator, PointerEvent, WheelEvent};
use super::super::FingerId;
use super::Engine;
use crate::event::{MouseButton, MouseScrollDelta, PointerKind};
use crate::event::{FingerId, MouseButton, MouseScrollDelta, PointerKind};
use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey};
bitflags::bitflags! {
@@ -164,7 +163,7 @@ pub fn mouse_scroll_delta(
pub fn pointer_type(event: &PointerEvent, pointer_id: i32) -> PointerKind {
match event.pointer_type().as_str() {
"mouse" => PointerKind::Mouse,
"touch" => PointerKind::Touch(FingerId::new(pointer_id, event.is_primary()).into()),
"touch" => PointerKind::Touch(FingerId::from_raw(pointer_id as usize)),
_ => PointerKind::Unknown,
}
}

View File

@@ -7,6 +7,7 @@ mod intersection_handle;
mod media_query_handle;
mod pointer;
mod resize_scaling;
mod safe_area;
mod schedule;
use std::cell::OnceCell;
@@ -20,6 +21,7 @@ use web_sys::{Document, HtmlCanvasElement, Navigator, PageTransitionEvent, Visib
pub use self::canvas::{Canvas, Style};
pub use self::event_handle::EventListenerHandle;
pub use self::resize_scaling::ResizeScaleHandle;
pub use self::safe_area::SafeAreaHandle;
pub use self::schedule::Schedule;
use crate::dpi::{LogicalPosition, LogicalSize};

View File

@@ -35,7 +35,8 @@ impl PointerHandler {
pub fn on_pointer_leave<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
F: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
{
let window = canvas_common.window.clone();
self.on_cursor_leave =
@@ -46,13 +47,14 @@ impl PointerHandler {
let position =
event::mouse_position(&event).to_physical(super::scale_factor(&window));
let kind = event::pointer_type(&event, pointer_id);
handler(modifiers, device_id, position, kind);
handler(modifiers, device_id, event.is_primary(), position, kind);
}));
}
pub fn on_pointer_enter<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
F: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
{
let window = canvas_common.window.clone();
self.on_cursor_enter =
@@ -63,13 +65,14 @@ impl PointerHandler {
let position =
event::mouse_position(&event).to_physical(super::scale_factor(&window));
let kind = event::pointer_type(&event, pointer_id);
handler(modifiers, device_id, position, kind);
handler(modifiers, device_id, event.is_primary(), position, kind);
}));
}
pub fn on_pointer_release<C>(&mut self, canvas_common: &Common, mut handler: C)
where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
C: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
{
let window = canvas_common.window.clone();
self.on_pointer_release =
@@ -92,6 +95,7 @@ impl PointerHandler {
handler(
modifiers,
mkdid(pointer_id),
event.is_primary(),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
@@ -104,7 +108,8 @@ impl PointerHandler {
mut handler: C,
prevent_default: Rc<Cell<bool>>,
) where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
C: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
{
let window = canvas_common.window.clone();
let canvas = canvas_common.raw().clone();
@@ -143,6 +148,7 @@ impl PointerHandler {
handler(
modifiers,
mkdid(pointer_id),
event.is_primary(),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
@@ -159,12 +165,15 @@ impl PointerHandler {
C: 'static
+ FnMut(
Option<DeviceId>,
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
&mut dyn Iterator<
Item = (ModifiersState, bool, PhysicalPosition<f64>, PointerSource),
>,
),
B: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
bool,
PhysicalPosition<f64>,
ElementState,
ButtonSource,
@@ -177,6 +186,7 @@ impl PointerHandler {
let pointer_id = event.pointer_id();
let device_id = mkdid(pointer_id);
let kind = event::pointer_type(&event, pointer_id);
let primary = event.is_primary();
// chorded button event
if let Some(button) = event::mouse_button(&event) {
@@ -213,6 +223,7 @@ impl PointerHandler {
button_handler(
event::mouse_modifiers(&event),
device_id,
primary,
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
state,
button,
@@ -229,6 +240,7 @@ impl PointerHandler {
&mut event::pointer_move_event(event).map(|event| {
(
event::mouse_modifiers(&event),
event.is_primary(),
event::mouse_position(&event).to_physical(scale),
match kind {
PointerKind::Mouse => PointerSource::Mouse,

View File

@@ -0,0 +1,56 @@
use dpi::{LogicalPosition, LogicalSize};
use wasm_bindgen::JsCast;
use web_sys::{Document, HtmlHtmlElement, Window};
use super::Style;
pub struct SafeAreaHandle {
style: Style,
}
impl SafeAreaHandle {
pub fn new(window: &Window, document: &Document) -> Self {
let document: HtmlHtmlElement = document.document_element().unwrap().unchecked_into();
#[allow(clippy::disallowed_methods)]
let write = document.style();
write
.set_property(
"--__winit_safe_area",
"env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) \
env(safe-area-inset-left)",
)
.expect("unexpected read-only declaration block");
#[allow(clippy::disallowed_methods)]
let read = window
.get_computed_style(&document)
.expect("failed to obtain computed style")
// this can't fail: we aren't using a pseudo-element
.expect("invalid pseudo-element");
SafeAreaHandle { style: Style { read, write } }
}
pub fn get(&self) -> (LogicalPosition<f64>, LogicalSize<f64>) {
let value = self.style.get("--__winit_safe_area");
let mut values = value
.split(' ')
.map(|value| value.strip_suffix("px").expect("unexpected unit other then `px` found"));
let top: f64 = values.next().unwrap().parse().unwrap();
let right: f64 = values.next().unwrap().parse().unwrap();
let bottom: f64 = values.next().unwrap().parse().unwrap();
let left: f64 = values.next().unwrap().parse().unwrap();
assert_eq!(values.next(), None, "unexpected fifth value");
let width = super::style_size_property(&self.style, "width") - left - right;
let height = super::style_size_property(&self.style, "height") - top - bottom;
(LogicalPosition::new(left, top), LogicalSize::new(width, height))
}
}
impl Drop for SafeAreaHandle {
fn drop(&mut self) {
self.style.remove("--__winit_safe_area");
}
}

View File

@@ -2,13 +2,14 @@ use std::cell::Ref;
use std::rc::Rc;
use std::sync::Arc;
use dpi::{LogicalPosition, LogicalSize};
use web_sys::HtmlCanvasElement;
use super::main_thread::{MainThreadMarker, MainThreadSafe};
use super::monitor::MonitorHandler;
use super::r#async::Dispatcher;
use super::{backend, lock, ActiveEventLoop};
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{LogicalInsets, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::icon::Icon;
use crate::monitor::MonitorHandle as RootMonitorHandle;
@@ -26,6 +27,7 @@ pub struct Inner {
id: WindowId,
pub window: web_sys::Window,
monitor: Rc<MonitorHandler>,
safe_area: Rc<backend::SafeAreaHandle>,
canvas: Rc<backend::Canvas>,
destroy_fn: Option<Box<dyn FnOnce()>>,
}
@@ -59,6 +61,7 @@ impl Window {
id,
window: window.clone(),
monitor: Rc::clone(target.runner.monitor()),
safe_area: Rc::clone(target.runner.safe_area()),
canvas,
destroy_fn: Some(destroy_fn),
};
@@ -109,9 +112,9 @@ impl RootWindow for Window {
// Not supported
}
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
// Note: the canvas element has no window decorations, so this is equal to `outer_position`.
self.outer_position()
fn surface_position(&self) -> PhysicalPosition<i32> {
// Note: the canvas element has no window decorations.
(0, 0).into()
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
@@ -152,6 +155,34 @@ impl RootWindow for Window {
self.surface_size()
}
fn safe_area(&self) -> PhysicalInsets<u32> {
self.inner.queue(|inner| {
let (safe_start_pos, safe_size) = inner.safe_area.get();
let safe_end_pos = LogicalPosition::new(
safe_start_pos.x + safe_size.width,
safe_start_pos.y + safe_size.height,
);
let surface_start_pos = inner.canvas.position();
let surface_size = LogicalSize::new(
backend::style_size_property(inner.canvas.style(), "width"),
backend::style_size_property(inner.canvas.style(), "height"),
);
let surface_end_pos = LogicalPosition::new(
surface_start_pos.x + surface_size.width,
surface_start_pos.y + surface_size.height,
);
let top = f64::max(safe_start_pos.y - surface_start_pos.y, 0.);
let left = f64::max(safe_start_pos.x - surface_start_pos.x, 0.);
let bottom = f64::max(surface_end_pos.y - safe_end_pos.y, 0.);
let right = f64::max(surface_end_pos.x - safe_end_pos.x, 0.);
let insets = LogicalInsets::new(top, left, bottom, right);
insets.to_physical(inner.scale_factor())
})
}
fn set_min_surface_size(&self, min_size: Option<Size>) {
self.inner.dispatch(move |inner| {
let dimensions = min_size.map(|min_size| min_size.to_logical(inner.scale_factor()));
@@ -375,18 +406,15 @@ impl RootWindow for Window {
self.inner.queue(|inner| inner.monitor.primary_monitor()).map(RootMonitorHandle::from)
}
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
MainThreadMarker::new()
@@ -408,7 +436,6 @@ impl rwh_06::HasWindowHandle for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
Ok(rwh_06::DisplayHandle::web())

View File

@@ -4,6 +4,7 @@ mod runner;
use std::cell::Cell;
use std::ffi::c_void;
use std::os::windows::io::{AsRawHandle as _, FromRawHandle as _, OwnedHandle, RawHandle};
use std::rc::Rc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, Mutex, MutexGuard};
@@ -12,13 +13,18 @@ use std::{mem, panic, ptr};
use runner::EventLoopRunner;
use windows_sys::Win32::Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE;
use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, POINT, RECT, WPARAM};
use windows_sys::Win32::Foundation::{
GetLastError, FALSE, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_FAILED, WPARAM,
};
use windows_sys::Win32::Graphics::Gdi::{
GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient,
ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE,
};
use windows_sys::Win32::System::Ole::RevokeDragDrop;
use windows_sys::Win32::System::Threading::{GetCurrentThreadId, INFINITE};
use windows_sys::Win32::System::Threading::{
CreateWaitableTimerExW, GetCurrentThreadId, SetWaitableTimer,
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, INFINITE, TIMER_ALL_ACCESS,
};
use windows_sys::Win32::UI::Controls::{HOVER_DEFAULT, WM_MOUSELEAVE};
use windows_sys::Win32::UI::Input::Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW};
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
@@ -34,14 +40,15 @@ use windows_sys::Win32::UI::Input::Touch::{
use windows_sys::Win32::UI::Input::{RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE};
use windows_sys::Win32::UI::WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect, GetCursorPos,
GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW,
RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos, TranslateMessage, CREATESTRUCTW,
GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS,
PM_REMOVE, PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM,
WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
GetMenu, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW,
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos, TranslateMessage,
CREATESTRUCTW, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG,
MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PM_REMOVE, PT_TOUCH, QS_ALLEVENTS, RI_MOUSE_HWHEEL,
RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE,
SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM, WMSZ_BOTTOMLEFT,
WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT,
WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE,
WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS,
WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL,
WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
@@ -58,12 +65,12 @@ use crate::application::ApplicationHandler;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::error::{EventLoopError, RequestError};
use crate::event::{
Event, FingerId as RootFingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase,
WindowEvent,
Event, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent,
};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
EventLoopProxy as RootEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::keyboard::ModifiersState;
use crate::monitor::MonitorHandle as RootMonitorHandle;
@@ -80,7 +87,7 @@ use crate::platform_impl::platform::window::InitData;
use crate::platform_impl::platform::window_state::{
CursorFlags, ImeState, WindowFlags, WindowState,
};
use crate::platform_impl::platform::{raw_input, util, wrap_device_id, FingerId, Fullscreen};
use crate::platform_impl::platform::{raw_input, util, wrap_device_id, Fullscreen};
use crate::platform_impl::Window;
use crate::utils::Lazy;
use crate::window::{
@@ -127,6 +134,10 @@ pub(crate) enum ProcResult {
pub struct EventLoop {
window_target: ActiveEventLoop,
msg_hook: Option<Box<dyn FnMut(*const c_void) -> bool + 'static>>,
// It is a timer used on timed waits.
// It is created lazily in case if we have `ControlFlow::WaitUntil`.
// Keep it as a field to avoid recreating it on every `ControlFlow::WaitUntil`.
high_resolution_timer: Option<OwnedHandle>,
}
pub(crate) struct PlatformSpecificEventLoopAttributes {
@@ -201,6 +212,7 @@ impl EventLoop {
Ok(EventLoop {
window_target: ActiveEventLoop { thread_id, thread_msg_target, runner_shared },
msg_hook: attributes.msg_hook.take(),
high_resolution_timer: None,
})
}
@@ -208,15 +220,21 @@ impl EventLoop {
&self.window_target
}
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
pub fn run<A: ApplicationHandler>(
mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
}
pub fn run_app_on_demand<A: ApplicationHandler>(
pub fn run_on_demand<A: ApplicationHandler>(
&mut self,
mut app: A,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.window_target.clear_exit();
let mut app = init_closure(&self.window_target);
{
let runner = &self.window_target.runner_shared;
@@ -238,15 +256,15 @@ impl EventLoop {
Event::Resumed => app.resumed(event_loop_windows_ref),
Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref),
Event::AboutToWait => app.about_to_wait(event_loop_windows_ref),
Event::LoopExiting => app.exiting(event_loop_windows_ref),
Event::MemoryWarning => app.memory_warning(event_loop_windows_ref),
});
}
}
let exit_code = loop {
self.wait_and_dispatch_message(None);
self.wait_for_messages(None);
// wait_for_messages calls user application before and after waiting
// so it may have decided to exit.
if let Some(code) = self.exit_code() {
break code;
}
@@ -304,7 +322,6 @@ impl EventLoop {
Event::Resumed => app.resumed(event_loop_windows_ref),
Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref),
Event::AboutToWait => app.about_to_wait(event_loop_windows_ref),
Event::LoopExiting => app.exiting(event_loop_windows_ref),
Event::MemoryWarning => app.memory_warning(event_loop_windows_ref),
});
@@ -312,8 +329,11 @@ impl EventLoop {
}
}
self.wait_and_dispatch_message(timeout);
if self.exit_code().is_none() {
self.wait_for_messages(timeout);
}
// wait_for_messages calls user application before and after waiting
// so it may have decided to exit.
if self.exit_code().is_none() {
self.dispatch_peeked_messages();
}
@@ -332,8 +352,10 @@ impl EventLoop {
PumpStatus::Continue
};
// We wait until we've checked for an exit status before clearing the
// application callback, in case we need to dispatch a LoopExiting event
// We wait until we've checked for an exit status before clearing the application callback,
// in case any of the methods above ends up triggering an event.
//
// This drops the user's `ApplicationHandler`.
//
// # Safety
// This pairs up with our call to `runner.set_event_handler` and ensures
@@ -343,101 +365,27 @@ impl EventLoop {
status
}
/// Wait for one message and dispatch it, optionally with a timeout
fn wait_and_dispatch_message(&mut self, timeout: Option<Duration>) {
fn get_msg_with_timeout(msg: &mut MSG, timeout: Option<Duration>) -> PumpStatus {
unsafe {
// A timeout of None means wait indefinitely (so we don't need to call SetTimer)
let timer_id = timeout.map(|timeout| SetTimer(0, 0, dur2timeout(timeout), None));
let get_status = GetMessageW(msg, 0, 0, 0);
if let Some(timer_id) = timer_id {
KillTimer(0, timer_id);
}
// A return value of 0 implies `WM_QUIT`
if get_status == 0 {
PumpStatus::Exit(0)
} else {
PumpStatus::Continue
}
}
}
/// Fetch the next MSG either via PeekMessage or GetMessage depending on whether the
/// requested timeout is `ZERO` (and so we don't want to block)
///
/// Returns `None` if no MSG was read, else a `Continue` or `Exit` status
fn wait_for_msg(msg: &mut MSG, timeout: Option<Duration>) -> Option<PumpStatus> {
if timeout == Some(Duration::ZERO) {
unsafe {
if PeekMessageW(msg, 0, 0, 0, PM_REMOVE) != 0 {
Some(PumpStatus::Continue)
} else {
None
}
}
} else {
Some(get_msg_with_timeout(msg, timeout))
}
}
/// Waits until new event messages arrive to be peeked.
/// Doesn't peek messages itself.
///
/// Parameter timeout is optional. This method would wait for the smaller timeout
/// between the argument and a timeout from control flow.
fn wait_for_messages(&mut self, timeout: Option<Duration>) {
let runner = &self.window_target.runner_shared;
// We aim to be consistent with the MacOS backend which has a RunLoop
// observer that will dispatch AboutToWait when about to wait for
// events, and NewEvents after the RunLoop wakes up.
//
// We emulate similar behaviour by treating `GetMessage` as our wait
// We emulate similar behaviour by treating `MsgWaitForMultipleObjectsEx` as our wait
// point and wake up point (when it returns) and we drain all other
// pending messages via `PeekMessage` until we come back to "wait" via
// `GetMessage`
// `MsgWaitForMultipleObjectsEx`.
//
runner.prepare_wait();
let control_flow_timeout = match runner.control_flow() {
ControlFlow::Wait => None,
ControlFlow::Poll => Some(Duration::ZERO),
ControlFlow::WaitUntil(wait_deadline) => {
let start = Instant::now();
Some(wait_deadline.saturating_duration_since(start))
},
};
let timeout = min_timeout(control_flow_timeout, timeout);
// # Safety
// The Windows API has no documented requirement for bitwise
// initializing a `MSG` struct (it can be uninitialized memory for the C
// API) and there's no API to construct or initialize a `MSG`. This
// is the simplest way avoid uninitialized memory in Rust
let mut msg = unsafe { mem::zeroed() };
let msg_status = wait_for_msg(&mut msg, timeout);
wait_for_messages_impl(&mut self.high_resolution_timer, runner.control_flow(), timeout);
// Before we potentially exit, make sure to consistently emit an event for the wake up
runner.wakeup();
match msg_status {
None => {}, // No MSG to dispatch
Some(PumpStatus::Exit(code)) => {
runner.set_exit_code(code);
},
Some(PumpStatus::Continue) => {
unsafe {
let handled = if let Some(callback) = self.msg_hook.as_deref_mut() {
callback(&mut msg as *mut _ as *mut _)
} else {
false
};
if !handled {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
if let Err(payload) = runner.take_panic_error() {
runner.reset_runner();
panic::resume_unwind(payload);
}
},
}
}
/// Dispatch all queued messages via `PeekMessageW`
@@ -456,7 +404,7 @@ impl EventLoop {
// initializing a `MSG` struct (it can be uninitialized memory for the C
// API) and there's no API to construct or initialize a `MSG`. This
// is the simplest way avoid uninitialized memory in Rust
let mut msg = unsafe { mem::zeroed() };
let mut msg: MSG = unsafe { mem::zeroed() };
loop {
unsafe {
@@ -495,6 +443,14 @@ impl EventLoop {
}
}
impl Drop for EventLoop {
fn drop(&mut self) {
unsafe {
DestroyWindow(self.window_target.thread_msg_target);
}
}
}
impl ActiveEventLoop {
#[inline(always)]
pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor {
@@ -513,7 +469,7 @@ impl ActiveEventLoop {
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = EventLoopProxy { target_window: self.thread_msg_target };
RootEventLoopProxy { event_loop_proxy }
RootEventLoopProxy::new(Arc::new(event_loop_proxy))
}
fn create_window(
@@ -566,8 +522,8 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.runner_shared.set_exit_code(0)
}
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
}
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
@@ -575,7 +531,6 @@ impl RootActiveEventLoop for ActiveEventLoop {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::Windows(rwh_06::WindowsDisplayHandle::new());
@@ -583,16 +538,13 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
}
}
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::WindowsDisplayHandle::new().into())
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::Windows(rwh_06::WindowsDisplayHandle::new());
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
}
}
@@ -617,7 +569,7 @@ impl OwnedDisplayHandle {
fn main_thread_id() -> u32 {
static mut MAIN_THREAD_ID: u32 = 0;
/// Function pointer used in CRT initialization section to set the above static field's value.
// Function pointer used in CRT initialization section to set the above static field's value.
// Mark as used so this is not removable.
#[used]
@@ -668,10 +620,139 @@ fn dur2timeout(dur: Duration) -> u32 {
.unwrap_or(INFINITE)
}
impl Drop for EventLoop {
fn drop(&mut self) {
unsafe {
DestroyWindow(self.window_target.thread_msg_target);
/// Set upper limit for waiting time to avoid overflows.
/// I chose 50 days as a limit because it is used in dur2timeout.
const FIFTY_DAYS: Duration = Duration::from_secs(50_u64 * 24 * 60 * 60);
/// Waitable timers use 100 ns intervals to indicate due time.
/// <https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setwaitabletimer#parameters>
/// And there is no point waiting using other ways for such small timings
/// because they are even less precise (can overshoot by few ms).
const MIN_WAIT: Duration = Duration::from_nanos(100);
fn create_high_resolution_timer() -> Option<OwnedHandle> {
unsafe {
let handle: HANDLE = CreateWaitableTimerExW(
ptr::null(),
ptr::null(),
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
TIMER_ALL_ACCESS,
);
// CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported only after
// Win10 1803 but it is already default option for rustc
// (std uses it to implement `std::thread::sleep`).
if handle == 0 {
None
} else {
Some(OwnedHandle::from_raw_handle(handle as *mut c_void))
}
}
}
/// This function should not return error if parameters are valid
/// but there is no guarantee about that at MSDN docs
/// so we return result of GetLastError if fail.
///
/// ## Safety
///
/// timer must be a valid timer handle created by [create_high_resolution_timer].
/// timeout divided by 100 nanoseconds must be more than 0 and less than i64::MAX.
unsafe fn set_high_resolution_timer(timer: RawHandle, timeout: Duration) -> Result<(), u32> {
const INTERVAL_NS: u32 = MIN_WAIT.subsec_nanos();
const INTERVALS_IN_SEC: u64 = (Duration::from_secs(1).as_nanos() / INTERVAL_NS as u128) as u64;
let intervals_to_wait: u64 =
timeout.as_secs() * INTERVALS_IN_SEC + u64::from(timeout.subsec_nanos() / INTERVAL_NS);
debug_assert!(intervals_to_wait < i64::MAX as u64, "Must be called with smaller duration",);
// Use negative time to indicate relative time.
let due_time: i64 = -(intervals_to_wait as i64);
unsafe {
let set_result = SetWaitableTimer(timer as HANDLE, &due_time, 0, None, ptr::null(), FALSE);
if set_result != FALSE {
Ok(())
} else {
Err(GetLastError())
}
}
}
/// Implementation detail of [EventLoop::wait_for_messages].
///
/// Does actual system-level waiting and doesn't process any messages itself,
/// including winits internal notifications about waiting and new messages arrival.
fn wait_for_messages_impl(
high_resolution_timer: &mut Option<OwnedHandle>,
control_flow: ControlFlow,
timeout: Option<Duration>,
) {
let timeout = {
let control_flow_timeout = match control_flow {
ControlFlow::Wait => None,
ControlFlow::Poll => Some(Duration::ZERO),
ControlFlow::WaitUntil(wait_deadline) => {
let start = Instant::now();
Some(wait_deadline.saturating_duration_since(start))
},
};
let timeout = min_timeout(timeout, control_flow_timeout);
if timeout == Some(Duration::ZERO) {
// Do not wait if we don't have time.
return;
}
// Now we decided to wait so need to do some clamping
// to avoid problems with overflow and calling WinAPI with invalid parameters.
timeout
.map(|t| t.min(FIFTY_DAYS))
// If timeout is less than minimally supported by Windows,
// increase it to that minimum. Who want less than microsecond delays anyway?
.map(|t| t.max(MIN_WAIT))
};
if timeout.is_some() && high_resolution_timer.is_none() {
*high_resolution_timer = create_high_resolution_timer();
}
let high_resolution_timer: Option<RawHandle> =
high_resolution_timer.as_ref().map(OwnedHandle::as_raw_handle);
let use_timer: bool;
if let (Some(handle), Some(timeout)) = (high_resolution_timer, timeout) {
let res = unsafe {
// Safety: handle can be Some only if we succeeded in creating high resolution
// timer. We properly clamped timeout so it can be used as argument
// to timer.
set_high_resolution_timer(handle, timeout)
};
if let Err(error_code) = res {
// We successfully got timer but failed to set it?
// Should be some bug in our code.
tracing::trace!("Failed to set high resolution timer: last error {}", error_code);
use_timer = false;
} else {
use_timer = true;
}
} else {
use_timer = false;
}
unsafe {
// Either:
// 1. User wants to wait indefinely if timeout is not set.
// 2. We failed to get and set high resolution timer and we need something instead of it.
let wait_duration_ms = timeout.map(dur2timeout).unwrap_or(INFINITE);
let (num_handles, raw_handles) =
if use_timer { (1, [high_resolution_timer.unwrap()]) } else { (0, [ptr::null_mut()]) };
let result = MsgWaitForMultipleObjectsEx(
num_handles,
raw_handles.as_ptr() as *const _,
wait_duration_ms,
QS_ALLEVENTS,
MWMO_INPUTAVAILABLE,
);
if result == WAIT_FAILED {
// Well, nothing smart to do in such case.
// Treat it as spurious wake up.
tracing::warn!("Failed to MsgWaitForMultipleObjectsEx: error code {}", GetLastError(),);
}
}
}
@@ -726,15 +807,14 @@ impl EventLoopThreadExecutor {
type ThreadExecFn = Box<Box<dyn FnMut()>>;
#[derive(Clone)]
pub struct EventLoopProxy {
target_window: HWND,
}
unsafe impl Send for EventLoopProxy {}
impl EventLoopProxy {
pub fn wake_up(&self) {
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) };
}
}
@@ -1542,6 +1622,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerEntered {
device_id: None,
primary: true,
position,
kind: PointerKind::Mouse,
},
@@ -1567,6 +1648,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerLeft {
device_id: None,
primary: true,
position: Some(position),
kind: PointerKind::Mouse,
},
@@ -1588,7 +1670,12 @@ unsafe fn public_window_callback_inner(
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerMoved { device_id: None, position, source: PointerSource::Mouse },
event: PointerMoved {
device_id: None,
primary: true,
position,
source: PointerSource::Mouse,
},
});
}
@@ -1606,7 +1693,7 @@ unsafe fn public_window_callback_inner(
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerLeft { device_id: None, position: None, kind: Mouse },
event: PointerLeft { device_id: None, primary: true, position: None, kind: Mouse },
});
result = ProcResult::Value(0);
@@ -1683,6 +1770,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
primary: true,
state: Pressed,
position,
button: Left.into(),
@@ -1708,6 +1796,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
primary: true,
state: Released,
position,
button: Left.into(),
@@ -1733,6 +1822,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
primary: true,
state: Pressed,
position,
button: Right.into(),
@@ -1758,6 +1848,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
primary: true,
state: Released,
position,
button: Right.into(),
@@ -1783,6 +1874,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
primary: true,
state: Pressed,
position,
button: Middle.into(),
@@ -1808,6 +1900,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
primary: true,
state: Released,
position,
button: Middle.into(),
@@ -1834,6 +1927,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
primary: true,
state: Pressed,
position,
button: match xbutton {
@@ -1865,6 +1959,7 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize),
event: PointerButton {
device_id: None,
primary: true,
state: Released,
position,
button: match xbutton {
@@ -1918,16 +2013,15 @@ unsafe fn public_window_callback_inner(
let position = PhysicalPosition::new(x, y);
let window_id = WindowId::from_raw(window as usize);
let finger_id = RootFingerId(FingerId {
id: input.dwID,
primary: util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY),
});
let finger_id = FingerId::from_raw(input.dwID as usize);
let primary = util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY);
if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id: None,
primary,
position,
kind: PointerKind::Touch(finger_id),
},
@@ -1936,6 +2030,7 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerButton {
device_id: None,
primary,
state: Pressed,
position,
button: Touch { finger_id, force: None },
@@ -1946,6 +2041,7 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerButton {
device_id: None,
primary,
state: Released,
position,
button: Touch { finger_id, force: None },
@@ -1955,6 +2051,7 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
primary,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
@@ -1964,6 +2061,7 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
primary,
position,
source: PointerSource::Touch { finger_id, force: None },
},
@@ -2086,16 +2184,15 @@ unsafe fn public_window_callback_inner(
let position = PhysicalPosition::new(x, y);
let window_id = WindowId::from_raw(window as usize);
let finger_id = RootFingerId(FingerId {
id: pointer_info.pointerId,
primary: util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY),
});
let finger_id = FingerId::from_raw(pointer_info.pointerId as usize);
let primary = util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY);
if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id: None,
primary,
position,
kind: if let PT_TOUCH = pointer_info.pointerType {
PointerKind::Touch(finger_id)
@@ -2108,6 +2205,7 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerButton {
device_id: None,
primary,
state: Pressed,
position,
button: if let PT_TOUCH = pointer_info.pointerType {
@@ -2122,6 +2220,7 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerButton {
device_id: None,
primary,
state: Released,
position,
button: if let PT_TOUCH = pointer_info.pointerType {
@@ -2135,6 +2234,7 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
primary,
position: Some(position),
kind: if let PT_TOUCH = pointer_info.pointerType {
PointerKind::Touch(finger_id)
@@ -2148,6 +2248,7 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
primary,
position,
source: if let PT_TOUCH = pointer_info.pointerType {
PointerSource::Touch { finger_id, force }

View File

@@ -248,9 +248,8 @@ impl EventLoopRunner {
}
}
/// Dispatch control flow events (`NewEvents`, `AboutToWait`, and
/// `LoopExiting`) as necessary to bring the internal `RunnerState` to the
/// new runner state.
/// Dispatch control flow events (`new_events`, `about_to_wait`) as necessary to bring the
/// internal `RunnerState` to the new runner state.
///
/// The state transitions are defined as follows:
///
@@ -294,7 +293,6 @@ impl EventLoopRunner {
self.call_new_events(true);
self.call_event_handler(Event::AboutToWait);
self.last_events_cleared.set(Instant::now());
self.call_event_handler(Event::LoopExiting);
},
(_, Uninitialized) => panic!("cannot move state to Uninitialized"),
@@ -302,9 +300,7 @@ impl EventLoopRunner {
(Idle, HandlingMainEvents) => {
self.call_new_events(false);
},
(Idle, Destroyed) => {
self.call_event_handler(Event::LoopExiting);
},
(Idle, Destroyed) => {},
(HandlingMainEvents, Idle) => {
// This is always the last event we dispatch before waiting for new events
@@ -314,7 +310,6 @@ impl EventLoopRunner {
(HandlingMainEvents, Destroyed) => {
self.call_event_handler(Event::AboutToWait);
self.last_events_cleared.set(Instant::now());
self.call_event_handler(Event::LoopExiting);
},
(Destroyed, _) => panic!("cannot move state from Destroyed"),

View File

@@ -97,7 +97,7 @@ impl KeyEventBuilder {
MatchResult::MessagesToDispatch(self.pending.complete_multi(key_events))
},
WM_KILLFOCUS => {
// sythesize keyup events
// synthesize keyup events
let kbd_state = get_kbd_state();
let key_events = Self::synthesize_kbd_state(ElementState::Released, &kbd_state);
MatchResult::MessagesToDispatch(self.pending.complete_multi(key_events))
@@ -333,11 +333,11 @@ impl KeyEventBuilder {
// We are synthesizing the press event for caps-lock first for the following reasons:
// 1. If caps-lock is *not* held down but *is* active, then we have to synthesize all
// printable keys, respecting the caps-lock state.
// 2. If caps-lock is held down, we could choose to sythesize its keypress after every other
// key, in which case all other keys *must* be sythesized as if the caps-lock state was
// be the opposite of what it currently is.
// 2. If caps-lock is held down, we could choose to synthesize its keypress after every
// other key, in which case all other keys *must* be sythesized as if the caps-lock state
// was be the opposite of what it currently is.
// --
// For the sake of simplicity we are choosing to always sythesize
// For the sake of simplicity we are choosing to always synthesize
// caps-lock first, and always use the current caps-lock state
// to determine the produced text
if is_key_pressed!(VK_CAPITAL) {
@@ -1079,6 +1079,20 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
KeyCode::AudioVolumeDown => Some(0xe02e),
KeyCode::AudioVolumeMute => Some(0xe020),
KeyCode::AudioVolumeUp => Some(0xe030),
// Extra from Chromium sources:
// https://chromium.googlesource.com/chromium/src.git/+/3e1a26c44c024d97dc9a4c09bbc6a2365398ca2c/ui/events/keycodes/dom/dom_code_data.inc
KeyCode::Lang4 => Some(0x0077),
KeyCode::Lang3 => Some(0x0078),
KeyCode::Undo => Some(0xe008),
KeyCode::Paste => Some(0xe00a),
KeyCode::Cut => Some(0xe017),
KeyCode::Copy => Some(0xe018),
KeyCode::Eject => Some(0xe02c),
KeyCode::Help => Some(0xe03b),
KeyCode::Sleep => Some(0xe05f),
KeyCode::WakeUp => Some(0xe063),
_ => None,
}
}
@@ -1238,6 +1252,20 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0xe02e => KeyCode::AudioVolumeDown,
0xe020 => KeyCode::AudioVolumeMute,
0xe030 => KeyCode::AudioVolumeUp,
// Extra from Chromium sources:
// https://chromium.googlesource.com/chromium/src.git/+/3e1a26c44c024d97dc9a4c09bbc6a2365398ca2c/ui/events/keycodes/dom/dom_code_data.inc
0x0077 => KeyCode::Lang4,
0x0078 => KeyCode::Lang3,
0xe008 => KeyCode::Undo,
0xe00a => KeyCode::Paste,
0xe017 => KeyCode::Cut,
0xe018 => KeyCode::Copy,
0xe02c => KeyCode::Eject,
0xe03b => KeyCode::Help,
0xe05f => KeyCode::Sleep,
0xe063 => KeyCode::WakeUp,
_ => return PhysicalKey::Unidentified(NativeKeyCode::Windows(scancode as u16)),
})
}

View File

@@ -2,9 +2,7 @@ use smol_str::SmolStr;
use windows_sys::Win32::Foundation::HWND;
use windows_sys::Win32::UI::WindowsAndMessaging::{HMENU, WINDOW_LONG_PTR_INDEX};
pub(crate) use self::event_loop::{
EventLoop, EventLoopProxy, OwnedDisplayHandle, PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
pub use self::icon::WinIcon as PlatformIcon;
pub(crate) use self::icon::{SelectedCursor, WinCursor as PlatformCustomCursor, WinIcon};
pub(crate) use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey};
@@ -59,25 +57,6 @@ impl Default for PlatformSpecificWindowAttributes {
unsafe impl Send for PlatformSpecificWindowAttributes {}
unsafe impl Sync for PlatformSpecificWindowAttributes {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId {
id: u32,
primary: bool,
}
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId { id: 0, primary: false }
}
}
impl FingerId {
pub fn is_primary(self) -> bool {
self.primary
}
}
fn wrap_device_id(id: u32) -> DeviceId {
DeviceId::from_raw(id as i64)
}

View File

@@ -46,7 +46,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
};
use crate::cursor::Cursor;
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::icon::Icon;
use crate::monitor::MonitorHandle as CoreMonitorHandle;
@@ -106,7 +106,6 @@ impl Window {
self.window
}
#[cfg(feature = "rwh_06")]
pub unsafe fn rwh_06_no_thread_check(
&self,
) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
@@ -119,7 +118,6 @@ impl Window {
Ok(rwh_06::RawWindowHandle::Win32(window_handle))
}
#[cfg(feature = "rwh_06")]
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
// TODO: Write a test once integration framework is ready to ensure that it holds.
// If we aren't in the GUI thread, we can't return the window.
@@ -132,7 +130,6 @@ impl Window {
unsafe { self.rwh_06_no_thread_check() }
}
#[cfg(feature = "rwh_06")]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
@@ -349,7 +346,6 @@ impl Drop for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?;
@@ -357,7 +353,6 @@ impl rwh_06::HasDisplayHandle for Window {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_window_handle_rwh_06()?;
@@ -421,15 +416,15 @@ impl CoreWindow for Window {
)
}
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
let mut position: POINT = unsafe { mem::zeroed() };
if unsafe { ClientToScreen(self.hwnd(), &mut position) } == false.into() {
fn surface_position(&self) -> PhysicalPosition<i32> {
let mut rect: RECT = unsafe { mem::zeroed() };
if unsafe { GetClientRect(self.hwnd(), &mut rect) } == false.into() {
panic!(
"Unexpected ClientToScreen failure: please report this error to \
"Unexpected GetClientRect failure: please report this error to \
rust-windowing/winit"
)
}
Ok(PhysicalPosition::new(position.x, position.y))
PhysicalPosition::new(rect.left, rect.top)
}
fn set_outer_position(&self, position: Position) {
@@ -499,6 +494,10 @@ impl CoreWindow for Window {
None
}
fn safe_area(&self) -> PhysicalInsets<u32> {
PhysicalInsets::new(0, 0, 0, 0)
}
fn set_min_surface_size(&self, size: Option<Size>) {
self.window_state_lock().min_size = size;
// Make windows re-check the window size bounds.
@@ -1054,12 +1053,10 @@ impl CoreWindow for Window {
}
}
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self
}
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
@@ -1074,7 +1071,7 @@ pub(super) struct InitData<'a> {
pub window: Option<Window>,
}
impl<'a> InitData<'a> {
impl InitData<'_> {
unsafe fn create_window(&self, window: HWND) -> Window {
// Register for touch events if applicable
{
@@ -1288,7 +1285,6 @@ unsafe fn init(
},
};
#[cfg(feature = "rwh_06")]
let parent = match attributes.parent_window.as_ref().map(|handle| handle.0) {
Some(rwh_06::RawWindowHandle::Win32(handle)) => {
window_flags.set(WindowFlags::CHILD, true);
@@ -1301,9 +1297,6 @@ unsafe fn init(
None => fallback_parent(),
};
#[cfg(not(feature = "rwh_06"))]
let parent = fallback_parent();
let menu = attributes.platform_specific.menu;
let fullscreen = attributes.fullscreen.clone();
let maximized = attributes.maximized;

View File

@@ -7,7 +7,7 @@ pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError};
use serde::{Deserialize, Serialize};
pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorSource, MAX_CURSOR_SIZE};
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::RequestError;
pub use crate::icon::{BadIcon, Icon};
use crate::monitor::{MonitorHandle, VideoModeHandle};
@@ -67,7 +67,6 @@ pub struct WindowAttributes {
pub window_level: WindowLevel,
pub active: bool,
pub cursor: Cursor,
#[cfg(feature = "rwh_06")]
pub(crate) parent_window: Option<SendSyncRawWindowHandle>,
pub fullscreen: Option<Fullscreen>,
// Platform-specific configuration.
@@ -98,7 +97,6 @@ impl Default for WindowAttributes {
preferred_theme: None,
content_protected: false,
cursor: Cursor::default(),
#[cfg(feature = "rwh_06")]
parent_window: None,
active: true,
platform_specific: Default::default(),
@@ -113,17 +111,13 @@ impl Default for WindowAttributes {
/// The user has to account for that when using [`WindowAttributes::with_parent_window()`],
/// which is `unsafe`.
#[derive(Debug, Clone, PartialEq)]
#[cfg(feature = "rwh_06")]
pub(crate) struct SendSyncRawWindowHandle(pub(crate) rwh_06::RawWindowHandle);
#[cfg(feature = "rwh_06")]
unsafe impl Send for SendSyncRawWindowHandle {}
#[cfg(feature = "rwh_06")]
unsafe impl Sync for SendSyncRawWindowHandle {}
impl WindowAttributes {
/// Get the parent window stored on the attributes.
#[cfg(feature = "rwh_06")]
pub fn parent_window(&self) -> Option<&rwh_06::RawWindowHandle> {
self.parent_window.as_ref().map(|handle| &handle.0)
}
@@ -409,7 +403,6 @@ impl WindowAttributes {
/// <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
/// - **X11**: A child window is confined to the client area of its parent window.
/// - **Android / iOS / Wayland / Web:** Unsupported.
#[cfg(feature = "rwh_06")]
#[inline]
pub unsafe fn with_parent_window(
mut self,
@@ -581,41 +574,51 @@ pub trait Window: AsAny + Send + Sync {
// extension trait
fn reset_dead_keys(&self);
/// Returns the position of the top-left hand corner of the window's client area relative to the
/// top-left hand corner of the desktop.
/// The position of the top-left hand corner of the surface relative to the top-left hand corner
/// of the window.
///
/// The same conditions that apply to [`Window::outer_position`] apply to this method.
/// This, combined with [`outer_position`], can be useful for calculating the position of the
/// surface relative to the desktop.
///
/// ## Platform-specific
/// This may also be useful for figuring out the size of the window's decorations (such as
/// buttons, title, etc.), but may also not correspond to that (e.g. if the title bar is made
/// transparent using [`with_titlebar_transparent`] on macOS, or your are drawing window
/// decorations yourself).
///
/// - **iOS:** Returns the top left coordinates of the window's [safe area] in the screen space
/// coordinate system.
/// - **Web:** Returns the top-left coordinates relative to the viewport. _Note: this returns
/// the same value as [`Window::outer_position`]._
/// - **Android / Wayland:** Always returns [`RequestError::NotSupported`].
/// This may be negative.
///
/// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError>;
/// If the window does not have any decorations, and the surface is in the exact same position
/// as the window itself, this simply returns `(0, 0)`.
///
/// [`outer_position`]: Self::outer_position
#[cfg_attr(
any(macos_platform, docsrs),
doc = "[`with_titlebar_transparent`]: \
crate::platform::macos::WindowAttributesExtMacOS::with_titlebar_transparent"
)]
#[cfg_attr(
not(any(macos_platform, docsrs)),
doc = "[`with_titlebar_transparent`]: #only-available-on-macos"
)]
fn surface_position(&self) -> PhysicalPosition<i32>;
/// Returns the position of the top-left hand corner of the window relative to the
/// top-left hand corner of the desktop.
/// The position of the top-left hand corner of the window relative to the top-left hand corner
/// of the desktop.
///
/// Note that the top-left hand corner of the desktop is not necessarily the same as
/// the screen. If the user uses a desktop with multiple monitors, the top-left hand corner
/// of the desktop is the top-left hand corner of the monitor at the top-left of the desktop.
/// of the desktop is the top-left hand corner of the primary monitor of the desktop.
///
/// The coordinates can be negative if the top-left hand corner of the window is outside
/// of the visible screen region.
/// of the visible screen region, or on another monitor than the primary.
///
/// ## Platform-specific
///
/// - **iOS:** Returns the top left coordinates of the window in the screen space coordinate
/// system.
/// - **Web:** Returns the top-left coordinates relative to the viewport.
/// - **Android / Wayland:** Always returns [`RequestError::NotSupported`].
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError>;
/// Modifies the position of the window.
/// Sets the position of the window on the desktop.
///
/// See [`Window::outer_position`] for more information about the coordinates.
/// This automatically un-maximizes the window if it's maximized.
@@ -645,16 +648,21 @@ pub trait Window: AsAny + Send + Sync {
/// Returns the size of the window's render-able surface.
///
/// This is the dimensions you should pass to things like Wgpu or Glutin when configuring.
/// This is the dimensions you should pass to things like Wgpu or Glutin when configuring the
/// surface for drawing. See [`WindowEvent::SurfaceResized`] for listening to changes to this
/// field.
///
/// Note that to ensure that your content is not obscured by things such as notches or the title
/// bar, you will likely want to only draw important content inside a specific area of the
/// surface, see [`safe_area()`] for details.
///
/// ## Platform-specific
///
/// - **iOS:** Returns the `PhysicalSize` of the window's [safe area] in screen space
/// coordinates.
/// - **Web:** Returns the size of the canvas element. Doesn't account for CSS [`transform`].
///
/// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
/// [`WindowEvent::SurfaceResized`]: crate::event::WindowEvent::SurfaceResized
/// [`safe_area()`]: Window::safe_area
fn surface_size(&self) -> PhysicalSize<u32>;
/// Request the new size for the surface.
@@ -701,11 +709,53 @@ pub trait Window: AsAny + Send + Sync {
///
/// ## Platform-specific
///
/// - **iOS:** Returns the [`PhysicalSize`] of the window in screen space coordinates.
/// - **Web:** Returns the size of the canvas element. _Note: this returns the same value as
/// [`Window::surface_size`]._
fn outer_size(&self) -> PhysicalSize<u32>;
/// The inset area of the surface that is unobstructed.
///
/// On some devices, especially mobile devices, the screen is not a perfect rectangle, and may
/// have rounded corners, notches, bezels, and so on. When drawing your content, you usually
/// want to draw your background and other such unimportant content on the entire surface, while
/// you will want to restrict important content such as text, interactable or visual indicators
/// to the part of the screen that is actually visible; for this, you use the safe area.
///
/// The safe area is a rectangle that is defined relative to the origin at the top-left corner
/// of the surface, and the size extending downwards to the right. The area will not extend
/// beyond [the bounds of the surface][Window::surface_size].
///
/// Note that the safe area does not take occlusion from other windows into account; in a way,
/// it is only a "hardware"-level occlusion.
///
/// If the entire content of the surface is visible, this returns `(0, 0, 0, 0)`.
///
/// ## Platform-specific
///
/// - **Android / Orbital / Wayland / Windows / X11:** Unimplemented, returns `(0, 0, 0, 0)`.
///
/// ## Examples
///
/// Convert safe area insets to a size and a position.
///
/// ```
/// use winit::dpi::{PhysicalPosition, PhysicalSize};
///
/// # let surface_size = dpi::PhysicalSize::new(0, 0);
/// # #[cfg(requires_window)]
/// let surface_size = window.surface_size();
/// # let insets = dpi::PhysicalInsets::new(0, 0, 0, 0);
/// # #[cfg(requires_window)]
/// let insets = window.safe_area();
///
/// let origin = PhysicalPosition::new(insets.left, insets.top);
/// let size = PhysicalSize::new(
/// surface_size.width - insets.left - insets.right,
/// surface_size.height - insets.top - insets.bottom,
/// );
/// ```
fn safe_area(&self) -> PhysicalInsets<u32>;
/// Sets a minimum dimensions of the window's surface.
///
/// ```no_run
@@ -978,8 +1028,8 @@ pub trait Window: AsAny + Send + Sync {
fn set_window_icon(&self, window_icon: Option<Icon>);
/// Set the IME cursor editing area, where the `position` is the top left corner of that area
/// and `size` is the size of this area starting from the position. An example of such area
/// could be a input field in the UI or line in the editor.
/// in surface coordinates and `size` is the size of this area starting from the position. An
/// example of such area could be a input field in the UI or line in the editor.
///
/// The windowing system could place a candidate box close to that area, but try to not obscure
/// the specified area, so the user input to it stays visible.
@@ -1012,7 +1062,8 @@ pub trait Window: AsAny + Send + Sync {
///
/// ## Platform-specific
///
/// - **X11:** - area is not supported, only position.
/// - **X11:** Area is not supported, only position. The bottom-right corner of the provided
/// area is reported as the position.
/// - **iOS / Android / Web / Orbital:** Unsupported.
///
/// [chinese]: https://support.apple.com/guide/chinese-input-method/use-the-candidate-window-cim12992/104/mac/12.0
@@ -1035,8 +1086,8 @@ pub trait Window: AsAny + Send + Sync {
///
/// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are
/// combined.
/// - **iOS:** This will show / hide the soft keyboard.
/// - **Android / Web / Orbital:** Unsupported.
/// - **iOS / Android:** This will show / hide the soft keyboard.
/// - **Web / Orbital:** Unsupported.
/// - **X11**: Enabling IME will disable dead keys reporting during compose.
///
/// [`Ime`]: crate::event::WindowEvent::Ime
@@ -1209,7 +1260,7 @@ pub trait Window: AsAny + Send + Sync {
/// - **iOS / Android / Web:** Always returns an [`RequestError::NotSupported`].
fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError>;
/// Show [window menu] at a specified position .
/// Show [window menu] at a specified position in surface coordinates.
///
/// This is the context menu that is normally shown when interacting with
/// the title bar. This is useful when implementing custom decorations.
@@ -1276,11 +1327,9 @@ pub trait Window: AsAny + Send + Sync {
fn primary_monitor(&self) -> Option<MonitorHandle>;
/// Get the raw-window-handle v0.6 display handle.
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
/// Get the raw-window-handle v0.6 window handle.
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle;
}
@@ -1306,14 +1355,12 @@ impl std::hash::Hash for dyn Window + '_ {
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for dyn Window + '_ {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
self.rwh_06_display_handle().display_handle()
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for dyn Window + '_ {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
self.rwh_06_window_handle().window_handle()

View File

@@ -5,3 +5,6 @@ TME_LEAVE = "TME_LEAVE" # From windows_sys::Win32::UI::Input::Keyboa
XF86_Calculater = "XF86_Calculater" # From xkbcommon_dl::keysyms::XF86_Calculater
ptd = "ptd" # From windows_sys::Win32::System::Com::FORMATETC { ptd, ..}
requestor = "requestor" # From x11_dl::xlib::XSelectionEvent { requestor ..}
[files]
extend-exclude = ["*.drawio"]