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
81 changed files with 2063 additions and 2476 deletions

View File

@@ -2,3 +2,4 @@
- [ ] Added an entry to the `changelog` module if knowledge of this change could be valuable to users - [ ] Added an entry to the `changelog` module if knowledge of this change could be valuable to users
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior - [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
- [ ] Created or updated an example program if it would help users understand this functionality - [ ] Created or updated an example program if it would help users understand this functionality
- [ ] Updated [feature matrix](https://github.com/rust-windowing/winit/blob/master/FEATURES.md), if new features were added or implemented

View File

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

View File

@@ -47,3 +47,201 @@ through the implementation work necessary to function on all platforms. When one
gets implemented across all platforms, a PR can be opened to upgrade the feature to a core feature. gets implemented across all platforms, a PR can be opened to upgrade the feature to a core feature.
If that gets accepted, the platform-specific functions get deprecated and become permanently If that gets accepted, the platform-specific functions get deprecated and become permanently
exposed through the core, cross-platform API. exposed through the core, cross-platform API.
# Features
## Extending this section
If your PR makes notable changes to Winit's features, please update this section as follows:
- If your PR adds a new feature, add a brief description to the relevant section. If the feature is a core
feature, add a row to the feature matrix and describe what platforms the feature has been implemented on.
- If your PR begins a new API rework, add a row to the `Pending API Reworks` table. If the PR implements the
API rework on all relevant platforms, please move it to the `Completed API Reworks` table.
- If your PR implements an already-existing feature on a new platform, either mark the feature as *completed*,
or mark it as *mostly completed* and link to an issue describing the problems with the implementation.
## Core
### Windowing
- **Window initialization**: Winit allows the creation of a window
- **Providing pointer to init OpenGL**: Winit provides the necessary pointers to initialize a working opengl context
- **Providing pointer to init Vulkan**: Same as OpenGL but for Vulkan
- **Window decorations**: The windows created by winit are properly decorated, and the decorations can
be deactivated
- **Window decorations toggle**: Decorations can be turned on or off after window creation
- **Window resizing**: The windows created by winit can be resized and generate the appropriate events
when they are. The application can precisely control its window size if desired.
- **Window resize increments**: When the window gets resized, the application can choose to snap the window's
size to specific values.
- **Window transparency**: Winit allows the creation of windows with a transparent background.
- **Window maximization**: The windows created by winit can be maximized upon creation.
- **Window maximization toggle**: The windows created by winit can be maximized and unmaximized after
creation.
- **Window minimization**: The windows created by winit can be minimized after creation.
- **Fullscreen**: The windows created by winit can be put into fullscreen mode.
- **Fullscreen toggle**: The windows created by winit can be switched to and from fullscreen after
creation.
- **Exclusive fullscreen**: Winit allows changing the video mode of the monitor
for fullscreen windows and, if applicable, captures the monitor for exclusive
use by this application.
- **HiDPI support**: Winit assists developers in appropriately scaling HiDPI content.
- **Popup / modal windows**: Windows can be created relative to the client area of other windows, and parent
windows can be disabled in favor of popup windows. This feature also guarantees that popup windows
get drawn above their owner.
### System Information
- **Monitor list**: Retrieve the list of monitors and their metadata, including which one is primary.
- **Video mode query**: Monitors can be queried for their supported fullscreen video modes (consisting of resolution, refresh rate, and bit depth).
### Input Handling
- **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events.
- **Mouse set location**: Forcibly changing the location of the pointer.
- **Cursor locking**: Locking the cursor inside the window so it cannot move.
- **Cursor confining**: Confining the cursor to the window bounds so it cannot leave them.
- **Cursor icon**: Changing the cursor icon or hiding the cursor.
- **Cursor image**: Changing the cursor to your own image.
- **Cursor hittest**: Handle or ignore mouse events for a window.
- **Touch events**: Single-touch events.
- **Touch pressure**: Touch events contain information about the amount of force being applied.
- **Multitouch**: Multi-touch events, including cancellation of a gesture.
- **Keyboard events**: Properly processing keyboard events using the user-specified keymap and
translating keypresses into UTF-8 characters, handling dead keys and IMEs.
- **Drag & Drop**: Dragging content into winit, detecting when content enters, drops, or if the drop is cancelled.
- **Raw Device Events**: Capturing input from input devices without any OS filtering.
- **Gamepad/Joystick events**: Capturing input from gamepads and joysticks.
- **Device movement events**: Capturing input from the device gyroscope and accelerometer.
## Platform
### Windows
* Setting the name of the internal window class
* Setting the taskbar icon
* Setting the parent window
* Setting a menu bar
* `WS_EX_NOREDIRECTIONBITMAP` support
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme
* Changing a system-drawn backdrop
* Setting the window border color
* Setting the title bar background color
* Setting the title color
* Setting the corner rounding preference
### macOS
* Window activation policy
* Window movable by background
* Transparent titlebar
* Hidden titlebar
* Hidden titlebar buttons
* Full-size content view
* Accepts first mouse
* Set a preferred theme and get current theme.
### Unix
* Window urgency
* X11 Window Class
* X11 Override Redirect Flag
* GTK Theme Variant
* Base window size
* Setting the X11 parent window
### iOS
* Get the `UIScreen` object pointer
* Setting the `UIView` hidpi factor
* Valid orientations
* Home indicator visibility
* Status bar visibility and style
* Deferring system gestures
* Getting the preferred video mode
### Web
* Get if the systems preferred color scheme is "dark"
## Compatibility Matrix
Legend:
- ✔️: Works as intended
- ▢: Mostly works, but some bugs are known
- ❌: Missing feature or large bugs making it unusable
- **N/A**: Not applicable for this platform
- ❓: Unknown status
### Windowing
|Feature |Windows|MacOS |Linux x11 |Linux Wayland |Android|iOS |Web |Redox OS|
|-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |✔️ |✔️ |
|Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |
|Providing pointer to init Vulkan |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A** |
|Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ |
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|Window resize increments |✔️ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ |
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |**N/A** |
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |**N/A** |
|Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**|**N/A** |
|HiDPI support |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|**N/A** |
### System information
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- | ------ |
|Monitor list |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|Video mode query |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
### Input handling
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|Mouse set location |✔️ |✔️ |✔️ |✔️(when locked) |**N/A**|**N/A**|**N/A**|**N/A** |
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |❌ |
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|Cursor image |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|Cursor hittest |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A** |
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |
|Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |**N/A** |
|Keyboard events |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |▢[#720] |**N/A**|**N/A**|❓ |**N/A** |
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ |**N/A** |
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |**N/A** |
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |**N/A** |
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
|Resize with cursor |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
### Pending API Reworks
Changes in the API that have been agreed upon but aren't implemented across all platforms.
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
|Event Loop 2.0 ([#459]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |✔️ |
|Keyboard Input 2.0 ([#753]) |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
### Completed API Reworks
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
[#165]: https://github.com/rust-windowing/winit/issues/165
[#219]: https://github.com/rust-windowing/winit/issues/219
[#242]: https://github.com/rust-windowing/winit/issues/242
[#306]: https://github.com/rust-windowing/winit/issues/306
[#315]: https://github.com/rust-windowing/winit/issues/315
[#319]: https://github.com/rust-windowing/winit/issues/319
[#33]: https://github.com/rust-windowing/winit/issues/33
[#459]: https://github.com/rust-windowing/winit/issues/459
[#5]: https://github.com/rust-windowing/winit/issues/5
[#63]: https://github.com/rust-windowing/winit/issues/63
[#720]: https://github.com/rust-windowing/winit/issues/720
[#721]: https://github.com/rust-windowing/winit/issues/721
[#750]: https://github.com/rust-windowing/winit/issues/750
[#753]: https://github.com/rust-windowing/winit/issues/753
[#804]: https://github.com/rust-windowing/winit/issues/804

View File

@@ -2,7 +2,7 @@
[![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit) [![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit)
[![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit) [![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit)
[![UNSTABLE docs](https://img.shields.io/github/actions/workflow/status/rust-windowing/winit/docs.yml?branch=master&label=UNSTABLE%20docs [![Master Docs](https://img.shields.io/github/actions/workflow/status/rust-windowing/winit/docs.yml?branch=master&label=master%20docs
)](https://rust-windowing.github.io/winit/winit/index.html) )](https://rust-windowing.github.io/winit/winit/index.html)
[![CI Status](https://github.com/rust-windowing/winit/workflows/CI/badge.svg)](https://github.com/rust-windowing/winit/actions) [![CI Status](https://github.com/rust-windowing/winit/workflows/CI/badge.svg)](https://github.com/rust-windowing/winit/actions)
@@ -66,4 +66,4 @@ same MSRV policy.
### Platform-specific usage ### Platform-specific usage
Check out the [`winit::platform`](https://docs.rs/winit/latest/winit/platform/index.html) module for platform-specific usage. Check out the [`winit::platform`](https://rust-windowing.github.io/winit/winit/platform/index.html) module for platform-specific usage.

View File

@@ -26,12 +26,12 @@ targets = [
[licenses] [licenses]
allow = [ allow = [
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0) "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-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised) "BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
"ISC", # https://tldrlegal.com/license/isc-license "ISC", # https://tldrlegal.com/license/-isc-license
"MIT", # https://tldrlegal.com/license/mit-license "MIT", # https://tldrlegal.com/license/mit-license
"Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html "Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
] ]
confidence-threshold = 1.0 confidence-threshold = 1.0
private = { ignore = true } 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) 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), 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. 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 ## Unreleased
- Added `Insets`, `LogicalInsets` and `PhysicalInsets` types. - Add `Rect`, `PhysicalRect` and `LogicalRect`.
## 0.1.1 ## 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)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalInsets<P> { pub struct LogicalRect<P> {
/// The distance to the top edge. pub origin: LogicalPosition<P>,
pub top: P, pub size: LogicalSize<P>,
/// The distance to the left edge.
pub left: P,
/// The distance to the bottom edge.
pub bottom: P,
/// The distance to the right edge.
pub right: P,
} }
impl<P> LogicalInsets<P> { impl<P> LogicalRect<P> {
#[inline] #[inline]
pub const fn new(top: P, left: P, bottom: P, right: P) -> Self { pub const fn new(origin: LogicalPosition<P>, size: LogicalSize<P>) -> Self {
Self { top, left, bottom, right } Self { origin, size }
} }
} }
impl<P: Pixel> LogicalInsets<P> { impl<P: Pixel> LogicalRect<P> {
#[inline] #[inline]
pub fn from_physical<T: Into<PhysicalInsets<X>>, X: Pixel>( pub fn from_physical<T: Into<PhysicalRect<X>>, X: Pixel>(
physical: T, physical: T,
scale_factor: f64, scale_factor: f64,
) -> Self { ) -> Self {
@@ -790,116 +784,92 @@ impl<P: Pixel> LogicalInsets<P> {
} }
#[inline] #[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalInsets<X> { pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalRect<X> {
assert!(validate_scale_factor(scale_factor)); let origin = self.origin.to_physical(scale_factor);
let top = self.top.into() * scale_factor; let size = self.size.to_physical(scale_factor);
let left = self.left.into() * scale_factor; PhysicalRect::new(origin, size)
let bottom = self.bottom.into() * scale_factor;
let right = self.right.into() * scale_factor;
PhysicalInsets::new(top, left, bottom, right).cast()
} }
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> LogicalInsets<X> { pub fn cast<X: Pixel>(&self) -> LogicalRect<X> {
LogicalInsets { LogicalRect { origin: self.origin.cast(), size: self.size.cast() }
top: self.top.cast(),
left: self.left.cast(),
bottom: self.bottom.cast(),
right: self.right.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)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalInsets<P> { pub struct PhysicalRect<P> {
/// The distance to the top edge. pub origin: PhysicalPosition<P>,
pub top: P, pub size: PhysicalSize<P>,
/// The distance to the left edge.
pub left: P,
/// The distance to the bottom edge.
pub bottom: P,
/// The distance to the right edge.
pub right: P,
} }
impl<P> PhysicalInsets<P> { impl<P> PhysicalRect<P> {
#[inline] #[inline]
pub const fn new(top: P, left: P, bottom: P, right: P) -> Self { pub const fn new(origin: PhysicalPosition<P>, size: PhysicalSize<P>) -> Self {
Self { top, left, bottom, right } Self { origin, size }
} }
} }
impl<P: Pixel> PhysicalInsets<P> { impl<P: Pixel> PhysicalRect<P> {
#[inline] #[inline]
pub fn from_logical<T: Into<LogicalInsets<X>>, X: Pixel>( pub fn from_logical<T: Into<LogicalRect<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
logical: T,
scale_factor: f64,
) -> Self {
logical.into().to_physical(scale_factor) logical.into().to_physical(scale_factor)
} }
#[inline] #[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)); assert!(validate_scale_factor(scale_factor));
let top = self.top.into() / scale_factor; let origin = self.origin.to_logical(scale_factor);
let left = self.left.into() / scale_factor; let size = self.size.to_logical(scale_factor);
let bottom = self.bottom.into() / scale_factor; LogicalRect::new(origin, size)
let right = self.right.into() / scale_factor;
LogicalInsets::new(top, left, bottom, right).cast()
} }
#[inline] #[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalInsets<X> { pub fn cast<X: Pixel>(&self) -> PhysicalRect<X> {
PhysicalInsets { PhysicalRect { origin: self.origin.cast(), size: self.size.cast() }
top: self.top.cast(),
left: self.left.cast(),
bottom: self.bottom.cast(),
right: self.right.cast(),
}
} }
} }
/// Insets that are either physical or logical. /// A rectangle that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Insets { pub enum Rect {
Physical(PhysicalInsets<u32>), Physical(PhysicalRect<i32>),
Logical(LogicalInsets<f64>), Logical(LogicalRect<f64>),
} }
impl Insets { impl Rect {
pub fn new<S: Into<Self>>(insets: S) -> Self { pub fn new<R: Into<Self>>(rect: R) -> Self {
insets.into() 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 { match *self {
Self::Physical(insets) => insets.to_logical(scale_factor), Self::Physical(rect) => rect.to_logical(scale_factor),
Self::Logical(insets) => insets.cast(), 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 { match *self {
Self::Physical(insets) => insets.cast(), Self::Physical(rect) => rect.cast(),
Self::Logical(insets) => insets.to_physical(scale_factor), 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] #[inline]
fn from(insets: PhysicalInsets<P>) -> Self { fn from(rect: PhysicalRect<P>) -> Self {
Self::Physical(insets.cast()) Self::Physical(rect.cast())
} }
} }
impl<P: Pixel> From<LogicalInsets<P>> for Insets { impl<P: Pixel> From<LogicalRect<P>> for Rect {
#[inline] #[inline]
fn from(insets: LogicalInsets<P>) -> Self { fn from(rect: LogicalRect<P>) -> Self {
Self::Logical(insets.cast()) Self::Logical(rect.cast())
} }
} }

View File

@@ -11,7 +11,7 @@
pub use platform::cleanup_window; pub use platform::cleanup_window;
pub use platform::fill_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 { mod platform {
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; 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 { mod platform {
pub fn fill_window(_window: &dyn winit::window::Window) { pub fn fill_window(_window: &dyn winit::window::Window) {
// No-op on mobile platforms. // No-op on mobile platforms.

View File

@@ -31,8 +31,6 @@ use winit::platform::startup_notify::{
}; };
#[cfg(web_platform)] #[cfg(web_platform)]
use winit::platform::web::{ActiveEventLoopExtWeb, CustomCursorExtWeb, WindowAttributesExtWeb}; use winit::platform::web::{ActiveEventLoopExtWeb, CustomCursorExtWeb, WindowAttributesExtWeb};
#[cfg(x11_platform)]
use winit::platform::x11::WindowAttributesExtX11;
use winit::window::{ use winit::window::{
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection, Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection,
Theme, Window, WindowAttributes, WindowId, Theme, Window, WindowAttributes, WindowId,
@@ -151,28 +149,6 @@ impl Application {
window_attributes = window_attributes.with_activation_token(token); 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)] #[cfg(macos_platform)]
if let Some(tab_id) = _tab_id { if let Some(tab_id) = _tab_id {
window_attributes = window_attributes.with_tabbing_identifier(&tab_id); window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
@@ -242,10 +218,6 @@ impl Application {
Action::ToggleResizable => window.toggle_resizable(), Action::ToggleResizable => window.toggle_resizable(),
Action::ToggleDecorations => window.toggle_decorations(), Action::ToggleDecorations => window.toggle_decorations(),
Action::ToggleFullscreen => window.toggle_fullscreen(), 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::ToggleMaximize => window.toggle_maximize(),
Action::ToggleImeInput => window.toggle_ime(), Action::ToggleImeInput => window.toggle_ime(),
Action::Minimize => window.minimize(), Action::Minimize => window.minimize(),
@@ -945,38 +917,18 @@ impl WindowState {
return Ok(()); return Ok(());
} }
const WHITE: u32 = 0xffffffff;
const DARK_GRAY: u32 = 0xff181818;
let color = match self.theme {
Theme::Light => WHITE,
Theme::Dark => DARK_GRAY,
};
let mut buffer = self.surface.buffer_mut()?; let mut buffer = self.surface.buffer_mut()?;
buffer.fill(color);
// 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
};
}
}
}
// Present the buffer
self.window.pre_present_notify(); self.window.pre_present_notify();
buffer.present()?; buffer.present()?;
Ok(()) Ok(())
} }
@@ -1013,8 +965,6 @@ enum Action {
ToggleDecorations, ToggleDecorations,
ToggleResizable, ToggleResizable,
ToggleFullscreen, ToggleFullscreen,
#[cfg(macos_platform)]
ToggleSimpleFullscreen,
ToggleMaximize, ToggleMaximize,
Minimize, Minimize,
NextCursor, NextCursor,
@@ -1048,8 +998,6 @@ impl Action {
Action::ToggleDecorations => "Toggle decorations", Action::ToggleDecorations => "Toggle decorations",
Action::ToggleResizable => "Toggle window resizable state", Action::ToggleResizable => "Toggle window resizable state",
Action::ToggleFullscreen => "Toggle fullscreen", Action::ToggleFullscreen => "Toggle fullscreen",
#[cfg(macos_platform)]
Action::ToggleSimpleFullscreen => "Toggle simple fullscreen",
Action::ToggleMaximize => "Maximize", Action::ToggleMaximize => "Maximize",
Action::Minimize => "Minimize", Action::Minimize => "Minimize",
Action::ToggleResizeIncrements => "Use resize increments when resizing window", Action::ToggleResizeIncrements => "Use resize increments when resizing window",
@@ -1192,8 +1140,6 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
Binding::new("Q", ModifiersState::CONTROL, Action::CloseWindow), Binding::new("Q", ModifiersState::CONTROL, Action::CloseWindow),
Binding::new("H", ModifiersState::CONTROL, Action::PrintHelp), Binding::new("H", ModifiersState::CONTROL, Action::PrintHelp),
Binding::new("F", ModifiersState::CONTROL, Action::ToggleFullscreen), 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("D", ModifiersState::CONTROL, Action::ToggleDecorations),
Binding::new("I", ModifiersState::CONTROL, Action::ToggleImeInput), Binding::new("I", ModifiersState::CONTROL, Action::ToggleImeInput),
Binding::new("L", ModifiersState::CONTROL, Action::CycleCursorGrab), Binding::new("L", ModifiersState::CONTROL, Action::CycleCursorGrab),

View File

@@ -59,23 +59,15 @@ changelog entry.
- Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd` - Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd`
and `Serialize` on many types. and `Serialize` on many types.
- Add `MonitorHandle::current_video_mode()`. - 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 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 `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. - 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` - 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. 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 `WindowId::into_raw()` and `from_raw()`.
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId`, `primary` and `position` to all - Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId` and `position` to all pointer
pointer events as part of the pointer event overhaul. events as part of the pointer event overhaul.
- Add `DeviceId::into_raw()` and `from_raw()`. - 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, Wayland, Windows and macOS, improved scancode conversions for more obscure key codes.
### Changed ### Changed
@@ -144,8 +136,6 @@ changelog entry.
- Rename `CursorEntered` to `PointerEntered`. - Rename `CursorEntered` to `PointerEntered`.
- Rename `CursorLeft` to `PointerLeft`. - Rename `CursorLeft` to `PointerLeft`.
- Rename `MouseInput` to `PointerButton`. - 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`. - Add `position` to every `PointerEvent`.
- `PointerMoved` is **not sent** after `PointerEntered` anymore. - `PointerMoved` is **not sent** after `PointerEntered` anymore.
- Remove `Touch`, which is folded into the `Pointer*` events. - Remove `Touch`, which is folded into the `Pointer*` events.
@@ -158,13 +148,10 @@ changelog entry.
type to a generic mouse button. type to a generic mouse button.
- New `FingerId` added to `PointerKind::Touch` and `PointerSource::Touch` able to uniquely - 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`. 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`. - In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`.
- Remove `Force::Calibrated::altitude_angle`. - 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.
- On iOS, emit more events immediately, instead of queuing them.
- Update `smol_str` to version `0.3`
### Removed ### Removed
@@ -196,7 +183,6 @@ changelog entry.
- Remove `WindowEvent::Touch` and `Touch` in favor of the new `PointerKind`, `PointerSource` and - Remove `WindowEvent::Touch` and `Touch` in favor of the new `PointerKind`, `PointerSource` and
`ButtonSource` as part of the new pointer event overhaul. `ButtonSource` as part of the new pointer event overhaul.
- Remove `Force::altitude_angle`. - Remove `Force::altitude_angle`.
- Removed `Window::inner_position`, use the new `Window::surface_position` instead.
### Fixed ### Fixed
@@ -206,13 +192,3 @@ changelog entry.
default activation policy, unless explicitly provided during initialization. default activation policy, unless explicitly provided during initialization.
- On macOS, fix crash when calling `drag_window()` without a left click present. - 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 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.
- On macOS, fixed the scancode conversion for audio volume keys.
- On macOS, fixed the scancode conversion for `IntlBackslash`.
- On macOS, fixed redundant `SurfaceResized` event at window creation.
- On macOS, fix crash when pressing Caps Lock in certain configurations.
- On iOS, fixed `MonitorHandle`'s `PartialEq` and `Hash` implementations.

View File

@@ -153,16 +153,10 @@ pub enum WindowEvent {
/// Contains the new dimensions of the surface (can also be retrieved with /// Contains the new dimensions of the surface (can also be retrieved with
/// [`Window::surface_size`]). /// [`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 /// [`Window::surface_size`]: crate::window::Window::surface_size
SurfaceResized(PhysicalSize<u32>), SurfaceResized(PhysicalSize<u32>),
/// The position of the window has changed. /// The position of the window has changed. Contains the window's new position.
///
/// Contains the window's new position in desktop coordinates (can also be retrieved with
/// [`Window::outer_position`]).
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
@@ -175,23 +169,18 @@ pub enum WindowEvent {
/// The window has been destroyed. /// The window has been destroyed.
Destroyed, Destroyed,
/// A file has been dropped into the window.
///
/// When the user drops multiple files at once, this event will be emitted for each file
/// separately.
DroppedFile(PathBuf),
/// A file is being hovered over the window. /// A file is being hovered over the window.
/// ///
/// When the user hovers multiple files at once, this event will be emitted for each file /// When the user hovers multiple files at once, this event will be emitted for each file
/// separately. /// separately.
HoveredFile(PathBuf), HoveredFile(PathBuf),
/// A file has been dropped into the window.
///
/// When the user drops multiple files at once, this event will be emitted for each file
/// separately.
///
/// The support for this is known to be incomplete, see [#720] for more
/// information.
///
/// [#720]: https://github.com/rust-windowing/winit/issues/720
DroppedFile(PathBuf),
/// A file was hovered, but has exited the window. /// A file was hovered, but has exited the window.
/// ///
/// There will be a single `HoveredFileCancelled` event triggered even if multiple files were /// There will be a single `HoveredFileCancelled` event triggered even if multiple files were
@@ -201,9 +190,6 @@ pub enum WindowEvent {
/// The window gained or lost focus. /// The window gained or lost focus.
/// ///
/// The parameter is true if the window has gained focus, and false if it has 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), Focused(bool),
/// An event from the keyboard has been received. /// An event from the keyboard has been received.
@@ -212,7 +198,6 @@ pub enum WindowEvent {
/// - **Windows:** The shift key overrides NumLock. In other words, while shift is held down, /// - **Windows:** The shift key overrides NumLock. In other words, while shift is held down,
/// numpad keys act as if NumLock wasn't active. When this is used, the OS sends fake key /// numpad keys act as if NumLock wasn't active. When this is used, the OS sends fake key
/// events which are not marked as `is_synthetic`. /// events which are not marked as `is_synthetic`.
/// - **iOS:** Unsupported.
KeyboardInput { KeyboardInput {
device_id: Option<DeviceId>, device_id: Option<DeviceId>,
event: KeyEvent, event: KeyEvent,
@@ -260,12 +245,6 @@ pub enum WindowEvent {
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: PhysicalPosition<f64>, 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, source: PointerSource,
}, },
@@ -285,12 +264,6 @@ pub enum WindowEvent {
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: PhysicalPosition<f64>, 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, kind: PointerKind,
}, },
@@ -311,12 +284,6 @@ pub enum WindowEvent {
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: Option<PhysicalPosition<f64>>, 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, kind: PointerKind,
}, },
@@ -340,12 +307,6 @@ pub enum WindowEvent {
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: PhysicalPosition<f64>, 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, button: ButtonSource,
}, },
@@ -416,18 +377,10 @@ pub enum WindowEvent {
/// Touchpad pressure event. /// Touchpad pressure event.
/// ///
/// ## Platform-specific /// At the moment, only supported on Apple forcetouch-capable macbooks.
/// /// The parameters are: pressure level (value between 0 and 1 representing how hard the
/// - **macOS**: Only supported on Apple forcetouch-capable macbooks. /// touchpad is being pressed) and stage (integer representing the click level).
/// - **Android / iOS / Wayland / X11 / Windows / Orbital / Web:** Unsupported. TouchpadPressure { device_id: Option<DeviceId>, pressure: f32, stage: i64 },
TouchpadPressure {
device_id: Option<DeviceId>,
/// Value between 0 and 1 representing how hard the touchpad is being
/// pressed.
pressure: f32,
/// Represents the click level.
stage: i64,
},
/// The window's scale factor has changed. /// The window's scale factor has changed.
/// ///
@@ -440,12 +393,7 @@ pub enum WindowEvent {
/// To update the window size, use the provided [`SurfaceSizeWriter`] handle. By default, the /// 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. /// 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. /// For more information about DPI in general, see the [`dpi`] crate.
///
/// [`Window::scale_factor`]: crate::window::Window::scale_factor
ScaleFactorChanged { ScaleFactorChanged {
scale_factor: f64, scale_factor: f64,
/// Handle to update surface size during scale changes. /// Handle to update surface size during scale changes.
@@ -497,15 +445,13 @@ pub enum WindowEvent {
/// Emitted when a window should be redrawn. /// 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 /// - 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`]. /// - The application has explicitly requested a redraw via [`Window::request_redraw`].
/// ///
/// Winit will aggregate duplicate redraw requests into a single event, to /// Winit will aggregate duplicate redraw requests into a single event, to
/// help avoid duplicating rendering work. /// help avoid duplicating rendering work.
///
/// [the safe area]: crate::window::Window::safe_area
RedrawRequested, RedrawRequested,
} }
@@ -664,23 +610,12 @@ impl DeviceId {
/// Whenever a touch event is received it contains a `FingerId` which uniquely identifies the finger /// Whenever a touch event is received it contains a `FingerId` which uniquely identifies the finger
/// used for the current interaction. /// used for the current interaction.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[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 { impl FingerId {
/// Convert the [`FingerId`] into the underlying integer. #[cfg(test)]
/// pub(crate) const fn dummy() -> Self {
/// This is useful if you need to pass the ID across an FFI boundary, or store it in an atomic. FingerId(platform_impl::FingerId::dummy())
#[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)
} }
} }
@@ -1195,7 +1130,7 @@ mod tests {
($closure:expr) => {{ ($closure:expr) => {{
#[allow(unused_mut)] #[allow(unused_mut)]
let mut x = $closure; let mut x = $closure;
let fid = event::FingerId::from_raw(0); let fid = event::FingerId::dummy();
#[allow(deprecated)] #[allow(deprecated)]
{ {
@@ -1227,19 +1162,16 @@ mod tests {
with_window_event(Ime(Enabled)); with_window_event(Ime(Enabled));
with_window_event(PointerMoved { with_window_event(PointerMoved {
device_id: None, device_id: None,
primary: true,
position: (0, 0).into(), position: (0, 0).into(),
source: PointerSource::Mouse, source: PointerSource::Mouse,
}); });
with_window_event(ModifiersChanged(event::Modifiers::default())); with_window_event(ModifiersChanged(event::Modifiers::default()));
with_window_event(PointerEntered { with_window_event(PointerEntered {
device_id: None, device_id: None,
primary: true,
position: (0, 0).into(), position: (0, 0).into(),
kind: PointerKind::Mouse, kind: PointerKind::Mouse,
}); });
with_window_event(PointerLeft { with_window_event(PointerLeft {
primary: true,
device_id: None, device_id: None,
position: Some((0, 0).into()), position: Some((0, 0).into()),
kind: PointerKind::Mouse, kind: PointerKind::Mouse,
@@ -1251,14 +1183,12 @@ mod tests {
}); });
with_window_event(PointerButton { with_window_event(PointerButton {
device_id: None, device_id: None,
primary: true,
state: event::ElementState::Pressed, state: event::ElementState::Pressed,
position: (0, 0).into(), position: (0, 0).into(),
button: event::MouseButton::Other(0).into(), button: event::MouseButton::Other(0).into(),
}); });
with_window_event(PointerButton { with_window_event(PointerButton {
device_id: None, device_id: None,
primary: true,
state: event::ElementState::Released, state: event::ElementState::Released,
position: (0, 0).into(), position: (0, 0).into(),
button: event::ButtonSource::Touch { button: event::ButtonSource::Touch {
@@ -1328,11 +1258,11 @@ mod tests {
#[test] #[test]
fn ensure_attrs_do_not_panic() { fn ensure_attrs_do_not_panic() {
foreach_event!(|event: event::Event| { foreach_event!(|event: event::Event| {
let _ = format!("{event:?}"); let _ = format!("{:?}", event);
}); });
let _ = event::StartCause::Init.clone(); 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); HashSet::new().insert(fid);
let mut set = [fid, fid, fid]; let mut set = [fid, fid, fid];
set.sort_unstable(); set.sort_unstable();

View File

@@ -13,11 +13,9 @@ use std::marker::PhantomData;
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time::{Duration, Instant}; use web_time::{Duration, Instant};
@@ -275,9 +273,10 @@ impl EventLoop {
} }
} }
impl HasDisplayHandle for EventLoop { #[cfg(feature = "rwh_06")]
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> { impl rwh_06::HasDisplayHandle for EventLoop {
HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle()) fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
} }
} }
@@ -408,11 +407,13 @@ pub trait ActiveEventLoop: AsAny {
fn owned_display_handle(&self) -> OwnedDisplayHandle; fn owned_display_handle(&self) -> OwnedDisplayHandle;
/// Get the raw-window-handle handle. /// 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 + '_ { #[cfg(feature = "rwh_06")]
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> { impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
self.rwh_06_handle().display_handle() self.rwh_06_handle().display_handle()
} }
} }
@@ -429,55 +430,36 @@ impl HasDisplayHandle for dyn ActiveEventLoop + '_ {
/// ///
/// - A zero-sized type that is likely optimized out. /// - A zero-sized type that is likely optimized out.
/// - A reference-counted pointer to the underlying type. /// - A reference-counted pointer to the underlying type.
#[derive(Clone)] #[derive(Clone, PartialEq, Eq)]
pub struct OwnedDisplayHandle { pub struct OwnedDisplayHandle {
pub(crate) handle: Arc<dyn HasDisplayHandle>, #[cfg_attr(not(feature = "rwh_06"), allow(dead_code))]
} pub(crate) platform: platform_impl::OwnedDisplayHandle,
impl OwnedDisplayHandle {
pub(crate) fn new(handle: Arc<dyn HasDisplayHandle>) -> Self {
Self { handle }
}
}
impl HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.handle.display_handle()
}
} }
impl fmt::Debug for OwnedDisplayHandle { impl fmt::Debug for OwnedDisplayHandle {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive() f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
} }
} }
impl PartialEq for OwnedDisplayHandle { #[cfg(feature = "rwh_06")]
fn eq(&self, other: &Self) -> bool { impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
match (self.display_handle(), other.display_handle()) { #[inline]
(Ok(lhs), Ok(rhs)) => lhs == rhs, fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
_ => false, 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. /// Control the [`EventLoop`], possibly from a different thread, without referencing it directly.
#[derive(Clone)] #[derive(Clone)]
pub struct EventLoopProxy { pub struct EventLoopProxy {
pub(crate) proxy: Arc<dyn EventLoopProxyProvider>, pub(crate) event_loop_proxy: platform_impl::EventLoopProxy,
}
impl fmt::Debug for EventLoopProxy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopProxy").finish_non_exhaustive()
}
} }
impl EventLoopProxy { impl EventLoopProxy {
@@ -497,11 +479,13 @@ impl EventLoopProxy {
/// ///
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687 /// [#3687]: https://github.com/rust-windowing/winit/pull/3687
pub fn wake_up(&self) { pub fn wake_up(&self) {
self.proxy.wake_up(); self.event_loop_proxy.wake_up();
} }
}
pub(crate) fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self { impl fmt::Debug for EventLoopProxy {
Self { proxy } fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ActiveEventLoop").finish_non_exhaustive()
} }
} }

View File

@@ -1,59 +1,5 @@
//! Winit is a cross-platform window creation and event loop management library. //! Winit is a cross-platform window creation and event loop management library.
//! //!
//! # Usage
//!
//! `winit` can be added to `Cargo.toml` as a dependency. It can be added via `cargo add`.
//!
//! ```bash
//! $ cargo add winit
//! ```
//!
//! To only enable the X11 backend on Free Unix[^unix] systems, disable default features
//! and enable the `x11` feature.
//!
//! ```bash
//! $ cargo add winit --no-default-features --features x11
//! ```
//!
//! To only enable the Wayland backend on Free Unix systems, disable default features
//! and enable the `wayland` feature.
//!
//! ```bash
//! $ cargo add winit --no-default-features --features wayland
//! ```
//!
//! These features have no effect on systems that are not Free Unix.
//!
//! ## Dependencies
//!
//! Dependencies on non-system libraries is managed through Cargo. For the X11
//! backend, the following Ubuntu packages or their equivalents must[^must] be installed.
//!
//! - `libx11-dev`
//! - `libxcb1-dev`
//! - `libxi-dev`
//! - `libxcbcommon-dev`
//! - `libxcbcommon-x11-dev`
//!
//! For the Wayland backend, the following Ubuntu packages or their equivalents
//! must be installed.
//!
//! - `libwayland-dev`
//! - `libxcbcommon-dev`
//! - `libfontconfig` (only with `sctk-adwaita` feature)
//! - `freetype` (only with `sctk-adwaita` feature)
//!
//! The "dev" packages are only needed for building binaries that use `winit`. On
//! deployed system the non-`dev` equivalents need to be installed.
//!
//! The other backends (Windows, macOS, etc) do not have any dependencies on system libraries
//! that don't already come with the operating system. However, note that the Windows backend
//! only supports Windows 10 and above, and the macOS backend only supports macOS
//! 10.14 and above.
//!
//! [^unix]: Unix systems outside of Android and Apple, like Linux or FreeBSD.
//! [^must]: This is not a "must" when the "dlopen" features are enabled
//!
//! # Building windows //! # Building windows
//! //!
//! Before you can create a [`Window`], you first need to build an [`EventLoop`]. This is done with //! Before you can create a [`Window`], you first need to build an [`EventLoop`]. This is done with
@@ -61,12 +7,7 @@
//! //!
//! ```no_run //! ```no_run
//! use winit::event_loop::EventLoop; //! use winit::event_loop::EventLoop;
//! //! let event_loop = EventLoop::new().unwrap();
//! # // Intentionally use `fn main` for clarity
//! fn main() {
//! let event_loop = EventLoop::new().unwrap();
//! // ...
//! }
//! ``` //! ```
//! //!
//! Then you create a [`Window`] with [`create_window`]. //! Then you create a [`Window`] with [`create_window`].
@@ -142,22 +83,19 @@
//! } //! }
//! } //! }
//! //!
//! # // Intentionally use `fn main` for clarity //! let event_loop = EventLoop::new().unwrap();
//! fn main() {
//! let event_loop = EventLoop::new().unwrap();
//! //!
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't //! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications. //! // dispatched any events. This is ideal for games and similar applications.
//! event_loop.set_control_flow(ControlFlow::Poll); //! event_loop.set_control_flow(ControlFlow::Poll);
//! //!
//! // ControlFlow::Wait pauses the event loop if no events are available to process. //! // 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 //! // 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. //! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! event_loop.set_control_flow(ControlFlow::Wait); //! event_loop.set_control_flow(ControlFlow::Wait);
//! //!
//! let mut app = App::default(); //! let mut app = App::default();
//! event_loop.run_app(&mut app); //! event_loop.run_app(&mut app);
//! }
//! ``` //! ```
//! //!
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be //! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
@@ -177,45 +115,6 @@
//! [`visible` set to `false`][crate::window::WindowAttributes::with_visible] and explicitly make //! [`visible` set to `false`][crate::window::WindowAttributes::with_visible] and explicitly make
//! the window visible only once you're ready to render into it. //! 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
//! //!
//! UI scaling is important, go read the docs for the [`dpi`] crate for an //! UI scaling is important, go read the docs for the [`dpi`] crate for an
@@ -331,11 +230,11 @@
// doc // doc
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))] #![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#![warn(clippy::uninlined_format_args)]
// Re-export DPI types so that users don't have to put it in Cargo.toml. // Re-export DPI types so that users don't have to put it in Cargo.toml.
#[doc(inline)] #[doc(inline)]
pub use dpi; pub use dpi;
#[cfg(feature = "rwh_06")]
pub use rwh_06 as raw_window_handle; pub use rwh_06 as raw_window_handle;
pub mod application; pub mod application;

View File

@@ -1,12 +1,18 @@
//! Types useful for interacting with a user's monitors. //! Types useful for interacting with a user's monitors.
//!
//! If you want to get basic information about a monitor, you can use the
//! [`MonitorHandle`] type. This is retrieved from one of the following
//! methods, which return an iterator of [`MonitorHandle`]:
//! - [`ActiveEventLoop::available_monitors`][crate::event_loop::ActiveEventLoop::available_monitors].
//! - [`Window::available_monitors`][crate::window::Window::available_monitors].
use std::num::{NonZeroU16, NonZeroU32}; use std::num::{NonZeroU16, NonZeroU32};
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::platform_impl; use crate::platform_impl;
/// A handle to a fullscreen video mode of a specific monitor. /// Describes a fullscreen video mode of a monitor.
/// ///
/// This can be acquired with [`MonitorHandle::video_modes`]. /// Can be acquired with [`MonitorHandle::video_modes`].
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub struct VideoModeHandle { pub struct VideoModeHandle {
pub(crate) video_mode: platform_impl::VideoModeHandle, pub(crate) video_mode: platform_impl::VideoModeHandle,
@@ -86,15 +92,7 @@ impl std::fmt::Display for VideoModeHandle {
/// Handle to a monitor. /// Handle to a monitor.
/// ///
/// Allows you to retrieve basic information and metadata about a monitor. /// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
///
/// Can be used in [`Window`] creation to place the window on a specific
/// monitor.
///
/// This can be retrieved from one of the following methods, which return an
/// iterator of [`MonitorHandle`]s:
/// - [`ActiveEventLoop::available_monitors`](crate::event_loop::ActiveEventLoop::available_monitors).
/// - [`Window::available_monitors`](crate::window::Window::available_monitors).
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
@@ -143,11 +141,8 @@ impl MonitorHandle {
self.inner.name() self.inner.name()
} }
/// Returns the top-left corner position of the monitor in desktop coordinates. /// Returns the top-left corner position of the monitor relative to the larger full
/// /// screen area.
/// This position is in the same coordinate system as [`Window::outer_position`].
///
/// [`Window::outer_position`]: crate::window::Window::outer_position
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///

View File

@@ -92,9 +92,6 @@ pub trait WindowExtMacOS {
/// This is how fullscreen used to work on macOS in versions before Lion. /// 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 /// And allows the user to have a fullscreen window without using another
/// space or taking control over the entire monitor. /// 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; fn set_simple_fullscreen(&self, fullscreen: bool) -> bool;
/// Returns whether or not the window has shadow. /// Returns whether or not the window has shadow.
@@ -160,13 +157,6 @@ pub trait WindowExtMacOS {
/// Getter for the [`WindowExtMacOS::set_borderless_game`]. /// Getter for the [`WindowExtMacOS::set_borderless_game`].
fn is_borderless_game(&self) -> bool; 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 + '_ { 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(); let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.is_borderless_game()) 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`. /// Corresponds to `NSApplicationActivationPolicy`.
@@ -330,8 +308,6 @@ pub trait WindowAttributesExtMacOS {
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self; fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
/// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set. /// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
fn with_borderless_game(self, borderless_game: bool) -> Self; 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 { impl WindowAttributesExtMacOS for WindowAttributes {
@@ -406,12 +382,6 @@ impl WindowAttributesExtMacOS for WindowAttributes {
self.platform_specific.borderless_game = borderless_game; self.platform_specific.borderless_game = borderless_game;
self self
} }
#[inline]
fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self {
self.platform_specific.unified_titlebar = unified_titlebar;
self
}
} }
pub trait EventLoopBuilderExtMacOS { pub trait EventLoopBuilderExtMacOS {

View File

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

View File

@@ -57,6 +57,7 @@ use web_sys::HtmlCanvasElement;
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::cursor::CustomCursorSource; use crate::cursor::CustomCursorSource;
use crate::error::NotSupportedError; use crate::error::NotSupportedError;
use crate::event::FingerId;
use crate::event_loop::{ActiveEventLoop, EventLoop}; use crate::event_loop::{ActiveEventLoop, EventLoop};
use crate::monitor::MonitorHandle; use crate::monitor::MonitorHandle;
use crate::platform_impl::PlatformCustomCursorSource; use crate::platform_impl::PlatformCustomCursorSource;
@@ -779,3 +780,16 @@ impl Display for OrientationLockError {
} }
impl Error 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 windows_sys::Win32::Foundation::HANDLE;
use crate::dpi::PhysicalSize; use crate::dpi::PhysicalSize;
use crate::event::DeviceId; use crate::event::{DeviceId, FingerId};
use crate::event_loop::EventLoopBuilder; use crate::event_loop::EventLoopBuilder;
use crate::monitor::MonitorHandle; use crate::monitor::MonitorHandle;
use crate::window::{BadIcon, Icon, Window, WindowAttributes}; 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> { impl<W: Window> rwh_06::HasWindowHandle for AnyThread<W> {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> { fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
// SAFETY: The top level user has asserted this is only used safely. // 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( unsafe fn window_handle_any_thread(
&self, &self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>; ) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>;
@@ -399,6 +401,7 @@ impl WindowExtWindows for dyn Window + '_ {
window.set_corner_preference(preference) window.set_corner_preference(preference)
} }
#[cfg(feature = "rwh_06")]
unsafe fn window_handle_any_thread( unsafe fn window_handle_any_thread(
&self, &self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> { ) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
@@ -432,8 +435,16 @@ pub trait WindowBorrowExtWindows: Borrow<dyn Window> + Sized {
/// Win32 APIs. /// Win32 APIs.
/// ///
/// [`Window`]: crate::window::Window /// [`Window`]: crate::window::Window
/// [`HasWindowHandle`]: rwh_06::HasWindowHandle #[cfg_attr(
/// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread 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> unsafe fn any_thread(self) -> AnyThread<Self>
where where
Self: Window, 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. /// Additional methods on `Icon` that are specific to Windows.
pub trait IconExtWindows: Sized { pub trait IconExtWindows: Sized {
/// Create an icon from a file path. /// Create an icon from a file path.

View File

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

View File

@@ -1,15 +1,15 @@
use std::cell::{Cell, OnceCell, RefCell}; use std::cell::{Cell, OnceCell, RefCell};
use std::mem; use std::mem;
use std::rc::{Rc, Weak}; 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::sync::Arc;
use std::time::Instant; use std::time::Instant;
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningApplication}; use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy};
use objc2_foundation::{MainThreadMarker, NSNotification}; use objc2_foundation::{MainThreadMarker, NSNotification};
use super::super::event_handler::EventHandler; use super::super::event_handler::EventHandler;
use super::event_loop::{stop_app_immediately, ActiveEventLoop, EventLoopProxy, PanicInfo}; use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
use super::menu; use super::menu;
use super::observer::{EventLoopWaker, RunLoop}; use super::observer::{EventLoopWaker, RunLoop};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
@@ -24,7 +24,7 @@ pub(super) struct AppState {
default_menu: bool, default_menu: bool,
activate_ignoring_other_apps: bool, activate_ignoring_other_apps: bool,
run_loop: RunLoop, run_loop: RunLoop,
event_loop_proxy: Arc<EventLoopProxy>, proxy_wake_up: Arc<AtomicBool>,
event_handler: EventHandler, event_handler: EventHandler,
stop_on_launch: Cell<bool>, stop_on_launch: Cell<bool>,
stop_before_wait: Cell<bool>, stop_before_wait: Cell<bool>,
@@ -72,7 +72,7 @@ impl AppState {
let this = Rc::new(AppState { let this = Rc::new(AppState {
mtm, mtm,
activation_policy, activation_policy,
event_loop_proxy: Arc::new(EventLoopProxy::new()), proxy_wake_up: Arc::new(AtomicBool::new(false)),
default_menu, default_menu,
activate_ignoring_other_apps, activate_ignoring_other_apps,
run_loop: RunLoop::main(mtm), run_loop: RunLoop::main(mtm),
@@ -113,21 +113,10 @@ impl AppState {
// We need to delay setting the activation policy and activating the app // We need to delay setting the activation policy and activating the app
// until `applicationDidFinishLaunching` has been called. Otherwise the // until `applicationDidFinishLaunching` has been called. Otherwise the
// menu bar is initially unresponsive on macOS 10.15. // menu bar is initially unresponsive on macOS 10.15.
if let Some(activation_policy) = self.activation_policy { // If no activation policy is explicitly provided, do not set it at all
app.setActivationPolicy(activation_policy); // to allow the package manifest to define behavior via LSUIElement.
} else { if self.activation_policy.is_some() {
// If no activation policy is explicitly provided, and the application app.setActivationPolicy(self.activation_policy.unwrap());
// is bundled, do not set the activation policy at all, to allow the
// package manifest to define the behavior via LSUIElement.
//
// See:
// - https://github.com/rust-windowing/winit/issues/261
// - https://github.com/rust-windowing/winit/issues/3958
let is_bundled =
unsafe { NSRunningApplication::currentApplication().bundleIdentifier().is_some() };
if !is_bundled {
app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
}
} }
#[allow(deprecated)] #[allow(deprecated)]
@@ -176,8 +165,8 @@ impl AppState {
self.event_handler.set(handler, closure) self.event_handler.set(handler, closure)
} }
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> { pub fn proxy_wake_up(&self) -> Arc<AtomicBool> {
&self.event_loop_proxy self.proxy_wake_up.clone()
} }
/// If `pump_events` is called to progress the event loop then we /// If `pump_events` is called to progress the event loop then we
@@ -361,7 +350,7 @@ impl AppState {
return; 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)); self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
} }

View File

@@ -92,12 +92,17 @@ fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
/// Create `KeyEvent` for the given `NSEvent`. /// Create `KeyEvent` for the given `NSEvent`.
/// ///
/// This function shouldn't be called when the IME input is in process. /// This function shouldn't be called when the IME input is in process.
pub(crate) fn create_key_event(ns_event: &NSEvent, is_press: bool, is_repeat: bool) -> KeyEvent { pub(crate) fn create_key_event(
ns_event: &NSEvent,
is_press: bool,
is_repeat: bool,
key_override: Option<PhysicalKey>,
) -> KeyEvent {
use ElementState::{Pressed, Released}; use ElementState::{Pressed, Released};
let state = if is_press { Pressed } else { Released }; let state = if is_press { Pressed } else { Released };
let scancode = unsafe { ns_event.keyCode() }; let scancode = unsafe { ns_event.keyCode() };
let mut physical_key = scancode_to_physicalkey(scancode as u32); let mut physical_key = key_override.unwrap_or_else(|| scancode_to_physicalkey(scancode as u32));
// NOTE: The logical key should heed both SHIFT and ALT if possible. // NOTE: The logical key should heed both SHIFT and ALT if possible.
// For instance: // For instance:
@@ -106,15 +111,20 @@ pub(crate) fn create_key_event(ns_event: &NSEvent, is_press: bool, is_repeat: bo
// * Pressing CTRL SHIFT A: logical key should also be "A" // * Pressing CTRL SHIFT A: logical key should also be "A"
// This is not easy to tease out of `NSEvent`, but we do our best. // This is not easy to tease out of `NSEvent`, but we do our best.
let characters = unsafe { ns_event.characters() }.map(|s| s.to_string()).unwrap_or_default(); let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() {
let text_with_all_modifiers = if characters.is_empty() {
None None
} else { } else {
if matches!(physical_key, PhysicalKey::Unidentified(_)) { let characters =
// The key may be one of the funky function keys unsafe { ns_event.characters() }.map(|s| s.to_string()).unwrap_or_default();
physical_key = extra_function_key_to_code(scancode, &characters); if characters.is_empty() {
None
} else {
if matches!(physical_key, PhysicalKey::Unidentified(_)) {
// The key may be one of the funky function keys
physical_key = extra_function_key_to_code(scancode, &characters);
}
Some(SmolStr::new(characters))
} }
Some(SmolStr::new(characters))
}; };
let key_from_code = code_to_key(physical_key, scancode); let key_from_code = code_to_key(physical_key, scancode);
@@ -367,7 +377,6 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
KeyCode::KeyX => Some(0x07), KeyCode::KeyX => Some(0x07),
KeyCode::KeyC => Some(0x08), KeyCode::KeyC => Some(0x08),
KeyCode::KeyV => Some(0x09), KeyCode::KeyV => Some(0x09),
KeyCode::IntlBackslash => Some(0x0a),
KeyCode::KeyB => Some(0x0b), KeyCode::KeyB => Some(0x0b),
KeyCode::KeyQ => Some(0x0c), KeyCode::KeyQ => Some(0x0c),
KeyCode::KeyW => Some(0x0d), KeyCode::KeyW => Some(0x0d),
@@ -413,21 +422,18 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
KeyCode::SuperRight => Some(0x36), KeyCode::SuperRight => Some(0x36),
KeyCode::SuperLeft => Some(0x37), KeyCode::SuperLeft => Some(0x37),
KeyCode::ShiftLeft => Some(0x38), KeyCode::ShiftLeft => Some(0x38),
KeyCode::CapsLock => Some(0x39),
KeyCode::AltLeft => Some(0x3a), KeyCode::AltLeft => Some(0x3a),
KeyCode::ControlLeft => Some(0x3b), KeyCode::ControlLeft => Some(0x3b),
KeyCode::ShiftRight => Some(0x3c), KeyCode::ShiftRight => Some(0x3c),
KeyCode::AltRight => Some(0x3d), KeyCode::AltRight => Some(0x3d),
KeyCode::ControlRight => Some(0x3e), KeyCode::ControlRight => Some(0x3e),
KeyCode::Fn => Some(0x3f),
KeyCode::F17 => Some(0x40), KeyCode::F17 => Some(0x40),
KeyCode::NumpadDecimal => Some(0x41), KeyCode::NumpadDecimal => Some(0x41),
KeyCode::NumpadMultiply => Some(0x43), KeyCode::NumpadMultiply => Some(0x43),
KeyCode::NumpadAdd => Some(0x45), KeyCode::NumpadAdd => Some(0x45),
KeyCode::NumLock => Some(0x47), KeyCode::NumLock => Some(0x47),
KeyCode::AudioVolumeUp => Some(0x48), KeyCode::AudioVolumeUp => Some(0x49),
KeyCode::AudioVolumeDown => Some(0x49), KeyCode::AudioVolumeDown => Some(0x4a),
KeyCode::AudioVolumeMute => Some(0x4a),
KeyCode::NumpadDivide => Some(0x4b), KeyCode::NumpadDivide => Some(0x4b),
KeyCode::NumpadEnter => Some(0x4c), KeyCode::NumpadEnter => Some(0x4c),
KeyCode::NumpadSubtract => Some(0x4e), KeyCode::NumpadSubtract => Some(0x4e),
@@ -446,22 +452,17 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
KeyCode::Numpad8 => Some(0x5b), KeyCode::Numpad8 => Some(0x5b),
KeyCode::Numpad9 => Some(0x5c), KeyCode::Numpad9 => Some(0x5c),
KeyCode::IntlYen => Some(0x5d), KeyCode::IntlYen => Some(0x5d),
KeyCode::IntlRo => Some(0x5e),
KeyCode::NumpadComma => Some(0x5f),
KeyCode::F5 => Some(0x60), KeyCode::F5 => Some(0x60),
KeyCode::F6 => Some(0x61), KeyCode::F6 => Some(0x61),
KeyCode::F7 => Some(0x62), KeyCode::F7 => Some(0x62),
KeyCode::F3 => Some(0x63), KeyCode::F3 => Some(0x63),
KeyCode::F8 => Some(0x64), KeyCode::F8 => Some(0x64),
KeyCode::F9 => Some(0x65), KeyCode::F9 => Some(0x65),
KeyCode::Lang2 => Some(0x66),
KeyCode::F11 => Some(0x67), KeyCode::F11 => Some(0x67),
KeyCode::Lang1 => Some(0x68),
KeyCode::F13 => Some(0x69), KeyCode::F13 => Some(0x69),
KeyCode::F16 => Some(0x6a), KeyCode::F16 => Some(0x6a),
KeyCode::F14 => Some(0x6b), KeyCode::F14 => Some(0x6b),
KeyCode::F10 => Some(0x6d), KeyCode::F10 => Some(0x6d),
KeyCode::ContextMenu => Some(0x6e),
KeyCode::F12 => Some(0x6f), KeyCode::F12 => Some(0x6f),
KeyCode::F15 => Some(0x71), KeyCode::F15 => Some(0x71),
KeyCode::Insert => Some(0x72), KeyCode::Insert => Some(0x72),
@@ -477,26 +478,11 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
KeyCode::ArrowRight => Some(0x7c), KeyCode::ArrowRight => Some(0x7c),
KeyCode::ArrowDown => Some(0x7d), KeyCode::ArrowDown => Some(0x7d),
KeyCode::ArrowUp => Some(0x7e), KeyCode::ArrowUp => Some(0x7e),
KeyCode::Power => Some(0x7f),
_ => None, _ => None,
} }
} }
pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// Follows what Chromium and Firefox do:
// https://chromium.googlesource.com/chromium/src.git/+/3e1a26c44c024d97dc9a4c09bbc6a2365398ca2c/ui/events/keycodes/dom/dom_code_data.inc
// https://searchfox.org/mozilla-central/rev/c597e9c789ad36af84a0370d395be066b7dc94f4/widget/NativeKeyToDOMCodeName.h
//
// See also:
// Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
//
// Also see https://developer.apple.com/documentation/appkit/function-key-unicode-values:
//
// > the system handles some function keys at a lower level and your app never sees them.
// > Examples include the Volume Up key, Volume Down key, Volume Mute key, Eject key, and
// > Function key found on many Macs.
//
// So the handling of some of these is mostly for show.
PhysicalKey::Code(match scancode { PhysicalKey::Code(match scancode {
0x00 => KeyCode::KeyA, 0x00 => KeyCode::KeyA,
0x01 => KeyCode::KeyS, 0x01 => KeyCode::KeyS,
@@ -508,11 +494,7 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0x07 => KeyCode::KeyX, 0x07 => KeyCode::KeyX,
0x08 => KeyCode::KeyC, 0x08 => KeyCode::KeyC,
0x09 => KeyCode::KeyV, 0x09 => KeyCode::KeyV,
// This key is typically located near LeftShift key, roughly the same location as backquote // 0x0a => World 1,
// (`) on Windows' US layout.
//
// The keycap varies on international keyboards.
0x0a => KeyCode::IntlBackslash,
0x0b => KeyCode::KeyB, 0x0b => KeyCode::KeyB,
0x0c => KeyCode::KeyQ, 0x0c => KeyCode::KeyQ,
0x0d => KeyCode::KeyW, 0x0d => KeyCode::KeyW,
@@ -554,7 +536,7 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0x31 => KeyCode::Space, 0x31 => KeyCode::Space,
0x32 => KeyCode::Backquote, 0x32 => KeyCode::Backquote,
0x33 => KeyCode::Backspace, 0x33 => KeyCode::Backspace,
// 0x34 => unknown, // kVK_Powerbook_KeypadEnter // 0x34 => unknown,
0x35 => KeyCode::Escape, 0x35 => KeyCode::Escape,
0x36 => KeyCode::SuperRight, 0x36 => KeyCode::SuperRight,
0x37 => KeyCode::SuperLeft, 0x37 => KeyCode::SuperLeft,
@@ -573,10 +555,15 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 0x44 => unknown, // 0x44 => unknown,
0x45 => KeyCode::NumpadAdd, 0x45 => KeyCode::NumpadAdd,
// 0x46 => unknown, // 0x46 => unknown,
0x47 => KeyCode::NumLock, // kVK_ANSI_KeypadClear 0x47 => KeyCode::NumLock,
0x48 => KeyCode::AudioVolumeUp, // 0x48 => KeyCode::NumpadClear,
0x49 => KeyCode::AudioVolumeDown,
0x4a => KeyCode::AudioVolumeMute, // TODO: (Artur) for me, kVK_VolumeUp is 0x48
// macOS 10.11
// /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/
// Versions/A/Headers/Events.h
0x49 => KeyCode::AudioVolumeUp,
0x4a => KeyCode::AudioVolumeDown,
0x4b => KeyCode::NumpadDivide, 0x4b => KeyCode::NumpadDivide,
0x4c => KeyCode::NumpadEnter, 0x4c => KeyCode::NumpadEnter,
// 0x4d => unknown, // 0x4d => unknown,
@@ -596,23 +583,23 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0x5b => KeyCode::Numpad8, 0x5b => KeyCode::Numpad8,
0x5c => KeyCode::Numpad9, 0x5c => KeyCode::Numpad9,
0x5d => KeyCode::IntlYen, 0x5d => KeyCode::IntlYen,
0x5e => KeyCode::IntlRo, // 0x5e => JIS Ro,
0x5f => KeyCode::NumpadComma, // 0x5f => unknown,
0x60 => KeyCode::F5, 0x60 => KeyCode::F5,
0x61 => KeyCode::F6, 0x61 => KeyCode::F6,
0x62 => KeyCode::F7, 0x62 => KeyCode::F7,
0x63 => KeyCode::F3, 0x63 => KeyCode::F3,
0x64 => KeyCode::F8, 0x64 => KeyCode::F8,
0x65 => KeyCode::F9, 0x65 => KeyCode::F9,
0x66 => KeyCode::Lang2, // 0x66 => JIS Eisuu (macOS),
0x67 => KeyCode::F11, 0x67 => KeyCode::F11,
0x68 => KeyCode::Lang1, // 0x68 => JIS Kanna (macOS),
0x69 => KeyCode::F13, 0x69 => KeyCode::F13,
0x6a => KeyCode::F16, 0x6a => KeyCode::F16,
0x6b => KeyCode::F14, 0x6b => KeyCode::F14,
// 0x6c => unknown, // 0x6c => unknown,
0x6d => KeyCode::F10, 0x6d => KeyCode::F10,
0x6e => KeyCode::ContextMenu, // 0x6e => unknown,
0x6f => KeyCode::F12, 0x6f => KeyCode::F12,
// 0x70 => unknown, // 0x70 => unknown,
0x71 => KeyCode::F15, 0x71 => KeyCode::F15,
@@ -629,7 +616,11 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0x7c => KeyCode::ArrowRight, 0x7c => KeyCode::ArrowRight,
0x7d => KeyCode::ArrowDown, 0x7d => KeyCode::ArrowDown,
0x7e => KeyCode::ArrowUp, 0x7e => KeyCode::ArrowUp,
0x7f => KeyCode::Power, // On 10.7 and 10.8 only // 0x7f => unknown,
// 0xA is the caret (^) an macOS's German QERTZ layout. This key is at the same location as
// backquote (`) on Windows' US layout.
0xa => KeyCode::Backquote,
_ => return PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode as u16)), _ => return PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode as u16)),
}) })
} }

View File

@@ -20,7 +20,6 @@ use objc2_app_kit::{
NSApplicationWillTerminateNotification, NSWindow, NSApplicationWillTerminateNotification, NSWindow,
}; };
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject, NSObjectProtocol}; use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject, NSObjectProtocol};
use rwh_06::HasDisplayHandle;
use super::super::notification_center::create_observer; use super::super::notification_center::create_observer;
use super::app::WinitApplication; use super::app::WinitApplication;
@@ -33,8 +32,7 @@ use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, RequestError}; use crate::error::{EventLoopError, RequestError};
use crate::event_loop::{ use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
}; };
use crate::monitor::MonitorHandle as RootMonitorHandle; use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform::macos::ActivationPolicy; use crate::platform::macos::ActivationPolicy;
@@ -97,8 +95,9 @@ impl ActiveEventLoop {
} }
impl RootActiveEventLoop for ActiveEventLoop { impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy { fn create_proxy(&self) -> RootEventLoopProxy {
CoreEventLoopProxy::new(self.app_state.event_loop_proxy().clone()) let event_loop_proxy = EventLoopProxy::new(self.app_state.proxy_wake_up());
RootEventLoopProxy { event_loop_proxy }
} }
fn create_window( fn create_window(
@@ -152,15 +151,17 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.app_state.exiting() self.app_state.exiting()
} }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle { fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle)) RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
} }
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle { fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self self
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop { impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new()); let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
@@ -390,12 +391,16 @@ impl EventLoop {
} }
} }
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle; pub(crate) struct OwnedDisplayHandle;
impl HasDisplayHandle for OwnedDisplayHandle { impl OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { #[cfg(feature = "rwh_06")]
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new()); #[inline]
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) } pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::AppKitDisplayHandle::new().into())
} }
} }
@@ -434,9 +439,8 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
} }
} }
#[derive(Debug)]
pub struct EventLoopProxy { pub struct EventLoopProxy {
pub(crate) wake_up: AtomicBool, proxy_wake_up: Arc<AtomicBool>,
source: CFRunLoopSourceRef, source: CFRunLoopSourceRef,
} }
@@ -451,8 +455,14 @@ impl Drop for EventLoopProxy {
} }
} }
impl Clone for EventLoopProxy {
fn clone(&self) -> Self {
EventLoopProxy::new(self.proxy_wake_up.clone())
}
}
impl EventLoopProxy { impl EventLoopProxy {
pub(crate) fn new() -> Self { fn new(proxy_wake_up: Arc<AtomicBool>) -> Self {
unsafe { unsafe {
// just wake up the eventloop // just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {} extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
@@ -476,16 +486,14 @@ impl EventLoopProxy {
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl); CFRunLoopWakeUp(rl);
EventLoopProxy { wake_up: AtomicBool::new(false), source } EventLoopProxy { proxy_wake_up, source }
} }
} }
}
impl EventLoopProxyProvider for EventLoopProxy { pub fn wake_up(&self) {
fn wake_up(&self) { self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
self.wake_up.store(true, AtomicOrdering::Relaxed);
unsafe { unsafe {
// Let the main thread know there's a new event. // let the main thread know there's a new event
CFRunLoopSourceSignal(self.source); CFRunLoopSourceSignal(self.source);
let rl = CFRunLoopGetMain(); let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl); CFRunLoopWakeUp(rl);

View File

@@ -17,7 +17,8 @@ mod window_delegate;
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor; 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::{physicalkey_to_scancode, scancode_to_physicalkey, KeyEventExtra};
pub(crate) use self::event_loop::{ 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::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::Window; 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::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
pub(crate) use crate::icon::NoIcon as PlatformIcon; pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; pub(crate) use crate::platform_impl::Fullscreen;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId;
impl FingerId {
#[cfg(test)]
pub const fn dummy() -> Self {
FingerId
}
}

View File

@@ -4,30 +4,30 @@ use std::collections::{HashMap, VecDeque};
use std::ptr; use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use objc2::rc::Retained; use objc2::rc::{Retained, WeakId};
use objc2::runtime::{AnyObject, Sel}; use objc2::runtime::{AnyObject, Sel};
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass};
use objc2_app_kit::{ use objc2_app_kit::{
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient,
NSTrackingRectTag, NSView, NSTrackingRectTag, NSView, NSViewFrameDidChangeNotification,
}; };
use objc2_foundation::{ use objc2_foundation::{
MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying,
NSMutableAttributedString, NSNotFound, NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect, NSMutableAttributedString, NSNotFound, NSNotificationCenter, NSObject, NSObjectProtocol,
NSSize, NSString, NSUInteger, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
}; };
use super::app_state::AppState; use super::app_state::AppState;
use super::cursor::{default_cursor, invisible_cursor}; use super::cursor::{default_cursor, invisible_cursor};
use super::event::{ use super::event::{
code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed, code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed,
scancode_to_physicalkey, KeyEventExtra, scancode_to_physicalkey,
}; };
use super::window::WinitWindow; use super::window::WinitWindow;
use crate::dpi::{LogicalPosition, LogicalSize}; use crate::dpi::{LogicalPosition, LogicalSize};
use crate::event::{ use crate::event::{
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta, DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, PointerKind,
PointerKind, PointerSource, TouchPhase, WindowEvent, PointerSource, TouchPhase, WindowEvent,
}; };
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey}; use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey};
use crate::platform::macos::OptionAsAlt; use crate::platform::macos::OptionAsAlt;
@@ -134,6 +134,9 @@ pub struct ViewState {
marked_text: RefCell<Retained<NSMutableAttributedString>>, marked_text: RefCell<Retained<NSMutableAttributedString>>,
accepts_first_mouse: bool, accepts_first_mouse: bool,
// Weak reference because the window keeps a strong reference to the view
_ns_window: WeakId<WinitWindow>,
/// The state of the `Option` as `Alt`. /// The state of the `Option` as `Alt`.
option_as_alt: Cell<OptionAsAlt>, option_as_alt: Cell<OptionAsAlt>,
} }
@@ -174,10 +177,9 @@ declare_class!(
self.ivars().tracking_rect.set(Some(tracking_rect)); self.ivars().tracking_rect.set(Some(tracking_rect));
} }
// Not a normal method on `NSView`, it's triggered by `NSViewFrameDidChangeNotification`. #[method(frameDidChange:)]
#[method(viewFrameDidChangeNotification:)] fn frame_did_change(&self, _event: &NSEvent) {
fn frame_did_change(&self, _notification: Option<&AnyObject>) { trace_scope!("frameDidChange:");
trace_scope!("NSViewFrameDidChangeNotification");
if let Some(tracking_rect) = self.ivars().tracking_rect.take() { if let Some(tracking_rect) = self.ivars().tracking_rect.take() {
self.removeTrackingRect(tracking_rect); self.removeTrackingRect(tracking_rect);
} }
@@ -201,7 +203,10 @@ declare_class!(
fn draw_rect(&self, _rect: NSRect) { fn draw_rect(&self, _rect: NSRect) {
trace_scope!("drawRect:"); trace_scope!("drawRect:");
self.ivars().app_state.handle_redraw(self.window().id()); // It's a workaround for https://github.com/rust-windowing/winit/issues/2640, don't replace with `self.window_id()`.
if let Some(window) = self.ivars()._ns_window.load() {
self.ivars().app_state.handle_redraw(window.id());
}
// This is a direct subclass of NSView, no need to call superclass' drawRect: // This is a direct subclass of NSView, no need to call superclass' drawRect:
} }
@@ -394,7 +399,7 @@ declare_class!(
unsafe { &*string }.to_string() 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. // Commit only if we have marked text.
if unsafe { self.hasMarkedText() } && self.is_ime_enabled() && !is_control { if unsafe { self.hasMarkedText() } && self.is_ime_enabled() && !is_control {
@@ -490,7 +495,7 @@ declare_class!(
}; };
if !had_ime_input || self.ivars().forward_key_to_app.get() { if !had_ime_input || self.ivars().forward_key_to_app.get() {
let key_event = create_key_event(&event, true, unsafe { event.isARepeat() }); let key_event = create_key_event(&event, true, unsafe { event.isARepeat() }, None);
self.queue_event(WindowEvent::KeyboardInput { self.queue_event(WindowEvent::KeyboardInput {
device_id: None, device_id: None,
event: key_event, event: key_event,
@@ -513,7 +518,7 @@ declare_class!(
) { ) {
self.queue_event(WindowEvent::KeyboardInput { self.queue_event(WindowEvent::KeyboardInput {
device_id: None, device_id: None,
event: create_key_event(&event, false, false), event: create_key_event(&event, false, false, None),
is_synthetic: false, is_synthetic: false,
}); });
} }
@@ -560,7 +565,7 @@ declare_class!(
.expect("could not find current event"); .expect("could not find current event");
self.update_modifiers(&event, false); self.update_modifiers(&event, false);
let event = create_key_event(&event, true, unsafe { event.isARepeat() }); let event = create_key_event(&event, true, unsafe { event.isARepeat() }, None);
self.queue_event(WindowEvent::KeyboardInput { self.queue_event(WindowEvent::KeyboardInput {
device_id: None, device_id: None,
@@ -652,7 +657,6 @@ declare_class!(
self.queue_event(WindowEvent::PointerEntered { self.queue_event(WindowEvent::PointerEntered {
device_id: None, device_id: None,
primary: true,
position, position,
kind: PointerKind::Mouse, kind: PointerKind::Mouse,
}); });
@@ -666,7 +670,6 @@ declare_class!(
self.queue_event(WindowEvent::PointerLeft { self.queue_event(WindowEvent::PointerLeft {
device_id: None, device_id: None,
primary: true,
position: Some(position), position: Some(position),
kind: PointerKind::Mouse, kind: PointerKind::Mouse,
}); });
@@ -801,10 +804,11 @@ declare_class!(
impl WinitView { impl WinitView {
pub(super) fn new( pub(super) fn new(
app_state: &Rc<AppState>, app_state: &Rc<AppState>,
window: &WinitWindow,
accepts_first_mouse: bool, accepts_first_mouse: bool,
option_as_alt: OptionAsAlt, option_as_alt: OptionAsAlt,
mtm: MainThreadMarker,
) -> Retained<Self> { ) -> Retained<Self> {
let mtm = MainThreadMarker::from(window);
let this = mtm.alloc().set_ivars(ViewState { let this = mtm.alloc().set_ivars(ViewState {
app_state: Rc::clone(app_state), app_state: Rc::clone(app_state),
cursor_state: Default::default(), cursor_state: Default::default(),
@@ -819,24 +823,34 @@ impl WinitView {
forward_key_to_app: Default::default(), forward_key_to_app: Default::default(),
marked_text: Default::default(), marked_text: Default::default(),
accepts_first_mouse, accepts_first_mouse,
_ns_window: WeakId::new(&window.retain()),
option_as_alt: Cell::new(option_as_alt), option_as_alt: Cell::new(option_as_alt),
}); });
let this: Retained<Self> = unsafe { msg_send_id![super(this), init] }; let this: Retained<Self> = unsafe { msg_send_id![super(this), init] };
this.setPostsFrameChangedNotifications(true);
let notification_center = unsafe { NSNotificationCenter::defaultCenter() };
unsafe {
notification_center.addObserver_selector_name_object(
&this,
sel!(frameDidChange:),
Some(NSViewFrameDidChangeNotification),
Some(&this),
)
}
*this.ivars().input_source.borrow_mut() = this.current_input_source(); *this.ivars().input_source.borrow_mut() = this.current_input_source();
this this
} }
fn window(&self) -> Retained<WinitWindow> { fn window(&self) -> Retained<WinitWindow> {
let window = (**self).window().expect("view must be installed in a window"); // TODO: Simply use `window` property on `NSView`.
// That only returns a window _after_ the view has been attached though!
if !window.isKindOfClass(WinitWindow::class()) { // (which is incompatible with `frameDidChange:`)
unreachable!("view installed in non-WinitWindow"); //
} // unsafe { msg_send_id![self, window] }
self.ivars()._ns_window.load().expect("view to have a window")
// SAFETY: Just checked that the window is `WinitWindow`
unsafe { Retained::cast(window) }
} }
fn queue_event(&self, event: WindowEvent) { fn queue_event(&self, event: WindowEvent) {
@@ -946,36 +960,22 @@ impl WinitView {
let scancode = unsafe { ns_event.keyCode() }; let scancode = unsafe { ns_event.keyCode() };
let physical_key = scancode_to_physicalkey(scancode as u32); let physical_key = scancode_to_physicalkey(scancode as u32);
let logical_key = code_to_key(physical_key, scancode); // We'll correct the `is_press` later.
let mut event = create_key_event(ns_event, false, false, Some(physical_key));
let key = code_to_key(physical_key, scancode);
// Ignore processing of unknown modifiers because we can't determine whether // Ignore processing of unknown modifiers because we can't determine whether
// it was pressed or release reliably. // it was pressed or release reliably.
// let Some(event_modifier) = key_to_modifier(&key) else {
// Furthermore, sometimes normal keys are reported inside flagsChanged:, such as
// when holding Caps Lock while pressing another key, see:
// https://github.com/alacritty/alacritty/issues/8268
let Some(event_modifier) = key_to_modifier(&logical_key) else {
break 'send_event; break 'send_event;
}; };
event.physical_key = physical_key;
let mut event = KeyEvent { event.logical_key = key.clone();
location: code_to_location(physical_key), event.location = code_to_location(physical_key);
logical_key: logical_key.clone(),
physical_key,
repeat: false,
// We'll correct this later.
state: Pressed,
text: None,
platform_specific: KeyEventExtra {
text_with_all_modifiers: None,
key_without_modifiers: logical_key.clone(),
},
};
let location_mask = ModLocationMask::from_location(event.location); let location_mask = ModLocationMask::from_location(event.location);
let mut phys_mod_state = self.ivars().phys_modifiers.borrow_mut(); let mut phys_mod_state = self.ivars().phys_modifiers.borrow_mut();
let phys_mod = let phys_mod = phys_mod_state.entry(key).or_insert(ModLocationMask::empty());
phys_mod_state.entry(logical_key).or_insert(ModLocationMask::empty());
let is_active = current_modifiers.state().contains(event_modifier); let is_active = current_modifiers.state().contains(event_modifier);
let mut events = VecDeque::with_capacity(2); let mut events = VecDeque::with_capacity(2);
@@ -1061,7 +1061,6 @@ impl WinitView {
self.queue_event(WindowEvent::PointerButton { self.queue_event(WindowEvent::PointerButton {
device_id: None, device_id: None,
primary: true,
state: button_state, state: button_state,
position, position,
button: button.into(), button: button.into(),
@@ -1088,7 +1087,6 @@ impl WinitView {
self.queue_event(WindowEvent::PointerMoved { self.queue_event(WindowEvent::PointerMoved {
device_id: None, device_id: None,
primary: true,
position: view_point.to_physical(self.scale_factor()), position: view_point.to_physical(self.scale_factor()),
source: PointerSource::Mouse, source: PointerSource::Mouse,
}); });

View File

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

View File

@@ -6,7 +6,7 @@ use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use core_graphics::display::CGDisplay; use core_graphics::display::{CGDisplay, CGPoint};
use monitor::VideoModeHandle; use monitor::VideoModeHandle;
use objc2::rc::{autoreleasepool, Retained}; use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::{AnyObject, ProtocolObject}; use objc2::runtime::{AnyObject, ProtocolObject};
@@ -15,16 +15,15 @@ use objc2_app_kit::{
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization, NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization,
NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType, NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType,
NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard, NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard,
NSRequestUserAttentionType, NSScreen, NSToolbar, NSView, NSViewFrameDidChangeNotification, NSRequestUserAttentionType, NSScreen, NSView, NSWindowButton, NSWindowDelegate,
NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel, NSWindowFullScreenButton, NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode,
NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask, NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility,
NSWindowTabbingMode, NSWindowTitleVisibility, NSWindowToolbarStyle,
}; };
use objc2_foundation::{ use objc2_foundation::{
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSEdgeInsets, ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSKeyValueChangeKey,
NSKeyValueChangeKey, NSKeyValueChangeNewKey, NSKeyValueChangeOldKey, NSKeyValueChangeNewKey, NSKeyValueChangeOldKey, NSKeyValueObservingOptions, NSObject,
NSKeyValueObservingOptions, NSNotificationCenter, NSObject, NSObjectNSDelayedPerforming, NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint, NSRect, NSSize, NSString, NSRect, NSSize, NSString,
}; };
use tracing::{trace, warn}; use tracing::{trace, warn};
@@ -35,10 +34,7 @@ use super::observer::RunLoop;
use super::view::WinitView; use super::view::WinitView;
use super::window::WinitWindow; use super::window::WinitWindow;
use super::{ffi, Fullscreen, MonitorHandle}; use super::{ffi, Fullscreen, MonitorHandle};
use crate::dpi::{ use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
Position, Size,
};
use crate::error::{NotSupportedError, RequestError}; use crate::error::{NotSupportedError, RequestError};
use crate::event::{SurfaceSizeWriter, WindowEvent}; use crate::event::{SurfaceSizeWriter, WindowEvent};
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS}; use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
@@ -61,7 +57,6 @@ pub struct PlatformSpecificWindowAttributes {
pub tabbing_identifier: Option<String>, pub tabbing_identifier: Option<String>,
pub option_as_alt: OptionAsAlt, pub option_as_alt: OptionAsAlt,
pub borderless_game: bool, pub borderless_game: bool,
pub unified_titlebar: bool,
} }
impl Default for PlatformSpecificWindowAttributes { impl Default for PlatformSpecificWindowAttributes {
@@ -80,7 +75,6 @@ impl Default for PlatformSpecificWindowAttributes {
tabbing_identifier: None, tabbing_identifier: None,
option_as_alt: Default::default(), option_as_alt: Default::default(),
borderless_game: false, borderless_game: false,
unified_titlebar: false,
} }
} }
} }
@@ -170,7 +164,7 @@ declare_class!(
#[method(windowDidResize:)] #[method(windowDidResize:)]
fn window_did_resize(&self, _: Option<&AnyObject>) { fn window_did_resize(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidResize:"); trace_scope!("windowDidResize:");
// NOTE: WindowEvent::SurfaceResized is reported using NSViewFrameDidChangeNotification. // NOTE: WindowEvent::SurfaceResized is reported in frameDidChange.
self.emit_move_event(); self.emit_move_event();
} }
@@ -445,15 +439,9 @@ declare_class!(
// NOTE: We don't _really_ need to check the key path, as there should only be one, but // 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. // in the future we might want to observe other key paths.
if key_path == Some(ns_string!("effectiveAppearance")) { if key_path == Some(ns_string!("effectiveAppearance")) {
let change = change.expect( let change = change.expect("requested a change dictionary in `addObserver`, but none was provided");
"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 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` // SAFETY: The value of `effectiveAppearance` is `NSAppearance`
let old: *const AnyObject = old; let old: *const AnyObject = old;
@@ -570,12 +558,6 @@ fn new_window(
} }
if attrs.platform_specific.fullsize_content_view { 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; masks |= NSWindowStyleMask::FullSizeContentView;
} }
@@ -634,14 +616,6 @@ fn new_window(
if attrs.platform_specific.movable_by_window_background { if attrs.platform_specific.movable_by_window_background {
window.setMovableByWindowBackground(true); 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 !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) {
if let Some(button) = window.standardWindowButton(NSWindowButton::NSWindowZoomButton) { if let Some(button) = window.standardWindowButton(NSWindowButton::NSWindowZoomButton) {
@@ -658,9 +632,9 @@ fn new_window(
let view = WinitView::new( let view = WinitView::new(
app_state, app_state,
&window,
attrs.platform_specific.accepts_first_mouse, attrs.platform_specific.accepts_first_mouse,
attrs.platform_specific.option_as_alt, attrs.platform_specific.option_as_alt,
mtm,
); );
// The default value of `setWantsBestResolutionOpenGLSurface:` was `false` until // The default value of `setWantsBestResolutionOpenGLSurface:` was `false` until
@@ -682,23 +656,6 @@ fn new_window(
window.setContentView(Some(&view)); window.setContentView(Some(&view));
window.setInitialFirstResponder(Some(&view)); window.setInitialFirstResponder(Some(&view));
// Configure the view to send notifications whenever its frame rectangle changes.
//
// We explicitly do this _after_ setting the view as the content view of the window, to
// avoid a resize event when creating the window.
view.setPostsFrameChangedNotifications(true);
// `setPostsFrameChangedNotifications` posts the notification immediately, so register the
// observer _after_, again so that the event isn't triggered initially.
let notification_center = unsafe { NSNotificationCenter::defaultCenter() };
unsafe {
notification_center.addObserver_selector_name_object(
&view,
sel!(viewFrameDidChangeNotification:),
Some(NSViewFrameDidChangeNotification),
Some(&view),
)
}
if attrs.transparent { if attrs.transparent {
window.setOpaque(false); window.setOpaque(false);
// See `set_transparent` for details on why we do this. // See `set_transparent` for details on why we do this.
@@ -724,6 +681,7 @@ impl WindowDelegate {
let window = new_window(app_state, &attrs, mtm) let window = new_window(app_state, &attrs, mtm)
.ok_or_else(|| os_error!("couldn't create `NSWindow`"))?; .ok_or_else(|| os_error!("couldn't create `NSWindow`"))?;
#[cfg(feature = "rwh_06")]
match attrs.parent_window.map(|handle| handle.0) { match attrs.parent_window.map(|handle| handle.0) {
Some(rwh_06::RawWindowHandle::AppKit(handle)) => { Some(rwh_06::RawWindowHandle::AppKit(handle)) => {
// SAFETY: Caller ensures the pointer is valid or NULL // SAFETY: Caller ensures the pointer is valid or NULL
@@ -782,6 +740,12 @@ impl WindowDelegate {
}); });
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] }; 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))); window.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
// Listen for theme change event. // Listen for theme change event.
@@ -812,6 +776,10 @@ impl WindowDelegate {
delegate.set_cursor(attrs.cursor); 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 // Set fullscreen mode after we setup everything
delegate.set_fullscreen(attrs.fullscreen.map(Into::into)); delegate.set_fullscreen(attrs.fullscreen.map(Into::into));
@@ -956,28 +924,15 @@ impl WindowDelegate {
#[inline] #[inline]
pub fn pre_present_notify(&self) {} 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()); 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> {
// The calculation here is a bit awkward because we've gotta reconcile the let content_rect = self.window().contentRectForFrameRect(self.window().frame());
// different origins (Winit prefers top-left vs. NSWindow's bottom-left), let position = flip_window_screen_coordinates(content_rect);
// and I couldn't find a built-in way to do so. LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
// The position of the window and the view, both in Winit screen coordinates.
let window_position = flip_window_screen_coordinates(self.window().frame());
let view_position = flip_window_screen_coordinates(
self.window().contentRectForFrameRect(self.window().frame()),
);
// And use that to convert the view position to window coordinates.
let surface_position =
NSPoint::new(view_position.x - window_position.x, view_position.y - window_position.y);
let logical = LogicalPosition::new(surface_position.x, surface_position.y);
logical.to_physical(self.scale_factor())
} }
pub fn set_outer_position(&self, position: Position) { pub fn set_outer_position(&self, position: Position) {
@@ -1003,37 +958,6 @@ impl WindowDelegate {
logical.to_physical(self.scale_factor()) 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 {
// If `safeAreaInsets` is not available, we'll have to do the calculation ourselves.
let window_rect = unsafe {
self.window().convertRectFromScreen(
self.window().contentRectForFrameRect(self.window().frame()),
)
};
// This includes NSWindowStyleMask::FullSizeContentView.
let layout_rect = unsafe { self.window().contentLayoutRect() };
// Calculate the insets from window coordinates in AppKit's coordinate system.
NSEdgeInsets {
top: (window_rect.size.height + window_rect.origin.y)
- (layout_rect.size.height + layout_rect.origin.y),
left: layout_rect.origin.x - window_rect.origin.x,
bottom: layout_rect.origin.y - window_rect.origin.y,
right: (window_rect.size.width + window_rect.origin.x)
- (layout_rect.size.width + layout_rect.origin.x),
}
};
let insets = LogicalInsets::new(insets.top, insets.left, insets.bottom, insets.right);
insets.to_physical(self.scale_factor())
}
#[inline] #[inline]
pub fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> { pub fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
let scale_factor = self.scale_factor(); let scale_factor = self.scale_factor();
@@ -1230,12 +1154,13 @@ impl WindowDelegate {
#[inline] #[inline]
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), RequestError> { pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), RequestError> {
let content_rect = self.window().contentRectForFrameRect(self.window().frame()); let physical_window_position = self.inner_position();
let window_position = flip_window_screen_coordinates(content_rect); let scale_factor = self.scale_factor();
let cursor_position = cursor_position.to_logical::<CGFloat>(self.scale_factor()); let window_position = physical_window_position.to_logical::<CGFloat>(scale_factor);
let point = core_graphics::display::CGPoint { let logical_cursor_position = cursor_position.to_logical::<CGFloat>(scale_factor);
x: window_position.x + cursor_position.x, let point = CGPoint {
y: window_position.y + cursor_position.y, x: logical_cursor_position.x + window_position.x,
y: logical_cursor_position.y + window_position.y,
}; };
CGDisplay::warp_mouse_cursor_position(point) CGDisplay::warp_mouse_cursor_position(point)
.map_err(|status| os_error!(format!("CGError {status}")))?; .map_err(|status| os_error!(format!("CGError {status}")))?;
@@ -1695,6 +1620,7 @@ impl WindowDelegate {
Some(monitor) Some(monitor)
} }
#[cfg(feature = "rwh_06")]
#[inline] #[inline]
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle { pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
let window_handle = rwh_06::AppKitWindowHandle::new({ let window_handle = rwh_06::AppKitWindowHandle::new({
@@ -1817,15 +1743,12 @@ impl WindowExtMacOS for WindowDelegate {
let screen = self.window().screen().expect("expected screen to be available"); let screen = self.window().screen().expect("expected screen to be available");
self.window().setFrame_display(screen.frame(), true); 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 // Fullscreen windows can't be resized, minimized, or moved
self.toggle_style_mask(NSWindowStyleMask::Miniaturizable, false); self.toggle_style_mask(NSWindowStyleMask::Miniaturizable, false);
self.toggle_style_mask(NSWindowStyleMask::Resizable, false); self.toggle_style_mask(NSWindowStyleMask::Resizable, false);
self.window().setMovable(false); self.window().setMovable(false);
true
} else { } else {
let new_mask = self.saved_style(); let new_mask = self.saved_style();
self.set_style_mask(new_mask); self.set_style_mask(new_mask);
@@ -1838,22 +1761,11 @@ impl WindowExtMacOS for WindowDelegate {
app.setPresentationOptions(presentation_opts); 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().setFrame_display(frame, true);
self.window().setMovable(true); self.window().setMovable(true);
}
true true
}
} }
#[inline] #[inline]
@@ -1925,34 +1837,6 @@ impl WindowExtMacOS for WindowDelegate {
fn is_borderless_game(&self) -> bool { fn is_borderless_game(&self) -> bool {
self.ivars().is_borderless_game.get() 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 = const DEFAULT_STANDARD_FRAME: NSRect =

View File

@@ -106,6 +106,7 @@ impl EventHandler {
self.inner.try_borrow().is_err() self.inner.try_borrow().is_err()
} }
#[cfg(target_os = "macos")]
pub(crate) fn ready(&self) -> bool { pub(crate) fn ready(&self) -> bool {
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_))) matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
} }

View File

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

View File

@@ -3,7 +3,7 @@
use std::cell::{OnceCell, RefCell, RefMut}; use std::cell::{OnceCell, RefCell, RefMut};
use std::collections::HashSet; use std::collections::HashSet;
use std::os::raw::c_void; 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::sync::{Arc, Mutex, OnceLock};
use std::time::Instant; use std::time::Instant;
use std::{mem, ptr}; use std::{mem, ptr};
@@ -24,12 +24,11 @@ use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
use super::super::event_handler::EventHandler; use super::super::event_handler::EventHandler;
use super::window::WinitUIWindow; use super::window::WinitUIWindow;
use super::{ActiveEventLoop, EventLoopProxy}; use super::ActiveEventLoop;
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::dpi::PhysicalSize; use crate::dpi::PhysicalSize;
use crate::event::{StartCause, SurfaceSizeWriter, WindowEvent}; use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
use crate::event_loop::ControlFlow; use crate::event_loop::ControlFlow;
use crate::window::WindowId;
macro_rules! bug { macro_rules! bug {
($($msg:tt)*) => { ($($msg:tt)*) => {
@@ -68,9 +67,25 @@ fn get_handler(mtm: MainThreadMarker) -> &'static EventHandler {
GLOBAL.get(mtm).get_or_init(EventHandler::new) GLOBAL.get(mtm).get_or_init(EventHandler::new)
} }
fn handle_event(mtm: MainThreadMarker, event: Event) {
let event_loop = &ActiveEventLoop { mtm };
get_handler(mtm).handle(|app| match event {
Event::NewEvents(cause) => app.new_events(event_loop, cause),
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
Event::UserWakeUp => app.proxy_wake_up(event_loop),
Event::Suspended => app.suspended(event_loop),
Event::Resumed => app.resumed(event_loop),
Event::CreateSurfaces => app.can_create_surfaces(event_loop),
Event::AboutToWait => app.about_to_wait(event_loop),
Event::LoopExiting => app.exiting(event_loop),
Event::MemoryWarning => app.memory_warning(event_loop),
})
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum EventWrapper { pub(crate) enum EventWrapper {
Window { window_id: WindowId, event: WindowEvent }, StaticEvent(Event),
ScaleFactorChanged(ScaleFactorChanged), ScaleFactorChanged(ScaleFactorChanged),
} }
@@ -81,9 +96,14 @@ pub struct ScaleFactorChanged {
pub(super) scale_factor: f64, pub(super) scale_factor: f64,
} }
impl EventWrapper { enum UserCallbackTransitionResult<'a> {
Success { active_control_flow: ControlFlow, processing_redraws: bool },
ReentrancyPrevented { queued_events: &'a mut Vec<EventWrapper> },
}
impl Event {
fn is_redraw(&self) -> bool { fn is_redraw(&self) -> bool {
matches!(self, Self::Window { event: WindowEvent::RedrawRequested, .. }) matches!(self, Event::WindowEvent { event: WindowEvent::RedrawRequested, .. })
} }
} }
@@ -92,12 +112,18 @@ impl EventWrapper {
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"] #[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
enum AppStateImpl { enum AppStateImpl {
Initial { Initial {
queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>, queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
}, },
ProcessingEvents { ProcessingEvents {
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>, queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
// special state to deal with reentrancy and prevent mutable aliasing.
InUserCallback {
queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
},
ProcessingRedraws { ProcessingRedraws {
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
@@ -113,8 +139,7 @@ pub(crate) struct AppState {
app_state: Option<AppStateImpl>, app_state: Option<AppStateImpl>,
control_flow: ControlFlow, control_flow: ControlFlow,
waker: EventLoopWaker, waker: EventLoopWaker,
event_loop_proxy: Arc<EventLoopProxy>, proxy_wake_up: Arc<AtomicBool>,
queued_events: Vec<EventWrapper>,
} }
impl AppState { impl AppState {
@@ -133,11 +158,13 @@ impl AppState {
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) { fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() }); let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() });
**guard = Some(AppState { **guard = Some(AppState {
app_state: Some(AppStateImpl::Initial { queued_gpu_redraws: HashSet::new() }), app_state: Some(AppStateImpl::Initial {
queued_events: Vec::new(),
queued_gpu_redraws: HashSet::new(),
}),
control_flow: ControlFlow::default(), control_flow: ControlFlow::default(),
waker, waker,
event_loop_proxy: Arc::new(EventLoopProxy::new()), proxy_wake_up: Arc::new(AtomicBool::new(false)),
queued_events: Vec::new(),
}); });
} }
init_guard(&mut guard); init_guard(&mut guard);
@@ -190,34 +217,48 @@ impl AppState {
matches!(self.state(), AppStateImpl::Terminated) matches!(self.state(), AppStateImpl::Terminated)
} }
fn did_finish_launching_transition(&mut self) { fn did_finish_launching_transition(&mut self) -> Vec<EventWrapper> {
let queued_gpu_redraws = match self.take_state() { let (events, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Initial { queued_gpu_redraws } => queued_gpu_redraws, AppStateImpl::Initial { queued_events, queued_gpu_redraws } => {
(queued_events, queued_gpu_redraws)
},
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingEvents { self.set_state(AppStateImpl::ProcessingEvents {
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
queued_gpu_redraws, queued_gpu_redraws,
}); });
events
} }
fn wakeup_transition(&mut self) -> Option<StartCause> { fn wakeup_transition(&mut self) -> Option<EventWrapper> {
// before `AppState::did_finish_launching` is called, pretend there is no running // before `AppState::did_finish_launching` is called, pretend there is no running
// event loop. // event loop.
if !self.has_launched() || self.has_terminated() { if !self.has_launched() || self.has_terminated() {
return None; return None;
} }
let start_cause = match (self.control_flow, self.take_state()) { let event = match (self.control_flow, self.take_state()) {
(ControlFlow::Poll, AppStateImpl::PollFinished) => StartCause::Poll, (ControlFlow::Poll, AppStateImpl::PollFinished) => {
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll))
},
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => { (ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
StartCause::WaitCancelled { start, requested_resume: None } EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: None,
}))
}, },
(ControlFlow::WaitUntil(requested_resume), AppStateImpl::Waiting { start }) => { (ControlFlow::WaitUntil(requested_resume), AppStateImpl::Waiting { start }) => {
if Instant::now() >= requested_resume { if Instant::now() >= requested_resume {
StartCause::ResumeTimeReached { start, requested_resume } EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached {
start,
requested_resume,
}))
} else { } else {
StartCause::WaitCancelled { start, requested_resume: Some(requested_resume) } EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: Some(requested_resume),
}))
} }
}, },
s => bug!("`EventHandler` unexpectedly woke up {:?}", s), s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
@@ -227,7 +268,55 @@ impl AppState {
queued_gpu_redraws: Default::default(), queued_gpu_redraws: Default::default(),
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
}); });
Some(start_cause) Some(event)
}
fn try_user_callback_transition(&mut self) -> UserCallbackTransitionResult<'_> {
// If we're not able to process an event due to recursion or `Init` not having been sent out
// yet, then queue the events up.
match self.state_mut() {
&mut AppStateImpl::Initial { ref mut queued_events, .. }
| &mut AppStateImpl::InUserCallback { ref mut queued_events, .. } => {
// A lifetime cast: early returns are not currently handled well with NLL, but
// polonius handles them well. This transmute is a safe workaround.
return unsafe {
mem::transmute::<
UserCallbackTransitionResult<'_>,
UserCallbackTransitionResult<'_>,
>(UserCallbackTransitionResult::ReentrancyPrevented {
queued_events,
})
};
},
&mut AppStateImpl::ProcessingEvents { .. }
| &mut AppStateImpl::ProcessingRedraws { .. } => {},
s @ &mut AppStateImpl::PollFinished { .. }
| s @ &mut AppStateImpl::Waiting { .. }
| s @ &mut AppStateImpl::Terminated => {
bug!("unexpected attempted to process an event {:?}", s)
},
}
let (queued_gpu_redraws, active_control_flow, processing_redraws) = match self.take_state()
{
AppStateImpl::Initial { .. } | AppStateImpl::InUserCallback { .. } => unreachable!(),
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
(queued_gpu_redraws, active_control_flow, false)
},
AppStateImpl::ProcessingRedraws { active_control_flow } => {
(Default::default(), active_control_flow, true)
},
AppStateImpl::PollFinished { .. }
| AppStateImpl::Waiting { .. }
| AppStateImpl::Terminated => unreachable!(),
};
self.set_state(AppStateImpl::InUserCallback {
queued_events: Vec::new(),
queued_gpu_redraws,
});
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws }
} }
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> { fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
@@ -283,12 +372,12 @@ impl AppState {
fn terminated_transition(&mut self) { fn terminated_transition(&mut self) {
match self.replace_state(AppStateImpl::Terminated) { match self.replace_state(AppStateImpl::Terminated) {
AppStateImpl::ProcessingEvents { .. } => {}, AppStateImpl::ProcessingEvents { .. } => {},
s => bug!("terminated while not processing events {:?}", s), s => bug!("`LoopExiting` happened while not processing events {:?}", s),
} }
} }
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> { pub(crate) fn proxy_wake_up(&self) -> Arc<AtomicBool> {
&self.event_loop_proxy self.proxy_wake_up.clone()
} }
pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) { pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) {
@@ -304,7 +393,8 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<W
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
match this.state_mut() { match this.state_mut() {
&mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. } &mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. }
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. } => { | &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. }
| &mut AppStateImpl::InUserCallback { ref mut queued_gpu_redraws, .. } => {
let _ = queued_gpu_redraws.insert(window); let _ = queued_gpu_redraws.insert(window);
}, },
s @ &mut AppStateImpl::ProcessingRedraws { .. } s @ &mut AppStateImpl::ProcessingRedraws { .. }
@@ -328,24 +418,27 @@ pub fn did_finish_launching(mtm: MainThreadMarker) {
// have to drop RefMut because the window setup code below can trigger new events // have to drop RefMut because the window setup code below can trigger new events
drop(this); drop(this);
AppState::get_mut(mtm).did_finish_launching_transition(); let events = AppState::get_mut(mtm).did_finish_launching_transition();
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, StartCause::Init)); let events = [
get_handler(mtm).handle(|app| app.can_create_surfaces(&ActiveEventLoop { mtm })); EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init)),
handle_nonuser_events(mtm, []); EventWrapper::StaticEvent(Event::CreateSurfaces),
]
.into_iter()
.chain(events);
handle_nonuser_events(mtm, events);
} }
// AppState::did_finish_launching handles the special transition `Init` // AppState::did_finish_launching handles the special transition `Init`
pub fn handle_wakeup_transition(mtm: MainThreadMarker) { pub fn handle_wakeup_transition(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let cause = match this.wakeup_transition() { let wakeup_event = match this.wakeup_transition() {
None => return, None => return,
Some(cause) => cause, Some(wakeup_event) => wakeup_event,
}; };
drop(this); drop(this);
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, cause)); handle_nonuser_event(mtm, wakeup_event)
handle_nonuser_events(mtm, []);
} }
pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) { pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) {
@@ -361,75 +454,132 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
return; return;
} }
if !get_handler(mtm).ready() { let (active_control_flow, processing_redraws) = match this.try_user_callback_transition() {
// Prevent re-entrancy; queue the events up for once we're done handling the event instead. UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
this.queued_events.extend(events); queued_events.extend(events);
return; return;
} },
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws } => {
let processing_redraws = matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }); (active_control_flow, processing_redraws)
},
};
drop(this); drop(this);
for event in events { for wrapper in events {
if !processing_redraws && event.is_redraw() { match wrapper {
tracing::info!("processing `RedrawRequested` during the main event loop"); EventWrapper::StaticEvent(event) => {
} else if processing_redraws && !event.is_redraw() { if !processing_redraws && event.is_redraw() {
tracing::warn!( tracing::info!("processing `RedrawRequested` during the main event loop");
"processing non `RedrawRequested` event after the main event loop: {:#?}", } else if processing_redraws && !event.is_redraw() {
event tracing::warn!(
); "processing non `RedrawRequested` event after the main event loop: {:#?}",
event
);
}
handle_event(mtm, event)
},
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
} }
handle_wrapped_event(mtm, event)
} }
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let queued_events = mem::take(&mut this.queued_events); let queued_events = match this.state_mut() {
&mut AppStateImpl::InUserCallback { ref mut queued_events, queued_gpu_redraws: _ } => {
mem::take(queued_events)
},
s => bug!("unexpected state {:?}", s),
};
if queued_events.is_empty() { if queued_events.is_empty() {
let queued_gpu_redraws = match this.take_state() {
AppStateImpl::InUserCallback { queued_events: _, queued_gpu_redraws } => {
queued_gpu_redraws
},
_ => unreachable!(),
};
this.app_state = Some(if processing_redraws {
bug_assert!(
queued_gpu_redraws.is_empty(),
"redraw queued while processing redraws"
);
AppStateImpl::ProcessingRedraws { active_control_flow }
} else {
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow }
});
break; break;
} }
drop(this); drop(this);
for event in queued_events { for wrapper in queued_events {
if !processing_redraws && event.is_redraw() { match wrapper {
tracing::info!("processing `RedrawRequested` during the main event loop"); EventWrapper::StaticEvent(event) => {
} else if processing_redraws && !event.is_redraw() { if !processing_redraws && event.is_redraw() {
tracing::warn!( tracing::info!("processing `RedrawRequested` during the main event loop");
"processing non-`RedrawRequested` event after the main event loop: {:#?}", } else if processing_redraws && !event.is_redraw() {
event tracing::warn!(
); "processing non-`RedrawRequested` event after the main event loop: \
{:#?}",
event
);
}
handle_event(mtm, event)
},
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
} }
handle_wrapped_event(mtm, event);
} }
} }
} }
fn handle_user_events(mtm: MainThreadMarker) { fn handle_user_events(mtm: MainThreadMarker) {
let this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
if matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }) { let (active_control_flow, processing_redraws) = match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
bug!("unexpected attempted to process an event")
},
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws } => {
(active_control_flow, processing_redraws)
},
};
if processing_redraws {
bug!("user events attempted to be sent out while `ProcessingRedraws`"); bug!("user events attempted to be sent out while `ProcessingRedraws`");
} }
let event_loop_proxy = this.event_loop_proxy().clone(); let proxy_wake_up = this.proxy_wake_up.clone();
drop(this); drop(this);
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) { if proxy_wake_up.swap(false, Ordering::Relaxed) {
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm })); handle_event(mtm, Event::UserWakeUp);
} }
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let queued_events = mem::take(&mut this.queued_events); let queued_events = match this.state_mut() {
&mut AppStateImpl::InUserCallback { ref mut queued_events, queued_gpu_redraws: _ } => {
mem::take(queued_events)
},
s => bug!("unexpected state {:?}", s),
};
if queued_events.is_empty() { if queued_events.is_empty() {
let queued_gpu_redraws = match this.take_state() {
AppStateImpl::InUserCallback { queued_events: _, queued_gpu_redraws } => {
queued_gpu_redraws
},
_ => unreachable!(),
};
this.app_state =
Some(AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow });
break; break;
} }
drop(this); drop(this);
for event in queued_events { for wrapper in queued_events {
handle_wrapped_event(mtm, event); match wrapper {
EventWrapper::StaticEvent(event) => handle_event(mtm, event),
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
}
} }
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) { if proxy_wake_up.swap(false, Ordering::Relaxed) {
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm })); handle_event(mtm, Event::UserWakeUp);
} }
} }
} }
@@ -447,10 +597,10 @@ pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, o
let ptr: *const WinitUIWindow = ptr.cast(); let ptr: *const WinitUIWindow = ptr.cast();
&*ptr &*ptr
}; };
events.push(EventWrapper::Window { events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::Occluded(occluded), event: WindowEvent::Occluded(occluded),
}); }));
} }
} }
handle_nonuser_events(mtm, events); handle_nonuser_events(mtm, events);
@@ -473,37 +623,23 @@ pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
let redraw_events: Vec<EventWrapper> = this let redraw_events: Vec<EventWrapper> = this
.main_events_cleared_transition() .main_events_cleared_transition()
.into_iter() .into_iter()
.map(|window| EventWrapper::Window { .map(|window| {
window_id: window.id(), EventWrapper::StaticEvent(Event::WindowEvent {
event: WindowEvent::RedrawRequested, window_id: window.id(),
event: WindowEvent::RedrawRequested,
})
}) })
.collect(); .collect();
drop(this); drop(this);
handle_nonuser_events(mtm, redraw_events); handle_nonuser_events(mtm, redraw_events);
get_handler(mtm).handle(|app| app.about_to_wait(&ActiveEventLoop { mtm })); handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::AboutToWait));
handle_nonuser_events(mtm, []);
} }
pub fn handle_events_cleared(mtm: MainThreadMarker) { pub fn handle_events_cleared(mtm: MainThreadMarker) {
AppState::get_mut(mtm).events_cleared_transition(); AppState::get_mut(mtm).events_cleared_transition();
} }
pub(crate) fn handle_resumed(mtm: MainThreadMarker) {
get_handler(mtm).handle(|app| app.resumed(&ActiveEventLoop { mtm }));
handle_nonuser_events(mtm, []);
}
pub(crate) fn handle_suspended(mtm: MainThreadMarker) {
get_handler(mtm).handle(|app| app.suspended(&ActiveEventLoop { mtm }));
handle_nonuser_events(mtm, []);
}
pub(crate) fn handle_memory_warning(mtm: MainThreadMarker) {
get_handler(mtm).handle(|app| app.memory_warning(&ActiveEventLoop { mtm }));
handle_nonuser_events(mtm, []);
}
pub(crate) fn terminated(application: &UIApplication) { pub(crate) fn terminated(application: &UIApplication) {
let mtm = MainThreadMarker::from(application); let mtm = MainThreadMarker::from(application);
@@ -517,10 +653,10 @@ pub(crate) fn terminated(application: &UIApplication) {
let ptr: *const WinitUIWindow = ptr.cast(); let ptr: *const WinitUIWindow = ptr.cast();
&*ptr &*ptr
}; };
events.push(EventWrapper::Window { events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::Destroyed, event: WindowEvent::Destroyed,
}); }));
} }
} }
handle_nonuser_events(mtm, events); handle_nonuser_events(mtm, events);
@@ -529,26 +665,20 @@ pub(crate) fn terminated(application: &UIApplication) {
this.terminated_transition(); this.terminated_transition();
drop(this); drop(this);
get_handler(mtm).handle(|app| app.exiting(&ActiveEventLoop { mtm })); handle_event(mtm, Event::LoopExiting)
}
fn handle_wrapped_event(mtm: MainThreadMarker, event: EventWrapper) {
match event {
EventWrapper::Window { window_id, event } => get_handler(mtm)
.handle(|app| app.window_event(&ActiveEventLoop { mtm }, window_id, event)),
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
}
} }
fn handle_hidpi_proxy(mtm: MainThreadMarker, event: ScaleFactorChanged) { fn handle_hidpi_proxy(mtm: MainThreadMarker, event: ScaleFactorChanged) {
let ScaleFactorChanged { suggested_size, scale_factor, window } = event; let ScaleFactorChanged { suggested_size, scale_factor, window } = event;
let new_surface_size = Arc::new(Mutex::new(suggested_size)); let new_surface_size = Arc::new(Mutex::new(suggested_size));
get_handler(mtm).handle(|app| { let event = Event::WindowEvent {
app.window_event(&ActiveEventLoop { mtm }, window.id(), WindowEvent::ScaleFactorChanged { window_id: window.id(),
event: WindowEvent::ScaleFactorChanged {
scale_factor, scale_factor,
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)), surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
}); },
}); };
handle_event(mtm, event);
let (view, screen_frame) = get_view_and_screen_frame(&window); let (view, screen_frame) = get_view_and_screen_frame(&window);
let physical_size = *new_surface_size.lock().unwrap(); let physical_size = *new_surface_size.lock().unwrap();
drop(new_surface_size); drop(new_surface_size);

View File

@@ -20,17 +20,16 @@ use objc2_ui_kit::{
UIApplicationWillEnterForegroundNotification, UIApplicationWillResignActiveNotification, UIApplicationWillEnterForegroundNotification, UIApplicationWillResignActiveNotification,
UIApplicationWillTerminateNotification, UIScreen, UIApplicationWillTerminateNotification, UIScreen,
}; };
use rwh_06::HasDisplayHandle;
use super::super::notification_center::create_observer; use super::super::notification_center::create_observer;
use super::app_state::{send_occluded_event_for_all_windows, AppState}; use super::app_state::{send_occluded_event_for_all_windows, AppState, EventWrapper};
use super::{app_state, monitor, MonitorHandle}; use super::{app_state, monitor, MonitorHandle};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, NotSupportedError, RequestError}; use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event::Event;
use crate::event_loop::{ use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
}; };
use crate::monitor::MonitorHandle as RootMonitorHandle; use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform_impl::Window; use crate::platform_impl::Window;
@@ -42,8 +41,9 @@ pub(crate) struct ActiveEventLoop {
} }
impl RootActiveEventLoop for ActiveEventLoop { impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy { fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
CoreEventLoopProxy::new(AppState::get_mut(self.mtm).event_loop_proxy().clone()) let event_loop_proxy = EventLoopProxy::new(AppState::get_mut(self.mtm).proxy_wake_up());
RootEventLoopProxy { event_loop_proxy }
} }
fn create_window( fn create_window(
@@ -94,15 +94,17 @@ impl RootActiveEventLoop for ActiveEventLoop {
false false
} }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle { fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle)) RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
} }
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle { fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self self
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop { impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::UiKit(rwh_06::UiKitDisplayHandle::new()); let raw = rwh_06::RawDisplayHandle::UiKit(rwh_06::UiKitDisplayHandle::new());
@@ -113,10 +115,13 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle; pub(crate) struct OwnedDisplayHandle;
impl HasDisplayHandle for OwnedDisplayHandle { impl OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { #[cfg(feature = "rwh_06")]
let raw = rwh_06::RawDisplayHandle::UiKit(rwh_06::UiKitDisplayHandle::new()); #[inline]
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) } pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::UiKitDisplayHandle::new().into())
} }
} }
@@ -173,13 +178,17 @@ impl EventLoop {
&center, &center,
// `applicationDidBecomeActive:` // `applicationDidBecomeActive:`
unsafe { UIApplicationDidBecomeActiveNotification }, unsafe { UIApplicationDidBecomeActiveNotification },
move |_| app_state::handle_resumed(mtm), move |_| {
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed));
},
); );
let _will_resign_active_observer = create_observer( let _will_resign_active_observer = create_observer(
&center, &center,
// `applicationWillResignActive:` // `applicationWillResignActive:`
unsafe { UIApplicationWillResignActiveNotification }, unsafe { UIApplicationWillResignActiveNotification },
move |_| app_state::handle_suspended(mtm), move |_| {
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended));
},
); );
let _will_enter_foreground_observer = create_observer( let _will_enter_foreground_observer = create_observer(
&center, &center,
@@ -226,7 +235,12 @@ impl EventLoop {
&center, &center,
// `applicationDidReceiveMemoryWarning:` // `applicationDidReceiveMemoryWarning:`
unsafe { UIApplicationDidReceiveMemoryWarningNotification }, unsafe { UIApplicationDidReceiveMemoryWarningNotification },
move |_| app_state::handle_memory_warning(mtm), move |_| {
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::MemoryWarning),
);
},
); );
Ok(EventLoop { Ok(EventLoop {
@@ -278,13 +292,19 @@ impl EventLoop {
} }
pub struct EventLoopProxy { pub struct EventLoopProxy {
pub(crate) wake_up: AtomicBool, proxy_wake_up: Arc<AtomicBool>,
source: CFRunLoopSourceRef, source: CFRunLoopSourceRef,
} }
unsafe impl Send for EventLoopProxy {} unsafe impl Send for EventLoopProxy {}
unsafe impl Sync 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 { impl Drop for EventLoopProxy {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
@@ -295,7 +315,7 @@ impl Drop for EventLoopProxy {
} }
impl EventLoopProxy { impl EventLoopProxy {
pub(crate) fn new() -> EventLoopProxy { fn new(proxy_wake_up: Arc<AtomicBool>) -> EventLoopProxy {
unsafe { unsafe {
// just wake up the eventloop // just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {} extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
@@ -319,14 +339,12 @@ impl EventLoopProxy {
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl); CFRunLoopWakeUp(rl);
EventLoopProxy { wake_up: AtomicBool::new(false), source } EventLoopProxy { proxy_wake_up, source }
} }
} }
}
impl EventLoopProxyProvider for EventLoopProxy { pub fn wake_up(&self) {
fn wake_up(&self) { self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
self.wake_up.store(true, AtomicOrdering::Relaxed);
unsafe { unsafe {
// let the main thread know there's a new event // let the main thread know there's a new event
CFRunLoopSourceSignal(self.source); CFRunLoopSourceSignal(self.source);

View File

@@ -10,7 +10,8 @@ mod window;
use std::fmt; use std::fmt;
pub(crate) use self::event_loop::{ 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::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window}; 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::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; 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)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyEventExtra {} pub struct KeyEventExtra {}

View File

@@ -101,20 +101,13 @@ impl Clone for MonitorHandle {
impl hash::Hash for MonitorHandle { impl hash::Hash for MonitorHandle {
fn hash<H: hash::Hasher>(&self, state: &mut H) { fn hash<H: hash::Hasher>(&self, state: &mut H) {
// SAFETY: Only getting the pointer. (self as *const Self).hash(state);
let mtm = unsafe { MainThreadMarker::new_unchecked() };
Retained::as_ptr(self.ui_screen.get(mtm)).hash(state);
} }
} }
impl PartialEq for MonitorHandle { impl PartialEq for MonitorHandle {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
// SAFETY: Only getting the pointer. ptr::eq(self, other)
let mtm = unsafe { MainThreadMarker::new_unchecked() };
ptr::eq(
Retained::as_ptr(self.ui_screen.get(mtm)),
Retained::as_ptr(other.ui_screen.get(mtm)),
)
} }
} }
@@ -128,10 +121,8 @@ impl PartialOrd for MonitorHandle {
impl Ord for MonitorHandle { impl Ord for MonitorHandle {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { fn cmp(&self, other: &Self) -> std::cmp::Ordering {
// SAFETY: Only getting the pointer.
// TODO: Make a better ordering // TODO: Make a better ordering
let mtm = unsafe { MainThreadMarker::new_unchecked() }; (self as *const Self).cmp(&(other as *const Self))
Retained::as_ptr(self.ui_screen.get(mtm)).cmp(&Retained::as_ptr(other.ui_screen.get(mtm)))
} }
} }
@@ -249,27 +240,3 @@ pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
#[allow(deprecated)] #[allow(deprecated)]
UIScreen::screens(mtm).into_iter().map(MonitorHandle::new).collect() UIScreen::screens(mtm).into_iter().map(MonitorHandle::new).collect()
} }
#[cfg(test)]
mod tests {
use objc2_foundation::NSSet;
use super::*;
// Test that UIScreen pointer comparisons are correct.
#[test]
#[allow(deprecated)]
fn screen_comparisons() {
// Test code, doesn't matter that it's not thread safe
let mtm = unsafe { MainThreadMarker::new_unchecked() };
assert!(ptr::eq(&*UIScreen::mainScreen(mtm), &*UIScreen::mainScreen(mtm)));
let main = UIScreen::mainScreen(mtm);
assert!(UIScreen::screens(mtm).iter().any(|screen| ptr::eq(screen, &*main)));
assert!(unsafe {
NSSet::setWithArray(&UIScreen::screens(mtm)).containsObject(&UIScreen::mainScreen(mtm))
});
}
}

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::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet, NSString}; use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet, NSString};
use objc2_ui_kit::{ use objc2_ui_kit::{
UIEvent, UIForceTouchCapability, UIGestureRecognizer, UIGestureRecognizerDelegate, UICoordinateSpace, UIEvent, UIForceTouchCapability, UIGestureRecognizer,
UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer, UIPinchGestureRecognizer, UIGestureRecognizerDelegate, UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer,
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITextInputTraits, UITouch, UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer,
UITouchPhase, UITouchType, UITraitEnvironment, UIView, UITextInputTraits, UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
}; };
use tracing::debug;
use super::app_state::{self, EventWrapper}; use super::app_state::{self, EventWrapper};
use super::window::WinitUIWindow; use super::window::WinitUIWindow;
use super::FingerId;
use crate::dpi::PhysicalPosition; use crate::dpi::PhysicalPosition;
use crate::event::{ use crate::event::{
ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource, TouchPhase, ButtonSource, ElementState, Event, FingerId as RootFingerId, Force, KeyEvent, PointerKind,
WindowEvent, PointerSource, TouchPhase, WindowEvent,
}; };
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey}; use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
use crate::platform_impl::KeyEventExtra; use crate::platform_impl::KeyEventExtra;
@@ -34,9 +34,6 @@ pub struct WinitViewState {
rotation_last_delta: Cell<CGFloat>, rotation_last_delta: Cell<CGFloat>,
pinch_last_delta: Cell<CGFloat>, pinch_last_delta: Cell<CGFloat>,
pan_last_delta: Cell<CGPoint>, pan_last_delta: Cell<CGPoint>,
primary_finger: Cell<Option<FingerId>>,
fingers: Cell<u8>,
} }
declare_class!( declare_class!(
@@ -60,10 +57,10 @@ declare_class!(
let window = self.window().unwrap(); let window = self.window().unwrap();
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::Window { EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}, }),
); );
let _: () = unsafe { msg_send![super(self), drawRect: rect] }; let _: () = unsafe { msg_send![super(self), drawRect: rect] };
} }
@@ -73,21 +70,32 @@ declare_class!(
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
let _: () = unsafe { msg_send![super(self), layoutSubviews] }; 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 = 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( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::Window { EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::SurfaceResized(size), event: WindowEvent::SurfaceResized(size),
}, }),
); );
} }
@@ -116,10 +124,13 @@ declare_class!(
"invalid scale_factor set on UIView", "invalid scale_factor set on UIView",
); );
let scale_factor = scale_factor as f64; 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 { let size = crate::dpi::LogicalSize {
width: frame.size.width as f64, width: screen_frame.size.width as f64,
height: frame.size.height as f64, height: screen_frame.size.height as f64,
}; };
let window_id = window.id(); let window_id = window.id();
app_state::handle_nonuser_events( app_state::handle_nonuser_events(
@@ -131,21 +142,15 @@ declare_class!(
suggested_size: size.to_physical(scale_factor), suggested_size: size.to_physical(scale_factor),
}, },
)) ))
.chain(std::iter::once(EventWrapper::Window { .chain(std::iter::once(EventWrapper::StaticEvent(
Event::WindowEvent {
window_id, window_id,
event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)), event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)),
}, },
)), ))),
); );
} }
#[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:)] #[method(touchesBegan:withEvent:)]
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) { fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
self.handle_touches(touches) self.handle_touches(touches)
@@ -188,17 +193,17 @@ declare_class!(
// Pass -delta so that action is reversed // Pass -delta so that action is reversed
(TouchPhase::Cancelled, -recognizer.scale()) (TouchPhase::Cancelled, -recognizer.scale())
} }
state => panic!("unexpected recognizer state: {state:?}"), state => panic!("unexpected recognizer state: {:?}", state),
}; };
let gesture_event = EventWrapper::Window { let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::PinchGesture { event: WindowEvent::PinchGesture {
device_id: None, device_id: None,
delta: delta as f64, delta: delta as f64,
phase, phase,
}, },
}; });
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event); app_state::handle_nonuser_event(mtm, gesture_event);
@@ -209,12 +214,12 @@ declare_class!(
let window = self.window().unwrap(); let window = self.window().unwrap();
if recognizer.state() == UIGestureRecognizerState::Ended { if recognizer.state() == UIGestureRecognizerState::Ended {
let gesture_event = EventWrapper::Window { let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::DoubleTapGesture { event: WindowEvent::DoubleTapGesture {
device_id: None, device_id: None,
}, },
}; });
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event); app_state::handle_nonuser_event(mtm, gesture_event);
@@ -247,18 +252,18 @@ declare_class!(
// Pass -delta so that action is reversed // Pass -delta so that action is reversed
(TouchPhase::Cancelled, -recognizer.rotation()) (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 // Make delta negative to match macos, convert to degrees
let gesture_event = EventWrapper::Window { let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::RotationGesture { event: WindowEvent::RotationGesture {
device_id: None, device_id: None,
delta: -delta.to_degrees() as _, delta: -delta.to_degrees() as _,
phase, phase,
}, },
}; });
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event); app_state::handle_nonuser_event(mtm, gesture_event);
@@ -298,18 +303,18 @@ declare_class!(
// Pass -delta so that action is reversed // Pass -delta so that action is reversed
(TouchPhase::Cancelled, -last_pan.x, -last_pan.y) (TouchPhase::Cancelled, -last_pan.x, -last_pan.y)
} }
state => panic!("unexpected recognizer state: {state:?}"), state => panic!("unexpected recognizer state: {:?}", state),
}; };
let gesture_event = EventWrapper::Window { let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::PanGesture { event: WindowEvent::PanGesture {
device_id: None, device_id: None,
delta: PhysicalPosition::new(dx as _, dy as _), delta: PhysicalPosition::new(dx as _, dy as _),
phase, phase,
}, },
}; });
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event); app_state::handle_nonuser_event(mtm, gesture_event);
@@ -366,9 +371,6 @@ impl WinitView {
rotation_last_delta: Cell::new(0.0), rotation_last_delta: Cell::new(0.0),
pinch_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 }), 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] }; let this: Retained<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
@@ -511,37 +513,14 @@ impl WinitView {
) )
}; };
let window_id = window.id(); let window_id = window.id();
let finger_id = FingerId::from_raw(touch_id); let finger_id = RootFingerId(FingerId(touch_id));
let ivars = self.ivars();
match phase { match phase {
UITouchPhase::Began => { UITouchPhase::Began => {
let primary = if let UITouchType::Pencil = touch_type { touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
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::Window {
window_id, window_id,
event: WindowEvent::PointerEntered { event: WindowEvent::PointerEntered {
device_id: None, device_id: None,
primary,
position, position,
kind: if let UITouchType::Pencil = touch_type { kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown PointerKind::Unknown
@@ -549,12 +528,11 @@ impl WinitView {
PointerKind::Touch(finger_id) PointerKind::Touch(finger_id)
}, },
}, },
}); }));
touch_events.push(EventWrapper::Window { touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id: None, device_id: None,
primary,
state: ElementState::Pressed, state: ElementState::Pressed,
position, position,
button: if let UITouchType::Pencil = touch_type { button: if let UITouchType::Pencil = touch_type {
@@ -563,47 +541,29 @@ impl WinitView {
ButtonSource::Touch { finger_id, force } ButtonSource::Touch { finger_id, force }
}, },
}, },
}); }));
}, },
UITouchPhase::Moved => { UITouchPhase::Moved => {
let (primary, source) = if let UITouchType::Pencil = touch_type { touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
(true, PointerSource::Unknown)
} else {
(ivars.primary_finger.get().unwrap() == finger_id, PointerSource::Touch {
finger_id,
force,
})
};
touch_events.push(EventWrapper::Window {
window_id, window_id,
event: WindowEvent::PointerMoved { event: WindowEvent::PointerMoved {
device_id: None, device_id: None,
primary,
position, 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 // 2 is UITouchPhase::Stationary and is not expected here
UITouchPhase::Ended | UITouchPhase::Cancelled => { 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 { if let UITouchPhase::Ended = phase {
touch_events.push(EventWrapper::Window { touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id: None, device_id: None,
primary,
state: ElementState::Released, state: ElementState::Released,
position, position,
button: if let UITouchType::Pencil = touch_type { button: if let UITouchType::Pencil = touch_type {
@@ -612,14 +572,13 @@ impl WinitView {
ButtonSource::Touch { finger_id, force } ButtonSource::Touch { finger_id, force }
}, },
}, },
}); }));
} }
touch_events.push(EventWrapper::Window { touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id, window_id,
event: WindowEvent::PointerLeft { event: WindowEvent::PointerLeft {
device_id: None, device_id: None,
primary,
position: Some(position), position: Some(position),
kind: if let UITouchType::Pencil = touch_type { kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown PointerKind::Unknown
@@ -627,7 +586,7 @@ impl WinitView {
PointerKind::Touch(finger_id) PointerKind::Touch(finger_id)
}, },
}, },
}); }));
}, },
_ => panic!("unexpected touch phase: {phase:?}"), _ => panic!("unexpected touch phase: {phase:?}"),
} }
@@ -646,25 +605,29 @@ impl WinitView {
text.to_string().chars().flat_map(|c| { text.to_string().chars().flat_map(|c| {
let text = smol_str::SmolStr::from_iter([c]); let text = smol_str::SmolStr::from_iter([c]);
// Emit both press and release events // Emit both press and release events
[ElementState::Pressed, ElementState::Released].map(|state| EventWrapper::Window { [ElementState::Pressed, ElementState::Released].map(|state| {
window_id, EventWrapper::StaticEvent(Event::WindowEvent {
event: WindowEvent::KeyboardInput { window_id,
device_id: None, event: WindowEvent::KeyboardInput {
event: KeyEvent { event: KeyEvent {
text: if state == ElementState::Pressed { text: if state == ElementState::Pressed {
Some(text.clone()) Some(text.clone())
} else { } else {
None None
},
state,
location: KeyLocation::Standard,
repeat: false,
logical_key: Key::Character(text.clone()),
physical_key: PhysicalKey::Unidentified(
NativeKeyCode::Unidentified,
),
platform_specific: KeyEventExtra {},
}, },
state, is_synthetic: false,
location: KeyLocation::Standard, device_id: None,
repeat: false,
logical_key: Key::Character(text.clone()),
physical_key: PhysicalKey::Unidentified(NativeKeyCode::Unidentified),
platform_specific: KeyEventExtra {},
}, },
is_synthetic: false, })
},
}) })
}), }),
); );
@@ -676,21 +639,23 @@ impl WinitView {
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_events( app_state::handle_nonuser_events(
mtm, mtm,
[ElementState::Pressed, ElementState::Released].map(|state| EventWrapper::Window { [ElementState::Pressed, ElementState::Released].map(|state| {
window_id, EventWrapper::StaticEvent(Event::WindowEvent {
event: WindowEvent::KeyboardInput { window_id,
device_id: None, event: WindowEvent::KeyboardInput {
event: KeyEvent { device_id: None,
state, event: KeyEvent {
logical_key: Key::Named(NamedKey::Backspace), state,
physical_key: PhysicalKey::Code(KeyCode::Backspace), logical_key: Key::Named(NamedKey::Backspace),
platform_specific: KeyEventExtra {}, physical_key: PhysicalKey::Code(KeyCode::Backspace),
repeat: false, platform_specific: KeyEventExtra {},
location: KeyLocation::Standard, repeat: false,
text: None, location: KeyLocation::Standard,
text: None,
},
is_synthetic: false,
}, },
is_synthetic: false, })
},
}), }),
); );
} }

View File

@@ -8,8 +8,8 @@ use objc2_foundation::{
CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker, NSObject, NSObjectProtocol, CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker, NSObject, NSObjectProtocol,
}; };
use objc2_ui_kit::{ use objc2_ui_kit::{
UIApplication, UICoordinateSpace, UIEdgeInsets, UIResponder, UIScreen, UIApplication, UICoordinateSpace, UIResponder, UIScreen, UIScreenOverscanCompensation,
UIScreenOverscanCompensation, UIViewController, UIWindow, UIViewController, UIWindow,
}; };
use tracing::{debug, warn}; use tracing::{debug, warn};
@@ -18,12 +18,9 @@ use super::view::WinitView;
use super::view_controller::WinitViewController; use super::view_controller::WinitViewController;
use super::{app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle}; use super::{app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle};
use crate::cursor::Cursor; use crate::cursor::Cursor;
use crate::dpi::{ use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
Position, Size,
};
use crate::error::{NotSupportedError, RequestError}; use crate::error::{NotSupportedError, RequestError};
use crate::event::WindowEvent; use crate::event::{Event, WindowEvent};
use crate::icon::Icon; use crate::icon::Icon;
use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::monitor::MonitorHandle as CoreMonitorHandle;
use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations}; use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations};
@@ -51,10 +48,10 @@ declare_class!(
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::Window { EventWrapper::StaticEvent(Event::WindowEvent {
window_id: self.id(), window_id: self.id(),
event: WindowEvent::Focused(true), event: WindowEvent::Focused(true),
}, }),
); );
let _: () = unsafe { msg_send![super(self), becomeKeyWindow] }; let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
} }
@@ -64,10 +61,10 @@ declare_class!(
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::Window { EventWrapper::StaticEvent(Event::WindowEvent {
window_id: self.id(), window_id: self.id(),
event: WindowEvent::Focused(false), event: WindowEvent::Focused(false),
}, }),
); );
let _: () = unsafe { msg_send![super(self), resignKeyWindow] }; let _: () = unsafe { msg_send![super(self), resignKeyWindow] };
} }
@@ -161,19 +158,20 @@ impl Inner {
pub fn pre_present_notify(&self) {} pub fn pre_present_notify(&self) {}
pub fn surface_position(&self) -> PhysicalPosition<i32> { pub fn inner_position(&self) -> PhysicalPosition<i32> {
let view_position = self.view.frame().origin; let safe_area = self.safe_area_screen_space();
let position = let position =
unsafe { self.window.convertPoint_fromView(view_position, Some(&self.view)) }; LogicalPosition { x: safe_area.origin.x as f64, y: safe_area.origin.y as f64 };
let position = LogicalPosition::new(position.x, position.y); let scale_factor = self.scale_factor();
position.to_physical(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 screen_frame = self.screen_frame();
let position = let position =
LogicalPosition { x: screen_frame.origin.x as f64, y: screen_frame.origin.y as f64 }; 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) { pub fn set_outer_position(&self, physical_position: Position) {
@@ -189,36 +187,29 @@ impl Inner {
} }
pub fn surface_size(&self) -> PhysicalSize<u32> { pub fn surface_size(&self) -> PhysicalSize<u32> {
let frame = self.view.frame(); let scale_factor = self.scale_factor();
let size = LogicalSize::new(frame.size.width, frame.size.height); let safe_area = self.safe_area_screen_space();
size.to_physical(self.scale_factor()) 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> { pub fn outer_size(&self) -> PhysicalSize<u32> {
let frame = self.window.frame(); let scale_factor = self.scale_factor();
let size = LogicalSize::new(frame.size.width, frame.size.height); let screen_frame = self.screen_frame();
size.to_physical(self.scale_factor()) 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>> { pub fn request_surface_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
Some(self.surface_size()) 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>) { pub fn set_min_surface_size(&self, _dimensions: Option<Size>) {
warn!("`Window::set_min_surface_size` is ignored on iOS") warn!("`Window::set_min_surface_size` is ignored on iOS")
} }
@@ -429,6 +420,7 @@ impl Inner {
self.window.id() self.window.id()
} }
#[cfg(feature = "rwh_06")]
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle { pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
let mut window_handle = rwh_06::UiKitWindowHandle::new({ let mut window_handle = rwh_06::UiKitWindowHandle::new({
let ui_view = Retained::as_ptr(&self.view) as _; 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); let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
window.makeKeyAndVisible(); 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 }; let inner = Inner { window, view_controller, view, gl_or_metal_backed };
Ok(Window { inner: MainThreadBound::new(inner, mtm) }) Ok(Window { inner: MainThreadBound::new(inner, mtm) })
} }
@@ -525,6 +546,7 @@ impl Window {
self.inner.get_on_main(|inner| f(inner)) self.inner.get_on_main(|inner| f(inner))
} }
#[cfg(feature = "rwh_06")]
#[inline] #[inline]
pub(crate) fn raw_window_handle_rwh_06( pub(crate) fn raw_window_handle_rwh_06(
&self, &self,
@@ -536,6 +558,7 @@ impl Window {
} }
} }
#[cfg(feature = "rwh_06")]
#[inline] #[inline]
pub(crate) fn raw_display_handle_rwh_06( pub(crate) fn raw_display_handle_rwh_06(
&self, &self,
@@ -544,6 +567,7 @@ impl Window {
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window { impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?; 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 { impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> { fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_window_handle_rwh_06()?; 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()); self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
} }
fn surface_position(&self) -> PhysicalPosition<i32> { fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
self.maybe_wait_on_main(|delegate| delegate.surface_position()) Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
} }
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> { 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) { fn set_outer_position(&self, position: Position) {
@@ -603,10 +628,6 @@ impl CoreWindow for Window {
self.maybe_wait_on_main(|delegate| delegate.outer_size()) 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>) { fn set_min_surface_size(&self, min_size: Option<Size>) {
self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_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 { fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self self
} }
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle { fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self self
} }
@@ -865,7 +888,7 @@ impl Inner {
impl Inner { impl Inner {
fn screen_frame(&self) -> CGRect { 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 { fn rect_to_screen_space(&self, rect: CGRect) -> CGRect {
@@ -877,6 +900,43 @@ impl Inner {
let screen_space = self.window.screen().coordinateSpace(); let screen_space = self.window.screen().coordinateSpace();
self.window.convertRect_fromCoordinateSpace(rect, &screen_space) 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)] #[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, // 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. // 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 // 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 // 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. // 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, 125 => KeyCode::SuperLeft,
126 => KeyCode::SuperRight, 126 => KeyCode::SuperRight,
127 => KeyCode::ContextMenu, 127 => KeyCode::ContextMenu,
128 => KeyCode::BrowserStop, // 128 => KeyCode::STOP,
129 => KeyCode::Again, // 129 => KeyCode::AGAIN,
130 => KeyCode::Props, // 130 => KeyCode::PROPS,
131 => KeyCode::Undo, // 131 => KeyCode::UNDO,
132 => KeyCode::Select, // FRONT // 132 => KeyCode::FRONT,
133 => KeyCode::Copy, // 133 => KeyCode::COPY,
134 => KeyCode::Open, // 134 => KeyCode::OPEN,
135 => KeyCode::Paste, // 135 => KeyCode::PASTE,
136 => KeyCode::Find, // 136 => KeyCode::FIND,
137 => KeyCode::Cut, // 137 => KeyCode::CUT,
138 => KeyCode::Help, // 138 => KeyCode::HELP,
// 139 => KeyCode::MENU, // 139 => KeyCode::MENU,
140 => KeyCode::LaunchApp2, // CALC // 140 => KeyCode::CALC,
// 141 => KeyCode::SETUP, // 141 => KeyCode::SETUP,
// 142 => KeyCode::SLEEP, // 142 => KeyCode::SLEEP,
143 => KeyCode::WakeUp, // 143 => KeyCode::WAKEUP,
144 => KeyCode::LaunchApp1, // FILE // 144 => KeyCode::FILE,
// 145 => KeyCode::SENDFILE, // 145 => KeyCode::SENDFILE,
// 146 => KeyCode::DELETEFILE, // 146 => KeyCode::DELETEFILE,
// 147 => KeyCode::XFER, // 147 => KeyCode::XFER,
@@ -196,13 +193,13 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 152 => KeyCode::COFFEE, // 152 => KeyCode::COFFEE,
// 153 => KeyCode::ROTATE_DISPLAY, // 153 => KeyCode::ROTATE_DISPLAY,
// 154 => KeyCode::CYCLEWINDOWS, // 154 => KeyCode::CYCLEWINDOWS,
155 => KeyCode::LaunchMail, // 155 => KeyCode::MAIL,
156 => KeyCode::BrowserFavorites, // BOOKMARKS // 156 => KeyCode::BOOKMARKS,
// 157 => KeyCode::COMPUTER, // 157 => KeyCode::COMPUTER,
158 => KeyCode::BrowserBack, // 158 => KeyCode::BACK,
159 => KeyCode::BrowserForward, // 159 => KeyCode::FORWARD,
// 160 => KeyCode::CLOSECD, // 160 => KeyCode::CLOSECD,
161 => KeyCode::Eject, // EJECTCD // 161 => KeyCode::EJECTCD,
// 162 => KeyCode::EJECTCLOSECD, // 162 => KeyCode::EJECTCLOSECD,
163 => KeyCode::MediaTrackNext, 163 => KeyCode::MediaTrackNext,
164 => KeyCode::MediaPlayPause, 164 => KeyCode::MediaPlayPause,
@@ -212,9 +209,9 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 168 => KeyCode::REWIND, // 168 => KeyCode::REWIND,
// 169 => KeyCode::PHONE, // 169 => KeyCode::PHONE,
// 170 => KeyCode::ISO, // 170 => KeyCode::ISO,
171 => KeyCode::MediaSelect, // CONFIG // 171 => KeyCode::CONFIG,
172 => KeyCode::BrowserHome, // 172 => KeyCode::HOMEPAGE,
173 => KeyCode::BrowserRefresh, // 173 => KeyCode::REFRESH,
// 174 => KeyCode::EXIT, // 174 => KeyCode::EXIT,
// 175 => KeyCode::MOVE, // 175 => KeyCode::MOVE,
// 176 => KeyCode::EDIT, // 176 => KeyCode::EDIT,
@@ -253,7 +250,7 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// 214 => KeyCode::QUESTION, // 214 => KeyCode::QUESTION,
// 215 => KeyCode::EMAIL, // 215 => KeyCode::EMAIL,
// 216 => KeyCode::CHAT, // 216 => KeyCode::CHAT,
217 => KeyCode::BrowserSearch, // 217 => KeyCode::SEARCH,
// 218 => KeyCode::CONNECT, // 218 => KeyCode::CONNECT,
// 219 => KeyCode::FINANCE, // 219 => KeyCode::FINANCE,
// 220 => KeyCode::SPORT, // 220 => KeyCode::SPORT,
@@ -422,32 +419,10 @@ pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option<u32> {
KeyCode::SuperLeft => Some(125), KeyCode::SuperLeft => Some(125),
KeyCode::SuperRight => Some(126), KeyCode::SuperRight => Some(126),
KeyCode::ContextMenu => Some(127), 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::MediaTrackNext => Some(163),
KeyCode::MediaPlayPause => Some(164), KeyCode::MediaPlayPause => Some(164),
KeyCode::MediaTrackPrevious => Some(165), KeyCode::MediaTrackPrevious => Some(165),
KeyCode::MediaStop => Some(166), KeyCode::MediaStop => Some(166),
KeyCode::MediaSelect => Some(171),
KeyCode::BrowserHome => Some(172),
KeyCode::BrowserRefresh => Some(173),
KeyCode::F13 => Some(183), KeyCode::F13 => Some(183),
KeyCode::F14 => Some(184), KeyCode::F14 => Some(184),
KeyCode::F15 => Some(185), KeyCode::F15 => Some(185),
@@ -460,7 +435,6 @@ pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option<u32> {
KeyCode::F22 => Some(192), KeyCode::F22 => Some(192),
KeyCode::F23 => Some(193), KeyCode::F23 => Some(193),
KeyCode::F24 => Some(194), KeyCode::F24 => Some(194),
KeyCode::BrowserSearch => Some(217),
_ => None, _ => None,
} }
} }

View File

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

View File

@@ -107,6 +107,24 @@ impl Default for PlatformSpecificWindowAttributes {
pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported>>> = pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported>>> =
Lazy::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new))); 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)] #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum MonitorHandle { pub enum MonitorHandle {
#[cfg(x11_platform)] #[cfg(x11_platform)]
@@ -277,6 +295,14 @@ pub enum EventLoop {
X(x11::EventLoop), X(x11::EventLoop),
} }
#[derive(Clone)]
pub enum EventLoopProxy {
#[cfg(x11_platform)]
X(x11::EventLoopProxy),
#[cfg(wayland_platform)]
Wayland(wayland::EventLoopProxy),
}
impl EventLoop { impl EventLoop {
pub(crate) fn new( pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes, attributes: &PlatformSpecificEventLoopAttributes,
@@ -396,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` /// Returns the minimum `Option<Duration>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use /// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`) /// `Option::min`)

View File

@@ -16,10 +16,7 @@ use crate::cursor::OnlyCursorImage;
use crate::dpi::LogicalSize; use crate::dpi::LogicalSize;
use crate::error::{EventLoopError, OsError, RequestError}; use crate::error::{EventLoopError, OsError, RequestError};
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent}; use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
use crate::event_loop::{ use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::platform::pump_events::PumpStatus; use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::platform::min_timeout; use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::PlatformCustomCursor; use crate::platform_impl::PlatformCustomCursor;
@@ -28,13 +25,12 @@ use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme}
mod proxy; mod proxy;
pub mod sink; pub mod sink;
use proxy::EventLoopProxy; pub use proxy::EventLoopProxy;
use sink::EventSink; use sink::EventSink;
use super::state::{WindowCompositorUpdate, WinitState}; use super::state::{WindowCompositorUpdate, WinitState};
use super::window::state::FrameCallbackState; use super::window::state::FrameCallbackState;
use super::{logical_to_physical_rounded, WindowId}; use super::{logical_to_physical_rounded, WindowId};
pub use crate::event_loop::EventLoopProxy as CoreEventLoopProxy;
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>; type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
@@ -52,7 +48,7 @@ pub struct EventLoop {
wayland_dispatcher: WaylandDispatcher, wayland_dispatcher: WaylandDispatcher,
/// Connection to the wayland server. /// Connection to the wayland server.
handle: Arc<OwnedDisplayHandle>, connection: Connection,
/// Event loop window target. /// Event loop window target.
active_event_loop: ActiveEventLoop, active_event_loop: ActiveEventLoop,
@@ -120,12 +116,11 @@ impl EventLoop {
}) })
.map_err(|err| os_error!(err))?; .map_err(|err| os_error!(err))?;
let handle = Arc::new(OwnedDisplayHandle::new(connection));
let active_event_loop = ActiveEventLoop { let active_event_loop = ActiveEventLoop {
handle: handle.clone(), connection: connection.clone(),
wayland_dispatcher: wayland_dispatcher.clone(), wayland_dispatcher: wayland_dispatcher.clone(),
event_loop_awakener, event_loop_awakener,
event_loop_proxy: EventLoopProxy::new(ping).into(), event_loop_proxy: EventLoopProxy::new(ping),
queue_handle, queue_handle,
control_flow: Cell::new(ControlFlow::default()), control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None), exit: Cell::new(None),
@@ -137,7 +132,7 @@ impl EventLoop {
compositor_updates: Vec::new(), compositor_updates: Vec::new(),
buffer_sink: EventSink::default(), buffer_sink: EventSink::default(),
window_ids: Vec::new(), window_ids: Vec::new(),
handle, connection,
wayland_dispatcher, wayland_dispatcher,
event_loop, event_loop,
active_event_loop, active_event_loop,
@@ -231,7 +226,7 @@ impl EventLoop {
// //
// Checking for flush error is essential to perform an exit with error, since // Checking for flush error is essential to perform an exit with error, since
// once we have a protocol error, we could get stuck retrying... // 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); self.set_exit_code(1);
return; return;
} }
@@ -544,7 +539,7 @@ impl AsRawFd for EventLoop {
pub struct ActiveEventLoop { pub struct ActiveEventLoop {
/// Event loop proxy /// Event loop proxy
event_loop_proxy: CoreEventLoopProxy, event_loop_proxy: EventLoopProxy,
/// The event loop wakeup source. /// The event loop wakeup source.
pub event_loop_awakener: calloop::ping::Ping, pub event_loop_awakener: calloop::ping::Ping,
@@ -565,13 +560,17 @@ pub struct ActiveEventLoop {
/// Dispatcher of Wayland events. /// Dispatcher of Wayland events.
pub wayland_dispatcher: WaylandDispatcher, pub wayland_dispatcher: WaylandDispatcher,
/// Handle for the underlying event loop. /// Connection to the wayland server.
pub handle: Arc<OwnedDisplayHandle>, pub connection: Connection,
} }
impl RootActiveEventLoop for ActiveEventLoop { impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy { fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
self.event_loop_proxy.clone() 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) { fn set_control_flow(&self, control_flow: ControlFlow) {
@@ -632,10 +631,13 @@ impl RootActiveEventLoop for ActiveEventLoop {
None None
} }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle { fn owned_display_handle(&self) -> crate::event_loop::OwnedDisplayHandle {
CoreOwnedDisplayHandle::new(self.handle.clone()) 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 { fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self self
} }
@@ -655,23 +657,8 @@ impl ActiveEventLoop {
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop { 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> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
use sctk::reexports::client::Proxy; use sctk::reexports::client::Proxy;

View File

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

View File

@@ -1,6 +1,6 @@
//! Winit's Wayland backend. //! Winit's Wayland backend.
pub use event_loop::{ActiveEventLoop, EventLoop}; pub use event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
pub use output::{MonitorHandle, VideoModeHandle}; pub use output::{MonitorHandle, VideoModeHandle};
use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::Proxy; use sctk::reexports::client::Proxy;
@@ -17,6 +17,16 @@ mod state;
mod types; mod types;
mod window; 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. /// Get the WindowId out of the surface.
#[inline] #[inline]
fn make_wid(surface: &WlSurface) -> WindowId { 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. /// The mapping from touched points to the surfaces they're present.
touch_map: AHashMap<i32, TouchPoint>, touch_map: AHashMap<i32, TouchPoint>,
/// Id of the first touch event.
first_touch_id: Option<i32>,
/// The text input bound on the seat. /// The text input bound on the seat.
text_input: Option<Arc<ZwpTextInputV3>>, text_input: Option<Arc<ZwpTextInputV3>>,

View File

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

View File

@@ -8,9 +8,9 @@ use sctk::seat::touch::{TouchData, TouchHandler};
use tracing::warn; use tracing::warn;
use crate::dpi::LogicalPosition; use crate::dpi::LogicalPosition;
use crate::event::{ButtonSource, ElementState, FingerId, PointerKind, PointerSource, WindowEvent}; use crate::event::{ButtonSource, ElementState, PointerKind, PointerSource, WindowEvent};
use crate::platform_impl::wayland;
use crate::platform_impl::wayland::state::WinitState; use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, FingerId};
impl TouchHandler for WinitState { impl TouchHandler for WinitState {
fn down( fn down(
@@ -40,20 +40,15 @@ impl TouchHandler for WinitState {
// Update the state of the point. // Update the state of the point.
let location = LogicalPosition::<f64>::from(position); 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 }); seat_state.touch_map.insert(id, TouchPoint { surface, location });
let position = location.to_physical(scale_factor); 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( self.events_sink.push_window_event(
WindowEvent::PointerEntered { WindowEvent::PointerEntered {
device_id: None, device_id: None,
primary,
position, position,
kind: PointerKind::Touch(finger_id), kind: PointerKind::Touch(finger_id),
}, },
@@ -62,7 +57,6 @@ impl TouchHandler for WinitState {
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::PointerButton { WindowEvent::PointerButton {
device_id: None, device_id: None,
primary,
state: ElementState::Pressed, state: ElementState::Pressed,
position, position,
button: ButtonSource::Touch { finger_id, force: None }, button: ButtonSource::Touch { finger_id, force: None },
@@ -94,15 +88,6 @@ impl TouchHandler for WinitState {
None => return, 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 window_id = wayland::make_wid(&touch_point.surface);
let scale_factor = match self.windows.get_mut().get(&window_id) { let scale_factor = match self.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().scale_factor(), 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 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( self.events_sink.push_window_event(
WindowEvent::PointerButton { WindowEvent::PointerButton {
device_id: None, device_id: None,
primary,
state: ElementState::Released, state: ElementState::Released,
position, position,
button: ButtonSource::Touch { finger_id, force: None }, button: ButtonSource::Touch { finger_id, force: None },
@@ -125,7 +110,6 @@ impl TouchHandler for WinitState {
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::PointerLeft { WindowEvent::PointerLeft {
device_id: None, device_id: None,
primary,
position: Some(position), position: Some(position),
kind: PointerKind::Touch(finger_id), kind: PointerKind::Touch(finger_id),
}, },
@@ -156,8 +140,6 @@ impl TouchHandler for WinitState {
None => return, None => return,
}; };
let primary = seat_state.first_touch_id == Some(id);
let window_id = wayland::make_wid(&touch_point.surface); let window_id = wayland::make_wid(&touch_point.surface);
let scale_factor = match self.windows.get_mut().get(&window_id) { let scale_factor = match self.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().scale_factor(), Some(window) => window.lock().unwrap().scale_factor(),
@@ -169,10 +151,11 @@ impl TouchHandler for WinitState {
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::PointerMoved { WindowEvent::PointerMoved {
device_id: None, device_id: None,
primary,
position: touch_point.location.to_physical(scale_factor), position: touch_point.location.to_physical(scale_factor),
source: PointerSource::Touch { 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, force: None,
}, },
}, },
@@ -196,21 +179,19 @@ impl TouchHandler for WinitState {
None => return, None => return,
}; };
let primary = seat_state.first_touch_id == Some(id);
let position = touch_point.location.to_physical(scale_factor); let position = touch_point.location.to_physical(scale_factor);
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::PointerLeft { WindowEvent::PointerLeft {
device_id: None, device_id: None,
primary,
position: Some(position), 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, window_id,
); );
} }
seat_state.first_touch_id = None;
} }
fn shape( fn shape(

View File

@@ -17,7 +17,7 @@ use super::output::MonitorHandle;
use super::state::WinitState; use super::state::WinitState;
use super::types::xdg_activation::XdgActivationTokenData; use super::types::xdg_activation::XdgActivationTokenData;
use super::ActiveEventLoop; 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::error::{NotSupportedError, RequestError};
use crate::event::{Ime, WindowEvent}; use crate::event::{Ime, WindowEvent};
use crate::event_loop::AsyncRequestSerial; use crate::event_loop::AsyncRequestSerial;
@@ -87,7 +87,7 @@ impl Window {
let compositor = state.compositor_state.clone(); let compositor = state.compositor_state.clone();
let xdg_activation = let xdg_activation =
state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone()); 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()); 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); state.xdg_shell.create_window(surface.clone(), default_decorations, &queue_handle);
let mut window_state = WindowState::new( let mut window_state = WindowState::new(
event_loop_window_target.handle.clone(), event_loop_window_target.connection.clone(),
&event_loop_window_target.queue_handle, &event_loop_window_target.queue_handle,
&state, &state,
size, size,
@@ -248,6 +248,7 @@ impl Drop for Window {
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window { impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> { fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::WaylandWindowHandle::new({ 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 { impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::WaylandDisplayHandle::new({ let raw = rwh_06::WaylandDisplayHandle::new({
@@ -303,8 +305,9 @@ impl CoreWindow for Window {
crate::platform_impl::common::xkb::reset_dead_keys() crate::platform_impl::common::xkb::reset_dead_keys()
} }
fn surface_position(&self) -> PhysicalPosition<i32> { fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
(0, 0).into() Err(NotSupportedError::new("window position information is not available on Wayland")
.into())
} }
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> { 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) 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>) { fn set_min_surface_size(&self, min_size: Option<Size>) {
let scale_factor = self.scale_factor(); let scale_factor = self.scale_factor();
let min_size = min_size.map(|size| size.to_logical(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. /// Get the raw-window-handle v0.6 display handle.
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle { fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self self
} }
/// Get the raw-window-handle v0.6 window handle. /// Get the raw-window-handle v0.6 window handle.
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle { fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self 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_seat::WlSeat;
use sctk::reexports::client::protocol::wl_shm::WlShm; use sctk::reexports::client::protocol::wl_shm::WlShm;
use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{Proxy, QueueHandle}; use sctk::reexports::client::{Connection, Proxy, QueueHandle};
use sctk::reexports::csd_frame::{ use sctk::reexports::csd_frame::{
DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowState as XdgWindowState, 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::cursor::CustomCursor as RootCustomCursor;
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size}; use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
use crate::error::{NotSupportedError, RequestError}; 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::logical_to_physical_rounded;
use crate::platform_impl::wayland::seat::{ use crate::platform_impl::wayland::seat::{
PointerConstraintsState, WinitPointerData, WinitPointerDataExt, ZwpTextInputV3Ext, 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`]. /// The state of the window which is being updated from the [`WinitState`].
pub struct WindowState { pub struct WindowState {
/// The connection to Wayland server. /// The connection to Wayland server.
pub handle: Arc<OwnedDisplayHandle>, pub connection: Connection,
/// The `Shm` to set cursor. /// The `Shm` to set cursor.
pub shm: WlShm, pub shm: WlShm,
@@ -162,7 +161,7 @@ pub struct WindowState {
impl WindowState { impl WindowState {
/// Create new window state. /// Create new window state.
pub fn new( pub fn new(
handle: Arc<OwnedDisplayHandle>, connection: Connection,
queue_handle: &QueueHandle<WinitState>, queue_handle: &QueueHandle<WinitState>,
winit_state: &WinitState, winit_state: &WinitState,
initial_size: Size, initial_size: Size,
@@ -184,7 +183,7 @@ impl WindowState {
blur: None, blur: None,
blur_manager: winit_state.kwin_blur_manager.clone(), blur_manager: winit_state.kwin_blur_manager.clone(),
compositor, compositor,
handle, connection,
csd_fails: false, csd_fails: false,
cursor_grab_mode: GrabState::new(), cursor_grab_mode: GrabState::new(),
selected_cursor: Default::default(), selected_cursor: Default::default(),
@@ -694,7 +693,7 @@ impl WindowState {
} }
self.apply_on_pointer(|pointer, _| { 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); 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>, 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 { fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.buffer.extend_from_slice(s.as_bytes()); self.buffer.extend_from_slice(s.as_bytes());
Ok(()) Ok(())
} }
} }
write!(Writer { buffer }, "{display}").unwrap(); write!(Writer { buffer }, "{}", display).unwrap();
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -22,9 +22,8 @@ use xkbcommon_dl::xkb_mod_mask_t;
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::event::{ use crate::event::{
ButtonSource, DeviceEvent, DeviceId, ElementState, Event, FingerId, Ime, MouseButton, ButtonSource, DeviceEvent, DeviceId, ElementState, Event, Ime, MouseButton, MouseScrollDelta,
MouseScrollDelta, PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, TouchPhase, PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent,
WindowEvent,
}; };
use crate::keyboard::ModifiersState; use crate::keyboard::ModifiersState;
use crate::platform_impl::common::xkb::{self, XkbState}; 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::atoms::*;
use crate::platform_impl::x11::util::cookie::GenericEventCookie; use crate::platform_impl::x11::util::cookie::GenericEventCookie;
use crate::platform_impl::x11::{ 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, ScrollOrientation, UnownedWindow, WindowId,
}; };
@@ -1035,14 +1034,12 @@ impl EventProcessor {
let event = match event.detail as u32 { let event = match event.detail as u32 {
xlib::Button1 => WindowEvent::PointerButton { xlib::Button1 => WindowEvent::PointerButton {
device_id, device_id,
primary: true,
state, state,
position, position,
button: MouseButton::Left.into(), button: MouseButton::Left.into(),
}, },
xlib::Button2 => WindowEvent::PointerButton { xlib::Button2 => WindowEvent::PointerButton {
device_id, device_id,
primary: true,
state, state,
position, position,
button: MouseButton::Middle.into(), button: MouseButton::Middle.into(),
@@ -1050,7 +1047,6 @@ impl EventProcessor {
xlib::Button3 => WindowEvent::PointerButton { xlib::Button3 => WindowEvent::PointerButton {
device_id, device_id,
primary: true,
state, state,
position, position,
button: MouseButton::Right.into(), button: MouseButton::Right.into(),
@@ -1073,7 +1069,6 @@ impl EventProcessor {
}, },
8 => WindowEvent::PointerButton { 8 => WindowEvent::PointerButton {
device_id, device_id,
primary: true,
state, state,
position, position,
button: MouseButton::Back.into(), button: MouseButton::Back.into(),
@@ -1081,14 +1076,12 @@ impl EventProcessor {
9 => WindowEvent::PointerButton { 9 => WindowEvent::PointerButton {
device_id, device_id,
primary: true,
state, state,
position, position,
button: MouseButton::Forward.into(), button: MouseButton::Forward.into(),
}, },
x => WindowEvent::PointerButton { x => WindowEvent::PointerButton {
device_id, device_id,
primary: true,
state, state,
position, position,
button: MouseButton::Other(x as u16).into(), button: MouseButton::Other(x as u16).into(),
@@ -1123,7 +1116,6 @@ impl EventProcessor {
window_id, window_id,
event: WindowEvent::PointerMoved { event: WindowEvent::PointerMoved {
device_id, device_id,
primary: true,
position, position,
source: PointerSource::Mouse, source: PointerSource::Mouse,
}, },
@@ -1213,7 +1205,6 @@ impl EventProcessor {
window_id, window_id,
event: WindowEvent::PointerEntered { event: WindowEvent::PointerEntered {
device_id, device_id,
primary: true,
position, position,
kind: PointerKind::Mouse, kind: PointerKind::Mouse,
}, },
@@ -1238,7 +1229,6 @@ impl EventProcessor {
window_id: mkwid(window), window_id: mkwid(window),
event: WindowEvent::PointerLeft { event: WindowEvent::PointerLeft {
device_id: Some(mkdid(event.deviceid as xinput::DeviceId)), device_id: Some(mkdid(event.deviceid as xinput::DeviceId)),
primary: true,
position: Some(PhysicalPosition::new(event.event_x, event.event_y)), position: Some(PhysicalPosition::new(event.event_x, event.event_y)),
kind: PointerKind::Mouse, kind: PointerKind::Mouse,
}, },
@@ -1299,12 +1289,7 @@ impl EventProcessor {
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id, window_id,
event: WindowEvent::PointerMoved { event: WindowEvent::PointerMoved { device_id, position, source: PointerSource::Mouse },
device_id,
primary: true,
position,
source: PointerSource::Mouse,
},
}; };
callback(&self.target, event); callback(&self.target, event);
} }
@@ -1375,14 +1360,11 @@ impl EventProcessor {
// Mouse cursor position changes when touch events are received. // Mouse cursor position changes when touch events are received.
// Only the first concurrently active touch ID moves the mouse cursor. // Only the first concurrently active touch ID moves the mouse cursor.
let is_first_touch = if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase) {
is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase);
if is_first_touch {
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id, window_id,
event: WindowEvent::PointerMoved { event: WindowEvent::PointerMoved {
device_id: None, device_id: None,
primary: true,
position: position.cast(), position: position.cast(),
source: PointerSource::Mouse, source: PointerSource::Mouse,
}, },
@@ -1391,7 +1373,7 @@ impl EventProcessor {
} }
let device_id = Some(mkdid(xev.deviceid as xinput::DeviceId)); 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 { match phase {
xinput2::XI_TouchBegin => { xinput2::XI_TouchBegin => {
@@ -1399,7 +1381,6 @@ impl EventProcessor {
window_id, window_id,
event: WindowEvent::PointerEntered { event: WindowEvent::PointerEntered {
device_id, device_id,
primary: is_first_touch,
position, position,
kind: PointerKind::Touch(finger_id), kind: PointerKind::Touch(finger_id),
}, },
@@ -1409,7 +1390,6 @@ impl EventProcessor {
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id, device_id,
primary: is_first_touch,
state: ElementState::Pressed, state: ElementState::Pressed,
position, position,
button: ButtonSource::Touch { finger_id, force: None }, button: ButtonSource::Touch { finger_id, force: None },
@@ -1422,7 +1402,6 @@ impl EventProcessor {
window_id, window_id,
event: WindowEvent::PointerMoved { event: WindowEvent::PointerMoved {
device_id, device_id,
primary: is_first_touch,
position, position,
source: PointerSource::Touch { finger_id, force: None }, source: PointerSource::Touch { finger_id, force: None },
}, },
@@ -1434,7 +1413,6 @@ impl EventProcessor {
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id, device_id,
primary: is_first_touch,
state: ElementState::Released, state: ElementState::Released,
position, position,
button: ButtonSource::Touch { finger_id, force: None }, button: ButtonSource::Touch { finger_id, force: None },
@@ -1445,7 +1423,6 @@ impl EventProcessor {
window_id, window_id,
event: WindowEvent::PointerLeft { event: WindowEvent::PointerLeft {
device_id, device_id,
primary: is_first_touch,
position: Some(position), position: Some(position),
kind: PointerKind::Touch(finger_id), 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::{DeviceId, Event, StartCause, WindowEvent};
use crate::event_loop::{ use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, OwnedDisplayHandle as RootOwnedDisplayHandle,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
}; };
use crate::platform::pump_events::PumpStatus; use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::common::xkb::Context; use crate::platform_impl::common::xkb::Context;
use crate::platform_impl::platform::min_timeout; use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::x11::window::Window; use crate::platform_impl::x11::window::Window;
use crate::platform_impl::PlatformCustomCursor; use crate::platform_impl::{OwnedDisplayHandle, PlatformCustomCursor};
use crate::window::{ use crate::window::{
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow, CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
WindowAttributes, WindowId, WindowAttributes, WindowId,
@@ -140,7 +139,7 @@ pub struct ActiveEventLoop {
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>, windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
redraw_sender: WakeSender<WindowId>, redraw_sender: WakeSender<WindowId>,
activation_sender: WakeSender<ActivationToken>, activation_sender: WakeSender<ActivationToken>,
event_loop_proxy: CoreEventLoopProxy, event_loop_proxy: EventLoopProxy,
device_events: Cell<DeviceEvents>, device_events: Cell<DeviceEvents>,
} }
@@ -307,7 +306,7 @@ impl EventLoop {
sender: activation_token_sender, // not used again so no clone sender: activation_token_sender, // not used again so no clone
waker: waker.clone(), waker: waker.clone(),
}, },
event_loop_proxy: event_loop_proxy.into(), event_loop_proxy,
device_events: Default::default(), device_events: Default::default(),
}; };
@@ -648,6 +647,21 @@ impl ActiveEventLoop {
.expect_then_ignore_error("Failed to update device event filter"); .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) { pub(crate) fn clear_exit(&self) {
self.exit.set(None) self.exit.set(None)
} }
@@ -662,8 +676,12 @@ impl ActiveEventLoop {
} }
impl RootActiveEventLoop for ActiveEventLoop { impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy { fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
self.event_loop_proxy.clone() crate::event_loop::EventLoopProxy {
event_loop_proxy: crate::platform_impl::EventLoopProxy::X(
self.event_loop_proxy.clone(),
),
}
} }
fn create_window( fn create_window(
@@ -725,18 +743,28 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.exit.get().is_some() self.exit.get().is_some()
} }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle { fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(self.x_connection().clone()) let handle = OwnedDisplayHandle::X(self.x_connection().clone());
RootOwnedDisplayHandle { platform: handle }
} }
#[cfg(feature = "rwh_06")]
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle { fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self self
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop { impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { 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();
} }
} }
@@ -762,14 +790,14 @@ impl<'a> DeviceInfo<'a> {
} }
} }
impl Drop for DeviceInfo<'_> { impl<'a> Drop for DeviceInfo<'a> {
fn drop(&mut self) { fn drop(&mut self) {
assert!(!self.info.is_null()); assert!(!self.info.is_null());
unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) }; unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
} }
} }
impl Deref for DeviceInfo<'_> { impl<'a> Deref for DeviceInfo<'a> {
type Target = [ffi::XIDeviceInfo]; type Target = [ffi::XIDeviceInfo];
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@@ -777,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)] #[derive(Clone)]
pub struct EventLoopProxy { pub struct EventLoopProxy {
ping: Ping, ping: Ping,
} }
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.ping.ping();
}
}
impl EventLoopProxy { impl EventLoopProxy {
fn new(ping: Ping) -> Self { fn new(ping: Ping) -> Self {
Self { ping } Self { ping }
} }
} }
impl From<EventLoopProxy> for CoreEventLoopProxy {
fn from(value: EventLoopProxy) -> Self {
CoreEventLoopProxy::new(Arc::new(value))
}
}
/// Generic sum error type for X11 errors. /// Generic sum error type for X11 errors.
#[derive(Debug)] #[derive(Debug)]
pub enum X11Error { pub enum X11Error {
@@ -843,24 +870,24 @@ pub enum X11Error {
impl fmt::Display for X11Error { impl fmt::Display for X11Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
X11Error::Xlib(e) => write!(f, "Xlib error: {e}"), X11Error::Xlib(e) => write!(f, "Xlib error: {}", e),
X11Error::Connect(e) => write!(f, "X11 connection error: {e}"), X11Error::Connect(e) => write!(f, "X11 connection error: {}", e),
X11Error::Connection(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::XidsExhausted(e) => write!(f, "XID range exhausted: {}", e),
X11Error::GetProperty(e) => write!(f, "Failed to get X property {e}"), X11Error::GetProperty(e) => write!(f, "Failed to get X property {}", e),
X11Error::X11(e) => write!(f, "X11 error: {e:?}"), X11Error::X11(e) => write!(f, "X11 error: {:?}", e),
X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {s}"), X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {}", s),
X11Error::InvalidActivationToken(s) => write!( X11Error::InvalidActivationToken(s) => write!(
f, f,
"Invalid activation token: {}", "Invalid activation token: {}",
std::str::from_utf8(s).unwrap_or("<invalid utf8>") 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) => { 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) => { X11Error::XsettingsParse(err) => {
write!(f, "Failed to parse xsettings: {err:?}") write!(f, "Failed to parse xsettings: {:?}", err)
}, },
X11Error::NoArgb32Format => { X11Error::NoArgb32Format => {
f.write_str("winit only supports X11 displays with ARGB32 picture formats") f.write_str("winit only supports X11 displays with ARGB32 picture formats")
@@ -954,7 +981,7 @@ trait CookieResultExt {
fn expect_then_ignore_error(self, msg: &str); 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) { fn expect_then_ignore_error(self, msg: &str) {
self.expect(msg).ignore_error() self.expect(msg).ignore_error()
} }
@@ -967,6 +994,10 @@ fn mkdid(w: xinput::DeviceId) -> DeviceId {
DeviceId::from_raw(w as i64) 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)] #[derive(Debug)]
pub struct Device { pub struct Device {
_name: String, _name: String,

View File

@@ -280,7 +280,7 @@ impl XConnection {
let info = self let info = self
.xcb_connection() .xcb_connection()
.extension_information(randr::X11_EXTENSION_NAME)? .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. // Select input data.
let event_mask = let event_mask =

View File

@@ -67,20 +67,15 @@ pub struct FrameExtentsHeuristic {
} }
impl 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::*; use self::FrameExtentsHeuristicPath::*;
if self.heuristic_path != UnsupportedBordered { 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 { } 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) { pub fn surface_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
( (
width.saturating_add( 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; type Target = T;
fn deref(&self) -> &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 { fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr } unsafe { &mut *self.ptr }
} }
} }
impl<T> Drop for XSmartPointer<'_, T> { impl<'a, T> Drop for XSmartPointer<'a, T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
(self.xconn.xlib.XFree)(self.ptr as *mut _); (self.xconn.xlib.XFree)(self.ptr as *mut _);

View File

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

View File

@@ -1,11 +1,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::ffi::c_int;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard}; use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard};
use std::{fmt, ptr}; use std::{fmt, ptr};
use rwh_06::HasDisplayHandle;
use x11rb::connection::Connection; use x11rb::connection::Connection;
use x11rb::protocol::randr::ConnectionExt as _; use x11rb::protocol::randr::ConnectionExt as _;
use x11rb::protocol::render; use x11rb::protocol::render;
@@ -64,13 +62,6 @@ pub struct XConnection {
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, xproto::Cursor>>, 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 Send for XConnection {}
unsafe impl Sync 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> { fn new_xsettings_screen(xcb: &XCBConnection, default_screen: usize) -> Option<xproto::Atom> {
// Fetch the _XSETTINGS_S[screen number] atom. // Fetch the _XSETTINGS_S[screen number] atom.
let xsettings_screen = xcb 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()? .ok()?
.reply() .reply()
.ok()? .ok()?
@@ -293,19 +284,6 @@ impl XConnection {
self.xcb_connection().setup().image_byte_order != endian 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 { impl fmt::Debug for XConnection {

View File

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

View File

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

View File

@@ -1,10 +1,9 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use super::event_loop::EventLoopProxy; use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, TimeSocket, WindowProperties};
use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, WindowProperties};
use crate::cursor::Cursor; 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::error::{NotSupportedError, RequestError};
use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::monitor::MonitorHandle as CoreMonitorHandle;
use crate::window::{self, Fullscreen, ImePurpose, Window as CoreWindow, WindowId}; use crate::window::{self, Fullscreen, ImePurpose, Window as CoreWindow, WindowId};
@@ -24,7 +23,7 @@ pub struct Window {
window_socket: Arc<RedoxSocket>, window_socket: Arc<RedoxSocket>,
redraws: Arc<Mutex<VecDeque<WindowId>>>, redraws: Arc<Mutex<VecDeque<WindowId>>>,
destroys: Arc<Mutex<VecDeque<WindowId>>>, destroys: Arc<Mutex<VecDeque<WindowId>>>,
event_loop_proxy: Arc<EventLoopProxy>, wake_socket: Arc<TimeSocket>,
} }
impl Window { impl Window {
@@ -114,13 +113,13 @@ impl Window {
creates.push_back(window_socket.clone()); creates.push_back(window_socket.clone());
} }
el.event_loop_proxy.wake_socket.wake().unwrap(); el.wake_socket.wake().unwrap();
Ok(Self { Ok(Self {
window_socket, window_socket,
redraws: el.redraws.clone(), redraws: el.redraws.clone(),
destroys: el.destroys.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(()) Ok(())
} }
#[cfg(feature = "rwh_06")]
#[inline] #[inline]
fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> { fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
let handle = rwh_06::OrbitalWindowHandle::new({ let handle = rwh_06::OrbitalWindowHandle::new({
@@ -147,6 +147,7 @@ impl Window {
Ok(rwh_06::RawWindowHandle::Orbital(handle)) Ok(rwh_06::RawWindowHandle::Orbital(handle))
} }
#[cfg(feature = "rwh_06")]
#[inline] #[inline]
fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> { fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new())) Ok(rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new()))
@@ -185,7 +186,7 @@ impl CoreWindow for Window {
if !redraws.contains(&window_id) { if !redraws.contains(&window_id) {
redraws.push_back(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] #[inline]
fn surface_position(&self) -> PhysicalPosition<i32> { fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
// TODO: adjust for window decorations
(0, 0).into()
}
#[inline]
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
let mut buf: [u8; 4096] = [0; 4096]; let mut buf: [u8; 4096] = [0; 4096];
let path = self.window_socket.fpath(&mut buf).expect("failed to read properties"); let path = self.window_socket.fpath(&mut buf).expect("failed to read properties");
let properties = WindowProperties::new(path); let properties = WindowProperties::new(path);
Ok((properties.x, properties.y).into()) Ok((properties.x, properties.y).into())
} }
#[inline]
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
// TODO: adjust for window decorations
self.inner_position()
}
#[inline] #[inline]
fn set_outer_position(&self, position: Position) { fn set_outer_position(&self, position: Position) {
// TODO: adjust for window decorations // TODO: adjust for window decorations
@@ -239,10 +240,6 @@ impl CoreWindow for Window {
self.surface_size() self.surface_size()
} }
fn safe_area(&self) -> PhysicalInsets<u32> {
PhysicalInsets::new(0, 0, 0, 0)
}
#[inline] #[inline]
fn set_min_surface_size(&self, _: Option<Size>) {} fn set_min_surface_size(&self, _: Option<Size>) {}
@@ -414,7 +411,7 @@ impl CoreWindow for Window {
window::ResizeDirection::West => "L", window::ResizeDirection::West => "L",
}; };
self.window_socket self.window_socket
.write(format!("D,{arg}").as_bytes()) .write(format!("D,{}", arg).as_bytes())
.map_err(|err| os_error!(format!("{err}")))?; .map_err(|err| os_error!(format!("{err}")))?;
Ok(()) Ok(())
} }
@@ -450,15 +447,18 @@ impl CoreWindow for Window {
fn set_content_protected(&self, _protected: bool) {} fn set_content_protected(&self, _protected: bool) {}
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle { fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self self
} }
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle { fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self self
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window { impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> { fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_window_handle_rwh_06()?; 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 { impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?; let raw = self.raw_display_handle_rwh_06()?;
@@ -480,6 +481,6 @@ impl Drop for Window {
destroys.push_back(self.id()); 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 concurrent_queue;
mod dispatcher; mod dispatcher;
mod notifier; mod notifier;
mod waker;
mod wrapper; mod wrapper;
pub(crate) use atomic_waker::AtomicWaker; use atomic_waker::AtomicWaker;
use concurrent_queue::{ConcurrentQueue, PushError}; use concurrent_queue::{ConcurrentQueue, PushError};
pub use self::abortable::{AbortHandle, Abortable, DropAbortHandle}; pub use self::abortable::{AbortHandle, Abortable, DropAbortHandle};
pub use self::channel::{channel, Receiver, Sender}; pub use self::channel::{channel, Receiver, Sender};
pub use self::dispatcher::{DispatchRunner, Dispatcher}; pub use self::dispatcher::{DispatchRunner, Dispatcher};
pub use self::notifier::{Notified, Notifier}; 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> { pub(crate) fn mkdid(pointer_id: i32) -> Option<DeviceId> {
if let Ok(pointer_id) = u32::try_from(pointer_id) { if let Ok(pointer_id) = u32::try_from(pointer_id) {
@@ -10,3 +10,30 @@ pub(crate) fn mkdid(pointer_id: i32) -> Option<DeviceId> {
None 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 state;
mod window_target; mod window_target;
pub(crate) use window_target::ActiveEventLoop; pub(crate) use proxy::EventLoopProxy;
pub(crate) use window_target::{ActiveEventLoop, OwnedDisplayHandle};
pub struct EventLoop { pub struct EventLoop {
elw: ActiveEventLoop, elw: ActiveEventLoop,

View File

@@ -1,101 +1,17 @@
use std::future; use super::runner::WeakShared;
use std::sync::atomic::{AtomicBool, Ordering}; use crate::platform_impl::platform::r#async::Waker;
use std::sync::Arc;
use std::task::Poll;
use super::super::main_thread::MainThreadMarker; #[derive(Clone)]
use crate::event_loop::EventLoopProxyProvider; pub struct EventLoopProxy {
use crate::platform_impl::web::event_loop::runner::WeakShared; runner: Waker<WeakShared>,
use crate::platform_impl::web::r#async::{AtomicWaker, Wrapper};
pub struct EventLoopProxy(Wrapper<WeakShared, Arc<State>, ()>);
struct State {
awoken: AtomicBool,
waker: AtomicWaker,
closed: AtomicBool,
} }
impl EventLoopProxy { impl EventLoopProxy {
pub fn new(main_thread: MainThreadMarker, runner: WeakShared) -> Self { pub fn new(runner: Waker<WeakShared>) -> Self {
let state = Arc::new(State { Self { runner }
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 take(&self) -> bool { pub fn wake_up(&self) {
debug_assert!( self.runner.wake();
MainThreadMarker::new().is_some(),
"this should only be called from the main thread"
);
self.0.with_sender_data(|state| state.awoken.swap(false, Ordering::Relaxed))
}
}
impl Drop for EventLoopProxy {
fn drop(&mut self) {
self.0.with_sender_data(|state| {
state.closed.store(true, Ordering::Relaxed);
state.waker.wake();
});
}
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.0.send(())
} }
} }

View File

@@ -3,7 +3,6 @@ use std::collections::{HashSet, VecDeque};
use std::iter; use std::iter;
use std::ops::Deref; use std::ops::Deref;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::Arc;
use wasm_bindgen::prelude::Closure; use wasm_bindgen::prelude::Closure;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
@@ -14,14 +13,13 @@ use super::super::event;
use super::super::main_thread::MainThreadMarker; use super::super::main_thread::MainThreadMarker;
use super::super::monitor::MonitorHandler; use super::super::monitor::MonitorHandler;
use super::backend; use super::backend;
use super::proxy::EventLoopProxy;
use super::state::State; use super::state::State;
use crate::dpi::PhysicalSize; use crate::dpi::PhysicalSize;
use crate::event::{DeviceEvent, ElementState, Event, RawKeyEvent, StartCause, WindowEvent}; use crate::event::{DeviceEvent, ElementState, Event, RawKeyEvent, StartCause, WindowEvent};
use crate::event_loop::{ControlFlow, DeviceEvents}; use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::platform::web::{PollStrategy, WaitUntilStrategy}; use crate::platform::web::{PollStrategy, WaitUntilStrategy};
use crate::platform_impl::platform::backend::{EventListenerHandle, SafeAreaHandle}; use crate::platform_impl::platform::backend::EventListenerHandle;
use crate::platform_impl::platform::r#async::DispatchRunner; use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
use crate::platform_impl::platform::window::Inner; use crate::platform_impl::platform::window::Inner;
use crate::window::WindowId; use crate::window::WindowId;
@@ -39,7 +37,7 @@ type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
struct Execution { struct Execution {
main_thread: MainThreadMarker, main_thread: MainThreadMarker,
event_loop_proxy: Arc<EventLoopProxy>, proxy_spawner: WakerSpawner<WeakShared>,
control_flow: Cell<ControlFlow>, control_flow: Cell<ControlFlow>,
poll_strategy: Cell<PollStrategy>, poll_strategy: Cell<PollStrategy>,
wait_until_strategy: Cell<WaitUntilStrategy>, wait_until_strategy: Cell<WaitUntilStrategy>,
@@ -57,7 +55,6 @@ struct Execution {
redraw_pending: RefCell<HashSet<WindowId>>, redraw_pending: RefCell<HashSet<WindowId>>,
destroy_pending: RefCell<VecDeque<WindowId>>, destroy_pending: RefCell<VecDeque<WindowId>>,
pub(crate) monitor: Rc<MonitorHandler>, pub(crate) monitor: Rc<MonitorHandler>,
safe_area: Rc<SafeAreaHandle>,
page_transition_event_handle: RefCell<Option<backend::PageTransitionEventHandle>>, page_transition_event_handle: RefCell<Option<backend::PageTransitionEventHandle>>,
device_events: Cell<DeviceEvents>, device_events: Cell<DeviceEvents>,
on_mouse_move: OnEventHandle<PointerEvent>, on_mouse_move: OnEventHandle<PointerEvent>,
@@ -143,7 +140,12 @@ impl Shared {
let document = window.document().expect("Failed to obtain document"); let document = window.document().expect("Failed to obtain document");
Shared(Rc::<Execution>::new_cyclic(|weak| { 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( let monitor = MonitorHandler::new(
main_thread, main_thread,
@@ -152,11 +154,9 @@ impl Shared {
WeakShared(weak.clone()), WeakShared(weak.clone()),
); );
let safe_area = SafeAreaHandle::new(&window, &document);
Execution { Execution {
main_thread, main_thread,
event_loop_proxy: Arc::new(proxy_spawner), proxy_spawner,
control_flow: Cell::new(ControlFlow::default()), control_flow: Cell::new(ControlFlow::default()),
poll_strategy: Cell::new(PollStrategy::default()), poll_strategy: Cell::new(PollStrategy::default()),
wait_until_strategy: Cell::new(WaitUntilStrategy::default()), wait_until_strategy: Cell::new(WaitUntilStrategy::default()),
@@ -173,7 +173,6 @@ impl Shared {
redraw_pending: RefCell::new(HashSet::new()), redraw_pending: RefCell::new(HashSet::new()),
destroy_pending: RefCell::new(VecDeque::new()), destroy_pending: RefCell::new(VecDeque::new()),
monitor: Rc::new(monitor), monitor: Rc::new(monitor),
safe_area: Rc::new(safe_area),
page_transition_event_handle: RefCell::new(None), page_transition_event_handle: RefCell::new(None),
device_events: Cell::default(), device_events: Cell::default(),
on_mouse_move: RefCell::new(None), on_mouse_move: RefCell::new(None),
@@ -654,7 +653,7 @@ impl Shared {
// Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle. // Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle.
events.extend( events.extend(
self.0 self.0
.event_loop_proxy .proxy_spawner
.take() .take()
.then_some(Event::UserWakeUp) .then_some(Event::UserWakeUp)
.map(EventWrapper::from), .map(EventWrapper::from),
@@ -819,8 +818,8 @@ impl Shared {
self.0.wait_until_strategy.get() self.0.wait_until_strategy.get()
} }
pub(crate) fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> { pub(crate) fn waker(&self) -> Waker<WeakShared> {
&self.0.event_loop_proxy self.0.proxy_spawner.waker()
} }
pub(crate) fn weak(&self) -> WeakShared { pub(crate) fn weak(&self) -> WeakShared {
@@ -830,10 +829,6 @@ impl Shared {
pub(crate) fn monitor(&self) -> &Rc<MonitorHandler> { pub(crate) fn monitor(&self) -> &Rc<MonitorHandler> {
&self.0.monitor &self.0.monitor
} }
pub(crate) fn safe_area(&self) -> &Rc<SafeAreaHandle> {
&self.0.safe_area
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,6 @@ mod intersection_handle;
mod media_query_handle; mod media_query_handle;
mod pointer; mod pointer;
mod resize_scaling; mod resize_scaling;
mod safe_area;
mod schedule; mod schedule;
use std::cell::OnceCell; 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::canvas::{Canvas, Style};
pub use self::event_handle::EventListenerHandle; pub use self::event_handle::EventListenerHandle;
pub use self::resize_scaling::ResizeScaleHandle; pub use self::resize_scaling::ResizeScaleHandle;
pub use self::safe_area::SafeAreaHandle;
pub use self::schedule::Schedule; pub use self::schedule::Schedule;
use crate::dpi::{LogicalPosition, LogicalSize}; 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) pub fn on_pointer_leave<F>(&mut self, canvas_common: &Common, mut handler: F)
where where
F: 'static F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
{ {
let window = canvas_common.window.clone(); let window = canvas_common.window.clone();
self.on_cursor_leave = self.on_cursor_leave =
@@ -47,14 +46,13 @@ impl PointerHandler {
let position = let position =
event::mouse_position(&event).to_physical(super::scale_factor(&window)); event::mouse_position(&event).to_physical(super::scale_factor(&window));
let kind = event::pointer_type(&event, pointer_id); 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) pub fn on_pointer_enter<F>(&mut self, canvas_common: &Common, mut handler: F)
where where
F: 'static F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
{ {
let window = canvas_common.window.clone(); let window = canvas_common.window.clone();
self.on_cursor_enter = self.on_cursor_enter =
@@ -65,14 +63,13 @@ impl PointerHandler {
let position = let position =
event::mouse_position(&event).to_physical(super::scale_factor(&window)); event::mouse_position(&event).to_physical(super::scale_factor(&window));
let kind = event::pointer_type(&event, pointer_id); 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) pub fn on_pointer_release<C>(&mut self, canvas_common: &Common, mut handler: C)
where where
C: 'static C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
{ {
let window = canvas_common.window.clone(); let window = canvas_common.window.clone();
self.on_pointer_release = self.on_pointer_release =
@@ -95,7 +92,6 @@ impl PointerHandler {
handler( handler(
modifiers, modifiers,
mkdid(pointer_id), mkdid(pointer_id),
event.is_primary(),
event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source, source,
) )
@@ -108,8 +104,7 @@ impl PointerHandler {
mut handler: C, mut handler: C,
prevent_default: Rc<Cell<bool>>, prevent_default: Rc<Cell<bool>>,
) where ) where
C: 'static C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
{ {
let window = canvas_common.window.clone(); let window = canvas_common.window.clone();
let canvas = canvas_common.raw().clone(); let canvas = canvas_common.raw().clone();
@@ -148,7 +143,6 @@ impl PointerHandler {
handler( handler(
modifiers, modifiers,
mkdid(pointer_id), mkdid(pointer_id),
event.is_primary(),
event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source, source,
) )
@@ -165,15 +159,12 @@ impl PointerHandler {
C: 'static C: 'static
+ FnMut( + FnMut(
Option<DeviceId>, Option<DeviceId>,
&mut dyn Iterator< &mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
Item = (ModifiersState, bool, PhysicalPosition<f64>, PointerSource),
>,
), ),
B: 'static B: 'static
+ FnMut( + FnMut(
ModifiersState, ModifiersState,
Option<DeviceId>, Option<DeviceId>,
bool,
PhysicalPosition<f64>, PhysicalPosition<f64>,
ElementState, ElementState,
ButtonSource, ButtonSource,
@@ -186,7 +177,6 @@ impl PointerHandler {
let pointer_id = event.pointer_id(); let pointer_id = event.pointer_id();
let device_id = mkdid(pointer_id); let device_id = mkdid(pointer_id);
let kind = event::pointer_type(&event, pointer_id); let kind = event::pointer_type(&event, pointer_id);
let primary = event.is_primary();
// chorded button event // chorded button event
if let Some(button) = event::mouse_button(&event) { if let Some(button) = event::mouse_button(&event) {
@@ -223,7 +213,6 @@ impl PointerHandler {
button_handler( button_handler(
event::mouse_modifiers(&event), event::mouse_modifiers(&event),
device_id, device_id,
primary,
event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_position(&event).to_physical(super::scale_factor(&window)),
state, state,
button, button,
@@ -240,7 +229,6 @@ impl PointerHandler {
&mut event::pointer_move_event(event).map(|event| { &mut event::pointer_move_event(event).map(|event| {
( (
event::mouse_modifiers(&event), event::mouse_modifiers(&event),
event.is_primary(),
event::mouse_position(&event).to_physical(scale), event::mouse_position(&event).to_physical(scale),
match kind { match kind {
PointerKind::Mouse => PointerSource::Mouse, 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::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use dpi::{LogicalPosition, LogicalSize};
use web_sys::HtmlCanvasElement; use web_sys::HtmlCanvasElement;
use super::main_thread::{MainThreadMarker, MainThreadSafe}; use super::main_thread::{MainThreadMarker, MainThreadSafe};
use super::monitor::MonitorHandler; use super::monitor::MonitorHandler;
use super::r#async::Dispatcher; use super::r#async::Dispatcher;
use super::{backend, lock, ActiveEventLoop}; 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::error::{NotSupportedError, RequestError};
use crate::icon::Icon; use crate::icon::Icon;
use crate::monitor::MonitorHandle as RootMonitorHandle; use crate::monitor::MonitorHandle as RootMonitorHandle;
@@ -27,7 +26,6 @@ pub struct Inner {
id: WindowId, id: WindowId,
pub window: web_sys::Window, pub window: web_sys::Window,
monitor: Rc<MonitorHandler>, monitor: Rc<MonitorHandler>,
safe_area: Rc<backend::SafeAreaHandle>,
canvas: Rc<backend::Canvas>, canvas: Rc<backend::Canvas>,
destroy_fn: Option<Box<dyn FnOnce()>>, destroy_fn: Option<Box<dyn FnOnce()>>,
} }
@@ -61,7 +59,6 @@ impl Window {
id, id,
window: window.clone(), window: window.clone(),
monitor: Rc::clone(target.runner.monitor()), monitor: Rc::clone(target.runner.monitor()),
safe_area: Rc::clone(target.runner.safe_area()),
canvas, canvas,
destroy_fn: Some(destroy_fn), destroy_fn: Some(destroy_fn),
}; };
@@ -112,9 +109,9 @@ impl RootWindow for Window {
// Not supported // Not supported
} }
fn surface_position(&self) -> PhysicalPosition<i32> { fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
// Note: the canvas element has no window decorations. // Note: the canvas element has no window decorations, so this is equal to `outer_position`.
(0, 0).into() self.outer_position()
} }
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> { fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
@@ -155,34 +152,6 @@ impl RootWindow for Window {
self.surface_size() 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>) { fn set_min_surface_size(&self, min_size: Option<Size>) {
self.inner.dispatch(move |inner| { self.inner.dispatch(move |inner| {
let dimensions = min_size.map(|min_size| min_size.to_logical(inner.scale_factor())); 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) 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 { fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self self
} }
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle { fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self self
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window { impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> { fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
MainThreadMarker::new() MainThreadMarker::new()
@@ -436,6 +408,7 @@ impl rwh_06::HasWindowHandle for Window {
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window { impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
Ok(rwh_06::DisplayHandle::web()) Ok(rwh_06::DisplayHandle::web())

View File

@@ -4,7 +4,6 @@ mod runner;
use std::cell::Cell; use std::cell::Cell;
use std::ffi::c_void; use std::ffi::c_void;
use std::os::windows::io::{AsRawHandle as _, FromRawHandle as _, OwnedHandle, RawHandle};
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
@@ -13,18 +12,13 @@ use std::{mem, panic, ptr};
use runner::EventLoopRunner; use runner::EventLoopRunner;
use windows_sys::Win32::Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE; use windows_sys::Win32::Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE;
use windows_sys::Win32::Foundation::{ use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, POINT, RECT, WPARAM};
GetLastError, FALSE, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_FAILED, WPARAM,
};
use windows_sys::Win32::Graphics::Gdi::{ use windows_sys::Win32::Graphics::Gdi::{
GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient, GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient,
ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE, ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE,
}; };
use windows_sys::Win32::System::Ole::RevokeDragDrop; use windows_sys::Win32::System::Ole::RevokeDragDrop;
use windows_sys::Win32::System::Threading::{ use windows_sys::Win32::System::Threading::{GetCurrentThreadId, INFINITE};
CreateWaitableTimerExW, GetCurrentThreadId, SetWaitableTimer,
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, INFINITE, TIMER_ALL_ACCESS,
};
use windows_sys::Win32::UI::Controls::{HOVER_DEFAULT, WM_MOUSELEAVE}; use windows_sys::Win32::UI::Controls::{HOVER_DEFAULT, WM_MOUSELEAVE};
use windows_sys::Win32::UI::Input::Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW}; use windows_sys::Win32::UI::Input::Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW};
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{ 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::Input::{RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE};
use windows_sys::Win32::UI::WindowsAndMessaging::{ use windows_sys::Win32::UI::WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect, GetCursorPos, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect, GetCursorPos,
GetMenu, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW, GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW,
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos, TranslateMessage, RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos, TranslateMessage, CREATESTRUCTW,
CREATESTRUCTW, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS,
MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PM_REMOVE, PT_TOUCH, QS_ALLEVENTS, RI_MOUSE_HWHEEL, PM_REMOVE, PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM,
SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT, WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
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_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_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL,
WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, 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::dpi::{PhysicalPosition, PhysicalSize};
use crate::error::{EventLoopError, RequestError}; use crate::error::{EventLoopError, RequestError};
use crate::event::{ 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::{ use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, EventLoopProxyProvider, EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
}; };
use crate::keyboard::ModifiersState; use crate::keyboard::ModifiersState;
use crate::monitor::MonitorHandle as RootMonitorHandle; use crate::monitor::MonitorHandle as RootMonitorHandle;
@@ -87,7 +80,7 @@ use crate::platform_impl::platform::window::InitData;
use crate::platform_impl::platform::window_state::{ use crate::platform_impl::platform::window_state::{
CursorFlags, ImeState, WindowFlags, WindowState, 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::platform_impl::Window;
use crate::utils::Lazy; use crate::utils::Lazy;
use crate::window::{ use crate::window::{
@@ -134,10 +127,6 @@ pub(crate) enum ProcResult {
pub struct EventLoop { pub struct EventLoop {
window_target: ActiveEventLoop, window_target: ActiveEventLoop,
msg_hook: Option<Box<dyn FnMut(*const c_void) -> bool + 'static>>, 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 { pub(crate) struct PlatformSpecificEventLoopAttributes {
@@ -212,7 +201,6 @@ impl EventLoop {
Ok(EventLoop { Ok(EventLoop {
window_target: ActiveEventLoop { thread_id, thread_msg_target, runner_shared }, window_target: ActiveEventLoop { thread_id, thread_msg_target, runner_shared },
msg_hook: attributes.msg_hook.take(), msg_hook: attributes.msg_hook.take(),
high_resolution_timer: None,
}) })
} }
@@ -257,9 +245,8 @@ impl EventLoop {
} }
let exit_code = loop { let exit_code = loop {
self.wait_for_messages(None); self.wait_and_dispatch_message(None);
// wait_for_messages calls user application before and after waiting
// so it may have decided to exit.
if let Some(code) = self.exit_code() { if let Some(code) = self.exit_code() {
break code; break code;
} }
@@ -325,11 +312,8 @@ impl EventLoop {
} }
} }
if self.exit_code().is_none() { self.wait_and_dispatch_message(timeout);
self.wait_for_messages(timeout);
}
// wait_for_messages calls user application before and after waiting
// so it may have decided to exit.
if self.exit_code().is_none() { if self.exit_code().is_none() {
self.dispatch_peeked_messages(); self.dispatch_peeked_messages();
} }
@@ -359,27 +343,101 @@ impl EventLoop {
status status
} }
/// Waits until new event messages arrive to be peeked. /// Wait for one message and dispatch it, optionally with a timeout
/// Doesn't peek messages itself. fn wait_and_dispatch_message(&mut self, timeout: Option<Duration>) {
/// fn get_msg_with_timeout(msg: &mut MSG, timeout: Option<Duration>) -> PumpStatus {
/// Parameter timeout is optional. This method would wait for the smaller timeout unsafe {
/// between the argument and a timeout from control flow. // A timeout of None means wait indefinitely (so we don't need to call SetTimer)
fn wait_for_messages(&mut self, timeout: Option<Duration>) { 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; let runner = &self.window_target.runner_shared;
// We aim to be consistent with the MacOS backend which has a RunLoop // We aim to be consistent with the MacOS backend which has a RunLoop
// observer that will dispatch AboutToWait when about to wait for // observer that will dispatch AboutToWait when about to wait for
// events, and NewEvents after the RunLoop wakes up. // 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 // point and wake up point (when it returns) and we drain all other
// pending messages via `PeekMessage` until we come back to "wait" via // pending messages via `PeekMessage` until we come back to "wait" via
// `MsgWaitForMultipleObjectsEx`. // `GetMessage`
// //
runner.prepare_wait(); 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 // Before we potentially exit, make sure to consistently emit an event for the wake up
runner.wakeup(); 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` /// Dispatch all queued messages via `PeekMessageW`
@@ -398,7 +456,7 @@ impl EventLoop {
// initializing a `MSG` struct (it can be uninitialized memory for the C // 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 // API) and there's no API to construct or initialize a `MSG`. This
// is the simplest way avoid uninitialized memory in Rust // is the simplest way avoid uninitialized memory in Rust
let mut msg: MSG = unsafe { mem::zeroed() }; let mut msg = unsafe { mem::zeroed() };
loop { loop {
unsafe { unsafe {
@@ -437,14 +495,6 @@ impl EventLoop {
} }
} }
impl Drop for EventLoop {
fn drop(&mut self) {
unsafe {
DestroyWindow(self.window_target.thread_msg_target);
}
}
}
impl ActiveEventLoop { impl ActiveEventLoop {
#[inline(always)] #[inline(always)]
pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor { pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor {
@@ -463,7 +513,7 @@ impl ActiveEventLoop {
impl RootActiveEventLoop for ActiveEventLoop { impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> RootEventLoopProxy { fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = EventLoopProxy { target_window: self.thread_msg_target }; 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( fn create_window(
@@ -516,8 +566,8 @@ impl RootActiveEventLoop for ActiveEventLoop {
self.runner_shared.set_exit_code(0) self.runner_shared.set_exit_code(0)
} }
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle { fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle)) RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
} }
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle { fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
@@ -525,6 +575,7 @@ impl RootActiveEventLoop for ActiveEventLoop {
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for ActiveEventLoop { impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::RawDisplayHandle::Windows(rwh_06::WindowsDisplayHandle::new()); let raw = rwh_06::RawDisplayHandle::Windows(rwh_06::WindowsDisplayHandle::new());
@@ -532,13 +583,16 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
} }
} }
#[derive(Clone)] #[derive(Clone, PartialEq, Eq)]
pub(crate) struct OwnedDisplayHandle; pub(crate) struct OwnedDisplayHandle;
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle { impl OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { #[cfg(feature = "rwh_06")]
let raw = rwh_06::RawDisplayHandle::Windows(rwh_06::WindowsDisplayHandle::new()); #[inline]
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) } pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::WindowsDisplayHandle::new().into())
} }
} }
@@ -563,7 +617,7 @@ impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
fn main_thread_id() -> u32 { fn main_thread_id() -> u32 {
static mut MAIN_THREAD_ID: u32 = 0; 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. // Mark as used so this is not removable.
#[used] #[used]
@@ -614,139 +668,10 @@ fn dur2timeout(dur: Duration) -> u32 {
.unwrap_or(INFINITE) .unwrap_or(INFINITE)
} }
/// Set upper limit for waiting time to avoid overflows. impl Drop for EventLoop {
/// I chose 50 days as a limit because it is used in dur2timeout. fn drop(&mut self) {
const FIFTY_DAYS: Duration = Duration::from_secs(50_u64 * 24 * 60 * 60); unsafe {
/// Waitable timers use 100 ns intervals to indicate due time. DestroyWindow(self.window_target.thread_msg_target);
/// <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(),);
} }
} }
} }
@@ -801,14 +726,15 @@ impl EventLoopThreadExecutor {
type ThreadExecFn = Box<Box<dyn FnMut()>>; type ThreadExecFn = Box<Box<dyn FnMut()>>;
#[derive(Clone)]
pub struct EventLoopProxy { pub struct EventLoopProxy {
target_window: HWND, target_window: HWND,
} }
unsafe impl Send for EventLoopProxy {} unsafe impl Send for EventLoopProxy {}
impl EventLoopProxyProvider for EventLoopProxy { impl EventLoopProxy {
fn wake_up(&self) { pub fn wake_up(&self) {
unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) }; unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) };
} }
} }
@@ -1616,7 +1542,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerEntered { event: PointerEntered {
device_id: None, device_id: None,
primary: true,
position, position,
kind: PointerKind::Mouse, kind: PointerKind::Mouse,
}, },
@@ -1642,7 +1567,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerLeft { event: PointerLeft {
device_id: None, device_id: None,
primary: true,
position: Some(position), position: Some(position),
kind: PointerKind::Mouse, kind: PointerKind::Mouse,
}, },
@@ -1664,12 +1588,7 @@ unsafe fn public_window_callback_inner(
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerMoved { event: PointerMoved { device_id: None, position, source: PointerSource::Mouse },
device_id: None,
primary: true,
position,
source: PointerSource::Mouse,
},
}); });
} }
@@ -1687,7 +1606,7 @@ unsafe fn public_window_callback_inner(
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(window as usize), 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); result = ProcResult::Value(0);
@@ -1764,7 +1683,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerButton { event: PointerButton {
device_id: None, device_id: None,
primary: true,
state: Pressed, state: Pressed,
position, position,
button: Left.into(), button: Left.into(),
@@ -1790,7 +1708,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerButton { event: PointerButton {
device_id: None, device_id: None,
primary: true,
state: Released, state: Released,
position, position,
button: Left.into(), button: Left.into(),
@@ -1816,7 +1733,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerButton { event: PointerButton {
device_id: None, device_id: None,
primary: true,
state: Pressed, state: Pressed,
position, position,
button: Right.into(), button: Right.into(),
@@ -1842,7 +1758,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerButton { event: PointerButton {
device_id: None, device_id: None,
primary: true,
state: Released, state: Released,
position, position,
button: Right.into(), button: Right.into(),
@@ -1868,7 +1783,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerButton { event: PointerButton {
device_id: None, device_id: None,
primary: true,
state: Pressed, state: Pressed,
position, position,
button: Middle.into(), button: Middle.into(),
@@ -1894,7 +1808,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerButton { event: PointerButton {
device_id: None, device_id: None,
primary: true,
state: Released, state: Released,
position, position,
button: Middle.into(), button: Middle.into(),
@@ -1921,7 +1834,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerButton { event: PointerButton {
device_id: None, device_id: None,
primary: true,
state: Pressed, state: Pressed,
position, position,
button: match xbutton { button: match xbutton {
@@ -1953,7 +1865,6 @@ unsafe fn public_window_callback_inner(
window_id: WindowId::from_raw(window as usize), window_id: WindowId::from_raw(window as usize),
event: PointerButton { event: PointerButton {
device_id: None, device_id: None,
primary: true,
state: Released, state: Released,
position, position,
button: match xbutton { button: match xbutton {
@@ -2007,15 +1918,16 @@ unsafe fn public_window_callback_inner(
let position = PhysicalPosition::new(x, y); let position = PhysicalPosition::new(x, y);
let window_id = WindowId::from_raw(window as usize); let window_id = WindowId::from_raw(window as usize);
let finger_id = FingerId::from_raw(input.dwID as usize); let finger_id = RootFingerId(FingerId {
let primary = util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY); id: input.dwID,
primary: util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY),
});
if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) { if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) {
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id, window_id,
event: WindowEvent::PointerEntered { event: WindowEvent::PointerEntered {
device_id: None, device_id: None,
primary,
position, position,
kind: PointerKind::Touch(finger_id), kind: PointerKind::Touch(finger_id),
}, },
@@ -2024,7 +1936,6 @@ unsafe fn public_window_callback_inner(
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id: None, device_id: None,
primary,
state: Pressed, state: Pressed,
position, position,
button: Touch { finger_id, force: None }, button: Touch { finger_id, force: None },
@@ -2035,7 +1946,6 @@ unsafe fn public_window_callback_inner(
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id: None, device_id: None,
primary,
state: Released, state: Released,
position, position,
button: Touch { finger_id, force: None }, button: Touch { finger_id, force: None },
@@ -2045,7 +1955,6 @@ unsafe fn public_window_callback_inner(
window_id, window_id,
event: WindowEvent::PointerLeft { event: WindowEvent::PointerLeft {
device_id: None, device_id: None,
primary,
position: Some(position), position: Some(position),
kind: PointerKind::Touch(finger_id), kind: PointerKind::Touch(finger_id),
}, },
@@ -2055,7 +1964,6 @@ unsafe fn public_window_callback_inner(
window_id, window_id,
event: WindowEvent::PointerMoved { event: WindowEvent::PointerMoved {
device_id: None, device_id: None,
primary,
position, position,
source: PointerSource::Touch { finger_id, force: None }, source: PointerSource::Touch { finger_id, force: None },
}, },
@@ -2178,15 +2086,16 @@ unsafe fn public_window_callback_inner(
let position = PhysicalPosition::new(x, y); let position = PhysicalPosition::new(x, y);
let window_id = WindowId::from_raw(window as usize); let window_id = WindowId::from_raw(window as usize);
let finger_id = FingerId::from_raw(pointer_info.pointerId as usize); let finger_id = RootFingerId(FingerId {
let primary = util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY); id: pointer_info.pointerId,
primary: util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY),
});
if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) { if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) {
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id, window_id,
event: WindowEvent::PointerEntered { event: WindowEvent::PointerEntered {
device_id: None, device_id: None,
primary,
position, position,
kind: if let PT_TOUCH = pointer_info.pointerType { kind: if let PT_TOUCH = pointer_info.pointerType {
PointerKind::Touch(finger_id) PointerKind::Touch(finger_id)
@@ -2199,7 +2108,6 @@ unsafe fn public_window_callback_inner(
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id: None, device_id: None,
primary,
state: Pressed, state: Pressed,
position, position,
button: if let PT_TOUCH = pointer_info.pointerType { button: if let PT_TOUCH = pointer_info.pointerType {
@@ -2214,7 +2122,6 @@ unsafe fn public_window_callback_inner(
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id: None, device_id: None,
primary,
state: Released, state: Released,
position, position,
button: if let PT_TOUCH = pointer_info.pointerType { button: if let PT_TOUCH = pointer_info.pointerType {
@@ -2228,7 +2135,6 @@ unsafe fn public_window_callback_inner(
window_id, window_id,
event: WindowEvent::PointerLeft { event: WindowEvent::PointerLeft {
device_id: None, device_id: None,
primary,
position: Some(position), position: Some(position),
kind: if let PT_TOUCH = pointer_info.pointerType { kind: if let PT_TOUCH = pointer_info.pointerType {
PointerKind::Touch(finger_id) PointerKind::Touch(finger_id)
@@ -2242,7 +2148,6 @@ unsafe fn public_window_callback_inner(
window_id, window_id,
event: WindowEvent::PointerMoved { event: WindowEvent::PointerMoved {
device_id: None, device_id: None,
primary,
position, position,
source: if let PT_TOUCH = pointer_info.pointerType { source: if let PT_TOUCH = pointer_info.pointerType {
PointerSource::Touch { finger_id, force } PointerSource::Touch { finger_id, force }

View File

@@ -97,7 +97,7 @@ impl KeyEventBuilder {
MatchResult::MessagesToDispatch(self.pending.complete_multi(key_events)) MatchResult::MessagesToDispatch(self.pending.complete_multi(key_events))
}, },
WM_KILLFOCUS => { WM_KILLFOCUS => {
// synthesize keyup events // sythesize keyup events
let kbd_state = get_kbd_state(); let kbd_state = get_kbd_state();
let key_events = Self::synthesize_kbd_state(ElementState::Released, &kbd_state); let key_events = Self::synthesize_kbd_state(ElementState::Released, &kbd_state);
MatchResult::MessagesToDispatch(self.pending.complete_multi(key_events)) 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: // 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 // 1. If caps-lock is *not* held down but *is* active, then we have to synthesize all
// printable keys, respecting the caps-lock state. // printable keys, respecting the caps-lock state.
// 2. If caps-lock is held down, we could choose to synthesize its keypress after every // 2. If caps-lock is held down, we could choose to sythesize its keypress after every other
// other key, in which case all other keys *must* be sythesized as if the caps-lock state // key, in which case all other keys *must* be sythesized as if the caps-lock state was
// was be the opposite of what it currently is. // 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 // caps-lock first, and always use the current caps-lock state
// to determine the produced text // to determine the produced text
if is_key_pressed!(VK_CAPITAL) { 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::AudioVolumeDown => Some(0xe02e),
KeyCode::AudioVolumeMute => Some(0xe020), KeyCode::AudioVolumeMute => Some(0xe020),
KeyCode::AudioVolumeUp => Some(0xe030), 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, _ => None,
} }
} }
@@ -1252,20 +1238,6 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
0xe02e => KeyCode::AudioVolumeDown, 0xe02e => KeyCode::AudioVolumeDown,
0xe020 => KeyCode::AudioVolumeMute, 0xe020 => KeyCode::AudioVolumeMute,
0xe030 => KeyCode::AudioVolumeUp, 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)), _ => 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::Foundation::HWND;
use windows_sys::Win32::UI::WindowsAndMessaging::{HMENU, WINDOW_LONG_PTR_INDEX}; 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 use self::icon::WinIcon as PlatformIcon;
pub(crate) use self::icon::{SelectedCursor, WinCursor as PlatformCustomCursor, WinIcon}; pub(crate) use self::icon::{SelectedCursor, WinCursor as PlatformCustomCursor, WinIcon};
pub(crate) use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey}; 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 Send for PlatformSpecificWindowAttributes {}
unsafe impl Sync 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 { fn wrap_device_id(id: u32) -> DeviceId {
DeviceId::from_raw(id as i64) DeviceId::from_raw(id as i64)
} }

View File

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

View File

@@ -7,7 +7,7 @@ pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorSource, MAX_CURSOR_SIZE}; 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; use crate::error::RequestError;
pub use crate::icon::{BadIcon, Icon}; pub use crate::icon::{BadIcon, Icon};
use crate::monitor::{MonitorHandle, VideoModeHandle}; use crate::monitor::{MonitorHandle, VideoModeHandle};
@@ -67,6 +67,7 @@ pub struct WindowAttributes {
pub window_level: WindowLevel, pub window_level: WindowLevel,
pub active: bool, pub active: bool,
pub cursor: Cursor, pub cursor: Cursor,
#[cfg(feature = "rwh_06")]
pub(crate) parent_window: Option<SendSyncRawWindowHandle>, pub(crate) parent_window: Option<SendSyncRawWindowHandle>,
pub fullscreen: Option<Fullscreen>, pub fullscreen: Option<Fullscreen>,
// Platform-specific configuration. // Platform-specific configuration.
@@ -97,6 +98,7 @@ impl Default for WindowAttributes {
preferred_theme: None, preferred_theme: None,
content_protected: false, content_protected: false,
cursor: Cursor::default(), cursor: Cursor::default(),
#[cfg(feature = "rwh_06")]
parent_window: None, parent_window: None,
active: true, active: true,
platform_specific: Default::default(), 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()`], /// The user has to account for that when using [`WindowAttributes::with_parent_window()`],
/// which is `unsafe`. /// which is `unsafe`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[cfg(feature = "rwh_06")]
pub(crate) struct SendSyncRawWindowHandle(pub(crate) rwh_06::RawWindowHandle); pub(crate) struct SendSyncRawWindowHandle(pub(crate) rwh_06::RawWindowHandle);
#[cfg(feature = "rwh_06")]
unsafe impl Send for SendSyncRawWindowHandle {} unsafe impl Send for SendSyncRawWindowHandle {}
#[cfg(feature = "rwh_06")]
unsafe impl Sync for SendSyncRawWindowHandle {} unsafe impl Sync for SendSyncRawWindowHandle {}
impl WindowAttributes { impl WindowAttributes {
/// Get the parent window stored on the attributes. /// Get the parent window stored on the attributes.
#[cfg(feature = "rwh_06")]
pub fn parent_window(&self) -> Option<&rwh_06::RawWindowHandle> { pub fn parent_window(&self) -> Option<&rwh_06::RawWindowHandle> {
self.parent_window.as_ref().map(|handle| &handle.0) 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> /// <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. /// - **X11**: A child window is confined to the client area of its parent window.
/// - **Android / iOS / Wayland / Web:** Unsupported. /// - **Android / iOS / Wayland / Web:** Unsupported.
#[cfg(feature = "rwh_06")]
#[inline] #[inline]
pub unsafe fn with_parent_window( pub unsafe fn with_parent_window(
mut self, mut self,
@@ -574,51 +581,41 @@ pub trait Window: AsAny + Send + Sync {
// extension trait // extension trait
fn reset_dead_keys(&self); fn reset_dead_keys(&self);
/// The position of the top-left hand corner of the surface relative to the top-left hand corner /// Returns the position of the top-left hand corner of the window's client area relative to the
/// of the window. /// top-left hand corner of the desktop.
/// ///
/// This, combined with [`outer_position`], can be useful for calculating the position of the /// The same conditions that apply to [`Window::outer_position`] apply to this method.
/// 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.
/// ///
/// ## Platform-specific /// ## 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. /// - **Web:** Returns the top-left coordinates relative to the viewport.
/// - **Android / Wayland:** Always returns [`RequestError::NotSupported`]. /// - **Android / Wayland:** Always returns [`RequestError::NotSupported`].
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError>; 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. /// See [`Window::outer_position`] for more information about the coordinates.
/// This automatically un-maximizes the window if it's maximized. /// 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. /// 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 /// This is the dimensions you should pass to things like Wgpu or Glutin when configuring.
/// surface for drawing. See [`WindowEvent::SurfaceResized`] for listening to changes to this
/// field.
///
/// Note that to ensure that your content is not obscured by things such as notches or the title
/// bar, you will likely want to only draw important content inside a specific area of the
/// surface, see [`safe_area()`] for details.
/// ///
/// ## Platform-specific /// ## 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`]. /// - **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 /// [`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>; fn surface_size(&self) -> PhysicalSize<u32>;
/// Request the new size for the surface. /// Request the new size for the surface.
@@ -709,53 +701,11 @@ pub trait Window: AsAny + Send + Sync {
/// ///
/// ## Platform-specific /// ## 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 /// - **Web:** Returns the size of the canvas element. _Note: this returns the same value as
/// [`Window::surface_size`]._ /// [`Window::surface_size`]._
fn outer_size(&self) -> PhysicalSize<u32>; 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. /// Sets a minimum dimensions of the window's surface.
/// ///
/// ```no_run /// ```no_run
@@ -909,7 +859,7 @@ pub trait Window: AsAny + Send + Sync {
/// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`]. /// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`].
fn enabled_buttons(&self) -> WindowButtons; fn enabled_buttons(&self) -> WindowButtons;
/// Minimize the window, or put it back from the minimized state. /// Sets the window to minimized or back
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
@@ -945,7 +895,7 @@ pub trait Window: AsAny + Send + Sync {
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web:** Unsupported.
fn is_maximized(&self) -> bool; fn is_maximized(&self) -> bool;
/// Set the window's fullscreen state. /// Sets the window to fullscreen or back.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
@@ -1028,8 +978,8 @@ pub trait Window: AsAny + Send + Sync {
fn set_window_icon(&self, window_icon: Option<Icon>); 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 /// 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 /// and `size` is the size of this area starting from the position. An example of such area
/// example of such area could be a input field in the UI or line in the editor. /// 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 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. /// the specified area, so the user input to it stays visible.
@@ -1062,8 +1012,7 @@ pub trait Window: AsAny + Send + Sync {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **X11:** Area is not supported, only position. The bottom-right corner of the provided /// - **X11:** - area is not supported, only position.
/// area is reported as the position.
/// - **iOS / Android / Web / Orbital:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
/// ///
/// [chinese]: https://support.apple.com/guide/chinese-input-method/use-the-candidate-window-cim12992/104/mac/12.0 /// [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 /// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are
/// combined. /// combined.
/// - **iOS / Android:** This will show / hide the soft keyboard. /// - **iOS:** This will show / hide the soft keyboard.
/// - **Web / Orbital:** Unsupported. /// - **Android / Web / Orbital:** Unsupported.
/// - **X11**: Enabling IME will disable dead keys reporting during compose. /// - **X11**: Enabling IME will disable dead keys reporting during compose.
/// ///
/// [`Ime`]: crate::event::WindowEvent::Ime /// [`Ime`]: crate::event::WindowEvent::Ime
@@ -1260,7 +1209,7 @@ pub trait Window: AsAny + Send + Sync {
/// - **iOS / Android / Web:** Always returns an [`RequestError::NotSupported`]. /// - **iOS / Android / Web:** Always returns an [`RequestError::NotSupported`].
fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError>; 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 /// This is the context menu that is normally shown when interacting with
/// the title bar. This is useful when implementing custom decorations. /// 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>; fn primary_monitor(&self) -> Option<MonitorHandle>;
/// Get the raw-window-handle v0.6 display handle. /// Get the raw-window-handle v0.6 display handle.
#[cfg(feature = "rwh_06")]
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle; fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
/// Get the raw-window-handle v0.6 window handle. /// Get the raw-window-handle v0.6 window handle.
#[cfg(feature = "rwh_06")]
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle; 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 + '_ { impl rwh_06::HasDisplayHandle for dyn Window + '_ {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
self.rwh_06_display_handle().display_handle() self.rwh_06_display_handle().display_handle()
} }
} }
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for dyn Window + '_ { impl rwh_06::HasWindowHandle for dyn Window + '_ {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> { fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
self.rwh_06_window_handle().window_handle() self.rwh_06_window_handle().window_handle()
@@ -1433,9 +1386,6 @@ impl From<ResizeDirection> for CursorIcon {
/// Fullscreen modes. /// Fullscreen modes.
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Fullscreen { pub enum Fullscreen {
/// This changes the video mode of the monitor for fullscreen windows and,
/// if applicable, captures the monitor for exclusive use by this
/// application.
Exclusive(VideoModeHandle), Exclusive(VideoModeHandle),
/// Providing `None` to `Borderless` will fullscreen on the current monitor. /// Providing `None` to `Borderless` will fullscreen on the current monitor.

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 XF86_Calculater = "XF86_Calculater" # From xkbcommon_dl::keysyms::XF86_Calculater
ptd = "ptd" # From windows_sys::Win32::System::Com::FORMATETC { ptd, ..} ptd = "ptd" # From windows_sys::Win32::System::Com::FORMATETC { ptd, ..}
requestor = "requestor" # From x11_dl::xlib::XSelectionEvent { requestor ..} requestor = "requestor" # From x11_dl::xlib::XSelectionEvent { requestor ..}
[files]
extend-exclude = ["*.drawio"]