mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
34 Commits
madsmtm/dp
...
notgull/in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19477d3f03 | ||
|
|
e47081e385 | ||
|
|
171d53c042 | ||
|
|
35379f305a | ||
|
|
4d2a0dd2b3 | ||
|
|
4a8b659228 | ||
|
|
f314cd2b9a | ||
|
|
3657506f6e | ||
|
|
edca3ebc41 | ||
|
|
ca46e29203 | ||
|
|
132fbe14d5 | ||
|
|
164bf85b5b | ||
|
|
cfa8f027cc | ||
|
|
2e5db75101 | ||
|
|
19e5bee3d1 | ||
|
|
fc6cf89ac0 | ||
|
|
dbcdb6f1b4 | ||
|
|
d0c6c34eaa | ||
|
|
f2688d94ea | ||
|
|
f6b20852bf | ||
|
|
59b1eb5410 | ||
|
|
f781e13166 | ||
|
|
9f8ac8feb5 | ||
|
|
74958ecc6f | ||
|
|
3a60cbaba5 | ||
|
|
ae4c449670 | ||
|
|
b2896d7408 | ||
|
|
edfb4b03f4 | ||
|
|
c8c1eca3c7 | ||
|
|
d3207a8d76 | ||
|
|
9d347b72d9 | ||
|
|
13bb51c7e0 | ||
|
|
bb4aa22cf9 | ||
|
|
3e9b80d47a |
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,4 +2,3 @@
|
||||
- [ ] 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
|
||||
- [ ] 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
|
||||
|
||||
16
Cargo.toml
16
Cargo.toml
@@ -25,7 +25,6 @@ version = "0.30.5"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [
|
||||
"rwh_06",
|
||||
"serde",
|
||||
"mint",
|
||||
# Enabled to get docs to compile
|
||||
@@ -55,9 +54,8 @@ targets = [
|
||||
[features]
|
||||
android-game-activity = ["android-activity/game-activity"]
|
||||
android-native-activity = ["android-activity/native-activity"]
|
||||
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||
mint = ["dpi/mint"]
|
||||
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
||||
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde", "bitflags/serde"]
|
||||
wayland = [
|
||||
"wayland-client",
|
||||
@@ -81,9 +79,9 @@ cfg_aliases = "0.2.1"
|
||||
bitflags = "2"
|
||||
cursor-icon = "1.1.0"
|
||||
dpi = { version = "0.1.1", path = "dpi" }
|
||||
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
|
||||
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"] }
|
||||
serde = { workspace = true, optional = true }
|
||||
smol_str = "0.2.0"
|
||||
smol_str = "0.3"
|
||||
tracing = { version = "0.1.40", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -102,7 +100,7 @@ softbuffer = { version = "0.4.6", default-features = false, features = [
|
||||
# Android
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android-activity = "0.6.0"
|
||||
ndk = { version = "0.9.0", default-features = false }
|
||||
ndk = { version = "0.9.0", features = ["rwh_06"], default-features = false }
|
||||
|
||||
# AppKit or UIKit
|
||||
[target.'cfg(target_vendor = "apple")'.dependencies]
|
||||
@@ -136,6 +134,7 @@ objc2-app-kit = { version = "0.2.2", features = [
|
||||
"NSScreen",
|
||||
"NSTextInputClient",
|
||||
"NSTextInputContext",
|
||||
"NSToolbar",
|
||||
"NSView",
|
||||
"NSWindow",
|
||||
"NSWindowScripting",
|
||||
@@ -150,6 +149,7 @@ objc2-foundation = { version = "0.2.2", features = [
|
||||
"NSDictionary",
|
||||
"NSDistributedNotificationCenter",
|
||||
"NSEnumerator",
|
||||
"NSGeometry",
|
||||
"NSKeyValueObserving",
|
||||
"NSNotification",
|
||||
"NSObjCRuntime",
|
||||
@@ -214,6 +214,7 @@ windows-sys = { version = "0.52.0", features = [
|
||||
"Win32_System_Com",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Ole",
|
||||
"Win32_Security",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_System_Threading",
|
||||
@@ -296,6 +297,7 @@ web_sys = { package = "web-sys", version = "0.3.70", features = [
|
||||
"FocusEvent",
|
||||
"HtmlCanvasElement",
|
||||
"HtmlElement",
|
||||
"HtmlHtmlElement",
|
||||
"HtmlImageElement",
|
||||
"ImageBitmap",
|
||||
"ImageBitmapOptions",
|
||||
@@ -343,11 +345,9 @@ wasm-bindgen-test = "0.3"
|
||||
[[example]]
|
||||
doc-scrape-examples = true
|
||||
name = "window"
|
||||
required-features = ["rwh_06"]
|
||||
|
||||
[[example]]
|
||||
name = "child_window"
|
||||
required-features = ["rwh_06"]
|
||||
|
||||
[workspace]
|
||||
members = ["dpi"]
|
||||
|
||||
198
FEATURES.md
198
FEATURES.md
@@ -47,201 +47,3 @@ 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.
|
||||
If that gets accepted, the platform-specific functions get deprecated and become permanently
|
||||
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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://crates.io/crates/winit)
|
||||
[](https://docs.rs/winit)
|
||||
[](https://rust-windowing.github.io/winit/winit/index.html)
|
||||
[](https://github.com/rust-windowing/winit/actions)
|
||||
|
||||
@@ -66,4 +66,4 @@ same MSRV policy.
|
||||
|
||||
### Platform-specific usage
|
||||
|
||||
Check out the [`winit::platform`](https://rust-windowing.github.io/winit/winit/platform/index.html) module for platform-specific usage.
|
||||
Check out the [`winit::platform`](https://docs.rs/winit/latest/winit/platform/index.html) module for platform-specific usage.
|
||||
|
||||
12
deny.toml
12
deny.toml
@@ -26,12 +26,12 @@ targets = [
|
||||
|
||||
[licenses]
|
||||
allow = [
|
||||
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
|
||||
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
|
||||
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
|
||||
"ISC", # https://tldrlegal.com/license/-isc-license
|
||||
"MIT", # https://tldrlegal.com/license/mit-license
|
||||
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
|
||||
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
|
||||
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
|
||||
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
|
||||
"ISC", # https://tldrlegal.com/license/isc-license
|
||||
"MIT", # https://tldrlegal.com/license/mit-license
|
||||
"Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html
|
||||
]
|
||||
confidence-threshold = 1.0
|
||||
private = { ignore = true }
|
||||
|
||||
@@ -9,3 +9,10 @@ by [Tomiĉo] (https://commons.wikimedia.org/wiki/User:Tomi%C4%89o). It was
|
||||
originally released under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)
|
||||
License. Minor modifications have been made by [John Nunley](https://github.com/notgull),
|
||||
which have been released under the same license as a derivative work.
|
||||
|
||||
## `coordinate-systems*`
|
||||
|
||||
These files are created by [Mads Marquart](https://github.com/madsmtm) using
|
||||
[draw.io](https://draw.io/), and compressed using [svgomg.net](https://svgomg.net/).
|
||||
|
||||
They are licensed under the [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/) license.
|
||||
130
docs/coordinate-systems.drawio
Normal file
130
docs/coordinate-systems.drawio
Normal file
@@ -0,0 +1,130 @@
|
||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0" version="24.8.6" pages="2">
|
||||
<diagram name="desktop" id="3DDum1nDijUk3y7wIDRm">
|
||||
<mxGraphModel dx="1080" dy="707" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1000" pageHeight="500" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="cRYnzpdCW-J0f_YpP3mc-1" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#E8E8E8;fontColor=#333333;strokeColor=#666666;" parent="1" vertex="1">
|
||||
<mxGeometry x="200" y="80" width="480" height="360" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="cRYnzpdCW-J0f_YpP3mc-4" value="" style="rounded=1;whiteSpace=wrap;html=1;shadow=0;fillColor=#d5e8d4;strokeColor=#666666;" parent="1" vertex="1">
|
||||
<mxGeometry x="260" y="340" width="360" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="cRYnzpdCW-J0f_YpP3mc-2" value="" style="rounded=1;whiteSpace=wrap;html=1;shadow=0;fillColor=#dae8fc;strokeColor=#666666;" parent="1" vertex="1">
|
||||
<mxGeometry x="260" y="140" width="360" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="cRYnzpdCW-J0f_YpP3mc-3" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#DBDBDB;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
|
||||
<mxGeometry x="200" y="60" width="480" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="cRYnzpdCW-J0f_YpP3mc-5" value="" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=none;fillColor=#d5e8d4;" parent="1" vertex="1">
|
||||
<mxGeometry x="260" y="180" width="360" height="180" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="cRYnzpdCW-J0f_YpP3mc-6" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#666666;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-4" target="cRYnzpdCW-J0f_YpP3mc-2" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="10" y="310" as="sourcePoint" />
|
||||
<mxPoint x="60" y="260" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="cRYnzpdCW-J0f_YpP3mc-7" value="" style="endArrow=none;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#666666;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-4" target="cRYnzpdCW-J0f_YpP3mc-2" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="770" y="570" as="sourcePoint" />
|
||||
<mxPoint x="770" y="210" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="cRYnzpdCW-J0f_YpP3mc-8" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;strokeColor=#666666;" parent="1" source="cRYnzpdCW-J0f_YpP3mc-2" target="cRYnzpdCW-J0f_YpP3mc-5" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="220.00000000000023" y="179.69" as="sourcePoint" />
|
||||
<mxPoint x="740.0000000000002" y="179.69" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="cRYnzpdCW-J0f_YpP3mc-9" value="<font>outer_position</font>" 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="<font>outer_size</font>" 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="<font>surface_size</font>" 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="<font>surface_position</font>" 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="<font>surface_size</font>" 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="<div>safe_area.top</div>" 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="<div>safe_area.bottom</div>" 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>
|
||||
1
docs/res/coordinate-systems-desktop.svg
Normal file
1
docs/res/coordinate-systems-desktop.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.9 KiB |
1
docs/res/coordinate-systems-mobile.svg
Normal file
1
docs/res/coordinate-systems-mobile.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="720" viewBox="-0.5 -0.5 720 720" class="ge-export-svg-auto"><defs><style><![CDATA[@media (prefers-color-scheme:dark){svg.ge-export-svg-auto svg:not(mjx-container>svg),svg.ge-export-svg-auto:not(mjx-container>svg){filter:invert(100%) hue-rotate(180deg)}}]]></style></defs><g data-cell-id="0"><g data-cell-id="1"><rect x="200" y="40" width="320" height="640" rx="48" ry="48" fill="#e8e8e8" stroke="#666" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-1"/><rect x="210" y="50" width="300" height="620" rx="45" ry="45" fill="#d5e8d4" stroke="#82b366" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-2"/><path fill="#ffe6cc" stroke="#d79b00" pointer-events="all" d="M210 90h300v540H210z" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-4"/><rect x="290" y="640" width="140" height="10" rx="1.5" ry="1.5" fill="#dbdbdb" stroke="#666" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-20"/><rect x="300" y="50" width="120" height="30" rx="4.5" ry="4.5" fill="#dbdbdb" stroke="#666" pointer-events="all" data-cell-id="RxwCrVmIsQwV7z5iJ9nY-3"/><g data-cell-id="RxwCrVmIsQwV7z5iJ9nY-12"><path d="M510 50h50v620h-50" fill="none" stroke="#82b366" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><switch transform="translate(-.5 -.5)"><foreignObject style="overflow:visible;text-align:left" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe flex-start;width:1px;height:1px;padding-top:360px;margin-left:575px"><div style="box-sizing:border-box;font-size:0;text-align:left" data-drawio-colors="color: #82B366;"><div style="display:inline-block;font-size:20px;font-family:"monospace";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=""monospace"" 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:"monospace";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=""monospace"" 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:"monospace";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=""monospace"" font-size="20" text-anchor="end">safe_...</text></switch></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -11,7 +11,7 @@ Unreleased` header.
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Add `Rect`, `PhysicalRect` and `LogicalRect`.
|
||||
- Added `Insets`, `LogicalInsets` and `PhysicalInsets` types.
|
||||
|
||||
## 0.1.1
|
||||
|
||||
|
||||
128
dpi/src/lib.rs
128
dpi/src/lib.rs
@@ -759,24 +759,30 @@ impl<P: Pixel> From<LogicalPosition<P>> for Position {
|
||||
}
|
||||
}
|
||||
|
||||
/// A rectangle represented in logical pixels.
|
||||
/// The logical distance between the edges of two rectangles.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct LogicalRect<P> {
|
||||
pub origin: LogicalPosition<P>,
|
||||
pub size: LogicalSize<P>,
|
||||
pub struct LogicalInsets<P> {
|
||||
/// The distance to the top edge.
|
||||
pub top: P,
|
||||
/// The distance to the left edge.
|
||||
pub left: P,
|
||||
/// The distance to the bottom edge.
|
||||
pub bottom: P,
|
||||
/// The distance to the right edge.
|
||||
pub right: P,
|
||||
}
|
||||
|
||||
impl<P> LogicalRect<P> {
|
||||
impl<P> LogicalInsets<P> {
|
||||
#[inline]
|
||||
pub const fn new(origin: LogicalPosition<P>, size: LogicalSize<P>) -> Self {
|
||||
Self { origin, size }
|
||||
pub const fn new(top: P, left: P, bottom: P, right: P) -> Self {
|
||||
Self { top, left, bottom, right }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> LogicalRect<P> {
|
||||
impl<P: Pixel> LogicalInsets<P> {
|
||||
#[inline]
|
||||
pub fn from_physical<T: Into<PhysicalRect<X>>, X: Pixel>(
|
||||
pub fn from_physical<T: Into<PhysicalInsets<X>>, X: Pixel>(
|
||||
physical: T,
|
||||
scale_factor: f64,
|
||||
) -> Self {
|
||||
@@ -784,92 +790,116 @@ impl<P: Pixel> LogicalRect<P> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalRect<X> {
|
||||
let origin = self.origin.to_physical(scale_factor);
|
||||
let size = self.size.to_physical(scale_factor);
|
||||
PhysicalRect::new(origin, size)
|
||||
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalInsets<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let top = self.top.into() * scale_factor;
|
||||
let left = self.left.into() * scale_factor;
|
||||
let bottom = self.bottom.into() * scale_factor;
|
||||
let right = self.right.into() * scale_factor;
|
||||
PhysicalInsets::new(top, left, bottom, right).cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cast<X: Pixel>(&self) -> LogicalRect<X> {
|
||||
LogicalRect { origin: self.origin.cast(), size: self.size.cast() }
|
||||
pub fn cast<X: Pixel>(&self) -> LogicalInsets<X> {
|
||||
LogicalInsets {
|
||||
top: self.top.cast(),
|
||||
left: self.left.cast(),
|
||||
bottom: self.bottom.cast(),
|
||||
right: self.right.cast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A rectangle represented in physical pixels.
|
||||
/// The physical distance between the edges of two rectangles.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct PhysicalRect<P> {
|
||||
pub origin: PhysicalPosition<P>,
|
||||
pub size: PhysicalSize<P>,
|
||||
pub struct PhysicalInsets<P> {
|
||||
/// The distance to the top edge.
|
||||
pub top: P,
|
||||
/// The distance to the left edge.
|
||||
pub left: P,
|
||||
/// The distance to the bottom edge.
|
||||
pub bottom: P,
|
||||
/// The distance to the right edge.
|
||||
pub right: P,
|
||||
}
|
||||
|
||||
impl<P> PhysicalRect<P> {
|
||||
impl<P> PhysicalInsets<P> {
|
||||
#[inline]
|
||||
pub const fn new(origin: PhysicalPosition<P>, size: PhysicalSize<P>) -> Self {
|
||||
Self { origin, size }
|
||||
pub const fn new(top: P, left: P, bottom: P, right: P) -> Self {
|
||||
Self { top, left, bottom, right }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> PhysicalRect<P> {
|
||||
impl<P: Pixel> PhysicalInsets<P> {
|
||||
#[inline]
|
||||
pub fn from_logical<T: Into<LogicalRect<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
|
||||
pub fn from_logical<T: Into<LogicalInsets<X>>, X: Pixel>(
|
||||
logical: T,
|
||||
scale_factor: f64,
|
||||
) -> Self {
|
||||
logical.into().to_physical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalRect<X> {
|
||||
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalInsets<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let origin = self.origin.to_logical(scale_factor);
|
||||
let size = self.size.to_logical(scale_factor);
|
||||
LogicalRect::new(origin, size)
|
||||
let top = self.top.into() / scale_factor;
|
||||
let left = self.left.into() / scale_factor;
|
||||
let bottom = self.bottom.into() / scale_factor;
|
||||
let right = self.right.into() / scale_factor;
|
||||
LogicalInsets::new(top, left, bottom, right).cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cast<X: Pixel>(&self) -> PhysicalRect<X> {
|
||||
PhysicalRect { origin: self.origin.cast(), size: self.size.cast() }
|
||||
pub fn cast<X: Pixel>(&self) -> PhysicalInsets<X> {
|
||||
PhysicalInsets {
|
||||
top: self.top.cast(),
|
||||
left: self.left.cast(),
|
||||
bottom: self.bottom.cast(),
|
||||
right: self.right.cast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A rectangle that's either physical or logical.
|
||||
/// Insets that are either physical or logical.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Rect {
|
||||
Physical(PhysicalRect<i32>),
|
||||
Logical(LogicalRect<f64>),
|
||||
pub enum Insets {
|
||||
Physical(PhysicalInsets<u32>),
|
||||
Logical(LogicalInsets<f64>),
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn new<R: Into<Self>>(rect: R) -> Self {
|
||||
rect.into()
|
||||
impl Insets {
|
||||
pub fn new<S: Into<Self>>(insets: S) -> Self {
|
||||
insets.into()
|
||||
}
|
||||
|
||||
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalRect<P> {
|
||||
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalInsets<P> {
|
||||
match *self {
|
||||
Self::Physical(rect) => rect.to_logical(scale_factor),
|
||||
Self::Logical(rect) => rect.cast(),
|
||||
Self::Physical(insets) => insets.to_logical(scale_factor),
|
||||
Self::Logical(insets) => insets.cast(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalRect<P> {
|
||||
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalInsets<P> {
|
||||
match *self {
|
||||
Self::Physical(rect) => rect.cast(),
|
||||
Self::Logical(rect) => rect.to_physical(scale_factor),
|
||||
Self::Physical(insets) => insets.cast(),
|
||||
Self::Logical(insets) => insets.to_physical(scale_factor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalRect<P>> for Rect {
|
||||
impl<P: Pixel> From<PhysicalInsets<P>> for Insets {
|
||||
#[inline]
|
||||
fn from(rect: PhysicalRect<P>) -> Self {
|
||||
Self::Physical(rect.cast())
|
||||
fn from(insets: PhysicalInsets<P>) -> Self {
|
||||
Self::Physical(insets.cast())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalRect<P>> for Rect {
|
||||
impl<P: Pixel> From<LogicalInsets<P>> for Insets {
|
||||
#[inline]
|
||||
fn from(rect: LogicalRect<P>) -> Self {
|
||||
Self::Logical(rect.cast())
|
||||
fn from(insets: LogicalInsets<P>) -> Self {
|
||||
Self::Logical(insets.cast())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
pub use platform::cleanup_window;
|
||||
pub use platform::fill_window;
|
||||
|
||||
#[cfg(all(feature = "rwh_06", not(any(target_os = "android", target_os = "ios"))))]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
mod platform {
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
@@ -106,7 +106,7 @@ mod platform {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "rwh_06", not(any(target_os = "android", target_os = "ios")))))]
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
mod platform {
|
||||
pub fn fill_window(_window: &dyn winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
|
||||
@@ -31,6 +31,8 @@ use winit::platform::startup_notify::{
|
||||
};
|
||||
#[cfg(web_platform)]
|
||||
use winit::platform::web::{ActiveEventLoopExtWeb, CustomCursorExtWeb, WindowAttributesExtWeb};
|
||||
#[cfg(x11_platform)]
|
||||
use winit::platform::x11::WindowAttributesExtX11;
|
||||
use winit::window::{
|
||||
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection,
|
||||
Theme, Window, WindowAttributes, WindowId,
|
||||
@@ -149,6 +151,28 @@ impl Application {
|
||||
window_attributes = window_attributes.with_activation_token(token);
|
||||
}
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
match std::env::var("X11_VISUAL_ID") {
|
||||
Ok(visual_id_str) => {
|
||||
info!("Using X11 visual id {visual_id_str}");
|
||||
let visual_id = visual_id_str.parse()?;
|
||||
window_attributes = window_attributes.with_x11_visual(visual_id);
|
||||
},
|
||||
Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"),
|
||||
}
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
match std::env::var("X11_SCREEN_ID") {
|
||||
Ok(screen_id_str) => {
|
||||
info!("Placing the window on X11 screen {screen_id_str}");
|
||||
let screen_id = screen_id_str.parse()?;
|
||||
window_attributes = window_attributes.with_x11_screen(screen_id);
|
||||
},
|
||||
Err(_) => info!(
|
||||
"Set the X11_SCREEN_ID env variable to place the window on non-default screen"
|
||||
),
|
||||
}
|
||||
|
||||
#[cfg(macos_platform)]
|
||||
if let Some(tab_id) = _tab_id {
|
||||
window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
|
||||
@@ -218,6 +242,10 @@ impl Application {
|
||||
Action::ToggleResizable => window.toggle_resizable(),
|
||||
Action::ToggleDecorations => window.toggle_decorations(),
|
||||
Action::ToggleFullscreen => window.toggle_fullscreen(),
|
||||
#[cfg(macos_platform)]
|
||||
Action::ToggleSimpleFullscreen => {
|
||||
window.window.set_simple_fullscreen(!window.window.simple_fullscreen());
|
||||
},
|
||||
Action::ToggleMaximize => window.toggle_maximize(),
|
||||
Action::ToggleImeInput => window.toggle_ime(),
|
||||
Action::Minimize => window.minimize(),
|
||||
@@ -917,18 +945,38 @@ impl WindowState {
|
||||
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()?;
|
||||
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();
|
||||
buffer.present()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -965,6 +1013,8 @@ enum Action {
|
||||
ToggleDecorations,
|
||||
ToggleResizable,
|
||||
ToggleFullscreen,
|
||||
#[cfg(macos_platform)]
|
||||
ToggleSimpleFullscreen,
|
||||
ToggleMaximize,
|
||||
Minimize,
|
||||
NextCursor,
|
||||
@@ -998,6 +1048,8 @@ impl Action {
|
||||
Action::ToggleDecorations => "Toggle decorations",
|
||||
Action::ToggleResizable => "Toggle window resizable state",
|
||||
Action::ToggleFullscreen => "Toggle fullscreen",
|
||||
#[cfg(macos_platform)]
|
||||
Action::ToggleSimpleFullscreen => "Toggle simple fullscreen",
|
||||
Action::ToggleMaximize => "Maximize",
|
||||
Action::Minimize => "Minimize",
|
||||
Action::ToggleResizeIncrements => "Use resize increments when resizing window",
|
||||
@@ -1140,6 +1192,8 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[
|
||||
Binding::new("Q", ModifiersState::CONTROL, Action::CloseWindow),
|
||||
Binding::new("H", ModifiersState::CONTROL, Action::PrintHelp),
|
||||
Binding::new("F", ModifiersState::CONTROL, Action::ToggleFullscreen),
|
||||
#[cfg(macos_platform)]
|
||||
Binding::new("F", ModifiersState::ALT, Action::ToggleSimpleFullscreen),
|
||||
Binding::new("D", ModifiersState::CONTROL, Action::ToggleDecorations),
|
||||
Binding::new("I", ModifiersState::CONTROL, Action::ToggleImeInput),
|
||||
Binding::new("L", ModifiersState::CONTROL, Action::CycleCursorGrab),
|
||||
|
||||
@@ -59,15 +59,23 @@ changelog entry.
|
||||
- Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd`
|
||||
and `Serialize` on many types.
|
||||
- Add `MonitorHandle::current_video_mode()`.
|
||||
- On Android, the soft keyboard can now be shown using `Window::set_ime_allowed`.
|
||||
- Add basic iOS IME support. The soft keyboard can now be shown using `Window::set_ime_allowed`.
|
||||
- Add `ApplicationHandlerExtMacOS` trait, and a `macos_handler` method to `ApplicationHandler` which returns a `dyn ApplicationHandlerExtMacOS` which allows for macOS specific extensions to winit.
|
||||
- Add a `standard_key_binding` method to the `ApplicationHandlerExtMacOS` trait. This allows handling of standard keybindings such as "go to end of line" on macOS.
|
||||
- On macOS, add `WindowExtMacOS::set_borderless_game` and `WindowAttributesExtMacOS::with_borderless_game`
|
||||
to fully disable the menu bar and dock in Borderless Fullscreen as commonly done in games.
|
||||
- On macOS, add `WindowExtMacOS::set_unified_titlebar` and `WindowAttributesExtMacOS::with_unified_titlebar`
|
||||
to use a larger style of titlebar.
|
||||
- Add `WindowId::into_raw()` and `from_raw()`.
|
||||
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId` and `position` to all pointer
|
||||
events as part of the pointer event overhaul.
|
||||
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId`, `primary` and `position` to all
|
||||
pointer events as part of the pointer event overhaul.
|
||||
- Add `DeviceId::into_raw()` and `from_raw()`.
|
||||
- On X11, the `window` example now understands the `X11_VISUAL_ID` and `X11_SCREEN_ID` env
|
||||
variables to test the respective modifiers of window creation.
|
||||
- Added `Window::surface_position`, which is the position of the surface inside the window.
|
||||
- Added `Window::safe_area`, which describes the area of the surface that is unobstructed.
|
||||
- On X11, Wayland, Windows and macOS, improved scancode conversions for more obscure key codes.
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -136,6 +144,8 @@ changelog entry.
|
||||
- Rename `CursorEntered` to `PointerEntered`.
|
||||
- Rename `CursorLeft` to `PointerLeft`.
|
||||
- Rename `MouseInput` to `PointerButton`.
|
||||
- Add `primary` to every `PointerEvent` as a way to identify discard non-primary pointers in a
|
||||
multi-touch interaction.
|
||||
- Add `position` to every `PointerEvent`.
|
||||
- `PointerMoved` is **not sent** after `PointerEntered` anymore.
|
||||
- Remove `Touch`, which is folded into the `Pointer*` events.
|
||||
@@ -148,10 +158,13 @@ changelog entry.
|
||||
type to a generic mouse button.
|
||||
- New `FingerId` added to `PointerKind::Touch` and `PointerSource::Touch` able to uniquely
|
||||
identify a finger in a multi-touch interaction. Replaces the old `Touch::id`.
|
||||
- On Web and Windows, add `FingerIdExt*::is_primary()`, exposing a way to determine
|
||||
the primary finger in a multi-touch interaction.
|
||||
- In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`.
|
||||
- Remove `Force::Calibrated::altitude_angle`.
|
||||
- On X11, use bottom-right corner for IME hotspot in `Window::set_ime_cursor_area`.
|
||||
- On macOS and iOS, no longer emit `ScaleFactorChanged` upon window creation.
|
||||
- On macOS, no longer emit `Focused` upon window creation.
|
||||
- On iOS, emit more events immediately, instead of queuing them.
|
||||
- Update `smol_str` to version `0.3`
|
||||
|
||||
### Removed
|
||||
|
||||
@@ -183,6 +196,7 @@ changelog entry.
|
||||
- Remove `WindowEvent::Touch` and `Touch` in favor of the new `PointerKind`, `PointerSource` and
|
||||
`ButtonSource` as part of the new pointer event overhaul.
|
||||
- Remove `Force::altitude_angle`.
|
||||
- Removed `Window::inner_position`, use the new `Window::surface_position` instead.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -192,3 +206,13 @@ changelog entry.
|
||||
default activation policy, unless explicitly provided during initialization.
|
||||
- On macOS, fix crash when calling `drag_window()` without a left click present.
|
||||
- On X11, key events forward to IME anyway, even when it's disabled.
|
||||
- On Windows, make `ControlFlow::WaitUntil` work more precisely using `CREATE_WAITABLE_TIMER_HIGH_RESOLUTION`.
|
||||
- On X11, creating windows on screen that is not the first one (e.g. `DISPLAY=:0.1`) works again.
|
||||
- On X11, creating windows while passing `with_x11_screen(non_default_screen)` works again.
|
||||
- On X11, fix XInput handling that prevented a new window from getting the focus in some cases.
|
||||
- On iOS, fixed `SurfaceResized` and `Window::surface_size` not reporting the size of the actual surface.
|
||||
- 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.
|
||||
|
||||
110
src/event.rs
110
src/event.rs
@@ -153,10 +153,16 @@ pub enum WindowEvent {
|
||||
/// Contains the new dimensions of the surface (can also be retrieved with
|
||||
/// [`Window::surface_size`]).
|
||||
///
|
||||
/// This event will not necessarily be emitted upon window creation, query
|
||||
/// [`Window::surface_size`] if you need to determine the surface's initial size.
|
||||
///
|
||||
/// [`Window::surface_size`]: crate::window::Window::surface_size
|
||||
SurfaceResized(PhysicalSize<u32>),
|
||||
|
||||
/// The position of the window has changed. Contains the window's new position.
|
||||
/// The position of the window has changed.
|
||||
///
|
||||
/// Contains the window's new position in desktop coordinates (can also be retrieved with
|
||||
/// [`Window::outer_position`]).
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
@@ -169,18 +175,23 @@ pub enum WindowEvent {
|
||||
/// The window has been 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.
|
||||
///
|
||||
/// When the user hovers multiple files at once, this event will be emitted for each file
|
||||
/// separately.
|
||||
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.
|
||||
///
|
||||
/// There will be a single `HoveredFileCancelled` event triggered even if multiple files were
|
||||
@@ -190,6 +201,9 @@ pub enum WindowEvent {
|
||||
/// The window gained or lost focus.
|
||||
///
|
||||
/// The parameter is true if the window has gained focus, and false if it has lost focus.
|
||||
///
|
||||
/// Windows are unfocused upon creation, but will usually be focused by the system soon
|
||||
/// afterwards.
|
||||
Focused(bool),
|
||||
|
||||
/// An event from the keyboard has been received.
|
||||
@@ -198,6 +212,7 @@ pub enum WindowEvent {
|
||||
/// - **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
|
||||
/// events which are not marked as `is_synthetic`.
|
||||
/// - **iOS:** Unsupported.
|
||||
KeyboardInput {
|
||||
device_id: Option<DeviceId>,
|
||||
event: KeyEvent,
|
||||
@@ -245,6 +260,12 @@ pub enum WindowEvent {
|
||||
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||
position: PhysicalPosition<f64>,
|
||||
|
||||
/// Indicates whether the event is created by a primary pointer.
|
||||
///
|
||||
/// A pointer is considered primary when it's a mouse, the first finger in a multi-touch
|
||||
/// interaction, or an unknown pointer source.
|
||||
primary: bool,
|
||||
|
||||
source: PointerSource,
|
||||
},
|
||||
|
||||
@@ -264,6 +285,12 @@ pub enum WindowEvent {
|
||||
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||
position: PhysicalPosition<f64>,
|
||||
|
||||
/// Indicates whether the event is created by a primary pointer.
|
||||
///
|
||||
/// A pointer is considered primary when it's a mouse, the first finger in a multi-touch
|
||||
/// interaction, or an unknown pointer source.
|
||||
primary: bool,
|
||||
|
||||
kind: PointerKind,
|
||||
},
|
||||
|
||||
@@ -284,6 +311,12 @@ pub enum WindowEvent {
|
||||
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||
position: Option<PhysicalPosition<f64>>,
|
||||
|
||||
/// Indicates whether the event is created by a primary pointer.
|
||||
///
|
||||
/// A pointer is considered primary when it's a mouse, the first finger in a multi-touch
|
||||
/// interaction, or an unknown pointer source.
|
||||
primary: bool,
|
||||
|
||||
kind: PointerKind,
|
||||
},
|
||||
|
||||
@@ -307,6 +340,12 @@ pub enum WindowEvent {
|
||||
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||
position: PhysicalPosition<f64>,
|
||||
|
||||
/// Indicates whether the event is created by a primary pointer.
|
||||
///
|
||||
/// A pointer is considered primary when it's a mouse, the first finger in a multi-touch
|
||||
/// interaction, or an unknown pointer source.
|
||||
primary: bool,
|
||||
|
||||
button: ButtonSource,
|
||||
},
|
||||
|
||||
@@ -377,10 +416,18 @@ pub enum WindowEvent {
|
||||
|
||||
/// Touchpad pressure event.
|
||||
///
|
||||
/// At the moment, only supported on Apple forcetouch-capable macbooks.
|
||||
/// The parameters are: pressure level (value between 0 and 1 representing how hard the
|
||||
/// touchpad is being pressed) and stage (integer representing the click level).
|
||||
TouchpadPressure { device_id: Option<DeviceId>, pressure: f32, stage: i64 },
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS**: Only supported on Apple forcetouch-capable macbooks.
|
||||
/// - **Android / iOS / Wayland / X11 / Windows / Orbital / Web:** Unsupported.
|
||||
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.
|
||||
///
|
||||
@@ -393,7 +440,12 @@ pub enum WindowEvent {
|
||||
/// To update the window size, use the provided [`SurfaceSizeWriter`] handle. By default, the
|
||||
/// window is resized to the value suggested by the OS, but it can be changed to any value.
|
||||
///
|
||||
/// This event will not necessarily be emitted upon window creation, query
|
||||
/// [`Window::scale_factor`] if you need to determine the window's initial scale factor.
|
||||
///
|
||||
/// For more information about DPI in general, see the [`dpi`] crate.
|
||||
///
|
||||
/// [`Window::scale_factor`]: crate::window::Window::scale_factor
|
||||
ScaleFactorChanged {
|
||||
scale_factor: f64,
|
||||
/// Handle to update surface size during scale changes.
|
||||
@@ -445,13 +497,15 @@ pub enum WindowEvent {
|
||||
|
||||
/// Emitted when a window should be redrawn.
|
||||
///
|
||||
/// This gets triggered in two scenarios:
|
||||
/// This gets triggered in a few scenarios:
|
||||
/// - The OS has performed an operation that's invalidated the window's contents (such as
|
||||
/// resizing the window).
|
||||
/// resizing the window, or changing [the safe area]).
|
||||
/// - The application has explicitly requested a redraw via [`Window::request_redraw`].
|
||||
///
|
||||
/// Winit will aggregate duplicate redraw requests into a single event, to
|
||||
/// help avoid duplicating rendering work.
|
||||
///
|
||||
/// [the safe area]: crate::window::Window::safe_area
|
||||
RedrawRequested,
|
||||
}
|
||||
|
||||
@@ -610,12 +664,23 @@ impl DeviceId {
|
||||
/// Whenever a touch event is received it contains a `FingerId` which uniquely identifies the finger
|
||||
/// used for the current interaction.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId(pub(crate) platform_impl::FingerId);
|
||||
pub struct FingerId(pub(crate) usize);
|
||||
|
||||
impl FingerId {
|
||||
#[cfg(test)]
|
||||
pub(crate) const fn dummy() -> Self {
|
||||
FingerId(platform_impl::FingerId::dummy())
|
||||
/// Convert the [`FingerId`] into the underlying integer.
|
||||
///
|
||||
/// This is useful if you need to pass the ID across an FFI boundary, or store it in an atomic.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) const fn into_raw(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Construct a [`FingerId`] from the underlying integer.
|
||||
///
|
||||
/// This should only be called with integers returned from [`FingerId::into_raw`].
|
||||
#[allow(dead_code)]
|
||||
pub(crate) const fn from_raw(id: usize) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1130,7 +1195,7 @@ mod tests {
|
||||
($closure:expr) => {{
|
||||
#[allow(unused_mut)]
|
||||
let mut x = $closure;
|
||||
let fid = event::FingerId::dummy();
|
||||
let fid = event::FingerId::from_raw(0);
|
||||
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
@@ -1162,16 +1227,19 @@ mod tests {
|
||||
with_window_event(Ime(Enabled));
|
||||
with_window_event(PointerMoved {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: (0, 0).into(),
|
||||
source: PointerSource::Mouse,
|
||||
});
|
||||
with_window_event(ModifiersChanged(event::Modifiers::default()));
|
||||
with_window_event(PointerEntered {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: (0, 0).into(),
|
||||
kind: PointerKind::Mouse,
|
||||
});
|
||||
with_window_event(PointerLeft {
|
||||
primary: true,
|
||||
device_id: None,
|
||||
position: Some((0, 0).into()),
|
||||
kind: PointerKind::Mouse,
|
||||
@@ -1183,12 +1251,14 @@ mod tests {
|
||||
});
|
||||
with_window_event(PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: event::ElementState::Pressed,
|
||||
position: (0, 0).into(),
|
||||
button: event::MouseButton::Other(0).into(),
|
||||
});
|
||||
with_window_event(PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: event::ElementState::Released,
|
||||
position: (0, 0).into(),
|
||||
button: event::ButtonSource::Touch {
|
||||
@@ -1258,11 +1328,11 @@ mod tests {
|
||||
#[test]
|
||||
fn ensure_attrs_do_not_panic() {
|
||||
foreach_event!(|event: event::Event| {
|
||||
let _ = format!("{:?}", event);
|
||||
let _ = format!("{event:?}");
|
||||
});
|
||||
let _ = event::StartCause::Init.clone();
|
||||
|
||||
let fid = crate::event::FingerId::dummy().clone();
|
||||
let fid = crate::event::FingerId::from_raw(0).clone();
|
||||
HashSet::new().insert(fid);
|
||||
let mut set = [fid, fid, fid];
|
||||
set.sort_unstable();
|
||||
|
||||
@@ -13,9 +13,11 @@ use std::marker::PhantomData;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
|
||||
#[cfg(web_platform)]
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
@@ -273,10 +275,9 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for EventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
|
||||
impl HasDisplayHandle for EventLoop {
|
||||
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
|
||||
HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,13 +408,11 @@ pub trait ActiveEventLoop: AsAny {
|
||||
fn owned_display_handle(&self) -> OwnedDisplayHandle;
|
||||
|
||||
/// Get the raw-window-handle handle.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
|
||||
fn rwh_06_handle(&self) -> &dyn HasDisplayHandle;
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
impl HasDisplayHandle for dyn ActiveEventLoop + '_ {
|
||||
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
|
||||
self.rwh_06_handle().display_handle()
|
||||
}
|
||||
}
|
||||
@@ -430,36 +429,55 @@ impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
|
||||
///
|
||||
/// - A zero-sized type that is likely optimized out.
|
||||
/// - A reference-counted pointer to the underlying type.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone)]
|
||||
pub struct OwnedDisplayHandle {
|
||||
#[cfg_attr(not(feature = "rwh_06"), allow(dead_code))]
|
||||
pub(crate) platform: platform_impl::OwnedDisplayHandle,
|
||||
pub(crate) handle: Arc<dyn HasDisplayHandle>,
|
||||
}
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
pub(crate) fn new(handle: Arc<dyn HasDisplayHandle>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
}
|
||||
|
||||
impl HasDisplayHandle for OwnedDisplayHandle {
|
||||
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
|
||||
self.handle.display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for OwnedDisplayHandle {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
|
||||
#[inline]
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.platform.raw_display_handle_rwh_06()?;
|
||||
|
||||
// SAFETY: The underlying display handle should be safe.
|
||||
let handle = unsafe { rwh_06::DisplayHandle::borrow_raw(raw) };
|
||||
|
||||
Ok(handle)
|
||||
impl PartialEq for OwnedDisplayHandle {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self.display_handle(), other.display_handle()) {
|
||||
(Ok(lhs), Ok(rhs)) => lhs == rhs,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for OwnedDisplayHandle {}
|
||||
|
||||
pub(crate) trait EventLoopProxyProvider: Send + Sync {
|
||||
/// See [`EventLoopProxy::wake_up`] for details.
|
||||
fn wake_up(&self);
|
||||
}
|
||||
|
||||
/// Control the [`EventLoop`], possibly from a different thread, without referencing it directly.
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
pub(crate) event_loop_proxy: platform_impl::EventLoopProxy,
|
||||
pub(crate) proxy: Arc<dyn EventLoopProxyProvider>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventLoopProxy {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EventLoopProxy").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
@@ -479,13 +497,11 @@ impl EventLoopProxy {
|
||||
///
|
||||
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
|
||||
pub fn wake_up(&self) {
|
||||
self.event_loop_proxy.wake_up();
|
||||
self.proxy.wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventLoopProxy {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ActiveEventLoop").finish_non_exhaustive()
|
||||
pub(crate) fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self {
|
||||
Self { proxy }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
125
src/lib.rs
125
src/lib.rs
@@ -1,5 +1,59 @@
|
||||
//! 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
|
||||
//!
|
||||
//! Before you can create a [`Window`], you first need to build an [`EventLoop`]. This is done with
|
||||
@@ -7,7 +61,12 @@
|
||||
//!
|
||||
//! ```no_run
|
||||
//! 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`].
|
||||
@@ -83,19 +142,22 @@
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! let event_loop = EventLoop::new().unwrap();
|
||||
//! # // Intentionally use `fn main` for clarity
|
||||
//! fn main() {
|
||||
//! let event_loop = EventLoop::new().unwrap();
|
||||
//!
|
||||
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
|
||||
//! // dispatched any events. This is ideal for games and similar applications.
|
||||
//! event_loop.set_control_flow(ControlFlow::Poll);
|
||||
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
|
||||
//! // dispatched any events. This is ideal for games and similar applications.
|
||||
//! event_loop.set_control_flow(ControlFlow::Poll);
|
||||
//!
|
||||
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
|
||||
//! // This is ideal for non-game applications that only update in response to user
|
||||
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
|
||||
//! event_loop.set_control_flow(ControlFlow::Wait);
|
||||
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
|
||||
//! // This is ideal for non-game applications that only update in response to user
|
||||
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
|
||||
//! event_loop.set_control_flow(ControlFlow::Wait);
|
||||
//!
|
||||
//! let mut app = App::default();
|
||||
//! event_loop.run_app(&mut app);
|
||||
//! let mut app = App::default();
|
||||
//! event_loop.run_app(&mut app);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
|
||||
@@ -115,6 +177,45 @@
|
||||
//! [`visible` set to `false`][crate::window::WindowAttributes::with_visible] and explicitly make
|
||||
//! the window visible only once you're ready to render into it.
|
||||
//!
|
||||
//! There is another important concept you need to know about when drawing: the "safe area". This
|
||||
//! can be accessed with [`Window::safe_area`], and describes a rectangle in the surface that is not
|
||||
//! obscured by notches, the status bar, and so on. You should be drawing your background and
|
||||
//! non-important content on the entire surface, but restrict important content (such as
|
||||
//! interactable UIs, text, etc.) to only being drawn inside the safe area.
|
||||
//!
|
||||
//! [`Window::safe_area`]: crate::window::Window::safe_area
|
||||
//!
|
||||
//! # Coordinate systems
|
||||
//!
|
||||
//! Windowing systems use many different coordinate systems, and this is reflected in Winit as well;
|
||||
//! there are "desktop coordinates", which is the coordinates of a window or monitor relative to the
|
||||
//! desktop at large, "window coordinates" which is the coordinates of the surface, relative to the
|
||||
//! window, and finally "surface coordinates", which is the coordinates relative to the drawn
|
||||
//! surface. All of these coordinates are relative to the top-left corner of their respective
|
||||
//! origin.
|
||||
//!
|
||||
//! Most of the functionality in Winit works with surface coordinates, so usually you only need to
|
||||
//! concern yourself with those. In case you need to convert to some other coordinate system, Winit
|
||||
//! provides [`Window::surface_position`] and [`Window::surface_size`] to describe the surface's
|
||||
//! location in window coordinates, and Winit provides [`Window::outer_position`] and
|
||||
//! [`Window::outer_size`] to describe the window's location in desktop coordinates. Using these
|
||||
//! methods, you should be able to convert a position in one coordinate system to another.
|
||||
//!
|
||||
//! An overview of how these four methods fit together can be seen in the image below:
|
||||
#![doc = concat!("\n\n", include_str!("../docs/res/coordinate-systems-desktop.svg"), "\n\n")] // Rustfmt removes \n, adding them like this works around that.
|
||||
//! On mobile, the situation is usually a bit different; because of the smaller screen space,
|
||||
//! windows usually fill the whole screen at a time, and as such there is _rarely_ a difference
|
||||
//! between these three coordinate systems, although you should still strive to handle this, as
|
||||
//! they're still relevant in more niche area such as Mac Catalyst, or multi-tasking on tablets.
|
||||
//!
|
||||
//! This is illustrated in the image below, along with the safe area since it's often relevant on
|
||||
//! mobile.
|
||||
#![doc = concat!("\n\n", include_str!("../docs/res/coordinate-systems-mobile.svg"), "\n\n")] // Rustfmt removes \n, adding them like this works around that.
|
||||
//! [`Window::surface_position`]: crate::window::Window::surface_position
|
||||
//! [`Window::surface_size`]: crate::window::Window::surface_size
|
||||
//! [`Window::outer_position`]: crate::window::Window::outer_position
|
||||
//! [`Window::outer_size`]: crate::window::Window::outer_size
|
||||
//!
|
||||
//! # UI scaling
|
||||
//!
|
||||
//! UI scaling is important, go read the docs for the [`dpi`] crate for an
|
||||
@@ -230,11 +331,11 @@
|
||||
// doc
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#![warn(clippy::uninlined_format_args)]
|
||||
|
||||
// Re-export DPI types so that users don't have to put it in Cargo.toml.
|
||||
#[doc(inline)]
|
||||
pub use dpi;
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub use rwh_06 as raw_window_handle;
|
||||
|
||||
pub mod application;
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
//! 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 crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::platform_impl;
|
||||
|
||||
/// Describes a fullscreen video mode of a monitor.
|
||||
/// A handle to a fullscreen video mode of a specific monitor.
|
||||
///
|
||||
/// Can be acquired with [`MonitorHandle::video_modes`].
|
||||
/// This can be acquired with [`MonitorHandle::video_modes`].
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VideoModeHandle {
|
||||
pub(crate) video_mode: platform_impl::VideoModeHandle,
|
||||
@@ -92,7 +86,15 @@ impl std::fmt::Display for VideoModeHandle {
|
||||
|
||||
/// Handle to a monitor.
|
||||
///
|
||||
/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
|
||||
/// Allows you to retrieve basic information and metadata about a monitor.
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
@@ -141,8 +143,11 @@ impl MonitorHandle {
|
||||
self.inner.name()
|
||||
}
|
||||
|
||||
/// Returns the top-left corner position of the monitor relative to the larger full
|
||||
/// screen area.
|
||||
/// Returns the top-left corner position of the monitor in desktop coordinates.
|
||||
///
|
||||
/// This position is in the same coordinate system as [`Window::outer_position`].
|
||||
///
|
||||
/// [`Window::outer_position`]: crate::window::Window::outer_position
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
|
||||
@@ -92,6 +92,9 @@ pub trait WindowExtMacOS {
|
||||
/// This is how fullscreen used to work on macOS in versions before Lion.
|
||||
/// And allows the user to have a fullscreen window without using another
|
||||
/// space or taking control over the entire monitor.
|
||||
///
|
||||
/// Make sure you only draw your important content inside the safe area so that it does not
|
||||
/// overlap with the notch on newer devices, see [`Window::safe_area`] for details.
|
||||
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool;
|
||||
|
||||
/// Returns whether or not the window has shadow.
|
||||
@@ -157,6 +160,13 @@ pub trait WindowExtMacOS {
|
||||
|
||||
/// Getter for the [`WindowExtMacOS::set_borderless_game`].
|
||||
fn is_borderless_game(&self) -> bool;
|
||||
|
||||
/// Makes the titlebar bigger, effectively adding more space around the
|
||||
/// window controls if the titlebar is invisible.
|
||||
fn set_unified_titlebar(&self, unified_titlebar: bool);
|
||||
|
||||
/// Getter for the [`WindowExtMacOS::set_unified_titlebar`].
|
||||
fn unified_titlebar(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WindowExtMacOS for dyn Window + '_ {
|
||||
@@ -255,6 +265,18 @@ impl WindowExtMacOS for dyn Window + '_ {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.is_borderless_game())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_unified_titlebar(&self, unified_titlebar: bool) {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.set_unified_titlebar(unified_titlebar))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unified_titlebar(&self) -> bool {
|
||||
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
|
||||
window.maybe_wait_on_main(|w| w.unified_titlebar())
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to `NSApplicationActivationPolicy`.
|
||||
@@ -308,6 +330,8 @@ pub trait WindowAttributesExtMacOS {
|
||||
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
|
||||
/// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
|
||||
fn with_borderless_game(self, borderless_game: bool) -> Self;
|
||||
/// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set.
|
||||
fn with_unified_titlebar(self, unified_titlebar: bool) -> Self;
|
||||
}
|
||||
|
||||
impl WindowAttributesExtMacOS for WindowAttributes {
|
||||
@@ -382,6 +406,12 @@ impl WindowAttributesExtMacOS for WindowAttributes {
|
||||
self.platform_specific.borderless_game = borderless_game;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self {
|
||||
self.platform_specific.unified_titlebar = unified_titlebar;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EventLoopBuilderExtMacOS {
|
||||
|
||||
@@ -21,6 +21,7 @@ pub mod windows;
|
||||
#[cfg(any(x11_platform, docsrs))]
|
||||
pub mod x11;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(any(
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
|
||||
@@ -57,7 +57,6 @@ use web_sys::HtmlCanvasElement;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::CustomCursorSource;
|
||||
use crate::error::NotSupportedError;
|
||||
use crate::event::FingerId;
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::platform_impl::PlatformCustomCursorSource;
|
||||
@@ -780,16 +779,3 @@ impl Display for OrientationLockError {
|
||||
}
|
||||
|
||||
impl Error for OrientationLockError {}
|
||||
|
||||
/// Additional methods on [`FingerId`] that are specific to Web.
|
||||
pub trait FingerIdExtWeb {
|
||||
/// Indicates if the finger represents the first contact in a multi-touch interaction.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_primary(self) -> bool;
|
||||
}
|
||||
|
||||
impl FingerIdExtWeb for FingerId {
|
||||
fn is_primary(self) -> bool {
|
||||
self.0.is_primary()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
||||
use windows_sys::Win32::Foundation::HANDLE;
|
||||
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::event::{DeviceId, FingerId};
|
||||
use crate::event::DeviceId;
|
||||
use crate::event_loop::EventLoopBuilder;
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::window::{BadIcon, Icon, Window, WindowAttributes};
|
||||
@@ -135,7 +135,6 @@ impl<W: Window> Deref for AnyThread<W> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl<W: Window> rwh_06::HasWindowHandle for AnyThread<W> {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
// SAFETY: The top level user has asserted this is only used safely.
|
||||
@@ -337,7 +336,6 @@ pub trait WindowExtWindows {
|
||||
/// });
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "rwh_06")]
|
||||
unsafe fn window_handle_any_thread(
|
||||
&self,
|
||||
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>;
|
||||
@@ -401,7 +399,6 @@ impl WindowExtWindows for dyn Window + '_ {
|
||||
window.set_corner_preference(preference)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
unsafe fn window_handle_any_thread(
|
||||
&self,
|
||||
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
@@ -435,16 +432,8 @@ pub trait WindowBorrowExtWindows: Borrow<dyn Window> + Sized {
|
||||
/// Win32 APIs.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
#[cfg_attr(
|
||||
feature = "rwh_06",
|
||||
doc = "[`HasWindowHandle`]: rwh_06::HasWindowHandle",
|
||||
doc = "[`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(feature = "rwh_06"),
|
||||
doc = "[`HasWindowHandle`]: #only-available-with-rwh_06",
|
||||
doc = "[`window_handle_any_thread`]: #only-available-with-rwh_06"
|
||||
)]
|
||||
/// [`HasWindowHandle`]: rwh_06::HasWindowHandle
|
||||
/// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread
|
||||
unsafe fn any_thread(self) -> AnyThread<Self>
|
||||
where
|
||||
Self: Window,
|
||||
@@ -670,20 +659,6 @@ impl DeviceIdExtWindows for DeviceId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `FingerId` that are specific to Windows.
|
||||
pub trait FingerIdExtWindows {
|
||||
/// Indicates if the finger represents the first contact in a multi-touch interaction.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_primary(self) -> bool;
|
||||
}
|
||||
|
||||
impl FingerIdExtWindows for FingerId {
|
||||
#[inline]
|
||||
fn is_primary(self) -> bool {
|
||||
self.0.is_primary()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Icon` that are specific to Windows.
|
||||
pub trait IconExtWindows: Sized {
|
||||
/// Create an icon from a file path.
|
||||
|
||||
@@ -13,12 +13,13 @@ use tracing::{debug, trace, warn};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use crate::event::{self, DeviceId, Force, StartCause, SurfaceSizeWriter};
|
||||
use crate::event::{self, DeviceId, FingerId, Force, StartCause, SurfaceSizeWriter};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
@@ -107,6 +108,7 @@ pub struct EventLoop {
|
||||
running: bool,
|
||||
pending_redraw: bool,
|
||||
cause: StartCause,
|
||||
primary_pointer: Option<FingerId>,
|
||||
ignore_volume_keys: bool,
|
||||
combining_accent: Option<char>,
|
||||
}
|
||||
@@ -130,22 +132,24 @@ impl EventLoop {
|
||||
pub(crate) fn new(
|
||||
attributes: &PlatformSpecificEventLoopAttributes,
|
||||
) -> Result<Self, EventLoopError> {
|
||||
let proxy_wake_up = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let android_app = attributes.android_app.as_ref().expect(
|
||||
"An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on \
|
||||
Android",
|
||||
);
|
||||
|
||||
let event_loop_proxy = Arc::new(EventLoopProxy::new(android_app.create_waker()));
|
||||
|
||||
let redraw_flag = SharedFlag::new();
|
||||
|
||||
Ok(Self {
|
||||
android_app: android_app.clone(),
|
||||
primary_pointer: None,
|
||||
window_target: ActiveEventLoop {
|
||||
app: android_app.clone(),
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
exit: Cell::new(false),
|
||||
redraw_requester: RedrawRequester::new(&redraw_flag, android_app.create_waker()),
|
||||
proxy_wake_up,
|
||||
event_loop_proxy,
|
||||
},
|
||||
redraw_flag,
|
||||
loop_running: false,
|
||||
@@ -274,7 +278,7 @@ impl EventLoop {
|
||||
},
|
||||
}
|
||||
|
||||
if self.window_target.proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||
if self.window_target.event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
|
||||
app.proxy_wake_up(&self.window_target);
|
||||
}
|
||||
|
||||
@@ -333,36 +337,83 @@ impl EventLoop {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(pointers) = pointers {
|
||||
for pointer in pointers {
|
||||
let tool_type = pointer.tool_type();
|
||||
let position =
|
||||
PhysicalPosition { x: pointer.x() as _, y: pointer.y() as _ };
|
||||
trace!(
|
||||
"Input event {device_id:?}, {action:?}, loc={position:?}, \
|
||||
pointer={pointer:?}, tool_type={tool_type:?}"
|
||||
);
|
||||
let finger_id = event::FingerId(FingerId(pointer.pointer_id()));
|
||||
let force = Some(Force::Normalized(pointer.pressure() as f64));
|
||||
for pointer in pointers.into_iter().flatten() {
|
||||
let tool_type = pointer.tool_type();
|
||||
let position = PhysicalPosition { x: pointer.x() as _, y: pointer.y() as _ };
|
||||
trace!(
|
||||
"Input event {device_id:?}, {action:?}, loc={position:?}, \
|
||||
pointer={pointer:?}, tool_type={tool_type:?}"
|
||||
);
|
||||
let finger_id = FingerId::from_raw(pointer.pointer_id() as usize);
|
||||
let force = Some(Force::Normalized(pointer.pressure() as f64));
|
||||
|
||||
match action {
|
||||
MotionAction::Down | MotionAction::PointerDown => {
|
||||
let event = event::WindowEvent::PointerEntered {
|
||||
device_id,
|
||||
position,
|
||||
kind: match tool_type {
|
||||
android_activity::input::ToolType::Finger => {
|
||||
event::PointerKind::Touch(finger_id)
|
||||
},
|
||||
// TODO mouse events
|
||||
android_activity::input::ToolType::Mouse => continue,
|
||||
_ => event::PointerKind::Unknown,
|
||||
match action {
|
||||
MotionAction::Down | MotionAction::PointerDown => {
|
||||
let primary = action == MotionAction::Down;
|
||||
if primary {
|
||||
self.primary_pointer = Some(finger_id);
|
||||
}
|
||||
let event = event::WindowEvent::PointerEntered {
|
||||
device_id,
|
||||
primary,
|
||||
position,
|
||||
kind: match tool_type {
|
||||
android_activity::input::ToolType::Finger => {
|
||||
event::PointerKind::Touch(finger_id)
|
||||
},
|
||||
};
|
||||
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
|
||||
// TODO mouse events
|
||||
android_activity::input::ToolType::Mouse => continue,
|
||||
_ => event::PointerKind::Unknown,
|
||||
},
|
||||
};
|
||||
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
|
||||
let event = event::WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary,
|
||||
state: event::ElementState::Pressed,
|
||||
position,
|
||||
button: match tool_type {
|
||||
android_activity::input::ToolType::Finger => {
|
||||
event::ButtonSource::Touch { finger_id, force }
|
||||
},
|
||||
// TODO mouse events
|
||||
android_activity::input::ToolType::Mouse => continue,
|
||||
_ => event::ButtonSource::Unknown(0),
|
||||
},
|
||||
};
|
||||
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
|
||||
},
|
||||
MotionAction::Move => {
|
||||
let primary = self.primary_pointer == Some(finger_id);
|
||||
let event = event::WindowEvent::PointerMoved {
|
||||
device_id,
|
||||
primary,
|
||||
position,
|
||||
source: match tool_type {
|
||||
android_activity::input::ToolType::Finger => {
|
||||
event::PointerSource::Touch { finger_id, force }
|
||||
},
|
||||
// TODO mouse events
|
||||
android_activity::input::ToolType::Mouse => continue,
|
||||
_ => event::PointerSource::Unknown,
|
||||
},
|
||||
};
|
||||
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
|
||||
},
|
||||
MotionAction::Up | MotionAction::PointerUp | MotionAction::Cancel => {
|
||||
let primary = action == MotionAction::Up
|
||||
|| (action == MotionAction::Cancel
|
||||
&& self.primary_pointer == Some(finger_id));
|
||||
|
||||
if primary {
|
||||
self.primary_pointer = None;
|
||||
}
|
||||
|
||||
if let MotionAction::Up | MotionAction::PointerUp = action {
|
||||
let event = event::WindowEvent::PointerButton {
|
||||
device_id,
|
||||
state: event::ElementState::Pressed,
|
||||
primary,
|
||||
state: event::ElementState::Released,
|
||||
position,
|
||||
button: match tool_type {
|
||||
android_activity::input::ToolType::Finger => {
|
||||
@@ -374,56 +425,24 @@ impl EventLoop {
|
||||
},
|
||||
};
|
||||
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
|
||||
},
|
||||
MotionAction::Move => {
|
||||
let event = event::WindowEvent::PointerMoved {
|
||||
device_id,
|
||||
position,
|
||||
source: match tool_type {
|
||||
android_activity::input::ToolType::Finger => {
|
||||
event::PointerSource::Touch { finger_id, force }
|
||||
},
|
||||
// TODO mouse events
|
||||
android_activity::input::ToolType::Mouse => continue,
|
||||
_ => event::PointerSource::Unknown,
|
||||
},
|
||||
};
|
||||
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
|
||||
},
|
||||
MotionAction::Up | MotionAction::PointerUp | MotionAction::Cancel => {
|
||||
if let MotionAction::Up | MotionAction::PointerUp = action {
|
||||
let event = event::WindowEvent::PointerButton {
|
||||
device_id,
|
||||
state: event::ElementState::Released,
|
||||
position,
|
||||
button: match tool_type {
|
||||
android_activity::input::ToolType::Finger => {
|
||||
event::ButtonSource::Touch { finger_id, force }
|
||||
},
|
||||
// TODO mouse events
|
||||
android_activity::input::ToolType::Mouse => continue,
|
||||
_ => event::ButtonSource::Unknown(0),
|
||||
},
|
||||
};
|
||||
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
|
||||
}
|
||||
}
|
||||
|
||||
let event = event::WindowEvent::PointerLeft {
|
||||
device_id,
|
||||
position: Some(position),
|
||||
kind: match tool_type {
|
||||
android_activity::input::ToolType::Finger => {
|
||||
event::PointerKind::Touch(finger_id)
|
||||
},
|
||||
// TODO mouse events
|
||||
android_activity::input::ToolType::Mouse => continue,
|
||||
_ => event::PointerKind::Unknown,
|
||||
let event = event::WindowEvent::PointerLeft {
|
||||
device_id,
|
||||
primary,
|
||||
position: Some(position),
|
||||
kind: match tool_type {
|
||||
android_activity::input::ToolType::Finger => {
|
||||
event::PointerKind::Touch(finger_id)
|
||||
},
|
||||
};
|
||||
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// TODO mouse events
|
||||
android_activity::input::ToolType::Mouse => continue,
|
||||
_ => event::PointerKind::Unknown,
|
||||
},
|
||||
};
|
||||
app.window_event(&self.window_target, GLOBAL_WINDOW, event);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -545,7 +564,8 @@ impl EventLoop {
|
||||
self.pending_redraw |= self.redraw_flag.get_and_reset();
|
||||
|
||||
timeout = if self.running
|
||||
&& (self.pending_redraw || self.window_target.proxy_wake_up.load(Ordering::Relaxed))
|
||||
&& (self.pending_redraw
|
||||
|| self.window_target.event_loop_proxy.wake_up.load(Ordering::Relaxed))
|
||||
{
|
||||
// If we already have work to do then we don't want to block on the next poll
|
||||
Some(Duration::ZERO)
|
||||
@@ -578,7 +598,7 @@ impl EventLoop {
|
||||
self.pending_redraw |= self.redraw_flag.get_and_reset();
|
||||
if !self.running
|
||||
|| (!self.pending_redraw
|
||||
&& !self.window_target.proxy_wake_up.load(Ordering::Relaxed))
|
||||
&& !self.window_target.event_loop_proxy.wake_up.load(Ordering::Relaxed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -617,15 +637,20 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
wake_up: AtomicBool,
|
||||
waker: AndroidAppWaker,
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
self.proxy_wake_up.store(true, Ordering::Relaxed);
|
||||
fn new(waker: AndroidAppWaker) -> Self {
|
||||
Self { wake_up: AtomicBool::new(false), waker }
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
self.wake_up.store(true, Ordering::Relaxed);
|
||||
self.waker.wake();
|
||||
}
|
||||
}
|
||||
@@ -635,7 +660,7 @@ pub struct ActiveEventLoop {
|
||||
control_flow: Cell<ControlFlow>,
|
||||
exit: Cell<bool>,
|
||||
redraw_requester: RedrawRequester,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
event_loop_proxy: Arc<EventLoopProxy>,
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
@@ -645,12 +670,8 @@ impl ActiveEventLoop {
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> RootEventLoopProxy {
|
||||
let event_loop_proxy = EventLoopProxy {
|
||||
proxy_wake_up: self.proxy_wake_up.clone(),
|
||||
waker: self.app.create_waker(),
|
||||
};
|
||||
RootEventLoopProxy { event_loop_proxy }
|
||||
fn create_proxy(&self) -> CoreEventLoopProxy {
|
||||
CoreEventLoopProxy::new(self.event_loop_proxy.clone())
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
@@ -697,17 +718,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
self.exit.get()
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
|
||||
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::AndroidDisplayHandle::new();
|
||||
@@ -718,23 +737,10 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::AndroidDisplayHandle::new().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct FingerId(i32);
|
||||
|
||||
impl FingerId {
|
||||
#[cfg(test)]
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId(0)
|
||||
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::AndroidDisplayHandle::new();
|
||||
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw.into()) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,7 +770,6 @@ impl Window {
|
||||
self.app.content_rect()
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
// Allow the usage of HasRawWindowHandle inside this function
|
||||
#[allow(deprecated)]
|
||||
fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
@@ -782,13 +787,11 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::Android(rwh_06::AndroidDisplayHandle::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
@@ -796,7 +799,6 @@ impl rwh_06::HasDisplayHandle for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
@@ -831,8 +833,8 @@ impl CoreWindow for Window {
|
||||
|
||||
fn pre_present_notify(&self) {}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Err(NotSupportedError::new("inner_position is not supported").into())
|
||||
fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
(0, 0).into()
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
@@ -855,6 +857,10 @@ impl CoreWindow for Window {
|
||||
screen_size(&self.app)
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
PhysicalInsets::new(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, _: Option<Size>) {}
|
||||
|
||||
fn set_max_surface_size(&self, _: Option<Size>) {}
|
||||
@@ -921,7 +927,13 @@ impl CoreWindow for Window {
|
||||
|
||||
fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
|
||||
|
||||
fn set_ime_allowed(&self, _allowed: bool) {}
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
if allowed {
|
||||
self.app.show_soft_input(true);
|
||||
} else {
|
||||
self.app.hide_soft_input(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_ime_purpose(&self, _purpose: ImePurpose) {}
|
||||
|
||||
@@ -974,12 +986,10 @@ impl CoreWindow for Window {
|
||||
|
||||
fn reset_dead_keys(&self) {}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use std::cell::{Cell, OnceCell, RefCell};
|
||||
use std::mem;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::atomic::Ordering as AtomicOrdering;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningApplication};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotification};
|
||||
|
||||
use super::super::event_handler::EventHandler;
|
||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, EventLoopProxy, PanicInfo};
|
||||
use super::menu;
|
||||
use super::observer::{EventLoopWaker, RunLoop};
|
||||
use crate::application::ApplicationHandler;
|
||||
@@ -24,7 +24,7 @@ pub(super) struct AppState {
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
run_loop: RunLoop,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
event_loop_proxy: Arc<EventLoopProxy>,
|
||||
event_handler: EventHandler,
|
||||
stop_on_launch: Cell<bool>,
|
||||
stop_before_wait: Cell<bool>,
|
||||
@@ -72,7 +72,7 @@ impl AppState {
|
||||
let this = Rc::new(AppState {
|
||||
mtm,
|
||||
activation_policy,
|
||||
proxy_wake_up: Arc::new(AtomicBool::new(false)),
|
||||
event_loop_proxy: Arc::new(EventLoopProxy::new()),
|
||||
default_menu,
|
||||
activate_ignoring_other_apps,
|
||||
run_loop: RunLoop::main(mtm),
|
||||
@@ -113,10 +113,21 @@ impl AppState {
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
// If no activation policy is explicitly provided, do not set it at all
|
||||
// to allow the package manifest to define behavior via LSUIElement.
|
||||
if self.activation_policy.is_some() {
|
||||
app.setActivationPolicy(self.activation_policy.unwrap());
|
||||
if let Some(activation_policy) = self.activation_policy {
|
||||
app.setActivationPolicy(activation_policy);
|
||||
} else {
|
||||
// If no activation policy is explicitly provided, and the application
|
||||
// is bundled, do not set the activation policy at all, to allow the
|
||||
// package manifest to define the behavior via LSUIElement.
|
||||
//
|
||||
// See:
|
||||
// - https://github.com/rust-windowing/winit/issues/261
|
||||
// - https://github.com/rust-windowing/winit/issues/3958
|
||||
let is_bundled =
|
||||
unsafe { NSRunningApplication::currentApplication().bundleIdentifier().is_some() };
|
||||
if !is_bundled {
|
||||
app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
@@ -165,8 +176,8 @@ impl AppState {
|
||||
self.event_handler.set(handler, closure)
|
||||
}
|
||||
|
||||
pub fn proxy_wake_up(&self) -> Arc<AtomicBool> {
|
||||
self.proxy_wake_up.clone()
|
||||
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
|
||||
&self.event_loop_proxy
|
||||
}
|
||||
|
||||
/// If `pump_events` is called to progress the event loop then we
|
||||
@@ -350,7 +361,7 @@ impl AppState {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
|
||||
if self.event_loop_proxy.wake_up.swap(false, AtomicOrdering::Relaxed) {
|
||||
self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
|
||||
}
|
||||
|
||||
|
||||
@@ -92,17 +92,12 @@ fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
|
||||
/// Create `KeyEvent` for the given `NSEvent`.
|
||||
///
|
||||
/// This function shouldn't be called when the IME input is in process.
|
||||
pub(crate) fn create_key_event(
|
||||
ns_event: &NSEvent,
|
||||
is_press: bool,
|
||||
is_repeat: bool,
|
||||
key_override: Option<PhysicalKey>,
|
||||
) -> KeyEvent {
|
||||
pub(crate) fn create_key_event(ns_event: &NSEvent, is_press: bool, is_repeat: bool) -> KeyEvent {
|
||||
use ElementState::{Pressed, Released};
|
||||
let state = if is_press { Pressed } else { Released };
|
||||
|
||||
let scancode = unsafe { ns_event.keyCode() };
|
||||
let mut physical_key = key_override.unwrap_or_else(|| scancode_to_physicalkey(scancode as u32));
|
||||
let mut physical_key = scancode_to_physicalkey(scancode as u32);
|
||||
|
||||
// NOTE: The logical key should heed both SHIFT and ALT if possible.
|
||||
// For instance:
|
||||
@@ -111,20 +106,15 @@ pub(crate) fn create_key_event(
|
||||
// * Pressing CTRL SHIFT A: logical key should also be "A"
|
||||
// This is not easy to tease out of `NSEvent`, but we do our best.
|
||||
|
||||
let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() {
|
||||
let characters = unsafe { ns_event.characters() }.map(|s| s.to_string()).unwrap_or_default();
|
||||
let text_with_all_modifiers = if characters.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let characters =
|
||||
unsafe { ns_event.characters() }.map(|s| s.to_string()).unwrap_or_default();
|
||||
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))
|
||||
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))
|
||||
};
|
||||
|
||||
let key_from_code = code_to_key(physical_key, scancode);
|
||||
@@ -377,6 +367,7 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
|
||||
KeyCode::KeyX => Some(0x07),
|
||||
KeyCode::KeyC => Some(0x08),
|
||||
KeyCode::KeyV => Some(0x09),
|
||||
KeyCode::IntlBackslash => Some(0x0a),
|
||||
KeyCode::KeyB => Some(0x0b),
|
||||
KeyCode::KeyQ => Some(0x0c),
|
||||
KeyCode::KeyW => Some(0x0d),
|
||||
@@ -422,18 +413,21 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
|
||||
KeyCode::SuperRight => Some(0x36),
|
||||
KeyCode::SuperLeft => Some(0x37),
|
||||
KeyCode::ShiftLeft => Some(0x38),
|
||||
KeyCode::CapsLock => Some(0x39),
|
||||
KeyCode::AltLeft => Some(0x3a),
|
||||
KeyCode::ControlLeft => Some(0x3b),
|
||||
KeyCode::ShiftRight => Some(0x3c),
|
||||
KeyCode::AltRight => Some(0x3d),
|
||||
KeyCode::ControlRight => Some(0x3e),
|
||||
KeyCode::Fn => Some(0x3f),
|
||||
KeyCode::F17 => Some(0x40),
|
||||
KeyCode::NumpadDecimal => Some(0x41),
|
||||
KeyCode::NumpadMultiply => Some(0x43),
|
||||
KeyCode::NumpadAdd => Some(0x45),
|
||||
KeyCode::NumLock => Some(0x47),
|
||||
KeyCode::AudioVolumeUp => Some(0x49),
|
||||
KeyCode::AudioVolumeDown => Some(0x4a),
|
||||
KeyCode::AudioVolumeUp => Some(0x48),
|
||||
KeyCode::AudioVolumeDown => Some(0x49),
|
||||
KeyCode::AudioVolumeMute => Some(0x4a),
|
||||
KeyCode::NumpadDivide => Some(0x4b),
|
||||
KeyCode::NumpadEnter => Some(0x4c),
|
||||
KeyCode::NumpadSubtract => Some(0x4e),
|
||||
@@ -452,17 +446,22 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
|
||||
KeyCode::Numpad8 => Some(0x5b),
|
||||
KeyCode::Numpad9 => Some(0x5c),
|
||||
KeyCode::IntlYen => Some(0x5d),
|
||||
KeyCode::IntlRo => Some(0x5e),
|
||||
KeyCode::NumpadComma => Some(0x5f),
|
||||
KeyCode::F5 => Some(0x60),
|
||||
KeyCode::F6 => Some(0x61),
|
||||
KeyCode::F7 => Some(0x62),
|
||||
KeyCode::F3 => Some(0x63),
|
||||
KeyCode::F8 => Some(0x64),
|
||||
KeyCode::F9 => Some(0x65),
|
||||
KeyCode::Lang2 => Some(0x66),
|
||||
KeyCode::F11 => Some(0x67),
|
||||
KeyCode::Lang1 => Some(0x68),
|
||||
KeyCode::F13 => Some(0x69),
|
||||
KeyCode::F16 => Some(0x6a),
|
||||
KeyCode::F14 => Some(0x6b),
|
||||
KeyCode::F10 => Some(0x6d),
|
||||
KeyCode::ContextMenu => Some(0x6e),
|
||||
KeyCode::F12 => Some(0x6f),
|
||||
KeyCode::F15 => Some(0x71),
|
||||
KeyCode::Insert => Some(0x72),
|
||||
@@ -478,11 +477,26 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
|
||||
KeyCode::ArrowRight => Some(0x7c),
|
||||
KeyCode::ArrowDown => Some(0x7d),
|
||||
KeyCode::ArrowUp => Some(0x7e),
|
||||
KeyCode::Power => Some(0x7f),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
0x00 => KeyCode::KeyA,
|
||||
0x01 => KeyCode::KeyS,
|
||||
@@ -494,7 +508,11 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
0x07 => KeyCode::KeyX,
|
||||
0x08 => KeyCode::KeyC,
|
||||
0x09 => KeyCode::KeyV,
|
||||
// 0x0a => World 1,
|
||||
// This key is typically located near LeftShift key, roughly the same location as backquote
|
||||
// (`) on Windows' US layout.
|
||||
//
|
||||
// The keycap varies on international keyboards.
|
||||
0x0a => KeyCode::IntlBackslash,
|
||||
0x0b => KeyCode::KeyB,
|
||||
0x0c => KeyCode::KeyQ,
|
||||
0x0d => KeyCode::KeyW,
|
||||
@@ -536,7 +554,7 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
0x31 => KeyCode::Space,
|
||||
0x32 => KeyCode::Backquote,
|
||||
0x33 => KeyCode::Backspace,
|
||||
// 0x34 => unknown,
|
||||
// 0x34 => unknown, // kVK_Powerbook_KeypadEnter
|
||||
0x35 => KeyCode::Escape,
|
||||
0x36 => KeyCode::SuperRight,
|
||||
0x37 => KeyCode::SuperLeft,
|
||||
@@ -555,15 +573,10 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
// 0x44 => unknown,
|
||||
0x45 => KeyCode::NumpadAdd,
|
||||
// 0x46 => unknown,
|
||||
0x47 => KeyCode::NumLock,
|
||||
// 0x48 => KeyCode::NumpadClear,
|
||||
|
||||
// TODO: (Artur) for me, kVK_VolumeUp is 0x48
|
||||
// macOS 10.11
|
||||
// /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/
|
||||
// Versions/A/Headers/Events.h
|
||||
0x49 => KeyCode::AudioVolumeUp,
|
||||
0x4a => KeyCode::AudioVolumeDown,
|
||||
0x47 => KeyCode::NumLock, // kVK_ANSI_KeypadClear
|
||||
0x48 => KeyCode::AudioVolumeUp,
|
||||
0x49 => KeyCode::AudioVolumeDown,
|
||||
0x4a => KeyCode::AudioVolumeMute,
|
||||
0x4b => KeyCode::NumpadDivide,
|
||||
0x4c => KeyCode::NumpadEnter,
|
||||
// 0x4d => unknown,
|
||||
@@ -583,23 +596,23 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
0x5b => KeyCode::Numpad8,
|
||||
0x5c => KeyCode::Numpad9,
|
||||
0x5d => KeyCode::IntlYen,
|
||||
// 0x5e => JIS Ro,
|
||||
// 0x5f => unknown,
|
||||
0x5e => KeyCode::IntlRo,
|
||||
0x5f => KeyCode::NumpadComma,
|
||||
0x60 => KeyCode::F5,
|
||||
0x61 => KeyCode::F6,
|
||||
0x62 => KeyCode::F7,
|
||||
0x63 => KeyCode::F3,
|
||||
0x64 => KeyCode::F8,
|
||||
0x65 => KeyCode::F9,
|
||||
// 0x66 => JIS Eisuu (macOS),
|
||||
0x66 => KeyCode::Lang2,
|
||||
0x67 => KeyCode::F11,
|
||||
// 0x68 => JIS Kanna (macOS),
|
||||
0x68 => KeyCode::Lang1,
|
||||
0x69 => KeyCode::F13,
|
||||
0x6a => KeyCode::F16,
|
||||
0x6b => KeyCode::F14,
|
||||
// 0x6c => unknown,
|
||||
0x6d => KeyCode::F10,
|
||||
// 0x6e => unknown,
|
||||
0x6e => KeyCode::ContextMenu,
|
||||
0x6f => KeyCode::F12,
|
||||
// 0x70 => unknown,
|
||||
0x71 => KeyCode::F15,
|
||||
@@ -616,11 +629,7 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
0x7c => KeyCode::ArrowRight,
|
||||
0x7d => KeyCode::ArrowDown,
|
||||
0x7e => KeyCode::ArrowUp,
|
||||
// 0x7f => unknown,
|
||||
|
||||
// 0xA is the caret (^) an macOS's German QERTZ layout. This key is at the same location as
|
||||
// backquote (`) on Windows' US layout.
|
||||
0xa => KeyCode::Backquote,
|
||||
0x7f => KeyCode::Power, // On 10.7 and 10.8 only
|
||||
_ => return PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode as u16)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use objc2_app_kit::{
|
||||
NSApplicationWillTerminateNotification, NSWindow,
|
||||
};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject, NSObjectProtocol};
|
||||
use rwh_06::HasDisplayHandle;
|
||||
|
||||
use super::super::notification_center::create_observer;
|
||||
use super::app::WinitApplication;
|
||||
@@ -32,7 +33,8 @@ use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, RequestError};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform::macos::ActivationPolicy;
|
||||
@@ -95,9 +97,8 @@ impl ActiveEventLoop {
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> RootEventLoopProxy {
|
||||
let event_loop_proxy = EventLoopProxy::new(self.app_state.proxy_wake_up());
|
||||
RootEventLoopProxy { event_loop_proxy }
|
||||
fn create_proxy(&self) -> CoreEventLoopProxy {
|
||||
CoreEventLoopProxy::new(self.app_state.event_loop_proxy().clone())
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
@@ -151,17 +152,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
self.app_state.exiting()
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
|
||||
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
|
||||
@@ -391,16 +390,12 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::AppKitDisplayHandle::new().into())
|
||||
impl HasDisplayHandle for OwnedDisplayHandle {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,8 +434,9 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventLoopProxy {
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
pub(crate) wake_up: AtomicBool,
|
||||
source: CFRunLoopSourceRef,
|
||||
}
|
||||
|
||||
@@ -455,14 +451,8 @@ impl Drop for EventLoopProxy {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for EventLoopProxy {
|
||||
fn clone(&self) -> Self {
|
||||
EventLoopProxy::new(self.proxy_wake_up.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
fn new(proxy_wake_up: Arc<AtomicBool>) -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
unsafe {
|
||||
// just wake up the eventloop
|
||||
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
|
||||
@@ -486,14 +476,16 @@ impl EventLoopProxy {
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||
CFRunLoopWakeUp(rl);
|
||||
|
||||
EventLoopProxy { proxy_wake_up, source }
|
||||
EventLoopProxy { wake_up: AtomicBool::new(false), source }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wake_up(&self) {
|
||||
self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
self.wake_up.store(true, AtomicOrdering::Relaxed);
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
// Let the main thread know there's a new event.
|
||||
CFRunLoopSourceSignal(self.source);
|
||||
let rl = CFRunLoopGetMain();
|
||||
CFRunLoopWakeUp(rl);
|
||||
|
||||
@@ -17,8 +17,7 @@ mod window_delegate;
|
||||
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
|
||||
pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey, KeyEventExtra};
|
||||
pub(crate) use self::event_loop::{
|
||||
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
|
||||
PlatformSpecificEventLoopAttributes,
|
||||
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
|
||||
};
|
||||
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
|
||||
pub(crate) use self::window::Window;
|
||||
@@ -26,13 +25,3 @@ pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
|
||||
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId;
|
||||
|
||||
impl FingerId {
|
||||
#[cfg(test)]
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,30 +4,30 @@ use std::collections::{HashMap, VecDeque};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use objc2::rc::{Retained, WeakId};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::{AnyObject, Sel};
|
||||
use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{
|
||||
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient,
|
||||
NSTrackingRectTag, NSView, NSViewFrameDidChangeNotification,
|
||||
NSTrackingRectTag, NSView,
|
||||
};
|
||||
use objc2_foundation::{
|
||||
MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying,
|
||||
NSMutableAttributedString, NSNotFound, NSNotificationCenter, NSObject, NSObjectProtocol,
|
||||
NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
||||
NSMutableAttributedString, NSNotFound, NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect,
|
||||
NSSize, NSString, NSUInteger,
|
||||
};
|
||||
|
||||
use super::app_state::AppState;
|
||||
use super::cursor::{default_cursor, invisible_cursor};
|
||||
use super::event::{
|
||||
code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed,
|
||||
scancode_to_physicalkey,
|
||||
scancode_to_physicalkey, KeyEventExtra,
|
||||
};
|
||||
use super::window::WinitWindow;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||
use crate::event::{
|
||||
DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, PointerKind,
|
||||
PointerSource, TouchPhase, WindowEvent,
|
||||
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
|
||||
PointerKind, PointerSource, TouchPhase, WindowEvent,
|
||||
};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey};
|
||||
use crate::platform::macos::OptionAsAlt;
|
||||
@@ -134,9 +134,6 @@ pub struct ViewState {
|
||||
marked_text: RefCell<Retained<NSMutableAttributedString>>,
|
||||
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`.
|
||||
option_as_alt: Cell<OptionAsAlt>,
|
||||
}
|
||||
@@ -177,9 +174,10 @@ declare_class!(
|
||||
self.ivars().tracking_rect.set(Some(tracking_rect));
|
||||
}
|
||||
|
||||
#[method(frameDidChange:)]
|
||||
fn frame_did_change(&self, _event: &NSEvent) {
|
||||
trace_scope!("frameDidChange:");
|
||||
// Not a normal method on `NSView`, it's triggered by `NSViewFrameDidChangeNotification`.
|
||||
#[method(viewFrameDidChangeNotification:)]
|
||||
fn frame_did_change(&self, _notification: Option<&AnyObject>) {
|
||||
trace_scope!("NSViewFrameDidChangeNotification");
|
||||
if let Some(tracking_rect) = self.ivars().tracking_rect.take() {
|
||||
self.removeTrackingRect(tracking_rect);
|
||||
}
|
||||
@@ -203,10 +201,7 @@ declare_class!(
|
||||
fn draw_rect(&self, _rect: NSRect) {
|
||||
trace_scope!("drawRect:");
|
||||
|
||||
// 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());
|
||||
}
|
||||
self.ivars().app_state.handle_redraw(self.window().id());
|
||||
|
||||
// This is a direct subclass of NSView, no need to call superclass' drawRect:
|
||||
}
|
||||
@@ -399,7 +394,7 @@ declare_class!(
|
||||
unsafe { &*string }.to_string()
|
||||
};
|
||||
|
||||
let is_control = string.chars().next().map_or(false, |c| c.is_control());
|
||||
let is_control = string.chars().next().is_some_and(|c| c.is_control());
|
||||
|
||||
// Commit only if we have marked text.
|
||||
if unsafe { self.hasMarkedText() } && self.is_ime_enabled() && !is_control {
|
||||
@@ -495,7 +490,7 @@ declare_class!(
|
||||
};
|
||||
|
||||
if !had_ime_input || self.ivars().forward_key_to_app.get() {
|
||||
let key_event = create_key_event(&event, true, unsafe { event.isARepeat() }, None);
|
||||
let key_event = create_key_event(&event, true, unsafe { event.isARepeat() });
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: None,
|
||||
event: key_event,
|
||||
@@ -518,7 +513,7 @@ declare_class!(
|
||||
) {
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: None,
|
||||
event: create_key_event(&event, false, false, None),
|
||||
event: create_key_event(&event, false, false),
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
@@ -565,7 +560,7 @@ declare_class!(
|
||||
.expect("could not find current event");
|
||||
|
||||
self.update_modifiers(&event, false);
|
||||
let event = create_key_event(&event, true, unsafe { event.isARepeat() }, None);
|
||||
let event = create_key_event(&event, true, unsafe { event.isARepeat() });
|
||||
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: None,
|
||||
@@ -657,6 +652,7 @@ declare_class!(
|
||||
|
||||
self.queue_event(WindowEvent::PointerEntered {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position,
|
||||
kind: PointerKind::Mouse,
|
||||
});
|
||||
@@ -670,6 +666,7 @@ declare_class!(
|
||||
|
||||
self.queue_event(WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: Some(position),
|
||||
kind: PointerKind::Mouse,
|
||||
});
|
||||
@@ -804,11 +801,10 @@ declare_class!(
|
||||
impl WinitView {
|
||||
pub(super) fn new(
|
||||
app_state: &Rc<AppState>,
|
||||
window: &WinitWindow,
|
||||
accepts_first_mouse: bool,
|
||||
option_as_alt: OptionAsAlt,
|
||||
mtm: MainThreadMarker,
|
||||
) -> Retained<Self> {
|
||||
let mtm = MainThreadMarker::from(window);
|
||||
let this = mtm.alloc().set_ivars(ViewState {
|
||||
app_state: Rc::clone(app_state),
|
||||
cursor_state: Default::default(),
|
||||
@@ -823,34 +819,24 @@ impl WinitView {
|
||||
forward_key_to_app: Default::default(),
|
||||
marked_text: Default::default(),
|
||||
accepts_first_mouse,
|
||||
_ns_window: WeakId::new(&window.retain()),
|
||||
option_as_alt: Cell::new(option_as_alt),
|
||||
});
|
||||
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
|
||||
}
|
||||
|
||||
fn window(&self) -> Retained<WinitWindow> {
|
||||
// TODO: Simply use `window` property on `NSView`.
|
||||
// That only returns a window _after_ the view has been attached though!
|
||||
// (which is incompatible with `frameDidChange:`)
|
||||
//
|
||||
// unsafe { msg_send_id![self, window] }
|
||||
self.ivars()._ns_window.load().expect("view to have a window")
|
||||
let window = (**self).window().expect("view must be installed in a window");
|
||||
|
||||
if !window.isKindOfClass(WinitWindow::class()) {
|
||||
unreachable!("view installed in non-WinitWindow");
|
||||
}
|
||||
|
||||
// SAFETY: Just checked that the window is `WinitWindow`
|
||||
unsafe { Retained::cast(window) }
|
||||
}
|
||||
|
||||
fn queue_event(&self, event: WindowEvent) {
|
||||
@@ -960,22 +946,36 @@ impl WinitView {
|
||||
let scancode = unsafe { ns_event.keyCode() };
|
||||
let physical_key = scancode_to_physicalkey(scancode as u32);
|
||||
|
||||
// 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);
|
||||
let logical_key = code_to_key(physical_key, scancode);
|
||||
// Ignore processing of unknown modifiers because we can't determine whether
|
||||
// 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;
|
||||
};
|
||||
event.physical_key = physical_key;
|
||||
event.logical_key = key.clone();
|
||||
event.location = code_to_location(physical_key);
|
||||
|
||||
let mut event = KeyEvent {
|
||||
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 mut phys_mod_state = self.ivars().phys_modifiers.borrow_mut();
|
||||
let phys_mod = phys_mod_state.entry(key).or_insert(ModLocationMask::empty());
|
||||
let phys_mod =
|
||||
phys_mod_state.entry(logical_key).or_insert(ModLocationMask::empty());
|
||||
|
||||
let is_active = current_modifiers.state().contains(event_modifier);
|
||||
let mut events = VecDeque::with_capacity(2);
|
||||
@@ -1061,6 +1061,7 @@ impl WinitView {
|
||||
|
||||
self.queue_event(WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: button_state,
|
||||
position,
|
||||
button: button.into(),
|
||||
@@ -1087,6 +1088,7 @@ impl WinitView {
|
||||
|
||||
self.queue_event(WindowEvent::PointerMoved {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: view_point.to_physical(self.scale_factor()),
|
||||
source: PointerSource::Mouse,
|
||||
});
|
||||
|
||||
@@ -42,7 +42,6 @@ impl Window {
|
||||
self.delegate.get_on_main(|delegate| f(delegate))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub(crate) fn raw_window_handle_rwh_06(
|
||||
&self,
|
||||
@@ -54,7 +53,6 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub(crate) fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
@@ -74,7 +72,6 @@ impl Drop for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
@@ -82,7 +79,6 @@ impl rwh_06::HasDisplayHandle for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
@@ -111,12 +107,12 @@ impl CoreWindow for Window {
|
||||
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
|
||||
fn surface_position(&self) -> dpi::PhysicalPosition<i32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_position())
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<dpi::PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
|
||||
self.maybe_wait_on_main(|delegate| delegate.outer_position())
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
@@ -135,6 +131,10 @@ impl CoreWindow for Window {
|
||||
self.maybe_wait_on_main(|delegate| delegate.outer_size())
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> dpi::PhysicalInsets<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.safe_area())
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size))
|
||||
}
|
||||
@@ -323,12 +323,10 @@ impl CoreWindow for Window {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use core_graphics::display::{CGDisplay, CGPoint};
|
||||
use core_graphics::display::CGDisplay;
|
||||
use monitor::VideoModeHandle;
|
||||
use objc2::rc::{autoreleasepool, Retained};
|
||||
use objc2::runtime::{AnyObject, ProtocolObject};
|
||||
@@ -15,15 +15,16 @@ use objc2_app_kit::{
|
||||
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization,
|
||||
NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType,
|
||||
NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard,
|
||||
NSRequestUserAttentionType, NSScreen, NSView, NSWindowButton, NSWindowDelegate,
|
||||
NSWindowFullScreenButton, NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode,
|
||||
NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility,
|
||||
NSRequestUserAttentionType, NSScreen, NSToolbar, NSView, NSViewFrameDidChangeNotification,
|
||||
NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel,
|
||||
NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask,
|
||||
NSWindowTabbingMode, NSWindowTitleVisibility, NSWindowToolbarStyle,
|
||||
};
|
||||
use objc2_foundation::{
|
||||
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSKeyValueChangeKey,
|
||||
NSKeyValueChangeNewKey, NSKeyValueChangeOldKey, NSKeyValueObservingOptions, NSObject,
|
||||
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
|
||||
NSRect, NSSize, NSString,
|
||||
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSEdgeInsets,
|
||||
NSKeyValueChangeKey, NSKeyValueChangeNewKey, NSKeyValueChangeOldKey,
|
||||
NSKeyValueObservingOptions, NSNotificationCenter, NSObject, NSObjectNSDelayedPerforming,
|
||||
NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint, NSRect, NSSize, NSString,
|
||||
};
|
||||
use tracing::{trace, warn};
|
||||
|
||||
@@ -34,7 +35,10 @@ use super::observer::RunLoop;
|
||||
use super::view::WinitView;
|
||||
use super::window::WinitWindow;
|
||||
use super::{ffi, Fullscreen, MonitorHandle};
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::dpi::{
|
||||
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
|
||||
Position, Size,
|
||||
};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{SurfaceSizeWriter, WindowEvent};
|
||||
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||
@@ -57,6 +61,7 @@ pub struct PlatformSpecificWindowAttributes {
|
||||
pub tabbing_identifier: Option<String>,
|
||||
pub option_as_alt: OptionAsAlt,
|
||||
pub borderless_game: bool,
|
||||
pub unified_titlebar: bool,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificWindowAttributes {
|
||||
@@ -75,6 +80,7 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
tabbing_identifier: None,
|
||||
option_as_alt: Default::default(),
|
||||
borderless_game: false,
|
||||
unified_titlebar: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +170,7 @@ declare_class!(
|
||||
#[method(windowDidResize:)]
|
||||
fn window_did_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidResize:");
|
||||
// NOTE: WindowEvent::SurfaceResized is reported in frameDidChange.
|
||||
// NOTE: WindowEvent::SurfaceResized is reported using NSViewFrameDidChangeNotification.
|
||||
self.emit_move_event();
|
||||
}
|
||||
|
||||
@@ -439,9 +445,15 @@ declare_class!(
|
||||
// NOTE: We don't _really_ need to check the key path, as there should only be one, but
|
||||
// in the future we might want to observe other key paths.
|
||||
if key_path == Some(ns_string!("effectiveAppearance")) {
|
||||
let change = change.expect("requested a change dictionary in `addObserver`, but none was provided");
|
||||
let old = change.get(unsafe { NSKeyValueChangeOldKey }).expect("requested change dictionary did not contain `NSKeyValueChangeOldKey`");
|
||||
let new = change.get(unsafe { NSKeyValueChangeNewKey }).expect("requested change dictionary did not contain `NSKeyValueChangeNewKey`");
|
||||
let change = change.expect(
|
||||
"requested a change dictionary in `addObserver`, but none was provided",
|
||||
);
|
||||
let old = change
|
||||
.get(unsafe { NSKeyValueChangeOldKey })
|
||||
.expect("requested change dictionary did not contain `NSKeyValueChangeOldKey`");
|
||||
let new = change
|
||||
.get(unsafe { NSKeyValueChangeNewKey })
|
||||
.expect("requested change dictionary did not contain `NSKeyValueChangeNewKey`");
|
||||
|
||||
// SAFETY: The value of `effectiveAppearance` is `NSAppearance`
|
||||
let old: *const AnyObject = old;
|
||||
@@ -558,6 +570,12 @@ fn new_window(
|
||||
}
|
||||
|
||||
if attrs.platform_specific.fullsize_content_view {
|
||||
// NOTE: If we decide to add an option to change this at runtime, we must emit a
|
||||
// `SurfaceResized` event to let applications know that the safe area changed.
|
||||
//
|
||||
// An alternative would be to add a `WindowEvent::SafeAreaChanged` event, this could be
|
||||
// done with an observer on `safeAreaRect` / `contentLayoutRect`, see:
|
||||
// <https://github.com/rust-windowing/winit/issues/3911>
|
||||
masks |= NSWindowStyleMask::FullSizeContentView;
|
||||
}
|
||||
|
||||
@@ -616,6 +634,14 @@ fn new_window(
|
||||
if attrs.platform_specific.movable_by_window_background {
|
||||
window.setMovableByWindowBackground(true);
|
||||
}
|
||||
if attrs.platform_specific.unified_titlebar {
|
||||
unsafe {
|
||||
// The toolbar style is ignored if there is no toolbar, so it is
|
||||
// necessary to add one.
|
||||
window.setToolbar(Some(&NSToolbar::new(mtm)));
|
||||
window.setToolbarStyle(NSWindowToolbarStyle::Unified);
|
||||
}
|
||||
}
|
||||
|
||||
if !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) {
|
||||
if let Some(button) = window.standardWindowButton(NSWindowButton::NSWindowZoomButton) {
|
||||
@@ -632,9 +658,9 @@ fn new_window(
|
||||
|
||||
let view = WinitView::new(
|
||||
app_state,
|
||||
&window,
|
||||
attrs.platform_specific.accepts_first_mouse,
|
||||
attrs.platform_specific.option_as_alt,
|
||||
mtm,
|
||||
);
|
||||
|
||||
// The default value of `setWantsBestResolutionOpenGLSurface:` was `false` until
|
||||
@@ -656,6 +682,23 @@ fn new_window(
|
||||
window.setContentView(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 {
|
||||
window.setOpaque(false);
|
||||
// See `set_transparent` for details on why we do this.
|
||||
@@ -681,7 +724,6 @@ impl WindowDelegate {
|
||||
let window = new_window(app_state, &attrs, mtm)
|
||||
.ok_or_else(|| os_error!("couldn't create `NSWindow`"))?;
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
match attrs.parent_window.map(|handle| handle.0) {
|
||||
Some(rwh_06::RawWindowHandle::AppKit(handle)) => {
|
||||
// SAFETY: Caller ensures the pointer is valid or NULL
|
||||
@@ -740,12 +782,6 @@ impl WindowDelegate {
|
||||
});
|
||||
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
|
||||
|
||||
if scale_factor != 1.0 {
|
||||
let delegate = delegate.clone();
|
||||
RunLoop::main(mtm).queue_closure(move || {
|
||||
delegate.handle_scale_factor_changed(scale_factor);
|
||||
});
|
||||
}
|
||||
window.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
|
||||
// Listen for theme change event.
|
||||
@@ -776,10 +812,6 @@ impl WindowDelegate {
|
||||
|
||||
delegate.set_cursor(attrs.cursor);
|
||||
|
||||
// XXX Send `Focused(false)` right after creating the window delegate, so we won't
|
||||
// obscure the real focused events on the startup.
|
||||
delegate.queue_event(WindowEvent::Focused(false));
|
||||
|
||||
// Set fullscreen mode after we setup everything
|
||||
delegate.set_fullscreen(attrs.fullscreen.map(Into::into));
|
||||
|
||||
@@ -924,15 +956,28 @@ impl WindowDelegate {
|
||||
#[inline]
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
pub fn outer_position(&self) -> PhysicalPosition<i32> {
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
let position = flip_window_screen_coordinates(self.window().frame());
|
||||
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
|
||||
Ok(LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor()))
|
||||
}
|
||||
|
||||
pub fn inner_position(&self) -> PhysicalPosition<i32> {
|
||||
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
|
||||
let position = flip_window_screen_coordinates(content_rect);
|
||||
LogicalPosition::new(position.x, position.y).to_physical(self.scale_factor())
|
||||
pub fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
// The calculation here is a bit awkward because we've gotta reconcile the
|
||||
// different origins (Winit prefers top-left vs. NSWindow's bottom-left),
|
||||
// and I couldn't find a built-in way to do so.
|
||||
|
||||
// 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) {
|
||||
@@ -958,6 +1003,37 @@ impl WindowDelegate {
|
||||
logical.to_physical(self.scale_factor())
|
||||
}
|
||||
|
||||
pub fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
// Only available on macOS 11.0
|
||||
let insets = if self.view().respondsToSelector(sel!(safeAreaInsets)) {
|
||||
// Includes NSWindowStyleMask::FullSizeContentView by default, and the notch because
|
||||
// we've set it up with `additionalSafeAreaInsets`.
|
||||
unsafe { self.view().safeAreaInsets() }
|
||||
} else {
|
||||
// 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]
|
||||
pub fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let scale_factor = self.scale_factor();
|
||||
@@ -1154,13 +1230,12 @@ impl WindowDelegate {
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), RequestError> {
|
||||
let physical_window_position = self.inner_position();
|
||||
let scale_factor = self.scale_factor();
|
||||
let window_position = physical_window_position.to_logical::<CGFloat>(scale_factor);
|
||||
let logical_cursor_position = cursor_position.to_logical::<CGFloat>(scale_factor);
|
||||
let point = CGPoint {
|
||||
x: logical_cursor_position.x + window_position.x,
|
||||
y: logical_cursor_position.y + window_position.y,
|
||||
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
|
||||
let window_position = flip_window_screen_coordinates(content_rect);
|
||||
let cursor_position = cursor_position.to_logical::<CGFloat>(self.scale_factor());
|
||||
let point = core_graphics::display::CGPoint {
|
||||
x: window_position.x + cursor_position.x,
|
||||
y: window_position.y + cursor_position.y,
|
||||
};
|
||||
CGDisplay::warp_mouse_cursor_position(point)
|
||||
.map_err(|status| os_error!(format!("CGError {status}")))?;
|
||||
@@ -1620,7 +1695,6 @@ impl WindowDelegate {
|
||||
Some(monitor)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
|
||||
let window_handle = rwh_06::AppKitWindowHandle::new({
|
||||
@@ -1743,12 +1817,15 @@ impl WindowExtMacOS for WindowDelegate {
|
||||
let screen = self.window().screen().expect("expected screen to be available");
|
||||
self.window().setFrame_display(screen.frame(), true);
|
||||
|
||||
// Configure the safe area rectangle, to ensure that we don't obscure the notch.
|
||||
if NSScreen::class().responds_to(sel!(safeAreaInsets)) {
|
||||
unsafe { self.view().setAdditionalSafeAreaInsets(screen.safeAreaInsets()) };
|
||||
}
|
||||
|
||||
// Fullscreen windows can't be resized, minimized, or moved
|
||||
self.toggle_style_mask(NSWindowStyleMask::Miniaturizable, false);
|
||||
self.toggle_style_mask(NSWindowStyleMask::Resizable, false);
|
||||
self.window().setMovable(false);
|
||||
|
||||
true
|
||||
} else {
|
||||
let new_mask = self.saved_style();
|
||||
self.set_style_mask(new_mask);
|
||||
@@ -1761,11 +1838,22 @@ impl WindowExtMacOS for WindowDelegate {
|
||||
app.setPresentationOptions(presentation_opts);
|
||||
}
|
||||
|
||||
if NSScreen::class().responds_to(sel!(safeAreaInsets)) {
|
||||
unsafe {
|
||||
self.view().setAdditionalSafeAreaInsets(NSEdgeInsets {
|
||||
top: 0.0,
|
||||
left: 0.0,
|
||||
bottom: 0.0,
|
||||
right: 0.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.window().setFrame_display(frame, true);
|
||||
self.window().setMovable(true);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1837,6 +1925,34 @@ impl WindowExtMacOS for WindowDelegate {
|
||||
fn is_borderless_game(&self) -> bool {
|
||||
self.ivars().is_borderless_game.get()
|
||||
}
|
||||
|
||||
fn set_unified_titlebar(&self, unified_titlebar: bool) {
|
||||
let window = self.window();
|
||||
|
||||
if unified_titlebar {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
|
||||
unsafe {
|
||||
// The toolbar style is ignored if there is no toolbar, so it is
|
||||
// necessary to add one.
|
||||
window.setToolbar(Some(&NSToolbar::new(mtm)));
|
||||
window.setToolbarStyle(NSWindowToolbarStyle::Unified);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
window.setToolbar(None);
|
||||
window.setToolbarStyle(NSWindowToolbarStyle::Automatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unified_titlebar(&self) -> bool {
|
||||
let window = self.window();
|
||||
|
||||
unsafe {
|
||||
window.toolbar().is_some() && window.toolbarStyle() == NSWindowToolbarStyle::Unified
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_STANDARD_FRAME: NSRect =
|
||||
|
||||
@@ -106,7 +106,6 @@ impl EventHandler {
|
||||
self.inner.try_borrow().is_err()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(crate) fn ready(&self) -> bool {
|
||||
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ mod notification_center;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
mod uikit;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use self::appkit::*;
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub use self::uikit::*;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use std::cell::{OnceCell, RefCell, RefMut};
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
use std::time::Instant;
|
||||
use std::{mem, ptr};
|
||||
@@ -24,11 +24,12 @@ use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
|
||||
|
||||
use super::super::event_handler::EventHandler;
|
||||
use super::window::WinitUIWindow;
|
||||
use super::ActiveEventLoop;
|
||||
use super::{ActiveEventLoop, EventLoopProxy};
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::event::{StartCause, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::event_loop::ControlFlow;
|
||||
use crate::window::WindowId;
|
||||
|
||||
macro_rules! bug {
|
||||
($($msg:tt)*) => {
|
||||
@@ -67,25 +68,9 @@ fn get_handler(mtm: MainThreadMarker) -> &'static EventHandler {
|
||||
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)]
|
||||
pub(crate) enum EventWrapper {
|
||||
StaticEvent(Event),
|
||||
Window { window_id: WindowId, event: WindowEvent },
|
||||
ScaleFactorChanged(ScaleFactorChanged),
|
||||
}
|
||||
|
||||
@@ -96,14 +81,9 @@ pub struct ScaleFactorChanged {
|
||||
pub(super) scale_factor: f64,
|
||||
}
|
||||
|
||||
enum UserCallbackTransitionResult<'a> {
|
||||
Success { active_control_flow: ControlFlow, processing_redraws: bool },
|
||||
ReentrancyPrevented { queued_events: &'a mut Vec<EventWrapper> },
|
||||
}
|
||||
|
||||
impl Event {
|
||||
impl EventWrapper {
|
||||
fn is_redraw(&self) -> bool {
|
||||
matches!(self, Event::WindowEvent { event: WindowEvent::RedrawRequested, .. })
|
||||
matches!(self, Self::Window { event: WindowEvent::RedrawRequested, .. })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,18 +92,12 @@ impl Event {
|
||||
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
|
||||
enum AppStateImpl {
|
||||
Initial {
|
||||
queued_events: Vec<EventWrapper>,
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
},
|
||||
ProcessingEvents {
|
||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||
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 {
|
||||
active_control_flow: ControlFlow,
|
||||
},
|
||||
@@ -139,7 +113,8 @@ pub(crate) struct AppState {
|
||||
app_state: Option<AppStateImpl>,
|
||||
control_flow: ControlFlow,
|
||||
waker: EventLoopWaker,
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
event_loop_proxy: Arc<EventLoopProxy>,
|
||||
queued_events: Vec<EventWrapper>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -158,13 +133,11 @@ impl AppState {
|
||||
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
|
||||
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() });
|
||||
**guard = Some(AppState {
|
||||
app_state: Some(AppStateImpl::Initial {
|
||||
queued_events: Vec::new(),
|
||||
queued_gpu_redraws: HashSet::new(),
|
||||
}),
|
||||
app_state: Some(AppStateImpl::Initial { queued_gpu_redraws: HashSet::new() }),
|
||||
control_flow: ControlFlow::default(),
|
||||
waker,
|
||||
proxy_wake_up: Arc::new(AtomicBool::new(false)),
|
||||
event_loop_proxy: Arc::new(EventLoopProxy::new()),
|
||||
queued_events: Vec::new(),
|
||||
});
|
||||
}
|
||||
init_guard(&mut guard);
|
||||
@@ -217,48 +190,34 @@ impl AppState {
|
||||
matches!(self.state(), AppStateImpl::Terminated)
|
||||
}
|
||||
|
||||
fn did_finish_launching_transition(&mut self) -> Vec<EventWrapper> {
|
||||
let (events, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::Initial { queued_events, queued_gpu_redraws } => {
|
||||
(queued_events, queued_gpu_redraws)
|
||||
},
|
||||
fn did_finish_launching_transition(&mut self) {
|
||||
let queued_gpu_redraws = match self.take_state() {
|
||||
AppStateImpl::Initial { queued_gpu_redraws } => queued_gpu_redraws,
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
self.set_state(AppStateImpl::ProcessingEvents {
|
||||
active_control_flow: self.control_flow,
|
||||
queued_gpu_redraws,
|
||||
});
|
||||
events
|
||||
}
|
||||
|
||||
fn wakeup_transition(&mut self) -> Option<EventWrapper> {
|
||||
fn wakeup_transition(&mut self) -> Option<StartCause> {
|
||||
// before `AppState::did_finish_launching` is called, pretend there is no running
|
||||
// event loop.
|
||||
if !self.has_launched() || self.has_terminated() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let event = match (self.control_flow, self.take_state()) {
|
||||
(ControlFlow::Poll, AppStateImpl::PollFinished) => {
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll))
|
||||
},
|
||||
let start_cause = match (self.control_flow, self.take_state()) {
|
||||
(ControlFlow::Poll, AppStateImpl::PollFinished) => StartCause::Poll,
|
||||
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: None,
|
||||
}))
|
||||
StartCause::WaitCancelled { start, requested_resume: None }
|
||||
},
|
||||
(ControlFlow::WaitUntil(requested_resume), AppStateImpl::Waiting { start }) => {
|
||||
if Instant::now() >= requested_resume {
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached {
|
||||
start,
|
||||
requested_resume,
|
||||
}))
|
||||
StartCause::ResumeTimeReached { start, requested_resume }
|
||||
} else {
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: Some(requested_resume),
|
||||
}))
|
||||
StartCause::WaitCancelled { start, requested_resume: Some(requested_resume) }
|
||||
}
|
||||
},
|
||||
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
|
||||
@@ -268,55 +227,7 @@ impl AppState {
|
||||
queued_gpu_redraws: Default::default(),
|
||||
active_control_flow: self.control_flow,
|
||||
});
|
||||
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 }
|
||||
Some(start_cause)
|
||||
}
|
||||
|
||||
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
|
||||
@@ -372,12 +283,12 @@ impl AppState {
|
||||
fn terminated_transition(&mut self) {
|
||||
match self.replace_state(AppStateImpl::Terminated) {
|
||||
AppStateImpl::ProcessingEvents { .. } => {},
|
||||
s => bug!("`LoopExiting` happened while not processing events {:?}", s),
|
||||
s => bug!("terminated while not processing events {:?}", s),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn proxy_wake_up(&self) -> Arc<AtomicBool> {
|
||||
self.proxy_wake_up.clone()
|
||||
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
|
||||
&self.event_loop_proxy
|
||||
}
|
||||
|
||||
pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) {
|
||||
@@ -393,8 +304,7 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<W
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. }
|
||||
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. }
|
||||
| &mut AppStateImpl::InUserCallback { ref mut queued_gpu_redraws, .. } => {
|
||||
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. } => {
|
||||
let _ = queued_gpu_redraws.insert(window);
|
||||
},
|
||||
s @ &mut AppStateImpl::ProcessingRedraws { .. }
|
||||
@@ -418,27 +328,24 @@ pub fn did_finish_launching(mtm: MainThreadMarker) {
|
||||
// have to drop RefMut because the window setup code below can trigger new events
|
||||
drop(this);
|
||||
|
||||
let events = AppState::get_mut(mtm).did_finish_launching_transition();
|
||||
AppState::get_mut(mtm).did_finish_launching_transition();
|
||||
|
||||
let events = [
|
||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init)),
|
||||
EventWrapper::StaticEvent(Event::CreateSurfaces),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(events);
|
||||
handle_nonuser_events(mtm, events);
|
||||
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, StartCause::Init));
|
||||
get_handler(mtm).handle(|app| app.can_create_surfaces(&ActiveEventLoop { mtm }));
|
||||
handle_nonuser_events(mtm, []);
|
||||
}
|
||||
|
||||
// AppState::did_finish_launching handles the special transition `Init`
|
||||
pub fn handle_wakeup_transition(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let wakeup_event = match this.wakeup_transition() {
|
||||
let cause = match this.wakeup_transition() {
|
||||
None => return,
|
||||
Some(wakeup_event) => wakeup_event,
|
||||
Some(cause) => cause,
|
||||
};
|
||||
drop(this);
|
||||
|
||||
handle_nonuser_event(mtm, wakeup_event)
|
||||
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, cause));
|
||||
handle_nonuser_events(mtm, []);
|
||||
}
|
||||
|
||||
pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) {
|
||||
@@ -454,132 +361,75 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
return;
|
||||
}
|
||||
|
||||
let (active_control_flow, processing_redraws) = match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
||||
queued_events.extend(events);
|
||||
return;
|
||||
},
|
||||
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws } => {
|
||||
(active_control_flow, processing_redraws)
|
||||
},
|
||||
};
|
||||
if !get_handler(mtm).ready() {
|
||||
// Prevent re-entrancy; queue the events up for once we're done handling the event instead.
|
||||
this.queued_events.extend(events);
|
||||
return;
|
||||
}
|
||||
|
||||
let processing_redraws = matches!(this.state(), AppStateImpl::ProcessingRedraws { .. });
|
||||
drop(this);
|
||||
|
||||
for wrapper in events {
|
||||
match wrapper {
|
||||
EventWrapper::StaticEvent(event) => {
|
||||
if !processing_redraws && event.is_redraw() {
|
||||
tracing::info!("processing `RedrawRequested` during the main event loop");
|
||||
} else if processing_redraws && !event.is_redraw() {
|
||||
tracing::warn!(
|
||||
"processing non `RedrawRequested` event after the main event loop: {:#?}",
|
||||
event
|
||||
);
|
||||
}
|
||||
handle_event(mtm, event)
|
||||
},
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||
for event in events {
|
||||
if !processing_redraws && event.is_redraw() {
|
||||
tracing::info!("processing `RedrawRequested` during the main event loop");
|
||||
} else if processing_redraws && !event.is_redraw() {
|
||||
tracing::warn!(
|
||||
"processing non `RedrawRequested` event after the main event loop: {:#?}",
|
||||
event
|
||||
);
|
||||
}
|
||||
handle_wrapped_event(mtm, event)
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
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),
|
||||
};
|
||||
let queued_events = mem::take(&mut this.queued_events);
|
||||
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;
|
||||
}
|
||||
drop(this);
|
||||
|
||||
for wrapper in queued_events {
|
||||
match wrapper {
|
||||
EventWrapper::StaticEvent(event) => {
|
||||
if !processing_redraws && event.is_redraw() {
|
||||
tracing::info!("processing `RedrawRequested` during the main event loop");
|
||||
} else if processing_redraws && !event.is_redraw() {
|
||||
tracing::warn!(
|
||||
"processing non-`RedrawRequested` event after the main event loop: \
|
||||
{:#?}",
|
||||
event
|
||||
);
|
||||
}
|
||||
handle_event(mtm, event)
|
||||
},
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||
for event in queued_events {
|
||||
if !processing_redraws && event.is_redraw() {
|
||||
tracing::info!("processing `RedrawRequested` during the main event loop");
|
||||
} else if processing_redraws && !event.is_redraw() {
|
||||
tracing::warn!(
|
||||
"processing non-`RedrawRequested` event after the main event loop: {:#?}",
|
||||
event
|
||||
);
|
||||
}
|
||||
handle_wrapped_event(mtm, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_user_events(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
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 {
|
||||
let this = AppState::get_mut(mtm);
|
||||
if matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }) {
|
||||
bug!("user events attempted to be sent out while `ProcessingRedraws`");
|
||||
}
|
||||
let proxy_wake_up = this.proxy_wake_up.clone();
|
||||
let event_loop_proxy = this.event_loop_proxy().clone();
|
||||
drop(this);
|
||||
|
||||
if proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||
handle_event(mtm, Event::UserWakeUp);
|
||||
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
|
||||
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
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),
|
||||
};
|
||||
let queued_events = mem::take(&mut this.queued_events);
|
||||
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;
|
||||
}
|
||||
drop(this);
|
||||
|
||||
for wrapper in queued_events {
|
||||
match wrapper {
|
||||
EventWrapper::StaticEvent(event) => handle_event(mtm, event),
|
||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||
}
|
||||
for event in queued_events {
|
||||
handle_wrapped_event(mtm, event);
|
||||
}
|
||||
|
||||
if proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||
handle_event(mtm, Event::UserWakeUp);
|
||||
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
|
||||
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -597,10 +447,10 @@ pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, o
|
||||
let ptr: *const WinitUIWindow = ptr.cast();
|
||||
&*ptr
|
||||
};
|
||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
events.push(EventWrapper::Window {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::Occluded(occluded),
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
handle_nonuser_events(mtm, events);
|
||||
@@ -623,23 +473,37 @@ pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
||||
let redraw_events: Vec<EventWrapper> = this
|
||||
.main_events_cleared_transition()
|
||||
.into_iter()
|
||||
.map(|window| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
})
|
||||
.map(|window| EventWrapper::Window {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
})
|
||||
.collect();
|
||||
drop(this);
|
||||
|
||||
handle_nonuser_events(mtm, redraw_events);
|
||||
handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::AboutToWait));
|
||||
get_handler(mtm).handle(|app| app.about_to_wait(&ActiveEventLoop { mtm }));
|
||||
handle_nonuser_events(mtm, []);
|
||||
}
|
||||
|
||||
pub fn handle_events_cleared(mtm: MainThreadMarker) {
|
||||
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) {
|
||||
let mtm = MainThreadMarker::from(application);
|
||||
|
||||
@@ -653,10 +517,10 @@ pub(crate) fn terminated(application: &UIApplication) {
|
||||
let ptr: *const WinitUIWindow = ptr.cast();
|
||||
&*ptr
|
||||
};
|
||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
events.push(EventWrapper::Window {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::Destroyed,
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
handle_nonuser_events(mtm, events);
|
||||
@@ -665,20 +529,26 @@ pub(crate) fn terminated(application: &UIApplication) {
|
||||
this.terminated_transition();
|
||||
drop(this);
|
||||
|
||||
handle_event(mtm, Event::LoopExiting)
|
||||
get_handler(mtm).handle(|app| app.exiting(&ActiveEventLoop { mtm }));
|
||||
}
|
||||
|
||||
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) {
|
||||
let ScaleFactorChanged { suggested_size, scale_factor, window } = event;
|
||||
let new_surface_size = Arc::new(Mutex::new(suggested_size));
|
||||
let event = Event::WindowEvent {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
get_handler(mtm).handle(|app| {
|
||||
app.window_event(&ActiveEventLoop { mtm }, window.id(), WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
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 physical_size = *new_surface_size.lock().unwrap();
|
||||
drop(new_surface_size);
|
||||
|
||||
@@ -20,16 +20,17 @@ use objc2_ui_kit::{
|
||||
UIApplicationWillEnterForegroundNotification, UIApplicationWillResignActiveNotification,
|
||||
UIApplicationWillTerminateNotification, UIScreen,
|
||||
};
|
||||
use rwh_06::HasDisplayHandle;
|
||||
|
||||
use super::super::notification_center::create_observer;
|
||||
use super::app_state::{send_occluded_event_for_all_windows, AppState, EventWrapper};
|
||||
use super::app_state::{send_occluded_event_for_all_windows, AppState};
|
||||
use super::{app_state, monitor, MonitorHandle};
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform_impl::Window;
|
||||
@@ -41,9 +42,8 @@ pub(crate) struct ActiveEventLoop {
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
|
||||
let event_loop_proxy = EventLoopProxy::new(AppState::get_mut(self.mtm).proxy_wake_up());
|
||||
RootEventLoopProxy { event_loop_proxy }
|
||||
fn create_proxy(&self) -> CoreEventLoopProxy {
|
||||
CoreEventLoopProxy::new(AppState::get_mut(self.mtm).event_loop_proxy().clone())
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
@@ -94,17 +94,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
false
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
|
||||
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::UiKit(rwh_06::UiKitDisplayHandle::new());
|
||||
@@ -115,13 +113,10 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::UiKitDisplayHandle::new().into())
|
||||
impl HasDisplayHandle for OwnedDisplayHandle {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::UiKit(rwh_06::UiKitDisplayHandle::new());
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,17 +173,13 @@ impl EventLoop {
|
||||
¢er,
|
||||
// `applicationDidBecomeActive:`
|
||||
unsafe { UIApplicationDidBecomeActiveNotification },
|
||||
move |_| {
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed));
|
||||
},
|
||||
move |_| app_state::handle_resumed(mtm),
|
||||
);
|
||||
let _will_resign_active_observer = create_observer(
|
||||
¢er,
|
||||
// `applicationWillResignActive:`
|
||||
unsafe { UIApplicationWillResignActiveNotification },
|
||||
move |_| {
|
||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended));
|
||||
},
|
||||
move |_| app_state::handle_suspended(mtm),
|
||||
);
|
||||
let _will_enter_foreground_observer = create_observer(
|
||||
¢er,
|
||||
@@ -235,12 +226,7 @@ impl EventLoop {
|
||||
¢er,
|
||||
// `applicationDidReceiveMemoryWarning:`
|
||||
unsafe { UIApplicationDidReceiveMemoryWarningNotification },
|
||||
move |_| {
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::MemoryWarning),
|
||||
);
|
||||
},
|
||||
move |_| app_state::handle_memory_warning(mtm),
|
||||
);
|
||||
|
||||
Ok(EventLoop {
|
||||
@@ -292,19 +278,13 @@ impl EventLoop {
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy {
|
||||
proxy_wake_up: Arc<AtomicBool>,
|
||||
pub(crate) wake_up: AtomicBool,
|
||||
source: CFRunLoopSourceRef,
|
||||
}
|
||||
|
||||
unsafe impl Send for EventLoopProxy {}
|
||||
unsafe impl Sync for EventLoopProxy {}
|
||||
|
||||
impl Clone for EventLoopProxy {
|
||||
fn clone(&self) -> EventLoopProxy {
|
||||
EventLoopProxy::new(self.proxy_wake_up.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventLoopProxy {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
@@ -315,7 +295,7 @@ impl Drop for EventLoopProxy {
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
fn new(proxy_wake_up: Arc<AtomicBool>) -> EventLoopProxy {
|
||||
pub(crate) fn new() -> EventLoopProxy {
|
||||
unsafe {
|
||||
// just wake up the eventloop
|
||||
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
|
||||
@@ -339,12 +319,14 @@ impl EventLoopProxy {
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||
CFRunLoopWakeUp(rl);
|
||||
|
||||
EventLoopProxy { proxy_wake_up, source }
|
||||
EventLoopProxy { wake_up: AtomicBool::new(false), source }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wake_up(&self) {
|
||||
self.proxy_wake_up.store(true, AtomicOrdering::Relaxed);
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
self.wake_up.store(true, AtomicOrdering::Relaxed);
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
CFRunLoopSourceSignal(self.source);
|
||||
|
||||
@@ -10,8 +10,7 @@ mod window;
|
||||
use std::fmt;
|
||||
|
||||
pub(crate) use self::event_loop::{
|
||||
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
|
||||
PlatformSpecificEventLoopAttributes,
|
||||
ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes,
|
||||
};
|
||||
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
|
||||
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
|
||||
@@ -21,16 +20,6 @@ pub(crate) use crate::cursor::{
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId(usize);
|
||||
|
||||
impl FingerId {
|
||||
#[cfg(test)]
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {}
|
||||
|
||||
|
||||
@@ -101,13 +101,20 @@ impl Clone for MonitorHandle {
|
||||
|
||||
impl hash::Hash for MonitorHandle {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
(self as *const Self).hash(state);
|
||||
// SAFETY: Only getting the pointer.
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
Retained::as_ptr(self.ui_screen.get(mtm)).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for MonitorHandle {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
ptr::eq(self, other)
|
||||
// SAFETY: Only getting the pointer.
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
ptr::eq(
|
||||
Retained::as_ptr(self.ui_screen.get(mtm)),
|
||||
Retained::as_ptr(other.ui_screen.get(mtm)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +128,10 @@ impl PartialOrd for MonitorHandle {
|
||||
|
||||
impl Ord for MonitorHandle {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
// SAFETY: Only getting the pointer.
|
||||
// TODO: Make a better ordering
|
||||
(self as *const Self).cmp(&(other as *const Self))
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
Retained::as_ptr(self.ui_screen.get(mtm)).cmp(&Retained::as_ptr(other.ui_screen.get(mtm)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,3 +249,27 @@ pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
||||
#[allow(deprecated)]
|
||||
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))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,19 @@ use objc2::runtime::{NSObjectProtocol, ProtocolObject};
|
||||
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet, NSString};
|
||||
use objc2_ui_kit::{
|
||||
UICoordinateSpace, UIEvent, UIForceTouchCapability, UIGestureRecognizer,
|
||||
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer,
|
||||
UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer,
|
||||
UITextInputTraits, UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||
UIEvent, UIForceTouchCapability, UIGestureRecognizer, UIGestureRecognizerDelegate,
|
||||
UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer, UIPinchGestureRecognizer,
|
||||
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITextInputTraits, UITouch,
|
||||
UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use super::app_state::{self, EventWrapper};
|
||||
use super::window::WinitUIWindow;
|
||||
use super::FingerId;
|
||||
use crate::dpi::PhysicalPosition;
|
||||
use crate::event::{
|
||||
ButtonSource, ElementState, Event, FingerId as RootFingerId, Force, KeyEvent, PointerKind,
|
||||
PointerSource, TouchPhase, WindowEvent,
|
||||
ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource, TouchPhase,
|
||||
WindowEvent,
|
||||
};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
|
||||
use crate::platform_impl::KeyEventExtra;
|
||||
@@ -34,6 +34,9 @@ pub struct WinitViewState {
|
||||
rotation_last_delta: Cell<CGFloat>,
|
||||
pinch_last_delta: Cell<CGFloat>,
|
||||
pan_last_delta: Cell<CGPoint>,
|
||||
|
||||
primary_finger: Cell<Option<FingerId>>,
|
||||
fingers: Cell<u8>,
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
@@ -57,10 +60,10 @@ declare_class!(
|
||||
let window = self.window().unwrap();
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
EventWrapper::Window {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
}),
|
||||
},
|
||||
);
|
||||
let _: () = unsafe { msg_send![super(self), drawRect: rect] };
|
||||
}
|
||||
@@ -70,32 +73,21 @@ declare_class!(
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
|
||||
|
||||
let window = self.window().unwrap();
|
||||
let window_bounds = window.bounds();
|
||||
let screen = window.screen();
|
||||
let screen_space = screen.coordinateSpace();
|
||||
let screen_frame = self.convertRect_toCoordinateSpace(window_bounds, &screen_space);
|
||||
let scale_factor = screen.scale();
|
||||
let frame = self.frame();
|
||||
let scale_factor = self.contentScaleFactor() as f64;
|
||||
let size = crate::dpi::LogicalSize {
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
}
|
||||
.to_physical(scale_factor as f64);
|
||||
|
||||
// If the app is started in landscape, the view frame and window bounds can be mismatched.
|
||||
// The view frame will be in portrait and the window bounds in landscape. So apply the
|
||||
// window bounds to the view frame to make it consistent.
|
||||
let view_frame = self.frame();
|
||||
if view_frame != window_bounds {
|
||||
self.setFrame(window_bounds);
|
||||
width: frame.size.width as f64,
|
||||
height: frame.size.height as f64,
|
||||
}
|
||||
.to_physical(scale_factor);
|
||||
|
||||
let window = self.window().unwrap();
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
EventWrapper::Window {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::SurfaceResized(size),
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,13 +116,10 @@ declare_class!(
|
||||
"invalid scale_factor set on UIView",
|
||||
);
|
||||
let scale_factor = scale_factor as f64;
|
||||
let bounds = self.bounds();
|
||||
let screen = window.screen();
|
||||
let screen_space = screen.coordinateSpace();
|
||||
let screen_frame = self.convertRect_toCoordinateSpace(bounds, &screen_space);
|
||||
let frame = self.frame();
|
||||
let size = crate::dpi::LogicalSize {
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
width: frame.size.width as f64,
|
||||
height: frame.size.height as f64,
|
||||
};
|
||||
let window_id = window.id();
|
||||
app_state::handle_nonuser_events(
|
||||
@@ -142,15 +131,21 @@ declare_class!(
|
||||
suggested_size: size.to_physical(scale_factor),
|
||||
},
|
||||
))
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
.chain(std::iter::once(EventWrapper::Window {
|
||||
window_id,
|
||||
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:)]
|
||||
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||
self.handle_touches(touches)
|
||||
@@ -193,17 +188,17 @@ declare_class!(
|
||||
// Pass -delta so that action is reversed
|
||||
(TouchPhase::Cancelled, -recognizer.scale())
|
||||
}
|
||||
state => panic!("unexpected recognizer state: {:?}", state),
|
||||
state => panic!("unexpected recognizer state: {state:?}"),
|
||||
};
|
||||
|
||||
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
let gesture_event = EventWrapper::Window {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::PinchGesture {
|
||||
device_id: None,
|
||||
delta: delta as f64,
|
||||
phase,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, gesture_event);
|
||||
@@ -214,12 +209,12 @@ declare_class!(
|
||||
let window = self.window().unwrap();
|
||||
|
||||
if recognizer.state() == UIGestureRecognizerState::Ended {
|
||||
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
let gesture_event = EventWrapper::Window {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::DoubleTapGesture {
|
||||
device_id: None,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, gesture_event);
|
||||
@@ -252,18 +247,18 @@ declare_class!(
|
||||
// Pass -delta so that action is reversed
|
||||
(TouchPhase::Cancelled, -recognizer.rotation())
|
||||
}
|
||||
state => panic!("unexpected recognizer state: {:?}", state),
|
||||
state => panic!("unexpected recognizer state: {state:?}"),
|
||||
};
|
||||
|
||||
// Make delta negative to match macos, convert to degrees
|
||||
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
let gesture_event = EventWrapper::Window {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::RotationGesture {
|
||||
device_id: None,
|
||||
delta: -delta.to_degrees() as _,
|
||||
phase,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, gesture_event);
|
||||
@@ -303,18 +298,18 @@ declare_class!(
|
||||
// Pass -delta so that action is reversed
|
||||
(TouchPhase::Cancelled, -last_pan.x, -last_pan.y)
|
||||
}
|
||||
state => panic!("unexpected recognizer state: {:?}", state),
|
||||
state => panic!("unexpected recognizer state: {state:?}"),
|
||||
};
|
||||
|
||||
|
||||
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
let gesture_event = EventWrapper::Window {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::PanGesture {
|
||||
device_id: None,
|
||||
delta: PhysicalPosition::new(dx as _, dy as _),
|
||||
phase,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(mtm, gesture_event);
|
||||
@@ -371,6 +366,9 @@ impl WinitView {
|
||||
rotation_last_delta: Cell::new(0.0),
|
||||
pinch_last_delta: Cell::new(0.0),
|
||||
pan_last_delta: Cell::new(CGPoint { x: 0.0, y: 0.0 }),
|
||||
|
||||
primary_finger: Cell::new(None),
|
||||
fingers: Cell::new(0),
|
||||
});
|
||||
let this: Retained<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
|
||||
|
||||
@@ -513,14 +511,37 @@ impl WinitView {
|
||||
)
|
||||
};
|
||||
let window_id = window.id();
|
||||
let finger_id = RootFingerId(FingerId(touch_id));
|
||||
let finger_id = FingerId::from_raw(touch_id);
|
||||
|
||||
let ivars = self.ivars();
|
||||
|
||||
match phase {
|
||||
UITouchPhase::Began => {
|
||||
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
let primary = if let UITouchType::Pencil = touch_type {
|
||||
true
|
||||
} else {
|
||||
ivars.fingers.set(ivars.fingers.get() + 1);
|
||||
// Keep the primary finger around until we clear all the fingers to
|
||||
// recognize it when user briefly removes it.
|
||||
match ivars.primary_finger.get() {
|
||||
Some(primary_id) => primary_id == finger_id,
|
||||
None => {
|
||||
debug_assert_eq!(
|
||||
ivars.fingers.get(),
|
||||
1,
|
||||
"number of fingers were not counted correctly"
|
||||
);
|
||||
ivars.primary_finger.set(Some(finger_id));
|
||||
true
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
touch_events.push(EventWrapper::Window {
|
||||
window_id,
|
||||
event: WindowEvent::PointerEntered {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
kind: if let UITouchType::Pencil = touch_type {
|
||||
PointerKind::Unknown
|
||||
@@ -528,11 +549,12 @@ impl WinitView {
|
||||
PointerKind::Touch(finger_id)
|
||||
},
|
||||
},
|
||||
}));
|
||||
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
});
|
||||
touch_events.push(EventWrapper::Window {
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: ElementState::Pressed,
|
||||
position,
|
||||
button: if let UITouchType::Pencil = touch_type {
|
||||
@@ -541,29 +563,47 @@ impl WinitView {
|
||||
ButtonSource::Touch { finger_id, force }
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
},
|
||||
UITouchPhase::Moved => {
|
||||
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
let (primary, source) = if let UITouchType::Pencil = touch_type {
|
||||
(true, PointerSource::Unknown)
|
||||
} else {
|
||||
(ivars.primary_finger.get().unwrap() == finger_id, PointerSource::Touch {
|
||||
finger_id,
|
||||
force,
|
||||
})
|
||||
};
|
||||
|
||||
touch_events.push(EventWrapper::Window {
|
||||
window_id,
|
||||
event: WindowEvent::PointerMoved {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
source: if let UITouchType::Pencil = touch_type {
|
||||
PointerSource::Unknown
|
||||
} else {
|
||||
PointerSource::Touch { finger_id, force }
|
||||
},
|
||||
source,
|
||||
},
|
||||
}));
|
||||
});
|
||||
},
|
||||
// 2 is UITouchPhase::Stationary and is not expected here
|
||||
UITouchPhase::Ended | UITouchPhase::Cancelled => {
|
||||
let primary = if let UITouchType::Pencil = touch_type {
|
||||
true
|
||||
} else {
|
||||
ivars.fingers.set(ivars.fingers.get() - 1);
|
||||
let primary = ivars.primary_finger.get().unwrap() == finger_id;
|
||||
if ivars.fingers.get() == 0 {
|
||||
ivars.primary_finger.set(None);
|
||||
}
|
||||
primary
|
||||
};
|
||||
|
||||
if let UITouchPhase::Ended = phase {
|
||||
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
touch_events.push(EventWrapper::Window {
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: ElementState::Released,
|
||||
position,
|
||||
button: if let UITouchType::Pencil = touch_type {
|
||||
@@ -572,13 +612,14 @@ impl WinitView {
|
||||
ButtonSource::Touch { finger_id, force }
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
touch_events.push(EventWrapper::Window {
|
||||
window_id,
|
||||
event: WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary,
|
||||
position: Some(position),
|
||||
kind: if let UITouchType::Pencil = touch_type {
|
||||
PointerKind::Unknown
|
||||
@@ -586,7 +627,7 @@ impl WinitView {
|
||||
PointerKind::Touch(finger_id)
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
},
|
||||
_ => panic!("unexpected touch phase: {phase:?}"),
|
||||
}
|
||||
@@ -605,29 +646,25 @@ impl WinitView {
|
||||
text.to_string().chars().flat_map(|c| {
|
||||
let text = smol_str::SmolStr::from_iter([c]);
|
||||
// Emit both press and release events
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
event: KeyEvent {
|
||||
text: if state == ElementState::Pressed {
|
||||
Some(text.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
state,
|
||||
location: KeyLocation::Standard,
|
||||
repeat: false,
|
||||
logical_key: Key::Character(text.clone()),
|
||||
physical_key: PhysicalKey::Unidentified(
|
||||
NativeKeyCode::Unidentified,
|
||||
),
|
||||
platform_specific: KeyEventExtra {},
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| EventWrapper::Window {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: None,
|
||||
event: KeyEvent {
|
||||
text: if state == ElementState::Pressed {
|
||||
Some(text.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
is_synthetic: false,
|
||||
device_id: None,
|
||||
state,
|
||||
location: KeyLocation::Standard,
|
||||
repeat: false,
|
||||
logical_key: Key::Character(text.clone()),
|
||||
physical_key: PhysicalKey::Unidentified(NativeKeyCode::Unidentified),
|
||||
platform_specific: KeyEventExtra {},
|
||||
},
|
||||
})
|
||||
is_synthetic: false,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
@@ -639,23 +676,21 @@ impl WinitView {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| {
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: None,
|
||||
event: KeyEvent {
|
||||
state,
|
||||
logical_key: Key::Named(NamedKey::Backspace),
|
||||
physical_key: PhysicalKey::Code(KeyCode::Backspace),
|
||||
platform_specific: KeyEventExtra {},
|
||||
repeat: false,
|
||||
location: KeyLocation::Standard,
|
||||
text: None,
|
||||
},
|
||||
is_synthetic: false,
|
||||
[ElementState::Pressed, ElementState::Released].map(|state| EventWrapper::Window {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: None,
|
||||
event: KeyEvent {
|
||||
state,
|
||||
logical_key: Key::Named(NamedKey::Backspace),
|
||||
physical_key: PhysicalKey::Code(KeyCode::Backspace),
|
||||
platform_specific: KeyEventExtra {},
|
||||
repeat: false,
|
||||
location: KeyLocation::Standard,
|
||||
text: None,
|
||||
},
|
||||
})
|
||||
is_synthetic: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ use objc2_foundation::{
|
||||
CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker, NSObject, NSObjectProtocol,
|
||||
};
|
||||
use objc2_ui_kit::{
|
||||
UIApplication, UICoordinateSpace, UIResponder, UIScreen, UIScreenOverscanCompensation,
|
||||
UIViewController, UIWindow,
|
||||
UIApplication, UICoordinateSpace, UIEdgeInsets, UIResponder, UIScreen,
|
||||
UIScreenOverscanCompensation, UIViewController, UIWindow,
|
||||
};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
@@ -18,9 +18,12 @@ use super::view::WinitView;
|
||||
use super::view_controller::WinitViewController;
|
||||
use super::{app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle};
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::dpi::{
|
||||
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
|
||||
Position, Size,
|
||||
};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{Event, WindowEvent};
|
||||
use crate::event::WindowEvent;
|
||||
use crate::icon::Icon;
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations};
|
||||
@@ -48,10 +51,10 @@ declare_class!(
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
EventWrapper::Window {
|
||||
window_id: self.id(),
|
||||
event: WindowEvent::Focused(true),
|
||||
}),
|
||||
},
|
||||
);
|
||||
let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
|
||||
}
|
||||
@@ -61,10 +64,10 @@ declare_class!(
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_event(
|
||||
mtm,
|
||||
EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
EventWrapper::Window {
|
||||
window_id: self.id(),
|
||||
event: WindowEvent::Focused(false),
|
||||
}),
|
||||
},
|
||||
);
|
||||
let _: () = unsafe { msg_send![super(self), resignKeyWindow] };
|
||||
}
|
||||
@@ -158,20 +161,19 @@ impl Inner {
|
||||
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
pub fn inner_position(&self) -> PhysicalPosition<i32> {
|
||||
let safe_area = self.safe_area_screen_space();
|
||||
pub fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
let view_position = self.view.frame().origin;
|
||||
let position =
|
||||
LogicalPosition { x: safe_area.origin.x as f64, y: safe_area.origin.y as f64 };
|
||||
let scale_factor = self.scale_factor();
|
||||
position.to_physical(scale_factor)
|
||||
unsafe { self.window.convertPoint_fromView(view_position, Some(&self.view)) };
|
||||
let position = LogicalPosition::new(position.x, position.y);
|
||||
position.to_physical(self.scale_factor())
|
||||
}
|
||||
|
||||
pub fn outer_position(&self) -> PhysicalPosition<i32> {
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
let screen_frame = self.screen_frame();
|
||||
let position =
|
||||
LogicalPosition { x: screen_frame.origin.x as f64, y: screen_frame.origin.y as f64 };
|
||||
let scale_factor = self.scale_factor();
|
||||
position.to_physical(scale_factor)
|
||||
Ok(position.to_physical(self.scale_factor()))
|
||||
}
|
||||
|
||||
pub fn set_outer_position(&self, physical_position: Position) {
|
||||
@@ -187,29 +189,36 @@ impl Inner {
|
||||
}
|
||||
|
||||
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let safe_area = self.safe_area_screen_space();
|
||||
let size = LogicalSize {
|
||||
width: safe_area.size.width as f64,
|
||||
height: safe_area.size.height as f64,
|
||||
};
|
||||
size.to_physical(scale_factor)
|
||||
let frame = self.view.frame();
|
||||
let size = LogicalSize::new(frame.size.width, frame.size.height);
|
||||
size.to_physical(self.scale_factor())
|
||||
}
|
||||
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let screen_frame = self.screen_frame();
|
||||
let size = LogicalSize {
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
};
|
||||
size.to_physical(scale_factor)
|
||||
let frame = self.window.frame();
|
||||
let size = LogicalSize::new(frame.size.width, frame.size.height);
|
||||
size.to_physical(self.scale_factor())
|
||||
}
|
||||
|
||||
pub fn request_surface_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
|
||||
Some(self.surface_size())
|
||||
}
|
||||
|
||||
pub fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
// Only available on iOS 11.0
|
||||
let insets = if app_state::os_capabilities().safe_area {
|
||||
self.view.safeAreaInsets()
|
||||
} else {
|
||||
// Assume the status bar frame is the only thing that obscures the view
|
||||
let app = UIApplication::sharedApplication(MainThreadMarker::new().unwrap());
|
||||
#[allow(deprecated)]
|
||||
let status_bar_frame = app.statusBarFrame();
|
||||
UIEdgeInsets { top: status_bar_frame.size.height, left: 0.0, bottom: 0.0, right: 0.0 }
|
||||
};
|
||||
let insets = LogicalInsets::new(insets.top, insets.left, insets.bottom, insets.right);
|
||||
insets.to_physical(self.scale_factor())
|
||||
}
|
||||
|
||||
pub fn set_min_surface_size(&self, _dimensions: Option<Size>) {
|
||||
warn!("`Window::set_min_surface_size` is ignored on iOS")
|
||||
}
|
||||
@@ -420,7 +429,6 @@ impl Inner {
|
||||
self.window.id()
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
|
||||
let mut window_handle = rwh_06::UiKitWindowHandle::new({
|
||||
let ui_view = Retained::as_ptr(&self.view) as _;
|
||||
@@ -509,35 +517,6 @@ impl Window {
|
||||
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
|
||||
window.makeKeyAndVisible();
|
||||
|
||||
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `SurfaceResized`
|
||||
// event on window creation if the DPI factor != 1.0
|
||||
let scale_factor = view.contentScaleFactor();
|
||||
let scale_factor = scale_factor as f64;
|
||||
if scale_factor != 1.0 {
|
||||
let bounds = view.bounds();
|
||||
let screen = window.screen();
|
||||
let screen_space = screen.coordinateSpace();
|
||||
let screen_frame = view.convertRect_toCoordinateSpace(bounds, &screen_space);
|
||||
let size = LogicalSize {
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
};
|
||||
app_state::handle_nonuser_events(
|
||||
mtm,
|
||||
std::iter::once(EventWrapper::ScaleFactorChanged(app_state::ScaleFactorChanged {
|
||||
window: window.clone(),
|
||||
scale_factor,
|
||||
suggested_size: size.to_physical(scale_factor),
|
||||
}))
|
||||
.chain(std::iter::once(EventWrapper::StaticEvent(
|
||||
Event::WindowEvent {
|
||||
window_id: window.id(),
|
||||
event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
let inner = Inner { window, view_controller, view, gl_or_metal_backed };
|
||||
Ok(Window { inner: MainThreadBound::new(inner, mtm) })
|
||||
}
|
||||
@@ -546,7 +525,6 @@ impl Window {
|
||||
self.inner.get_on_main(|inner| f(inner))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub(crate) fn raw_window_handle_rwh_06(
|
||||
&self,
|
||||
@@ -558,7 +536,6 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub(crate) fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
@@ -567,7 +544,6 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
@@ -575,7 +551,6 @@ impl rwh_06::HasDisplayHandle for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
@@ -604,12 +579,12 @@ impl CoreWindow for Window {
|
||||
self.maybe_wait_on_main(|delegate| delegate.reset_dead_keys());
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.inner_position()))
|
||||
fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.surface_position())
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.maybe_wait_on_main(|delegate| delegate.outer_position()))
|
||||
self.maybe_wait_on_main(|delegate| delegate.outer_position())
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
@@ -628,6 +603,10 @@ impl CoreWindow for Window {
|
||||
self.maybe_wait_on_main(|delegate| delegate.outer_size())
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
self.maybe_wait_on_main(|delegate| delegate.safe_area())
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size))
|
||||
}
|
||||
@@ -815,12 +794,10 @@ impl CoreWindow for Window {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
@@ -888,7 +865,7 @@ impl Inner {
|
||||
|
||||
impl Inner {
|
||||
fn screen_frame(&self) -> CGRect {
|
||||
self.rect_to_screen_space(self.window.bounds())
|
||||
self.rect_to_screen_space(self.window.frame())
|
||||
}
|
||||
|
||||
fn rect_to_screen_space(&self, rect: CGRect) -> CGRect {
|
||||
@@ -900,43 +877,6 @@ impl Inner {
|
||||
let screen_space = self.window.screen().coordinateSpace();
|
||||
self.window.convertRect_fromCoordinateSpace(rect, &screen_space)
|
||||
}
|
||||
|
||||
fn safe_area_screen_space(&self) -> CGRect {
|
||||
let bounds = self.window.bounds();
|
||||
if app_state::os_capabilities().safe_area {
|
||||
let safe_area = self.window.safeAreaInsets();
|
||||
let safe_bounds = CGRect {
|
||||
origin: CGPoint {
|
||||
x: bounds.origin.x + safe_area.left,
|
||||
y: bounds.origin.y + safe_area.top,
|
||||
},
|
||||
size: CGSize {
|
||||
width: bounds.size.width - safe_area.left - safe_area.right,
|
||||
height: bounds.size.height - safe_area.top - safe_area.bottom,
|
||||
},
|
||||
};
|
||||
self.rect_to_screen_space(safe_bounds)
|
||||
} else {
|
||||
let screen_frame = self.rect_to_screen_space(bounds);
|
||||
let status_bar_frame = {
|
||||
let app = UIApplication::sharedApplication(MainThreadMarker::new().unwrap());
|
||||
#[allow(deprecated)]
|
||||
app.statusBarFrame()
|
||||
};
|
||||
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
|
||||
(screen_frame.origin.y, screen_frame.size.height)
|
||||
} else {
|
||||
let y = status_bar_frame.size.height;
|
||||
let height = screen_frame.size.height
|
||||
- (status_bar_frame.size.height - screen_frame.origin.y);
|
||||
(y, height)
|
||||
};
|
||||
CGRect {
|
||||
origin: CGPoint { x: screen_frame.origin.x, y },
|
||||
size: CGSize { width: screen_frame.size.width, height },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
|
||||
@@ -35,6 +35,9 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
// are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes,
|
||||
// I can only hope they agree on what the keycodes mean.
|
||||
//
|
||||
// The mapping here is heavily influenced by Firefox' source:
|
||||
// https://searchfox.org/mozilla-central/rev/c597e9c789ad36af84a0370d395be066b7dc94f4/widget/NativeKeyToDOMCodeName.h
|
||||
//
|
||||
// Some of the keycodes are likely superfluous for our purposes, and some are ones which are
|
||||
// difficult to test the correctness of, or discover the purpose of. Because of this, they've
|
||||
// either been commented out here, or not included at all.
|
||||
@@ -166,23 +169,23 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
125 => KeyCode::SuperLeft,
|
||||
126 => KeyCode::SuperRight,
|
||||
127 => KeyCode::ContextMenu,
|
||||
// 128 => KeyCode::STOP,
|
||||
// 129 => KeyCode::AGAIN,
|
||||
// 130 => KeyCode::PROPS,
|
||||
// 131 => KeyCode::UNDO,
|
||||
// 132 => KeyCode::FRONT,
|
||||
// 133 => KeyCode::COPY,
|
||||
// 134 => KeyCode::OPEN,
|
||||
// 135 => KeyCode::PASTE,
|
||||
// 136 => KeyCode::FIND,
|
||||
// 137 => KeyCode::CUT,
|
||||
// 138 => KeyCode::HELP,
|
||||
128 => KeyCode::BrowserStop,
|
||||
129 => KeyCode::Again,
|
||||
130 => KeyCode::Props,
|
||||
131 => KeyCode::Undo,
|
||||
132 => KeyCode::Select, // FRONT
|
||||
133 => KeyCode::Copy,
|
||||
134 => KeyCode::Open,
|
||||
135 => KeyCode::Paste,
|
||||
136 => KeyCode::Find,
|
||||
137 => KeyCode::Cut,
|
||||
138 => KeyCode::Help,
|
||||
// 139 => KeyCode::MENU,
|
||||
// 140 => KeyCode::CALC,
|
||||
140 => KeyCode::LaunchApp2, // CALC
|
||||
// 141 => KeyCode::SETUP,
|
||||
// 142 => KeyCode::SLEEP,
|
||||
// 143 => KeyCode::WAKEUP,
|
||||
// 144 => KeyCode::FILE,
|
||||
143 => KeyCode::WakeUp,
|
||||
144 => KeyCode::LaunchApp1, // FILE
|
||||
// 145 => KeyCode::SENDFILE,
|
||||
// 146 => KeyCode::DELETEFILE,
|
||||
// 147 => KeyCode::XFER,
|
||||
@@ -193,13 +196,13 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
// 152 => KeyCode::COFFEE,
|
||||
// 153 => KeyCode::ROTATE_DISPLAY,
|
||||
// 154 => KeyCode::CYCLEWINDOWS,
|
||||
// 155 => KeyCode::MAIL,
|
||||
// 156 => KeyCode::BOOKMARKS,
|
||||
155 => KeyCode::LaunchMail,
|
||||
156 => KeyCode::BrowserFavorites, // BOOKMARKS
|
||||
// 157 => KeyCode::COMPUTER,
|
||||
// 158 => KeyCode::BACK,
|
||||
// 159 => KeyCode::FORWARD,
|
||||
158 => KeyCode::BrowserBack,
|
||||
159 => KeyCode::BrowserForward,
|
||||
// 160 => KeyCode::CLOSECD,
|
||||
// 161 => KeyCode::EJECTCD,
|
||||
161 => KeyCode::Eject, // EJECTCD
|
||||
// 162 => KeyCode::EJECTCLOSECD,
|
||||
163 => KeyCode::MediaTrackNext,
|
||||
164 => KeyCode::MediaPlayPause,
|
||||
@@ -209,9 +212,9 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
// 168 => KeyCode::REWIND,
|
||||
// 169 => KeyCode::PHONE,
|
||||
// 170 => KeyCode::ISO,
|
||||
// 171 => KeyCode::CONFIG,
|
||||
// 172 => KeyCode::HOMEPAGE,
|
||||
// 173 => KeyCode::REFRESH,
|
||||
171 => KeyCode::MediaSelect, // CONFIG
|
||||
172 => KeyCode::BrowserHome,
|
||||
173 => KeyCode::BrowserRefresh,
|
||||
// 174 => KeyCode::EXIT,
|
||||
// 175 => KeyCode::MOVE,
|
||||
// 176 => KeyCode::EDIT,
|
||||
@@ -250,7 +253,7 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
// 214 => KeyCode::QUESTION,
|
||||
// 215 => KeyCode::EMAIL,
|
||||
// 216 => KeyCode::CHAT,
|
||||
// 217 => KeyCode::SEARCH,
|
||||
217 => KeyCode::BrowserSearch,
|
||||
// 218 => KeyCode::CONNECT,
|
||||
// 219 => KeyCode::FINANCE,
|
||||
// 220 => KeyCode::SPORT,
|
||||
@@ -419,10 +422,32 @@ pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option<u32> {
|
||||
KeyCode::SuperLeft => Some(125),
|
||||
KeyCode::SuperRight => Some(126),
|
||||
KeyCode::ContextMenu => Some(127),
|
||||
KeyCode::BrowserStop => Some(128),
|
||||
KeyCode::Again => Some(129),
|
||||
KeyCode::Props => Some(130),
|
||||
KeyCode::Undo => Some(131),
|
||||
KeyCode::Select => Some(132),
|
||||
KeyCode::Copy => Some(133),
|
||||
KeyCode::Open => Some(134),
|
||||
KeyCode::Paste => Some(135),
|
||||
KeyCode::Find => Some(136),
|
||||
KeyCode::Cut => Some(137),
|
||||
KeyCode::Help => Some(138),
|
||||
KeyCode::LaunchApp2 => Some(140),
|
||||
KeyCode::WakeUp => Some(143),
|
||||
KeyCode::LaunchApp1 => Some(144),
|
||||
KeyCode::LaunchMail => Some(155),
|
||||
KeyCode::BrowserFavorites => Some(156),
|
||||
KeyCode::BrowserBack => Some(158),
|
||||
KeyCode::BrowserForward => Some(159),
|
||||
KeyCode::Eject => Some(161),
|
||||
KeyCode::MediaTrackNext => Some(163),
|
||||
KeyCode::MediaPlayPause => Some(164),
|
||||
KeyCode::MediaTrackPrevious => Some(165),
|
||||
KeyCode::MediaStop => Some(166),
|
||||
KeyCode::MediaSelect => Some(171),
|
||||
KeyCode::BrowserHome => Some(172),
|
||||
KeyCode::BrowserRefresh => Some(173),
|
||||
KeyCode::F13 => Some(183),
|
||||
KeyCode::F14 => Some(184),
|
||||
KeyCode::F15 => Some(185),
|
||||
@@ -435,6 +460,7 @@ pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option<u32> {
|
||||
KeyCode::F22 => Some(192),
|
||||
KeyCode::F23 => Some(193),
|
||||
KeyCode::F24 => Some(194),
|
||||
KeyCode::BrowserSearch => Some(217),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ pub struct KeyContext<'a> {
|
||||
scratch_buffer: &'a mut Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a> KeyContext<'a> {
|
||||
impl KeyContext<'_> {
|
||||
pub fn process_key_event(
|
||||
&mut self,
|
||||
keycode: u32,
|
||||
|
||||
@@ -107,24 +107,6 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported>>> =
|
||||
Lazy::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)));
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum FingerId {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::FingerId),
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(wayland::FingerId),
|
||||
}
|
||||
|
||||
impl FingerId {
|
||||
#[cfg(test)]
|
||||
pub const fn dummy() -> Self {
|
||||
#[cfg(wayland_platform)]
|
||||
return FingerId::Wayland(wayland::FingerId::dummy());
|
||||
#[cfg(all(not(wayland_platform), x11_platform))]
|
||||
return FingerId::X(x11::FingerId::dummy());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum MonitorHandle {
|
||||
#[cfg(x11_platform)]
|
||||
@@ -295,14 +277,6 @@ pub enum EventLoop {
|
||||
X(x11::EventLoop),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum EventLoopProxy {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::EventLoopProxy),
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(wayland::EventLoopProxy),
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
pub(crate) fn new(
|
||||
attributes: &PlatformSpecificEventLoopAttributes,
|
||||
@@ -422,65 +396,6 @@ impl AsRawFd for EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.wake_up())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum OwnedDisplayHandle {
|
||||
#[cfg(x11_platform)]
|
||||
X(Arc<XConnection>),
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland(wayland_client::Connection),
|
||||
}
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
use std::ptr::NonNull;
|
||||
|
||||
match self {
|
||||
#[cfg(x11_platform)]
|
||||
Self::X(xconn) => Ok(rwh_06::XlibDisplayHandle::new(
|
||||
NonNull::new(xconn.display.cast()),
|
||||
xconn.default_screen_index() as _,
|
||||
)
|
||||
.into()),
|
||||
|
||||
#[cfg(wayland_platform)]
|
||||
Self::Wayland(conn) => {
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
Ok(rwh_06::WaylandDisplayHandle::new(
|
||||
NonNull::new(conn.display().id().as_ptr().cast()).unwrap(),
|
||||
)
|
||||
.into())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for OwnedDisplayHandle {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
#[cfg(x11_platform)]
|
||||
(Self::X(this), Self::X(other)) => Arc::as_ptr(this).eq(&Arc::as_ptr(other)),
|
||||
#[cfg(wayland_platform)]
|
||||
(Self::Wayland(this), Self::Wayland(other)) => this.eq(other),
|
||||
#[cfg(all(x11_platform, wayland_platform))]
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for OwnedDisplayHandle {}
|
||||
|
||||
/// Returns the minimum `Option<Duration>`, taking into account that `None`
|
||||
/// equates to an infinite timeout, not a zero timeout (so can't just use
|
||||
/// `Option::min`)
|
||||
|
||||
@@ -16,7 +16,10 @@ use crate::cursor::OnlyCursorImage;
|
||||
use crate::dpi::LogicalSize;
|
||||
use crate::error::{EventLoopError, OsError, RequestError};
|
||||
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::platform::min_timeout;
|
||||
use crate::platform_impl::PlatformCustomCursor;
|
||||
@@ -25,12 +28,13 @@ use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme}
|
||||
mod proxy;
|
||||
pub mod sink;
|
||||
|
||||
pub use proxy::EventLoopProxy;
|
||||
use proxy::EventLoopProxy;
|
||||
use sink::EventSink;
|
||||
|
||||
use super::state::{WindowCompositorUpdate, WinitState};
|
||||
use super::window::state::FrameCallbackState;
|
||||
use super::{logical_to_physical_rounded, WindowId};
|
||||
pub use crate::event_loop::EventLoopProxy as CoreEventLoopProxy;
|
||||
|
||||
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
|
||||
|
||||
@@ -48,7 +52,7 @@ pub struct EventLoop {
|
||||
wayland_dispatcher: WaylandDispatcher,
|
||||
|
||||
/// Connection to the wayland server.
|
||||
connection: Connection,
|
||||
handle: Arc<OwnedDisplayHandle>,
|
||||
|
||||
/// Event loop window target.
|
||||
active_event_loop: ActiveEventLoop,
|
||||
@@ -116,11 +120,12 @@ impl EventLoop {
|
||||
})
|
||||
.map_err(|err| os_error!(err))?;
|
||||
|
||||
let handle = Arc::new(OwnedDisplayHandle::new(connection));
|
||||
let active_event_loop = ActiveEventLoop {
|
||||
connection: connection.clone(),
|
||||
handle: handle.clone(),
|
||||
wayland_dispatcher: wayland_dispatcher.clone(),
|
||||
event_loop_awakener,
|
||||
event_loop_proxy: EventLoopProxy::new(ping),
|
||||
event_loop_proxy: EventLoopProxy::new(ping).into(),
|
||||
queue_handle,
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
exit: Cell::new(None),
|
||||
@@ -132,7 +137,7 @@ impl EventLoop {
|
||||
compositor_updates: Vec::new(),
|
||||
buffer_sink: EventSink::default(),
|
||||
window_ids: Vec::new(),
|
||||
connection,
|
||||
handle,
|
||||
wayland_dispatcher,
|
||||
event_loop,
|
||||
active_event_loop,
|
||||
@@ -226,7 +231,7 @@ impl EventLoop {
|
||||
//
|
||||
// Checking for flush error is essential to perform an exit with error, since
|
||||
// once we have a protocol error, we could get stuck retrying...
|
||||
if self.connection.flush().is_err() {
|
||||
if self.handle.connection.flush().is_err() {
|
||||
self.set_exit_code(1);
|
||||
return;
|
||||
}
|
||||
@@ -539,7 +544,7 @@ impl AsRawFd for EventLoop {
|
||||
|
||||
pub struct ActiveEventLoop {
|
||||
/// Event loop proxy
|
||||
event_loop_proxy: EventLoopProxy,
|
||||
event_loop_proxy: CoreEventLoopProxy,
|
||||
|
||||
/// The event loop wakeup source.
|
||||
pub event_loop_awakener: calloop::ping::Ping,
|
||||
@@ -560,17 +565,13 @@ pub struct ActiveEventLoop {
|
||||
/// Dispatcher of Wayland events.
|
||||
pub wayland_dispatcher: WaylandDispatcher,
|
||||
|
||||
/// Connection to the wayland server.
|
||||
pub connection: Connection,
|
||||
/// Handle for the underlying event loop.
|
||||
pub handle: Arc<OwnedDisplayHandle>,
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
|
||||
crate::event_loop::EventLoopProxy {
|
||||
event_loop_proxy: crate::platform_impl::EventLoopProxy::Wayland(
|
||||
self.event_loop_proxy.clone(),
|
||||
),
|
||||
}
|
||||
fn create_proxy(&self) -> CoreEventLoopProxy {
|
||||
self.event_loop_proxy.clone()
|
||||
}
|
||||
|
||||
fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
@@ -631,13 +632,10 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
None
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> crate::event_loop::OwnedDisplayHandle {
|
||||
crate::event_loop::OwnedDisplayHandle {
|
||||
platform: crate::platform_impl::OwnedDisplayHandle::Wayland(self.connection.clone()),
|
||||
}
|
||||
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
|
||||
CoreOwnedDisplayHandle::new(self.handle.clone())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
@@ -657,8 +655,23 @@ impl ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
self.handle.display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OwnedDisplayHandle {
|
||||
pub(crate) connection: Connection,
|
||||
}
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
fn new(connection: Connection) -> Self {
|
||||
Self { connection }
|
||||
}
|
||||
}
|
||||
|
||||
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
//! An event loop proxy.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use sctk::reexports::calloop::ping::Ping;
|
||||
|
||||
use crate::event_loop::{EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider};
|
||||
|
||||
/// A handle that can be sent across the threads and used to wake up the `EventLoop`.
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
ping: Ping,
|
||||
}
|
||||
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
self.ping.ping();
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn new(ping: Ping) -> Self {
|
||||
Self { ping }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wake_up(&self) {
|
||||
self.ping.ping();
|
||||
impl From<EventLoopProxy> for CoreEventLoopProxy {
|
||||
fn from(value: EventLoopProxy) -> Self {
|
||||
CoreEventLoopProxy::new(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Winit's Wayland backend.
|
||||
|
||||
pub use event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
|
||||
pub use event_loop::{ActiveEventLoop, EventLoop};
|
||||
pub use output::{MonitorHandle, VideoModeHandle};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::Proxy;
|
||||
@@ -17,16 +17,6 @@ mod state;
|
||||
mod types;
|
||||
mod window;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId(i32);
|
||||
|
||||
impl FingerId {
|
||||
#[cfg(test)]
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the WindowId out of the surface.
|
||||
#[inline]
|
||||
fn make_wid(surface: &WlSurface) -> WindowId {
|
||||
|
||||
@@ -40,6 +40,9 @@ pub struct WinitSeatState {
|
||||
/// The mapping from touched points to the surfaces they're present.
|
||||
touch_map: AHashMap<i32, TouchPoint>,
|
||||
|
||||
/// Id of the first touch event.
|
||||
first_touch_id: Option<i32>,
|
||||
|
||||
/// The text input bound on the seat.
|
||||
text_input: Option<Arc<ZwpTextInputV3>>,
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ impl PointerHandler for WinitState {
|
||||
PointerEventKind::Enter { .. } => {
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerEntered {
|
||||
primary: true,
|
||||
device_id: None,
|
||||
position,
|
||||
kind: PointerKind::Mouse,
|
||||
@@ -144,6 +145,7 @@ impl PointerHandler for WinitState {
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerLeft {
|
||||
primary: true,
|
||||
device_id: None,
|
||||
position: Some(position),
|
||||
kind: PointerKind::Mouse,
|
||||
@@ -154,6 +156,7 @@ impl PointerHandler for WinitState {
|
||||
PointerEventKind::Motion { .. } => {
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerMoved {
|
||||
primary: true,
|
||||
device_id: None,
|
||||
position,
|
||||
source: PointerSource::Mouse,
|
||||
@@ -174,6 +177,7 @@ impl PointerHandler for WinitState {
|
||||
};
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerButton {
|
||||
primary: true,
|
||||
device_id: None,
|
||||
state,
|
||||
position,
|
||||
|
||||
@@ -8,9 +8,9 @@ use sctk::seat::touch::{TouchData, TouchHandler};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::dpi::LogicalPosition;
|
||||
use crate::event::{ButtonSource, ElementState, PointerKind, PointerSource, WindowEvent};
|
||||
use crate::event::{ButtonSource, ElementState, FingerId, PointerKind, PointerSource, WindowEvent};
|
||||
use crate::platform_impl::wayland;
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
use crate::platform_impl::wayland::{self, FingerId};
|
||||
|
||||
impl TouchHandler for WinitState {
|
||||
fn down(
|
||||
@@ -40,15 +40,20 @@ impl TouchHandler for WinitState {
|
||||
|
||||
// Update the state of the point.
|
||||
let location = LogicalPosition::<f64>::from(position);
|
||||
// Only update primary finger once we don't have any touch.
|
||||
if seat_state.touch_map.is_empty() {
|
||||
seat_state.first_touch_id = Some(id);
|
||||
}
|
||||
let primary = seat_state.first_touch_id == Some(id);
|
||||
seat_state.touch_map.insert(id, TouchPoint { surface, location });
|
||||
|
||||
let position = location.to_physical(scale_factor);
|
||||
let finger_id =
|
||||
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
|
||||
let finger_id = FingerId::from_raw(id as usize);
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerEntered {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
kind: PointerKind::Touch(finger_id),
|
||||
},
|
||||
@@ -57,6 +62,7 @@ impl TouchHandler for WinitState {
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: ElementState::Pressed,
|
||||
position,
|
||||
button: ButtonSource::Touch { finger_id, force: None },
|
||||
@@ -88,6 +94,15 @@ impl TouchHandler for WinitState {
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Update the primary touch point.
|
||||
let primary = seat_state.first_touch_id == Some(id);
|
||||
// Reset primary finger once all the other fingers are lifted to not transfer primary
|
||||
// finger to some other finger and still accept it when it's briefly moved between the
|
||||
// windows.
|
||||
if seat_state.touch_map.is_empty() {
|
||||
seat_state.first_touch_id = None;
|
||||
}
|
||||
|
||||
let window_id = wayland::make_wid(&touch_point.surface);
|
||||
let scale_factor = match self.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().scale_factor(),
|
||||
@@ -95,12 +110,12 @@ impl TouchHandler for WinitState {
|
||||
};
|
||||
|
||||
let position = touch_point.location.to_physical(scale_factor);
|
||||
let finger_id =
|
||||
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
|
||||
let finger_id = FingerId::from_raw(id as usize);
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: ElementState::Released,
|
||||
position,
|
||||
button: ButtonSource::Touch { finger_id, force: None },
|
||||
@@ -110,6 +125,7 @@ impl TouchHandler for WinitState {
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary,
|
||||
position: Some(position),
|
||||
kind: PointerKind::Touch(finger_id),
|
||||
},
|
||||
@@ -140,6 +156,8 @@ impl TouchHandler for WinitState {
|
||||
None => return,
|
||||
};
|
||||
|
||||
let primary = seat_state.first_touch_id == Some(id);
|
||||
|
||||
let window_id = wayland::make_wid(&touch_point.surface);
|
||||
let scale_factor = match self.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().scale_factor(),
|
||||
@@ -151,11 +169,10 @@ impl TouchHandler for WinitState {
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerMoved {
|
||||
device_id: None,
|
||||
primary,
|
||||
position: touch_point.location.to_physical(scale_factor),
|
||||
source: PointerSource::Touch {
|
||||
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
|
||||
FingerId(id),
|
||||
)),
|
||||
finger_id: FingerId::from_raw(id as usize),
|
||||
force: None,
|
||||
},
|
||||
},
|
||||
@@ -179,19 +196,21 @@ impl TouchHandler for WinitState {
|
||||
None => return,
|
||||
};
|
||||
|
||||
let primary = seat_state.first_touch_id == Some(id);
|
||||
let position = touch_point.location.to_physical(scale_factor);
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary,
|
||||
position: Some(position),
|
||||
kind: PointerKind::Touch(crate::event::FingerId(
|
||||
crate::platform_impl::FingerId::Wayland(FingerId(id)),
|
||||
)),
|
||||
kind: PointerKind::Touch(FingerId::from_raw(id as usize)),
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
|
||||
seat_state.first_touch_id = None;
|
||||
}
|
||||
|
||||
fn shape(
|
||||
|
||||
@@ -17,7 +17,7 @@ use super::output::MonitorHandle;
|
||||
use super::state::WinitState;
|
||||
use super::types::xdg_activation::XdgActivationTokenData;
|
||||
use super::ActiveEventLoop;
|
||||
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::dpi::{LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{Ime, WindowEvent};
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
@@ -87,7 +87,7 @@ impl Window {
|
||||
let compositor = state.compositor_state.clone();
|
||||
let xdg_activation =
|
||||
state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone());
|
||||
let display = event_loop_window_target.connection.display();
|
||||
let display = event_loop_window_target.handle.connection.display();
|
||||
|
||||
let size: Size = attributes.surface_size.unwrap_or(LogicalSize::new(800., 600.).into());
|
||||
|
||||
@@ -103,7 +103,7 @@ impl Window {
|
||||
state.xdg_shell.create_window(surface.clone(), default_decorations, &queue_handle);
|
||||
|
||||
let mut window_state = WindowState::new(
|
||||
event_loop_window_target.connection.clone(),
|
||||
event_loop_window_target.handle.clone(),
|
||||
&event_loop_window_target.queue_handle,
|
||||
&state,
|
||||
size,
|
||||
@@ -248,7 +248,6 @@ impl Drop for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::WaylandWindowHandle::new({
|
||||
@@ -260,7 +259,6 @@ impl rwh_06::HasWindowHandle for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::WaylandDisplayHandle::new({
|
||||
@@ -305,9 +303,8 @@ impl CoreWindow for Window {
|
||||
crate::platform_impl::common::xkb::reset_dead_keys()
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Err(NotSupportedError::new("window position information is not available on Wayland")
|
||||
.into())
|
||||
fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
(0, 0).into()
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
@@ -338,6 +335,10 @@ impl CoreWindow for Window {
|
||||
super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
PhysicalInsets::new(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let min_size = min_size.map(|size| size.to_logical(scale_factor));
|
||||
@@ -648,13 +649,11 @@ impl CoreWindow for Window {
|
||||
}
|
||||
|
||||
/// Get the raw-window-handle v0.6 display handle.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the raw-window-handle v0.6 window handle.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use sctk::reexports::client::backend::ObjectId;
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_shm::WlShm;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
use sctk::reexports::client::{Proxy, QueueHandle};
|
||||
use sctk::reexports::csd_frame::{
|
||||
DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowState as XdgWindowState,
|
||||
};
|
||||
@@ -31,6 +31,7 @@ use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
||||
use crate::cursor::CustomCursor as RootCustomCursor;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::platform_impl::wayland::event_loop::OwnedDisplayHandle;
|
||||
use crate::platform_impl::wayland::logical_to_physical_rounded;
|
||||
use crate::platform_impl::wayland::seat::{
|
||||
PointerConstraintsState, WinitPointerData, WinitPointerDataExt, ZwpTextInputV3Ext,
|
||||
@@ -52,7 +53,7 @@ const MIN_WINDOW_SIZE: LogicalSize<u32> = LogicalSize::new(2, 1);
|
||||
/// The state of the window which is being updated from the [`WinitState`].
|
||||
pub struct WindowState {
|
||||
/// The connection to Wayland server.
|
||||
pub connection: Connection,
|
||||
pub handle: Arc<OwnedDisplayHandle>,
|
||||
|
||||
/// The `Shm` to set cursor.
|
||||
pub shm: WlShm,
|
||||
@@ -161,7 +162,7 @@ pub struct WindowState {
|
||||
impl WindowState {
|
||||
/// Create new window state.
|
||||
pub fn new(
|
||||
connection: Connection,
|
||||
handle: Arc<OwnedDisplayHandle>,
|
||||
queue_handle: &QueueHandle<WinitState>,
|
||||
winit_state: &WinitState,
|
||||
initial_size: Size,
|
||||
@@ -183,7 +184,7 @@ impl WindowState {
|
||||
blur: None,
|
||||
blur_manager: winit_state.kwin_blur_manager.clone(),
|
||||
compositor,
|
||||
connection,
|
||||
handle,
|
||||
csd_fails: false,
|
||||
cursor_grab_mode: GrabState::new(),
|
||||
selected_cursor: Default::default(),
|
||||
@@ -693,7 +694,7 @@ impl WindowState {
|
||||
}
|
||||
|
||||
self.apply_on_pointer(|pointer, _| {
|
||||
if pointer.set_cursor(&self.connection, cursor_icon).is_err() {
|
||||
if pointer.set_cursor(&self.handle.connection, cursor_icon).is_err() {
|
||||
warn!("Failed to set cursor to {:?}", cursor_icon);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -165,14 +165,14 @@ fn push_display(buffer: &mut Vec<u8>, display: &impl std::fmt::Display) {
|
||||
buffer: &'a mut Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Write for Writer<'a> {
|
||||
impl std::fmt::Write for Writer<'_> {
|
||||
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||
self.buffer.extend_from_slice(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
write!(Writer { buffer }, "{}", display).unwrap();
|
||||
write!(Writer { buffer }, "{display}").unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -22,8 +22,9 @@ use xkbcommon_dl::xkb_mod_mask_t;
|
||||
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::event::{
|
||||
ButtonSource, DeviceEvent, DeviceId, ElementState, Event, Ime, MouseButton, MouseScrollDelta,
|
||||
PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent,
|
||||
ButtonSource, DeviceEvent, DeviceId, ElementState, Event, FingerId, Ime, MouseButton,
|
||||
MouseScrollDelta, PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, TouchPhase,
|
||||
WindowEvent,
|
||||
};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::platform_impl::common::xkb::{self, XkbState};
|
||||
@@ -33,7 +34,7 @@ use crate::platform_impl::platform::x11::ActiveEventLoop;
|
||||
use crate::platform_impl::x11::atoms::*;
|
||||
use crate::platform_impl::x11::util::cookie::GenericEventCookie;
|
||||
use crate::platform_impl::x11::{
|
||||
mkdid, mkfid, mkwid, util, CookieResultExt, Device, DeviceInfo, Dnd, DndState, ImeReceiver,
|
||||
mkdid, mkwid, util, CookieResultExt, Device, DeviceInfo, Dnd, DndState, ImeReceiver,
|
||||
ScrollOrientation, UnownedWindow, WindowId,
|
||||
};
|
||||
|
||||
@@ -1034,12 +1035,14 @@ impl EventProcessor {
|
||||
let event = match event.detail as u32 {
|
||||
xlib::Button1 => WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary: true,
|
||||
state,
|
||||
position,
|
||||
button: MouseButton::Left.into(),
|
||||
},
|
||||
xlib::Button2 => WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary: true,
|
||||
state,
|
||||
position,
|
||||
button: MouseButton::Middle.into(),
|
||||
@@ -1047,6 +1050,7 @@ impl EventProcessor {
|
||||
|
||||
xlib::Button3 => WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary: true,
|
||||
state,
|
||||
position,
|
||||
button: MouseButton::Right.into(),
|
||||
@@ -1069,6 +1073,7 @@ impl EventProcessor {
|
||||
},
|
||||
8 => WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary: true,
|
||||
state,
|
||||
position,
|
||||
button: MouseButton::Back.into(),
|
||||
@@ -1076,12 +1081,14 @@ impl EventProcessor {
|
||||
|
||||
9 => WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary: true,
|
||||
state,
|
||||
position,
|
||||
button: MouseButton::Forward.into(),
|
||||
},
|
||||
x => WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary: true,
|
||||
state,
|
||||
position,
|
||||
button: MouseButton::Other(x as u16).into(),
|
||||
@@ -1116,6 +1123,7 @@ impl EventProcessor {
|
||||
window_id,
|
||||
event: WindowEvent::PointerMoved {
|
||||
device_id,
|
||||
primary: true,
|
||||
position,
|
||||
source: PointerSource::Mouse,
|
||||
},
|
||||
@@ -1205,6 +1213,7 @@ impl EventProcessor {
|
||||
window_id,
|
||||
event: WindowEvent::PointerEntered {
|
||||
device_id,
|
||||
primary: true,
|
||||
position,
|
||||
kind: PointerKind::Mouse,
|
||||
},
|
||||
@@ -1229,6 +1238,7 @@ impl EventProcessor {
|
||||
window_id: mkwid(window),
|
||||
event: WindowEvent::PointerLeft {
|
||||
device_id: Some(mkdid(event.deviceid as xinput::DeviceId)),
|
||||
primary: true,
|
||||
position: Some(PhysicalPosition::new(event.event_x, event.event_y)),
|
||||
kind: PointerKind::Mouse,
|
||||
},
|
||||
@@ -1289,7 +1299,12 @@ impl EventProcessor {
|
||||
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::PointerMoved { device_id, position, source: PointerSource::Mouse },
|
||||
event: WindowEvent::PointerMoved {
|
||||
device_id,
|
||||
primary: true,
|
||||
position,
|
||||
source: PointerSource::Mouse,
|
||||
},
|
||||
};
|
||||
callback(&self.target, event);
|
||||
}
|
||||
@@ -1360,11 +1375,14 @@ impl EventProcessor {
|
||||
|
||||
// Mouse cursor position changes when touch events are received.
|
||||
// Only the first concurrently active touch ID moves the mouse cursor.
|
||||
if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase) {
|
||||
let is_first_touch =
|
||||
is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase);
|
||||
if is_first_touch {
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::PointerMoved {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: position.cast(),
|
||||
source: PointerSource::Mouse,
|
||||
},
|
||||
@@ -1373,7 +1391,7 @@ impl EventProcessor {
|
||||
}
|
||||
|
||||
let device_id = Some(mkdid(xev.deviceid as xinput::DeviceId));
|
||||
let finger_id = mkfid(id);
|
||||
let finger_id = FingerId::from_raw(id as usize);
|
||||
|
||||
match phase {
|
||||
xinput2::XI_TouchBegin => {
|
||||
@@ -1381,6 +1399,7 @@ impl EventProcessor {
|
||||
window_id,
|
||||
event: WindowEvent::PointerEntered {
|
||||
device_id,
|
||||
primary: is_first_touch,
|
||||
position,
|
||||
kind: PointerKind::Touch(finger_id),
|
||||
},
|
||||
@@ -1390,6 +1409,7 @@ impl EventProcessor {
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary: is_first_touch,
|
||||
state: ElementState::Pressed,
|
||||
position,
|
||||
button: ButtonSource::Touch { finger_id, force: None },
|
||||
@@ -1402,6 +1422,7 @@ impl EventProcessor {
|
||||
window_id,
|
||||
event: WindowEvent::PointerMoved {
|
||||
device_id,
|
||||
primary: is_first_touch,
|
||||
position,
|
||||
source: PointerSource::Touch { finger_id, force: None },
|
||||
},
|
||||
@@ -1413,6 +1434,7 @@ impl EventProcessor {
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary: is_first_touch,
|
||||
state: ElementState::Released,
|
||||
position,
|
||||
button: ButtonSource::Touch { finger_id, force: None },
|
||||
@@ -1423,6 +1445,7 @@ impl EventProcessor {
|
||||
window_id,
|
||||
event: WindowEvent::PointerLeft {
|
||||
device_id,
|
||||
primary: is_first_touch,
|
||||
position: Some(position),
|
||||
kind: PointerKind::Touch(finger_id),
|
||||
},
|
||||
|
||||
@@ -27,13 +27,14 @@ use crate::error::{EventLoopError, RequestError};
|
||||
use crate::event::{DeviceId, Event, StartCause, WindowEvent};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::common::xkb::Context;
|
||||
use crate::platform_impl::platform::min_timeout;
|
||||
use crate::platform_impl::x11::window::Window;
|
||||
use crate::platform_impl::{OwnedDisplayHandle, PlatformCustomCursor};
|
||||
use crate::platform_impl::PlatformCustomCursor;
|
||||
use crate::window::{
|
||||
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
|
||||
WindowAttributes, WindowId,
|
||||
@@ -139,7 +140,7 @@ pub struct ActiveEventLoop {
|
||||
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
|
||||
redraw_sender: WakeSender<WindowId>,
|
||||
activation_sender: WakeSender<ActivationToken>,
|
||||
event_loop_proxy: EventLoopProxy,
|
||||
event_loop_proxy: CoreEventLoopProxy,
|
||||
device_events: Cell<DeviceEvents>,
|
||||
}
|
||||
|
||||
@@ -306,7 +307,7 @@ impl EventLoop {
|
||||
sender: activation_token_sender, // not used again so no clone
|
||||
waker: waker.clone(),
|
||||
},
|
||||
event_loop_proxy,
|
||||
event_loop_proxy: event_loop_proxy.into(),
|
||||
device_events: Default::default(),
|
||||
};
|
||||
|
||||
@@ -647,21 +648,6 @@ impl ActiveEventLoop {
|
||||
.expect_then_ignore_error("Failed to update device event filter");
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
let display_handle = rwh_06::XlibDisplayHandle::new(
|
||||
// SAFETY: display will never be null
|
||||
Some(
|
||||
std::ptr::NonNull::new(self.xconn.display as *mut _)
|
||||
.expect("X11 display should never be null"),
|
||||
),
|
||||
self.xconn.default_screen_index() as c_int,
|
||||
);
|
||||
Ok(display_handle.into())
|
||||
}
|
||||
|
||||
pub(crate) fn clear_exit(&self) {
|
||||
self.exit.set(None)
|
||||
}
|
||||
@@ -676,12 +662,8 @@ impl ActiveEventLoop {
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> crate::event_loop::EventLoopProxy {
|
||||
crate::event_loop::EventLoopProxy {
|
||||
event_loop_proxy: crate::platform_impl::EventLoopProxy::X(
|
||||
self.event_loop_proxy.clone(),
|
||||
),
|
||||
}
|
||||
fn create_proxy(&self) -> CoreEventLoopProxy {
|
||||
self.event_loop_proxy.clone()
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
@@ -743,28 +725,18 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
self.exit.get().is_some()
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
|
||||
let handle = OwnedDisplayHandle::X(self.x_connection().clone());
|
||||
RootOwnedDisplayHandle { platform: handle }
|
||||
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
|
||||
CoreOwnedDisplayHandle::new(self.x_connection().clone())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
self.ping.ping();
|
||||
self.xconn.display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,14 +762,14 @@ impl<'a> DeviceInfo<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for DeviceInfo<'a> {
|
||||
impl Drop for DeviceInfo<'_> {
|
||||
fn drop(&mut self) {
|
||||
assert!(!self.info.is_null());
|
||||
unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for DeviceInfo<'a> {
|
||||
impl Deref for DeviceInfo<'_> {
|
||||
type Target = [ffi::XIDeviceInfo];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -805,28 +777,29 @@ impl<'a> Deref for DeviceInfo<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId(u32);
|
||||
|
||||
impl FingerId {
|
||||
#[cfg(test)]
|
||||
#[allow(unused)]
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
ping: Ping,
|
||||
}
|
||||
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
self.ping.ping();
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
fn new(ping: Ping) -> Self {
|
||||
Self { ping }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EventLoopProxy> for CoreEventLoopProxy {
|
||||
fn from(value: EventLoopProxy) -> Self {
|
||||
CoreEventLoopProxy::new(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic sum error type for X11 errors.
|
||||
#[derive(Debug)]
|
||||
pub enum X11Error {
|
||||
@@ -870,24 +843,24 @@ pub enum X11Error {
|
||||
impl fmt::Display for X11Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
X11Error::Xlib(e) => write!(f, "Xlib error: {}", e),
|
||||
X11Error::Connect(e) => write!(f, "X11 connection error: {}", e),
|
||||
X11Error::Connection(e) => write!(f, "X11 connection error: {}", e),
|
||||
X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {}", e),
|
||||
X11Error::GetProperty(e) => write!(f, "Failed to get X property {}", e),
|
||||
X11Error::X11(e) => write!(f, "X11 error: {:?}", e),
|
||||
X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {}", s),
|
||||
X11Error::Xlib(e) => write!(f, "Xlib error: {e}"),
|
||||
X11Error::Connect(e) => write!(f, "X11 connection error: {e}"),
|
||||
X11Error::Connection(e) => write!(f, "X11 connection error: {e}"),
|
||||
X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {e}"),
|
||||
X11Error::GetProperty(e) => write!(f, "Failed to get X property {e}"),
|
||||
X11Error::X11(e) => write!(f, "X11 error: {e:?}"),
|
||||
X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {s}"),
|
||||
X11Error::InvalidActivationToken(s) => write!(
|
||||
f,
|
||||
"Invalid activation token: {}",
|
||||
std::str::from_utf8(s).unwrap_or("<invalid utf8>")
|
||||
),
|
||||
X11Error::MissingExtension(s) => write!(f, "Missing X11 extension: {}", s),
|
||||
X11Error::MissingExtension(s) => write!(f, "Missing X11 extension: {s}"),
|
||||
X11Error::NoSuchVisual(visualid) => {
|
||||
write!(f, "Could not find a matching X11 visual for ID `{:x}`", visualid)
|
||||
write!(f, "Could not find a matching X11 visual for ID `{visualid:x}`")
|
||||
},
|
||||
X11Error::XsettingsParse(err) => {
|
||||
write!(f, "Failed to parse xsettings: {:?}", err)
|
||||
write!(f, "Failed to parse xsettings: {err:?}")
|
||||
},
|
||||
X11Error::NoArgb32Format => {
|
||||
f.write_str("winit only supports X11 displays with ARGB32 picture formats")
|
||||
@@ -981,7 +954,7 @@ trait CookieResultExt {
|
||||
fn expect_then_ignore_error(self, msg: &str);
|
||||
}
|
||||
|
||||
impl<'a, E: fmt::Debug> CookieResultExt for Result<VoidCookie<'a>, E> {
|
||||
impl<E: fmt::Debug> CookieResultExt for Result<VoidCookie<'_>, E> {
|
||||
fn expect_then_ignore_error(self, msg: &str) {
|
||||
self.expect(msg).ignore_error()
|
||||
}
|
||||
@@ -994,10 +967,6 @@ fn mkdid(w: xinput::DeviceId) -> DeviceId {
|
||||
DeviceId::from_raw(w as i64)
|
||||
}
|
||||
|
||||
fn mkfid(w: u32) -> crate::event::FingerId {
|
||||
crate::event::FingerId(crate::platform_impl::FingerId::X(FingerId(w)))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
_name: String,
|
||||
|
||||
@@ -280,7 +280,7 @@ impl XConnection {
|
||||
let info = self
|
||||
.xcb_connection()
|
||||
.extension_information(randr::X11_EXTENSION_NAME)?
|
||||
.ok_or_else(|| X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
|
||||
.ok_or(X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
|
||||
|
||||
// Select input data.
|
||||
let event_mask =
|
||||
|
||||
@@ -67,15 +67,20 @@ pub struct FrameExtentsHeuristic {
|
||||
}
|
||||
|
||||
impl FrameExtentsHeuristic {
|
||||
pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) {
|
||||
pub fn surface_position(&self) -> (i32, i32) {
|
||||
use self::FrameExtentsHeuristicPath::*;
|
||||
if self.heuristic_path != UnsupportedBordered {
|
||||
(x - self.frame_extents.left as i32, y - self.frame_extents.top as i32)
|
||||
(self.frame_extents.left as i32, self.frame_extents.top as i32)
|
||||
} else {
|
||||
(x, y)
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) {
|
||||
let (left, top) = self.surface_position();
|
||||
(x - left, y - top)
|
||||
}
|
||||
|
||||
pub fn surface_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
|
||||
(
|
||||
width.saturating_add(
|
||||
|
||||
@@ -19,7 +19,7 @@ impl<'a, T> XSmartPointer<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
impl<T> Deref for XSmartPointer<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
@@ -27,13 +27,13 @@ impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
||||
impl<T> DerefMut for XSmartPointer<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||
impl<T> Drop for XSmartPointer<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
||||
|
||||
@@ -21,7 +21,7 @@ use super::{
|
||||
ffi, ActiveEventLoop, CookieResultExt, ImeRequest, ImeSender, VoidCookie, XConnection,
|
||||
};
|
||||
use crate::cursor::{Cursor, CustomCursor as RootCustomCursor};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{Event, SurfaceSizeWriter, WindowEvent};
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
@@ -82,8 +82,8 @@ impl CoreWindow for Window {
|
||||
common::xkb::reset_dead_keys();
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
self.0.inner_position()
|
||||
fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
self.0.surface_position()
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
@@ -106,6 +106,10 @@ impl CoreWindow for Window {
|
||||
self.0.outer_size()
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
self.0.safe_area()
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.0.set_min_surface_size(min_size)
|
||||
}
|
||||
@@ -294,18 +298,15 @@ impl CoreWindow for Window {
|
||||
.map(|inner| crate::monitor::MonitorHandle { inner })
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.0.raw_display_handle_rwh_06()?;
|
||||
@@ -313,7 +314,6 @@ impl rwh_06::HasDisplayHandle for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.0.raw_window_handle_rwh_06()?;
|
||||
@@ -443,15 +443,26 @@ impl UnownedWindow {
|
||||
) -> Result<UnownedWindow, RequestError> {
|
||||
let xconn = &event_loop.xconn;
|
||||
let atoms = xconn.atoms();
|
||||
#[cfg(feature = "rwh_06")]
|
||||
|
||||
let screen_id = match window_attrs.platform_specific.x11.screen_id {
|
||||
Some(id) => id,
|
||||
None => xconn.default_screen_index() as c_int,
|
||||
};
|
||||
|
||||
let screen = {
|
||||
let screen_id_usize = usize::try_from(screen_id)
|
||||
.map_err(|_| NotSupportedError::new("screen id must be non-negative"))?;
|
||||
xconn.xcb_connection().setup().roots.get(screen_id_usize).ok_or(
|
||||
NotSupportedError::new("requested screen id not present in server's response"),
|
||||
)?
|
||||
};
|
||||
|
||||
let root = match window_attrs.parent_window.as_ref().map(|handle| handle.0) {
|
||||
Some(rwh_06::RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window,
|
||||
Some(rwh_06::RawWindowHandle::Xcb(handle)) => handle.window.get(),
|
||||
Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"),
|
||||
None => event_loop.root,
|
||||
None => screen.root,
|
||||
};
|
||||
#[cfg(not(feature = "rwh_06"))]
|
||||
let root = event_loop.root;
|
||||
|
||||
let mut monitors = leap!(xconn.available_monitors());
|
||||
let guessed_monitor = if monitors.is_empty() {
|
||||
@@ -506,18 +517,10 @@ impl UnownedWindow {
|
||||
dimensions
|
||||
};
|
||||
|
||||
let screen_id = match window_attrs.platform_specific.x11.screen_id {
|
||||
Some(id) => id,
|
||||
None => xconn.default_screen_index() as c_int,
|
||||
};
|
||||
|
||||
// An iterator over all of the visuals combined with their depths.
|
||||
let mut all_visuals = xconn
|
||||
.xcb_connection()
|
||||
.setup()
|
||||
.roots
|
||||
// An iterator over the visuals matching screen id combined with their depths.
|
||||
let mut all_visuals = screen
|
||||
.allowed_depths
|
||||
.iter()
|
||||
.flat_map(|root| &root.allowed_depths)
|
||||
.flat_map(|depth| depth.visuals.iter().map(move |visual| (visual, depth.depth)));
|
||||
|
||||
// creating
|
||||
@@ -806,6 +809,20 @@ impl UnownedWindow {
|
||||
leap!(result).ignore_error();
|
||||
}
|
||||
|
||||
// Select XInput2 events
|
||||
let mask = xinput::XIEventMask::MOTION
|
||||
| xinput::XIEventMask::BUTTON_PRESS
|
||||
| xinput::XIEventMask::BUTTON_RELEASE
|
||||
| xinput::XIEventMask::ENTER
|
||||
| xinput::XIEventMask::LEAVE
|
||||
| xinput::XIEventMask::FOCUS_IN
|
||||
| xinput::XIEventMask::FOCUS_OUT
|
||||
| xinput::XIEventMask::TOUCH_BEGIN
|
||||
| xinput::XIEventMask::TOUCH_UPDATE
|
||||
| xinput::XIEventMask::TOUCH_END;
|
||||
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
|
||||
.ignore_error();
|
||||
|
||||
// Set visibility (map window)
|
||||
if window_attrs.visible {
|
||||
leap!(xconn.xcb_connection().map_window(window.xwindow)).ignore_error();
|
||||
@@ -829,20 +846,6 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// Select XInput2 events
|
||||
let mask = xinput::XIEventMask::MOTION
|
||||
| xinput::XIEventMask::BUTTON_PRESS
|
||||
| xinput::XIEventMask::BUTTON_RELEASE
|
||||
| xinput::XIEventMask::ENTER
|
||||
| xinput::XIEventMask::LEAVE
|
||||
| xinput::XIEventMask::FOCUS_IN
|
||||
| xinput::XIEventMask::FOCUS_OUT
|
||||
| xinput::XIEventMask::TOUCH_BEGIN
|
||||
| xinput::XIEventMask::TOUCH_UPDATE
|
||||
| xinput::XIEventMask::TOUCH_END;
|
||||
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
|
||||
.ignore_error();
|
||||
|
||||
// Try to create input context for the window.
|
||||
if let Some(ime) = event_loop.ime.as_ref() {
|
||||
ime.borrow_mut()
|
||||
@@ -1509,7 +1512,7 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn inner_position_physical(&self) -> (i32, i32) {
|
||||
fn inner_position_physical(&self) -> (i32, i32) {
|
||||
// This should be okay to unwrap since the only error XTranslateCoordinates can return
|
||||
// is BadWindow, and if the window handle is bad we have bigger problems.
|
||||
self.xconn
|
||||
@@ -1519,8 +1522,14 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
Ok(self.inner_position_physical().into())
|
||||
pub fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
let extents = self.shared_state_lock().frame_extents.clone();
|
||||
if let Some(extents) = extents {
|
||||
extents.surface_position().into()
|
||||
} else {
|
||||
self.update_cached_frame_extents();
|
||||
self.surface_position()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_position_inner(
|
||||
@@ -1583,6 +1592,10 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
PhysicalInsets::new(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn request_surface_size_physical(&self, width: u32, height: u32) {
|
||||
self.xconn
|
||||
.xcb_connection()
|
||||
@@ -1990,7 +2003,7 @@ impl UnownedWindow {
|
||||
.query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER)
|
||||
.map_err(|err| os_error!(err))?;
|
||||
|
||||
let window_position = self.inner_position()?;
|
||||
let window_position = self.inner_position_physical();
|
||||
|
||||
let atoms = self.xconn.atoms();
|
||||
let message = atoms[_NET_WM_MOVERESIZE];
|
||||
@@ -2017,8 +2030,8 @@ impl UnownedWindow {
|
||||
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
|
||||
),
|
||||
[
|
||||
(window_position.x + xinput_fp1616_to_float(pointer.win_x) as i32) as u32,
|
||||
(window_position.y + xinput_fp1616_to_float(pointer.win_y) as i32) as u32,
|
||||
(window_position.0 + xinput_fp1616_to_float(pointer.win_x) as i32) as u32,
|
||||
(window_position.1 + xinput_fp1616_to_float(pointer.win_y) as i32) as u32,
|
||||
action.try_into().unwrap(),
|
||||
1, // Button 1
|
||||
1,
|
||||
@@ -2032,12 +2045,19 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_cursor_area(&self, spot: Position, _size: Size) {
|
||||
let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
|
||||
pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
|
||||
let PhysicalPosition { x, y } = spot.to_physical::<i16>(self.scale_factor());
|
||||
let PhysicalSize { width, height } = size.to_physical::<i16>(self.scale_factor());
|
||||
// We only currently support reporting a caret position via XIM.
|
||||
// No IM servers currently process preedit area information from XIM clients
|
||||
// and it is unclear this is even part of the standard protocol.
|
||||
// Fcitx and iBus both assume that the position reported is at the insertion
|
||||
// caret, and by default will place the candidate window under and to the
|
||||
// right of the reported point.
|
||||
let _ = self.ime_sender.lock().unwrap().send(ImeRequest::Position(
|
||||
self.xwindow as ffi::Window,
|
||||
x,
|
||||
y,
|
||||
x.saturating_add(width),
|
||||
y.saturating_add(height),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -2152,7 +2172,6 @@ impl UnownedWindow {
|
||||
// TODO timer
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
let mut window_handle = rwh_06::XlibWindowHandle::new(self.xlib_window());
|
||||
@@ -2160,7 +2179,6 @@ impl UnownedWindow {
|
||||
Ok(window_handle.into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::ffi::c_int;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard};
|
||||
use std::{fmt, ptr};
|
||||
|
||||
use rwh_06::HasDisplayHandle;
|
||||
use x11rb::connection::Connection;
|
||||
use x11rb::protocol::randr::ConnectionExt as _;
|
||||
use x11rb::protocol::render;
|
||||
@@ -62,6 +64,13 @@ pub struct XConnection {
|
||||
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, xproto::Cursor>>,
|
||||
}
|
||||
|
||||
impl HasDisplayHandle for XConnection {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for XConnection {}
|
||||
unsafe impl Sync for XConnection {}
|
||||
|
||||
@@ -155,7 +164,7 @@ impl XConnection {
|
||||
fn new_xsettings_screen(xcb: &XCBConnection, default_screen: usize) -> Option<xproto::Atom> {
|
||||
// Fetch the _XSETTINGS_S[screen number] atom.
|
||||
let xsettings_screen = xcb
|
||||
.intern_atom(false, format!("_XSETTINGS_S{}", default_screen).as_bytes())
|
||||
.intern_atom(false, format!("_XSETTINGS_S{default_screen}").as_bytes())
|
||||
.ok()?
|
||||
.reply()
|
||||
.ok()?
|
||||
@@ -284,6 +293,19 @@ impl XConnection {
|
||||
|
||||
self.xcb_connection().setup().image_byte_order != endian
|
||||
}
|
||||
|
||||
pub fn raw_display_handle(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
let display_handle = rwh_06::XlibDisplayHandle::new(
|
||||
// SAFETY: display will never be null
|
||||
Some(
|
||||
std::ptr::NonNull::new(self.display as *mut _)
|
||||
.expect("X11 display should never be null"),
|
||||
),
|
||||
self.default_screen_index() as c_int,
|
||||
);
|
||||
|
||||
Ok(display_handle.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for XConnection {
|
||||
|
||||
@@ -22,6 +22,7 @@ use self::apple as platform;
|
||||
use self::linux as platform;
|
||||
#[cfg(orbital_platform)]
|
||||
use self::orbital as platform;
|
||||
#[allow(unused_imports)]
|
||||
pub use self::platform::*;
|
||||
#[cfg(web_platform)]
|
||||
use self::web as platform;
|
||||
|
||||
@@ -18,7 +18,11 @@ use super::{
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use crate::event::{self, Ime, Modifiers, StartCause};
|
||||
use crate::event_loop::{self, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use crate::keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
|
||||
PhysicalKey,
|
||||
@@ -286,8 +290,7 @@ impl EventLoop {
|
||||
let event_socket =
|
||||
Arc::new(RedoxSocket::event().map_err(|error| os_error!(format!("{error}")))?);
|
||||
|
||||
let wake_socket =
|
||||
Arc::new(TimeSocket::open().map_err(|error| os_error!(format!("{error}")))?);
|
||||
let wake_socket = TimeSocket::open().map_err(|error| os_error!(format!("{error}")))?;
|
||||
|
||||
event_socket
|
||||
.write(&syscall::Event {
|
||||
@@ -306,8 +309,7 @@ impl EventLoop {
|
||||
redraws: Arc::new(Mutex::new(VecDeque::new())),
|
||||
destroys: Arc::new(Mutex::new(VecDeque::new())),
|
||||
event_socket,
|
||||
wake_socket,
|
||||
user_events_sender,
|
||||
event_loop_proxy: Arc::new(EventLoopProxy { wake_socket, user_events_sender }),
|
||||
},
|
||||
user_events_receiver,
|
||||
})
|
||||
@@ -404,6 +406,7 @@ impl EventLoop {
|
||||
EventOption::Mouse(MouseEvent { x, y }) => {
|
||||
app.window_event(window_target, window_id, event::WindowEvent::PointerMoved {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: (x, y).into(),
|
||||
source: event::PointerSource::Mouse,
|
||||
});
|
||||
@@ -417,6 +420,7 @@ impl EventLoop {
|
||||
while let Some((button, state)) = event_state.mouse(left, middle, right) {
|
||||
app.window_event(window_target, window_id, event::WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state,
|
||||
position: dpi::PhysicalPosition::default(),
|
||||
button: button.into(),
|
||||
@@ -458,12 +462,14 @@ impl EventLoop {
|
||||
let event = if entered {
|
||||
event::WindowEvent::PointerEntered {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: dpi::PhysicalPosition::default(),
|
||||
kind: event::PointerKind::Mouse,
|
||||
}
|
||||
} else {
|
||||
event::WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: None,
|
||||
kind: event::PointerKind::Mouse,
|
||||
}
|
||||
@@ -660,11 +666,11 @@ impl EventLoop {
|
||||
|
||||
pub struct EventLoopProxy {
|
||||
user_events_sender: mpsc::SyncSender<()>,
|
||||
wake_socket: Arc<TimeSocket>,
|
||||
pub(super) wake_socket: TimeSocket,
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
// When we fail to send the event it means that we haven't woken up to read the previous
|
||||
// event.
|
||||
if self.user_events_sender.try_send(()).is_ok() {
|
||||
@@ -673,15 +679,6 @@ impl EventLoopProxy {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for EventLoopProxy {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
user_events_sender: self.user_events_sender.clone(),
|
||||
wake_socket: self.wake_socket.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpin for EventLoopProxy {}
|
||||
|
||||
pub struct ActiveEventLoop {
|
||||
@@ -691,18 +688,12 @@ pub struct ActiveEventLoop {
|
||||
pub(super) redraws: Arc<Mutex<VecDeque<WindowId>>>,
|
||||
pub(super) destroys: Arc<Mutex<VecDeque<WindowId>>>,
|
||||
pub(super) event_socket: Arc<RedoxSocket>,
|
||||
pub(super) wake_socket: Arc<TimeSocket>,
|
||||
user_events_sender: mpsc::SyncSender<()>,
|
||||
pub(super) event_loop_proxy: Arc<EventLoopProxy>,
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> event_loop::EventLoopProxy {
|
||||
event_loop::EventLoopProxy {
|
||||
event_loop_proxy: EventLoopProxy {
|
||||
user_events_sender: self.user_events_sender.clone(),
|
||||
wake_socket: self.wake_socket.clone(),
|
||||
},
|
||||
}
|
||||
fn create_proxy(&self) -> CoreEventLoopProxy {
|
||||
CoreEventLoopProxy::new(self.event_loop_proxy.clone())
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
@@ -751,17 +742,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
self.exit.get()
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> event_loop::OwnedDisplayHandle {
|
||||
event_loop::OwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
|
||||
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new());
|
||||
@@ -769,15 +758,12 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::OrbitalDisplayHandle::new().into())
|
||||
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new());
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{fmt, str};
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
||||
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle};
|
||||
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::keyboard::Key;
|
||||
mod event_loop;
|
||||
@@ -99,16 +99,6 @@ impl TimeSocket {
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct FingerId;
|
||||
|
||||
impl FingerId {
|
||||
#[cfg(test)]
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct PlatformSpecificWindowAttributes;
|
||||
|
||||
@@ -135,7 +125,7 @@ impl<'a> WindowProperties<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for WindowProperties<'a> {
|
||||
impl fmt::Display for WindowProperties<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, TimeSocket, WindowProperties};
|
||||
use super::event_loop::EventLoopProxy;
|
||||
use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, WindowProperties};
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
use crate::window::{self, Fullscreen, ImePurpose, Window as CoreWindow, WindowId};
|
||||
@@ -23,7 +24,7 @@ pub struct Window {
|
||||
window_socket: Arc<RedoxSocket>,
|
||||
redraws: Arc<Mutex<VecDeque<WindowId>>>,
|
||||
destroys: Arc<Mutex<VecDeque<WindowId>>>,
|
||||
wake_socket: Arc<TimeSocket>,
|
||||
event_loop_proxy: Arc<EventLoopProxy>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
@@ -113,13 +114,13 @@ impl Window {
|
||||
creates.push_back(window_socket.clone());
|
||||
}
|
||||
|
||||
el.wake_socket.wake().unwrap();
|
||||
el.event_loop_proxy.wake_socket.wake().unwrap();
|
||||
|
||||
Ok(Self {
|
||||
window_socket,
|
||||
redraws: el.redraws.clone(),
|
||||
destroys: el.destroys.clone(),
|
||||
wake_socket: el.wake_socket.clone(),
|
||||
event_loop_proxy: el.event_loop_proxy.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -137,7 +138,6 @@ impl Window {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
let handle = rwh_06::OrbitalWindowHandle::new({
|
||||
@@ -147,7 +147,6 @@ impl Window {
|
||||
Ok(rwh_06::RawWindowHandle::Orbital(handle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new()))
|
||||
@@ -186,7 +185,7 @@ impl CoreWindow for Window {
|
||||
if !redraws.contains(&window_id) {
|
||||
redraws.push_back(window_id);
|
||||
|
||||
self.wake_socket.wake().unwrap();
|
||||
self.event_loop_proxy.wake_socket.wake().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,17 +198,17 @@ impl CoreWindow for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
let path = self.window_socket.fpath(&mut buf).expect("failed to read properties");
|
||||
let properties = WindowProperties::new(path);
|
||||
Ok((properties.x, properties.y).into())
|
||||
fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
// TODO: adjust for window decorations
|
||||
(0, 0).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
// TODO: adjust for window decorations
|
||||
self.inner_position()
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
let path = self.window_socket.fpath(&mut buf).expect("failed to read properties");
|
||||
let properties = WindowProperties::new(path);
|
||||
Ok((properties.x, properties.y).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -240,6 +239,10 @@ impl CoreWindow for Window {
|
||||
self.surface_size()
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
PhysicalInsets::new(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_min_surface_size(&self, _: Option<Size>) {}
|
||||
|
||||
@@ -411,7 +414,7 @@ impl CoreWindow for Window {
|
||||
window::ResizeDirection::West => "L",
|
||||
};
|
||||
self.window_socket
|
||||
.write(format!("D,{}", arg).as_bytes())
|
||||
.write(format!("D,{arg}").as_bytes())
|
||||
.map_err(|err| os_error!(format!("{err}")))?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -447,18 +450,15 @@ impl CoreWindow for Window {
|
||||
|
||||
fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
@@ -466,7 +466,6 @@ impl rwh_06::HasWindowHandle for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
@@ -481,6 +480,6 @@ impl Drop for Window {
|
||||
destroys.push_back(self.id());
|
||||
}
|
||||
|
||||
self.wake_socket.wake().unwrap();
|
||||
self.event_loop_proxy.wake_socket.wake().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,13 @@ mod channel;
|
||||
mod concurrent_queue;
|
||||
mod dispatcher;
|
||||
mod notifier;
|
||||
mod waker;
|
||||
mod wrapper;
|
||||
|
||||
use atomic_waker::AtomicWaker;
|
||||
pub(crate) use atomic_waker::AtomicWaker;
|
||||
use concurrent_queue::{ConcurrentQueue, PushError};
|
||||
|
||||
pub use self::abortable::{AbortHandle, Abortable, DropAbortHandle};
|
||||
pub use self::channel::{channel, Receiver, Sender};
|
||||
pub use self::dispatcher::{DispatchRunner, Dispatcher};
|
||||
pub use self::notifier::{Notified, Notifier};
|
||||
pub use self::waker::{Waker, WakerSpawner};
|
||||
use self::wrapper::Wrapper;
|
||||
pub(crate) use self::wrapper::Wrapper;
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
use std::future;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll;
|
||||
|
||||
use super::super::main_thread::MainThreadMarker;
|
||||
use super::{AtomicWaker, Wrapper};
|
||||
|
||||
pub struct WakerSpawner<T: 'static>(Wrapper<Handler<T>, Sender, ()>);
|
||||
|
||||
pub struct Waker<T: 'static>(Wrapper<Handler<T>, Sender, ()>);
|
||||
|
||||
struct Handler<T> {
|
||||
value: T,
|
||||
handler: fn(&T, bool),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Sender(Arc<Inner>);
|
||||
|
||||
impl<T> WakerSpawner<T> {
|
||||
pub fn new(main_thread: MainThreadMarker, value: T, handler: fn(&T, bool)) -> Self {
|
||||
let inner = Arc::new(Inner {
|
||||
awoken: AtomicBool::new(false),
|
||||
waker: AtomicWaker::new(),
|
||||
closed: AtomicBool::new(false),
|
||||
});
|
||||
|
||||
let handler = Handler { value, handler };
|
||||
|
||||
let sender = Sender(Arc::clone(&inner));
|
||||
|
||||
Self(Wrapper::new(
|
||||
main_thread,
|
||||
handler,
|
||||
|handler, _| {
|
||||
let handler = handler.borrow();
|
||||
let handler = handler.as_ref().unwrap();
|
||||
(handler.handler)(&handler.value, true);
|
||||
},
|
||||
{
|
||||
let inner = Arc::clone(&inner);
|
||||
|
||||
move |handler| async move {
|
||||
while future::poll_fn(|cx| {
|
||||
if inner.awoken.swap(false, Ordering::Relaxed) {
|
||||
Poll::Ready(true)
|
||||
} else {
|
||||
inner.waker.register(cx.waker());
|
||||
|
||||
if inner.awoken.swap(false, Ordering::Relaxed) {
|
||||
Poll::Ready(true)
|
||||
} else {
|
||||
if inner.closed.load(Ordering::Relaxed) {
|
||||
return Poll::Ready(false);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
{
|
||||
let handler = handler.borrow();
|
||||
let handler = handler.as_ref().unwrap();
|
||||
(handler.handler)(&handler.value, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
sender,
|
||||
|inner, _| {
|
||||
inner.0.awoken.store(true, Ordering::Relaxed);
|
||||
inner.0.waker.wake();
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn waker(&self) -> Waker<T> {
|
||||
Waker(self.0.clone())
|
||||
}
|
||||
|
||||
pub fn take(&self) -> bool {
|
||||
debug_assert!(
|
||||
MainThreadMarker::new().is_some(),
|
||||
"this should only be called from the main thread"
|
||||
);
|
||||
|
||||
self.0.with_sender_data(|inner| inner.0.awoken.swap(false, Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for WakerSpawner<T> {
|
||||
fn drop(&mut self) {
|
||||
self.0.with_sender_data(|inner| {
|
||||
inner.0.closed.store(true, Ordering::Relaxed);
|
||||
inner.0.waker.wake();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Waker<T> {
|
||||
pub fn wake(&self) {
|
||||
self.0.send(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Waker<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
awoken: AtomicBool,
|
||||
waker: AtomicWaker,
|
||||
closed: AtomicBool,
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::event::{DeviceId, FingerId as RootFingerId};
|
||||
use crate::event::DeviceId;
|
||||
|
||||
pub(crate) fn mkdid(pointer_id: i32) -> Option<DeviceId> {
|
||||
if let Ok(pointer_id) = u32::try_from(pointer_id) {
|
||||
@@ -10,30 +10,3 @@ pub(crate) fn mkdid(pointer_id: i32) -> Option<DeviceId> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId {
|
||||
pointer_id: i32,
|
||||
primary: bool,
|
||||
}
|
||||
|
||||
impl FingerId {
|
||||
pub fn new(pointer_id: i32, primary: bool) -> Self {
|
||||
Self { pointer_id, primary }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub const fn dummy() -> Self {
|
||||
Self { pointer_id: -1, primary: false }
|
||||
}
|
||||
|
||||
pub fn is_primary(self) -> bool {
|
||||
self.primary
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FingerId> for RootFingerId {
|
||||
fn from(id: FingerId) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ pub(crate) mod runner;
|
||||
mod state;
|
||||
mod window_target;
|
||||
|
||||
pub(crate) use proxy::EventLoopProxy;
|
||||
pub(crate) use window_target::{ActiveEventLoop, OwnedDisplayHandle};
|
||||
pub(crate) use window_target::ActiveEventLoop;
|
||||
|
||||
pub struct EventLoop {
|
||||
elw: ActiveEventLoop,
|
||||
|
||||
@@ -1,17 +1,101 @@
|
||||
use super::runner::WeakShared;
|
||||
use crate::platform_impl::platform::r#async::Waker;
|
||||
use std::future;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
runner: Waker<WeakShared>,
|
||||
use super::super::main_thread::MainThreadMarker;
|
||||
use crate::event_loop::EventLoopProxyProvider;
|
||||
use crate::platform_impl::web::event_loop::runner::WeakShared;
|
||||
use crate::platform_impl::web::r#async::{AtomicWaker, Wrapper};
|
||||
|
||||
pub struct EventLoopProxy(Wrapper<WeakShared, Arc<State>, ()>);
|
||||
|
||||
struct State {
|
||||
awoken: AtomicBool,
|
||||
waker: AtomicWaker,
|
||||
closed: AtomicBool,
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn new(runner: Waker<WeakShared>) -> Self {
|
||||
Self { runner }
|
||||
pub fn new(main_thread: MainThreadMarker, runner: WeakShared) -> Self {
|
||||
let state = Arc::new(State {
|
||||
awoken: AtomicBool::new(false),
|
||||
waker: AtomicWaker::new(),
|
||||
closed: AtomicBool::new(false),
|
||||
});
|
||||
|
||||
Self(Wrapper::new(
|
||||
main_thread,
|
||||
runner,
|
||||
|runner, _| {
|
||||
let runner = runner.borrow();
|
||||
let runner = runner.as_ref().unwrap();
|
||||
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
runner.send_proxy_wake_up(true);
|
||||
}
|
||||
},
|
||||
{
|
||||
let state = Arc::clone(&state);
|
||||
|
||||
move |runner| async move {
|
||||
while future::poll_fn(|cx| {
|
||||
if state.awoken.swap(false, Ordering::Relaxed) {
|
||||
Poll::Ready(true)
|
||||
} else {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
if state.awoken.swap(false, Ordering::Relaxed) {
|
||||
Poll::Ready(true)
|
||||
} else {
|
||||
if state.closed.load(Ordering::Relaxed) {
|
||||
return Poll::Ready(false);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
{
|
||||
let runner = runner.borrow();
|
||||
let runner = runner.as_ref().unwrap();
|
||||
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
runner.send_proxy_wake_up(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
state,
|
||||
|state, _| {
|
||||
state.awoken.store(true, Ordering::Relaxed);
|
||||
state.waker.wake();
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn wake_up(&self) {
|
||||
self.runner.wake();
|
||||
pub fn take(&self) -> bool {
|
||||
debug_assert!(
|
||||
MainThreadMarker::new().is_some(),
|
||||
"this should only be called from the main thread"
|
||||
);
|
||||
|
||||
self.0.with_sender_data(|state| state.awoken.swap(false, Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventLoopProxy {
|
||||
fn drop(&mut self) {
|
||||
self.0.with_sender_data(|state| {
|
||||
state.closed.store(true, Ordering::Relaxed);
|
||||
state.waker.wake();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
self.0.send(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::collections::{HashSet, VecDeque};
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::Arc;
|
||||
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
@@ -13,13 +14,14 @@ use super::super::event;
|
||||
use super::super::main_thread::MainThreadMarker;
|
||||
use super::super::monitor::MonitorHandler;
|
||||
use super::backend;
|
||||
use super::proxy::EventLoopProxy;
|
||||
use super::state::State;
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::event::{DeviceEvent, ElementState, Event, RawKeyEvent, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ControlFlow, DeviceEvents};
|
||||
use crate::platform::web::{PollStrategy, WaitUntilStrategy};
|
||||
use crate::platform_impl::platform::backend::EventListenerHandle;
|
||||
use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
|
||||
use crate::platform_impl::platform::backend::{EventListenerHandle, SafeAreaHandle};
|
||||
use crate::platform_impl::platform::r#async::DispatchRunner;
|
||||
use crate::platform_impl::platform::window::Inner;
|
||||
use crate::window::WindowId;
|
||||
|
||||
@@ -37,7 +39,7 @@ type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
|
||||
|
||||
struct Execution {
|
||||
main_thread: MainThreadMarker,
|
||||
proxy_spawner: WakerSpawner<WeakShared>,
|
||||
event_loop_proxy: Arc<EventLoopProxy>,
|
||||
control_flow: Cell<ControlFlow>,
|
||||
poll_strategy: Cell<PollStrategy>,
|
||||
wait_until_strategy: Cell<WaitUntilStrategy>,
|
||||
@@ -55,6 +57,7 @@ struct Execution {
|
||||
redraw_pending: RefCell<HashSet<WindowId>>,
|
||||
destroy_pending: RefCell<VecDeque<WindowId>>,
|
||||
pub(crate) monitor: Rc<MonitorHandler>,
|
||||
safe_area: Rc<SafeAreaHandle>,
|
||||
page_transition_event_handle: RefCell<Option<backend::PageTransitionEventHandle>>,
|
||||
device_events: Cell<DeviceEvents>,
|
||||
on_mouse_move: OnEventHandle<PointerEvent>,
|
||||
@@ -140,12 +143,7 @@ impl Shared {
|
||||
let document = window.document().expect("Failed to obtain document");
|
||||
|
||||
Shared(Rc::<Execution>::new_cyclic(|weak| {
|
||||
let proxy_spawner =
|
||||
WakerSpawner::new(main_thread, WeakShared(weak.clone()), |runner, local| {
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
runner.send_proxy_wake_up(local);
|
||||
}
|
||||
});
|
||||
let proxy_spawner = EventLoopProxy::new(main_thread, WeakShared(weak.clone()));
|
||||
|
||||
let monitor = MonitorHandler::new(
|
||||
main_thread,
|
||||
@@ -154,9 +152,11 @@ impl Shared {
|
||||
WeakShared(weak.clone()),
|
||||
);
|
||||
|
||||
let safe_area = SafeAreaHandle::new(&window, &document);
|
||||
|
||||
Execution {
|
||||
main_thread,
|
||||
proxy_spawner,
|
||||
event_loop_proxy: Arc::new(proxy_spawner),
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
poll_strategy: Cell::new(PollStrategy::default()),
|
||||
wait_until_strategy: Cell::new(WaitUntilStrategy::default()),
|
||||
@@ -173,6 +173,7 @@ impl Shared {
|
||||
redraw_pending: RefCell::new(HashSet::new()),
|
||||
destroy_pending: RefCell::new(VecDeque::new()),
|
||||
monitor: Rc::new(monitor),
|
||||
safe_area: Rc::new(safe_area),
|
||||
page_transition_event_handle: RefCell::new(None),
|
||||
device_events: Cell::default(),
|
||||
on_mouse_move: RefCell::new(None),
|
||||
@@ -653,7 +654,7 @@ impl Shared {
|
||||
// Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle.
|
||||
events.extend(
|
||||
self.0
|
||||
.proxy_spawner
|
||||
.event_loop_proxy
|
||||
.take()
|
||||
.then_some(Event::UserWakeUp)
|
||||
.map(EventWrapper::from),
|
||||
@@ -818,8 +819,8 @@ impl Shared {
|
||||
self.0.wait_until_strategy.get()
|
||||
}
|
||||
|
||||
pub(crate) fn waker(&self) -> Waker<WeakShared> {
|
||||
self.0.proxy_spawner.waker()
|
||||
pub(crate) fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
|
||||
&self.0.event_loop_proxy
|
||||
}
|
||||
|
||||
pub(crate) fn weak(&self) -> WeakShared {
|
||||
@@ -829,6 +830,10 @@ impl Shared {
|
||||
pub(crate) fn monitor(&self) -> &Rc<MonitorHandler> {
|
||||
&self.0.monitor
|
||||
}
|
||||
|
||||
pub(crate) fn safe_area(&self) -> &Rc<SafeAreaHandle> {
|
||||
&self.0.safe_area
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -2,24 +2,25 @@ use std::cell::Cell;
|
||||
use std::clone::Clone;
|
||||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use web_sys::Element;
|
||||
|
||||
use super::super::monitor::MonitorPermissionFuture;
|
||||
use super::super::{lock, KeyEventExtra};
|
||||
use super::runner::{EventWrapper, WeakShared};
|
||||
use super::{backend, runner, EventLoopProxy};
|
||||
use super::runner::EventWrapper;
|
||||
use super::{backend, runner};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::event::{ElementState, Event, KeyEvent, TouchPhase, WindowEvent};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform::web::{CustomCursorFuture, PollStrategy, WaitUntilStrategy};
|
||||
use crate::platform_impl::platform::cursor::CustomCursor;
|
||||
use crate::platform_impl::platform::r#async::Waker;
|
||||
use crate::platform_impl::web::event_loop::proxy::EventLoopProxy;
|
||||
use crate::platform_impl::Window;
|
||||
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId};
|
||||
|
||||
@@ -197,7 +198,7 @@ impl ActiveEventLoop {
|
||||
let has_focus = has_focus.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, device_id, position, kind| {
|
||||
move |active_modifiers, device_id, primary, position, kind| {
|
||||
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
@@ -208,7 +209,12 @@ impl ActiveEventLoop {
|
||||
|
||||
runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::PointerLeft { device_id, position: Some(position), kind },
|
||||
event: WindowEvent::PointerLeft {
|
||||
device_id,
|
||||
primary,
|
||||
position: Some(position),
|
||||
kind,
|
||||
},
|
||||
})))
|
||||
}
|
||||
});
|
||||
@@ -218,7 +224,7 @@ impl ActiveEventLoop {
|
||||
let has_focus = has_focus.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, device_id, position, kind| {
|
||||
move |active_modifiers, device_id, primary, position, kind| {
|
||||
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
@@ -229,7 +235,7 @@ impl ActiveEventLoop {
|
||||
|
||||
runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::PointerEntered { device_id, position, kind },
|
||||
event: WindowEvent::PointerEntered { device_id, primary, position, kind },
|
||||
})))
|
||||
}
|
||||
});
|
||||
@@ -241,21 +247,31 @@ impl ActiveEventLoop {
|
||||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |device_id, events| {
|
||||
runner.send_events(events.flat_map(|(active_modifiers, position, source)| {
|
||||
let modifiers = (has_focus.get() && modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
runner.send_events(events.flat_map(
|
||||
|(active_modifiers, primary, position, source)| {
|
||||
let modifiers = (has_focus.get()
|
||||
&& modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged(
|
||||
active_modifiers.into(),
|
||||
),
|
||||
}
|
||||
});
|
||||
|
||||
modifiers.into_iter().chain(iter::once(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::PointerMoved { device_id, position, source },
|
||||
}))
|
||||
}));
|
||||
modifiers.into_iter().chain(iter::once(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::PointerMoved {
|
||||
device_id,
|
||||
primary,
|
||||
position,
|
||||
source,
|
||||
},
|
||||
}))
|
||||
},
|
||||
));
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -263,7 +279,7 @@ impl ActiveEventLoop {
|
||||
let has_focus = has_focus.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, device_id, position, state, button| {
|
||||
move |active_modifiers, device_id, primary, position, state, button| {
|
||||
let modifiers =
|
||||
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
@@ -275,7 +291,13 @@ impl ActiveEventLoop {
|
||||
|
||||
runner.send_events(modifiers.into_iter().chain([Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton { device_id, state, position, button },
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary,
|
||||
state,
|
||||
position,
|
||||
button,
|
||||
},
|
||||
}]));
|
||||
}
|
||||
},
|
||||
@@ -285,7 +307,7 @@ impl ActiveEventLoop {
|
||||
let runner = self.runner.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, device_id, position, button| {
|
||||
move |active_modifiers, device_id, primary, position, button| {
|
||||
let modifiers = (modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
@@ -298,6 +320,7 @@ impl ActiveEventLoop {
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary,
|
||||
state: ElementState::Pressed,
|
||||
position,
|
||||
button,
|
||||
@@ -311,7 +334,7 @@ impl ActiveEventLoop {
|
||||
let has_focus = has_focus.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, device_id, position, button| {
|
||||
move |active_modifiers, device_id, primary, position, button| {
|
||||
let modifiers =
|
||||
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
@@ -325,6 +348,7 @@ impl ActiveEventLoop {
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id,
|
||||
primary,
|
||||
state: ElementState::Released,
|
||||
position,
|
||||
button,
|
||||
@@ -453,15 +477,15 @@ impl ActiveEventLoop {
|
||||
self.runner.monitor().has_detailed_monitor_permission()
|
||||
}
|
||||
|
||||
pub(crate) fn waker(&self) -> Waker<WeakShared> {
|
||||
self.runner.waker()
|
||||
pub(crate) fn event_loop_proxy(&self) -> Arc<EventLoopProxy> {
|
||||
self.runner.event_loop_proxy().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> RootEventLoopProxy {
|
||||
let event_loop_proxy = EventLoopProxy::new(self.waker());
|
||||
RootEventLoopProxy { event_loop_proxy }
|
||||
let event_loop_proxy = self.event_loop_proxy();
|
||||
RootEventLoopProxy::new(event_loop_proxy)
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
@@ -523,17 +547,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
self.runner.exiting()
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
|
||||
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::Web(rwh_06::WebDisplayHandle::new());
|
||||
@@ -541,15 +563,12 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::WebDisplayHandle::new().into())
|
||||
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::Web(rwh_06::WebDisplayHandle::new());
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +37,8 @@ pub(crate) use cursor::{
|
||||
CustomCursorSource as PlatformCustomCursorSource,
|
||||
};
|
||||
|
||||
pub use self::event::FingerId;
|
||||
pub(crate) use self::event_loop::{
|
||||
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
|
||||
PlatformSpecificEventLoopAttributes,
|
||||
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
|
||||
};
|
||||
pub(crate) use self::keyboard::KeyEventExtra;
|
||||
pub(crate) use self::monitor::{
|
||||
|
||||
@@ -72,8 +72,8 @@ pub struct Common {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Style {
|
||||
read: CssStyleDeclaration,
|
||||
write: CssStyleDeclaration,
|
||||
pub(super) read: CssStyleDeclaration,
|
||||
pub(super) write: CssStyleDeclaration,
|
||||
}
|
||||
|
||||
impl Canvas {
|
||||
@@ -331,28 +331,32 @@ impl Canvas {
|
||||
|
||||
pub fn on_pointer_leave<F>(&self, handler: F)
|
||||
where
|
||||
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
|
||||
F: 'static
|
||||
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
|
||||
{
|
||||
self.handlers.borrow_mut().pointer_handler.on_pointer_leave(&self.common, handler)
|
||||
}
|
||||
|
||||
pub fn on_pointer_enter<F>(&self, handler: F)
|
||||
where
|
||||
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
|
||||
F: 'static
|
||||
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
|
||||
{
|
||||
self.handlers.borrow_mut().pointer_handler.on_pointer_enter(&self.common, handler)
|
||||
}
|
||||
|
||||
pub fn on_pointer_release<C>(&self, handler: C)
|
||||
where
|
||||
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
|
||||
C: 'static
|
||||
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
|
||||
{
|
||||
self.handlers.borrow_mut().pointer_handler.on_pointer_release(&self.common, handler)
|
||||
}
|
||||
|
||||
pub fn on_pointer_press<C>(&self, handler: C)
|
||||
where
|
||||
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
|
||||
C: 'static
|
||||
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
|
||||
{
|
||||
self.handlers.borrow_mut().pointer_handler.on_pointer_press(
|
||||
&self.common,
|
||||
@@ -366,12 +370,15 @@ impl Canvas {
|
||||
C: 'static
|
||||
+ FnMut(
|
||||
Option<DeviceId>,
|
||||
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
|
||||
&mut dyn Iterator<
|
||||
Item = (ModifiersState, bool, PhysicalPosition<f64>, PointerSource),
|
||||
>,
|
||||
),
|
||||
B: 'static
|
||||
+ FnMut(
|
||||
ModifiersState,
|
||||
Option<DeviceId>,
|
||||
bool,
|
||||
PhysicalPosition<f64>,
|
||||
ElementState,
|
||||
ButtonSource,
|
||||
|
||||
@@ -6,9 +6,8 @@ use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{KeyboardEvent, MouseEvent, Navigator, PointerEvent, WheelEvent};
|
||||
|
||||
use super::super::FingerId;
|
||||
use super::Engine;
|
||||
use crate::event::{MouseButton, MouseScrollDelta, PointerKind};
|
||||
use crate::event::{FingerId, MouseButton, MouseScrollDelta, PointerKind};
|
||||
use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey};
|
||||
|
||||
bitflags::bitflags! {
|
||||
@@ -164,7 +163,7 @@ pub fn mouse_scroll_delta(
|
||||
pub fn pointer_type(event: &PointerEvent, pointer_id: i32) -> PointerKind {
|
||||
match event.pointer_type().as_str() {
|
||||
"mouse" => PointerKind::Mouse,
|
||||
"touch" => PointerKind::Touch(FingerId::new(pointer_id, event.is_primary()).into()),
|
||||
"touch" => PointerKind::Touch(FingerId::from_raw(pointer_id as usize)),
|
||||
_ => PointerKind::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ mod intersection_handle;
|
||||
mod media_query_handle;
|
||||
mod pointer;
|
||||
mod resize_scaling;
|
||||
mod safe_area;
|
||||
mod schedule;
|
||||
|
||||
use std::cell::OnceCell;
|
||||
@@ -20,6 +21,7 @@ use web_sys::{Document, HtmlCanvasElement, Navigator, PageTransitionEvent, Visib
|
||||
pub use self::canvas::{Canvas, Style};
|
||||
pub use self::event_handle::EventListenerHandle;
|
||||
pub use self::resize_scaling::ResizeScaleHandle;
|
||||
pub use self::safe_area::SafeAreaHandle;
|
||||
pub use self::schedule::Schedule;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ impl PointerHandler {
|
||||
|
||||
pub fn on_pointer_leave<F>(&mut self, canvas_common: &Common, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
|
||||
F: 'static
|
||||
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
|
||||
{
|
||||
let window = canvas_common.window.clone();
|
||||
self.on_cursor_leave =
|
||||
@@ -46,13 +47,14 @@ impl PointerHandler {
|
||||
let position =
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window));
|
||||
let kind = event::pointer_type(&event, pointer_id);
|
||||
handler(modifiers, device_id, position, kind);
|
||||
handler(modifiers, device_id, event.is_primary(), position, kind);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_pointer_enter<F>(&mut self, canvas_common: &Common, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
|
||||
F: 'static
|
||||
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, PointerKind),
|
||||
{
|
||||
let window = canvas_common.window.clone();
|
||||
self.on_cursor_enter =
|
||||
@@ -63,13 +65,14 @@ impl PointerHandler {
|
||||
let position =
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window));
|
||||
let kind = event::pointer_type(&event, pointer_id);
|
||||
handler(modifiers, device_id, position, kind);
|
||||
handler(modifiers, device_id, event.is_primary(), position, kind);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_pointer_release<C>(&mut self, canvas_common: &Common, mut handler: C)
|
||||
where
|
||||
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
|
||||
C: 'static
|
||||
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
|
||||
{
|
||||
let window = canvas_common.window.clone();
|
||||
self.on_pointer_release =
|
||||
@@ -92,6 +95,7 @@ impl PointerHandler {
|
||||
handler(
|
||||
modifiers,
|
||||
mkdid(pointer_id),
|
||||
event.is_primary(),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||
source,
|
||||
)
|
||||
@@ -104,7 +108,8 @@ impl PointerHandler {
|
||||
mut handler: C,
|
||||
prevent_default: Rc<Cell<bool>>,
|
||||
) where
|
||||
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
|
||||
C: 'static
|
||||
+ FnMut(ModifiersState, Option<DeviceId>, bool, PhysicalPosition<f64>, ButtonSource),
|
||||
{
|
||||
let window = canvas_common.window.clone();
|
||||
let canvas = canvas_common.raw().clone();
|
||||
@@ -143,6 +148,7 @@ impl PointerHandler {
|
||||
handler(
|
||||
modifiers,
|
||||
mkdid(pointer_id),
|
||||
event.is_primary(),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||
source,
|
||||
)
|
||||
@@ -159,12 +165,15 @@ impl PointerHandler {
|
||||
C: 'static
|
||||
+ FnMut(
|
||||
Option<DeviceId>,
|
||||
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
|
||||
&mut dyn Iterator<
|
||||
Item = (ModifiersState, bool, PhysicalPosition<f64>, PointerSource),
|
||||
>,
|
||||
),
|
||||
B: 'static
|
||||
+ FnMut(
|
||||
ModifiersState,
|
||||
Option<DeviceId>,
|
||||
bool,
|
||||
PhysicalPosition<f64>,
|
||||
ElementState,
|
||||
ButtonSource,
|
||||
@@ -177,6 +186,7 @@ impl PointerHandler {
|
||||
let pointer_id = event.pointer_id();
|
||||
let device_id = mkdid(pointer_id);
|
||||
let kind = event::pointer_type(&event, pointer_id);
|
||||
let primary = event.is_primary();
|
||||
|
||||
// chorded button event
|
||||
if let Some(button) = event::mouse_button(&event) {
|
||||
@@ -213,6 +223,7 @@ impl PointerHandler {
|
||||
button_handler(
|
||||
event::mouse_modifiers(&event),
|
||||
device_id,
|
||||
primary,
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||
state,
|
||||
button,
|
||||
@@ -229,6 +240,7 @@ impl PointerHandler {
|
||||
&mut event::pointer_move_event(event).map(|event| {
|
||||
(
|
||||
event::mouse_modifiers(&event),
|
||||
event.is_primary(),
|
||||
event::mouse_position(&event).to_physical(scale),
|
||||
match kind {
|
||||
PointerKind::Mouse => PointerSource::Mouse,
|
||||
|
||||
56
src/platform_impl/web/web_sys/safe_area.rs
Normal file
56
src/platform_impl/web/web_sys/safe_area.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use dpi::{LogicalPosition, LogicalSize};
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Document, HtmlHtmlElement, Window};
|
||||
|
||||
use super::Style;
|
||||
|
||||
pub struct SafeAreaHandle {
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl SafeAreaHandle {
|
||||
pub fn new(window: &Window, document: &Document) -> Self {
|
||||
let document: HtmlHtmlElement = document.document_element().unwrap().unchecked_into();
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let write = document.style();
|
||||
write
|
||||
.set_property(
|
||||
"--__winit_safe_area",
|
||||
"env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) \
|
||||
env(safe-area-inset-left)",
|
||||
)
|
||||
.expect("unexpected read-only declaration block");
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let read = window
|
||||
.get_computed_style(&document)
|
||||
.expect("failed to obtain computed style")
|
||||
// this can't fail: we aren't using a pseudo-element
|
||||
.expect("invalid pseudo-element");
|
||||
|
||||
SafeAreaHandle { style: Style { read, write } }
|
||||
}
|
||||
|
||||
pub fn get(&self) -> (LogicalPosition<f64>, LogicalSize<f64>) {
|
||||
let value = self.style.get("--__winit_safe_area");
|
||||
|
||||
let mut values = value
|
||||
.split(' ')
|
||||
.map(|value| value.strip_suffix("px").expect("unexpected unit other then `px` found"));
|
||||
let top: f64 = values.next().unwrap().parse().unwrap();
|
||||
let right: f64 = values.next().unwrap().parse().unwrap();
|
||||
let bottom: f64 = values.next().unwrap().parse().unwrap();
|
||||
let left: f64 = values.next().unwrap().parse().unwrap();
|
||||
assert_eq!(values.next(), None, "unexpected fifth value");
|
||||
|
||||
let width = super::style_size_property(&self.style, "width") - left - right;
|
||||
let height = super::style_size_property(&self.style, "height") - top - bottom;
|
||||
|
||||
(LogicalPosition::new(left, top), LogicalSize::new(width, height))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SafeAreaHandle {
|
||||
fn drop(&mut self) {
|
||||
self.style.remove("--__winit_safe_area");
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,14 @@ use std::cell::Ref;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use dpi::{LogicalPosition, LogicalSize};
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
use super::main_thread::{MainThreadMarker, MainThreadSafe};
|
||||
use super::monitor::MonitorHandler;
|
||||
use super::r#async::Dispatcher;
|
||||
use super::{backend, lock, ActiveEventLoop};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::dpi::{LogicalInsets, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::icon::Icon;
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
@@ -26,6 +27,7 @@ pub struct Inner {
|
||||
id: WindowId,
|
||||
pub window: web_sys::Window,
|
||||
monitor: Rc<MonitorHandler>,
|
||||
safe_area: Rc<backend::SafeAreaHandle>,
|
||||
canvas: Rc<backend::Canvas>,
|
||||
destroy_fn: Option<Box<dyn FnOnce()>>,
|
||||
}
|
||||
@@ -59,6 +61,7 @@ impl Window {
|
||||
id,
|
||||
window: window.clone(),
|
||||
monitor: Rc::clone(target.runner.monitor()),
|
||||
safe_area: Rc::clone(target.runner.safe_area()),
|
||||
canvas,
|
||||
destroy_fn: Some(destroy_fn),
|
||||
};
|
||||
@@ -109,9 +112,9 @@ impl RootWindow for Window {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
// Note: the canvas element has no window decorations, so this is equal to `outer_position`.
|
||||
self.outer_position()
|
||||
fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
// Note: the canvas element has no window decorations.
|
||||
(0, 0).into()
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
@@ -152,6 +155,34 @@ impl RootWindow for Window {
|
||||
self.surface_size()
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
self.inner.queue(|inner| {
|
||||
let (safe_start_pos, safe_size) = inner.safe_area.get();
|
||||
let safe_end_pos = LogicalPosition::new(
|
||||
safe_start_pos.x + safe_size.width,
|
||||
safe_start_pos.y + safe_size.height,
|
||||
);
|
||||
|
||||
let surface_start_pos = inner.canvas.position();
|
||||
let surface_size = LogicalSize::new(
|
||||
backend::style_size_property(inner.canvas.style(), "width"),
|
||||
backend::style_size_property(inner.canvas.style(), "height"),
|
||||
);
|
||||
let surface_end_pos = LogicalPosition::new(
|
||||
surface_start_pos.x + surface_size.width,
|
||||
surface_start_pos.y + surface_size.height,
|
||||
);
|
||||
|
||||
let top = f64::max(safe_start_pos.y - surface_start_pos.y, 0.);
|
||||
let left = f64::max(safe_start_pos.x - surface_start_pos.x, 0.);
|
||||
let bottom = f64::max(surface_end_pos.y - safe_end_pos.y, 0.);
|
||||
let right = f64::max(surface_end_pos.x - safe_end_pos.x, 0.);
|
||||
|
||||
let insets = LogicalInsets::new(top, left, bottom, right);
|
||||
insets.to_physical(inner.scale_factor())
|
||||
})
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<Size>) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
let dimensions = min_size.map(|min_size| min_size.to_logical(inner.scale_factor()));
|
||||
@@ -375,18 +406,15 @@ impl RootWindow for Window {
|
||||
self.inner.queue(|inner| inner.monitor.primary_monitor()).map(RootMonitorHandle::from)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
MainThreadMarker::new()
|
||||
@@ -408,7 +436,6 @@ impl rwh_06::HasWindowHandle for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
Ok(rwh_06::DisplayHandle::web())
|
||||
|
||||
@@ -4,6 +4,7 @@ mod runner;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::ffi::c_void;
|
||||
use std::os::windows::io::{AsRawHandle as _, FromRawHandle as _, OwnedHandle, RawHandle};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
@@ -12,13 +13,18 @@ use std::{mem, panic, ptr};
|
||||
|
||||
use runner::EventLoopRunner;
|
||||
use windows_sys::Win32::Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE;
|
||||
use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, POINT, RECT, WPARAM};
|
||||
use windows_sys::Win32::Foundation::{
|
||||
GetLastError, FALSE, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_FAILED, WPARAM,
|
||||
};
|
||||
use windows_sys::Win32::Graphics::Gdi::{
|
||||
GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient,
|
||||
ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE,
|
||||
};
|
||||
use windows_sys::Win32::System::Ole::RevokeDragDrop;
|
||||
use windows_sys::Win32::System::Threading::{GetCurrentThreadId, INFINITE};
|
||||
use windows_sys::Win32::System::Threading::{
|
||||
CreateWaitableTimerExW, GetCurrentThreadId, SetWaitableTimer,
|
||||
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, INFINITE, TIMER_ALL_ACCESS,
|
||||
};
|
||||
use windows_sys::Win32::UI::Controls::{HOVER_DEFAULT, WM_MOUSELEAVE};
|
||||
use windows_sys::Win32::UI::Input::Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW};
|
||||
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
|
||||
@@ -34,14 +40,15 @@ use windows_sys::Win32::UI::Input::Touch::{
|
||||
use windows_sys::Win32::UI::Input::{RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE};
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect, GetCursorPos,
|
||||
GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW,
|
||||
RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos, TranslateMessage, CREATESTRUCTW,
|
||||
GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS,
|
||||
PM_REMOVE, PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
|
||||
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM,
|
||||
WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
|
||||
WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
|
||||
WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
|
||||
GetMenu, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW,
|
||||
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos, TranslateMessage,
|
||||
CREATESTRUCTW, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG,
|
||||
MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PM_REMOVE, PT_TOUCH, QS_ALLEVENTS, RI_MOUSE_HWHEEL,
|
||||
RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE,
|
||||
SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM, WMSZ_BOTTOMLEFT,
|
||||
WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT,
|
||||
WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE,
|
||||
WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
|
||||
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS,
|
||||
WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL,
|
||||
WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
|
||||
@@ -58,12 +65,12 @@ use crate::application::ApplicationHandler;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::error::{EventLoopError, RequestError};
|
||||
use crate::event::{
|
||||
Event, FingerId as RootFingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase,
|
||||
WindowEvent,
|
||||
Event, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent,
|
||||
};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as RootOwnedDisplayHandle,
|
||||
EventLoopProxy as RootEventLoopProxy, EventLoopProxyProvider,
|
||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
@@ -80,7 +87,7 @@ use crate::platform_impl::platform::window::InitData;
|
||||
use crate::platform_impl::platform::window_state::{
|
||||
CursorFlags, ImeState, WindowFlags, WindowState,
|
||||
};
|
||||
use crate::platform_impl::platform::{raw_input, util, wrap_device_id, FingerId, Fullscreen};
|
||||
use crate::platform_impl::platform::{raw_input, util, wrap_device_id, Fullscreen};
|
||||
use crate::platform_impl::Window;
|
||||
use crate::utils::Lazy;
|
||||
use crate::window::{
|
||||
@@ -127,6 +134,10 @@ pub(crate) enum ProcResult {
|
||||
pub struct EventLoop {
|
||||
window_target: ActiveEventLoop,
|
||||
msg_hook: Option<Box<dyn FnMut(*const c_void) -> bool + 'static>>,
|
||||
// It is a timer used on timed waits.
|
||||
// It is created lazily in case if we have `ControlFlow::WaitUntil`.
|
||||
// Keep it as a field to avoid recreating it on every `ControlFlow::WaitUntil`.
|
||||
high_resolution_timer: Option<OwnedHandle>,
|
||||
}
|
||||
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {
|
||||
@@ -201,6 +212,7 @@ impl EventLoop {
|
||||
Ok(EventLoop {
|
||||
window_target: ActiveEventLoop { thread_id, thread_msg_target, runner_shared },
|
||||
msg_hook: attributes.msg_hook.take(),
|
||||
high_resolution_timer: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -245,8 +257,9 @@ impl EventLoop {
|
||||
}
|
||||
|
||||
let exit_code = loop {
|
||||
self.wait_and_dispatch_message(None);
|
||||
|
||||
self.wait_for_messages(None);
|
||||
// wait_for_messages calls user application before and after waiting
|
||||
// so it may have decided to exit.
|
||||
if let Some(code) = self.exit_code() {
|
||||
break code;
|
||||
}
|
||||
@@ -312,8 +325,11 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
self.wait_and_dispatch_message(timeout);
|
||||
|
||||
if self.exit_code().is_none() {
|
||||
self.wait_for_messages(timeout);
|
||||
}
|
||||
// wait_for_messages calls user application before and after waiting
|
||||
// so it may have decided to exit.
|
||||
if self.exit_code().is_none() {
|
||||
self.dispatch_peeked_messages();
|
||||
}
|
||||
@@ -343,101 +359,27 @@ impl EventLoop {
|
||||
status
|
||||
}
|
||||
|
||||
/// Wait for one message and dispatch it, optionally with a timeout
|
||||
fn wait_and_dispatch_message(&mut self, timeout: Option<Duration>) {
|
||||
fn get_msg_with_timeout(msg: &mut MSG, timeout: Option<Duration>) -> PumpStatus {
|
||||
unsafe {
|
||||
// A timeout of None means wait indefinitely (so we don't need to call SetTimer)
|
||||
let timer_id = timeout.map(|timeout| SetTimer(0, 0, dur2timeout(timeout), None));
|
||||
let get_status = GetMessageW(msg, 0, 0, 0);
|
||||
if let Some(timer_id) = timer_id {
|
||||
KillTimer(0, timer_id);
|
||||
}
|
||||
// A return value of 0 implies `WM_QUIT`
|
||||
if get_status == 0 {
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
PumpStatus::Continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the next MSG either via PeekMessage or GetMessage depending on whether the
|
||||
/// requested timeout is `ZERO` (and so we don't want to block)
|
||||
///
|
||||
/// Returns `None` if no MSG was read, else a `Continue` or `Exit` status
|
||||
fn wait_for_msg(msg: &mut MSG, timeout: Option<Duration>) -> Option<PumpStatus> {
|
||||
if timeout == Some(Duration::ZERO) {
|
||||
unsafe {
|
||||
if PeekMessageW(msg, 0, 0, 0, PM_REMOVE) != 0 {
|
||||
Some(PumpStatus::Continue)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Some(get_msg_with_timeout(msg, timeout))
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits until new event messages arrive to be peeked.
|
||||
/// Doesn't peek messages itself.
|
||||
///
|
||||
/// Parameter timeout is optional. This method would wait for the smaller timeout
|
||||
/// between the argument and a timeout from control flow.
|
||||
fn wait_for_messages(&mut self, timeout: Option<Duration>) {
|
||||
let runner = &self.window_target.runner_shared;
|
||||
|
||||
// We aim to be consistent with the MacOS backend which has a RunLoop
|
||||
// observer that will dispatch AboutToWait when about to wait for
|
||||
// events, and NewEvents after the RunLoop wakes up.
|
||||
//
|
||||
// We emulate similar behaviour by treating `GetMessage` as our wait
|
||||
// We emulate similar behaviour by treating `MsgWaitForMultipleObjectsEx` as our wait
|
||||
// point and wake up point (when it returns) and we drain all other
|
||||
// pending messages via `PeekMessage` until we come back to "wait" via
|
||||
// `GetMessage`
|
||||
// `MsgWaitForMultipleObjectsEx`.
|
||||
//
|
||||
runner.prepare_wait();
|
||||
|
||||
let control_flow_timeout = match runner.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
let start = Instant::now();
|
||||
Some(wait_deadline.saturating_duration_since(start))
|
||||
},
|
||||
};
|
||||
let timeout = min_timeout(control_flow_timeout, timeout);
|
||||
|
||||
// # Safety
|
||||
// The Windows API has no documented requirement for bitwise
|
||||
// initializing a `MSG` struct (it can be uninitialized memory for the C
|
||||
// API) and there's no API to construct or initialize a `MSG`. This
|
||||
// is the simplest way avoid uninitialized memory in Rust
|
||||
let mut msg = unsafe { mem::zeroed() };
|
||||
let msg_status = wait_for_msg(&mut msg, timeout);
|
||||
|
||||
wait_for_messages_impl(&mut self.high_resolution_timer, runner.control_flow(), timeout);
|
||||
// Before we potentially exit, make sure to consistently emit an event for the wake up
|
||||
runner.wakeup();
|
||||
|
||||
match msg_status {
|
||||
None => {}, // No MSG to dispatch
|
||||
Some(PumpStatus::Exit(code)) => {
|
||||
runner.set_exit_code(code);
|
||||
},
|
||||
Some(PumpStatus::Continue) => {
|
||||
unsafe {
|
||||
let handled = if let Some(callback) = self.msg_hook.as_deref_mut() {
|
||||
callback(&mut msg as *mut _ as *mut _)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !handled {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(payload) = runner.take_panic_error() {
|
||||
runner.reset_runner();
|
||||
panic::resume_unwind(payload);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch all queued messages via `PeekMessageW`
|
||||
@@ -456,7 +398,7 @@ impl EventLoop {
|
||||
// initializing a `MSG` struct (it can be uninitialized memory for the C
|
||||
// API) and there's no API to construct or initialize a `MSG`. This
|
||||
// is the simplest way avoid uninitialized memory in Rust
|
||||
let mut msg = unsafe { mem::zeroed() };
|
||||
let mut msg: MSG = unsafe { mem::zeroed() };
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
@@ -495,6 +437,14 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventLoop {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
DestroyWindow(self.window_target.thread_msg_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveEventLoop {
|
||||
#[inline(always)]
|
||||
pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor {
|
||||
@@ -513,7 +463,7 @@ impl ActiveEventLoop {
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> RootEventLoopProxy {
|
||||
let event_loop_proxy = EventLoopProxy { target_window: self.thread_msg_target };
|
||||
RootEventLoopProxy { event_loop_proxy }
|
||||
RootEventLoopProxy::new(Arc::new(event_loop_proxy))
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
@@ -566,8 +516,8 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
self.runner_shared.set_exit_code(0)
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> RootOwnedDisplayHandle {
|
||||
RootOwnedDisplayHandle { platform: OwnedDisplayHandle }
|
||||
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
|
||||
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
|
||||
}
|
||||
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
@@ -575,7 +525,6 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::Windows(rwh_06::WindowsDisplayHandle::new());
|
||||
@@ -583,16 +532,13 @@ impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl OwnedDisplayHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::WindowsDisplayHandle::new().into())
|
||||
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::Windows(rwh_06::WindowsDisplayHandle::new());
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,7 +563,7 @@ impl OwnedDisplayHandle {
|
||||
fn main_thread_id() -> u32 {
|
||||
static mut MAIN_THREAD_ID: u32 = 0;
|
||||
|
||||
/// Function pointer used in CRT initialization section to set the above static field's value.
|
||||
// Function pointer used in CRT initialization section to set the above static field's value.
|
||||
|
||||
// Mark as used so this is not removable.
|
||||
#[used]
|
||||
@@ -668,10 +614,139 @@ fn dur2timeout(dur: Duration) -> u32 {
|
||||
.unwrap_or(INFINITE)
|
||||
}
|
||||
|
||||
impl Drop for EventLoop {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
DestroyWindow(self.window_target.thread_msg_target);
|
||||
/// Set upper limit for waiting time to avoid overflows.
|
||||
/// I chose 50 days as a limit because it is used in dur2timeout.
|
||||
const FIFTY_DAYS: Duration = Duration::from_secs(50_u64 * 24 * 60 * 60);
|
||||
/// Waitable timers use 100 ns intervals to indicate due time.
|
||||
/// <https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setwaitabletimer#parameters>
|
||||
/// And there is no point waiting using other ways for such small timings
|
||||
/// because they are even less precise (can overshoot by few ms).
|
||||
const MIN_WAIT: Duration = Duration::from_nanos(100);
|
||||
|
||||
fn create_high_resolution_timer() -> Option<OwnedHandle> {
|
||||
unsafe {
|
||||
let handle: HANDLE = CreateWaitableTimerExW(
|
||||
ptr::null(),
|
||||
ptr::null(),
|
||||
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
|
||||
TIMER_ALL_ACCESS,
|
||||
);
|
||||
// CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported only after
|
||||
// Win10 1803 but it is already default option for rustc
|
||||
// (std uses it to implement `std::thread::sleep`).
|
||||
if handle == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(OwnedHandle::from_raw_handle(handle as *mut c_void))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function should not return error if parameters are valid
|
||||
/// but there is no guarantee about that at MSDN docs
|
||||
/// so we return result of GetLastError if fail.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// timer must be a valid timer handle created by [create_high_resolution_timer].
|
||||
/// timeout divided by 100 nanoseconds must be more than 0 and less than i64::MAX.
|
||||
unsafe fn set_high_resolution_timer(timer: RawHandle, timeout: Duration) -> Result<(), u32> {
|
||||
const INTERVAL_NS: u32 = MIN_WAIT.subsec_nanos();
|
||||
const INTERVALS_IN_SEC: u64 = (Duration::from_secs(1).as_nanos() / INTERVAL_NS as u128) as u64;
|
||||
let intervals_to_wait: u64 =
|
||||
timeout.as_secs() * INTERVALS_IN_SEC + u64::from(timeout.subsec_nanos() / INTERVAL_NS);
|
||||
debug_assert!(intervals_to_wait < i64::MAX as u64, "Must be called with smaller duration",);
|
||||
// Use negative time to indicate relative time.
|
||||
let due_time: i64 = -(intervals_to_wait as i64);
|
||||
unsafe {
|
||||
let set_result = SetWaitableTimer(timer as HANDLE, &due_time, 0, None, ptr::null(), FALSE);
|
||||
if set_result != FALSE {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(GetLastError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation detail of [EventLoop::wait_for_messages].
|
||||
///
|
||||
/// Does actual system-level waiting and doesn't process any messages itself,
|
||||
/// including winits internal notifications about waiting and new messages arrival.
|
||||
fn wait_for_messages_impl(
|
||||
high_resolution_timer: &mut Option<OwnedHandle>,
|
||||
control_flow: ControlFlow,
|
||||
timeout: Option<Duration>,
|
||||
) {
|
||||
let timeout = {
|
||||
let control_flow_timeout = match control_flow {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
let start = Instant::now();
|
||||
Some(wait_deadline.saturating_duration_since(start))
|
||||
},
|
||||
};
|
||||
let timeout = min_timeout(timeout, control_flow_timeout);
|
||||
if timeout == Some(Duration::ZERO) {
|
||||
// Do not wait if we don't have time.
|
||||
return;
|
||||
}
|
||||
// Now we decided to wait so need to do some clamping
|
||||
// to avoid problems with overflow and calling WinAPI with invalid parameters.
|
||||
timeout
|
||||
.map(|t| t.min(FIFTY_DAYS))
|
||||
// If timeout is less than minimally supported by Windows,
|
||||
// increase it to that minimum. Who want less than microsecond delays anyway?
|
||||
.map(|t| t.max(MIN_WAIT))
|
||||
};
|
||||
|
||||
if timeout.is_some() && high_resolution_timer.is_none() {
|
||||
*high_resolution_timer = create_high_resolution_timer();
|
||||
}
|
||||
|
||||
let high_resolution_timer: Option<RawHandle> =
|
||||
high_resolution_timer.as_ref().map(OwnedHandle::as_raw_handle);
|
||||
|
||||
let use_timer: bool;
|
||||
if let (Some(handle), Some(timeout)) = (high_resolution_timer, timeout) {
|
||||
let res = unsafe {
|
||||
// Safety: handle can be Some only if we succeeded in creating high resolution
|
||||
// timer. We properly clamped timeout so it can be used as argument
|
||||
// to timer.
|
||||
set_high_resolution_timer(handle, timeout)
|
||||
};
|
||||
if let Err(error_code) = res {
|
||||
// We successfully got timer but failed to set it?
|
||||
// Should be some bug in our code.
|
||||
tracing::trace!("Failed to set high resolution timer: last error {}", error_code);
|
||||
use_timer = false;
|
||||
} else {
|
||||
use_timer = true;
|
||||
}
|
||||
} else {
|
||||
use_timer = false;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// Either:
|
||||
// 1. User wants to wait indefinely if timeout is not set.
|
||||
// 2. We failed to get and set high resolution timer and we need something instead of it.
|
||||
let wait_duration_ms = timeout.map(dur2timeout).unwrap_or(INFINITE);
|
||||
|
||||
let (num_handles, raw_handles) =
|
||||
if use_timer { (1, [high_resolution_timer.unwrap()]) } else { (0, [ptr::null_mut()]) };
|
||||
|
||||
let result = MsgWaitForMultipleObjectsEx(
|
||||
num_handles,
|
||||
raw_handles.as_ptr() as *const _,
|
||||
wait_duration_ms,
|
||||
QS_ALLEVENTS,
|
||||
MWMO_INPUTAVAILABLE,
|
||||
);
|
||||
if result == WAIT_FAILED {
|
||||
// Well, nothing smart to do in such case.
|
||||
// Treat it as spurious wake up.
|
||||
tracing::warn!("Failed to MsgWaitForMultipleObjectsEx: error code {}", GetLastError(),);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -726,15 +801,14 @@ impl EventLoopThreadExecutor {
|
||||
|
||||
type ThreadExecFn = Box<Box<dyn FnMut()>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventLoopProxy {
|
||||
target_window: HWND,
|
||||
}
|
||||
|
||||
unsafe impl Send for EventLoopProxy {}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wake_up(&self) {
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) };
|
||||
}
|
||||
}
|
||||
@@ -1542,6 +1616,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerEntered {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position,
|
||||
kind: PointerKind::Mouse,
|
||||
},
|
||||
@@ -1567,6 +1642,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerLeft {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: Some(position),
|
||||
kind: PointerKind::Mouse,
|
||||
},
|
||||
@@ -1588,7 +1664,12 @@ unsafe fn public_window_callback_inner(
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerMoved { device_id: None, position, source: PointerSource::Mouse },
|
||||
event: PointerMoved {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position,
|
||||
source: PointerSource::Mouse,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1606,7 +1687,7 @@ unsafe fn public_window_callback_inner(
|
||||
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerLeft { device_id: None, position: None, kind: Mouse },
|
||||
event: PointerLeft { device_id: None, primary: true, position: None, kind: Mouse },
|
||||
});
|
||||
|
||||
result = ProcResult::Value(0);
|
||||
@@ -1683,6 +1764,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: Pressed,
|
||||
position,
|
||||
button: Left.into(),
|
||||
@@ -1708,6 +1790,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: Released,
|
||||
position,
|
||||
button: Left.into(),
|
||||
@@ -1733,6 +1816,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: Pressed,
|
||||
position,
|
||||
button: Right.into(),
|
||||
@@ -1758,6 +1842,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: Released,
|
||||
position,
|
||||
button: Right.into(),
|
||||
@@ -1783,6 +1868,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: Pressed,
|
||||
position,
|
||||
button: Middle.into(),
|
||||
@@ -1808,6 +1894,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: Released,
|
||||
position,
|
||||
button: Middle.into(),
|
||||
@@ -1834,6 +1921,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: Pressed,
|
||||
position,
|
||||
button: match xbutton {
|
||||
@@ -1865,6 +1953,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id: WindowId::from_raw(window as usize),
|
||||
event: PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: Released,
|
||||
position,
|
||||
button: match xbutton {
|
||||
@@ -1918,16 +2007,15 @@ unsafe fn public_window_callback_inner(
|
||||
let position = PhysicalPosition::new(x, y);
|
||||
|
||||
let window_id = WindowId::from_raw(window as usize);
|
||||
let finger_id = RootFingerId(FingerId {
|
||||
id: input.dwID,
|
||||
primary: util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY),
|
||||
});
|
||||
let finger_id = FingerId::from_raw(input.dwID as usize);
|
||||
let primary = util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY);
|
||||
|
||||
if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) {
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::PointerEntered {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
kind: PointerKind::Touch(finger_id),
|
||||
},
|
||||
@@ -1936,6 +2024,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: Pressed,
|
||||
position,
|
||||
button: Touch { finger_id, force: None },
|
||||
@@ -1946,6 +2035,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: Released,
|
||||
position,
|
||||
button: Touch { finger_id, force: None },
|
||||
@@ -1955,6 +2045,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id,
|
||||
event: WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary,
|
||||
position: Some(position),
|
||||
kind: PointerKind::Touch(finger_id),
|
||||
},
|
||||
@@ -1964,6 +2055,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id,
|
||||
event: WindowEvent::PointerMoved {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
source: PointerSource::Touch { finger_id, force: None },
|
||||
},
|
||||
@@ -2086,16 +2178,15 @@ unsafe fn public_window_callback_inner(
|
||||
let position = PhysicalPosition::new(x, y);
|
||||
|
||||
let window_id = WindowId::from_raw(window as usize);
|
||||
let finger_id = RootFingerId(FingerId {
|
||||
id: pointer_info.pointerId,
|
||||
primary: util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY),
|
||||
});
|
||||
let finger_id = FingerId::from_raw(pointer_info.pointerId as usize);
|
||||
let primary = util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY);
|
||||
|
||||
if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) {
|
||||
userdata.send_event(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::PointerEntered {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
kind: if let PT_TOUCH = pointer_info.pointerType {
|
||||
PointerKind::Touch(finger_id)
|
||||
@@ -2108,6 +2199,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: Pressed,
|
||||
position,
|
||||
button: if let PT_TOUCH = pointer_info.pointerType {
|
||||
@@ -2122,6 +2214,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id,
|
||||
event: WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: Released,
|
||||
position,
|
||||
button: if let PT_TOUCH = pointer_info.pointerType {
|
||||
@@ -2135,6 +2228,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id,
|
||||
event: WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary,
|
||||
position: Some(position),
|
||||
kind: if let PT_TOUCH = pointer_info.pointerType {
|
||||
PointerKind::Touch(finger_id)
|
||||
@@ -2148,6 +2242,7 @@ unsafe fn public_window_callback_inner(
|
||||
window_id,
|
||||
event: WindowEvent::PointerMoved {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
source: if let PT_TOUCH = pointer_info.pointerType {
|
||||
PointerSource::Touch { finger_id, force }
|
||||
|
||||
@@ -97,7 +97,7 @@ impl KeyEventBuilder {
|
||||
MatchResult::MessagesToDispatch(self.pending.complete_multi(key_events))
|
||||
},
|
||||
WM_KILLFOCUS => {
|
||||
// sythesize keyup events
|
||||
// synthesize keyup events
|
||||
let kbd_state = get_kbd_state();
|
||||
let key_events = Self::synthesize_kbd_state(ElementState::Released, &kbd_state);
|
||||
MatchResult::MessagesToDispatch(self.pending.complete_multi(key_events))
|
||||
@@ -333,11 +333,11 @@ impl KeyEventBuilder {
|
||||
// We are synthesizing the press event for caps-lock first for the following reasons:
|
||||
// 1. If caps-lock is *not* held down but *is* active, then we have to synthesize all
|
||||
// printable keys, respecting the caps-lock state.
|
||||
// 2. If caps-lock is held down, we could choose to sythesize its keypress after every other
|
||||
// key, in which case all other keys *must* be sythesized as if the caps-lock state was
|
||||
// be the opposite of what it currently is.
|
||||
// 2. If caps-lock is held down, we could choose to synthesize its keypress after every
|
||||
// other key, in which case all other keys *must* be sythesized as if the caps-lock state
|
||||
// was be the opposite of what it currently is.
|
||||
// --
|
||||
// For the sake of simplicity we are choosing to always sythesize
|
||||
// For the sake of simplicity we are choosing to always synthesize
|
||||
// caps-lock first, and always use the current caps-lock state
|
||||
// to determine the produced text
|
||||
if is_key_pressed!(VK_CAPITAL) {
|
||||
@@ -1079,6 +1079,20 @@ pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32>
|
||||
KeyCode::AudioVolumeDown => Some(0xe02e),
|
||||
KeyCode::AudioVolumeMute => Some(0xe020),
|
||||
KeyCode::AudioVolumeUp => Some(0xe030),
|
||||
|
||||
// Extra from Chromium sources:
|
||||
// https://chromium.googlesource.com/chromium/src.git/+/3e1a26c44c024d97dc9a4c09bbc6a2365398ca2c/ui/events/keycodes/dom/dom_code_data.inc
|
||||
KeyCode::Lang4 => Some(0x0077),
|
||||
KeyCode::Lang3 => Some(0x0078),
|
||||
KeyCode::Undo => Some(0xe008),
|
||||
KeyCode::Paste => Some(0xe00a),
|
||||
KeyCode::Cut => Some(0xe017),
|
||||
KeyCode::Copy => Some(0xe018),
|
||||
KeyCode::Eject => Some(0xe02c),
|
||||
KeyCode::Help => Some(0xe03b),
|
||||
KeyCode::Sleep => Some(0xe05f),
|
||||
KeyCode::WakeUp => Some(0xe063),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1238,6 +1252,20 @@ pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
0xe02e => KeyCode::AudioVolumeDown,
|
||||
0xe020 => KeyCode::AudioVolumeMute,
|
||||
0xe030 => KeyCode::AudioVolumeUp,
|
||||
|
||||
// Extra from Chromium sources:
|
||||
// https://chromium.googlesource.com/chromium/src.git/+/3e1a26c44c024d97dc9a4c09bbc6a2365398ca2c/ui/events/keycodes/dom/dom_code_data.inc
|
||||
0x0077 => KeyCode::Lang4,
|
||||
0x0078 => KeyCode::Lang3,
|
||||
0xe008 => KeyCode::Undo,
|
||||
0xe00a => KeyCode::Paste,
|
||||
0xe017 => KeyCode::Cut,
|
||||
0xe018 => KeyCode::Copy,
|
||||
0xe02c => KeyCode::Eject,
|
||||
0xe03b => KeyCode::Help,
|
||||
0xe05f => KeyCode::Sleep,
|
||||
0xe063 => KeyCode::WakeUp,
|
||||
|
||||
_ => return PhysicalKey::Unidentified(NativeKeyCode::Windows(scancode as u16)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ use smol_str::SmolStr;
|
||||
use windows_sys::Win32::Foundation::HWND;
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::{HMENU, WINDOW_LONG_PTR_INDEX};
|
||||
|
||||
pub(crate) use self::event_loop::{
|
||||
EventLoop, EventLoopProxy, OwnedDisplayHandle, PlatformSpecificEventLoopAttributes,
|
||||
};
|
||||
pub(crate) use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
|
||||
pub use self::icon::WinIcon as PlatformIcon;
|
||||
pub(crate) use self::icon::{SelectedCursor, WinCursor as PlatformCustomCursor, WinIcon};
|
||||
pub(crate) use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey};
|
||||
@@ -59,25 +57,6 @@ impl Default for PlatformSpecificWindowAttributes {
|
||||
unsafe impl Send for PlatformSpecificWindowAttributes {}
|
||||
unsafe impl Sync for PlatformSpecificWindowAttributes {}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FingerId {
|
||||
id: u32,
|
||||
primary: bool,
|
||||
}
|
||||
|
||||
impl FingerId {
|
||||
#[cfg(test)]
|
||||
pub const fn dummy() -> Self {
|
||||
FingerId { id: 0, primary: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl FingerId {
|
||||
pub fn is_primary(self) -> bool {
|
||||
self.primary
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_device_id(id: u32) -> DeviceId {
|
||||
DeviceId::from_raw(id as i64)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
};
|
||||
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{NotSupportedError, RequestError};
|
||||
use crate::icon::Icon;
|
||||
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
||||
@@ -106,7 +106,6 @@ impl Window {
|
||||
self.window
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub unsafe fn rwh_06_no_thread_check(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
@@ -119,7 +118,6 @@ impl Window {
|
||||
Ok(rwh_06::RawWindowHandle::Win32(window_handle))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
// TODO: Write a test once integration framework is ready to ensure that it holds.
|
||||
// If we aren't in the GUI thread, we can't return the window.
|
||||
@@ -132,7 +130,6 @@ impl Window {
|
||||
unsafe { self.rwh_06_no_thread_check() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_display_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
@@ -349,7 +346,6 @@ impl Drop for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
@@ -357,7 +353,6 @@ impl rwh_06::HasDisplayHandle for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
@@ -421,15 +416,15 @@ impl CoreWindow for Window {
|
||||
)
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
let mut position: POINT = unsafe { mem::zeroed() };
|
||||
if unsafe { ClientToScreen(self.hwnd(), &mut position) } == false.into() {
|
||||
fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
let mut rect: RECT = unsafe { mem::zeroed() };
|
||||
if unsafe { GetClientRect(self.hwnd(), &mut rect) } == false.into() {
|
||||
panic!(
|
||||
"Unexpected ClientToScreen failure: please report this error to \
|
||||
"Unexpected GetClientRect failure: please report this error to \
|
||||
rust-windowing/winit"
|
||||
)
|
||||
}
|
||||
Ok(PhysicalPosition::new(position.x, position.y))
|
||||
PhysicalPosition::new(rect.left, rect.top)
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: Position) {
|
||||
@@ -499,6 +494,10 @@ impl CoreWindow for Window {
|
||||
None
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
PhysicalInsets::new(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, size: Option<Size>) {
|
||||
self.window_state_lock().min_size = size;
|
||||
// Make windows re-check the window size bounds.
|
||||
@@ -1054,12 +1053,10 @@ impl CoreWindow for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
@@ -1074,7 +1071,7 @@ pub(super) struct InitData<'a> {
|
||||
pub window: Option<Window>,
|
||||
}
|
||||
|
||||
impl<'a> InitData<'a> {
|
||||
impl InitData<'_> {
|
||||
unsafe fn create_window(&self, window: HWND) -> Window {
|
||||
// Register for touch events if applicable
|
||||
{
|
||||
@@ -1288,7 +1285,6 @@ unsafe fn init(
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
let parent = match attributes.parent_window.as_ref().map(|handle| handle.0) {
|
||||
Some(rwh_06::RawWindowHandle::Win32(handle)) => {
|
||||
window_flags.set(WindowFlags::CHILD, true);
|
||||
@@ -1301,9 +1297,6 @@ unsafe fn init(
|
||||
None => fallback_parent(),
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "rwh_06"))]
|
||||
let parent = fallback_parent();
|
||||
|
||||
let menu = attributes.platform_specific.menu;
|
||||
let fullscreen = attributes.fullscreen.clone();
|
||||
let maximized = attributes.maximized;
|
||||
|
||||
136
src/window.rs
136
src/window.rs
@@ -7,7 +7,7 @@ pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorSource, MAX_CURSOR_SIZE};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::RequestError;
|
||||
pub use crate::icon::{BadIcon, Icon};
|
||||
use crate::monitor::{MonitorHandle, VideoModeHandle};
|
||||
@@ -67,7 +67,6 @@ pub struct WindowAttributes {
|
||||
pub window_level: WindowLevel,
|
||||
pub active: bool,
|
||||
pub cursor: Cursor,
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub(crate) parent_window: Option<SendSyncRawWindowHandle>,
|
||||
pub fullscreen: Option<Fullscreen>,
|
||||
// Platform-specific configuration.
|
||||
@@ -98,7 +97,6 @@ impl Default for WindowAttributes {
|
||||
preferred_theme: None,
|
||||
content_protected: false,
|
||||
cursor: Cursor::default(),
|
||||
#[cfg(feature = "rwh_06")]
|
||||
parent_window: None,
|
||||
active: true,
|
||||
platform_specific: Default::default(),
|
||||
@@ -113,17 +111,13 @@ impl Default for WindowAttributes {
|
||||
/// The user has to account for that when using [`WindowAttributes::with_parent_window()`],
|
||||
/// which is `unsafe`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub(crate) struct SendSyncRawWindowHandle(pub(crate) rwh_06::RawWindowHandle);
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
unsafe impl Send for SendSyncRawWindowHandle {}
|
||||
#[cfg(feature = "rwh_06")]
|
||||
unsafe impl Sync for SendSyncRawWindowHandle {}
|
||||
|
||||
impl WindowAttributes {
|
||||
/// Get the parent window stored on the attributes.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn parent_window(&self) -> Option<&rwh_06::RawWindowHandle> {
|
||||
self.parent_window.as_ref().map(|handle| &handle.0)
|
||||
}
|
||||
@@ -409,7 +403,6 @@ impl WindowAttributes {
|
||||
/// <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
|
||||
/// - **X11**: A child window is confined to the client area of its parent window.
|
||||
/// - **Android / iOS / Wayland / Web:** Unsupported.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub unsafe fn with_parent_window(
|
||||
mut self,
|
||||
@@ -581,41 +574,51 @@ pub trait Window: AsAny + Send + Sync {
|
||||
// extension trait
|
||||
fn reset_dead_keys(&self);
|
||||
|
||||
/// Returns the position of the top-left hand corner of the window's client area relative to the
|
||||
/// top-left hand corner of the desktop.
|
||||
/// The position of the top-left hand corner of the surface relative to the top-left hand corner
|
||||
/// of the window.
|
||||
///
|
||||
/// The same conditions that apply to [`Window::outer_position`] apply to this method.
|
||||
/// This, combined with [`outer_position`], can be useful for calculating the position of the
|
||||
/// surface relative to the desktop.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// This may also be useful for figuring out the size of the window's decorations (such as
|
||||
/// buttons, title, etc.), but may also not correspond to that (e.g. if the title bar is made
|
||||
/// transparent using [`with_titlebar_transparent`] on macOS, or your are drawing window
|
||||
/// decorations yourself).
|
||||
///
|
||||
/// - **iOS:** Returns the top left coordinates of the window's [safe area] in the screen space
|
||||
/// coordinate system.
|
||||
/// - **Web:** Returns the top-left coordinates relative to the viewport. _Note: this returns
|
||||
/// the same value as [`Window::outer_position`]._
|
||||
/// - **Android / Wayland:** Always returns [`RequestError::NotSupported`].
|
||||
/// This may be negative.
|
||||
///
|
||||
/// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError>;
|
||||
/// If the window does not have any decorations, and the surface is in the exact same position
|
||||
/// as the window itself, this simply returns `(0, 0)`.
|
||||
///
|
||||
/// [`outer_position`]: Self::outer_position
|
||||
#[cfg_attr(
|
||||
any(macos_platform, docsrs),
|
||||
doc = "[`with_titlebar_transparent`]: \
|
||||
crate::platform::macos::WindowAttributesExtMacOS::with_titlebar_transparent"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(any(macos_platform, docsrs)),
|
||||
doc = "[`with_titlebar_transparent`]: #only-available-on-macos"
|
||||
)]
|
||||
fn surface_position(&self) -> PhysicalPosition<i32>;
|
||||
|
||||
/// Returns the position of the top-left hand corner of the window relative to the
|
||||
/// top-left hand corner of the desktop.
|
||||
/// The position of the top-left hand corner of the window relative to the top-left hand corner
|
||||
/// of the desktop.
|
||||
///
|
||||
/// Note that the top-left hand corner of the desktop is not necessarily the same as
|
||||
/// the screen. If the user uses a desktop with multiple monitors, the top-left hand corner
|
||||
/// of the desktop is the top-left hand corner of the monitor at the top-left of the desktop.
|
||||
/// of the desktop is the top-left hand corner of the primary monitor of the desktop.
|
||||
///
|
||||
/// The coordinates can be negative if the top-left hand corner of the window is outside
|
||||
/// of the visible screen region.
|
||||
/// of the visible screen region, or on another monitor than the primary.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS:** Returns the top left coordinates of the window in the screen space coordinate
|
||||
/// system.
|
||||
/// - **Web:** Returns the top-left coordinates relative to the viewport.
|
||||
/// - **Android / Wayland:** Always returns [`RequestError::NotSupported`].
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError>;
|
||||
|
||||
/// Modifies the position of the window.
|
||||
/// Sets the position of the window on the desktop.
|
||||
///
|
||||
/// See [`Window::outer_position`] for more information about the coordinates.
|
||||
/// This automatically un-maximizes the window if it's maximized.
|
||||
@@ -645,16 +648,21 @@ pub trait Window: AsAny + Send + Sync {
|
||||
|
||||
/// Returns the size of the window's render-able surface.
|
||||
///
|
||||
/// This is the dimensions you should pass to things like Wgpu or Glutin when configuring.
|
||||
/// This is the dimensions you should pass to things like Wgpu or Glutin when configuring the
|
||||
/// surface for drawing. See [`WindowEvent::SurfaceResized`] for listening to changes to this
|
||||
/// field.
|
||||
///
|
||||
/// Note that to ensure that your content is not obscured by things such as notches or the title
|
||||
/// bar, you will likely want to only draw important content inside a specific area of the
|
||||
/// surface, see [`safe_area()`] for details.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS:** Returns the `PhysicalSize` of the window's [safe area] in screen space
|
||||
/// coordinates.
|
||||
/// - **Web:** Returns the size of the canvas element. Doesn't account for CSS [`transform`].
|
||||
///
|
||||
/// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
|
||||
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||
/// [`WindowEvent::SurfaceResized`]: crate::event::WindowEvent::SurfaceResized
|
||||
/// [`safe_area()`]: Window::safe_area
|
||||
fn surface_size(&self) -> PhysicalSize<u32>;
|
||||
|
||||
/// Request the new size for the surface.
|
||||
@@ -701,11 +709,53 @@ pub trait Window: AsAny + Send + Sync {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS:** Returns the [`PhysicalSize`] of the window in screen space coordinates.
|
||||
/// - **Web:** Returns the size of the canvas element. _Note: this returns the same value as
|
||||
/// [`Window::surface_size`]._
|
||||
fn outer_size(&self) -> PhysicalSize<u32>;
|
||||
|
||||
/// The inset area of the surface that is unobstructed.
|
||||
///
|
||||
/// On some devices, especially mobile devices, the screen is not a perfect rectangle, and may
|
||||
/// have rounded corners, notches, bezels, and so on. When drawing your content, you usually
|
||||
/// want to draw your background and other such unimportant content on the entire surface, while
|
||||
/// you will want to restrict important content such as text, interactable or visual indicators
|
||||
/// to the part of the screen that is actually visible; for this, you use the safe area.
|
||||
///
|
||||
/// The safe area is a rectangle that is defined relative to the origin at the top-left corner
|
||||
/// of the surface, and the size extending downwards to the right. The area will not extend
|
||||
/// beyond [the bounds of the surface][Window::surface_size].
|
||||
///
|
||||
/// Note that the safe area does not take occlusion from other windows into account; in a way,
|
||||
/// it is only a "hardware"-level occlusion.
|
||||
///
|
||||
/// If the entire content of the surface is visible, this returns `(0, 0, 0, 0)`.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android / Orbital / Wayland / Windows / X11:** Unimplemented, returns `(0, 0, 0, 0)`.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// Convert safe area insets to a size and a position.
|
||||
///
|
||||
/// ```
|
||||
/// use winit::dpi::{PhysicalPosition, PhysicalSize};
|
||||
///
|
||||
/// # let surface_size = dpi::PhysicalSize::new(0, 0);
|
||||
/// # #[cfg(requires_window)]
|
||||
/// let surface_size = window.surface_size();
|
||||
/// # let insets = dpi::PhysicalInsets::new(0, 0, 0, 0);
|
||||
/// # #[cfg(requires_window)]
|
||||
/// let insets = window.safe_area();
|
||||
///
|
||||
/// let origin = PhysicalPosition::new(insets.left, insets.top);
|
||||
/// let size = PhysicalSize::new(
|
||||
/// surface_size.width - insets.left - insets.right,
|
||||
/// surface_size.height - insets.top - insets.bottom,
|
||||
/// );
|
||||
/// ```
|
||||
fn safe_area(&self) -> PhysicalInsets<u32>;
|
||||
|
||||
/// Sets a minimum dimensions of the window's surface.
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -859,7 +909,7 @@ pub trait Window: AsAny + Send + Sync {
|
||||
/// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`].
|
||||
fn enabled_buttons(&self) -> WindowButtons;
|
||||
|
||||
/// Sets the window to minimized or back
|
||||
/// Minimize the window, or put it back from the minimized state.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
@@ -895,7 +945,7 @@ pub trait Window: AsAny + Send + Sync {
|
||||
/// - **iOS / Android / Web:** Unsupported.
|
||||
fn is_maximized(&self) -> bool;
|
||||
|
||||
/// Sets the window to fullscreen or back.
|
||||
/// Set the window's fullscreen state.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
@@ -978,8 +1028,8 @@ pub trait Window: AsAny + Send + Sync {
|
||||
fn set_window_icon(&self, window_icon: Option<Icon>);
|
||||
|
||||
/// Set the IME cursor editing area, where the `position` is the top left corner of that area
|
||||
/// and `size` is the size of this area starting from the position. An example of such area
|
||||
/// could be a input field in the UI or line in the editor.
|
||||
/// in surface coordinates and `size` is the size of this area starting from the position. An
|
||||
/// example of such area could be a input field in the UI or line in the editor.
|
||||
///
|
||||
/// The windowing system could place a candidate box close to that area, but try to not obscure
|
||||
/// the specified area, so the user input to it stays visible.
|
||||
@@ -1012,7 +1062,8 @@ pub trait Window: AsAny + Send + Sync {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11:** - area is not supported, only position.
|
||||
/// - **X11:** Area is not supported, only position. The bottom-right corner of the provided
|
||||
/// area is reported as the position.
|
||||
/// - **iOS / Android / Web / Orbital:** Unsupported.
|
||||
///
|
||||
/// [chinese]: https://support.apple.com/guide/chinese-input-method/use-the-candidate-window-cim12992/104/mac/12.0
|
||||
@@ -1035,8 +1086,8 @@ pub trait Window: AsAny + Send + Sync {
|
||||
///
|
||||
/// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are
|
||||
/// combined.
|
||||
/// - **iOS:** This will show / hide the soft keyboard.
|
||||
/// - **Android / Web / Orbital:** Unsupported.
|
||||
/// - **iOS / Android:** This will show / hide the soft keyboard.
|
||||
/// - **Web / Orbital:** Unsupported.
|
||||
/// - **X11**: Enabling IME will disable dead keys reporting during compose.
|
||||
///
|
||||
/// [`Ime`]: crate::event::WindowEvent::Ime
|
||||
@@ -1209,7 +1260,7 @@ pub trait Window: AsAny + Send + Sync {
|
||||
/// - **iOS / Android / Web:** Always returns an [`RequestError::NotSupported`].
|
||||
fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError>;
|
||||
|
||||
/// Show [window menu] at a specified position .
|
||||
/// Show [window menu] at a specified position in surface coordinates.
|
||||
///
|
||||
/// This is the context menu that is normally shown when interacting with
|
||||
/// the title bar. This is useful when implementing custom decorations.
|
||||
@@ -1276,11 +1327,9 @@ pub trait Window: AsAny + Send + Sync {
|
||||
fn primary_monitor(&self) -> Option<MonitorHandle>;
|
||||
|
||||
/// Get the raw-window-handle v0.6 display handle.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle;
|
||||
|
||||
/// Get the raw-window-handle v0.6 window handle.
|
||||
#[cfg(feature = "rwh_06")]
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle;
|
||||
}
|
||||
|
||||
@@ -1306,14 +1355,12 @@ impl std::hash::Hash for dyn Window + '_ {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasDisplayHandle for dyn Window + '_ {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
self.rwh_06_display_handle().display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl rwh_06::HasWindowHandle for dyn Window + '_ {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
self.rwh_06_window_handle().window_handle()
|
||||
@@ -1386,6 +1433,9 @@ impl From<ResizeDirection> for CursorIcon {
|
||||
/// Fullscreen modes.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
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),
|
||||
|
||||
/// Providing `None` to `Borderless` will fullscreen on the current monitor.
|
||||
|
||||
@@ -5,3 +5,6 @@ TME_LEAVE = "TME_LEAVE" # From windows_sys::Win32::UI::Input::Keyboa
|
||||
XF86_Calculater = "XF86_Calculater" # From xkbcommon_dl::keysyms::XF86_Calculater
|
||||
ptd = "ptd" # From windows_sys::Win32::System::Com::FORMATETC { ptd, ..}
|
||||
requestor = "requestor" # From x11_dl::xlib::XSelectionEvent { requestor ..}
|
||||
|
||||
[files]
|
||||
extend-exclude = ["*.drawio"]
|
||||
|
||||
Reference in New Issue
Block a user