Compare commits

..

1 Commits

Author SHA1 Message Date
Mads Marquart
d94b067e0c dpi: Add Rect, PhysicalRect and LogicalRect 2024-10-25 15:48:42 +02:00
84 changed files with 1740 additions and 2404 deletions

View File

@@ -25,6 +25,7 @@ version = "0.30.5"
[package.metadata.docs.rs]
features = [
"rwh_06",
"serde",
"mint",
# Enabled to get docs to compile
@@ -54,8 +55,9 @@ targets = [
[features]
android-game-activity = ["android-activity/game-activity"]
android-native-activity = ["android-activity/native-activity"]
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
default = ["rwh_06", "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",
@@ -79,7 +81,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"] }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
serde = { workspace = true, optional = true }
smol_str = "0.2.0"
tracing = { version = "0.1.40", default-features = false }
@@ -100,7 +102,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", features = ["rwh_06"], default-features = false }
ndk = { version = "0.9.0", default-features = false }
# AppKit or UIKit
[target.'cfg(target_vendor = "apple")'.dependencies]
@@ -134,7 +136,6 @@ objc2-app-kit = { version = "0.2.2", features = [
"NSScreen",
"NSTextInputClient",
"NSTextInputContext",
"NSToolbar",
"NSView",
"NSWindow",
"NSWindowScripting",
@@ -149,7 +150,6 @@ objc2-foundation = { version = "0.2.2", features = [
"NSDictionary",
"NSDistributedNotificationCenter",
"NSEnumerator",
"NSGeometry",
"NSKeyValueObserving",
"NSNotification",
"NSObjCRuntime",
@@ -214,7 +214,6 @@ 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",
@@ -297,7 +296,6 @@ web_sys = { package = "web-sys", version = "0.3.70", features = [
"FocusEvent",
"HtmlCanvasElement",
"HtmlElement",
"HtmlHtmlElement",
"HtmlImageElement",
"ImageBitmap",
"ImageBitmapOptions",
@@ -345,9 +343,11 @@ 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-3.0", # https://spdx.org/licenses/Unicode-3.0.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-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
]
confidence-threshold = 1.0
private = { ignore = true }

View File

@@ -1,130 +0,0 @@
<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>

View File

@@ -9,10 +9,3 @@ 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.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

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

View File

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

View File

@@ -13,12 +13,26 @@ fn main() -> Result<(), impl std::error::Error> {
#[path = "util/fill.rs"]
mod fill;
#[derive(Default)]
struct Application {
parent_window_id: WindowId,
parent_window_id: Option<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,
@@ -42,7 +56,7 @@ fn main() -> Result<(), impl std::error::Error> {
event: KeyEvent { state: ElementState::Pressed, .. },
..
} => {
let parent_window = self.windows.get(&self.parent_window_id).unwrap();
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).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:?}");
@@ -58,20 +72,6 @@ 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(init)
event_loop.run_app(Application::default())
}
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))]

View File

@@ -43,31 +43,16 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap();
event_loop.run(ControlFlowDemo::new)
event_loop.run_app(ControlFlowDemo::default())
}
#[derive(Default)]
struct ControlFlowDemo {
mode: Mode,
request_redraw: bool,
wait_cancelled: bool,
close_requested: bool,
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,
}
}
window: Option<Box<dyn Window>>,
}
impl ApplicationHandler for ControlFlowDemo {
@@ -80,6 +65,13 @@ 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,
@@ -120,8 +112,9 @@ impl ApplicationHandler for ControlFlowDemo {
_ => (),
},
WindowEvent::RedrawRequested => {
self.window.pre_present_notify();
fill::fill_window(&*self.window);
let window = self.window.as_ref().unwrap();
window.pre_present_notify();
fill::fill_window(window.as_ref());
},
_ => (),
}
@@ -129,7 +122,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.request_redraw();
self.window.as_ref().unwrap().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::{StartCause, WindowEvent};
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus};
use winit::window::{Window, WindowAttributes, WindowId};
@@ -22,12 +22,9 @@ fn main() -> std::process::ExitCode {
}
impl ApplicationHandler for PumpDemo {
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 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 window_event(

View File

@@ -21,8 +21,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
window: Option<Box<dyn Window>>,
}
impl App {
fn init_window(&mut self, event_loop: &dyn ActiveEventLoop) {
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) {
let window_attributes = WindowAttributes::default()
.with_title("Fantastic window number one!")
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
@@ -30,14 +36,6 @@ 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,
@@ -83,20 +81,14 @@ 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_on_demand(|event_loop| {
app.init_window(event_loop);
&mut app
})?;
event_loop.run_app_on_demand(&mut app)?;
println!("--------------------------------------------------------- Finished first loop");
println!("--------------------------------------------------------- Waiting 5 seconds");
std::thread::sleep(Duration::from_secs(5));
app.idx += 1;
event_loop.run_on_demand(|event_loop| {
app.init_window(event_loop);
&mut app
})?;
event_loop.run_app_on_demand(&mut app)?;
println!("--------------------------------------------------------- Finished second loop");
Ok(())
}

View File

@@ -11,7 +11,7 @@
pub use platform::cleanup_window;
pub use platform::fill_window;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(all(feature = "rwh_06", 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(any(target_os = "android", target_os = "ios"))]
#[cfg(not(all(feature = "rwh_06", not(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,8 +31,6 @@ 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,
@@ -70,7 +68,8 @@ fn main() -> Result<(), Box<dyn Error>> {
});
}
Ok(event_loop.run(|event_loop| Application::new(event_loop, receiver, sender))?)
let app = Application::new(&event_loop, receiver, sender);
Ok(event_loop.run_app(app)?)
}
/// Application state and event handling.
@@ -87,24 +86,21 @@ struct Application {
///
/// With OpenGL it could be EGLDisplay.
#[cfg(not(android_platform))]
context: Context<DisplayHandle<'static>>,
context: Option<Context<DisplayHandle<'static>>>,
}
impl Application {
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.
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.
#[cfg(not(android_platform))]
let context = Context::new(unsafe {
std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(
event_loop.display_handle().unwrap(),
)
})
.unwrap();
let context = Some(
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
@@ -122,7 +118,7 @@ impl Application {
.into_iter()
.collect();
let mut app = Self {
Self {
receiver,
sender,
#[cfg(not(android_platform))]
@@ -130,16 +126,7 @@ 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(
@@ -162,28 +149,6 @@ 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);
@@ -253,10 +218,6 @@ 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(),
@@ -571,12 +532,14 @@ impl ApplicationHandler for Application {
info!("Device {device_id:?} event: {event:?}");
}
#[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 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();
}
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
@@ -587,10 +550,9 @@ impl ApplicationHandler for Application {
}
#[cfg(not(android_platform))]
fn destroy_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {
for window in self.windows.values_mut() {
window.surface = None;
}
fn exiting(&mut self, _event_loop: &dyn ActiveEventLoop) {
// We must drop the context here.
self.context = None;
}
#[cfg(target_os = "macos")]
@@ -617,9 +579,9 @@ struct WindowState {
ime: bool,
/// Render surface.
///
/// `None` when not between `can_create_surfaces` and `destroy_surfaces`.
/// NOTE: This surface must be dropped before the `Window`.
#[cfg(not(android_platform))]
surface: Option<Surface<DisplayHandle<'static>, Arc<dyn Window>>>,
surface: Surface<DisplayHandle<'static>, Arc<dyn Window>>,
/// The actual winit Window.
window: Arc<dyn Window>,
/// The window theme we're drawing with.
@@ -652,6 +614,11 @@ 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;
@@ -661,14 +628,15 @@ impl WindowState {
let ime = true;
window.set_ime_allowed(ime);
Ok(Self {
let size = window.surface_size();
let mut state = 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: None,
surface,
window,
theme,
ime,
@@ -679,7 +647,10 @@ impl WindowState {
rotated: Default::default(),
panned: Default::default(),
zoom: Default::default(),
})
};
state.resize(size);
Ok(state)
}
pub fn toggle_ime(&mut self) {
@@ -853,11 +824,7 @@ impl WindowState {
(Some(width), Some(height)) => (width, height),
_ => return,
};
self.surface
.as_mut()
.unwrap()
.resize(width, height)
.expect("failed to resize inner buffer");
self.surface.resize(width, height).expect("failed to resize inner buffer");
}
self.window.request_redraw();
}
@@ -950,38 +917,18 @@ impl WindowState {
return Ok(());
}
let mut buffer = self.surface.as_mut().unwrap().buffer_mut()?;
const WHITE: u32 = 0xffffffff;
const DARK_GRAY: u32 = 0xff181818;
// 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 color = match self.theme {
Theme::Light => WHITE,
Theme::Dark => DARK_GRAY,
};
// Present the buffer
let mut buffer = self.surface.buffer_mut()?;
buffer.fill(color);
self.window.pre_present_notify();
buffer.present()?;
Ok(())
}
@@ -1018,8 +965,6 @@ enum Action {
ToggleDecorations,
ToggleResizable,
ToggleFullscreen,
#[cfg(macos_platform)]
ToggleSimpleFullscreen,
ToggleMaximize,
Minimize,
NextCursor,
@@ -1053,8 +998,6 @@ 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",
@@ -1197,8 +1140,6 @@ 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,28 +13,39 @@ fn main() -> Result<(), Box<dyn Error>> {
mod fill;
pub struct XEmbedDemo {
window: Box<dyn Window>,
parent_window_id: u32,
window: Option<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 => {
self.window.pre_present_notify();
fill::fill_window(&*self.window);
window.pre_present_notify();
fill::fill_window(window.as_ref());
},
_ => (),
}
}
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
self.window.request_redraw();
self.window.as_ref().unwrap().request_redraw();
}
}
@@ -47,15 +58,7 @@ fn main() -> Result<(), Box<dyn Error>> {
tracing_subscriber::fmt::init();
let event_loop = EventLoop::new()?;
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 }
})?)
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?)
}
#[cfg(not(x11_platform))]

View File

@@ -7,8 +7,6 @@ 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.
///
@@ -103,9 +101,7 @@ 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) {
let _ = event_loop;
}
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop);
/// Called after a wake up is requested using [`EventLoopProxy::wake_up()`].
///
@@ -146,6 +142,8 @@ 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.
/// //
@@ -164,6 +162,8 @@ 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,8 +182,9 @@ pub trait ApplicationHandler {
/// }
/// });
///
/// event_loop.run(|_event_loop| MyApp { receiver })?;
/// event_loop.run_app(&mut app)?;
///
/// drop(app);
/// background_thread.join().unwrap();
///
/// Ok(())
@@ -285,13 +286,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 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`]).
/// 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`]).
///
/// 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.
/// 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()`].
///
/// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window
/// [`Surface`]: https://developer.android.com/reference/android/view/Surface
@@ -309,6 +310,14 @@ 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
@@ -404,6 +413,11 @@ 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);
@@ -473,6 +487,11 @@ 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,24 +59,15 @@ 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`, `primary` and `position` to all
pointer events as part of the pointer event overhaul.
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId` 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
@@ -145,8 +136,6 @@ 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.
@@ -159,11 +148,10 @@ 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
@@ -195,7 +183,6 @@ 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
@@ -205,8 +192,3 @@ 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,4 +1,39 @@
//! 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))]
@@ -50,6 +85,11 @@ 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()
@@ -60,6 +100,11 @@ 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()
@@ -108,16 +153,10 @@ 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 in desktop coordinates (can also be retrieved with
/// [`Window::outer_position`]).
/// The position of the window has changed. Contains the window's new position.
///
/// ## Platform-specific
///
@@ -151,9 +190,6 @@ 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.
@@ -209,12 +245,6 @@ 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,
},
@@ -234,12 +264,6 @@ 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,
},
@@ -260,12 +284,6 @@ 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,
},
@@ -289,12 +307,6 @@ 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,
},
@@ -381,12 +393,7 @@ 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.
@@ -438,15 +445,13 @@ pub enum WindowEvent {
/// Emitted when a window should be redrawn.
///
/// This gets triggered in a few scenarios:
/// This gets triggered in two scenarios:
/// - The OS has performed an operation that's invalidated the window's contents (such as
/// resizing the window, or changing [the safe area]).
/// resizing the window).
/// - 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,
}
@@ -605,23 +610,12 @@ 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) usize);
pub struct FingerId(pub(crate) platform_impl::FingerId);
impl FingerId {
/// 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)
#[cfg(test)]
pub(crate) const fn dummy() -> Self {
FingerId(platform_impl::FingerId::dummy())
}
}
@@ -1136,7 +1130,7 @@ mod tests {
($closure:expr) => {{
#[allow(unused_mut)]
let mut x = $closure;
let fid = event::FingerId::from_raw(0);
let fid = event::FingerId::dummy();
#[allow(deprecated)]
{
@@ -1150,6 +1144,7 @@ mod tests {
let wid = WindowId::from_raw(0);
x(NewEvents(event::StartCause::Init));
x(AboutToWait);
x(LoopExiting);
x(Suspended);
x(Resumed);
@@ -1167,19 +1162,16 @@ 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,
@@ -1191,14 +1183,12 @@ 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 {
@@ -1268,11 +1258,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::from_raw(0).clone();
let fid = crate::event::FingerId::dummy().clone();
HashSet::new().insert(fid);
let mut set = [fid, fid, fid];
set.sort_unstable();

View File

@@ -13,11 +13,9 @@ 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};
@@ -195,48 +193,10 @@ impl EventLoop {
}
impl EventLoop {
/// 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.
/// Run the application with the event loop on the calling thread.
///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
///
/// ## 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
@@ -251,7 +211,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()`] to avoid the need for the Javascript exception trick, and to
/// [^1] instead of [`run_app()`] 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.
@@ -261,20 +221,11 @@ impl EventLoop {
/// [^1]: `spawn_app()` is only available on the Web platform.
///
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`run()`]: Self::run()
/// [`run_app()`]: Self::run_app()
#[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.run(|_event_loop| app)
self.event_loop.run_app(app)
}
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
@@ -322,9 +273,10 @@ impl EventLoop {
}
}
impl HasDisplayHandle for EventLoop {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
#[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())
}
}
@@ -439,7 +391,9 @@ pub trait ActiveEventLoop: AsAny {
/// Gets the current [`ControlFlow`].
fn control_flow(&self) -> ControlFlow;
/// This stops the event loop.
/// This exits the event loop.
///
/// See [`exiting`][crate::application::ApplicationHandler::exiting].
fn exit(&self);
/// Returns if the [`EventLoop`] is about to stop.
@@ -453,11 +407,13 @@ pub trait ActiveEventLoop: AsAny {
fn owned_display_handle(&self) -> OwnedDisplayHandle;
/// Get the raw-window-handle handle.
fn rwh_06_handle(&self) -> &dyn HasDisplayHandle;
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
}
impl HasDisplayHandle for dyn ActiveEventLoop + '_ {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
self.rwh_06_handle().display_handle()
}
}
@@ -474,55 +430,36 @@ impl HasDisplayHandle for dyn ActiveEventLoop + '_ {
///
/// - A zero-sized type that is likely optimized out.
/// - A reference-counted pointer to the underlying type.
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub struct 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()
}
#[cfg_attr(not(feature = "rwh_06"), allow(dead_code))]
pub(crate) platform: platform_impl::OwnedDisplayHandle,
}
impl fmt::Debug for OwnedDisplayHandle {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
}
}
impl PartialEq for OwnedDisplayHandle {
fn eq(&self, other: &Self) -> bool {
match (self.display_handle(), other.display_handle()) {
(Ok(lhs), Ok(rhs)) => lhs == rhs,
_ => false,
}
#[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 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) 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()
}
pub(crate) event_loop_proxy: platform_impl::EventLoopProxy,
}
impl EventLoopProxy {
@@ -542,11 +479,13 @@ impl EventLoopProxy {
///
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
pub fn wake_up(&self) {
self.proxy.wake_up();
self.event_loop_proxy.wake_up();
}
}
pub(crate) fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self {
Self { proxy }
impl fmt::Debug for EventLoopProxy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ActiveEventLoop").finish_non_exhaustive()
}
}

View File

@@ -1,14 +1,19 @@
//! 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
@@ -16,10 +21,9 @@
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`].
//!
//! 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.
//! 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.
//!
//! 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
@@ -40,20 +44,19 @@
//! use winit::application::ApplicationHandler;
//! use winit::event::WindowEvent;
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
//! use winit::window::{Window, WindowId};
//! use winit::window::{Window, WindowId, WindowAttributes};
//!
//! #[derive(Default)]
//! struct App {
//! window: Box<dyn Window>,
//! window: Option<Box<dyn Window>>,
//! }
//!
//! impl ApplicationHandler for App {
//! 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.
//! 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) {
//! match event {
//! WindowEvent::CloseRequested => {
//! println!("The close button was pressed; stopping");
@@ -73,45 +76,26 @@
//! // 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.request_redraw();
//! },
//! self.window.as_ref().unwrap().request_redraw();
//! }
//! _ => (),
//! }
//! }
//! }
//!
//! # // Intentionally use `fn main` for clarity
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Create a new event loop.
//! let event_loop = EventLoop::new()?;
//! let event_loop = EventLoop::new().unwrap();
//!
//! // Configure settings before launching.
//! // 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);
//!
//! // 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);
//! // 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::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(())
//! }
//! let mut app = App::default();
//! event_loop.run_app(&mut app);
//! ```
//!
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
@@ -131,45 +115,6 @@
//! [`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
@@ -258,21 +203,20 @@
//! |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()`]: event_loop::EventLoop::run
//! [`ActiveEventLoop::exit()`]: event_loop::ActiveEventLoop::exit
//! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app
//! [`exit()`]: event_loop::ActiveEventLoop::exit
//! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId
//! [`WindowAttributes`]: window::WindowAttributes
//! [window_new]: window::Window::new
//! [`ActiveEventLoop::create_window()`]: event_loop::ActiveEventLoop::create_window
//! [`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.
@@ -286,11 +230,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,11 +141,8 @@ impl MonitorHandle {
self.inner.name()
}
/// 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
/// Returns the top-left corner position of the monitor relative to the larger full
/// screen area.
///
/// ## Platform-specific
///

View File

@@ -65,7 +65,7 @@
//! let app = NSApplication::sharedApplication(mtm);
//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
//!
//! // event_loop.run(|event_loop| { ... })?;
//! // event_loop.run_app(&mut my_app);
//! Ok(())
//! }
//! ```
@@ -92,9 +92,6 @@ 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.
@@ -160,13 +157,6 @@ 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 + '_ {
@@ -265,18 +255,6 @@ 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`.
@@ -330,8 +308,6 @@ 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 {
@@ -406,12 +382,6 @@ 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,7 +21,6 @@ pub mod windows;
#[cfg(any(x11_platform, docsrs))]
pub mod x11;
#[allow(unused_imports)]
#[cfg(any(
windows_platform,
macos_platform,

View File

@@ -1,14 +1,16 @@
use crate::application::ApplicationHandler;
use crate::error::EventLoopError;
use crate::event_loop::{ActiveEventLoop, EventLoop};
use crate::event_loop::EventLoop;
#[cfg(doc)]
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
use crate::{
event_loop::ActiveEventLoop, platform::pump_events::EventLoopExtPumpEvents, window::Window,
};
/// Additional methods on [`EventLoop`] to return control flow to the caller.
pub trait EventLoopExtRunOnDemand {
/// Run the application with the event loop on the calling thread.
///
/// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`)
/// Unlike [`EventLoop::run_app`], 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.
@@ -21,7 +23,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_on_demand` is called the startup sequence of `init`, followed by
/// Each time `run_app_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.
@@ -38,7 +40,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()`] for portability, unless you
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you
/// specifically need the ability to re-run a single event loop more than once
///
/// # Supported Platforms
@@ -58,18 +60,12 @@ pub trait EventLoopExtRunOnDemand {
///
/// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError>;
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>;
}
impl EventLoopExtRunOnDemand for EventLoop {
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)
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError> {
self.event_loop.run_app_on_demand(app)
}
}

View File

@@ -57,6 +57,7 @@ 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;
@@ -190,10 +191,10 @@ pub trait EventLoopExtWeb {
/// Initializes the winit event loop.
///
/// Unlike
#[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run()`")]
#[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run_app()`")]
#[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run()`]"
doc = "[`run_app()`]"
)]
/// [^1], this returns immediately, and doesn't throw an exception in order to
/// satisfy its [`!`] return type.
@@ -206,9 +207,9 @@ pub trait EventLoopExtWeb {
///
#[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run()`]: EventLoop::run()"
doc = "[`run_app()`]: EventLoop::run_app()"
)]
/// [^1]: `run()` is _not_ available on Wasm when the target supports `exception-handling`.
/// [^1]: `run_app()` is _not_ available on Wasm when the target supports `exception-handling`.
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A);
/// Sets the strategy for [`ControlFlow::Poll`].
@@ -779,3 +780,16 @@ 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;
use crate::event::{DeviceId, FingerId};
use crate::event_loop::EventLoopBuilder;
use crate::monitor::MonitorHandle;
use crate::window::{BadIcon, Icon, Window, WindowAttributes};
@@ -135,6 +135,7 @@ 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.
@@ -336,6 +337,7 @@ pub trait WindowExtWindows {
/// });
/// # }
/// ```
#[cfg(feature = "rwh_06")]
unsafe fn window_handle_any_thread(
&self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>;
@@ -399,6 +401,7 @@ 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> {
@@ -432,8 +435,16 @@ pub trait WindowBorrowExtWindows: Borrow<dyn Window> + Sized {
/// Win32 APIs.
///
/// [`Window`]: crate::window::Window
/// [`HasWindowHandle`]: rwh_06::HasWindowHandle
/// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread
#[cfg_attr(
feature = "rwh_06",
doc = "[`HasWindowHandle`]: rwh_06::HasWindowHandle",
doc = "[`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread"
)]
#[cfg_attr(
not(feature = "rwh_06"),
doc = "[`HasWindowHandle`]: #only-available-with-rwh_06",
doc = "[`window_handle_any_thread`]: #only-available-with-rwh_06"
)]
unsafe fn any_thread(self) -> AnyThread<Self>
where
Self: Window,
@@ -659,6 +670,20 @@ impl DeviceIdExtWindows for DeviceId {
}
}
/// Additional methods on `FingerId` that are specific to Windows.
pub trait FingerIdExtWindows {
/// Indicates if the finger represents the first contact in a multi-touch interaction.
#[allow(clippy::wrong_self_convention)]
fn is_primary(self) -> bool;
}
impl FingerIdExtWindows for FingerId {
#[inline]
fn is_primary(self) -> bool {
self.0.is_primary()
}
}
/// Additional methods on `Icon` that are specific to Windows.
pub trait IconExtWindows: Sized {
/// Create an icon from a file path.

View File

@@ -13,13 +13,12 @@ use tracing::{debug, trace, warn};
use crate::application::ApplicationHandler;
use crate::cursor::Cursor;
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event::{self, DeviceId, FingerId, Force, StartCause, SurfaceSizeWriter};
use crate::event::{self, DeviceId, Force, StartCause, SurfaceSizeWriter};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
};
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform::pump_events::PumpStatus;
@@ -108,7 +107,6 @@ pub struct EventLoop {
running: bool,
pending_redraw: bool,
cause: StartCause,
primary_pointer: Option<FingerId>,
ignore_volume_keys: bool,
combining_accent: Option<char>,
}
@@ -132,24 +130,22 @@ 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()),
event_loop_proxy,
proxy_wake_up,
},
redraw_flag,
loop_running: false,
@@ -278,7 +274,7 @@ impl EventLoop {
},
}
if self.window_target.event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
if self.window_target.proxy_wake_up.swap(false, Ordering::Relaxed) {
app.proxy_wake_up(&self.window_target);
}
@@ -337,83 +333,36 @@ impl EventLoop {
_ => None,
};
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));
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));
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)
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,
},
// 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 {
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
let event = event::WindowEvent::PointerButton {
device_id,
primary,
state: event::ElementState::Released,
state: event::ElementState::Pressed,
position,
button: match tool_type {
android_activity::input::ToolType::Finger => {
@@ -425,24 +374,56 @@ impl EventLoop {
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
}
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)
},
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,
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerKind::Unknown,
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
_ => unreachable!(),
};
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,
},
};
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
},
_ => unreachable!(),
}
}
}
},
@@ -496,21 +477,15 @@ impl EventLoop {
input_status
}
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<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
}
pub fn run_on_demand<A: ApplicationHandler>(
pub fn run_app_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
mut app: 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) => {
@@ -552,6 +527,8 @@ impl EventLoop {
if self.exiting() {
self.loop_running = false;
app.exiting(&self.window_target);
PumpStatus::Exit(0)
} else {
PumpStatus::Continue
@@ -568,8 +545,7 @@ impl EventLoop {
self.pending_redraw |= self.redraw_flag.get_and_reset();
timeout = if self.running
&& (self.pending_redraw
|| self.window_target.event_loop_proxy.wake_up.load(Ordering::Relaxed))
&& (self.pending_redraw || self.window_target.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)
@@ -602,7 +578,7 @@ impl EventLoop {
self.pending_redraw |= self.redraw_flag.get_and_reset();
if !self.running
|| (!self.pending_redraw
&& !self.window_target.event_loop_proxy.wake_up.load(Ordering::Relaxed))
&& !self.window_target.proxy_wake_up.load(Ordering::Relaxed))
{
return;
}
@@ -641,20 +617,15 @@ impl EventLoop {
}
}
#[derive(Clone)]
pub struct EventLoopProxy {
wake_up: AtomicBool,
proxy_wake_up: Arc<AtomicBool>,
waker: AndroidAppWaker,
}
impl EventLoopProxy {
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);
pub fn wake_up(&self) {
self.proxy_wake_up.store(true, Ordering::Relaxed);
self.waker.wake();
}
}
@@ -664,7 +635,7 @@ pub struct ActiveEventLoop {
control_flow: Cell<ControlFlow>,
exit: Cell<bool>,
redraw_requester: RedrawRequester,
event_loop_proxy: Arc<EventLoopProxy>,
proxy_wake_up: Arc<AtomicBool>,
}
impl ActiveEventLoop {
@@ -674,8 +645,12 @@ impl ActiveEventLoop {
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(self.event_loop_proxy.clone())
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_window(
@@ -722,15 +697,17 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.exit.get()
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: 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();
@@ -741,10 +718,23 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle;
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()) })
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)
}
}
@@ -774,6 +764,7 @@ 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> {
@@ -791,11 +782,13 @@ 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()?;
@@ -803,6 +796,7 @@ 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()?;
@@ -837,8 +831,8 @@ impl CoreWindow for Window {
fn pre_present_notify(&self) {}
fn surface_position(&self) -> PhysicalPosition<i32> {
(0, 0).into()
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Err(NotSupportedError::new("inner_position is not supported").into())
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
@@ -861,10 +855,6 @@ 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>) {}
@@ -931,13 +921,7 @@ impl CoreWindow for Window {
fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
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_allowed(&self, _allowed: bool) {}
fn set_ime_purpose(&self, _purpose: ImePurpose) {}
@@ -990,10 +974,12 @@ 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::Ordering as AtomicOrdering;
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
use std::sync::Arc;
use std::time::Instant;
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningApplication};
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy};
use objc2_foundation::{MainThreadMarker, NSNotification};
use super::super::event_handler::EventHandler;
use super::event_loop::{stop_app_immediately, EventLoopProxy, PanicInfo};
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
use super::menu;
use super::observer::{EventLoopWaker, RunLoop};
use crate::application::ApplicationHandler;
use crate::event::{StartCause, WindowEvent};
use crate::event_loop::{ActiveEventLoop, ControlFlow};
use crate::event_loop::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,
event_loop_proxy: Arc<EventLoopProxy>,
proxy_wake_up: Arc<AtomicBool>,
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,
event_loop_proxy: Arc::new(EventLoopProxy::new()),
proxy_wake_up: Arc::new(AtomicBool::new(false)),
default_menu,
activate_ignoring_other_apps,
run_loop: RunLoop::main(mtm),
@@ -113,21 +113,10 @@ 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 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);
}
// 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());
}
#[allow(deprecated)]
@@ -163,29 +152,21 @@ 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_init_closure<A: ApplicationHandler, R>(
pub fn set_event_handler<R>(
&self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
handler: &mut dyn ApplicationHandler,
closure: impl FnOnce() -> R,
) -> R {
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)
self.event_handler.set(handler, closure)
}
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.event_loop_proxy
pub fn proxy_wake_up(&self) -> Arc<AtomicBool> {
self.proxy_wake_up.clone()
}
/// If `pump_events` is called to progress the event loop then we
@@ -216,6 +197,10 @@ 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);
@@ -365,7 +350,7 @@ impl AppState {
return;
}
if self.event_loop_proxy.wake_up.swap(false, AtomicOrdering::Relaxed) {
if self.proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
}

View File

@@ -20,7 +20,6 @@ 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;
@@ -33,8 +32,7 @@ use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, RequestError};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
};
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform::macos::ActivationPolicy;
@@ -97,8 +95,9 @@ impl ActiveEventLoop {
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(self.app_state.event_loop_proxy().clone())
fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = EventLoopProxy::new(self.app_state.proxy_wake_up());
RootEventLoopProxy { event_loop_proxy }
}
fn create_window(
@@ -152,15 +151,17 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.app_state.exiting()
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: 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());
@@ -274,23 +275,20 @@ impl EventLoop {
&self.window_target
}
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<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
}
// 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_on_demand<A: ApplicationHandler>(
pub fn run_app_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
mut app: A,
) -> Result<(), EventLoopError> {
self.app_state.clear_exit();
self.app_state.set_init_closure(init_closure, || {
self.app_state.set_event_handler(&mut app, || {
autoreleasepool(|_| {
// clear / normalize pump_events state
self.app_state.set_wait_timeout(None);
@@ -393,12 +391,16 @@ impl EventLoop {
}
}
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle;
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)) }
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())
}
}
@@ -437,9 +439,8 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
}
}
#[derive(Debug)]
pub struct EventLoopProxy {
pub(crate) wake_up: AtomicBool,
proxy_wake_up: Arc<AtomicBool>,
source: CFRunLoopSourceRef,
}
@@ -454,8 +455,14 @@ impl Drop for EventLoopProxy {
}
}
impl Clone for EventLoopProxy {
fn clone(&self) -> Self {
EventLoopProxy::new(self.proxy_wake_up.clone())
}
}
impl EventLoopProxy {
pub(crate) fn new() -> Self {
fn new(proxy_wake_up: Arc<AtomicBool>) -> Self {
unsafe {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
@@ -479,16 +486,14 @@ impl EventLoopProxy {
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);
EventLoopProxy { wake_up: AtomicBool::new(false), source }
EventLoopProxy { proxy_wake_up, source }
}
}
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.wake_up.store(true, AtomicOrdering::Relaxed);
pub fn wake_up(&self) {
self.proxy_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,7 +17,8 @@ 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, PlatformSpecificEventLoopAttributes,
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::Window;
@@ -25,3 +26,13 @@ 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().is_some_and(|c| c.is_control());
let is_control = string.chars().next().map_or(false, |c| c.is_control());
// Commit only if we have marked text.
if unsafe { self.hasMarkedText() } && self.is_ime_enabled() && !is_control {
@@ -657,7 +657,6 @@ declare_class!(
self.queue_event(WindowEvent::PointerEntered {
device_id: None,
primary: true,
position,
kind: PointerKind::Mouse,
});
@@ -671,7 +670,6 @@ declare_class!(
self.queue_event(WindowEvent::PointerLeft {
device_id: None,
primary: true,
position: Some(position),
kind: PointerKind::Mouse,
});
@@ -1063,7 +1061,6 @@ impl WinitView {
self.queue_event(WindowEvent::PointerButton {
device_id: None,
primary: true,
state: button_state,
position,
button: button.into(),
@@ -1090,7 +1087,6 @@ 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,6 +42,7 @@ impl Window {
self.delegate.get_on_main(|delegate| f(delegate))
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_window_handle_rwh_06(
&self,
@@ -53,6 +54,7 @@ impl Window {
}
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_display_handle_rwh_06(
&self,
@@ -72,6 +74,7 @@ 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()?;
@@ -79,6 +82,7 @@ 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()?;
@@ -107,12 +111,12 @@ impl CoreWindow for Window {
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
}
fn surface_position(&self) -> dpi::PhysicalPosition<i32> {
self.maybe_wait_on_main(|delegate| delegate.surface_position())
fn inner_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
}
fn outer_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
self.maybe_wait_on_main(|delegate| delegate.outer_position())
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
}
fn set_outer_position(&self, position: Position) {
@@ -131,10 +135,6 @@ 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,10 +323,12 @@ 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;
use core_graphics::display::{CGDisplay, CGPoint};
use monitor::VideoModeHandle;
use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::{AnyObject, ProtocolObject};
@@ -15,16 +15,15 @@ use objc2_app_kit::{
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization,
NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType,
NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard,
NSRequestUserAttentionType, NSScreen, NSToolbar, NSView, NSWindowButton, NSWindowDelegate,
NSRequestUserAttentionType, NSScreen, NSView, NSWindowButton, NSWindowDelegate,
NSWindowFullScreenButton, NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode,
NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility,
NSWindowToolbarStyle,
};
use objc2_foundation::{
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSEdgeInsets,
NSKeyValueChangeKey, NSKeyValueChangeNewKey, NSKeyValueChangeOldKey,
NSKeyValueObservingOptions, NSObject, NSObjectNSDelayedPerforming,
NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint, NSRect, NSSize, NSString,
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSKeyValueChangeKey,
NSKeyValueChangeNewKey, NSKeyValueChangeOldKey, NSKeyValueObservingOptions, NSObject,
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
NSRect, NSSize, NSString,
};
use tracing::{trace, warn};
@@ -35,10 +34,7 @@ use super::observer::RunLoop;
use super::view::WinitView;
use super::window::WinitWindow;
use super::{ffi, Fullscreen, MonitorHandle};
use crate::dpi::{
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
Position, Size,
};
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{SurfaceSizeWriter, WindowEvent};
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
@@ -61,7 +57,6 @@ 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 {
@@ -80,7 +75,6 @@ impl Default for PlatformSpecificWindowAttributes {
tabbing_identifier: None,
option_as_alt: Default::default(),
borderless_game: false,
unified_titlebar: false,
}
}
}
@@ -445,15 +439,9 @@ 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;
@@ -570,12 +558,6 @@ 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;
}
@@ -634,14 +616,6 @@ 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) {
@@ -707,6 +681,7 @@ 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
@@ -765,6 +740,12 @@ 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.
@@ -795,6 +776,10 @@ 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));
@@ -939,15 +924,15 @@ impl WindowDelegate {
#[inline]
pub fn pre_present_notify(&self) {}
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
pub fn outer_position(&self) -> PhysicalPosition<i32> {
let position = flip_window_screen_coordinates(self.window().frame());
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
}
pub fn surface_position(&self) -> PhysicalPosition<i32> {
pub fn inner_position(&self) -> PhysicalPosition<i32> {
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
let logical = LogicalPosition::new(content_rect.origin.x, content_rect.origin.y);
logical.to_physical(self.scale_factor())
let position = flip_window_screen_coordinates(content_rect);
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
}
pub fn set_outer_position(&self, position: Position) {
@@ -973,32 +958,6 @@ 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();
@@ -1195,12 +1154,13 @@ impl WindowDelegate {
#[inline]
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), RequestError> {
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,
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,
};
CGDisplay::warp_mouse_cursor_position(point)
.map_err(|status| os_error!(format!("CGError {status}")))?;
@@ -1660,6 +1620,7 @@ 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({
@@ -1782,15 +1743,12 @@ 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);
@@ -1803,22 +1761,11 @@ 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]
@@ -1890,34 +1837,6 @@ 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,55 +1,33 @@
use std::cell::Cell;
use std::cell::RefCell;
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 {
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?
/// 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>>,
}
impl fmt::Debug for EventHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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>",
let state = match self.inner.try_borrow().as_deref() {
Ok(Some(_)) => "<available>",
Ok(None) => "<not set>",
Err(_) => "<in use>",
};
self.state.set(state);
f.debug_struct("EventHandler").field("state", &string).finish_non_exhaustive()
f.debug_struct("EventHandler").field("state", &state).finish_non_exhaustive()
}
}
impl EventHandler {
pub(crate) const fn new() -> Self {
Self { state: Cell::new(State::NotRegistered) }
pub(crate) fn new() -> Self {
Self { inner: RefCell::new(None) }
}
/// Set the event loop handler for the duration of the given closure.
@@ -59,7 +37,7 @@ impl EventHandler {
/// from within the closure.
pub(crate) fn set<'handler, R>(
&self,
init_closure: InitClosure<'handler>,
app: &'handler mut dyn ApplicationHandler,
closure: impl FnOnce() -> R,
) -> R {
// SAFETY: We extend the lifetime of the handler here so that we can
@@ -68,9 +46,14 @@ 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::<InitClosure<'handler>, InitClosure<'static>>(app) };
let handler = unsafe {
mem::transmute::<
&'handler mut dyn ApplicationHandler,
&'static mut dyn ApplicationHandler,
>(app)
};
match self.state.try_borrow_mut().as_deref_mut() {
match self.inner.try_borrow_mut().as_deref_mut() {
Ok(Some(_)) => {
unreachable!("tried to set handler while another was already set");
},
@@ -86,7 +69,7 @@ impl EventHandler {
impl Drop for ClearOnDrop<'_> {
fn drop(&mut self) {
match self.0.state.try_borrow_mut().as_deref_mut() {
match self.0.inner.try_borrow_mut().as_deref_mut() {
Ok(data @ Some(_)) => {
*data = None;
},
@@ -118,10 +101,6 @@ 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()
@@ -141,7 +120,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,9 +7,7 @@ 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::Ordering;
use std::sync::atomic::{AtomicBool, 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, EventLoopProxy};
use super::ActiveEventLoop;
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,
event_loop_proxy: Arc<EventLoopProxy>,
proxy_wake_up: Arc<AtomicBool>,
}
impl AppState {
@@ -164,7 +164,7 @@ impl AppState {
}),
control_flow: ControlFlow::default(),
waker,
event_loop_proxy: Arc::new(EventLoopProxy::new()),
proxy_wake_up: Arc::new(AtomicBool::new(false)),
});
}
init_guard(&mut guard);
@@ -376,8 +376,8 @@ impl AppState {
}
}
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.event_loop_proxy
pub(crate) fn proxy_wake_up(&self) -> Arc<AtomicBool> {
self.proxy_wake_up.clone()
}
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 event_loop_proxy = this.event_loop_proxy().clone();
let proxy_wake_up = this.proxy_wake_up.clone();
drop(this);
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
if proxy_wake_up.swap(false, Ordering::Relaxed) {
handle_event(mtm, Event::UserWakeUp);
}
@@ -578,7 +578,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
}
}
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
if proxy_wake_up.swap(false, Ordering::Relaxed) {
handle_event(mtm, Event::UserWakeUp);
}
}

View File

@@ -20,7 +20,6 @@ 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};
@@ -30,8 +29,7 @@ use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event::Event;
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
};
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform_impl::Window;
@@ -43,8 +41,9 @@ pub(crate) struct ActiveEventLoop {
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(AppState::get_mut(self.mtm).event_loop_proxy().clone())
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_window(
@@ -95,15 +94,17 @@ impl RootActiveEventLoop for ActiveEventLoop {
false
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: 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());
@@ -114,10 +115,13 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle;
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)) }
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())
}
}
@@ -288,13 +292,19 @@ impl EventLoop {
}
pub struct EventLoopProxy {
pub(crate) wake_up: AtomicBool,
proxy_wake_up: Arc<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 {
@@ -305,7 +315,7 @@ impl Drop for EventLoopProxy {
}
impl EventLoopProxy {
pub(crate) fn new() -> EventLoopProxy {
fn new(proxy_wake_up: Arc<AtomicBool>) -> EventLoopProxy {
unsafe {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
@@ -329,14 +339,12 @@ impl EventLoopProxy {
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);
EventLoopProxy { wake_up: AtomicBool::new(false), source }
EventLoopProxy { proxy_wake_up, source }
}
}
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.wake_up.store(true, AtomicOrdering::Relaxed);
pub fn wake_up(&self) {
self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
unsafe {
// let the main thread know there's a new event
CFRunLoopSourceSignal(self.source);

View File

@@ -10,7 +10,8 @@ mod window;
use std::fmt;
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes,
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
@@ -20,6 +21,16 @@ 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::{
UIEvent, UIForceTouchCapability, UIGestureRecognizer, UIGestureRecognizerDelegate,
UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer, UIPinchGestureRecognizer,
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITextInputTraits, UITouch,
UITouchPhase, UITouchType, UITraitEnvironment, UIView,
UICoordinateSpace, 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, Force, KeyEvent, PointerKind, PointerSource,
TouchPhase, WindowEvent,
ButtonSource, ElementState, Event, FingerId as RootFingerId, Force, KeyEvent, PointerKind,
PointerSource, TouchPhase, WindowEvent,
};
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
use crate::platform_impl::KeyEventExtra;
@@ -34,9 +34,6 @@ 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!(
@@ -73,15 +70,26 @@ declare_class!(
let mtm = MainThreadMarker::new().unwrap();
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
let frame = self.frame();
let scale_factor = self.contentScaleFactor() as f64;
let size = crate::dpi::LogicalSize {
width: frame.size.width as f64,
height: frame.size.height as f64,
}
.to_physical(scale_factor);
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 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);
}
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::WindowEvent {
@@ -116,10 +124,13 @@ declare_class!(
"invalid scale_factor set on UIView",
);
let scale_factor = scale_factor as f64;
let frame = self.frame();
let bounds = self.bounds();
let screen = window.screen();
let screen_space = screen.coordinateSpace();
let screen_frame = self.convertRect_toCoordinateSpace(bounds, &screen_space);
let size = crate::dpi::LogicalSize {
width: frame.size.width as f64,
height: frame.size.height as f64,
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
};
let window_id = window.id();
app_state::handle_nonuser_events(
@@ -140,13 +151,6 @@ 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)
@@ -189,7 +193,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 {
@@ -248,7 +252,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
@@ -299,7 +303,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),
};
@@ -367,9 +371,6 @@ 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] };
@@ -512,37 +513,14 @@ impl WinitView {
)
};
let window_id = window.id();
let finger_id = FingerId::from_raw(touch_id);
let ivars = self.ivars();
let finger_id = RootFingerId(FingerId(touch_id));
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
@@ -555,7 +533,6 @@ impl WinitView {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
primary,
state: ElementState::Pressed,
position,
button: if let UITouchType::Pencil = touch_type {
@@ -567,44 +544,26 @@ 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,
source: if let UITouchType::Pencil = touch_type {
PointerSource::Unknown
} else {
PointerSource::Touch { finger_id, force }
},
},
}));
},
// 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 {
@@ -620,7 +579,6 @@ 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, UIEdgeInsets, UIResponder, UIScreen,
UIScreenOverscanCompensation, UIViewController, UIWindow,
UIApplication, UICoordinateSpace, UIResponder, UIScreen, UIScreenOverscanCompensation,
UIViewController, UIWindow,
};
use tracing::{debug, warn};
@@ -18,10 +18,7 @@ use super::view::WinitView;
use super::view_controller::WinitViewController;
use super::{app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle};
use crate::cursor::Cursor;
use crate::dpi::{
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
Position, Size,
};
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{Event, WindowEvent};
use crate::icon::Icon;
@@ -161,19 +158,20 @@ impl Inner {
pub fn pre_present_notify(&self) {}
pub fn surface_position(&self) -> PhysicalPosition<i32> {
let view_position = self.view.frame().origin;
pub fn inner_position(&self) -> PhysicalPosition<i32> {
let safe_area = self.safe_area_screen_space();
let position =
unsafe { self.window.convertPoint_fromView(view_position, Some(&self.view)) };
let position = LogicalPosition::new(position.x, position.y);
position.to_physical(self.scale_factor())
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)
}
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
pub fn outer_position(&self) -> PhysicalPosition<i32> {
let screen_frame = self.screen_frame();
let position =
LogicalPosition { x: screen_frame.origin.x as f64, y: screen_frame.origin.y as f64 };
Ok(position.to_physical(self.scale_factor()))
let scale_factor = self.scale_factor();
position.to_physical(scale_factor)
}
pub fn set_outer_position(&self, physical_position: Position) {
@@ -189,36 +187,29 @@ impl Inner {
}
pub fn surface_size(&self) -> PhysicalSize<u32> {
let frame = self.view.frame();
let size = LogicalSize::new(frame.size.width, frame.size.height);
size.to_physical(self.scale_factor())
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)
}
pub fn outer_size(&self) -> PhysicalSize<u32> {
let frame = self.window.frame();
let size = LogicalSize::new(frame.size.width, frame.size.height);
size.to_physical(self.scale_factor())
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)
}
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")
}
@@ -429,6 +420,7 @@ 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 _;
@@ -517,6 +509,35 @@ 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) })
}
@@ -525,6 +546,7 @@ impl Window {
self.inner.get_on_main(|inner| f(inner))
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_window_handle_rwh_06(
&self,
@@ -536,6 +558,7 @@ impl Window {
}
}
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_display_handle_rwh_06(
&self,
@@ -544,6 +567,7 @@ 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()?;
@@ -551,6 +575,7 @@ 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()?;
@@ -579,12 +604,12 @@ impl CoreWindow for Window {
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
}
fn surface_position(&self) -> PhysicalPosition<i32> {
self.maybe_wait_on_main(|delegate| delegate.surface_position())
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
self.maybe_wait_on_main(|delegate| delegate.outer_position())
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
}
fn set_outer_position(&self, position: Position) {
@@ -603,10 +628,6 @@ 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))
}
@@ -794,10 +815,12 @@ 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
}
@@ -865,7 +888,7 @@ impl Inner {
impl Inner {
fn screen_frame(&self) -> CGRect {
self.rect_to_screen_space(self.window.frame())
self.rect_to_screen_space(self.window.bounds())
}
fn rect_to_screen_space(&self, rect: CGRect) -> CGRect {
@@ -877,6 +900,43 @@ 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,9 +35,6 @@ 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.
@@ -169,23 +166,23 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
125 => KeyCode::SuperLeft,
126 => KeyCode::SuperRight,
127 => KeyCode::ContextMenu,
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,
// 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,
// 139 => KeyCode::MENU,
140 => KeyCode::LaunchApp2, // CALC
// 140 => KeyCode::CALC,
// 141 => KeyCode::SETUP,
// 142 => KeyCode::SLEEP,
143 => KeyCode::WakeUp,
144 => KeyCode::LaunchApp1, // FILE
// 143 => KeyCode::WAKEUP,
// 144 => KeyCode::FILE,
// 145 => KeyCode::SENDFILE,
// 146 => KeyCode::DELETEFILE,
// 147 => KeyCode::XFER,
@@ -196,13 +193,13 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 152 => KeyCode::COFFEE,
// 153 => KeyCode::ROTATE_DISPLAY,
// 154 => KeyCode::CYCLEWINDOWS,
155 => KeyCode::LaunchMail,
156 => KeyCode::BrowserFavorites, // BOOKMARKS
// 155 => KeyCode::MAIL,
// 156 => KeyCode::BOOKMARKS,
// 157 => KeyCode::COMPUTER,
158 => KeyCode::BrowserBack,
159 => KeyCode::BrowserForward,
// 158 => KeyCode::BACK,
// 159 => KeyCode::FORWARD,
// 160 => KeyCode::CLOSECD,
161 => KeyCode::Eject, // EJECTCD
// 161 => KeyCode::EJECTCD,
// 162 => KeyCode::EJECTCLOSECD,
163 => KeyCode::MediaTrackNext,
164 => KeyCode::MediaPlayPause,
@@ -212,9 +209,9 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 168 => KeyCode::REWIND,
// 169 => KeyCode::PHONE,
// 170 => KeyCode::ISO,
171 => KeyCode::MediaSelect, // CONFIG
172 => KeyCode::BrowserHome,
173 => KeyCode::BrowserRefresh,
// 171 => KeyCode::CONFIG,
// 172 => KeyCode::HOMEPAGE,
// 173 => KeyCode::REFRESH,
// 174 => KeyCode::EXIT,
// 175 => KeyCode::MOVE,
// 176 => KeyCode::EDIT,
@@ -253,7 +250,7 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 214 => KeyCode::QUESTION,
// 215 => KeyCode::EMAIL,
// 216 => KeyCode::CHAT,
217 => KeyCode::BrowserSearch,
// 217 => KeyCode::SEARCH,
// 218 => KeyCode::CONNECT,
// 219 => KeyCode::FINANCE,
// 220 => KeyCode::SPORT,
@@ -422,32 +419,10 @@ 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),
@@ -460,7 +435,6 @@ 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 KeyContext<'_> {
impl<'a> KeyContext<'a> {
pub fn process_key_event(
&mut self,
keycode: u32,

View File

@@ -107,6 +107,24 @@ 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)]
@@ -277,6 +295,14 @@ 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,
@@ -360,18 +386,15 @@ impl EventLoop {
}
}
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<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app))
}
pub fn run_on_demand<A: ApplicationHandler>(
pub fn run_app_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
app: A,
) -> Result<(), EventLoopError> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(init_closure))
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app_on_demand(app))
}
pub fn pump_app_events<A: ApplicationHandler>(
@@ -399,6 +422,65 @@ 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,10 +16,7 @@ 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,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::PlatformCustomCursor;
@@ -28,13 +25,12 @@ use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme}
mod proxy;
pub mod sink;
use proxy::EventLoopProxy;
pub 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>;
@@ -52,7 +48,7 @@ pub struct EventLoop {
wayland_dispatcher: WaylandDispatcher,
/// Connection to the wayland server.
handle: Arc<OwnedDisplayHandle>,
connection: Connection,
/// Event loop window target.
active_event_loop: ActiveEventLoop,
@@ -120,12 +116,11 @@ impl EventLoop {
})
.map_err(|err| os_error!(err))?;
let handle = Arc::new(OwnedDisplayHandle::new(connection));
let active_event_loop = ActiveEventLoop {
handle: handle.clone(),
connection: connection.clone(),
wayland_dispatcher: wayland_dispatcher.clone(),
event_loop_awakener,
event_loop_proxy: EventLoopProxy::new(ping).into(),
event_loop_proxy: EventLoopProxy::new(ping),
queue_handle,
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None),
@@ -137,7 +132,7 @@ impl EventLoop {
compositor_updates: Vec::new(),
buffer_sink: EventSink::default(),
window_ids: Vec::new(),
handle,
connection,
wayland_dispatcher,
event_loop,
active_event_loop,
@@ -146,21 +141,15 @@ impl EventLoop {
Ok(event_loop)
}
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<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
}
pub fn run_on_demand<A: ApplicationHandler>(
pub fn run_app_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
mut app: 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) => {
@@ -204,6 +193,8 @@ 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
@@ -235,7 +226,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.handle.connection.flush().is_err() {
if self.connection.flush().is_err() {
self.set_exit_code(1);
return;
}
@@ -548,7 +539,7 @@ impl AsRawFd for EventLoop {
pub struct ActiveEventLoop {
/// Event loop proxy
event_loop_proxy: CoreEventLoopProxy,
event_loop_proxy: EventLoopProxy,
/// The event loop wakeup source.
pub event_loop_awakener: calloop::ping::Ping,
@@ -569,13 +560,17 @@ pub struct ActiveEventLoop {
/// Dispatcher of Wayland events.
pub wayland_dispatcher: WaylandDispatcher,
/// Handle for the underlying event loop.
pub handle: Arc<OwnedDisplayHandle>,
/// Connection to the wayland server.
pub connection: Connection,
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy {
self.event_loop_proxy.clone()
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 set_control_flow(&self, control_flow: ControlFlow) {
@@ -636,10 +631,13 @@ impl RootActiveEventLoop for ActiveEventLoop {
None
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(self.handle.clone())
fn owned_display_handle(&self) -> crate::event_loop::OwnedDisplayHandle {
crate::event_loop::OwnedDisplayHandle {
platform: crate::platform_impl::OwnedDisplayHandle::Wayland(self.connection.clone()),
}
}
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
@@ -659,23 +657,8 @@ 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,30 +1,19 @@
//! 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 }
}
}
impl From<EventLoopProxy> for CoreEventLoopProxy {
fn from(value: EventLoopProxy) -> Self {
CoreEventLoopProxy::new(Arc::new(value))
pub fn wake_up(&self) {
self.ping.ping();
}
}

View File

@@ -1,6 +1,6 @@
//! Winit's Wayland backend.
pub use event_loop::{ActiveEventLoop, EventLoop};
pub use event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
pub use output::{MonitorHandle, VideoModeHandle};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::Proxy;
@@ -17,6 +17,16 @@ 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,9 +40,6 @@ 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,7 +124,6 @@ impl PointerHandler for WinitState {
PointerEventKind::Enter { .. } => {
self.events_sink.push_window_event(
WindowEvent::PointerEntered {
primary: true,
device_id: None,
position,
kind: PointerKind::Mouse,
@@ -145,7 +144,6 @@ impl PointerHandler for WinitState {
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
primary: true,
device_id: None,
position: Some(position),
kind: PointerKind::Mouse,
@@ -156,7 +154,6 @@ impl PointerHandler for WinitState {
PointerEventKind::Motion { .. } => {
self.events_sink.push_window_event(
WindowEvent::PointerMoved {
primary: true,
device_id: None,
position,
source: PointerSource::Mouse,
@@ -177,7 +174,6 @@ 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, FingerId, PointerKind, PointerSource, WindowEvent};
use crate::platform_impl::wayland;
use crate::event::{ButtonSource, ElementState, PointerKind, PointerSource, WindowEvent};
use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, FingerId};
impl TouchHandler for WinitState {
fn down(
@@ -40,20 +40,15 @@ 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 = FingerId::from_raw(id as usize);
let finger_id =
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
self.events_sink.push_window_event(
WindowEvent::PointerEntered {
device_id: None,
primary,
position,
kind: PointerKind::Touch(finger_id),
},
@@ -62,7 +57,6 @@ 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 },
@@ -94,15 +88,6 @@ 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(),
@@ -110,12 +95,12 @@ impl TouchHandler for WinitState {
};
let position = touch_point.location.to_physical(scale_factor);
let finger_id = FingerId::from_raw(id as usize);
let finger_id =
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
self.events_sink.push_window_event(
WindowEvent::PointerButton {
device_id: None,
primary,
state: ElementState::Released,
position,
button: ButtonSource::Touch { finger_id, force: None },
@@ -125,7 +110,6 @@ impl TouchHandler for WinitState {
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
device_id: None,
primary,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
@@ -156,8 +140,6 @@ 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(),
@@ -169,10 +151,11 @@ 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: FingerId::from_raw(id as usize),
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
FingerId(id),
)),
force: None,
},
},
@@ -196,21 +179,19 @@ 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(FingerId::from_raw(id as usize)),
kind: PointerKind::Touch(crate::event::FingerId(
crate::platform_impl::FingerId::Wayland(FingerId(id)),
)),
},
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, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{LogicalSize, 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.handle.connection.display();
let display = event_loop_window_target.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.handle.clone(),
event_loop_window_target.connection.clone(),
&event_loop_window_target.queue_handle,
&state,
size,
@@ -248,6 +248,7 @@ 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({
@@ -259,6 +260,7 @@ 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({
@@ -303,8 +305,9 @@ impl CoreWindow for Window {
crate::platform_impl::common::xkb::reset_dead_keys()
}
fn surface_position(&self) -> PhysicalPosition<i32> {
(0, 0).into()
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Err(NotSupportedError::new("window position information is not available on Wayland")
.into())
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
@@ -335,10 +338,6 @@ 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));
@@ -649,11 +648,13 @@ 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::{Proxy, QueueHandle};
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
use sctk::reexports::csd_frame::{
DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowState as XdgWindowState,
};
@@ -31,7 +31,6 @@ 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,
@@ -53,7 +52,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 handle: Arc<OwnedDisplayHandle>,
pub connection: Connection,
/// The `Shm` to set cursor.
pub shm: WlShm,
@@ -162,7 +161,7 @@ pub struct WindowState {
impl WindowState {
/// Create new window state.
pub fn new(
handle: Arc<OwnedDisplayHandle>,
connection: Connection,
queue_handle: &QueueHandle<WinitState>,
winit_state: &WinitState,
initial_size: Size,
@@ -184,7 +183,7 @@ impl WindowState {
blur: None,
blur_manager: winit_state.kwin_blur_manager.clone(),
compositor,
handle,
connection,
csd_fails: false,
cursor_grab_mode: GrabState::new(),
selected_cursor: Default::default(),
@@ -694,7 +693,7 @@ impl WindowState {
}
self.apply_on_pointer(|pointer, _| {
if pointer.set_cursor(&self.handle.connection, cursor_icon).is_err() {
if pointer.set_cursor(&self.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 std::fmt::Write for Writer<'_> {
impl<'a> std::fmt::Write for Writer<'a> {
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,9 +22,8 @@ use xkbcommon_dl::xkb_mod_mask_t;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::event::{
ButtonSource, DeviceEvent, DeviceId, ElementState, Event, FingerId, Ime, MouseButton,
MouseScrollDelta, PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, TouchPhase,
WindowEvent,
ButtonSource, DeviceEvent, DeviceId, ElementState, Event, Ime, MouseButton, MouseScrollDelta,
PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent,
};
use crate::keyboard::ModifiersState;
use crate::platform_impl::common::xkb::{self, XkbState};
@@ -34,7 +33,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, mkwid, util, CookieResultExt, Device, DeviceInfo, Dnd, DndState, ImeReceiver,
mkdid, mkfid, mkwid, util, CookieResultExt, Device, DeviceInfo, Dnd, DndState, ImeReceiver,
ScrollOrientation, UnownedWindow, WindowId,
};
@@ -1035,14 +1034,12 @@ 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(),
@@ -1050,7 +1047,6 @@ impl EventProcessor {
xlib::Button3 => WindowEvent::PointerButton {
device_id,
primary: true,
state,
position,
button: MouseButton::Right.into(),
@@ -1073,7 +1069,6 @@ impl EventProcessor {
},
8 => WindowEvent::PointerButton {
device_id,
primary: true,
state,
position,
button: MouseButton::Back.into(),
@@ -1081,14 +1076,12 @@ 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(),
@@ -1123,7 +1116,6 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerMoved {
device_id,
primary: true,
position,
source: PointerSource::Mouse,
},
@@ -1213,7 +1205,6 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerEntered {
device_id,
primary: true,
position,
kind: PointerKind::Mouse,
},
@@ -1238,7 +1229,6 @@ 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,
},
@@ -1299,12 +1289,7 @@ impl EventProcessor {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id,
primary: true,
position,
source: PointerSource::Mouse,
},
event: WindowEvent::PointerMoved { device_id, position, source: PointerSource::Mouse },
};
callback(&self.target, event);
}
@@ -1375,14 +1360,11 @@ impl EventProcessor {
// Mouse cursor position changes when touch events are received.
// Only the first concurrently active touch ID moves the mouse cursor.
let is_first_touch =
is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase);
if is_first_touch {
if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase) {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
primary: true,
position: position.cast(),
source: PointerSource::Mouse,
},
@@ -1391,7 +1373,7 @@ impl EventProcessor {
}
let device_id = Some(mkdid(xev.deviceid as xinput::DeviceId));
let finger_id = FingerId::from_raw(id as usize);
let finger_id = mkfid(id);
match phase {
xinput2::XI_TouchBegin => {
@@ -1399,7 +1381,6 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerEntered {
device_id,
primary: is_first_touch,
position,
kind: PointerKind::Touch(finger_id),
},
@@ -1409,7 +1390,6 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerButton {
device_id,
primary: is_first_touch,
state: ElementState::Pressed,
position,
button: ButtonSource::Touch { finger_id, force: None },
@@ -1422,7 +1402,6 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerMoved {
device_id,
primary: is_first_touch,
position,
source: PointerSource::Touch { finger_id, force: None },
},
@@ -1434,7 +1413,6 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerButton {
device_id,
primary: is_first_touch,
state: ElementState::Released,
position,
button: ButtonSource::Touch { finger_id, force: None },
@@ -1445,7 +1423,6 @@ impl EventProcessor {
window_id,
event: WindowEvent::PointerLeft {
device_id,
primary: is_first_touch,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},

View File

@@ -27,14 +27,13 @@ use crate::error::{EventLoopError, RequestError};
use crate::event::{DeviceId, Event, StartCause, WindowEvent};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
OwnedDisplayHandle as RootOwnedDisplayHandle,
};
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::PlatformCustomCursor;
use crate::platform_impl::{OwnedDisplayHandle, PlatformCustomCursor};
use crate::window::{
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
WindowAttributes, WindowId,
@@ -140,7 +139,7 @@ pub struct ActiveEventLoop {
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
redraw_sender: WakeSender<WindowId>,
activation_sender: WakeSender<ActivationToken>,
event_loop_proxy: CoreEventLoopProxy,
event_loop_proxy: EventLoopProxy,
device_events: Cell<DeviceEvents>,
}
@@ -307,7 +306,7 @@ impl EventLoop {
sender: activation_token_sender, // not used again so no clone
waker: waker.clone(),
},
event_loop_proxy: event_loop_proxy.into(),
event_loop_proxy,
device_events: Default::default(),
};
@@ -373,21 +372,15 @@ impl EventLoop {
&self.event_processor.target
}
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<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
}
pub fn run_on_demand<A: ApplicationHandler>(
pub fn run_app_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
mut app: 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) => {
@@ -435,6 +428,8 @@ impl EventLoop {
if let Some(code) = self.exit_code() {
self.loop_running = false;
app.exiting(self.window_target());
PumpStatus::Exit(code)
} else {
PumpStatus::Continue
@@ -652,6 +647,21 @@ 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)
}
@@ -666,8 +676,12 @@ impl ActiveEventLoop {
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy {
self.event_loop_proxy.clone()
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_window(
@@ -729,18 +743,28 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.exit.get().is_some()
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(self.x_connection().clone())
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
let handle = OwnedDisplayHandle::X(self.x_connection().clone());
RootOwnedDisplayHandle { platform: handle }
}
#[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> {
self.xconn.display_handle()
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();
}
}
@@ -766,14 +790,14 @@ impl<'a> DeviceInfo<'a> {
}
}
impl Drop for DeviceInfo<'_> {
impl<'a> Drop for DeviceInfo<'a> {
fn drop(&mut self) {
assert!(!self.info.is_null());
unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
}
}
impl Deref for DeviceInfo<'_> {
impl<'a> Deref for DeviceInfo<'a> {
type Target = [ffi::XIDeviceInfo];
fn deref(&self) -> &Self::Target {
@@ -781,29 +805,28 @@ impl Deref for DeviceInfo<'_> {
}
}
#[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 {
@@ -847,24 +870,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 `{visualid:x}`")
write!(f, "Could not find a matching X11 visual for ID `{:x}`", visualid)
},
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")
@@ -958,7 +981,7 @@ trait CookieResultExt {
fn expect_then_ignore_error(self, msg: &str);
}
impl<E: fmt::Debug> CookieResultExt for Result<VoidCookie<'_>, E> {
impl<'a, E: fmt::Debug> CookieResultExt for Result<VoidCookie<'a>, E> {
fn expect_then_ignore_error(self, msg: &str) {
self.expect(msg).ignore_error()
}
@@ -971,6 +994,10 @@ 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(X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
.ok_or_else(|| X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
// Select input data.
let event_mask =

View File

@@ -67,20 +67,15 @@ pub struct FrameExtentsHeuristic {
}
impl FrameExtentsHeuristic {
pub fn surface_position(&self) -> (i32, i32) {
pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) {
use self::FrameExtentsHeuristicPath::*;
if self.heuristic_path != UnsupportedBordered {
(self.frame_extents.left as i32, self.frame_extents.top as i32)
(x - self.frame_extents.left as i32, y - self.frame_extents.top as i32)
} else {
(0, 0)
(x, y)
}
}
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<T> Deref for XSmartPointer<'_, T> {
impl<'a, T> Deref for XSmartPointer<'a, T> {
type Target = T;
fn deref(&self) -> &T {
@@ -27,13 +27,13 @@ impl<T> Deref for XSmartPointer<'_, T> {
}
}
impl<T> DerefMut for XSmartPointer<'_, T> {
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
impl<T> Drop for XSmartPointer<'_, T> {
impl<'a, T> Drop for XSmartPointer<'a, 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::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{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 surface_position(&self) -> PhysicalPosition<i32> {
self.0.surface_position()
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
self.0.inner_position()
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
@@ -106,10 +106,6 @@ 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)
}
@@ -298,15 +294,18 @@ 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()?;
@@ -314,6 +313,7 @@ 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,26 +443,15 @@ impl UnownedWindow {
) -> Result<UnownedWindow, RequestError> {
let xconn = &event_loop.xconn;
let atoms = xconn.atoms();
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"),
)?
};
#[cfg(feature = "rwh_06")]
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 => screen.root,
None => event_loop.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() {
@@ -517,10 +506,18 @@ impl UnownedWindow {
dimensions
};
// An iterator over the visuals matching screen id combined with their depths.
let mut all_visuals = screen
.allowed_depths
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
.iter()
.flat_map(|root| &root.allowed_depths)
.flat_map(|depth| depth.visuals.iter().map(move |visual| (visual, depth.depth)));
// creating
@@ -809,20 +806,6 @@ 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();
@@ -846,6 +829,20 @@ 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()
@@ -1512,7 +1509,7 @@ impl UnownedWindow {
}
}
fn inner_position_physical(&self) -> (i32, i32) {
pub(crate) 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
@@ -1522,14 +1519,8 @@ impl UnownedWindow {
}
#[inline]
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 fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Ok(self.inner_position_physical().into())
}
pub(crate) fn set_position_inner(
@@ -1592,10 +1583,6 @@ 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()
@@ -2003,7 +1990,7 @@ impl UnownedWindow {
.query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER)
.map_err(|err| os_error!(err))?;
let window_position = self.inner_position_physical();
let window_position = self.inner_position()?;
let atoms = self.xconn.atoms();
let message = atoms[_NET_WM_MOVERESIZE];
@@ -2030,8 +2017,8 @@ impl UnownedWindow {
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
),
[
(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,
(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,
action.try_into().unwrap(),
1, // Button 1
1,
@@ -2045,19 +2032,12 @@ impl UnownedWindow {
}
#[inline]
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.
pub fn set_ime_cursor_area(&self, spot: Position, _size: Size) {
let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
let _ = self.ime_sender.lock().unwrap().send(ImeRequest::Position(
self.xwindow as ffi::Window,
x.saturating_add(width),
y.saturating_add(height),
x,
y,
));
}
@@ -2172,6 +2152,7 @@ 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());
@@ -2179,6 +2160,7 @@ impl UnownedWindow {
Ok(window_handle.into())
}
#[cfg(feature = "rwh_06")]
#[inline]
pub fn raw_display_handle_rwh_06(
&self,

View File

@@ -1,11 +1,9 @@
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;
@@ -64,13 +62,6 @@ 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 {}
@@ -164,7 +155,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()?
@@ -293,19 +284,6 @@ 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,7 +22,6 @@ 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,11 +18,7 @@ use super::{
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event::{self, Ime, Modifiers, StartCause};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::event_loop::{self, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
use crate::keyboard::{
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
PhysicalKey,
@@ -290,7 +286,8 @@ impl EventLoop {
let event_socket =
Arc::new(RedoxSocket::event().map_err(|error| os_error!(format!("{error}")))?);
let wake_socket = TimeSocket::open().map_err(|error| os_error!(format!("{error}")))?;
let wake_socket =
Arc::new(TimeSocket::open().map_err(|error| os_error!(format!("{error}")))?);
event_socket
.write(&syscall::Event {
@@ -309,7 +306,8 @@ impl EventLoop {
redraws: Arc::new(Mutex::new(VecDeque::new())),
destroys: Arc::new(Mutex::new(VecDeque::new())),
event_socket,
event_loop_proxy: Arc::new(EventLoopProxy { wake_socket, user_events_sender }),
wake_socket,
user_events_sender,
},
user_events_receiver,
})
@@ -406,7 +404,6 @@ 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,
});
@@ -420,7 +417,6 @@ 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(),
@@ -462,14 +458,12 @@ 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,
}
@@ -483,11 +477,7 @@ impl EventLoop {
}
}
pub fn run<A: ApplicationHandler>(
mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
let mut app = init_closure(&self.window_target);
pub fn run_app<A: ApplicationHandler>(mut self, mut app: A) -> Result<(), EventLoopError> {
let mut start_cause = StartCause::Init;
loop {
app.new_events(&self.window_target, start_cause);
@@ -658,6 +648,8 @@ impl EventLoop {
}
}
app.exiting(&self.window_target);
Ok(())
}
@@ -668,11 +660,11 @@ impl EventLoop {
pub struct EventLoopProxy {
user_events_sender: mpsc::SyncSender<()>,
pub(super) wake_socket: TimeSocket,
wake_socket: Arc<TimeSocket>,
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
impl EventLoopProxy {
pub 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() {
@@ -681,6 +673,15 @@ impl EventLoopProxyProvider for 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 {
@@ -690,12 +691,18 @@ 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) event_loop_proxy: Arc<EventLoopProxy>,
pub(super) wake_socket: Arc<TimeSocket>,
user_events_sender: mpsc::SyncSender<()>,
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(self.event_loop_proxy.clone())
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_window(
@@ -744,15 +751,17 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.exit.get()
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
fn owned_display_handle(&self) -> event_loop::OwnedDisplayHandle {
event_loop::OwnedDisplayHandle { platform: 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());
@@ -760,12 +769,15 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
}
}
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle;
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)) }
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())
}
}

View File

@@ -5,7 +5,7 @@ use std::{fmt, str};
use smol_str::SmolStr;
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop};
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle};
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::keyboard::Key;
mod event_loop;
@@ -99,6 +99,16 @@ 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;
@@ -125,7 +135,7 @@ impl<'a> WindowProperties<'a> {
}
}
impl fmt::Display for WindowProperties<'_> {
impl<'a> fmt::Display for WindowProperties<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,

View File

@@ -1,10 +1,9 @@
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use super::event_loop::EventLoopProxy;
use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, WindowProperties};
use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, TimeSocket, WindowProperties};
use crate::cursor::Cursor;
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{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};
@@ -24,7 +23,7 @@ pub struct Window {
window_socket: Arc<RedoxSocket>,
redraws: Arc<Mutex<VecDeque<WindowId>>>,
destroys: Arc<Mutex<VecDeque<WindowId>>>,
event_loop_proxy: Arc<EventLoopProxy>,
wake_socket: Arc<TimeSocket>,
}
impl Window {
@@ -114,13 +113,13 @@ impl Window {
creates.push_back(window_socket.clone());
}
el.event_loop_proxy.wake_socket.wake().unwrap();
el.wake_socket.wake().unwrap();
Ok(Self {
window_socket,
redraws: el.redraws.clone(),
destroys: el.destroys.clone(),
event_loop_proxy: el.event_loop_proxy.clone(),
wake_socket: el.wake_socket.clone(),
})
}
@@ -138,6 +137,7 @@ 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,6 +147,7 @@ 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()))
@@ -185,7 +186,7 @@ impl CoreWindow for Window {
if !redraws.contains(&window_id) {
redraws.push_back(window_id);
self.event_loop_proxy.wake_socket.wake().unwrap();
self.wake_socket.wake().unwrap();
}
}
@@ -198,19 +199,19 @@ impl CoreWindow for Window {
}
#[inline]
fn surface_position(&self) -> PhysicalPosition<i32> {
// TODO: adjust for window decorations
(0, 0).into()
}
#[inline]
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
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())
}
#[inline]
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
// TODO: adjust for window decorations
self.inner_position()
}
#[inline]
fn set_outer_position(&self, position: Position) {
// TODO: adjust for window decorations
@@ -239,10 +240,6 @@ 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>) {}
@@ -414,7 +411,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(())
}
@@ -450,15 +447,18 @@ 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,6 +466,7 @@ 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()?;
@@ -480,6 +481,6 @@ impl Drop for Window {
destroys.push_back(self.id());
}
self.event_loop_proxy.wake_socket.wake().unwrap();
self.wake_socket.wake().unwrap();
}
}

View File

@@ -6,13 +6,15 @@ mod channel;
mod concurrent_queue;
mod dispatcher;
mod notifier;
mod waker;
mod wrapper;
pub(crate) use atomic_waker::AtomicWaker;
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(crate) use self::wrapper::Wrapper;
pub use self::waker::{Waker, WakerSpawner};
use self::wrapper::Wrapper;

View File

@@ -0,0 +1,117 @@
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;
use crate::event::{DeviceId, FingerId as RootFingerId};
pub(crate) fn mkdid(pointer_id: i32) -> Option<DeviceId> {
if let Ok(pointer_id) = u32::try_from(pointer_id) {
@@ -10,3 +10,30 @@ 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,7 +10,8 @@ pub(crate) mod runner;
mod state;
mod window_target;
pub(crate) use window_target::ActiveEventLoop;
pub(crate) use proxy::EventLoopProxy;
pub(crate) use window_target::{ActiveEventLoop, OwnedDisplayHandle};
pub struct EventLoop {
elw: ActiveEventLoop,
@@ -96,6 +97,7 @@ 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,101 +1,17 @@
use std::future;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::Poll;
use super::runner::WeakShared;
use crate::platform_impl::platform::r#async::Waker;
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,
#[derive(Clone)]
pub struct EventLoopProxy {
runner: Waker<WeakShared>,
}
impl EventLoopProxy {
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 new(runner: Waker<WeakShared>) -> Self {
Self { runner }
}
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(())
pub fn wake_up(&self) {
self.runner.wake();
}
}

View File

@@ -3,7 +3,6 @@ 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;
@@ -14,14 +13,13 @@ 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, SafeAreaHandle};
use crate::platform_impl::platform::r#async::DispatchRunner;
use crate::platform_impl::platform::backend::EventListenerHandle;
use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
use crate::platform_impl::platform::window::Inner;
use crate::window::WindowId;
@@ -39,7 +37,7 @@ type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
struct Execution {
main_thread: MainThreadMarker,
event_loop_proxy: Arc<EventLoopProxy>,
proxy_spawner: WakerSpawner<WeakShared>,
control_flow: Cell<ControlFlow>,
poll_strategy: Cell<PollStrategy>,
wait_until_strategy: Cell<WaitUntilStrategy>,
@@ -57,7 +55,6 @@ 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>,
@@ -143,7 +140,12 @@ impl Shared {
let document = window.document().expect("Failed to obtain document");
Shared(Rc::<Execution>::new_cyclic(|weak| {
let proxy_spawner = EventLoopProxy::new(main_thread, WeakShared(weak.clone()));
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 monitor = MonitorHandler::new(
main_thread,
@@ -152,11 +154,9 @@ impl Shared {
WeakShared(weak.clone()),
);
let safe_area = SafeAreaHandle::new(&window, &document);
Execution {
main_thread,
event_loop_proxy: Arc::new(proxy_spawner),
proxy_spawner,
control_flow: Cell::new(ControlFlow::default()),
poll_strategy: Cell::new(PollStrategy::default()),
wait_until_strategy: Cell::new(WaitUntilStrategy::default()),
@@ -173,7 +173,6 @@ 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),
@@ -609,7 +608,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.
todo!("drop the application handler");
self.handle_event(Event::LoopExiting);
}
// handle_event takes in events and either queues them or applies a callback
@@ -654,7 +653,7 @@ impl Shared {
// Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle.
events.extend(
self.0
.event_loop_proxy
.proxy_spawner
.take()
.then_some(Event::UserWakeUp)
.map(EventWrapper::from),
@@ -713,7 +712,7 @@ impl Shared {
}
fn handle_loop_destroyed(&self) {
todo!("drop the application handler");
self.handle_event(Event::LoopExiting);
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;
@@ -819,8 +818,8 @@ impl Shared {
self.0.wait_until_strategy.get()
}
pub(crate) fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.0.event_loop_proxy
pub(crate) fn waker(&self) -> Waker<WeakShared> {
self.0.proxy_spawner.waker()
}
pub(crate) fn weak(&self) -> WeakShared {
@@ -830,10 +829,6 @@ 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,25 +2,24 @@ 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;
use super::{backend, runner};
use super::runner::{EventWrapper, WeakShared};
use super::{backend, runner, EventLoopProxy};
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 CoreOwnedDisplayHandle,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
};
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::web::event_loop::proxy::EventLoopProxy;
use crate::platform_impl::platform::r#async::Waker;
use crate::platform_impl::Window;
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId};
@@ -198,7 +197,7 @@ impl ActiveEventLoop {
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, primary, position, kind| {
move |active_modifiers, device_id, position, kind| {
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
@@ -209,12 +208,7 @@ impl ActiveEventLoop {
runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft {
device_id,
primary,
position: Some(position),
kind,
},
event: WindowEvent::PointerLeft { device_id, position: Some(position), kind },
})))
}
});
@@ -224,7 +218,7 @@ impl ActiveEventLoop {
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, primary, position, kind| {
move |active_modifiers, device_id, position, kind| {
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
@@ -235,7 +229,7 @@ impl ActiveEventLoop {
runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered { device_id, primary, position, kind },
event: WindowEvent::PointerEntered { device_id, position, kind },
})))
}
});
@@ -247,31 +241,21 @@ impl ActiveEventLoop {
let modifiers = self.modifiers.clone();
move |device_id, events| {
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(),
),
}
});
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()),
}
});
modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id,
primary,
position,
source,
},
}))
},
));
modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved { device_id, position, source },
}))
}));
}
},
{
@@ -279,7 +263,7 @@ impl ActiveEventLoop {
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, primary, position, state, button| {
move |active_modifiers, device_id, position, state, button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
@@ -291,13 +275,7 @@ impl ActiveEventLoop {
runner.send_events(modifiers.into_iter().chain([Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id,
primary,
state,
position,
button,
},
event: WindowEvent::PointerButton { device_id, state, position, button },
}]));
}
},
@@ -307,7 +285,7 @@ impl ActiveEventLoop {
let runner = self.runner.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, primary, position, button| {
move |active_modifiers, device_id, position, button| {
let modifiers = (modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
@@ -320,7 +298,6 @@ impl ActiveEventLoop {
window_id,
event: WindowEvent::PointerButton {
device_id,
primary,
state: ElementState::Pressed,
position,
button,
@@ -334,7 +311,7 @@ impl ActiveEventLoop {
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, primary, position, button| {
move |active_modifiers, device_id, position, button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
@@ -348,7 +325,6 @@ impl ActiveEventLoop {
window_id,
event: WindowEvent::PointerButton {
device_id,
primary,
state: ElementState::Released,
position,
button,
@@ -477,15 +453,15 @@ impl ActiveEventLoop {
self.runner.monitor().has_detailed_monitor_permission()
}
pub(crate) fn event_loop_proxy(&self) -> Arc<EventLoopProxy> {
self.runner.event_loop_proxy().clone()
pub(crate) fn waker(&self) -> Waker<WeakShared> {
self.runner.waker()
}
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = self.event_loop_proxy();
RootEventLoopProxy::new(event_loop_proxy)
let event_loop_proxy = EventLoopProxy::new(self.waker());
RootEventLoopProxy { event_loop_proxy }
}
fn create_window(
@@ -547,15 +523,17 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.runner.exiting()
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: 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());
@@ -563,12 +541,15 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
}
}
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle;
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)) }
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())
}
}

View File

@@ -37,8 +37,10 @@ pub(crate) use cursor::{
CustomCursorSource as PlatformCustomCursorSource,
};
pub use self::event::FingerId;
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
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 {
pub(super) read: CssStyleDeclaration,
pub(super) write: CssStyleDeclaration,
read: CssStyleDeclaration,
write: CssStyleDeclaration,
}
impl Canvas {
@@ -331,32 +331,28 @@ impl Canvas {
pub fn on_pointer_leave<F>(&self, handler: F)
where
F: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
F: 'static + FnMut(ModifiersState, Option<DeviceId>, 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>, bool, PhysicalPosition<f64>, PointerKind),
F: 'static + FnMut(ModifiersState, Option<DeviceId>, 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>, bool, PhysicalPosition<f64>, ButtonSource),
C: 'static + FnMut(ModifiersState, Option<DeviceId>, 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>, bool, PhysicalPosition<f64>, ButtonSource),
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_press(
&self.common,
@@ -370,15 +366,12 @@ impl Canvas {
C: 'static
+ FnMut(
Option<DeviceId>,
&mut dyn Iterator<
Item = (ModifiersState, bool, PhysicalPosition<f64>, PointerSource),
>,
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
),
B: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
bool,
PhysicalPosition<f64>,
ElementState,
ButtonSource,

View File

@@ -6,8 +6,9 @@ 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::{FingerId, MouseButton, MouseScrollDelta, PointerKind};
use crate::event::{MouseButton, MouseScrollDelta, PointerKind};
use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey};
bitflags::bitflags! {
@@ -163,7 +164,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::from_raw(pointer_id as usize)),
"touch" => PointerKind::Touch(FingerId::new(pointer_id, event.is_primary()).into()),
_ => PointerKind::Unknown,
}
}

View File

@@ -7,7 +7,6 @@ mod intersection_handle;
mod media_query_handle;
mod pointer;
mod resize_scaling;
mod safe_area;
mod schedule;
use std::cell::OnceCell;
@@ -21,7 +20,6 @@ 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,8 +35,7 @@ impl PointerHandler {
pub fn on_pointer_leave<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{
let window = canvas_common.window.clone();
self.on_cursor_leave =
@@ -47,14 +46,13 @@ 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, event.is_primary(), position, kind);
handler(modifiers, device_id, position, kind);
}));
}
pub fn on_pointer_enter<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{
let window = canvas_common.window.clone();
self.on_cursor_enter =
@@ -65,14 +63,13 @@ 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, event.is_primary(), position, kind);
handler(modifiers, device_id, position, kind);
}));
}
pub fn on_pointer_release<C>(&mut self, canvas_common: &Common, mut handler: C)
where
C: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
{
let window = canvas_common.window.clone();
self.on_pointer_release =
@@ -95,7 +92,6 @@ impl PointerHandler {
handler(
modifiers,
mkdid(pointer_id),
event.is_primary(),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
@@ -108,8 +104,7 @@ impl PointerHandler {
mut handler: C,
prevent_default: Rc<Cell<bool>>,
) where
C: 'static
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
{
let window = canvas_common.window.clone();
let canvas = canvas_common.raw().clone();
@@ -148,7 +143,6 @@ impl PointerHandler {
handler(
modifiers,
mkdid(pointer_id),
event.is_primary(),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
@@ -165,15 +159,12 @@ impl PointerHandler {
C: 'static
+ FnMut(
Option<DeviceId>,
&mut dyn Iterator<
Item = (ModifiersState, bool, PhysicalPosition<f64>, PointerSource),
>,
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
),
B: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
bool,
PhysicalPosition<f64>,
ElementState,
ButtonSource,
@@ -186,7 +177,6 @@ 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) {
@@ -223,7 +213,6 @@ impl PointerHandler {
button_handler(
event::mouse_modifiers(&event),
device_id,
primary,
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
state,
button,
@@ -240,7 +229,6 @@ 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

@@ -1,56 +0,0 @@
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,14 +2,13 @@ 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::{LogicalInsets, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::icon::Icon;
use crate::monitor::MonitorHandle as RootMonitorHandle;
@@ -27,7 +26,6 @@ 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()>>,
}
@@ -61,7 +59,6 @@ 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),
};
@@ -112,9 +109,9 @@ impl RootWindow for Window {
// Not supported
}
fn surface_position(&self) -> PhysicalPosition<i32> {
// Note: the canvas element has no window decorations.
(0, 0).into()
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 outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
@@ -155,34 +152,6 @@ 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()));
@@ -406,15 +375,18 @@ 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()
@@ -436,6 +408,7 @@ 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,7 +4,6 @@ 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};
@@ -13,18 +12,13 @@ use std::{mem, panic, ptr};
use runner::EventLoopRunner;
use windows_sys::Win32::Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE;
use windows_sys::Win32::Foundation::{
GetLastError, FALSE, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_FAILED, WPARAM,
};
use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, POINT, RECT, 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::{
CreateWaitableTimerExW, GetCurrentThreadId, SetWaitableTimer,
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, INFINITE, TIMER_ALL_ACCESS,
};
use windows_sys::Win32::System::Threading::{GetCurrentThreadId, INFINITE};
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::{
@@ -40,15 +34,14 @@ 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, 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,
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,
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,
@@ -65,12 +58,12 @@ use crate::application::ApplicationHandler;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::error::{EventLoopError, RequestError};
use crate::event::{
Event, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent,
Event, FingerId as RootFingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase,
WindowEvent,
};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
};
use crate::keyboard::ModifiersState;
use crate::monitor::MonitorHandle as RootMonitorHandle;
@@ -87,7 +80,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, Fullscreen};
use crate::platform_impl::platform::{raw_input, util, wrap_device_id, FingerId, Fullscreen};
use crate::platform_impl::Window;
use crate::utils::Lazy;
use crate::window::{
@@ -134,10 +127,6 @@ 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 {
@@ -212,7 +201,6 @@ impl EventLoop {
Ok(EventLoop {
window_target: ActiveEventLoop { thread_id, thread_msg_target, runner_shared },
msg_hook: attributes.msg_hook.take(),
high_resolution_timer: None,
})
}
@@ -220,21 +208,15 @@ impl EventLoop {
&self.window_target
}
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<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
}
pub fn run_on_demand<A: ApplicationHandler>(
pub fn run_app_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
mut app: A,
) -> Result<(), EventLoopError> {
self.window_target.clear_exit();
let mut app = init_closure(&self.window_target);
{
let runner = &self.window_target.runner_shared;
@@ -256,15 +238,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_for_messages(None);
// wait_for_messages calls user application before and after waiting
// so it may have decided to exit.
self.wait_and_dispatch_message(None);
if let Some(code) = self.exit_code() {
break code;
}
@@ -322,6 +304,7 @@ 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),
});
@@ -329,11 +312,8 @@ impl EventLoop {
}
}
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.
self.wait_and_dispatch_message(timeout);
if self.exit_code().is_none() {
self.dispatch_peeked_messages();
}
@@ -352,10 +332,8 @@ impl EventLoop {
PumpStatus::Continue
};
// 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`.
// We wait until we've checked for an exit status before clearing the
// application callback, in case we need to dispatch a LoopExiting event
//
// # Safety
// This pairs up with our call to `runner.set_event_handler` and ensures
@@ -365,27 +343,101 @@ impl EventLoop {
status
}
/// 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>) {
/// 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))
}
}
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 `MsgWaitForMultipleObjectsEx` as our wait
// We emulate similar behaviour by treating `GetMessage` 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
// `MsgWaitForMultipleObjectsEx`.
// `GetMessage`
//
runner.prepare_wait();
wait_for_messages_impl(&mut self.high_resolution_timer, runner.control_flow(), timeout);
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);
// 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`
@@ -404,7 +456,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: MSG = unsafe { mem::zeroed() };
let mut msg = unsafe { mem::zeroed() };
loop {
unsafe {
@@ -443,14 +495,6 @@ 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 {
@@ -469,7 +513,7 @@ impl ActiveEventLoop {
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = EventLoopProxy { target_window: self.thread_msg_target };
RootEventLoopProxy::new(Arc::new(event_loop_proxy))
RootEventLoopProxy { event_loop_proxy }
}
fn create_window(
@@ -522,8 +566,8 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.runner_shared.set_exit_code(0)
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
}
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
@@ -531,6 +575,7 @@ 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());
@@ -538,13 +583,16 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
}
}
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle;
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)) }
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())
}
}
@@ -569,7 +617,7 @@ impl rwh_06::HasDisplayHandle for 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]
@@ -620,139 +668,10 @@ fn dur2timeout(dur: Duration) -> u32 {
.unwrap_or(INFINITE)
}
/// 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(),);
impl Drop for EventLoop {
fn drop(&mut self) {
unsafe {
DestroyWindow(self.window_target.thread_msg_target);
}
}
}
@@ -807,14 +726,15 @@ impl EventLoopThreadExecutor {
type ThreadExecFn = Box<Box<dyn FnMut()>>;
#[derive(Clone)]
pub struct EventLoopProxy {
target_window: HWND,
}
unsafe impl Send for EventLoopProxy {}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
impl EventLoopProxy {
pub fn wake_up(&self) {
unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) };
}
}
@@ -1622,7 +1542,6 @@ 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,
},
@@ -1648,7 +1567,6 @@ 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,
},
@@ -1670,12 +1588,7 @@ unsafe fn public_window_callback_inner(
userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize),
event: PointerMoved {
device_id: None,
primary: true,
position,
source: PointerSource::Mouse,
},
event: PointerMoved { device_id: None, position, source: PointerSource::Mouse },
});
}
@@ -1693,7 +1606,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, primary: true, position: None, kind: Mouse },
event: PointerLeft { device_id: None, position: None, kind: Mouse },
});
result = ProcResult::Value(0);
@@ -1770,7 +1683,6 @@ 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(),
@@ -1796,7 +1708,6 @@ 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(),
@@ -1822,7 +1733,6 @@ 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(),
@@ -1848,7 +1758,6 @@ 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(),
@@ -1874,7 +1783,6 @@ 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(),
@@ -1900,7 +1808,6 @@ 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(),
@@ -1927,7 +1834,6 @@ 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 {
@@ -1959,7 +1865,6 @@ 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 {
@@ -2013,15 +1918,16 @@ unsafe fn public_window_callback_inner(
let position = PhysicalPosition::new(x, y);
let window_id = WindowId::from_raw(window as usize);
let finger_id = FingerId::from_raw(input.dwID as usize);
let primary = util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY);
let finger_id = RootFingerId(FingerId {
id: input.dwID,
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),
},
@@ -2030,7 +1936,6 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerButton {
device_id: None,
primary,
state: Pressed,
position,
button: Touch { finger_id, force: None },
@@ -2041,7 +1946,6 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerButton {
device_id: None,
primary,
state: Released,
position,
button: Touch { finger_id, force: None },
@@ -2051,7 +1955,6 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
primary,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
@@ -2061,7 +1964,6 @@ unsafe fn public_window_callback_inner(
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
primary,
position,
source: PointerSource::Touch { finger_id, force: None },
},
@@ -2184,15 +2086,16 @@ unsafe fn public_window_callback_inner(
let position = PhysicalPosition::new(x, y);
let window_id = WindowId::from_raw(window as usize);
let finger_id = FingerId::from_raw(pointer_info.pointerId as usize);
let primary = util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY);
let finger_id = RootFingerId(FingerId {
id: pointer_info.pointerId,
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)
@@ -2205,7 +2108,6 @@ 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 {
@@ -2220,7 +2122,6 @@ 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 {
@@ -2234,7 +2135,6 @@ 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)
@@ -2248,7 +2148,6 @@ 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,8 +248,9 @@ impl EventLoopRunner {
}
}
/// Dispatch control flow events (`new_events`, `about_to_wait`) as necessary to bring the
/// internal `RunnerState` to the new runner state.
/// Dispatch control flow events (`NewEvents`, `AboutToWait`, and
/// `LoopExiting`) as necessary to bring the internal `RunnerState` to the
/// new runner state.
///
/// The state transitions are defined as follows:
///
@@ -293,6 +294,7 @@ 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"),
@@ -300,7 +302,9 @@ impl EventLoopRunner {
(Idle, HandlingMainEvents) => {
self.call_new_events(false);
},
(Idle, Destroyed) => {},
(Idle, Destroyed) => {
self.call_event_handler(Event::LoopExiting);
},
(HandlingMainEvents, Idle) => {
// This is always the last event we dispatch before waiting for new events
@@ -310,6 +314,7 @@ 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 => {
// synthesize keyup events
// sythesize 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 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.
// 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.
// --
// For the sake of simplicity we are choosing to always synthesize
// For the sake of simplicity we are choosing to always sythesize
// caps-lock first, and always use the current caps-lock state
// to determine the produced text
if is_key_pressed!(VK_CAPITAL) {
@@ -1079,20 +1079,6 @@ 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,
}
}
@@ -1252,20 +1238,6 @@ 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,7 +2,9 @@ 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, PlatformSpecificEventLoopAttributes};
pub(crate) use self::event_loop::{
EventLoop, EventLoopProxy, OwnedDisplayHandle, 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};
@@ -57,6 +59,25 @@ 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::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::icon::Icon;
use crate::monitor::MonitorHandle as CoreMonitorHandle;
@@ -106,6 +106,7 @@ impl Window {
self.window
}
#[cfg(feature = "rwh_06")]
pub unsafe fn rwh_06_no_thread_check(
&self,
) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
@@ -118,6 +119,7 @@ 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.
@@ -130,6 +132,7 @@ 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> {
@@ -346,6 +349,7 @@ 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()?;
@@ -353,6 +357,7 @@ 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()?;
@@ -416,15 +421,15 @@ impl CoreWindow for Window {
)
}
fn surface_position(&self) -> PhysicalPosition<i32> {
let mut rect: RECT = unsafe { mem::zeroed() };
if unsafe { GetClientRect(self.hwnd(), &mut rect) } == false.into() {
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
let mut position: POINT = unsafe { mem::zeroed() };
if unsafe { ClientToScreen(self.hwnd(), &mut position) } == false.into() {
panic!(
"Unexpected GetClientRect failure: please report this error to \
"Unexpected ClientToScreen failure: please report this error to \
rust-windowing/winit"
)
}
PhysicalPosition::new(rect.left, rect.top)
Ok(PhysicalPosition::new(position.x, position.y))
}
fn set_outer_position(&self, position: Position) {
@@ -494,10 +499,6 @@ 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.
@@ -1053,10 +1054,12 @@ 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
}
@@ -1071,7 +1074,7 @@ pub(super) struct InitData<'a> {
pub window: Option<Window>,
}
impl InitData<'_> {
impl<'a> InitData<'a> {
unsafe fn create_window(&self, window: HWND) -> Window {
// Register for touch events if applicable
{
@@ -1285,6 +1288,7 @@ 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);
@@ -1297,6 +1301,9 @@ 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::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::RequestError;
pub use crate::icon::{BadIcon, Icon};
use crate::monitor::{MonitorHandle, VideoModeHandle};
@@ -67,6 +67,7 @@ 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.
@@ -97,6 +98,7 @@ 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(),
@@ -111,13 +113,17 @@ 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)
}
@@ -403,6 +409,7 @@ 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,
@@ -574,51 +581,41 @@ pub trait Window: AsAny + Send + Sync {
// extension trait
fn reset_dead_keys(&self);
/// The position of the top-left hand corner of the surface relative to the top-left hand corner
/// of the window.
/// 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.
///
/// This, combined with [`outer_position`], can be useful for calculating the position of the
/// surface relative to the desktop.
///
/// 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).
///
/// This may be negative.
///
/// 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>;
/// 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 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, or on another monitor than the primary.
/// The same conditions that apply to [`Window::outer_position`] apply to this method.
///
/// ## Platform-specific
///
/// - **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`].
///
/// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError>;
/// Returns 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.
///
/// The coordinates can be negative if the top-left hand corner of the window is outside
/// of the visible screen region.
///
/// ## 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>;
/// Sets the position of the window on the desktop.
/// Modifies the position of the window.
///
/// See [`Window::outer_position`] for more information about the coordinates.
/// This automatically un-maximizes the window if it's maximized.
@@ -648,21 +645,16 @@ 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 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.
/// This is the dimensions you should pass to things like Wgpu or Glutin when configuring.
///
/// ## 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.
@@ -709,53 +701,11 @@ 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
@@ -1028,8 +978,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
/// 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.
/// 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.
@@ -1062,8 +1012,7 @@ pub trait Window: AsAny + Send + Sync {
///
/// ## Platform-specific
///
/// - **X11:** Area is not supported, only position. The bottom-right corner of the provided
/// area is reported as the position.
/// - **X11:** - area is not supported, only position.
/// - **iOS / Android / Web / Orbital:** Unsupported.
///
/// [chinese]: https://support.apple.com/guide/chinese-input-method/use-the-candidate-window-cim12992/104/mac/12.0
@@ -1086,8 +1035,8 @@ pub trait Window: AsAny + Send + Sync {
///
/// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are
/// combined.
/// - **iOS / Android:** This will show / hide the soft keyboard.
/// - **Web / Orbital:** Unsupported.
/// - **iOS:** This will show / hide the soft keyboard.
/// - **Android / Web / Orbital:** Unsupported.
/// - **X11**: Enabling IME will disable dead keys reporting during compose.
///
/// [`Ime`]: crate::event::WindowEvent::Ime
@@ -1260,7 +1209,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 in surface coordinates.
/// Show [window menu] at a specified position .
///
/// This is the context menu that is normally shown when interacting with
/// the title bar. This is useful when implementing custom decorations.
@@ -1327,9 +1276,11 @@ 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;
}
@@ -1355,12 +1306,14 @@ 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,6 +5,3 @@ 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"]