Compare commits

..

1 Commits

Author SHA1 Message Date
Mads Marquart
e66eba38f5 Add OwnedWindowHandle to avoid lifetime on WindowHandle<'_> 2023-12-24 00:46:13 +01:00
160 changed files with 7307 additions and 9764 deletions

16
.github/CODEOWNERS vendored
View File

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

View File

@@ -48,15 +48,6 @@ jobs:
include: include:
- toolchain: '1.70.0' - toolchain: '1.70.0'
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' } platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
- toolchain: 'nightly'
platform: {
name: 'web Atomic',
target: wasm32-unknown-unknown,
os: ubuntu-latest,
options: '-Zbuild-std=panic_abort,std',
rustflags: '-Ctarget-feature=+atomics,+bulk-memory',
components: rust-src,
}
env: env:
# Set more verbose terminal output # Set more verbose terminal output
@@ -64,7 +55,8 @@ jobs:
RUST_BACKTRACE: 1 RUST_BACKTRACE: 1
# Faster compilation and error on warnings # Faster compilation and error on warnings
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}' RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings'
RUSTDOCFLAGS: '--deny=warnings'
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }} OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
CMD: ${{ matrix.platform.cmd }} CMD: ${{ matrix.platform.cmd }}
@@ -89,7 +81,7 @@ jobs:
- name: Generate lockfile - name: Generate lockfile
# Also updates the crates.io index # Also updates the crates.io index
run: cargo generate-lockfile && cargo update -p ahash --precise 0.8.7 run: cargo generate-lockfile
- name: Install GCC Multilib - name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686') if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
@@ -117,12 +109,10 @@ jobs:
with: with:
toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }} toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
targets: ${{ matrix.platform.target }} targets: ${{ matrix.platform.target }}
components: clippy, ${{ matrix.platform.components }} components: clippy
- name: Check documentation - name: Check documentation
run: cargo doc --no-deps $OPTIONS --document-private-items run: cargo doc --no-deps $OPTIONS --document-private-items
env:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }}'
- name: Build crate - name: Build crate
run: cargo $CMD build $OPTIONS run: cargo $CMD build $OPTIONS
@@ -161,12 +151,6 @@ jobs:
matrix.toolchain != '1.70.0' matrix.toolchain != '1.70.0'
run: cargo $CMD test $OPTIONS --features serde run: cargo $CMD test $OPTIONS --features serde
- name: Check docs.rs documentation
if: matrix.toolchain == 'nightly'
run: cargo doc --no-deps $OPTIONS --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
env:
RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs'
# See restore step above # See restore step above
- name: Save cache of cargo folder - name: Save cache of cargo folder
uses: actions/cache/save@v3 uses: actions/cache/save@v3

View File

@@ -1,50 +0,0 @@
name: Docs
on:
push:
branches: [master]
jobs:
docs:
name: Documentation
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}winit
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- name: Run Rustdoc
env:
RUSTDOCFLAGS: --crate-version master --cfg=docsrs
run: |
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=rwh_04,rwh_05,rwh_06,serde,mint,android-native-activity
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Fix permissions
run: |
chmod -c -R +rX "target/doc" | while read line; do
echo "::warning title=Invalid file permissions automatically fixed::$line"
done
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: target/doc
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View File

@@ -11,97 +11,18 @@ Unreleased` header.
# Unreleased # Unreleased
- On X11, don't require XIM to run.
- Fix compatibility with 32-bit platforms without 64-bit atomics.
- On X11, fix swapped instance and general class names.
- **Breaking:** Removed unnecessary generic parameter `T` from `EventLoopWindowTarget`.
- On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example. - On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example.
- **Breaking:** Remove `Window::set_cursor_icon` - Add `Window::set_custom_cursor`
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a `CursorIcon` or `CustomCursor`
- Add `CustomCursor` - Add `CustomCursor`
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data. - Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs. - Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated cursors from other `CustomCursor`s.
- On macOS, add services menu. - On macOS, add services menu.
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation. - **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
- On Web, fix setting cursor icon overriding cursor visibility. - On Web, fix setting cursor icon overriding cursor visibility.
- On Web, fix context menu not being disabled by `with_prevent_default(true)`.
- **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`. - **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
- **Breaking:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available. - **Breaking:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available.
- **Breaking:** Bump MSRV from `1.65` to `1.70`. - **Breaking:** Bump MSRV from `1.65` to `1.70`.
- On Web, add the ability to toggle calling `Event.preventDefault()` on `Window`.
- **Breaking:** Remove `WindowAttributes::fullscreen()` and expose as field directly.
- **Breaking:** Rename `VideoMode` to `VideoModeHandle` to represent that it doesn't hold static data.
- **Breaking:** No longer export `platform::x11::XNotSupported`.
- **Breaking:** Renamed `platform::x11::XWindowType` to `platform::x11::WindowType`.
- Add the `OwnedDisplayHandle` type for allowing safe display handle usage outside of trivial cases.
- **Breaking:** Rename `TouchpadMagnify` to `PinchGesture`, `SmartMagnify` to `DoubleTapGesture` and `TouchpadRotate` to `RotationGesture` to represent the action rather than the intent.
- on iOS, add detection support for `PinchGesture`, `DoubleTapGesture` and `RotationGesture`.
- on Windows: add `with_system_backdrop`, `with_border_color`, `with_title_background_color`, `with_title_text_color` and `with_corner_preference`
- On Windows, Remove `WS_CAPTION`, `WS_BORDER` and `WS_EX_WINDOWEDGE` styles for child windows without decorations.
- On Windows, fixed a race condition when sending an event through the loop proxy.
- **Breaking:** Removed `EventLoopError::AlreadyRunning`, which can't happen as it is already prevented by the type system.
- On Wayland, disable `Occluded` event handling.
- Added `EventLoop::builder`, which is intended to replace the (now deprecated) `EventLoopBuilder::new`.
- **Breaking:** Changed the signature of `EventLoop::with_user_event` to return a builder.
- **Breaking:** Removed `EventLoopBuilder::with_user_event`, the functionality is now available in `EventLoop::with_user_event`.
- Add `Window::builder`, which is intended to replace the (now deprecated) `WindowBuilder::new`.
- On X11, reload dpi on `_XSETTINGS_SETTINGS` update.
- On X11, fix deadlock when adjusting DPI and resizing at the same time.
- On Wayland, fix `Focused(false)` being send when other seats still have window focused.
- On Wayland, fix `Window::set_{min,max}_inner_size` not always applied.
- On Windows, fix inconsistent resizing behavior with multi-monitor setups when repositioning outside the event loop.
- On Wayland, fix `WAYLAND_SOCKET` not used when detecting platform.
- On Orbital, fix `logical_key` and `text` not reported in `KeyEvent`.
- On Orbital, implement `KeyEventExtModifierSupplement`.
- On Orbital, map keys to `NamedKey` when possible.
- On Orbital, implement `set_cursor_grab`.
- On Orbital, implement `set_cursor_visible`.
- On Orbital, implement `drag_window`.
- On Orbital, implement `drag_resize_window`.
- On Orbital, implement `set_transparent`.
- On Orbital, implement `set_visible`.
- On Orbital, implement `is_visible`.
- On Orbital, implement `set_resizable`.
- On Orbital, implement `is_resizable`.
- On Orbital, implement `set_maximized`.
- On Orbital, implement `is_maximized`.
- On Orbital, implement `set_decorations`.
- On Orbital, implement `is_decorated`.
- On Orbital, implement `set_window_level`.
- On Orbital, emit `DeviceEvent::MouseMotion`.
- On Wayland, fix title in CSD not updated from `AboutToWait`.
# 0.29.10
- On Web, account for canvas being focused already before event loop starts.
- On Web, increase cursor position accuracy.
# 0.29.9
- On X11, fix `NotSupported` error not propagated when creating event loop.
- On Wayland, fix resize not issued when scale changes
- On X11 and Wayland, fix arrow up on keypad reported as `ArrowLeft`.
- On macOS, report correct logical key when Ctrl or Cmd is pressed.
# 0.29.8
- On X11, fix IME input lagging behind.
- On X11, fix `ModifiersChanged` not sent from xdotool-like input
- On X11, fix keymap not updated from xmodmap.
- On X11, reduce the amount of time spent fetching screen resources.
- On Wayland, fix `Window::request_inner_size` being overwritten by resize.
- On Wayland, fix `Window::inner_size` not using the correct rounding.
# 0.29.7
- On X11, fix `Xft.dpi` reload during runtime.
- On X11, fix window minimize.
# 0.29.6
- On Web, fix context menu not being disabled by `with_prevent_default(true)`.
- On Wayland, fix `WindowEvent::Destroyed` not being delivered after destroying window.
- Fix `EventLoopExtRunOnDemand::run_on_demand` not working for consequent invocation
# 0.29.5 # 0.29.5
@@ -126,7 +47,6 @@ Unreleased` header.
- On macOS, send a `Resized` event after each `ScaleFactorChanged` event. - On macOS, send a `Resized` event after each `ScaleFactorChanged` event.
- On Wayland, fix `wl_surface` being destroyed before associated objects. - On Wayland, fix `wl_surface` being destroyed before associated objects.
- On macOS, fix assertion when pressing `Fn` key. - On macOS, fix assertion when pressing `Fn` key.
- On Windows, add `WindowBuilderExtWindows::with_clip_children` to control `WS_CLIPCHILDREN` style.
# 0.29.3 # 0.29.3

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "winit" name = "winit"
version = "0.29.10" version = "0.29.5"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"] authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library." description = "Cross-platform window creation library."
edition = "2021" edition = "2021"
@@ -18,7 +18,6 @@ features = [
"rwh_05", "rwh_05",
"rwh_06", "rwh_06",
"serde", "serde",
"mint",
# Enabled to get docs to compile # Enabled to get docs to compile
"android-native-activity", "android-native-activity",
] ]
@@ -37,7 +36,7 @@ targets = [
"x86_64-apple-ios", "x86_64-apple-ios",
# Android # Android
"aarch64-linux-android", "aarch64-linux-android",
# Web # WebAssembly
"wasm32-unknown-unknown", "wasm32-unknown-unknown",
] ]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
@@ -58,7 +57,7 @@ rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"] rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
[build-dependencies] [build-dependencies]
cfg_aliases = "0.2.0" cfg_aliases = "0.1.1"
[dependencies] [dependencies]
bitflags = "2" bitflags = "2"
@@ -67,7 +66,7 @@ log = "0.4"
mint = { version = "0.5.6", optional = true } mint = { version = "0.5.6", optional = true }
once_cell = "1.12" once_cell = "1.12"
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true } rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true } rwh_05 = { package = "raw-window-handle", version = "0.5", features = ["std"], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
serde = { version = "1", optional = true, features = ["serde_derive"] } serde = { version = "1", optional = true, features = ["serde_derive"] }
smol_str = "0.2.0" smol_str = "0.2.0"
@@ -78,7 +77,7 @@ simple_logger = { version = "4.2.0", default_features = false }
winit = { path = ".", features = ["rwh_05"] } winit = { path = ".", features = ["rwh_05"] }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
softbuffer = { version = "0.3.0", default-features = false, features = ["x11", "x11-dlopen", "wayland", "wayland-dlopen"] } softbuffer = "0.3.0"
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.5.0" android-activity = "0.5.0"
@@ -174,7 +173,7 @@ features = [
] ]
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies] [target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
ahash = { version = "0.8.7", features = ["no-rng"], optional = true } ahash = { version = "0.8.3", features = ["no-rng"], optional = true }
bytemuck = { version = "1.13.1", default-features = false, optional = true } bytemuck = { version = "1.13.1", default-features = false, optional = true }
calloop = "0.12.3" calloop = "0.12.3"
libc = "0.2.64" libc = "0.2.64"
@@ -192,8 +191,8 @@ x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsaf
xkbcommon-dl = "0.4.0" xkbcommon-dl = "0.4.0"
[target.'cfg(target_os = "redox")'.dependencies] [target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.47", default-features = false } orbclient = { version = "0.3.42", default-features = false }
redox_syscall = "0.4.1" redox_syscall = "0.3"
[target.'cfg(target_family = "wasm")'.dependencies.web_sys] [target.'cfg(target_family = "wasm")'.dependencies.web_sys]
package = "web-sys" package = "web-sys"
@@ -205,7 +204,6 @@ features = [
'console', 'console',
'CssStyleDeclaration', 'CssStyleDeclaration',
'Document', 'Document',
'DomException',
'DomRect', 'DomRect',
'DomRectReadOnly', 'DomRectReadOnly',
'Element', 'Element',
@@ -241,15 +239,11 @@ features = [
] ]
[target.'cfg(target_family = "wasm")'.dependencies] [target.'cfg(target_family = "wasm")'.dependencies]
atomic-waker = "1"
js-sys = "0.3.64" js-sys = "0.3.64"
pin-project = "1"
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4" wasm-bindgen-futures = "0.4"
web-time = "1" web-time = "0.2"
[target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies]
atomic-waker = "1"
concurrent-queue = { version = "2", default-features = false }
[target.'cfg(target_family = "wasm")'.dev-dependencies] [target.'cfg(target_family = "wasm")'.dev-dependencies]
console_log = "1" console_log = "1"
@@ -259,7 +253,3 @@ web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
members = [ members = [
"run-wasm", "run-wasm",
] ]
[[example]]
doc-scrape-examples = true
name = "window"

View File

@@ -126,11 +126,6 @@ If your PR makes notable changes to Winit's features, please update this section
* Setting a menu bar * Setting a menu bar
* `WS_EX_NOREDIRECTIONBITMAP` support * `WS_EX_NOREDIRECTIONBITMAP` support
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme * 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 ### macOS
* Window activation policy * Window activation policy

View File

@@ -2,13 +2,11 @@
[![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit) [![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit)
[![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit) [![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit)
[![Master Docs](https://img.shields.io/github/actions/workflow/status/rust-windowing/winit/docs.yml?branch=master&label=master%20docs
)](https://rust-windowing.github.io/winit/winit/index.html)
[![CI Status](https://github.com/rust-windowing/winit/workflows/CI/badge.svg)](https://github.com/rust-windowing/winit/actions) [![CI Status](https://github.com/rust-windowing/winit/workflows/CI/badge.svg)](https://github.com/rust-windowing/winit/actions)
```toml ```toml
[dependencies] [dependencies]
winit = "0.29.10" winit = "0.29.5"
``` ```
## [Documentation](https://docs.rs/winit) ## [Documentation](https://docs.rs/winit)
@@ -19,9 +17,10 @@ For features _outside_ the scope of winit, see [Are we GUI Yet?](https://arewegu
## Contact Us ## Contact Us
Join us in our [![Matrix](https://img.shields.io/badge/Matrix-%23rust--windowing%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#rust-windowing:matrix.org) room. If you don't get an answer there, try [![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit). Join us in any of these:
The maintainers have a meeting every friday at UTC 15. The meeting notes can be found [here](https://hackmd.io/@winit-meetings). [![Matrix](https://img.shields.io/badge/Matrix-%23rust--windowing%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#rust-windowing:matrix.org)
[![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit)
## Usage ## Usage
@@ -75,7 +74,7 @@ same MSRV policy.
Note that windows don't appear on Wayland until you draw/present to them. Note that windows don't appear on Wayland until you draw/present to them.
#### Web #### WebAssembly
To run the web example: `cargo run-wasm --example web` To run the web example: `cargo run-wasm --example web`
@@ -86,7 +85,7 @@ either [provide Winit with a `<canvas>` element][web with_canvas], or [let Winit
create a `<canvas>` element which you can then retrieve][web canvas getter] and create a `<canvas>` element which you can then retrieve][web canvas getter] and
insert it into the DOM yourself. insert it into the DOM yourself.
For the example code using Winit on Web, check out the [web example]. For For the example code using Winit with WebAssembly, check out the [web example]. For
information on using Rust on WebAssembly, check out the [Rust and WebAssembly information on using Rust on WebAssembly, check out the [Rust and WebAssembly
book]. book].
@@ -151,13 +150,13 @@ class. Your application _must_ specify the base class it needs via a feature fla
[agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries [agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
[Gradle]: https://developer.android.com/studio/build [Gradle]: https://developer.android.com/studio/build
For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples). For more details, refer to these `android-activity` [example applications](https://github.com/rib/android-activity/tree/main/examples).
##### Converting from `ndk-glue` to `android-activity` ##### Converting from `ndk-glue` to `android-activity`
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be: If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
1. Remove `ndk-glue` from your `Cargo.toml` 1. Remove `ndk-glue` from your `Cargo.toml`
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.10", features = [ "android-native-activity" ] }` 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.5", features = [ "android-native-activity" ] }`
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above). 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above). 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).

View File

@@ -8,7 +8,7 @@ fn main() {
cfg_aliases! { cfg_aliases! {
// Systems. // Systems.
android_platform: { target_os = "android" }, android_platform: { target_os = "android" },
web_platform: { all(target_family = "wasm", target_os = "unknown") }, wasm_platform: { all(target_family = "wasm", not(target_os = "emscripten")) },
macos_platform: { target_os = "macos" }, macos_platform: { target_os = "macos" },
ios_platform: { target_os = "ios" }, ios_platform: { target_os = "ios" },
windows_platform: { target_os = "windows" }, windows_platform: { target_os = "windows" },
@@ -17,8 +17,8 @@ fn main() {
redox: { target_os = "redox" }, redox: { target_os = "redox" },
// Native displays. // Native displays.
x11_platform: { all(feature = "x11", free_unix, not(redox)) }, x11_platform: { all(feature = "x11", free_unix, not(wasm), not(redox)) },
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) }, wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
orbital_platform: { redox }, orbital_platform: { redox },
} }
} }

View File

@@ -11,5 +11,4 @@ disallowed-methods = [
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" }, { path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" }, { path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
{ path = "icrate::AppKit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." }, { path = "icrate::AppKit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
{ path = "icrate::AppKit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
] ]

View File

@@ -34,6 +34,7 @@ skip = [
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this { name = "raw-window-handle" }, # we intentionally have multiple versions of this
{ name = "bitflags" }, # the ecosystem is in the process of migrating. { name = "bitflags" }, # the ecosystem is in the process of migrating.
{ name = "libloading" }, # x11rb uses a different version until the next update { name = "libloading" }, # x11rb uses a different version until the next update
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
] ]
skip-tree = [] skip-tree = []

View File

@@ -17,23 +17,22 @@ fn main() -> Result<(), impl std::error::Error> {
dpi::{LogicalPosition, LogicalSize, Position}, dpi::{LogicalPosition, LogicalSize, Position},
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget}, event_loop::{EventLoop, EventLoopWindowTarget},
raw_window_handle::HasRawWindowHandle, raw_window_handle::HasWindowHandle,
window::{Window, WindowId}, window::{Window, WindowBuilder, WindowId},
}; };
fn spawn_child_window( fn spawn_child_window(
parent: &Window, parent: &Window,
event_loop: &EventLoopWindowTarget, event_loop: &EventLoopWindowTarget<()>,
windows: &mut HashMap<WindowId, Window>, windows: &mut HashMap<WindowId, Window>,
) { ) {
let parent = parent.raw_window_handle().unwrap(); let parent = parent.window_handle().unwrap();
let mut builder = Window::builder() let mut builder = WindowBuilder::new()
.with_title("child window") .with_title("child window")
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32)) .with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_visible(true); .with_visible(true);
// `with_parent_window` is unsafe. Parent window must be a valid window. builder = builder.with_parent_window(Some(parent));
builder = unsafe { builder.with_parent_window(Some(parent)) };
let child_window = builder.build(event_loop).unwrap(); let child_window = builder.build(event_loop).unwrap();
let id = child_window.id(); let id = child_window.id();
@@ -44,7 +43,7 @@ fn main() -> Result<(), impl std::error::Error> {
let mut windows = HashMap::new(); let mut windows = HashMap::new();
let event_loop: EventLoop<()> = EventLoop::new().unwrap(); let event_loop: EventLoop<()> = EventLoop::new().unwrap();
let parent_window = Window::builder() let parent_window = WindowBuilder::new()
.with_title("parent window") .with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32)) .with_inner_size(LogicalSize::new(640.0f32, 480.0f32))

View File

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

View File

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

View File

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

View File

@@ -1,24 +1,15 @@
#![allow(clippy::single_match, clippy::disallowed_methods)] #![allow(clippy::single_match, clippy::disallowed_methods)]
#[cfg(not(web_platform))] #[cfg(not(wasm_platform))]
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget}, event_loop::{EventLoop, EventLoopWindowTarget},
keyboard::Key, keyboard::Key,
window::{CursorIcon, CustomCursor, Window}, window::{CustomCursor, WindowBuilder},
};
#[cfg(web_platform)]
use {
std::sync::atomic::{AtomicU64, Ordering},
std::time::Duration,
winit::platform::web::CustomCursorExtWebSys,
}; };
#[cfg(web_platform)] fn decode_cursor<T>(bytes: &[u8], window_target: &EventLoopWindowTarget<T>) -> CustomCursor {
static COUNTER: AtomicU64 = AtomicU64::new(0);
fn decode_cursor(bytes: &[u8], window_target: &EventLoopWindowTarget) -> CustomCursor {
let img = image::load_from_memory(bytes).unwrap().to_rgba8(); let img = image::load_from_memory(bytes).unwrap().to_rgba8();
let samples = img.into_flat_samples(); let samples = img.into_flat_samples();
let (_, w, h) = samples.extents(); let (_, w, h) = samples.extents();
@@ -28,22 +19,22 @@ fn decode_cursor(bytes: &[u8], window_target: &EventLoopWindowTarget) -> CustomC
builder.build(window_target) builder.build(window_target)
} }
#[cfg(not(web_platform))] #[cfg(not(wasm_platform))]
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
#[cfg(not(web_platform))] #[cfg(not(wasm_platform))]
SimpleLogger::new() SimpleLogger::new()
.with_level(log::LevelFilter::Info) .with_level(log::LevelFilter::Info)
.init() .init()
.unwrap(); .unwrap();
#[cfg(web_platform)] #[cfg(wasm_platform)]
console_log::init_with_level(log::Level::Debug).unwrap(); console_log::init_with_level(log::Level::Debug).unwrap();
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let builder = Window::builder().with_title("A fantastic window!"); let builder = WindowBuilder::new().with_title("A fantastic window!");
#[cfg(web_platform)] #[cfg(wasm_platform)]
let builder = { let builder = {
use winit::platform::web::WindowBuilderExtWebSys; use winit::platform::web::WindowBuilderExtWebSys;
builder.with_append(true) builder.with_append(true)
@@ -56,7 +47,6 @@ fn main() -> Result<(), impl std::error::Error> {
let custom_cursors = [ let custom_cursors = [
decode_cursor(include_bytes!("data/cross.png"), &event_loop), decode_cursor(include_bytes!("data/cross.png"), &event_loop),
decode_cursor(include_bytes!("data/cross2.png"), &event_loop), decode_cursor(include_bytes!("data/cross2.png"), &event_loop),
decode_cursor(include_bytes!("data/gradient.png"), &event_loop),
]; ];
event_loop.run(move |event, _elwt| match event { event_loop.run(move |event, _elwt| match event {
@@ -72,65 +62,26 @@ fn main() -> Result<(), impl std::error::Error> {
} => match key.as_ref() { } => match key.as_ref() {
Key::Character("1") => { Key::Character("1") => {
log::debug!("Setting cursor to {:?}", cursor_idx); log::debug!("Setting cursor to {:?}", cursor_idx);
window.set_cursor(custom_cursors[cursor_idx].clone()); window.set_custom_cursor(&custom_cursors[cursor_idx]);
cursor_idx = (cursor_idx + 1) % 3; cursor_idx = (cursor_idx + 1) % 2;
} }
Key::Character("2") => { Key::Character("2") => {
log::debug!("Setting cursor icon to default"); log::debug!("Setting cursor icon to default");
window.set_cursor(CursorIcon::default()); window.set_cursor_icon(Default::default());
} }
Key::Character("3") => { Key::Character("3") => {
cursor_visible = !cursor_visible; cursor_visible = !cursor_visible;
log::debug!("Setting cursor visibility to {:?}", cursor_visible); log::debug!("Setting cursor visibility to {:?}", cursor_visible);
window.set_cursor_visible(cursor_visible); window.set_cursor_visible(cursor_visible);
} }
#[cfg(web_platform)]
Key::Character("4") => {
log::debug!("Setting cursor to a random image from an URL");
window.set_cursor(
CustomCursor::from_url(
format!(
"https://picsum.photos/128?random={}",
COUNTER.fetch_add(1, Ordering::Relaxed)
),
64,
64,
)
.build(_elwt),
);
}
#[cfg(web_platform)]
Key::Character("5") => {
log::debug!("Setting cursor to an animation");
window.set_cursor(
CustomCursor::from_animation(
Duration::from_secs(3),
vec![
custom_cursors[0].clone(),
custom_cursors[1].clone(),
CustomCursor::from_url(
format!(
"https://picsum.photos/128?random={}",
COUNTER.fetch_add(1, Ordering::Relaxed)
),
64,
64,
)
.build(_elwt),
],
)
.unwrap()
.build(_elwt),
);
}
_ => {} _ => {}
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
#[cfg(not(web_platform))] #[cfg(not(wasm_platform))]
fill::fill_window(&window); fill::fill_window(&window);
} }
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
#[cfg(not(web_platform))] #[cfg(not(wasm_platform))]
_elwt.exit(); _elwt.exit();
} }
_ => (), _ => (),

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 B

View File

@@ -5,7 +5,7 @@ use winit::{
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent}, event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
keyboard::Key, keyboard::Key,
window::{Window, WindowId}, window::{Window, WindowBuilder, WindowId},
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -15,8 +15,8 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window_1 = Window::builder().build(&event_loop).unwrap(); let window_1 = WindowBuilder::new().build(&event_loop).unwrap();
let window_2 = Window::builder().build(&event_loop).unwrap(); let window_2 = WindowBuilder::new().build(&event_loop).unwrap();
let mut switched = false; let mut switched = false;
let mut entered_id = window_2.id(); let mut entered_id = window_2.id();

View File

@@ -3,14 +3,14 @@
//! Example for focusing a window. //! Example for focusing a window.
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
#[cfg(not(web_platform))] #[cfg(not(wasm_platform))]
use std::time; use std::time;
#[cfg(web_platform)] #[cfg(wasm_platform)]
use web_time as time; use web_time as time;
use winit::{ use winit::{
event::{Event, StartCause, WindowEvent}, event::{Event, StartCause, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
window::Window, window::WindowBuilder,
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = Window::builder() let window = WindowBuilder::new()
.with_title("A fantastic window!") .with_title("A fantastic window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(&event_loop) .build(&event_loop)

View File

@@ -1,11 +1,11 @@
#![allow(clippy::single_match)] #![allow(clippy::single_match)]
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::dpi::LogicalSize; use winit::dpi::PhysicalSize;
use winit::event::{ElementState, Event, KeyEvent, WindowEvent}; use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
use winit::keyboard::{Key, NamedKey}; use winit::keyboard::{Key, NamedKey};
use winit::window::{Fullscreen, Window}; use winit::window::{Fullscreen, WindowBuilder};
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use winit::platform::macos::WindowExtMacOS; use winit::platform::macos::WindowExtMacOS;
@@ -22,7 +22,7 @@ fn main() -> Result<(), impl std::error::Error> {
let mut with_min_size = false; let mut with_min_size = false;
let mut with_max_size = false; let mut with_max_size = false;
let window = Window::builder() let window = WindowBuilder::new()
.with_title("Hello world!") .with_title("Hello world!")
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
@@ -126,7 +126,7 @@ fn main() -> Result<(), impl std::error::Error> {
"i" => { "i" => {
with_min_size = !with_min_size; with_min_size = !with_min_size;
let min_size = if with_min_size { let min_size = if with_min_size {
Some(LogicalSize::new(100, 100)) Some(PhysicalSize::new(100, 100))
} else { } else {
None None
}; };
@@ -139,7 +139,7 @@ fn main() -> Result<(), impl std::error::Error> {
"a" => { "a" => {
with_max_size = !with_max_size; with_max_size = !with_max_size;
let max_size = if with_max_size { let max_size = if with_max_size {
Some(LogicalSize::new(200, 200)) Some(PhysicalSize::new(200, 200))
} else { } else {
None None
}; };

View File

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

View File

@@ -7,7 +7,7 @@ use winit::{
event::{ElementState, Event, Ime, WindowEvent}, event::{ElementState, Event, Ime, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
keyboard::NamedKey, keyboard::NamedKey,
window::{ImePurpose, Window}, window::{ImePurpose, WindowBuilder},
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -26,7 +26,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = Window::builder() let window = WindowBuilder::new()
.with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64)) .with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64))
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();

View File

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

View File

@@ -3,12 +3,12 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::dpi::{PhysicalPosition, PhysicalSize}; use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::monitor::MonitorHandle; use winit::monitor::MonitorHandle;
use winit::{event_loop::EventLoop, window::Window}; use winit::{event_loop::EventLoop, window::WindowBuilder};
fn main() { fn main() {
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = Window::builder().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
if let Some(mon) = window.primary_monitor() { if let Some(mon) = window.primary_monitor() {
print_info("Primary output", mon); print_info("Primary output", mon);

View File

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

View File

@@ -1,6 +1,6 @@
#![allow(clippy::single_match)] #![allow(clippy::single_match)]
#[cfg(not(web_platform))] #[cfg(not(wasm_platform))]
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
use std::{collections::HashMap, sync::mpsc, thread, time::Duration}; use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
@@ -10,7 +10,7 @@ fn main() -> Result<(), impl std::error::Error> {
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
keyboard::{Key, ModifiersState, NamedKey}, keyboard::{Key, ModifiersState, NamedKey},
window::{CursorGrabMode, CursorIcon, Fullscreen, Window, WindowLevel}, window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel},
}; };
const WINDOW_COUNT: usize = 3; const WINDOW_COUNT: usize = 3;
@@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT); let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
for _ in 0..WINDOW_COUNT { for _ in 0..WINDOW_COUNT {
let window = Window::builder() let window = WindowBuilder::new()
.with_inner_size(WINDOW_SIZE) .with_inner_size(WINDOW_SIZE)
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
@@ -84,7 +84,7 @@ fn main() -> Result<(), impl std::error::Error> {
"1" => window.set_window_level(WindowLevel::AlwaysOnTop), "1" => window.set_window_level(WindowLevel::AlwaysOnTop),
"2" => window.set_window_level(WindowLevel::AlwaysOnBottom), "2" => window.set_window_level(WindowLevel::AlwaysOnBottom),
"3" => window.set_window_level(WindowLevel::Normal), "3" => window.set_window_level(WindowLevel::Normal),
"c" => window.set_cursor(match state { "c" => window.set_cursor_icon(match state {
true => CursorIcon::Progress, true => CursorIcon::Progress,
false => CursorIcon::Default, false => CursorIcon::Default,
}), }),
@@ -96,13 +96,21 @@ fn main() -> Result<(), impl std::error::Error> {
)), )),
(false, _) => None, (false, _) => None,
}), }),
ch @ ("g" | "l") => { "l" if state => {
let mode = match (ch, state) { if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked)
("l", true) => CursorGrabMode::Locked, {
("g", true) => CursorGrabMode::Confined, println!("error: {err}");
(_, _) => CursorGrabMode::None, }
}; }
if let Err(err) = window.set_cursor_grab(mode) { "g" if state => {
if let Err(err) =
window.set_cursor_grab(CursorGrabMode::Confined)
{
println!("error: {err}");
}
}
"g" | "l" if !state => {
if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
println!("error: {err}"); println!("error: {err}");
} }
} }
@@ -115,6 +123,10 @@ fn main() -> Result<(), impl std::error::Error> {
println!("-> inner_size : {:?}", window.inner_size()); println!("-> inner_size : {:?}", window.inner_size());
println!("-> fullscreen : {:?}", window.fullscreen()); println!("-> fullscreen : {:?}", window.fullscreen());
} }
"l" => window.set_min_inner_size(match state {
true => Some(WINDOW_SIZE),
false => None,
}),
"m" => window.set_maximized(state), "m" => window.set_maximized(state),
"p" => window.set_outer_position({ "p" => window.set_outer_position({
let mut position = window.outer_position().unwrap(); let mut position = window.outer_position().unwrap();
@@ -128,26 +140,12 @@ fn main() -> Result<(), impl std::error::Error> {
"s" => { "s" => {
let _ = window.request_inner_size(match state { let _ = window.request_inner_size(match state {
true => PhysicalSize::new( true => PhysicalSize::new(
WINDOW_SIZE.width + 50, WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 50, WINDOW_SIZE.height + 100,
), ),
false => WINDOW_SIZE, false => WINDOW_SIZE,
}); });
} }
"k" => window.set_min_inner_size(match state {
true => Some(PhysicalSize::new(
WINDOW_SIZE.width - 100,
WINDOW_SIZE.height - 100,
)),
false => None,
}),
"o" => window.set_max_inner_size(match state {
true => Some(PhysicalSize::new(
WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 100,
)),
false => None,
}),
"w" => { "w" => {
if let Size::Physical(size) = WINDOW_SIZE.into() { if let Size::Physical(size) = WINDOW_SIZE.into() {
window window
@@ -205,7 +203,7 @@ fn main() -> Result<(), impl std::error::Error> {
}) })
} }
#[cfg(web_platform)] #[cfg(wasm_platform)]
fn main() { fn main() {
panic!("Example not supported on Web"); panic!("Example not supported on Wasm");
} }

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ mod example {
use winit::platform::startup_notify::{ use winit::platform::startup_notify::{
EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify, EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify,
}; };
use winit::window::{Window, WindowId}; use winit::window::{Window, WindowBuilder, WindowId};
pub(super) fn main() -> Result<(), impl std::error::Error> { pub(super) fn main() -> Result<(), impl std::error::Error> {
// Create the event loop and get the activation token. // Create the event loop and get the activation token.
@@ -84,7 +84,8 @@ mod example {
if current_token.is_some() || create_first_window { if current_token.is_some() || create_first_window {
// Create the initial window. // Create the initial window.
let window = { let window = {
let mut builder = Window::builder().with_title(format!("Window {}", counter)); let mut builder =
WindowBuilder::new().with_title(format!("Window {}", counter));
if let Some(token) = current_token.take() { if let Some(token) = current_token.take() {
println!("Creating a window with token {token:?}"); println!("Creating a window with token {token:?}");

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,30 +7,17 @@
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could //! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
//! also be used to fill the window buffer, but they are more complicated to use. //! also be used to fill the window buffer, but they are more complicated to use.
#[allow(unused_imports)] use winit::window::Window;
pub use platform::cleanup_window;
pub use platform::fill_window;
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))] #[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
mod platform { pub(super) fn fill_window(window: &Window) {
use softbuffer::{Context, Surface};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use softbuffer::{Context, Surface};
use winit::window::Window;
use winit::window::WindowId; use winit::window::WindowId;
thread_local! {
// NOTE: You should never do things like that, create context and drop it before
// you drop the event loop. We do this for brevity to not blow up examples. We use
// ManuallyDrop to prevent destructors from running.
//
// A static, thread-local map of graphics contexts to open windows.
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
}
/// The graphics context used to draw to a window. /// The graphics context used to draw to a window.
struct GraphicsContext { struct GraphicsContext {
/// The global softbuffer context. /// The global softbuffer context.
@@ -48,69 +35,55 @@ mod platform {
} }
} }
fn create_surface(&mut self, window: &Window) -> &mut Surface { fn surface(&mut self, w: &Window) -> &mut Surface {
self.surfaces.entry(window.id()).or_insert_with(|| { self.surfaces.entry(w.id()).or_insert_with(|| {
unsafe { Surface::new(&self.context, window) } unsafe { Surface::new(&self.context, w) }
.expect("Failed to create a softbuffer surface") .expect("Failed to create a softbuffer surface")
}) })
} }
fn destroy_surface(&mut self, window: &Window) {
self.surfaces.remove(&window.id());
}
} }
pub fn fill_window(window: &Window) { thread_local! {
GC.with(|gc| { // NOTE: You should never do things like that, create context and drop it before
let size = window.inner_size(); // you drop the event loop. We do this for brevity to not blow up examples. We use
let (Some(width), Some(height)) = // ManuallyDrop to prevent destructors from running.
(NonZeroU32::new(size.width), NonZeroU32::new(size.height)) //
else { // A static, thread-local map of graphics contexts to open windows.
return; static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
};
// Either get the last context used or create a new one.
let mut gc = gc.borrow_mut();
let surface = gc
.get_or_insert_with(|| GraphicsContext::new(window))
.create_surface(window);
// Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xFF181818;
surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");
let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY);
buffer
.present()
.expect("Failed to present the softbuffer buffer");
})
} }
#[allow(dead_code)] GC.with(|gc| {
pub fn cleanup_window(window: &Window) { let size = window.inner_size();
GC.with(|gc| { let (Some(width), Some(height)) =
let mut gc = gc.borrow_mut(); (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
if let Some(context) = gc.as_mut() { else {
context.destroy_surface(window); return;
} };
});
} // Either get the last context used or create a new one.
let mut gc = gc.borrow_mut();
let surface = gc
.get_or_insert_with(|| GraphicsContext::new(window))
.surface(window);
// Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xFF181818;
surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");
let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY);
buffer
.present()
.expect("Failed to present the softbuffer buffer");
})
} }
#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))] #[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
mod platform { pub(super) fn fill_window(_window: &Window) {
pub fn fill_window(_window: &winit::window::Window) { // No-op on mobile platforms.
// No-op on mobile platforms.
}
#[allow(dead_code)]
pub fn cleanup_window(_window: &winit::window::Window) {
// No-op on mobile platforms.
}
} }

View File

@@ -4,13 +4,13 @@ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
keyboard::Key, keyboard::Key,
window::{Fullscreen, Window}, window::{Fullscreen, WindowBuilder},
}; };
pub fn main() -> Result<(), impl std::error::Error> { pub fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let builder = Window::builder().with_title("A fantastic window!"); let builder = WindowBuilder::new().with_title("A fantastic window!");
#[cfg(wasm_platform)] #[cfg(wasm_platform)]
let builder = { let builder = {
use winit::platform::web::WindowBuilderExtWebSys; use winit::platform::web::WindowBuilderExtWebSys;
@@ -18,11 +18,11 @@ pub fn main() -> Result<(), impl std::error::Error> {
}; };
let window = builder.build(&event_loop).unwrap(); let window = builder.build(&event_loop).unwrap();
#[cfg(web_platform)] #[cfg(wasm_platform)]
let log_list = wasm::insert_canvas_and_create_log_list(&window); let log_list = wasm::insert_canvas_and_create_log_list(&window);
event_loop.run(move |event, elwt| { event_loop.run(move |event, elwt| {
#[cfg(web_platform)] #[cfg(wasm_platform)]
wasm::log_event(&log_list, &event); wasm::log_event(&log_list, &event);
match event { match event {
@@ -57,7 +57,7 @@ pub fn main() -> Result<(), impl std::error::Error> {
}) })
} }
#[cfg(web_platform)] #[cfg(wasm_platform)]
mod wasm { mod wasm {
use std::num::NonZeroU32; use std::num::NonZeroU32;

View File

@@ -4,7 +4,7 @@ pub fn main() {
println!("This example must be run with cargo run-wasm --example web_aspect_ratio") println!("This example must be run with cargo run-wasm --example web_aspect_ratio")
} }
#[cfg(web_platform)] #[cfg(wasm_platform)]
mod wasm { mod wasm {
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
@@ -14,7 +14,7 @@ mod wasm {
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
platform::web::WindowBuilderExtWebSys, platform::web::WindowBuilderExtWebSys,
window::Window, window::{Window, WindowBuilder},
}; };
const EXPLANATION: &str = " const EXPLANATION: &str = "
@@ -33,7 +33,7 @@ This example demonstrates the desired future functionality which will possibly b
console_log::init_with_level(log::Level::Debug).expect("error initializing logger"); console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = Window::builder() let window = WindowBuilder::new()
.with_title("A fantastic window!") .with_title("A fantastic window!")
// When running in a non-wasm environment this would set the window size to 100x100. // When running in a non-wasm environment this would set the window size to 100x100.
// However in this example it just sets a default initial size of 100x100 that is immediately overwritten due to the layout + styling of the page. // However in this example it just sets a default initial size of 100x100 that is immediately overwritten due to the layout + styling of the page.

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ use winit::{
event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent}, event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent},
event_loop::{DeviceEvents, EventLoop}, event_loop::{DeviceEvents, EventLoop},
keyboard::{Key, KeyCode, PhysicalKey}, keyboard::{Key, KeyCode, PhysicalKey},
window::{Fullscreen, Window}, window::{Fullscreen, WindowBuilder},
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -18,7 +18,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = Window::builder() let window = WindowBuilder::new()
.with_title("A fantastic window!") .with_title("A fantastic window!")
.with_inner_size(LogicalSize::new(100.0, 100.0)) .with_inner_size(LogicalSize::new(100.0, 100.0))
.build(&event_loop) .build(&event_loop)

View File

@@ -5,7 +5,7 @@ use winit::{
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent}, event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
keyboard::Key, keyboard::Key,
window::{CursorIcon, ResizeDirection, Window}, window::{CursorIcon, ResizeDirection, WindowBuilder},
}; };
const BORDER: f64 = 8.0; const BORDER: f64 = 8.0;
@@ -17,7 +17,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = Window::builder() let window = WindowBuilder::new()
.with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0)) .with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0))
.with_min_inner_size(winit::dpi::LogicalSize::new(400.0, 200.0)) .with_min_inner_size(winit::dpi::LogicalSize::new(400.0, 200.0))
.with_decorations(false) .with_decorations(false)
@@ -40,7 +40,7 @@ fn main() -> Result<(), impl std::error::Error> {
if new_location != cursor_location { if new_location != cursor_location {
cursor_location = new_location; cursor_location = new_location;
window.set_cursor(cursor_direction_icon(cursor_location)) window.set_cursor_icon(cursor_direction_icon(cursor_location))
} }
} }
} }

View File

@@ -6,7 +6,7 @@ use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
window::{Icon, Window}, window::{Icon, WindowBuilder},
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -25,7 +25,7 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = Window::builder() let window = WindowBuilder::new()
.with_title("An iconic window!") .with_title("An iconic window!")
// At present, this only does anything on Windows and X11, so if you want to save load // At present, this only does anything on Windows and X11, so if you want to save load
// time, you can put icon loading behind a function that returns `None` on other platforms. // time, you can put icon loading behind a function that returns `None` on other platforms.

View File

@@ -12,7 +12,7 @@ fn main() -> Result<(), impl std::error::Error> {
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
platform::run_on_demand::EventLoopExtRunOnDemand, platform::run_on_demand::EventLoopExtRunOnDemand,
window::{Window, WindowId}, window::{Window, WindowBuilder, WindowId},
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -40,7 +40,6 @@ fn main() -> Result<(), impl std::error::Error> {
window_id, window_id,
} if window.id() == window_id => { } if window.id() == window_id => {
println!("--------------------------------------------------------- Window {idx} CloseRequested"); println!("--------------------------------------------------------- Window {idx} CloseRequested");
fill::cleanup_window(window);
app.window = None; app.window = None;
} }
Event::AboutToWait => window.request_redraw(), Event::AboutToWait => window.request_redraw(),
@@ -65,7 +64,7 @@ fn main() -> Result<(), impl std::error::Error> {
_ => (), _ => (),
} }
} else if let Event::Resumed = event { } else if let Event::Resumed = event {
let window = Window::builder() let window = WindowBuilder::new()
.with_title("Fantastic window number one!") .with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(elwt) .build(elwt)

View File

@@ -8,7 +8,7 @@ use winit::{
event::ElementState, event::ElementState,
event::{Event, MouseButton, WindowEvent}, event::{Event, MouseButton, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
window::Window, window::WindowBuilder,
}; };
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@@ -21,7 +21,7 @@ mod fill;
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = Window::builder() let window = WindowBuilder::new()
.with_title("A fantastic window!") .with_title("A fantastic window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(&event_loop) .build(&event_loop)

View File

@@ -16,7 +16,7 @@ fn main() -> std::process::ExitCode {
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus}, platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
window::Window, window::WindowBuilder,
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -25,7 +25,7 @@ fn main() -> std::process::ExitCode {
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
let window = Window::builder() let window = WindowBuilder::new()
.with_title("A fantastic window!") .with_title("A fantastic window!")
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
@@ -68,7 +68,7 @@ fn main() -> std::process::ExitCode {
} }
} }
#[cfg(any(ios_platform, web_platform, orbital_platform))] #[cfg(any(ios_platform, wasm_platform, orbital_platform))]
fn main() { fn main() {
println!("This platform doesn't support pump_events."); println!("This platform doesn't support pump_events.");
} }

View File

@@ -5,7 +5,7 @@ use winit::{
event::{ElementState, Event, WindowEvent}, event::{ElementState, Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
keyboard::NamedKey, keyboard::NamedKey,
window::Window, window::WindowBuilder,
}; };
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -15,7 +15,7 @@ fn main() -> Result<(), impl std::error::Error> {
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let window = Window::builder() let window = WindowBuilder::new()
.with_title("A fantastic window!") .with_title("A fantastic window!")
.with_inner_size(LogicalSize::new(128.0, 128.0)) .with_inner_size(LogicalSize::new(128.0, 128.0))
.with_resize_increments(LogicalSize::new(25.0, 25.0)) .with_resize_increments(LogicalSize::new(25.0, 25.0))

View File

@@ -11,7 +11,7 @@ use winit::{
event_loop::EventLoop, event_loop::EventLoop,
keyboard::{Key, NamedKey}, keyboard::{Key, NamedKey},
platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS}, platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS},
window::Window, window::{Window, WindowBuilder},
}; };
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@@ -60,7 +60,7 @@ fn main() -> Result<(), impl std::error::Error> {
} => match logical_key.as_ref() { } => match logical_key.as_ref() {
Key::Character("t") => { Key::Character("t") => {
let tabbing_id = windows.get(&window_id).unwrap().tabbing_identifier(); let tabbing_id = windows.get(&window_id).unwrap().tabbing_identifier();
let window = Window::builder() let window = WindowBuilder::new()
.with_tabbing_identifier(&tabbing_id) .with_tabbing_identifier(&tabbing_id)
.build(elwt) .build(elwt)
.unwrap(); .unwrap();

View File

@@ -12,7 +12,7 @@ mod imple {
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
platform::x11::WindowBuilderExtX11, platform::x11::WindowBuilderExtX11,
window::Window, window::WindowBuilder,
}; };
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> { pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
@@ -25,7 +25,7 @@ mod imple {
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new()?; let event_loop = EventLoop::new()?;
let window = Window::builder() let window = WindowBuilder::new()
.with_title("An embedded window!") .with_title("An embedded window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(parent_window_id) .with_embed_parent_window(parent_window_id)

View File

@@ -3,8 +3,6 @@ use std::hash::Hasher;
use std::sync::Arc; use std::sync::Arc;
use std::{error::Error, hash::Hash}; use std::{error::Error, hash::Hash};
use cursor_icon::CursorIcon;
use crate::event_loop::EventLoopWindowTarget; use crate::event_loop::EventLoopWindowTarget;
use crate::platform_impl::{self, PlatformCustomCursor, PlatformCustomCursorBuilder}; use crate::platform_impl::{self, PlatformCustomCursor, PlatformCustomCursorBuilder};
@@ -13,35 +11,8 @@ pub const MAX_CURSOR_SIZE: u16 = 2048;
const PIXEL_SIZE: usize = 4; const PIXEL_SIZE: usize = 4;
/// See [`Window::set_cursor()`](crate::window::Window::set_cursor) for more details.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Cursor {
Icon(CursorIcon),
Custom(CustomCursor),
}
impl Default for Cursor {
fn default() -> Self {
Self::Icon(CursorIcon::default())
}
}
impl From<CursorIcon> for Cursor {
fn from(icon: CursorIcon) -> Self {
Self::Icon(icon)
}
}
impl From<CustomCursor> for Cursor {
fn from(custom: CustomCursor) -> Self {
Self::Custom(custom)
}
}
/// Use a custom image as a cursor (mouse pointer). /// Use a custom image as a cursor (mouse pointer).
/// ///
/// Is guaranteed to be cheap to clone.
///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// **Web**: Some browsers have limits on cursor sizes usually at 128x128. /// **Web**: Some browsers have limits on cursor sizes usually at 128x128.
@@ -73,7 +44,7 @@ impl From<CustomCursor> for Cursor {
/// let custom_cursor = builder.build(&event_loop); /// let custom_cursor = builder.build(&event_loop);
/// ///
/// let window = Window::new(&event_loop).unwrap(); /// let window = Window::new(&event_loop).unwrap();
/// window.set_cursor(custom_cursor.clone()); /// window.set_custom_cursor(&custom_cursor);
/// ``` /// ```
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct CustomCursor { pub struct CustomCursor {
@@ -84,7 +55,10 @@ pub struct CustomCursor {
impl CustomCursor { impl CustomCursor {
/// Creates a new cursor from an rgba buffer. /// Creates a new cursor from an rgba buffer.
/// ///
/// The alpha channel is assumed to be **not** premultiplied. /// ## Platform-specific
///
/// - **Web:** Setting cursor could be delayed due to the creation of `Blob` objects,
/// which are async by nature.
pub fn from_rgba( pub fn from_rgba(
rgba: impl Into<Vec<u8>>, rgba: impl Into<Vec<u8>>,
width: u16, width: u16,
@@ -113,7 +87,7 @@ pub struct CustomCursorBuilder {
} }
impl CustomCursorBuilder { impl CustomCursorBuilder {
pub fn build(self, window_target: &EventLoopWindowTarget) -> CustomCursor { pub fn build<T>(self, window_target: &EventLoopWindowTarget<T>) -> CustomCursor {
CustomCursor { CustomCursor {
inner: PlatformCustomCursor::build(self.inner, &window_target.p), inner: PlatformCustomCursor::build(self.inner, &window_target.p),
} }
@@ -215,9 +189,9 @@ impl Eq for OnlyCursorImage {}
#[allow(dead_code)] #[allow(dead_code)]
impl OnlyCursorImage { impl OnlyCursorImage {
pub(crate) fn build( fn build<T>(
builder: OnlyCursorImageBuilder, builder: OnlyCursorImageBuilder,
_: &platform_impl::EventLoopWindowTarget, _: &platform_impl::EventLoopWindowTarget<T>,
) -> Self { ) -> Self {
Self(Arc::new(builder.0)) Self(Arc::new(builder.0))
} }
@@ -298,7 +272,7 @@ impl NoCustomCursor {
Ok(Self) Ok(Self)
} }
fn build(self, _: &platform_impl::EventLoopWindowTarget) -> NoCustomCursor { fn build<T>(self, _: &platform_impl::EventLoopWindowTarget<T>) -> NoCustomCursor {
self self
} }
} }

View File

@@ -104,9 +104,6 @@
//! [android_1]: https://developer.android.com/training/multiscreen/screendensities //! [android_1]: https://developer.android.com/training/multiscreen/screendensities
//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio //! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub trait Pixel: Copy + Into<f64> { pub trait Pixel: Copy + Into<f64> {
fn from_f64(f: f64) -> Self; fn from_f64(f: f64) -> Self;
fn cast<P: Pixel>(self) -> P { fn cast<P: Pixel>(self) -> P {

View File

@@ -35,6 +35,8 @@ pub enum EventLoopError {
NotSupported(NotSupportedError), NotSupported(NotSupportedError),
/// The OS cannot perform the operation. /// The OS cannot perform the operation.
Os(OsError), Os(OsError),
/// The event loop can't be re-run while it's already running
AlreadyRunning,
/// The event loop can't be re-created. /// The event loop can't be re-created.
RecreationAttempt, RecreationAttempt,
/// Application has exit with an error status. /// Application has exit with an error status.
@@ -103,6 +105,7 @@ impl fmt::Display for NotSupportedError {
impl fmt::Display for EventLoopError { impl fmt::Display for EventLoopError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self { match self {
EventLoopError::AlreadyRunning => write!(f, "EventLoop is already running"),
EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"), EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
EventLoopError::NotSupported(e) => e.fmt(f), EventLoopError::NotSupported(e) => e.fmt(f),
EventLoopError::Os(e) => e.fmt(f), EventLoopError::Os(e) => e.fmt(f),

View File

@@ -34,13 +34,11 @@
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil //! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Mutex, Weak}; use std::sync::{Mutex, Weak};
#[cfg(not(web_platform))] #[cfg(not(wasm_platform))]
use std::time::Instant; use std::time::Instant;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use smol_str::SmolStr; use smol_str::SmolStr;
#[cfg(web_platform)] #[cfg(wasm_platform)]
use web_time::Instant; use web_time::Instant;
use crate::error::ExternalError; use crate::error::ExternalError;
@@ -447,23 +445,21 @@ pub enum WindowEvent {
button: MouseButton, button: MouseButton,
}, },
/// Two-finger pinch gesture, often used for magnification. /// Touchpad magnification event with two-finger pinch gesture.
///
/// Positive delta values indicate magnification (zooming in) and
/// negative delta values indicate shrinking (zooming out).
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - Only available on **macOS** and **iOS**. /// - Only available on **macOS**.
/// - On iOS, not recognized by default. It must be enabled when needed. TouchpadMagnify {
PinchGesture {
device_id: DeviceId, device_id: DeviceId,
/// Positive values indicate magnification (zooming in) and negative
/// values indicate shrinking (zooming out).
///
/// This value may be NaN.
delta: f64, delta: f64,
phase: TouchPhase, phase: TouchPhase,
}, },
/// Double tap gesture. /// Smart magnification event.
/// ///
/// On a Mac, smart magnification is triggered by a double tap with two fingers /// On a Mac, smart magnification is triggered by a double tap with two fingers
/// on the trackpad and is commonly used to zoom on a certain object /// on the trackpad and is commonly used to zoom on a certain object
@@ -479,20 +475,18 @@ pub enum WindowEvent {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - Only available on **macOS 10.8** and later, and **iOS**. /// - Only available on **macOS 10.8** and later.
/// - On iOS, not recognized by default. It must be enabled when needed. SmartMagnify { device_id: DeviceId },
DoubleTapGesture { device_id: DeviceId },
/// Two-finger rotation gesture. /// Touchpad rotation event with two-finger rotation gesture.
/// ///
/// Positive delta values indicate rotation counterclockwise and /// Positive delta values indicate rotation counterclockwise and
/// negative delta values indicate rotation clockwise. /// negative delta values indicate rotation clockwise.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - Only available on **macOS** and **iOS**. /// - Only available on **macOS**.
/// - On iOS, not recognized by default. It must be enabled when needed. TouchpadRotate {
RotationGesture {
device_id: DeviceId, device_id: DeviceId,
delta: f32, delta: f32,
phase: TouchPhase, phase: TouchPhase,
@@ -536,8 +530,9 @@ pub enum WindowEvent {
/// * Changing the display's scale factor (e.g. in Control Panel on Windows). /// * Changing the display's scale factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different scale factor. /// * Moving the window to a display with a different scale factor.
/// ///
/// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the window is /// After this event callback has been processed, the window will be resized to whatever value
/// resized to the value suggested by the OS, but it can be changed to any value. /// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested
/// by the OS, but it can be changed to any value.
/// ///
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module. /// For more information about DPI in general, see the [`dpi`](crate::dpi) module.
ScaleFactorChanged { ScaleFactorChanged {
@@ -579,7 +574,7 @@ pub enum WindowEvent {
/// ### Others /// ### Others
/// ///
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`].
/// - **Android / Wayland / Windows / Orbital:** Unsupported. /// - **Android / Windows / Orbital:** Unsupported.
/// ///
/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
@@ -1204,13 +1199,13 @@ mod tests {
state: event::ElementState::Pressed, state: event::ElementState::Pressed,
button: event::MouseButton::Other(0), button: event::MouseButton::Other(0),
}); });
with_window_event(PinchGesture { with_window_event(TouchpadMagnify {
device_id: did, device_id: did,
delta: 0.0, delta: 0.0,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
}); });
with_window_event(DoubleTapGesture { device_id: did }); with_window_event(SmartMagnify { device_id: did });
with_window_event(RotationGesture { with_window_event(TouchpadRotate {
device_id: did, device_id: did,
delta: 0.0, delta: 0.0,
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,

View File

@@ -11,12 +11,12 @@ use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
#[cfg(any(x11_platform, wayland_platform))] #[cfg(any(x11_platform, wayland_platform))]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::{error, fmt}; use std::{error, fmt};
#[cfg(not(web_platform))] #[cfg(not(wasm_platform))]
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
#[cfg(web_platform)] #[cfg(wasm_platform)]
use web_time::{Duration, Instant}; use web_time::{Duration, Instant};
use crate::error::EventLoopError; use crate::error::EventLoopError;
@@ -48,8 +48,8 @@ pub struct EventLoop<T: 'static> {
/// your callback. [`EventLoop`] will coerce into this type (`impl<T> Deref for /// your callback. [`EventLoop`] will coerce into this type (`impl<T> Deref for
/// EventLoop<T>`), so functions that take this as a parameter can also take /// EventLoop<T>`), so functions that take this as a parameter can also take
/// `&EventLoop`. /// `&EventLoop`.
pub struct EventLoopWindowTarget { pub struct EventLoopWindowTarget<T: 'static> {
pub(crate) p: platform_impl::EventLoopWindowTarget, pub(crate) p: platform_impl::EventLoopWindowTarget<T>,
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
} }
@@ -57,26 +57,33 @@ pub struct EventLoopWindowTarget {
/// ///
/// This is used to make specifying options that affect the whole application /// This is used to make specifying options that affect the whole application
/// easier. But note that constructing multiple event loops is not supported. /// easier. But note that constructing multiple event loops is not supported.
///
/// This can be created using [`EventLoop::new`] or [`EventLoop::with_user_event`].
#[derive(Default)] #[derive(Default)]
pub struct EventLoopBuilder<T: 'static> { pub struct EventLoopBuilder<T: 'static> {
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes, pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
_p: PhantomData<T>, _p: PhantomData<T>,
} }
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl EventLoopBuilder<()> { impl EventLoopBuilder<()> {
/// Start building a new event loop. /// Start building a new event loop.
#[inline] #[inline]
#[deprecated = "use `EventLoop::builder` instead"]
pub fn new() -> Self { pub fn new() -> Self {
EventLoop::builder() Self::with_user_event()
} }
} }
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl<T> EventLoopBuilder<T> { impl<T> EventLoopBuilder<T> {
/// Start building a new event loop, with the given type as the user event
/// type.
#[inline]
pub fn with_user_event() -> Self {
Self {
platform_specific: Default::default(),
_p: PhantomData,
}
}
/// Builds a new event loop. /// Builds a new event loop.
/// ///
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread, /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
@@ -123,7 +130,7 @@ impl<T> EventLoopBuilder<T> {
}) })
} }
#[cfg(web_platform)] #[cfg(wasm_platform)]
pub(crate) fn allow_event_loop_recreation() { pub(crate) fn allow_event_loop_recreation() {
EVENT_LOOP_CREATED.store(false, Ordering::Relaxed); EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
} }
@@ -135,7 +142,7 @@ impl<T> fmt::Debug for EventLoop<T> {
} }
} }
impl fmt::Debug for EventLoopWindowTarget { impl<T> fmt::Debug for EventLoopWindowTarget<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EventLoopWindowTarget { .. }") f.pad("EventLoopWindowTarget { .. }")
} }
@@ -186,38 +193,27 @@ impl ControlFlow {
} }
impl EventLoop<()> { impl EventLoop<()> {
/// Create the event loop. /// Alias for [`EventLoopBuilder::new().build()`].
/// ///
/// This is an alias of `EventLoop::builder().build()`. /// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build
#[inline] #[inline]
pub fn new() -> Result<EventLoop<()>, EventLoopError> { pub fn new() -> Result<EventLoop<()>, EventLoopError> {
Self::builder().build() EventLoopBuilder::new().build()
}
/// Start building a new event loop.
///
/// This returns an [`EventLoopBuilder`], to allow configuring the event loop before creation.
///
/// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that.
#[inline]
pub fn builder() -> EventLoopBuilder<()> {
Self::with_user_event()
} }
} }
impl<T> EventLoop<T> { impl<T> EventLoop<T> {
/// Start building a new event loop, with the given type as the user event #[deprecated = "Use `EventLoopBuilder::<T>::with_user_event().build()` instead."]
/// type. pub fn with_user_event() -> Result<EventLoop<T>, EventLoopError> {
pub fn with_user_event() -> EventLoopBuilder<T> { EventLoopBuilder::<T>::with_user_event().build()
EventLoopBuilder {
platform_specific: Default::default(),
_p: PhantomData,
}
} }
/// Runs the event loop in the calling thread and calls the given `event_handler` closure /// Runs the event loop in the calling thread and calls the given `event_handler` closure
/// to dispatch any pending events. /// to dispatch any pending events.
/// ///
/// Since the closure is `'static`, it must be a `move` closure if it needs to
/// access any data from the calling context.
///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
/// ## Platform-specific /// ## Platform-specific
@@ -230,10 +226,10 @@ impl<T> EventLoop<T> {
/// ///
/// Web applications are recommended to use /// Web applications are recommended to use
#[cfg_attr( #[cfg_attr(
web_platform, wasm_platform,
doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]" doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]"
)] )]
#[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")] #[cfg_attr(not(wasm_platform), doc = "`EventLoopExtWebSys::spawn()`")]
/// [^1] instead of [`run()`] to avoid the need /// [^1] instead of [`run()`] to avoid the need
/// for the Javascript exception trick, and to make it clearer that the event loop runs /// for the Javascript exception trick, and to make it clearer that the event loop runs
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the /// asynchronously (via the browser's own, internal, event loop) and doesn't block the
@@ -243,12 +239,12 @@ impl<T> EventLoop<T> {
/// ///
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow() /// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow()
/// [`run()`]: Self::run() /// [`run()`]: Self::run()
/// [^1]: `EventLoopExtWebSys::spawn()` is only available on Web. /// [^1]: `EventLoopExtWebSys::spawn()` is only available on WASM.
#[inline] #[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))] #[cfg(not(all(wasm_platform, target_feature = "exception-handling")))]
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError> pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &EventLoopWindowTarget), F: FnMut(Event<T>, &EventLoopWindowTarget<T>),
{ {
self.event_loop.run(event_handler) self.event_loop.run(event_handler)
} }
@@ -305,13 +301,13 @@ impl<T> AsRawFd for EventLoop<T> {
} }
impl<T> Deref for EventLoop<T> { impl<T> Deref for EventLoop<T> {
type Target = EventLoopWindowTarget; type Target = EventLoopWindowTarget<T>;
fn deref(&self) -> &EventLoopWindowTarget { fn deref(&self) -> &EventLoopWindowTarget<T> {
self.event_loop.window_target() self.event_loop.window_target()
} }
} }
impl EventLoopWindowTarget { impl<T> EventLoopWindowTarget<T> {
/// Returns the list of all the monitors available on the system. /// Returns the list of all the monitors available on the system.
#[inline] #[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
@@ -374,19 +370,10 @@ impl EventLoopWindowTarget {
pub fn exiting(&self) -> bool { pub fn exiting(&self) -> bool {
self.p.exiting() self.p.exiting()
} }
/// Gets a persistent reference to the underlying platform display.
///
/// See the [`OwnedDisplayHandle`] type for more information.
pub fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle {
platform: self.p.owned_display_handle(),
}
}
} }
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for EventLoopWindowTarget { impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> { fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.p.raw_display_handle_rwh_06()?; let raw = self.p.raw_display_handle_rwh_06()?;
// SAFETY: The display will never be deallocated while the event loop is alive. // SAFETY: The display will never be deallocated while the event loop is alive.
@@ -395,59 +382,13 @@ impl rwh_06::HasDisplayHandle for EventLoopWindowTarget {
} }
#[cfg(feature = "rwh_05")] #[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawDisplayHandle for EventLoopWindowTarget { unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoopWindowTarget<T> {
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop. /// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle { fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.p.raw_display_handle_rwh_05() self.p.raw_display_handle_rwh_05()
} }
} }
/// A proxy for the underlying display handle.
///
/// The purpose of this type is to provide a cheaply clonable handle to the underlying
/// display handle. This is often used by graphics APIs to connect to the underlying APIs.
/// It is difficult to keep a handle to the [`EventLoop`] type or the [`EventLoopWindowTarget`]
/// type. In contrast, this type involves no lifetimes and can be persisted for as long as
/// needed.
///
/// For all platforms, this is one of the following:
///
/// - A zero-sized type that is likely optimized out.
/// - A reference-counted pointer to the underlying type.
#[derive(Clone)]
pub struct OwnedDisplayHandle {
#[cfg_attr(not(any(feature = "rwh_05", feature = "rwh_06")), allow(dead_code))]
platform: platform_impl::OwnedDisplayHandle,
}
impl fmt::Debug for OwnedDisplayHandle {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
}
}
#[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)
}
}
#[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawDisplayHandle for OwnedDisplayHandle {
#[inline]
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.platform.raw_display_handle_rwh_05()
}
}
/// Used to send custom events to [`EventLoop`]. /// Used to send custom events to [`EventLoop`].
pub struct EventLoopProxy<T: 'static> { pub struct EventLoopProxy<T: 'static> {
event_loop_proxy: platform_impl::EventLoopProxy<T>, event_loop_proxy: platform_impl::EventLoopProxy<T>,
@@ -518,16 +459,16 @@ pub enum DeviceEvents {
/// executed and removed from the list. /// executed and removed from the list.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AsyncRequestSerial { pub struct AsyncRequestSerial {
serial: usize, serial: u64,
} }
impl AsyncRequestSerial { impl AsyncRequestSerial {
// TODO(kchibisov): Remove `cfg` when the clipboard will be added. // TODO(kchibisov) remove `cfg` when the clipboard will be added.
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn get() -> Self { pub(crate) fn get() -> Self {
static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0); static CURRENT_SERIAL: AtomicU64 = AtomicU64::new(0);
// NOTE: We rely on wrap around here, while the user may just request // NOTE: we rely on wrap around here, while the user may just request
// in the loop usize::MAX times that's issue is considered on them. // in the loop u64::MAX times that's issue is considered on them.
let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed); let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
Self { serial } Self { serial }
} }

View File

@@ -69,9 +69,6 @@
// //
// --------- END OF W3C SHORT NOTICE --------------------------------------------------------------- // --------- END OF W3C SHORT NOTICE ---------------------------------------------------------------
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub use smol_str::SmolStr; pub use smol_str::SmolStr;
/// Contains the platform-native physical key identifier /// Contains the platform-native physical key identifier

View File

@@ -13,7 +13,7 @@
//! Once this is done, there are two ways to create a [`Window`]: //! Once this is done, there are two ways to create a [`Window`]:
//! //!
//! - Calling [`Window::new(&event_loop)`][window_new]. //! - Calling [`Window::new(&event_loop)`][window_new].
//! - Calling [`let builder = Window::builder()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build]. //! - Calling [`let builder = WindowBuilder::new()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build].
//! //!
//! The first method is the simplest and will give you default values for everything. The second //! The first method is the simplest and will give you default values for everything. The second
//! method allows you to customize the way your [`Window`] will look and behave by modifying the //! method allows you to customize the way your [`Window`] will look and behave by modifying the
@@ -63,11 +63,11 @@
//! use winit::{ //! use winit::{
//! event::{Event, WindowEvent}, //! event::{Event, WindowEvent},
//! event_loop::{ControlFlow, EventLoop}, //! event_loop::{ControlFlow, EventLoop},
//! window::Window, //! window::WindowBuilder,
//! }; //! };
//! //!
//! let event_loop = EventLoop::new().unwrap(); //! let event_loop = EventLoop::new().unwrap();
//! let window = Window::builder().build(&event_loop).unwrap(); //! let window = WindowBuilder::new().build(&event_loop).unwrap();
//! //!
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't //! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications. //! // dispatched any events. This is ideal for games and similar applications.
@@ -137,7 +137,7 @@
//! [`WindowId`]: window::WindowId //! [`WindowId`]: window::WindowId
//! [`WindowBuilder`]: window::WindowBuilder //! [`WindowBuilder`]: window::WindowBuilder
//! [window_new]: window::Window::new //! [window_new]: window::Window::new
//! [window_builder_new]: window::Window::builder //! [window_builder_new]: window::WindowBuilder::new
//! [window_builder_build]: window::WindowBuilder::build //! [window_builder_build]: window::WindowBuilder::build
//! [`Window::id()`]: window::Window::id //! [`Window::id()`]: window::Window::id
//! [`WindowEvent`]: event::WindowEvent //! [`WindowEvent`]: event::WindowEvent
@@ -154,16 +154,21 @@
#![deny(unsafe_op_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)]
#![cfg_attr(feature = "cargo-clippy", deny(warnings))] #![cfg_attr(feature = "cargo-clippy", deny(warnings))]
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc // Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
#![cfg_attr( #![cfg_attr(docsrs, feature(doc_auto_cfg))]
docsrs,
feature(doc_auto_cfg, doc_cfg_hide),
doc(cfg_hide(doc, docsrs))
)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
pub use rwh_06 as raw_window_handle; pub use rwh_06 as raw_window_handle;
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[macro_use]
extern crate bitflags;
pub mod dpi; pub mod dpi;
#[macro_use] #[macro_use]
pub mod error; pub mod error;
@@ -177,3 +182,18 @@ mod platform_impl;
pub mod window; pub mod window;
pub mod platform; pub mod platform;
/// Wrapper for objects which winit will access on the main thread so they are effectively `Send`
/// and `Sync`, since they always execute on a single thread.
///
/// # Safety
///
/// Winit can run only one event loop at a time, and the event loop itself is tied to some thread.
/// The objects could be sent across the threads, but once passed to winit, they execute on the
/// main thread if the platform demands it. Thus, marking such objects as `Send + Sync` is safe.
#[doc(hidden)]
#[derive(Clone, Debug)]
pub(crate) struct SendSyncWrapper<T>(pub(crate) T);
unsafe impl<T> Send for SendSyncWrapper<T> {}
unsafe impl<T> Sync for SendSyncWrapper<T> {}

View File

@@ -10,32 +10,28 @@ use crate::{
platform_impl, platform_impl,
}; };
/// Deprecated! Use `VideoModeHandle` instead.
#[deprecated = "Renamed to `VideoModeHandle`"]
pub type VideoMode = VideoModeHandle;
/// Describes a fullscreen video mode of a monitor. /// Describes a fullscreen video mode of a monitor.
/// ///
/// Can be acquired with [`MonitorHandle::video_modes`]. /// Can be acquired with [`MonitorHandle::video_modes`].
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub struct VideoModeHandle { pub struct VideoMode {
pub(crate) video_mode: platform_impl::VideoModeHandle, pub(crate) video_mode: platform_impl::VideoMode,
} }
impl std::fmt::Debug for VideoModeHandle { impl std::fmt::Debug for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.video_mode.fmt(f) self.video_mode.fmt(f)
} }
} }
impl PartialOrd for VideoModeHandle { impl PartialOrd for VideoMode {
fn partial_cmp(&self, other: &VideoModeHandle) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &VideoMode) -> Option<std::cmp::Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
impl Ord for VideoModeHandle { impl Ord for VideoMode {
fn cmp(&self, other: &VideoModeHandle) -> std::cmp::Ordering { fn cmp(&self, other: &VideoMode) -> std::cmp::Ordering {
self.monitor().cmp(&other.monitor()).then( self.monitor().cmp(&other.monitor()).then(
self.size() self.size()
.cmp(&other.size()) .cmp(&other.size())
@@ -49,7 +45,7 @@ impl Ord for VideoModeHandle {
} }
} }
impl VideoModeHandle { impl VideoMode {
/// Returns the resolution of this video mode. /// Returns the resolution of this video mode.
#[inline] #[inline]
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
@@ -85,7 +81,7 @@ impl VideoModeHandle {
} }
} }
impl std::fmt::Display for VideoModeHandle { impl std::fmt::Display for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
@@ -135,8 +131,8 @@ impl MonitorHandle {
/// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor /// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor
/// the window is on is removed. /// the window is on is removed.
/// ///
/// When using exclusive fullscreen, the refresh rate of the [`VideoModeHandle`] that was /// When using exclusive fullscreen, the refresh rate of the [`VideoMode`] that was used to
/// used to enter fullscreen should be used instead. /// enter fullscreen should be used instead.
#[inline] #[inline]
pub fn refresh_rate_millihertz(&self) -> Option<u32> { pub fn refresh_rate_millihertz(&self) -> Option<u32> {
self.inner.refresh_rate_millihertz() self.inner.refresh_rate_millihertz()
@@ -165,9 +161,9 @@ impl MonitorHandle {
/// ///
/// - **Web:** Always returns an empty iterator /// - **Web:** Always returns an empty iterator
#[inline] #[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.inner self.inner
.video_modes() .video_modes()
.map(|video_mode| VideoModeHandle { video_mode }) .map(|video_mode| VideoMode { video_mode })
} }
} }

View File

@@ -3,7 +3,7 @@ use crate::{
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use self::activity::{AndroidApp, ConfigurationRef, Rect}; use android_activity::{AndroidApp, ConfigurationRef, Rect};
/// Additional methods on [`EventLoop`] that are specific to Android. /// Additional methods on [`EventLoop`] that are specific to Android.
pub trait EventLoopExtAndroid {} pub trait EventLoopExtAndroid {}
@@ -30,7 +30,7 @@ impl WindowExtAndroid for Window {
} }
} }
impl EventLoopWindowTargetExtAndroid for EventLoopWindowTarget {} impl<T> EventLoopWindowTargetExtAndroid for EventLoopWindowTarget<T> {}
/// Additional methods on [`WindowBuilder`] that are specific to Android. /// Additional methods on [`WindowBuilder`] that are specific to Android.
pub trait WindowBuilderExtAndroid {} pub trait WindowBuilderExtAndroid {}
@@ -89,16 +89,5 @@ pub mod activity {
// feature enabled, so we avoid inlining it so that they're forced to view // feature enabled, so we avoid inlining it so that they're forced to view
// it on the crate's own docs.rs page. // it on the crate's own docs.rs page.
#[doc(no_inline)] #[doc(no_inline)]
#[cfg(android_platform)]
pub use android_activity::*; pub use android_activity::*;
#[cfg(not(android_platform))]
#[doc(hidden)]
pub struct Rect;
#[cfg(not(android_platform))]
#[doc(hidden)]
pub struct ConfigurationRef;
#[cfg(not(android_platform))]
#[doc(hidden)]
pub struct AndroidApp;
} }

View File

@@ -1,8 +1,11 @@
use std::os::raw::c_void; use std::os::raw::c_void;
use icrate::Foundation::MainThreadMarker;
use objc2::rc::Id;
use crate::{ use crate::{
event_loop::EventLoop, event_loop::EventLoop,
monitor::{MonitorHandle, VideoModeHandle}, monitor::{MonitorHandle, VideoMode},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
@@ -85,21 +88,6 @@ pub trait WindowExtIOS {
/// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc) /// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc)
/// is also called for you. /// is also called for you.
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle); fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle);
/// Sets whether the [`Window`] should recognize pinch gestures.
///
/// The default is to not recognize gestures.
fn recognize_pinch_gesture(&self, should_recognize: bool);
/// Sets whether the [`Window`] should recognize double tap gestures.
///
/// The default is to not recognize gestures.
fn recognize_doubletap_gesture(&self, should_recognize: bool);
/// Sets whether the [`Window`] should recognize rotation gestures.
///
/// The default is to not recognize gestures.
fn recognize_rotation_gesture(&self, should_recognize: bool);
} }
impl WindowExtIOS for Window { impl WindowExtIOS for Window {
@@ -139,24 +127,6 @@ impl WindowExtIOS for Window {
self.window self.window
.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style)) .maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
} }
#[inline]
fn recognize_pinch_gesture(&self, should_recognize: bool) {
self.window
.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
}
#[inline]
fn recognize_doubletap_gesture(&self, should_recognize: bool) {
self.window
.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
}
#[inline]
fn recognize_rotation_gesture(&self, should_recognize: bool) {
self.window
.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
}
} }
/// Additional methods on [`WindowBuilder`] that are specific to iOS. /// Additional methods on [`WindowBuilder`] that are specific to iOS.
@@ -217,39 +187,38 @@ pub trait WindowBuilderExtIOS {
impl WindowBuilderExtIOS for WindowBuilder { impl WindowBuilderExtIOS for WindowBuilder {
#[inline] #[inline]
fn with_scale_factor(mut self, scale_factor: f64) -> Self { fn with_scale_factor(mut self, scale_factor: f64) -> Self {
self.window.platform_specific.scale_factor = Some(scale_factor); self.platform_specific.scale_factor = Some(scale_factor);
self self
} }
#[inline] #[inline]
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self { fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self {
self.window.platform_specific.valid_orientations = valid_orientations; self.platform_specific.valid_orientations = valid_orientations;
self self
} }
#[inline] #[inline]
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self { fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self {
self.window.platform_specific.prefers_home_indicator_hidden = hidden; self.platform_specific.prefers_home_indicator_hidden = hidden;
self self
} }
#[inline] #[inline]
fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self { fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
self.window self.platform_specific
.platform_specific
.preferred_screen_edges_deferring_system_gestures = edges; .preferred_screen_edges_deferring_system_gestures = edges;
self self
} }
#[inline] #[inline]
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self { fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self {
self.window.platform_specific.prefers_status_bar_hidden = hidden; self.platform_specific.prefers_status_bar_hidden = hidden;
self self
} }
#[inline] #[inline]
fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self { fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self {
self.window.platform_specific.preferred_status_bar_style = status_bar_style; self.platform_specific.preferred_status_bar_style = status_bar_style;
self self
} }
} }
@@ -261,23 +230,23 @@ pub trait MonitorHandleExtIOS {
/// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc /// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc
fn ui_screen(&self) -> *mut c_void; fn ui_screen(&self) -> *mut c_void;
/// Returns the preferred [`VideoModeHandle`] for this monitor. /// Returns the preferred [`VideoMode`] for this monitor.
/// ///
/// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc). /// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc).
fn preferred_video_mode(&self) -> VideoModeHandle; fn preferred_video_mode(&self) -> VideoMode;
} }
impl MonitorHandleExtIOS for MonitorHandle { impl MonitorHandleExtIOS for MonitorHandle {
#[inline] #[inline]
fn ui_screen(&self) -> *mut c_void { fn ui_screen(&self) -> *mut c_void {
// SAFETY: The marker is only used to get the pointer of the screen // SAFETY: The marker is only used to get the pointer of the screen
let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() }; let mtm = unsafe { MainThreadMarker::new_unchecked() };
objc2::rc::Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
} }
#[inline] #[inline]
fn preferred_video_mode(&self) -> VideoModeHandle { fn preferred_video_mode(&self) -> VideoMode {
VideoModeHandle { VideoMode {
video_mode: self.inner.preferred_video_mode(), video_mode: self.inner.preferred_video_mode(),
} }
} }
@@ -314,7 +283,7 @@ pub enum Idiom {
CarPlay, CarPlay,
} }
bitflags::bitflags! { bitflags! {
/// The [edges] of a screen. /// The [edges] of a screen.
/// ///
/// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc /// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc

View File

@@ -1,7 +1,7 @@
use std::os::raw::c_void; use std::os::raw::c_void;
#[cfg(feature = "serde")] use icrate::Foundation::MainThreadMarker;
use serde::{Deserialize, Serialize}; use objc2::rc::Id;
use crate::{ use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget}, event_loop::{EventLoopBuilder, EventLoopWindowTarget},
@@ -212,62 +212,61 @@ pub trait WindowBuilderExtMacOS {
impl WindowBuilderExtMacOS for WindowBuilder { impl WindowBuilderExtMacOS for WindowBuilder {
#[inline] #[inline]
fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self { fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self {
self.window.platform_specific.movable_by_window_background = movable_by_window_background; self.platform_specific.movable_by_window_background = movable_by_window_background;
self self
} }
#[inline] #[inline]
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self { fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self {
self.window.platform_specific.titlebar_transparent = titlebar_transparent; self.platform_specific.titlebar_transparent = titlebar_transparent;
self self
} }
#[inline] #[inline]
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self { fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self {
self.window.platform_specific.titlebar_hidden = titlebar_hidden; self.platform_specific.titlebar_hidden = titlebar_hidden;
self self
} }
#[inline] #[inline]
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self { fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self {
self.window.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden; self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
self self
} }
#[inline] #[inline]
fn with_title_hidden(mut self, title_hidden: bool) -> Self { fn with_title_hidden(mut self, title_hidden: bool) -> Self {
self.window.platform_specific.title_hidden = title_hidden; self.platform_specific.title_hidden = title_hidden;
self self
} }
#[inline] #[inline]
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self { fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self {
self.window.platform_specific.fullsize_content_view = fullsize_content_view; self.platform_specific.fullsize_content_view = fullsize_content_view;
self self
} }
#[inline] #[inline]
fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self { fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self {
self.window.platform_specific.disallow_hidpi = disallow_hidpi; self.platform_specific.disallow_hidpi = disallow_hidpi;
self self
} }
#[inline] #[inline]
fn with_has_shadow(mut self, has_shadow: bool) -> Self { fn with_has_shadow(mut self, has_shadow: bool) -> Self {
self.window.platform_specific.has_shadow = has_shadow; self.platform_specific.has_shadow = has_shadow;
self self
} }
#[inline] #[inline]
fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self { fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self {
self.window.platform_specific.accepts_first_mouse = accepts_first_mouse; self.platform_specific.accepts_first_mouse = accepts_first_mouse;
self self
} }
#[inline] #[inline]
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self { fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
self.window self.platform_specific
.platform_specific
.tabbing_identifier .tabbing_identifier
.replace(tabbing_identifier.to_string()); .replace(tabbing_identifier.to_string());
self self
@@ -275,7 +274,7 @@ impl WindowBuilderExtMacOS for WindowBuilder {
#[inline] #[inline]
fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self { fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self {
self.window.platform_specific.option_as_alt = option_as_alt; self.platform_specific.option_as_alt = option_as_alt;
self self
} }
} }
@@ -368,10 +367,8 @@ impl MonitorHandleExtMacOS for MonitorHandle {
fn ns_screen(&self) -> Option<*mut c_void> { fn ns_screen(&self) -> Option<*mut c_void> {
// SAFETY: We only use the marker to get a pointer // SAFETY: We only use the marker to get a pointer
let mtm = unsafe { icrate::Foundation::MainThreadMarker::new_unchecked() }; let mtm = unsafe { MainThreadMarker::new_unchecked() };
self.inner self.inner.ns_screen(mtm).map(|s| Id::as_ptr(&s) as _)
.ns_screen(mtm)
.map(|s| objc2::rc::Id::as_ptr(&s) as _)
} }
} }
@@ -389,7 +386,7 @@ pub trait EventLoopWindowTargetExtMacOS {
fn allows_automatic_window_tabbing(&self) -> bool; fn allows_automatic_window_tabbing(&self) -> bool;
} }
impl EventLoopWindowTargetExtMacOS for EventLoopWindowTarget { impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
fn hide_application(&self) { fn hide_application(&self) {
self.p.hide_application() self.p.hide_application()
} }

View File

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

View File

@@ -1,4 +1,5 @@
use crate::event::KeyEvent; #![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
use crate::keyboard::Key; use crate::keyboard::Key;
/// Additional methods for the `KeyEvent` which cannot be implemented on all /// Additional methods for the `KeyEvent` which cannot be implemented on all
@@ -21,18 +22,3 @@ pub trait KeyEventExtModifierSupplement {
/// cannot be `Dead`. /// cannot be `Dead`.
fn key_without_modifiers(&self) -> Key; fn key_without_modifiers(&self) -> Key;
} }
impl KeyEventExtModifierSupplement for KeyEvent {
#[inline]
fn text_with_all_modifiers(&self) -> Option<&str> {
self.platform_specific
.text_with_all_modifiers
.as_ref()
.map(|s| s.as_str())
}
#[inline]
fn key_without_modifiers(&self) -> Key {
self.platform_specific.key_without_modifiers.clone()
}
}

View File

@@ -51,12 +51,12 @@ pub trait EventLoopExtPumpEvents {
/// # event::{Event, WindowEvent}, /// # event::{Event, WindowEvent},
/// # event_loop::EventLoop, /// # event_loop::EventLoop,
/// # platform::pump_events::{EventLoopExtPumpEvents, PumpStatus}, /// # platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
/// # window::Window, /// # window::WindowBuilder,
/// # }; /// # };
/// let mut event_loop = EventLoop::new().unwrap(); /// let mut event_loop = EventLoop::new().unwrap();
/// # /// #
/// # SimpleLogger::new().init().unwrap(); /// # SimpleLogger::new().init().unwrap();
/// let window = Window::builder() /// let window = WindowBuilder::new()
/// .with_title("A fantastic window!") /// .with_title("A fantastic window!")
/// .build(&event_loop) /// .build(&event_loop)
/// .unwrap(); /// .unwrap();
@@ -174,7 +174,7 @@ pub trait EventLoopExtPumpEvents {
/// callback. /// callback.
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget); F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
} }
impl<T> EventLoopExtPumpEvents for EventLoop<T> { impl<T> EventLoopExtPumpEvents for EventLoop<T> {
@@ -182,7 +182,7 @@ impl<T> EventLoopExtPumpEvents for EventLoop<T> {
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget), F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{ {
self.event_loop.pump_events(timeout, event_handler) self.event_loop.pump_events(timeout, event_handler)
} }

View File

@@ -55,10 +55,10 @@ pub trait EventLoopExtRunOnDemand {
/// loop that would block the browser and there is nothing that can be /// loop that would block the browser and there is nothing that can be
/// polled to ask for new events. Events are delivered via callbacks based /// polled to ask for new events. Events are delivered via callbacks based
/// on an event loop that is internal to the browser itself. /// on an event loop that is internal to the browser itself.
/// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS. /// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS.
/// ///
#[cfg_attr( #[cfg_attr(
not(web_platform), not(wasm_platform),
doc = "[^1]: `spawn()` is only available on `wasm` platforms." doc = "[^1]: `spawn()` is only available on `wasm` platforms."
)] )]
/// ///
@@ -66,7 +66,7 @@ pub trait EventLoopExtRunOnDemand {
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow() /// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow()
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError> fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget); F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
} }
impl<T> EventLoopExtRunOnDemand for EventLoop<T> { impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
@@ -74,29 +74,8 @@ impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError> fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget), F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{ {
self.event_loop.window_target().clear_exit();
self.event_loop.run_on_demand(event_handler) self.event_loop.run_on_demand(event_handler)
} }
} }
impl EventLoopWindowTarget {
/// Clear exit status.
pub(crate) fn clear_exit(&self) {
self.p.clear_exit()
}
}
/// ```compile_fail
/// use winit::event_loop::EventLoop;
/// use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
///
/// let mut event_loop = EventLoop::new().unwrap();
/// event_loop.run_on_demand(|_, _| {
/// // Attempt to run the event loop re-entrantly; this must fail.
/// event_loop.run_on_demand(|_, _| {});
/// });
/// ```
#[allow(dead_code)]
fn test_run_on_demand_cannot_access_event_loop() {}

View File

@@ -1,3 +1,5 @@
#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
use crate::keyboard::{KeyCode, PhysicalKey}; use crate::keyboard::{KeyCode, PhysicalKey};
// TODO: Describe what this value contains for each platform // TODO: Describe what this value contains for each platform
@@ -27,24 +29,17 @@ pub trait PhysicalKeyExtScancode {
fn from_scancode(scancode: u32) -> PhysicalKey; fn from_scancode(scancode: u32) -> PhysicalKey;
} }
impl PhysicalKeyExtScancode for PhysicalKey { impl PhysicalKeyExtScancode for KeyCode
fn to_scancode(self) -> Option<u32> { where
crate::platform_impl::physicalkey_to_scancode(self) PhysicalKey: PhysicalKeyExtScancode,
} {
fn from_scancode(scancode: u32) -> PhysicalKey {
crate::platform_impl::scancode_to_physicalkey(scancode)
}
}
impl PhysicalKeyExtScancode for KeyCode {
#[inline]
fn to_scancode(self) -> Option<u32> {
<PhysicalKey as PhysicalKeyExtScancode>::to_scancode(PhysicalKey::Code(self))
}
#[inline] #[inline]
fn from_scancode(scancode: u32) -> PhysicalKey { fn from_scancode(scancode: u32) -> PhysicalKey {
<PhysicalKey as PhysicalKeyExtScancode>::from_scancode(scancode) <PhysicalKey as PhysicalKeyExtScancode>::from_scancode(scancode)
} }
#[inline]
fn to_scancode(self) -> Option<u32> {
<PhysicalKey as PhysicalKeyExtScancode>::to_scancode(PhysicalKey::Code(self))
}
} }

View File

@@ -55,7 +55,7 @@ pub trait WindowBuilderExtStartupNotify {
fn with_activation_token(self, token: ActivationToken) -> Self; fn with_activation_token(self, token: ActivationToken) -> Self;
} }
impl EventLoopExtStartupNotify for EventLoopWindowTarget { impl<T> EventLoopExtStartupNotify for EventLoopWindowTarget<T> {
fn read_token_from_env(&self) -> Option<ActivationToken> { fn read_token_from_env(&self) -> Option<ActivationToken> {
match self.p { match self.p {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
@@ -76,7 +76,7 @@ impl WindowExtStartupNotify for Window {
impl WindowBuilderExtStartupNotify for WindowBuilder { impl WindowBuilderExtStartupNotify for WindowBuilder {
fn with_activation_token(mut self, token: ActivationToken) -> Self { fn with_activation_token(mut self, token: ActivationToken) -> Self {
self.window.platform_specific.activation_token = Some(token); self.platform_specific.activation_token = Some(token);
self self
} }
} }

View File

@@ -4,6 +4,8 @@ use crate::{
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use crate::platform_impl::{ApplicationName, Backend};
pub use crate::window::Theme; pub use crate::window::Theme;
/// Additional methods on [`EventLoopWindowTarget`] that are specific to Wayland. /// Additional methods on [`EventLoopWindowTarget`] that are specific to Wayland.
@@ -12,7 +14,7 @@ pub trait EventLoopWindowTargetExtWayland {
fn is_wayland(&self) -> bool; fn is_wayland(&self) -> bool;
} }
impl EventLoopWindowTargetExtWayland for EventLoopWindowTarget { impl<T> EventLoopWindowTargetExtWayland for EventLoopWindowTarget<T> {
#[inline] #[inline]
fn is_wayland(&self) -> bool { fn is_wayland(&self) -> bool {
self.p.is_wayland() self.p.is_wayland()
@@ -34,7 +36,7 @@ pub trait EventLoopBuilderExtWayland {
impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> { impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> {
#[inline] #[inline]
fn with_wayland(&mut self) -> &mut Self { fn with_wayland(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland); self.platform_specific.forced_backend = Some(Backend::Wayland);
self self
} }
@@ -65,10 +67,7 @@ pub trait WindowBuilderExtWayland {
impl WindowBuilderExtWayland for WindowBuilder { impl WindowBuilderExtWayland for WindowBuilder {
#[inline] #[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self { fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.window.platform_specific.name = Some(crate::platform_impl::ApplicationName::new( self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into()));
general.into(),
instance.into(),
));
self self
} }
} }

View File

@@ -27,47 +27,20 @@
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border //! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding //! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
#[cfg(web_platform)]
use web_sys::HtmlCanvasElement;
use crate::cursor::CustomCursorBuilder; use crate::cursor::CustomCursorBuilder;
use crate::event::Event; use crate::event::Event;
use crate::event_loop::{EventLoop, EventLoopWindowTarget}; use crate::event_loop::EventLoop;
#[cfg(web_platform)] use crate::event_loop::EventLoopWindowTarget;
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture; use crate::platform_impl::PlatformCustomCursorBuilder;
use crate::platform_impl::{PlatformCustomCursor, PlatformCustomCursorBuilder}; use crate::window::CustomCursor;
use crate::window::{CustomCursor, Window, WindowBuilder}; use crate::window::{Window, WindowBuilder};
use crate::SendSyncWrapper;
#[cfg(not(web_platform))] use web_sys::HtmlCanvasElement;
#[doc(hidden)]
pub struct HtmlCanvasElement;
pub trait WindowExtWebSys { pub trait WindowExtWebSys {
/// Only returns the canvas if called from inside the window context (the /// Only returns the canvas if called from inside the window.
/// main thread).
fn canvas(&self) -> Option<HtmlCanvasElement>; fn canvas(&self) -> Option<HtmlCanvasElement>;
/// Returns [`true`] if calling `event.preventDefault()` is enabled.
///
/// See [`Window::set_prevent_default()`] for more details.
fn prevent_default(&self) -> bool;
/// Sets whether `event.preventDefault()` should be called on events on the
/// canvas that have side effects.
///
/// For example, by default using the mouse wheel would cause the page to scroll, enabling this
/// would prevent that.
///
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
/// context menu with Shift+Rightclick.
fn set_prevent_default(&self, prevent_default: bool);
} }
impl WindowExtWebSys for Window { impl WindowExtWebSys for Window {
@@ -75,14 +48,6 @@ impl WindowExtWebSys for Window {
fn canvas(&self) -> Option<HtmlCanvasElement> { fn canvas(&self) -> Option<HtmlCanvasElement> {
self.window.canvas() self.window.canvas()
} }
fn prevent_default(&self) -> bool {
self.window.prevent_default()
}
fn set_prevent_default(&self, prevent_default: bool) {
self.window.set_prevent_default(prevent_default)
}
} }
pub trait WindowBuilderExtWebSys { pub trait WindowBuilderExtWebSys {
@@ -92,17 +57,16 @@ pub trait WindowBuilderExtWebSys {
/// In any case, the canvas won't be automatically inserted into the web page. /// In any case, the canvas won't be automatically inserted into the web page.
/// ///
/// [`None`] by default. /// [`None`] by default.
#[cfg_attr(
not(web_platform),
doc = "",
doc = "[`HtmlCanvasElement`]: #only-available-on-wasm"
)]
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self; fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
/// Sets whether `event.preventDefault()` should be called on events on the /// Whether `event.preventDefault` should be automatically called to prevent event propagation
/// canvas that have side effects. /// when appropriate.
/// ///
/// See [`Window::set_prevent_default()`] for more details. /// For example, mouse wheel events are only handled by the canvas by default. This avoids
/// the default behavior of scrolling the page.
///
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
/// context menu with Shift+Rightclick.
/// ///
/// Enabled by default. /// Enabled by default.
fn with_prevent_default(self, prevent_default: bool) -> Self; fn with_prevent_default(self, prevent_default: bool) -> Self;
@@ -121,22 +85,22 @@ pub trait WindowBuilderExtWebSys {
impl WindowBuilderExtWebSys for WindowBuilder { impl WindowBuilderExtWebSys for WindowBuilder {
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self { fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
self.window.platform_specific.set_canvas(canvas); self.platform_specific.canvas = SendSyncWrapper(canvas);
self self
} }
fn with_prevent_default(mut self, prevent_default: bool) -> Self { fn with_prevent_default(mut self, prevent_default: bool) -> Self {
self.window.platform_specific.prevent_default = prevent_default; self.platform_specific.prevent_default = prevent_default;
self self
} }
fn with_focusable(mut self, focusable: bool) -> Self { fn with_focusable(mut self, focusable: bool) -> Self {
self.window.platform_specific.focusable = focusable; self.platform_specific.focusable = focusable;
self self
} }
fn with_append(mut self, append: bool) -> Self { fn with_append(mut self, append: bool) -> Self {
self.window.platform_specific.append = append; self.platform_specific.append = append;
self self
} }
} }
@@ -150,11 +114,11 @@ pub trait EventLoopExtWebSys {
/// ///
/// Unlike /// Unlike
#[cfg_attr( #[cfg_attr(
all(web_platform, target_feature = "exception-handling"), all(wasm_platform, target_feature = "exception-handling"),
doc = "`run()`" doc = "`run()`"
)] )]
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(wasm_platform, target_feature = "exception-handling")),
doc = "[`run()`]" doc = "[`run()`]"
)] )]
/// [^1], this returns immediately, and doesn't throw an exception in order to /// [^1], this returns immediately, and doesn't throw an exception in order to
@@ -166,13 +130,13 @@ pub trait EventLoopExtWebSys {
/// event loop when switching between tabs on a single page application. /// event loop when switching between tabs on a single page application.
/// ///
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(wasm_platform, target_feature = "exception-handling")),
doc = "[`run()`]: EventLoop::run()" doc = "[`run()`]: EventLoop::run()"
)] )]
/// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`. /// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`.
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget); F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
} }
impl<T> EventLoopExtWebSys for EventLoop<T> { impl<T> EventLoopExtWebSys for EventLoop<T> {
@@ -180,7 +144,7 @@ impl<T> EventLoopExtWebSys for EventLoop<T> {
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget), F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{ {
self.event_loop.spawn(event_handler) self.event_loop.spawn(event_handler)
} }
@@ -202,7 +166,7 @@ pub trait EventLoopWindowTargetExtWebSys {
fn poll_strategy(&self) -> PollStrategy; fn poll_strategy(&self) -> PollStrategy;
} }
impl EventLoopWindowTargetExtWebSys for EventLoopWindowTarget { impl<T> EventLoopWindowTargetExtWebSys for EventLoopWindowTarget<T> {
#[inline] #[inline]
fn set_poll_strategy(&self, strategy: PollStrategy) { fn set_poll_strategy(&self, strategy: PollStrategy) {
self.p.set_poll_strategy(strategy); self.p.set_poll_strategy(strategy);
@@ -241,29 +205,15 @@ pub enum PollStrategy {
} }
pub trait CustomCursorExtWebSys { pub trait CustomCursorExtWebSys {
/// Returns if this cursor is an animation.
fn is_animation(&self) -> bool;
/// Creates a new cursor from a URL pointing to an image. /// Creates a new cursor from a URL pointing to an image.
/// It uses the [url css function](https://developer.mozilla.org/en-US/docs/Web/CSS/url), /// It uses the [url css function](https://developer.mozilla.org/en-US/docs/Web/CSS/url),
/// but browser support for image formats is inconsistent. Using [PNG] is recommended. /// but browser support for image formats is inconsistent. Using [PNG] is recommended.
/// ///
/// [PNG]: https://en.wikipedia.org/wiki/PNG /// [PNG]: https://en.wikipedia.org/wiki/PNG
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder; fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder;
/// Crates a new animated cursor from multiple [`CustomCursor`]s.
/// Supplied `cursors` can't be empty or other animations.
fn from_animation(
duration: Duration,
cursors: Vec<CustomCursor>,
) -> Result<CustomCursorBuilder, BadAnimation>;
} }
impl CustomCursorExtWebSys for CustomCursor { impl CustomCursorExtWebSys for CustomCursor {
fn is_animation(&self) -> bool {
self.inner.animation
}
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder { fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder {
CustomCursorBuilder { CustomCursorBuilder {
inner: PlatformCustomCursorBuilder::Url { inner: PlatformCustomCursorBuilder::Url {
@@ -273,92 +223,4 @@ impl CustomCursorExtWebSys for CustomCursor {
}, },
} }
} }
fn from_animation(
duration: Duration,
cursors: Vec<CustomCursor>,
) -> Result<CustomCursorBuilder, BadAnimation> {
if cursors.is_empty() {
return Err(BadAnimation::Empty);
}
if cursors.iter().any(CustomCursor::is_animation) {
return Err(BadAnimation::Animation);
}
Ok(CustomCursorBuilder {
inner: PlatformCustomCursorBuilder::Animation { duration, cursors },
})
}
}
/// An error produced when using [`CustomCursor::from_animation`] with invalid arguments.
#[derive(Debug, Clone)]
pub enum BadAnimation {
/// Produced when no cursors were supplied.
Empty,
/// Produced when a supplied cursor is an animation.
Animation,
}
impl fmt::Display for BadAnimation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => write!(f, "No cursors supplied"),
Self::Animation => write!(f, "A supplied cursor is an animtion"),
}
}
}
impl Error for BadAnimation {}
pub trait CustomCursorBuilderExtWebSys {
/// Async version of [`CustomCursorBuilder::build()`] which waits until the
/// cursor has completely finished loading.
fn build_async(self, window_target: &EventLoopWindowTarget) -> CustomCursorFuture;
}
impl CustomCursorBuilderExtWebSys for CustomCursorBuilder {
fn build_async(self, window_target: &EventLoopWindowTarget) -> CustomCursorFuture {
CustomCursorFuture(PlatformCustomCursor::build_async(
self.inner,
&window_target.p,
))
}
}
#[cfg(not(web_platform))]
struct PlatformCustomCursorFuture;
#[derive(Debug)]
pub struct CustomCursorFuture(PlatformCustomCursorFuture);
impl Future for CustomCursorFuture {
type Output = Result<CustomCursor, CustomCursorError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0)
.poll(cx)
.map_ok(|cursor| CustomCursor { inner: cursor })
}
}
#[derive(Clone, Debug)]
pub enum CustomCursorError {
Blob,
Decode(String),
Animation,
}
impl Display for CustomCursorError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Blob => write!(f, "failed to create `Blob`"),
Self::Decode(error) => write!(f, "failed to decode image: {error}"),
Self::Animation => write!(
f,
"found `CustomCursor` that is an animation when building an animation"
),
}
}
} }

View File

@@ -2,9 +2,12 @@ use std::{ffi::c_void, path::Path};
use crate::{ use crate::{
dpi::PhysicalSize, dpi::PhysicalSize,
event::DeviceId, event::{DeviceId, KeyEvent},
event_loop::EventLoopBuilder, event_loop::EventLoopBuilder,
keyboard::Key,
monitor::MonitorHandle, monitor::MonitorHandle,
platform::modifier_supplement::KeyEventExtModifierSupplement,
platform_impl::WinIcon,
window::{BadIcon, Icon, Window, WindowBuilder}, window::{BadIcon, Icon, Window, WindowBuilder},
}; };
@@ -15,92 +18,6 @@ pub type HMENU = isize;
/// Monitor Handle type used by Win32 API /// Monitor Handle type used by Win32 API
pub type HMONITOR = isize; pub type HMONITOR = isize;
/// Describes a system-drawn backdrop material of a window.
///
/// For a detailed explanation, see [`DWM_SYSTEMBACKDROP_TYPE docs`].
///
/// [`DWM_SYSTEMBACKDROP_TYPE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BackdropType {
/// Corresponds to `DWMSBT_AUTO`.
///
/// Usually draws a default backdrop effect on the title bar.
#[default]
Auto = 0,
/// Corresponds to `DWMSBT_NONE`.
None = 1,
/// Corresponds to `DWMSBT_MAINWINDOW`.
///
/// Draws the Mica backdrop material.
MainWindow = 2,
/// Corresponds to `DWMSBT_TRANSIENTWINDOW`.
///
/// Draws the Background Acrylic backdrop material.
TransientWindow = 3,
/// Corresponds to `DWMSBT_TABBEDWINDOW`.
///
/// Draws the Alt Mica backdrop material.
TabbedWindow = 4,
}
/// Describes a color used by Windows
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Color(u32);
impl Color {
/// Use the system's default color
pub const SYSTEM_DEFAULT: Color = Color(0xFFFFFFFF);
//Special constant only valid for the window border and therefore modeled using Option<Color> for user facing code
const NONE: Color = Color(0xFFFFFFFE);
/// Create a new color from the given RGB values
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self((r as u32) | ((g as u32) << 8) | ((b as u32) << 16))
}
}
impl Default for Color {
fn default() -> Self {
Self::SYSTEM_DEFAULT
}
}
/// Describes how the corners of a window should look like.
///
/// For a detailed explanation, see [`DWM_WINDOW_CORNER_PREFERENCE docs`].
///
/// [`DWM_WINDOW_CORNER_PREFERENCE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference
#[repr(i32)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CornerPreference {
/// Corresponds to `DWMWCP_DEFAULT`.
///
/// Let the system decide when to round window corners.
#[default]
Default = 0,
/// Corresponds to `DWMWCP_DONOTROUND`.
///
/// Never round window corners.
DoNotRound = 1,
/// Corresponds to `DWMWCP_ROUND`.
///
/// Round the corners, if appropriate.
Round = 2,
/// Corresponds to `DWMWCP_ROUNDSMALL`.
///
/// Round the corners if appropriate, with a small radius.
RoundSmall = 3,
}
/// Additional methods on `EventLoop` that are specific to Windows. /// Additional methods on `EventLoop` that are specific to Windows.
pub trait EventLoopBuilderExtWindows { pub trait EventLoopBuilderExtWindows {
/// Whether to allow the event loop to be created off of the main thread. /// Whether to allow the event loop to be created off of the main thread.
@@ -219,31 +136,6 @@ pub trait WindowExtWindows {
/// ///
/// Enabling the shadow causes a thin 1px line to appear on the top of the window. /// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn set_undecorated_shadow(&self, shadow: bool); fn set_undecorated_shadow(&self, shadow: bool);
/// Sets system-drawn backdrop type.
///
/// Requires Windows 11 build 22523+.
fn set_system_backdrop(&self, backdrop_type: BackdropType);
/// Sets the color of the window border.
///
/// Supported starting with Windows 11 Build 22000.
fn set_border_color(&self, color: Option<Color>);
/// Sets the background color of the title bar.
///
/// Supported starting with Windows 11 Build 22000.
fn set_title_background_color(&self, color: Option<Color>);
/// Sets the color of the window title.
///
/// Supported starting with Windows 11 Build 22000.
fn set_title_text_color(&self, color: Color);
/// Sets the preferred style of the window corners.
///
/// Supported starting with Windows 11 Build 22000.
fn set_corner_preference(&self, preference: CornerPreference);
} }
impl WindowExtWindows for Window { impl WindowExtWindows for Window {
@@ -266,34 +158,6 @@ impl WindowExtWindows for Window {
fn set_undecorated_shadow(&self, shadow: bool) { fn set_undecorated_shadow(&self, shadow: bool) {
self.window.set_undecorated_shadow(shadow) self.window.set_undecorated_shadow(shadow)
} }
#[inline]
fn set_system_backdrop(&self, backdrop_type: BackdropType) {
self.window.set_system_backdrop(backdrop_type)
}
#[inline]
fn set_border_color(&self, color: Option<Color>) {
self.window.set_border_color(color.unwrap_or(Color::NONE))
}
#[inline]
fn set_title_background_color(&self, color: Option<Color>) {
// The windows docs don't mention NONE as a valid options but it works in practice and is useful
// to circumvent the Windows option "Show accent color on title bars and window borders"
self.window
.set_title_background_color(color.unwrap_or(Color::NONE))
}
#[inline]
fn set_title_text_color(&self, color: Color) {
self.window.set_title_text_color(color)
}
#[inline]
fn set_corner_preference(&self, preference: CornerPreference) {
self.window.set_corner_preference(preference)
}
} }
/// Additional methods on `WindowBuilder` that are specific to Windows. /// Additional methods on `WindowBuilder` that are specific to Windows.
@@ -321,14 +185,7 @@ pub trait WindowBuilderExtWindows {
/// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look. /// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look.
/// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect. /// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect.
/// ///
#[cfg_attr( /// [`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu
platform_windows,
doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
)]
#[cfg_attr(
not(platform_windows),
doc = "[`CreateMenu`]: #only-available-on-windows"
)]
fn with_menu(self, menu: HMENU) -> Self; fn with_menu(self, menu: HMENU) -> Self;
/// This sets `ICON_BIG`. A good ceiling here is 256x256. /// This sets `ICON_BIG`. A good ceiling here is 256x256.
@@ -356,118 +213,54 @@ pub trait WindowBuilderExtWindows {
/// The shadow is hidden by default. /// The shadow is hidden by default.
/// Enabling the shadow causes a thin 1px line to appear on the top of the window. /// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn with_undecorated_shadow(self, shadow: bool) -> Self; fn with_undecorated_shadow(self, shadow: bool) -> Self;
/// Sets system-drawn backdrop type.
///
/// Requires Windows 11 build 22523+.
fn with_system_backdrop(self, backdrop_type: BackdropType) -> Self;
/// This sets or removes `WS_CLIPCHILDREN` style.
fn with_clip_children(self, flag: bool) -> Self;
/// Sets the color of the window border.
///
/// Supported starting with Windows 11 Build 22000.
fn with_border_color(self, color: Option<Color>) -> Self;
/// Sets the background color of the title bar.
///
/// Supported starting with Windows 11 Build 22000.
fn with_title_background_color(self, color: Option<Color>) -> Self;
/// Sets the color of the window title.
///
/// Supported starting with Windows 11 Build 22000.
fn with_title_text_color(self, color: Color) -> Self;
/// Sets the preferred style of the window corners.
///
/// Supported starting with Windows 11 Build 22000.
fn with_corner_preference(self, corners: CornerPreference) -> Self;
} }
impl WindowBuilderExtWindows for WindowBuilder { impl WindowBuilderExtWindows for WindowBuilder {
#[inline] #[inline]
fn with_owner_window(mut self, parent: HWND) -> Self { fn with_owner_window(mut self, parent: HWND) -> Self {
self.window.platform_specific.owner = Some(parent); self.platform_specific.owner = Some(parent);
self self
} }
#[inline] #[inline]
fn with_menu(mut self, menu: HMENU) -> Self { fn with_menu(mut self, menu: HMENU) -> Self {
self.window.platform_specific.menu = Some(menu); self.platform_specific.menu = Some(menu);
self self
} }
#[inline] #[inline]
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self { fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self {
self.window.platform_specific.taskbar_icon = taskbar_icon; self.platform_specific.taskbar_icon = taskbar_icon;
self self
} }
#[inline] #[inline]
fn with_no_redirection_bitmap(mut self, flag: bool) -> Self { fn with_no_redirection_bitmap(mut self, flag: bool) -> Self {
self.window.platform_specific.no_redirection_bitmap = flag; self.platform_specific.no_redirection_bitmap = flag;
self self
} }
#[inline] #[inline]
fn with_drag_and_drop(mut self, flag: bool) -> Self { fn with_drag_and_drop(mut self, flag: bool) -> Self {
self.window.platform_specific.drag_and_drop = flag; self.platform_specific.drag_and_drop = flag;
self self
} }
#[inline] #[inline]
fn with_skip_taskbar(mut self, skip: bool) -> Self { fn with_skip_taskbar(mut self, skip: bool) -> Self {
self.window.platform_specific.skip_taskbar = skip; self.platform_specific.skip_taskbar = skip;
self self
} }
#[inline] #[inline]
fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self { fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self {
self.window.platform_specific.class_name = class_name.into(); self.platform_specific.class_name = class_name.into();
self self
} }
#[inline] #[inline]
fn with_undecorated_shadow(mut self, shadow: bool) -> Self { fn with_undecorated_shadow(mut self, shadow: bool) -> Self {
self.window.platform_specific.decoration_shadow = shadow; self.platform_specific.decoration_shadow = shadow;
self
}
#[inline]
fn with_system_backdrop(mut self, backdrop_type: BackdropType) -> Self {
self.window.platform_specific.backdrop_type = backdrop_type;
self
}
#[inline]
fn with_clip_children(mut self, flag: bool) -> Self {
self.window.platform_specific.clip_children = flag;
self
}
#[inline]
fn with_border_color(mut self, color: Option<Color>) -> Self {
self.window.platform_specific.border_color = Some(color.unwrap_or(Color::NONE));
self
}
#[inline]
fn with_title_background_color(mut self, color: Option<Color>) -> Self {
self.window.platform_specific.title_background_color = Some(color.unwrap_or(Color::NONE));
self
}
#[inline]
fn with_title_text_color(mut self, color: Color) -> Self {
self.window.platform_specific.title_text_color = Some(color);
self
}
#[inline]
fn with_corner_preference(mut self, corners: CornerPreference) -> Self {
self.window.platform_specific.corner_preference = Some(corners);
self self
} }
} }
@@ -535,12 +328,27 @@ impl IconExtWindows for Icon {
path: P, path: P,
size: Option<PhysicalSize<u32>>, size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> { ) -> Result<Self, BadIcon> {
let win_icon = crate::platform_impl::WinIcon::from_path(path, size)?; let win_icon = WinIcon::from_path(path, size)?;
Ok(Icon { inner: win_icon }) Ok(Icon { inner: win_icon })
} }
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> { fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
let win_icon = crate::platform_impl::WinIcon::from_resource(ordinal, size)?; let win_icon = WinIcon::from_resource(ordinal, size)?;
Ok(Icon { inner: win_icon }) Ok(Icon { inner: win_icon })
} }
} }
impl KeyEventExtModifierSupplement for KeyEvent {
#[inline]
fn text_with_all_modifiers(&self) -> Option<&str> {
self.platform_specific
.text_with_all_modifers
.as_ref()
.map(|s| s.as_str())
}
#[inline]
fn key_without_modifiers(&self) -> Key {
self.platform_specific.key_without_modifiers.clone()
}
}

View File

@@ -1,6 +1,3 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget}, event_loop::{EventLoopBuilder, EventLoopWindowTarget},
monitor::MonitorHandle, monitor::MonitorHandle,
@@ -8,50 +5,9 @@ use crate::{
}; };
use crate::dpi::Size; use crate::dpi::Size;
use crate::platform_impl::{ApplicationName, Backend, XLIB_ERROR_HOOKS};
/// X window type. Maps directly to pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported};
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum WindowType {
/// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
/// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
/// root window clicks.
Desktop,
/// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
Dock,
/// Toolbar windows. "Torn off" from the main application.
Toolbar,
/// Pinnable menu windows. "Torn off" from the main application.
Menu,
/// A small persistent utility window, such as a palette or toolbox.
Utility,
/// The window is a splash screen displayed as an application is starting up.
Splash,
/// This is a dialog window.
Dialog,
/// A dropdown menu that usually appears when the user clicks on an item in a menu bar.
/// This property is typically used on override-redirect windows.
DropdownMenu,
/// A popup menu that usually appears when the user right clicks on an object.
/// This property is typically used on override-redirect windows.
PopupMenu,
/// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.
/// This property is typically used on override-redirect windows.
Tooltip,
/// The window is a notification.
/// This property is typically used on override-redirect windows.
Notification,
/// This should be used on the windows that are popped up by combo boxes.
/// This property is typically used on override-redirect windows.
Combo,
/// This indicates the the window is being dragged.
/// This property is typically used on override-redirect windows.
Dnd,
/// This is a normal, top-level window.
#[default]
Normal,
}
/// The first argument in the provided hook will be the pointer to `XDisplay` /// The first argument in the provided hook will be the pointer to `XDisplay`
/// and the second one the pointer to [`XErrorEvent`]. The returned `bool` is an /// and the second one the pointer to [`XErrorEvent`]. The returned `bool` is an
@@ -82,10 +38,7 @@ pub type XWindow = u32;
pub fn register_xlib_error_hook(hook: XlibErrorHook) { pub fn register_xlib_error_hook(hook: XlibErrorHook) {
// Append new hook. // Append new hook.
unsafe { unsafe {
crate::platform_impl::XLIB_ERROR_HOOKS XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
.lock()
.unwrap()
.push(hook);
} }
} }
@@ -95,7 +48,7 @@ pub trait EventLoopWindowTargetExtX11 {
fn is_x11(&self) -> bool; fn is_x11(&self) -> bool;
} }
impl EventLoopWindowTargetExtX11 for EventLoopWindowTarget { impl<T> EventLoopWindowTargetExtX11 for EventLoopWindowTarget<T> {
#[inline] #[inline]
fn is_x11(&self) -> bool { fn is_x11(&self) -> bool {
!self.p.is_wayland() !self.p.is_wayland()
@@ -117,7 +70,7 @@ pub trait EventLoopBuilderExtX11 {
impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> { impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> {
#[inline] #[inline]
fn with_x11(&mut self) -> &mut Self { fn with_x11(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X); self.platform_specific.forced_backend = Some(Backend::X);
self self
} }
@@ -143,29 +96,29 @@ pub trait WindowBuilderExtX11 {
/// Build window with the given `general` and `instance` names. /// Build window with the given `general` and `instance` names.
/// ///
/// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the /// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the
/// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "instance", "general"`. /// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "general", "instance"`.
/// ///
/// For details about application ID conventions, see the /// For details about application ID conventions, see the
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id) /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self; fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;
/// Build window with override-redirect flag; defaults to false. /// Build window with override-redirect flag; defaults to false. Only relevant on X11.
fn with_override_redirect(self, override_redirect: bool) -> Self; fn with_override_redirect(self, override_redirect: bool) -> Self;
/// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. /// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. Only relevant on X11.
fn with_x11_window_type(self, x11_window_type: Vec<WindowType>) -> Self; fn with_x11_window_type(self, x11_window_type: Vec<XWindowType>) -> Self;
/// Build window with base size hint. /// Build window with base size hint. Only implemented on X11.
/// ///
/// ``` /// ```
/// # use winit::dpi::{LogicalSize, PhysicalSize}; /// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::window::Window; /// # use winit::window::WindowBuilder;
/// # use winit::platform::x11::WindowBuilderExtX11; /// # use winit::platform::x11::WindowBuilderExtX11;
/// // Specify the size in logical dimensions like this: /// // Specify the size in logical dimensions like this:
/// Window::builder().with_base_size(LogicalSize::new(400.0, 200.0)); /// WindowBuilder::new().with_base_size(LogicalSize::new(400.0, 200.0));
/// ///
/// // Or specify the size in physical dimensions like this: /// // Or specify the size in physical dimensions like this:
/// Window::builder().with_base_size(PhysicalSize::new(400, 200)); /// WindowBuilder::new().with_base_size(PhysicalSize::new(400, 200));
/// ``` /// ```
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self; fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
@@ -174,12 +127,12 @@ pub trait WindowBuilderExtX11 {
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
/// use winit::window::Window; /// use winit::window::WindowBuilder;
/// use winit::platform::x11::{XWindow, WindowBuilderExtX11}; /// use winit::platform::x11::{XWindow, WindowBuilderExtX11};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> { /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let event_loop = winit::event_loop::EventLoop::new().unwrap(); /// let event_loop = winit::event_loop::EventLoop::new().unwrap();
/// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?; /// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?;
/// let window = Window::builder() /// let window = WindowBuilder::new()
/// .with_embed_parent_window(parent_window_id) /// .with_embed_parent_window(parent_window_id)
/// .build(&event_loop)?; /// .build(&event_loop)?;
/// # Ok(()) } /// # Ok(()) }
@@ -190,46 +143,43 @@ pub trait WindowBuilderExtX11 {
impl WindowBuilderExtX11 for WindowBuilder { impl WindowBuilderExtX11 for WindowBuilder {
#[inline] #[inline]
fn with_x11_visual(mut self, visual_id: XVisualID) -> Self { fn with_x11_visual(mut self, visual_id: XVisualID) -> Self {
self.window.platform_specific.x11.visual_id = Some(visual_id); self.platform_specific.x11.visual_id = Some(visual_id);
self self
} }
#[inline] #[inline]
fn with_x11_screen(mut self, screen_id: i32) -> Self { fn with_x11_screen(mut self, screen_id: i32) -> Self {
self.window.platform_specific.x11.screen_id = Some(screen_id); self.platform_specific.x11.screen_id = Some(screen_id);
self self
} }
#[inline] #[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self { fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.window.platform_specific.name = Some(crate::platform_impl::ApplicationName::new( self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into()));
general.into(),
instance.into(),
));
self self
} }
#[inline] #[inline]
fn with_override_redirect(mut self, override_redirect: bool) -> Self { fn with_override_redirect(mut self, override_redirect: bool) -> Self {
self.window.platform_specific.x11.override_redirect = override_redirect; self.platform_specific.x11.override_redirect = override_redirect;
self self
} }
#[inline] #[inline]
fn with_x11_window_type(mut self, x11_window_types: Vec<WindowType>) -> Self { fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self {
self.window.platform_specific.x11.x11_window_types = x11_window_types; self.platform_specific.x11.x11_window_types = x11_window_types;
self self
} }
#[inline] #[inline]
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self { fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
self.window.platform_specific.x11.base_size = Some(base_size.into()); self.platform_specific.x11.base_size = Some(base_size.into());
self self
} }
#[inline] #[inline]
fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self { fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self {
self.window.platform_specific.x11.embed_window = Some(parent_window_id); self.platform_specific.x11.embed_window = Some(parent_window_id);
self self
} }
} }

View File

@@ -4,7 +4,6 @@ use std::{
cell::Cell, cell::Cell,
collections::VecDeque, collections::VecDeque,
hash::Hash, hash::Hash,
marker::PhantomData,
sync::{ sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
mpsc, Arc, Mutex, RwLock, mpsc, Arc, Mutex, RwLock,
@@ -16,11 +15,9 @@ use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
use android_activity::{ use android_activity::{
AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect, AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect,
}; };
use log::{debug, trace, warn};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::{ use crate::{
cursor::Cursor,
dpi::{PhysicalPosition, PhysicalSize, Position, Size}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error, error,
event::{self, Force, InnerSizeWriter, StartCause}, event::{self, Force, InnerSizeWriter, StartCause},
@@ -141,7 +138,7 @@ pub struct KeyEventExtra {}
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
android_app: AndroidApp, android_app: AndroidApp,
window_target: event_loop::EventLoopWindowTarget, window_target: event_loop::EventLoopWindowTarget<T>,
redraw_flag: SharedFlag, redraw_flag: SharedFlag,
user_events_sender: mpsc::Sender<T>, user_events_sender: mpsc::Sender<T>,
user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent
@@ -188,8 +185,9 @@ impl<T: 'static> EventLoop<T> {
&redraw_flag, &redraw_flag,
android_app.create_waker(), android_app.create_waker(),
), ),
_marker: std::marker::PhantomData,
}, },
_marker: PhantomData, _marker: std::marker::PhantomData,
}, },
redraw_flag, redraw_flag,
user_events_sender, user_events_sender,
@@ -205,7 +203,7 @@ impl<T: 'static> EventLoop<T> {
fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F) fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
where where
F: FnMut(event::Event<T>, &RootELW), F: FnMut(event::Event<T>, &RootELW<T>),
{ {
trace!("Mainloop iteration"); trace!("Mainloop iteration");
@@ -377,7 +375,7 @@ impl<T: 'static> EventLoop<T> {
callback: &mut F, callback: &mut F,
) -> InputStatus ) -> InputStatus
where where
F: FnMut(event::Event<T>, &RootELW), F: FnMut(event::Event<T>, &RootELW<T>),
{ {
let mut input_status = InputStatus::Handled; let mut input_status = InputStatus::Handled;
match event { match event {
@@ -482,15 +480,19 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError> pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget), F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>),
{ {
self.run_on_demand(event_handler) self.run_on_demand(event_handler)
} }
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget), F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>),
{ {
if self.loop_running {
return Err(EventLoopError::AlreadyRunning);
}
loop { loop {
match self.pump_events(None, &mut event_handler) { match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
@@ -508,7 +510,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where where
F: FnMut(event::Event<T>, &RootELW), F: FnMut(event::Event<T>, &RootELW<T>),
{ {
if !self.loop_running { if !self.loop_running {
self.loop_running = true; self.loop_running = true;
@@ -541,7 +543,7 @@ impl<T: 'static> EventLoop<T> {
fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where where
F: FnMut(event::Event<T>, &RootELW), F: FnMut(event::Event<T>, &RootELW<T>),
{ {
let start = Instant::now(); let start = Instant::now();
@@ -617,7 +619,7 @@ impl<T: 'static> EventLoop<T> {
}); });
} }
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget { pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {
&self.window_target &self.window_target
} }
@@ -661,14 +663,15 @@ impl<T> EventLoopProxy<T> {
} }
} }
pub struct EventLoopWindowTarget { pub struct EventLoopWindowTarget<T: 'static> {
app: AndroidApp, app: AndroidApp,
control_flow: Cell<ControlFlow>, control_flow: Cell<ControlFlow>,
exit: Cell<bool>, exit: Cell<bool>,
redraw_requester: RedrawRequester, redraw_requester: RedrawRequester,
_marker: std::marker::PhantomData<T>,
} }
impl EventLoopWindowTarget { impl<T: 'static> EventLoopWindowTarget<T> {
pub fn primary_monitor(&self) -> Option<MonitorHandle> { pub fn primary_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle::new(self.app.clone())) Some(MonitorHandle::new(self.app.clone()))
} }
@@ -710,36 +713,9 @@ impl EventLoopWindowTarget {
self.exit.set(true) self.exit.set(true)
} }
pub(crate) fn clear_exit(&self) {
self.exit.set(false)
}
pub(crate) fn exiting(&self) -> bool { pub(crate) fn exiting(&self) -> bool {
self.exit.get() self.exit.get()
} }
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle
}
}
#[derive(Clone)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_05")]
#[inline]
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
rwh_05::AndroidDisplayHandle::empty().into()
}
#[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)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
@@ -775,15 +751,28 @@ impl DeviceId {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PlatformSpecificWindowBuilderAttributes; pub struct PlatformSpecificWindowBuilderAttributes;
#[derive(Debug, Clone)]
pub(crate) struct OwnedWindowHandle {}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
// Parent windows are currently unsupported, though owned window
// handles would be implementable.
Self {}
}
}
pub(crate) struct Window { pub(crate) struct Window {
app: AndroidApp, app: AndroidApp,
redraw_requester: RedrawRequester, redraw_requester: RedrawRequester,
} }
impl Window { impl Window {
pub(crate) fn new( pub(crate) fn new<T: 'static>(
el: &EventLoopWindowTarget, el: &EventLoopWindowTarget<T>,
_window_attrs: window::WindowAttributes, _window_attrs: window::WindowAttributes,
_: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, error::OsError> { ) -> Result<Self, error::OsError> {
// FIXME this ignores requested window attributes // FIXME this ignores requested window attributes
@@ -927,7 +916,9 @@ impl Window {
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {} pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
pub fn set_cursor(&self, _: Cursor) {} pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {}
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> { pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported( Err(error::ExternalError::NotSupported(
@@ -1107,11 +1098,11 @@ impl MonitorHandle {
None None
} }
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let size = self.size().into(); let size = self.size().into();
// FIXME this is not the real refresh rate // FIXME this is not the real refresh rate
// (it is guaranteed to support 32 bit color though) // (it is guaranteed to support 32 bit color though)
std::iter::once(VideoModeHandle { std::iter::once(VideoMode {
size, size,
bit_depth: 32, bit_depth: 32,
refresh_rate_millihertz: 60000, refresh_rate_millihertz: 60000,
@@ -1121,14 +1112,14 @@ impl MonitorHandle {
} }
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct VideoModeHandle { pub struct VideoMode {
size: (u32, u32), size: (u32, u32),
bit_depth: u16, bit_depth: u16,
refresh_rate_millihertz: u32, refresh_rate_millihertz: u32,
monitor: MonitorHandle, monitor: MonitorHandle,
} }
impl VideoModeHandle { impl VideoMode {
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
self.size.into() self.size.into()
} }

View File

@@ -3,7 +3,7 @@
use std::{ use std::{
cell::{RefCell, RefMut}, cell::{RefCell, RefMut},
collections::HashSet, collections::HashSet,
fmt, mem, mem,
os::raw::c_void, os::raw::c_void,
ptr, ptr,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@@ -24,12 +24,13 @@ use objc2::runtime::AnyObject;
use objc2::{msg_send, sel}; use objc2::{msg_send, sel};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use super::event_loop::{EventHandler, Never};
use super::uikit::UIView; use super::uikit::UIView;
use super::view::WinitUIWindow; use super::view::WinitUIWindow;
use crate::{ use crate::{
dpi::PhysicalSize, dpi::PhysicalSize,
event::{Event, InnerSizeWriter, StartCause, WindowEvent}, event::{Event, InnerSizeWriter, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget}, event_loop::ControlFlow,
window::WindowId as RootWindowId, window::WindowId as RootWindowId,
}; };
@@ -46,32 +47,8 @@ macro_rules! bug_assert {
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct HandlePendingUserEvents; pub enum EventWrapper {
StaticEvent(Event<Never>),
pub(crate) struct EventLoopHandler {
#[allow(clippy::type_complexity)]
pub(crate) handler: Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>,
pub(crate) event_loop: RootEventLoopWindowTarget,
}
impl fmt::Debug for EventLoopHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopHandler")
.field("handler", &"...")
.field("event_loop", &self.event_loop)
.finish()
}
}
impl EventLoopHandler {
fn handle_event(&mut self, event: Event<HandlePendingUserEvents>) {
(self.handler)(event, &self.event_loop)
}
}
#[derive(Debug)]
pub(crate) enum EventWrapper {
StaticEvent(Event<HandlePendingUserEvents>),
ScaleFactorChanged(ScaleFactorChanged), ScaleFactorChanged(ScaleFactorChanged),
} }
@@ -84,7 +61,7 @@ pub struct ScaleFactorChanged {
enum UserCallbackTransitionResult<'a> { enum UserCallbackTransitionResult<'a> {
Success { Success {
handler: EventLoopHandler, event_handler: Box<dyn EventHandler>,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
processing_redraws: bool, processing_redraws: bool,
}, },
@@ -93,7 +70,7 @@ enum UserCallbackTransitionResult<'a> {
}, },
} }
impl Event<HandlePendingUserEvents> { impl Event<Never> {
fn is_redraw(&self) -> bool { fn is_redraw(&self) -> bool {
matches!( matches!(
self, self,
@@ -117,11 +94,11 @@ enum AppStateImpl {
Launching { Launching {
queued_windows: Vec<Id<WinitUIWindow>>, queued_windows: Vec<Id<WinitUIWindow>>,
queued_events: Vec<EventWrapper>, queued_events: Vec<EventWrapper>,
queued_handler: EventLoopHandler, queued_event_handler: Box<dyn EventHandler>,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
}, },
ProcessingEvents { ProcessingEvents {
handler: EventLoopHandler, event_handler: Box<dyn EventHandler>,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
@@ -131,15 +108,15 @@ enum AppStateImpl {
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
}, },
ProcessingRedraws { ProcessingRedraws {
handler: EventLoopHandler, event_handler: Box<dyn EventHandler>,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
Waiting { Waiting {
waiting_handler: EventLoopHandler, waiting_event_handler: Box<dyn EventHandler>,
start: Instant, start: Instant,
}, },
PollFinished { PollFinished {
waiting_handler: EventLoopHandler, waiting_event_handler: Box<dyn EventHandler>,
}, },
Terminated, Terminated,
} }
@@ -227,7 +204,7 @@ impl AppState {
matches!(self.state(), AppStateImpl::Terminated) matches!(self.state(), AppStateImpl::Terminated)
} }
fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) { fn will_launch_transition(&mut self, queued_event_handler: Box<dyn EventHandler>) {
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() { let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::NotLaunched { AppStateImpl::NotLaunched {
queued_windows, queued_windows,
@@ -239,28 +216,28 @@ impl AppState {
self.set_state(AppStateImpl::Launching { self.set_state(AppStateImpl::Launching {
queued_windows, queued_windows,
queued_events, queued_events,
queued_handler, queued_event_handler,
queued_gpu_redraws, queued_gpu_redraws,
}); });
} }
fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) { fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) {
let (windows, events, handler, queued_gpu_redraws) = match self.take_state() { let (windows, events, event_handler, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Launching { AppStateImpl::Launching {
queued_windows, queued_windows,
queued_events, queued_events,
queued_handler, queued_event_handler,
queued_gpu_redraws, queued_gpu_redraws,
} => ( } => (
queued_windows, queued_windows,
queued_events, queued_events,
queued_handler, queued_event_handler,
queued_gpu_redraws, queued_gpu_redraws,
), ),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingEvents { self.set_state(AppStateImpl::ProcessingEvents {
handler, event_handler,
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
queued_gpu_redraws, queued_gpu_redraws,
}); });
@@ -274,19 +251,24 @@ impl AppState {
return None; return None;
} }
let (handler, event) = match (self.control_flow, self.take_state()) { let (event_handler, event) = match (self.control_flow, self.take_state()) {
(ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => ( (
waiting_handler, ControlFlow::Poll,
AppStateImpl::PollFinished {
waiting_event_handler,
},
) => (
waiting_event_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)), EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)),
), ),
( (
ControlFlow::Wait, ControlFlow::Wait,
AppStateImpl::Waiting { AppStateImpl::Waiting {
waiting_handler, waiting_event_handler,
start, start,
}, },
) => ( ) => (
waiting_handler, waiting_event_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled { EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
start, start,
requested_resume: None, requested_resume: None,
@@ -295,7 +277,7 @@ impl AppState {
( (
ControlFlow::WaitUntil(requested_resume), ControlFlow::WaitUntil(requested_resume),
AppStateImpl::Waiting { AppStateImpl::Waiting {
waiting_handler, waiting_event_handler,
start, start,
}, },
) => { ) => {
@@ -310,13 +292,13 @@ impl AppState {
requested_resume: Some(requested_resume), requested_resume: Some(requested_resume),
})) }))
}; };
(waiting_handler, event) (waiting_event_handler, event)
} }
s => bug!("`EventHandler` unexpectedly woke up {:?}", s), s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingEvents { self.set_state(AppStateImpl::ProcessingEvents {
handler, event_handler,
queued_gpu_redraws: Default::default(), queued_gpu_redraws: Default::default(),
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
}); });
@@ -361,20 +343,25 @@ impl AppState {
} }
} }
let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) = let (event_handler, queued_gpu_redraws, active_control_flow, processing_redraws) =
match self.take_state() { match self.take_state() {
AppStateImpl::Launching { .. } AppStateImpl::Launching { .. }
| AppStateImpl::NotLaunched { .. } | AppStateImpl::NotLaunched { .. }
| AppStateImpl::InUserCallback { .. } => unreachable!(), | AppStateImpl::InUserCallback { .. } => unreachable!(),
AppStateImpl::ProcessingEvents { AppStateImpl::ProcessingEvents {
handler, event_handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
} => (handler, queued_gpu_redraws, active_control_flow, false), } => (
AppStateImpl::ProcessingRedraws { event_handler,
handler, queued_gpu_redraws,
active_control_flow, active_control_flow,
} => (handler, Default::default(), active_control_flow, true), false,
),
AppStateImpl::ProcessingRedraws {
event_handler,
active_control_flow,
} => (event_handler, Default::default(), active_control_flow, true),
AppStateImpl::PollFinished { .. } AppStateImpl::PollFinished { .. }
| AppStateImpl::Waiting { .. } | AppStateImpl::Waiting { .. }
| AppStateImpl::Terminated => unreachable!(), | AppStateImpl::Terminated => unreachable!(),
@@ -384,23 +371,23 @@ impl AppState {
queued_gpu_redraws, queued_gpu_redraws,
}); });
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
handler, event_handler,
active_control_flow, active_control_flow,
processing_redraws, processing_redraws,
} }
} }
fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> { fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> {
let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() { let (event_handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
AppStateImpl::ProcessingEvents { AppStateImpl::ProcessingEvents {
handler, event_handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
} => (handler, queued_gpu_redraws, active_control_flow), } => (event_handler, queued_gpu_redraws, active_control_flow),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingRedraws { self.set_state(AppStateImpl::ProcessingRedraws {
handler, event_handler,
active_control_flow, active_control_flow,
}); });
queued_gpu_redraws queued_gpu_redraws
@@ -410,11 +397,11 @@ impl AppState {
if !self.has_launched() || self.has_terminated() { if !self.has_launched() || self.has_terminated() {
return; return;
} }
let (waiting_handler, old) = match self.take_state() { let (waiting_event_handler, old) = match self.take_state() {
AppStateImpl::ProcessingRedraws { AppStateImpl::ProcessingRedraws {
handler, event_handler,
active_control_flow, active_control_flow,
} => (handler, active_control_flow), } => (event_handler, active_control_flow),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
@@ -423,7 +410,7 @@ impl AppState {
(ControlFlow::Wait, ControlFlow::Wait) => { (ControlFlow::Wait, ControlFlow::Wait) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_handler, waiting_event_handler,
start, start,
}); });
} }
@@ -432,14 +419,14 @@ impl AppState {
{ {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_handler, waiting_event_handler,
start, start,
}); });
} }
(_, ControlFlow::Wait) => { (_, ControlFlow::Wait) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_handler, waiting_event_handler,
start, start,
}); });
self.waker.stop() self.waker.stop()
@@ -447,22 +434,24 @@ impl AppState {
(_, ControlFlow::WaitUntil(new_instant)) => { (_, ControlFlow::WaitUntil(new_instant)) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_handler, waiting_event_handler,
start, start,
}); });
self.waker.start_at(new_instant) self.waker.start_at(new_instant)
} }
// Unlike on macOS, handle Poll to Poll transition here to call the waker // Unlike on macOS, handle Poll to Poll transition here to call the waker
(_, ControlFlow::Poll) => { (_, ControlFlow::Poll) => {
self.set_state(AppStateImpl::PollFinished { waiting_handler }); self.set_state(AppStateImpl::PollFinished {
waiting_event_handler,
});
self.waker.start() self.waker.start()
} }
} }
} }
fn terminated_transition(&mut self) -> EventLoopHandler { fn terminated_transition(&mut self) -> Box<dyn EventHandler> {
match self.replace_state(AppStateImpl::Terminated) { match self.replace_state(AppStateImpl::Terminated) {
AppStateImpl::ProcessingEvents { handler, .. } => handler, AppStateImpl::ProcessingEvents { event_handler, .. } => event_handler,
s => bug!("`LoopExiting` happened while not processing events {:?}", s), s => bug!("`LoopExiting` happened while not processing events {:?}", s),
} }
} }
@@ -527,8 +516,8 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUI
} }
} }
pub(crate) fn will_launch(mtm: MainThreadMarker, queued_handler: EventLoopHandler) { pub fn will_launch(mtm: MainThreadMarker, queued_event_handler: Box<dyn EventHandler>) {
AppState::get_mut(mtm).will_launch_transition(queued_handler) AppState::get_mut(mtm).will_launch_transition(queued_event_handler)
} }
pub fn did_finish_launching(mtm: MainThreadMarker) { pub fn did_finish_launching(mtm: MainThreadMarker) {
@@ -605,17 +594,17 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
return; return;
} }
let (mut handler, active_control_flow, processing_redraws) = let (mut event_handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() { match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => { UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
queued_events.extend(events); queued_events.extend(events);
return; return;
} }
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
handler, event_handler,
active_control_flow, active_control_flow,
processing_redraws, processing_redraws,
} => (handler, active_control_flow, processing_redraws), } => (event_handler, active_control_flow, processing_redraws),
}; };
drop(this); drop(this);
@@ -630,9 +619,11 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
event event
); );
} }
handler.handle_event(event) event_handler.handle_nonuser_event(event)
}
EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
} }
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
} }
} }
@@ -659,12 +650,12 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
"redraw queued while processing redraws" "redraw queued while processing redraws"
); );
AppStateImpl::ProcessingRedraws { AppStateImpl::ProcessingRedraws {
handler, event_handler,
active_control_flow, active_control_flow,
} }
} else { } else {
AppStateImpl::ProcessingEvents { AppStateImpl::ProcessingEvents {
handler, event_handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
} }
@@ -684,9 +675,11 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
event event
); );
} }
handler.handle_event(event) event_handler.handle_nonuser_event(event)
}
EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
} }
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
} }
} }
} }
@@ -694,23 +687,23 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
fn handle_user_events(mtm: MainThreadMarker) { fn handle_user_events(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let (mut handler, active_control_flow, processing_redraws) = let (mut event_handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() { match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { .. } => { UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
bug!("unexpected attempted to process an event") bug!("unexpected attempted to process an event")
} }
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
handler, event_handler,
active_control_flow, active_control_flow,
processing_redraws, processing_redraws,
} => (handler, active_control_flow, processing_redraws), } => (event_handler, active_control_flow, processing_redraws),
}; };
if processing_redraws { if processing_redraws {
bug!("user events attempted to be sent out while `ProcessingRedraws`"); bug!("user events attempted to be sent out while `ProcessingRedraws`");
} }
drop(this); drop(this);
handler.handle_event(Event::UserEvent(HandlePendingUserEvents)); event_handler.handle_user_events();
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
@@ -730,7 +723,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
_ => unreachable!(), _ => unreachable!(),
}; };
this.app_state = Some(AppStateImpl::ProcessingEvents { this.app_state = Some(AppStateImpl::ProcessingEvents {
handler, event_handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
}); });
@@ -740,12 +733,13 @@ fn handle_user_events(mtm: MainThreadMarker) {
for wrapper in queued_events { for wrapper in queued_events {
match wrapper { match wrapper {
EventWrapper::StaticEvent(event) => handler.handle_event(event), EventWrapper::StaticEvent(event) => event_handler.handle_nonuser_event(event),
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event), EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
}
} }
} }
event_handler.handle_user_events();
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
} }
} }
@@ -785,13 +779,13 @@ pub fn handle_events_cleared(mtm: MainThreadMarker) {
pub fn terminated(mtm: MainThreadMarker) { pub fn terminated(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let mut handler = this.terminated_transition(); let mut event_handler = this.terminated_transition();
drop(this); drop(this);
handler.handle_event(Event::LoopExiting) event_handler.handle_nonuser_event(Event::LoopExiting)
} }
fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged) { fn handle_hidpi_proxy(event_handler: &mut Box<dyn EventHandler>, event: ScaleFactorChanged) {
let ScaleFactorChanged { let ScaleFactorChanged {
suggested_size, suggested_size,
scale_factor, scale_factor,
@@ -805,7 +799,7 @@ fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged)
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)), inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
}, },
}; };
handler.handle_event(event); event_handler.handle_nonuser_event(event);
let (view, screen_frame) = get_view_and_screen_frame(&window); let (view, screen_frame) = get_view_and_screen_frame(&window);
let physical_size = *new_inner_size.lock().unwrap(); let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size); drop(new_inner_size);

View File

@@ -1,6 +1,7 @@
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
ffi::c_void, ffi::c_void,
fmt::{self, Debug},
marker::PhantomData, marker::PhantomData,
ptr, ptr,
sync::mpsc::{self, Receiver, Sender}, sync::mpsc::{self, Receiver, Sender},
@@ -24,7 +25,6 @@ use crate::{
EventLoopWindowTarget as RootEventLoopWindowTarget, EventLoopWindowTarget as RootEventLoopWindowTarget,
}, },
platform::ios::Idiom, platform::ios::Idiom,
platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents},
}; };
use super::{app_state, monitor, view, MonitorHandle}; use super::{app_state, monitor, view, MonitorHandle};
@@ -34,11 +34,12 @@ use super::{
}; };
#[derive(Debug)] #[derive(Debug)]
pub struct EventLoopWindowTarget { pub struct EventLoopWindowTarget<T: 'static> {
pub(super) mtm: MainThreadMarker, pub(super) mtm: MainThreadMarker,
p: PhantomData<T>,
} }
impl EventLoopWindowTarget { impl<T: 'static> EventLoopWindowTarget<T> {
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> { pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
monitor::uiscreens(self.mtm) monitor::uiscreens(self.mtm)
} }
@@ -77,56 +78,19 @@ impl EventLoopWindowTarget {
pub(crate) fn exit(&self) { pub(crate) fn exit(&self) {
// https://developer.apple.com/library/archive/qa/qa1561/_index.html // https://developer.apple.com/library/archive/qa/qa1561/_index.html
// it is not possible to quit an iOS app gracefully and programatically // it is not possible to quit an iOS app gracefully and programatically
log::warn!("`ControlFlow::Exit` ignored on iOS"); warn!("`ControlFlow::Exit` ignored on iOS");
} }
pub(crate) fn exiting(&self) -> bool { pub(crate) fn exiting(&self) -> bool {
false false
} }
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle
}
}
#[derive(Clone)]
pub(crate) struct OwnedDisplayHandle;
impl OwnedDisplayHandle {
#[cfg(feature = "rwh_05")]
#[inline]
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
rwh_05::UiKitDisplayHandle::empty().into()
}
#[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())
}
}
fn map_user_event<T: 'static>(
mut handler: impl FnMut(Event<T>, &RootEventLoopWindowTarget),
receiver: mpsc::Receiver<T>,
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget) {
move |event, window_target| match event.map_nonuser_event() {
Ok(event) => (handler)(event, window_target),
Err(_) => {
for event in receiver.try_iter() {
(handler)(Event::UserEvent(event), window_target);
}
}
}
} }
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
mtm: MainThreadMarker, mtm: MainThreadMarker,
sender: Sender<T>, sender: Sender<T>,
receiver: Receiver<T>, receiver: Receiver<T>,
window_target: RootEventLoopWindowTarget, window_target: RootEventLoopWindowTarget<T>,
} }
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -159,59 +123,59 @@ impl<T: 'static> EventLoop<T> {
sender, sender,
receiver, receiver,
window_target: RootEventLoopWindowTarget { window_target: RootEventLoopWindowTarget {
p: EventLoopWindowTarget { mtm }, p: EventLoopWindowTarget {
mtm,
p: PhantomData,
},
_marker: PhantomData, _marker: PhantomData,
}, },
}) })
} }
pub fn run<F>(self, handler: F) -> ! pub fn run<F>(self, event_handler: F) -> !
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
let application = UIApplication::shared(self.mtm); unsafe {
assert!( let application = UIApplication::shared(self.mtm);
application.is_none(), assert!(
"\ application.is_none(),
"\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\ `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS", Note: `EventLoop::run` calls `UIApplicationMain` on iOS",
); );
let handler = map_user_event(handler, self.receiver); let event_handler = std::mem::transmute::<
Box<dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>)>,
Box<EventHandlerCallback<T>>,
>(Box::new(event_handler));
let handler = unsafe { let handler = EventLoopHandler {
std::mem::transmute::< f: event_handler,
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>, receiver: self.receiver,
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>, event_loop: self.window_target,
>(Box::new(handler)) };
};
let handler = EventLoopHandler { app_state::will_launch(self.mtm, Box::new(handler));
handler,
event_loop: self.window_target,
};
app_state::will_launch(self.mtm, handler); // Ensure application delegate is initialized
view::WinitApplicationDelegate::class();
// Ensure application delegate is initialized
view::WinitApplicationDelegate::class();
unsafe {
UIApplicationMain( UIApplicationMain(
0, 0,
ptr::null(), ptr::null(),
None, None,
Some(&NSString::from_str("WinitApplicationDelegate")), Some(&NSString::from_str("WinitApplicationDelegate")),
) );
}; unreachable!()
unreachable!() }
} }
pub fn create_proxy(&self) -> EventLoopProxy<T> { pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.sender.clone()) EventLoopProxy::new(self.sender.clone())
} }
pub fn window_target(&self) -> &RootEventLoopWindowTarget { pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
&self.window_target &self.window_target
} }
} }
@@ -378,3 +342,39 @@ fn setup_control_flow_observers() {
CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode); CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode);
} }
} }
#[derive(Debug)]
pub enum Never {}
type EventHandlerCallback<T> = dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>) + 'static;
pub trait EventHandler: Debug {
fn handle_nonuser_event(&mut self, event: Event<Never>);
fn handle_user_events(&mut self);
}
struct EventLoopHandler<T: 'static> {
f: Box<EventHandlerCallback<T>>,
receiver: Receiver<T>,
event_loop: RootEventLoopWindowTarget<T>,
}
impl<T: 'static> Debug for EventLoopHandler<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopHandler")
.field("event_loop", &self.event_loop)
.finish()
}
}
impl<T: 'static> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>) {
(self.f)(event.map_nonuser_event().unwrap(), &self.event_loop);
}
fn handle_user_events(&mut self) {
for event in self.receiver.try_iter() {
(self.f)(Event::UserEvent(event), &self.event_loop);
}
}
}

View File

@@ -68,35 +68,35 @@ mod window;
use std::fmt; use std::fmt;
use crate::event::DeviceId as RootDeviceId;
pub(crate) use self::{ pub(crate) use self::{
event_loop::{ event_loop::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, OwnedDisplayHandle, EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
PlatformSpecificEventLoopAttributes,
}, },
monitor::{MonitorHandle, VideoModeHandle}, monitor::{MonitorHandle, VideoMode},
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId}, window::{OwnedWindowHandle, PlatformSpecificWindowBuilderAttributes, Window, WindowId},
}; };
use self::uikit::UIScreen;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor; pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder; pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::NoIcon as PlatformIcon; pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; pub(crate) use crate::platform_impl::Fullscreen;
/// There is no way to detect which device that performed a certain event in
/// UIKit (i.e. you can't differentiate between different external keyboards,
/// or wether it was the main touchscreen, assistive technologies, or some
/// other pointer device that caused a touch event).
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId; pub struct DeviceId {
uiscreen: *const UIScreen,
}
impl DeviceId { impl DeviceId {
pub const unsafe fn dummy() -> Self { pub const unsafe fn dummy() -> Self {
DeviceId DeviceId {
uiscreen: std::ptr::null(),
}
} }
} }
pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId); unsafe impl Send for DeviceId {}
unsafe impl Sync for DeviceId {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyEventExtra {} pub struct KeyEventExtra {}

View File

@@ -13,7 +13,7 @@ use objc2::Message;
use super::uikit::{UIScreen, UIScreenMode}; use super::uikit::{UIScreen, UIScreenMode};
use crate::{ use crate::{
dpi::{PhysicalPosition, PhysicalSize}, dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoModeHandle as RootVideoModeHandle, monitor::VideoMode as RootVideoMode,
platform_impl::platform::app_state, platform_impl::platform::app_state,
}; };
@@ -48,7 +48,7 @@ impl<T: IsRetainable + Message> PartialEq for MainThreadBoundDelegateImpls<T> {
impl<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {} impl<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {}
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct VideoModeHandle { pub struct VideoMode {
pub(crate) size: (u32, u32), pub(crate) size: (u32, u32),
pub(crate) bit_depth: u16, pub(crate) bit_depth: u16,
pub(crate) refresh_rate_millihertz: u32, pub(crate) refresh_rate_millihertz: u32,
@@ -56,15 +56,15 @@ pub struct VideoModeHandle {
pub(crate) monitor: MonitorHandle, pub(crate) monitor: MonitorHandle,
} }
impl VideoModeHandle { impl VideoMode {
fn new( fn new(
uiscreen: Id<UIScreen>, uiscreen: Id<UIScreen>,
screen_mode: Id<UIScreenMode>, screen_mode: Id<UIScreenMode>,
mtm: MainThreadMarker, mtm: MainThreadMarker,
) -> VideoModeHandle { ) -> VideoMode {
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen); let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
let size = screen_mode.size(); let size = screen_mode.size();
VideoModeHandle { VideoMode {
size: (size.width as u32, size.height as u32), size: (size.width as u32, size.height as u32),
bit_depth: 32, bit_depth: 32,
refresh_rate_millihertz, refresh_rate_millihertz,
@@ -135,13 +135,24 @@ impl Ord for MonitorHandle {
impl fmt::Debug for MonitorHandle { impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MonitorHandle") // TODO: Do this using the proper fmt API
.field("name", &self.name()) #[derive(Debug)]
.field("size", &self.size()) #[allow(dead_code)]
.field("position", &self.position()) struct MonitorHandle {
.field("scale_factor", &self.scale_factor()) name: Option<String>,
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz()) size: PhysicalSize<u32>,
.finish_non_exhaustive() position: PhysicalPosition<i32>,
scale_factor: f64,
}
let monitor_id_proxy = MonitorHandle {
name: self.name(),
size: self.size(),
position: self.position(),
scale_factor: self.scale_factor(),
};
monitor_id_proxy.fmt(f)
} }
} }
@@ -196,16 +207,16 @@ impl MonitorHandle {
) )
} }
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
MainThreadMarker::run_on_main(|mtm| { MainThreadMarker::run_on_main(|mtm| {
let ui_screen = self.ui_screen(mtm); let ui_screen = self.ui_screen(mtm);
// Use Ord impl of RootVideoModeHandle // Use Ord impl of RootVideoMode
let modes: BTreeSet<_> = ui_screen let modes: BTreeSet<_> = ui_screen
.availableModes() .availableModes()
.into_iter() .into_iter()
.map(|mode| RootVideoModeHandle { .map(|mode| RootVideoMode {
video_mode: VideoModeHandle::new(ui_screen.clone(), mode, mtm), video_mode: VideoMode::new(ui_screen.clone(), mode, mtm),
}) })
.collect(); .collect();
@@ -217,9 +228,9 @@ impl MonitorHandle {
self.ui_screen.get(mtm) self.ui_screen.get(mtm)
} }
pub fn preferred_video_mode(&self) -> VideoModeHandle { pub fn preferred_video_mode(&self) -> VideoMode {
MainThreadMarker::run_on_main(|mtm| { MainThreadMarker::run_on_main(|mtm| {
VideoModeHandle::new( VideoMode::new(
self.ui_screen(mtm).clone(), self.ui_screen(mtm).clone(),
self.ui_screen(mtm).preferredMode().unwrap(), self.ui_screen(mtm).preferredMode().unwrap(),
mtm, mtm,

View File

@@ -1,121 +0,0 @@
use icrate::Foundation::{CGFloat, NSInteger, NSObject, NSUInteger};
use objc2::{
encode::{Encode, Encoding},
extern_class, extern_methods, mutability, ClassType,
};
// https://developer.apple.com/documentation/uikit/uigesturerecognizer
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIGestureRecognizer;
unsafe impl ClassType for UIGestureRecognizer {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIGestureRecognizer {
#[method(state)]
pub fn state(&self) -> UIGestureRecognizerState;
}
);
unsafe impl Encode for UIGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
// https://developer.apple.com/documentation/uikit/uigesturerecognizer/state
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIGestureRecognizerState(NSInteger);
unsafe impl Encode for UIGestureRecognizerState {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[allow(dead_code)]
impl UIGestureRecognizerState {
pub const Possible: Self = Self(0);
pub const Began: Self = Self(1);
pub const Changed: Self = Self(2);
pub const Ended: Self = Self(3);
pub const Cancelled: Self = Self(4);
pub const Failed: Self = Self(5);
}
// https://developer.apple.com/documentation/uikit/uipinchgesturerecognizer
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIPinchGestureRecognizer;
unsafe impl ClassType for UIPinchGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIPinchGestureRecognizer {
#[method(scale)]
pub fn scale(&self) -> CGFloat;
#[method(velocity)]
pub fn velocity(&self) -> CGFloat;
}
);
unsafe impl Encode for UIPinchGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
// https://developer.apple.com/documentation/uikit/uirotationgesturerecognizer
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIRotationGestureRecognizer;
unsafe impl ClassType for UIRotationGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIRotationGestureRecognizer {
#[method(rotation)]
pub fn rotation(&self) -> CGFloat;
#[method(velocity)]
pub fn velocity(&self) -> CGFloat;
}
);
unsafe impl Encode for UIRotationGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
// https://developer.apple.com/documentation/uikit/uitapgesturerecognizer
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UITapGestureRecognizer;
unsafe impl ClassType for UITapGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UITapGestureRecognizer {
#[method(setNumberOfTapsRequired:)]
pub fn setNumberOfTapsRequired(&self, number_of_taps_required: NSUInteger);
#[method(setNumberOfTouchesRequired:)]
pub fn setNumberOfTouchesRequired(&self, number_of_touches_required: NSUInteger);
}
);
unsafe impl Encode for UITapGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}

View File

@@ -9,7 +9,6 @@ mod application;
mod coordinate_space; mod coordinate_space;
mod device; mod device;
mod event; mod event;
mod gesture_recognizer;
mod responder; mod responder;
mod screen; mod screen;
mod screen_mode; mod screen_mode;
@@ -24,10 +23,6 @@ pub(crate) use self::application::UIApplication;
pub(crate) use self::coordinate_space::UICoordinateSpace; pub(crate) use self::coordinate_space::UICoordinateSpace;
pub(crate) use self::device::UIDevice; pub(crate) use self::device::UIDevice;
pub(crate) use self::event::UIEvent; pub(crate) use self::event::UIEvent;
pub(crate) use self::gesture_recognizer::{
UIGestureRecognizer, UIGestureRecognizerState, UIPinchGestureRecognizer,
UIRotationGestureRecognizer, UITapGestureRecognizer,
};
pub(crate) use self::responder::UIResponder; pub(crate) use self::responder::UIResponder;
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation}; pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};
pub(crate) use self::screen_mode::UIScreenMode; pub(crate) use self::screen_mode::UIScreenMode;

View File

@@ -3,7 +3,7 @@ use objc2::encode::{Encode, Encoding};
use objc2::rc::Id; use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType}; use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use super::{UICoordinateSpace, UIGestureRecognizer, UIResponder, UIViewController}; use super::{UICoordinateSpace, UIResponder, UIViewController};
extern_class!( extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
@@ -65,12 +65,6 @@ extern_methods!(
#[method(setNeedsDisplay)] #[method(setNeedsDisplay)]
pub fn setNeedsDisplay(&self); pub fn setNeedsDisplay(&self);
#[method(addGestureRecognizer:)]
pub fn addGestureRecognizer(&self, gestureRecognizer: &UIGestureRecognizer);
#[method(removeGestureRecognizer:)]
pub fn removeGestureRecognizer(&self, gestureRecognizer: &UIGestureRecognizer);
} }
); );

View File

@@ -39,7 +39,7 @@ extern_methods!(
} }
); );
bitflags::bitflags! { bitflags! {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct UIInterfaceOrientationMask: NSUInteger { pub struct UIInterfaceOrientationMask: NSUInteger {
const Portrait = 1 << 1; const Portrait = 1 << 1;

View File

@@ -1,38 +1,32 @@
#![allow(clippy::unnecessary_cast)] #![allow(clippy::unnecessary_cast)]
use std::cell::{Cell, RefCell}; use std::cell::Cell;
use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet}; use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet};
use objc2::rc::Id; use objc2::rc::Id;
use objc2::runtime::AnyClass; use objc2::runtime::AnyClass;
use objc2::{ use objc2::{
declare_class, extern_methods, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass, declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType, DeclaredClass,
}; };
use super::app_state::{self, EventWrapper}; use super::app_state::{self, EventWrapper};
use super::uikit::{ use super::uikit::{
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIGestureRecognizerState, UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIInterfaceOrientationMask,
UIInterfaceOrientationMask, UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, UIResponder, UIStatusBarStyle, UITouch, UITouchPhase, UITouchType, UITraitCollection, UIView,
UIStatusBarStyle, UITapGestureRecognizer, UITouch, UITouchPhase, UITouchType, UIViewController, UIWindow,
UITraitCollection, UIView, UIViewController, UIWindow,
}; };
use super::window::WindowId; use super::window::WindowId;
use crate::{ use crate::{
dpi::PhysicalPosition, dpi::PhysicalPosition,
event::{Event, Force, Touch, TouchPhase, WindowEvent}, event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
platform::ios::ValidOrientations, platform::ios::ValidOrientations,
platform_impl::platform::{ platform_impl::platform::{
ffi::{UIRectEdge, UIUserInterfaceIdiom}, ffi::{UIRectEdge, UIUserInterfaceIdiom},
Fullscreen, DEVICE_ID, window::PlatformSpecificWindowBuilderAttributes,
DeviceId, Fullscreen,
}, },
window::{WindowAttributes, WindowId as RootWindowId}, window::{WindowAttributes, WindowId as RootWindowId},
}; };
pub struct WinitViewState {
pinch_gesture_recognizer: RefCell<Option<Id<UIPinchGestureRecognizer>>>,
doubletap_gesture_recognizer: RefCell<Option<Id<UITapGestureRecognizer>>>,
rotation_gesture_recognizer: RefCell<Option<Id<UIRotationGestureRecognizer>>>,
}
declare_class!( declare_class!(
pub(crate) struct WinitView; pub(crate) struct WinitView;
@@ -43,9 +37,7 @@ declare_class!(
const NAME: &'static str = "WinitUIView"; const NAME: &'static str = "WinitUIView";
} }
impl DeclaredClass for WinitView { impl DeclaredClass for WinitView {}
type Ivars = WinitViewState;
}
unsafe impl WinitView { unsafe impl WinitView {
#[method(drawRect:)] #[method(drawRect:)]
@@ -167,79 +159,6 @@ declare_class!(
fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) { fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
self.handle_touches(touches) self.handle_touches(touches)
} }
#[method(pinchGesture:)]
fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) {
let window = self.window().unwrap();
let phase = match recognizer.state() {
UIGestureRecognizerState::Began => TouchPhase::Started,
UIGestureRecognizerState::Changed => TouchPhase::Moved,
UIGestureRecognizerState::Ended => TouchPhase::Ended,
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
TouchPhase::Cancelled
}
state => panic!("unexpected recognizer state: {:?}", state),
};
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::PinchGesture {
device_id: DEVICE_ID,
delta: recognizer.velocity() as _,
phase,
},
});
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event);
}
#[method(doubleTapGesture:)]
fn double_tap_gesture(&self, recognizer: &UITapGestureRecognizer) {
let window = self.window().unwrap();
if recognizer.state() == UIGestureRecognizerState::Ended {
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::DoubleTapGesture {
device_id: DEVICE_ID,
},
});
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event);
}
}
#[method(rotationGesture:)]
fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) {
let window = self.window().unwrap();
let phase = match recognizer.state() {
UIGestureRecognizerState::Began => TouchPhase::Started,
UIGestureRecognizerState::Changed => TouchPhase::Moved,
UIGestureRecognizerState::Ended => TouchPhase::Ended,
UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => {
TouchPhase::Cancelled
}
state => panic!("unexpected recognizer state: {:?}", state),
};
// Flip the velocity to match macOS.
let delta = -recognizer.velocity() as _;
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()),
event: WindowEvent::RotationGesture {
device_id: DEVICE_ID,
delta,
phase,
},
});
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event);
}
} }
); );
@@ -263,73 +182,24 @@ extern_methods!(
impl WinitView { impl WinitView {
pub(crate) fn new( pub(crate) fn new(
_mtm: MainThreadMarker, _mtm: MainThreadMarker,
window_attributes: &WindowAttributes, _window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
frame: CGRect, frame: CGRect,
) -> Id<Self> { ) -> Id<Self> {
let this = Self::alloc().set_ivars(WinitViewState { let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] };
pinch_gesture_recognizer: RefCell::new(None),
doubletap_gesture_recognizer: RefCell::new(None),
rotation_gesture_recognizer: RefCell::new(None),
});
let this: Id<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
this.setMultipleTouchEnabled(true); this.setMultipleTouchEnabled(true);
if let Some(scale_factor) = window_attributes.platform_specific.scale_factor { if let Some(scale_factor) = platform_attributes.scale_factor {
this.setContentScaleFactor(scale_factor as _); this.setContentScaleFactor(scale_factor as _);
} }
this this
} }
pub(crate) fn recognize_pinch_gesture(&self, should_recognize: bool) {
if should_recognize {
if self.ivars().pinch_gesture_recognizer.borrow().is_none() {
let pinch: Id<UIPinchGestureRecognizer> = unsafe {
msg_send_id![UIPinchGestureRecognizer::alloc(), initWithTarget: self, action: sel!(pinchGesture:)]
};
self.addGestureRecognizer(&pinch);
self.ivars().pinch_gesture_recognizer.replace(Some(pinch));
}
} else if let Some(recognizer) = self.ivars().pinch_gesture_recognizer.take() {
self.removeGestureRecognizer(&recognizer);
}
}
pub(crate) fn recognize_doubletap_gesture(&self, should_recognize: bool) {
if should_recognize {
if self.ivars().doubletap_gesture_recognizer.borrow().is_none() {
let tap: Id<UITapGestureRecognizer> = unsafe {
msg_send_id![UITapGestureRecognizer::alloc(), initWithTarget: self, action: sel!(doubleTapGesture:)]
};
tap.setNumberOfTapsRequired(2);
tap.setNumberOfTouchesRequired(1);
self.addGestureRecognizer(&tap);
self.ivars().doubletap_gesture_recognizer.replace(Some(tap));
}
} else if let Some(recognizer) = self.ivars().doubletap_gesture_recognizer.take() {
self.removeGestureRecognizer(&recognizer);
}
}
pub(crate) fn recognize_rotation_gesture(&self, should_recognize: bool) {
if should_recognize {
if self.ivars().rotation_gesture_recognizer.borrow().is_none() {
let rotation: Id<UIRotationGestureRecognizer> = unsafe {
msg_send_id![UIRotationGestureRecognizer::alloc(), initWithTarget: self, action: sel!(rotationGesture:)]
};
self.addGestureRecognizer(&rotation);
self.ivars()
.rotation_gesture_recognizer
.replace(Some(rotation));
}
} else if let Some(recognizer) = self.ivars().rotation_gesture_recognizer.take() {
self.removeGestureRecognizer(&recognizer);
}
}
fn handle_touches(&self, touches: &NSSet<UITouch>) { fn handle_touches(&self, touches: &NSSet<UITouch>) {
let window = self.window().unwrap(); let window = self.window().unwrap();
let uiscreen = window.screen();
let mut touch_events = Vec::new(); let mut touch_events = Vec::new();
let os_supports_force = app_state::os_capabilities().force_touch; let os_supports_force = app_state::os_capabilities().force_touch;
for touch in touches { for touch in touches {
@@ -382,7 +252,9 @@ impl WinitView {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.id()), window_id: RootWindowId(window.id()),
event: WindowEvent::Touch(Touch { event: WindowEvent::Touch(Touch {
device_id: DEVICE_ID, device_id: RootDeviceId(DeviceId {
uiscreen: Id::as_ptr(&uiscreen),
}),
id: touch_id, id: touch_id,
location: physical_location, location: physical_location,
force, force,
@@ -513,7 +385,8 @@ impl WinitViewController {
pub(crate) fn new( pub(crate) fn new(
mtm: MainThreadMarker, mtm: MainThreadMarker,
window_attributes: &WindowAttributes, _window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
view: &UIView, view: &UIView,
) -> Id<Self> { ) -> Id<Self> {
// These are set properly below, we just to set them to something in the meantime. // These are set properly below, we just to set them to something in the meantime.
@@ -526,33 +399,18 @@ impl WinitViewController {
}); });
let this: Id<Self> = unsafe { msg_send_id![super(this), init] }; let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
this.set_prefers_status_bar_hidden( this.set_prefers_status_bar_hidden(platform_attributes.prefers_status_bar_hidden);
window_attributes
.platform_specific
.prefers_status_bar_hidden,
);
this.set_preferred_status_bar_style( this.set_preferred_status_bar_style(platform_attributes.preferred_status_bar_style.into());
window_attributes
.platform_specific
.preferred_status_bar_style
.into(),
);
this.set_supported_interface_orientations( this.set_supported_interface_orientations(mtm, platform_attributes.valid_orientations);
mtm,
window_attributes.platform_specific.valid_orientations,
);
this.set_prefers_home_indicator_auto_hidden( this.set_prefers_home_indicator_auto_hidden(
window_attributes platform_attributes.prefers_home_indicator_hidden,
.platform_specific
.prefers_home_indicator_hidden,
); );
this.set_preferred_screen_edges_deferring_system_gestures( this.set_preferred_screen_edges_deferring_system_gestures(
window_attributes platform_attributes
.platform_specific
.preferred_screen_edges_deferring_system_gestures .preferred_screen_edges_deferring_system_gestures
.into(), .into(),
); );
@@ -609,6 +467,7 @@ impl WinitUIWindow {
pub(crate) fn new( pub(crate) fn new(
mtm: MainThreadMarker, mtm: MainThreadMarker,
window_attributes: &WindowAttributes, window_attributes: &WindowAttributes,
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
frame: CGRect, frame: CGRect,
view_controller: &UIViewController, view_controller: &UIViewController,
) -> Id<Self> { ) -> Id<Self> {
@@ -616,7 +475,7 @@ impl WinitUIWindow {
this.setRootViewController(Some(view_controller)); this.setRootViewController(Some(view_controller));
match window_attributes.fullscreen.clone().map(Into::into) { match window_attributes.fullscreen.0.clone().map(Into::into) {
Some(Fullscreen::Exclusive(ref video_mode)) => { Some(Fullscreen::Exclusive(ref video_mode)) => {
let monitor = video_mode.monitor(); let monitor = video_mode.monitor();
let screen = monitor.ui_screen(mtm); let screen = monitor.ui_screen(mtm);

View File

@@ -3,7 +3,6 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker}; use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
use log::{debug, warn};
use objc2::rc::Id; use objc2::rc::Id;
use objc2::runtime::AnyObject; use objc2::runtime::AnyObject;
use objc2::{class, msg_send}; use objc2::{class, msg_send};
@@ -12,21 +11,33 @@ use super::app_state::EventWrapper;
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation}; use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
use super::view::{WinitUIWindow, WinitView, WinitViewController}; use super::view::{WinitUIWindow, WinitView, WinitViewController};
use crate::{ use crate::{
cursor::Cursor,
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError}, error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::{Event, WindowEvent}, event::{Event, WindowEvent},
icon::Icon, icon::Icon,
platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations}, platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations},
platform_impl::platform::{ platform_impl::platform::{
app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle, app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle, PlatformCustomCursor,
}, },
window::{ window::{
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes, CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowButtons, WindowId as RootWindowId, WindowLevel, WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel,
}, },
}; };
#[derive(Debug, Clone)]
pub(crate) struct OwnedWindowHandle {}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
// Parent windows are currently unsupported, though owned window
// handles would be implementable (would work similar to macOS).
warn!("parent windows are unsupported on iOS");
Self {}
}
}
pub struct Inner { pub struct Inner {
window: Id<WinitUIWindow>, window: Id<WinitUIWindow>,
view_controller: Id<WinitViewController>, view_controller: Id<WinitViewController>,
@@ -175,8 +186,12 @@ impl Inner {
self.view.contentScaleFactor() as _ self.view.contentScaleFactor() as _
} }
pub fn set_cursor(&self, _cursor: Cursor) { pub fn set_cursor_icon(&self, _cursor: CursorIcon) {
debug!("`Window::set_cursor` ignored on iOS") debug!("`Window::set_cursor_icon` ignored on iOS")
}
pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {
debug!("`Window::set_custom_cursor` ignored on iOS")
} }
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> { pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
@@ -367,6 +382,15 @@ impl Inner {
rwh_06::RawWindowHandle::UiKit(window_handle) rwh_06::RawWindowHandle::UiKit(window_handle)
} }
#[cfg(feature = "rwh_06")]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::UiKit(
rwh_06::UiKitDisplayHandle::new(),
))
}
pub fn theme(&self) -> Option<Theme> { pub fn theme(&self) -> Option<Theme> {
warn!("`Window::theme` is ignored on iOS"); warn!("`Window::theme` is ignored on iOS");
None None
@@ -398,9 +422,10 @@ pub struct Window {
} }
impl Window { impl Window {
pub(crate) fn new( pub(crate) fn new<T>(
event_loop: &EventLoopWindowTarget, event_loop: &EventLoopWindowTarget<T>,
window_attributes: WindowAttributes, window_attributes: WindowAttributes,
platform_attributes: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, RootOsError> { ) -> Result<Window, RootOsError> {
let mtm = event_loop.mtm; let mtm = event_loop.mtm;
@@ -414,7 +439,7 @@ impl Window {
// TODO: transparency, visible // TODO: transparency, visible
let main_screen = UIScreen::main(mtm); let main_screen = UIScreen::main(mtm);
let fullscreen = window_attributes.fullscreen.clone().map(Into::into); let fullscreen = window_attributes.fullscreen.0.clone().map(Into::into);
let screen = match fullscreen { let screen = match fullscreen {
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm), Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm),
Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm), Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm),
@@ -438,7 +463,7 @@ impl Window {
None => screen_bounds, None => screen_bounds,
}; };
let view = WinitView::new(mtm, &window_attributes, frame); let view = WinitView::new(mtm, &window_attributes, &platform_attributes, frame);
let gl_or_metal_backed = unsafe { let gl_or_metal_backed = unsafe {
let layer_class = WinitView::layerClass(); let layer_class = WinitView::layerClass();
@@ -447,8 +472,15 @@ impl Window {
is_metal || is_gl is_metal || is_gl
}; };
let view_controller = WinitViewController::new(mtm, &window_attributes, &view); let view_controller =
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller); WinitViewController::new(mtm, &window_attributes, &platform_attributes, &view);
let window = WinitUIWindow::new(
mtm,
&window_attributes,
&platform_attributes,
frame,
&view_controller,
);
app_state::set_key_window(mtm, &window); app_state::set_key_window(mtm, &window);
@@ -515,16 +547,6 @@ impl Window {
Err(rwh_06::HandleError::Unavailable) Err(rwh_06::HandleError::Unavailable)
} }
} }
#[cfg(feature = "rwh_06")]
#[inline]
pub(crate) fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::RawDisplayHandle::UiKit(
rwh_06::UiKitDisplayHandle::new(),
))
}
} }
// WindowExtIOS // WindowExtIOS
@@ -563,18 +585,6 @@ impl Inner {
self.view_controller self.view_controller
.set_preferred_status_bar_style(status_bar_style.into()); .set_preferred_status_bar_style(status_bar_style.into());
} }
pub fn recognize_pinch_gesture(&self, should_recognize: bool) {
self.view.recognize_pinch_gesture(should_recognize);
}
pub fn recognize_doubletap_gesture(&self, should_recognize: bool) {
self.view.recognize_doubletap_gesture(should_recognize);
}
pub fn recognize_rotation_gesture(&self, should_recognize: bool) {
self.view.recognize_rotation_gesture(should_recognize);
}
} }
impl Inner { impl Inner {
@@ -677,7 +687,7 @@ impl From<&AnyObject> for WindowId {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes { pub struct PlatformSpecificWindowBuilderAttributes {
pub scale_factor: Option<f64>, pub scale_factor: Option<f64>,
pub valid_orientations: ValidOrientations, pub valid_orientations: ValidOrientations,

View File

@@ -6,13 +6,13 @@ use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyC
/// ///
/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses. /// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses.
pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey { pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey {
scancode_to_physicalkey(keycode.saturating_sub(8)) scancode_to_keycode(keycode.saturating_sub(8))
} }
/// Map the linux scancode to Keycode. /// Map the linux scancode to Keycode.
/// ///
/// Both X11 and Wayland use keys with `+ 8` offset to linux scancode. /// Both X11 and Wayland use keys with `+ 8` offset to linux scancode.
pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { pub fn scancode_to_keycode(scancode: u32) -> PhysicalKey {
// The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as // The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as
// libxkbcommon's documentation seems to suggest that the keycode values we're interested in // libxkbcommon's documentation seems to suggest that the keycode values we're interested in
// are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes, // are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes,
@@ -504,7 +504,7 @@ pub fn keysym_to_key(keysym: u32) -> Key {
keysyms::KP_F4 => NamedKey::F4, keysyms::KP_F4 => NamedKey::F4,
keysyms::KP_Home => NamedKey::Home, keysyms::KP_Home => NamedKey::Home,
keysyms::KP_Left => NamedKey::ArrowLeft, keysyms::KP_Left => NamedKey::ArrowLeft,
keysyms::KP_Up => NamedKey::ArrowUp, keysyms::KP_Up => NamedKey::ArrowLeft,
keysyms::KP_Right => NamedKey::ArrowRight, keysyms::KP_Right => NamedKey::ArrowRight,
keysyms::KP_Down => NamedKey::ArrowDown, keysyms::KP_Down => NamedKey::ArrowDown,
// keysyms::KP_Prior => NamedKey::PageUp, // keysyms::KP_Prior => NamedKey::PageUp,

View File

@@ -398,8 +398,8 @@ impl KbdState {
let text_with_all_modifiers = event.text_with_all_modifiers(); let text_with_all_modifiers = event.text_with_all_modifiers();
let platform_specific = KeyEventExtra { let platform_specific = KeyEventExtra {
text_with_all_modifiers,
key_without_modifiers, key_without_modifiers,
text_with_all_modifiers,
}; };
KeyEvent { KeyEvent {
@@ -498,7 +498,7 @@ impl<'a> KeyEventResults<'a> {
} }
} }
fn physical_key(&self) -> PhysicalKey { fn physical_key(&mut self) -> PhysicalKey {
keymap::raw_keycode_to_physicalkey(self.keycode) keymap::raw_keycode_to_physicalkey(self.keycode)
} }
@@ -553,7 +553,6 @@ impl<'a> KeyEventResults<'a> {
} else { } else {
0 0
}; };
self.keysym_to_key(keysym) self.keysym_to_key(keysym)
.unwrap_or_else(|(key, location)| { .unwrap_or_else(|(key, location)| {
( (
@@ -566,7 +565,7 @@ impl<'a> KeyEventResults<'a> {
}) })
} }
fn keysym_to_key(&self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> { fn keysym_to_key(&mut self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> {
let location = super::keymap::keysym_location(keysym); let location = super::keymap::keysym_location(keysym);
let key = super::keymap::keysym_to_key(keysym); let key = super::keymap::keysym_to_key(keysym);
if matches!(key, Key::Unidentified(_)) { if matches!(key, Key::Unidentified(_)) {
@@ -683,7 +682,7 @@ fn byte_slice_to_smol_str(bytes: &[u8]) -> Option<SmolStr> {
std::str::from_utf8(bytes) std::str::from_utf8(bytes)
.map(SmolStr::new) .map(SmolStr::new)
.map_err(|e| { .map_err(|e| {
log::warn!( warn!(
"UTF-8 received from libxkbcommon ({:?}) was invalid: {e}", "UTF-8 received from libxkbcommon ({:?}) was invalid: {e}",
bytes bytes
) )

View File

@@ -15,35 +15,41 @@ use once_cell::sync::Lazy;
use smol_str::SmolStr; use smol_str::SmolStr;
#[cfg(x11_platform)] #[cfg(x11_platform)]
use self::x11::{X11Error, XConnection, XError, XNotSupported}; use crate::platform::x11::XlibErrorHook;
#[cfg(x11_platform)]
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
use crate::{ use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError}, error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError},
event::KeyEvent,
event_loop::{ event_loop::{
AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
EventLoopWindowTarget as RootELW, EventLoopWindowTarget as RootELW,
}, },
icon::Icon, icon::Icon,
keyboard::Key, keyboard::{Key, PhysicalKey},
platform::pump_events::PumpStatus, platform::{
modifier_supplement::KeyEventExtModifierSupplement, pump_events::PumpStatus,
scancode::PhysicalKeyExtScancode,
},
window::{ window::{
ActivationToken, Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, ActivationToken, CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme,
UserAttentionType, WindowAttributes, WindowButtons, WindowLevel, UserAttentionType, WindowAttributes, WindowButtons, WindowLevel,
}, },
}; };
#[cfg(x11_platform)]
pub use x11::XNotSupported;
#[cfg(x11_platform)]
use x11::{util::WindowType as XWindowType, X11Error, XConnection, XError};
pub(crate) use self::common::keymap::{physicalkey_to_scancode, scancode_to_physicalkey}; pub(crate) use crate::cursor::OnlyCursorImage as PlatformCustomCursor;
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder; pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::RgbaIcon as PlatformIcon; pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; pub(crate) use crate::platform_impl::Fullscreen;
pub(crate) mod common; pub mod common;
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
pub(crate) mod wayland; pub mod wayland;
#[cfg(x11_platform)] #[cfg(x11_platform)]
pub(crate) mod x11; pub mod x11;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum Backend { pub(crate) enum Backend {
@@ -71,7 +77,7 @@ impl ApplicationName {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes { pub struct PlatformSpecificWindowBuilderAttributes {
pub name: Option<ApplicationName>, pub name: Option<ApplicationName>,
pub activation_token: Option<ActivationToken>, pub activation_token: Option<ActivationToken>,
@@ -79,7 +85,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub x11: X11WindowBuilderAttributes, pub x11: X11WindowBuilderAttributes,
} }
#[derive(Clone, Debug)] #[derive(Clone)]
#[cfg(x11_platform)] #[cfg(x11_platform)]
pub struct X11WindowBuilderAttributes { pub struct X11WindowBuilderAttributes {
pub visual_id: Option<x11rb::protocol::xproto::Visualid>, pub visual_id: Option<x11rb::protocol::xproto::Visualid>,
@@ -135,6 +141,43 @@ impl fmt::Display for OsError {
} }
} }
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub(crate) enum OwnedWindowHandle {
#[cfg(x11_platform)]
X(x11rb::protocol::xproto::Window),
#[cfg(wayland_platform)]
Wayland,
}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(handle: rwh_06::WindowHandle<'_>) -> Self {
// TODO: Do we need to do something extra to extend the lifetime of
// the window lives beyond the passed-in handle?
match handle.as_raw() {
#[cfg(x11_platform)]
rwh_06::RawWindowHandle::Xlib(handle) => {
Self::X(handle.window as x11rb::protocol::xproto::Window)
}
#[cfg(x11_platform)]
rwh_06::RawWindowHandle::Xcb(handle) => Self::X(handle.window.get()),
#[cfg(wayland_platform)]
rwh_06::RawWindowHandle::Wayland(_handle) => {
// Wayland does not currently support parent windows, but it
// could support owned handles.
Self::Wayland
}
#[cfg(not(x11_platform))]
handle => panic!("invalid window handle {handle:?} on Wayland"),
#[cfg(not(wayland_platform))]
handle => panic!("invalid window handle {handle:?} on X11"),
#[cfg(all(x11_platform, wayland_platform))]
handle => panic!("invalid window handle {handle:?} on X11 or Wayland"),
}
}
}
pub(crate) enum Window { pub(crate) enum Window {
#[cfg(x11_platform)] #[cfg(x11_platform)]
X(x11::Window), X(x11::Window),
@@ -248,55 +291,56 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoModeHandle>> { pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes())) x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes()))
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum VideoModeHandle { pub enum VideoMode {
#[cfg(x11_platform)] #[cfg(x11_platform)]
X(x11::VideoModeHandle), X(x11::VideoMode),
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
Wayland(wayland::VideoModeHandle), Wayland(wayland::VideoMode),
} }
impl VideoModeHandle { impl VideoMode {
#[inline] #[inline]
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
x11_or_wayland!(match self; VideoModeHandle(m) => m.size()) x11_or_wayland!(match self; VideoMode(m) => m.size())
} }
#[inline] #[inline]
pub fn bit_depth(&self) -> u16 { pub fn bit_depth(&self) -> u16 {
x11_or_wayland!(match self; VideoModeHandle(m) => m.bit_depth()) x11_or_wayland!(match self; VideoMode(m) => m.bit_depth())
} }
#[inline] #[inline]
pub fn refresh_rate_millihertz(&self) -> u32 { pub fn refresh_rate_millihertz(&self) -> u32 {
x11_or_wayland!(match self; VideoModeHandle(m) => m.refresh_rate_millihertz()) x11_or_wayland!(match self; VideoMode(m) => m.refresh_rate_millihertz())
} }
#[inline] #[inline]
pub fn monitor(&self) -> MonitorHandle { pub fn monitor(&self) -> MonitorHandle {
x11_or_wayland!(match self; VideoModeHandle(m) => m.monitor(); as MonitorHandle) x11_or_wayland!(match self; VideoMode(m) => m.monitor(); as MonitorHandle)
} }
} }
impl Window { impl Window {
#[inline] #[inline]
pub(crate) fn new( pub(crate) fn new<T>(
window_target: &EventLoopWindowTarget, window_target: &EventLoopWindowTarget<T>,
attribs: WindowAttributes, attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> { ) -> Result<Self, RootOsError> {
match *window_target { match *window_target {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
EventLoopWindowTarget::Wayland(ref window_target) => { EventLoopWindowTarget::Wayland(ref window_target) => {
wayland::Window::new(window_target, attribs).map(Window::Wayland) wayland::Window::new(window_target, attribs, pl_attribs).map(Window::Wayland)
} }
#[cfg(x11_platform)] #[cfg(x11_platform)]
EventLoopWindowTarget::X(ref window_target) => { EventLoopWindowTarget::X(ref window_target) => {
x11::Window::new(window_target, attribs).map(Window::X) x11::Window::new(window_target, attribs, pl_attribs).map(Window::X)
} }
} }
} }
@@ -415,8 +459,13 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_cursor(&self, cursor: Cursor) { pub fn set_cursor_icon(&self, cursor: CursorIcon) {
x11_or_wayland!(match self; Window(w) => w.set_cursor(cursor)) x11_or_wayland!(match self; Window(w) => w.set_cursor_icon(cursor))
}
#[inline]
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
x11_or_wayland!(match self; Window(w) => w.set_custom_cursor(cursor))
} }
#[inline] #[inline]
@@ -633,30 +682,32 @@ impl Window {
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeyEventExtra { pub struct KeyEventExtra {
pub text_with_all_modifiers: Option<SmolStr>,
pub key_without_modifiers: Key, pub key_without_modifiers: Key,
pub text_with_all_modifiers: Option<SmolStr>,
} }
#[derive(Clone, Debug, Eq, Hash, PartialEq)] impl KeyEventExtModifierSupplement for KeyEvent {
pub(crate) enum PlatformCustomCursor { #[inline]
#[cfg(wayland_platform)] fn text_with_all_modifiers(&self) -> Option<&str> {
Wayland(wayland::CustomCursor), self.platform_specific
#[cfg(x11_platform)] .text_with_all_modifiers
X(x11::CustomCursor), .as_ref()
.map(|s| s.as_str())
}
#[inline]
fn key_without_modifiers(&self) -> Key {
self.platform_specific.key_without_modifiers.clone()
}
} }
impl PlatformCustomCursor {
pub(crate) fn build( impl PhysicalKeyExtScancode for PhysicalKey {
builder: PlatformCustomCursorBuilder, fn from_scancode(scancode: u32) -> PhysicalKey {
p: &EventLoopWindowTarget, common::keymap::scancode_to_keycode(scancode)
) -> PlatformCustomCursor { }
match p {
#[cfg(wayland_platform)] fn to_scancode(self) -> Option<u32> {
EventLoopWindowTarget::Wayland(_) => { common::keymap::physicalkey_to_scancode(self)
Self::Wayland(wayland::CustomCursor::build(builder, p))
}
#[cfg(x11_platform)]
EventLoopWindowTarget::X(p) => Self::X(x11::CustomCursor::build(builder, p)),
}
} }
} }
@@ -703,7 +754,7 @@ unsafe extern "C" fn x_error_callback(
// Don't log error. // Don't log error.
if !error_handled { if !error_handled {
log::error!("X11 error: {:#?}", error); error!("X11 error: {:#?}", error);
// XXX only update the error, if it wasn't handled by any of the hooks. // XXX only update the error, if it wasn't handled by any of the hooks.
*xconn.latest_error.lock().unwrap() = Some(error); *xconn.latest_error.lock().unwrap() = Some(error);
} }
@@ -750,11 +801,8 @@ impl<T: 'static> EventLoop<T> {
let backend = match ( let backend = match (
attributes.forced_backend, attributes.forced_backend,
env::var("WAYLAND_DISPLAY") env::var("WAYLAND_DISPLAY")
.ok() .map(|var| !var.is_empty())
.filter(|var| !var.is_empty()) .unwrap_or(false),
.or_else(|| env::var("WAYLAND_SOCKET").ok())
.filter(|var| !var.is_empty())
.is_some(),
env::var("DISPLAY") env::var("DISPLAY")
.map(|var| !var.is_empty()) .map(|var| !var.is_empty())
.unwrap_or(false), .unwrap_or(false),
@@ -768,15 +816,10 @@ impl<T: 'static> EventLoop<T> {
#[cfg(x11_platform)] #[cfg(x11_platform)]
(None, _, true) => Backend::X, (None, _, true) => Backend::X,
// No backend is present. // No backend is present.
(_, wayland_display, x11_display) => { _ => {
let msg = if wayland_display && !cfg!(wayland_platform) { return Err(EventLoopError::Os(os_error!(OsError::Misc(
"DISPLAY is not set; note: enable the `winit/wayland` feature to support Wayland" "neither WAYLAND_DISPLAY nor DISPLAY is set."
} else if x11_display && !cfg!(x11_platform) { ))));
"neither WAYLAND_DISPLAY nor WAYLAND_SOCKET is set; note: enable the `winit/x11` feature to support X11"
} else {
"neither WAYLAND_DISPLAY nor WAYLAND_SOCKET nor DISPLAY is set."
};
return Err(EventLoopError::Os(os_error!(OsError::Misc(msg))));
} }
}; };
@@ -785,7 +828,7 @@ impl<T: 'static> EventLoop<T> {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
Backend::Wayland => EventLoop::new_wayland_any_thread().map_err(Into::into), Backend::Wayland => EventLoop::new_wayland_any_thread().map_err(Into::into),
#[cfg(x11_platform)] #[cfg(x11_platform)]
Backend::X => EventLoop::new_x11_any_thread().map_err(Into::into), Backend::X => Ok(EventLoop::new_x11_any_thread().unwrap()),
} }
} }
@@ -795,10 +838,10 @@ impl<T: 'static> EventLoop<T> {
} }
#[cfg(x11_platform)] #[cfg(x11_platform)]
fn new_x11_any_thread() -> Result<EventLoop<T>, EventLoopError> { fn new_x11_any_thread() -> Result<EventLoop<T>, XNotSupported> {
let xconn = match X11_BACKEND.lock().unwrap().as_ref() { let xconn = match X11_BACKEND.lock().unwrap().as_ref() {
Ok(xconn) => xconn.clone(), Ok(xconn) => xconn.clone(),
Err(_) => return Err(EventLoopError::NotSupported(NotSupportedError::new())), Err(err) => return Err(err.clone()),
}; };
Ok(EventLoop::X(x11::EventLoop::new(xconn))) Ok(EventLoop::X(x11::EventLoop::new(xconn)))
@@ -810,26 +853,26 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError> pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
where where
F: FnMut(crate::event::Event<T>, &RootELW), F: FnMut(crate::event::Event<T>, &RootELW<T>),
{ {
self.run_on_demand(callback) self.run_on_demand(callback)
} }
pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError> pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
where where
F: FnMut(crate::event::Event<T>, &RootELW), F: FnMut(crate::event::Event<T>, &RootELW<T>),
{ {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(callback)) x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(callback))
} }
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
where where
F: FnMut(crate::event::Event<T>, &RootELW), F: FnMut(crate::event::Event<T>, &RootELW<T>),
{ {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback)) x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
} }
pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget { pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget<T> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target()) x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
} }
} }
@@ -852,14 +895,14 @@ impl<T: 'static> EventLoopProxy<T> {
} }
} }
pub enum EventLoopWindowTarget { pub enum EventLoopWindowTarget<T> {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
Wayland(wayland::EventLoopWindowTarget), Wayland(wayland::EventLoopWindowTarget<T>),
#[cfg(x11_platform)] #[cfg(x11_platform)]
X(x11::EventLoopWindowTarget), X(x11::EventLoopWindowTarget<T>),
} }
impl EventLoopWindowTarget { impl<T> EventLoopWindowTarget<T> {
#[inline] #[inline]
pub fn is_wayland(&self) -> bool { pub fn is_wayland(&self) -> bool {
match *self { match *self {
@@ -919,10 +962,6 @@ impl EventLoopWindowTarget {
x11_or_wayland!(match self; Self(evlp) => evlp.control_flow()) x11_or_wayland!(match self; Self(evlp) => evlp.control_flow())
} }
pub(crate) fn clear_exit(&self) {
x11_or_wayland!(match self; Self(evlp) => evlp.clear_exit())
}
pub(crate) fn exit(&self) { pub(crate) fn exit(&self) {
x11_or_wayland!(match self; Self(evlp) => evlp.exit()) x11_or_wayland!(match self; Self(evlp) => evlp.exit())
} }
@@ -931,87 +970,15 @@ impl EventLoopWindowTarget {
x11_or_wayland!(match self; Self(evlp) => evlp.exiting()) x11_or_wayland!(match self; Self(evlp) => evlp.exiting())
} }
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
match self {
#[cfg(x11_platform)]
Self::X(conn) => OwnedDisplayHandle::X(conn.x_connection().clone()),
#[cfg(wayland_platform)]
Self::Wayland(conn) => OwnedDisplayHandle::Wayland(conn.connection.clone()),
}
}
#[allow(dead_code)]
fn set_exit_code(&self, code: i32) { fn set_exit_code(&self, code: i32) {
x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code)) x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code))
} }
#[allow(dead_code)]
fn exit_code(&self) -> Option<i32> { fn exit_code(&self) -> Option<i32> {
x11_or_wayland!(match self; Self(evlp) => evlp.exit_code()) x11_or_wayland!(match self; Self(evlp) => evlp.exit_code())
} }
} }
#[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_05")]
#[inline]
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
match self {
#[cfg(x11_platform)]
Self::X(xconn) => {
let mut xlib_handle = rwh_05::XlibDisplayHandle::empty();
xlib_handle.display = xconn.display.cast();
xlib_handle.screen = xconn.default_screen_index() as _;
xlib_handle.into()
}
#[cfg(wayland_platform)]
Self::Wayland(conn) => {
use sctk::reexports::client::Proxy;
let mut wayland_handle = rwh_05::WaylandDisplayHandle::empty();
wayland_handle.display = conn.display().id().as_ptr() as *mut _;
wayland_handle.into()
}
}
}
#[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())
}
}
}
}
/// Returns the minimum `Option<Duration>`, taking into account that `None` /// Returns the minimum `Option<Duration>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use /// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`) /// `Option::min`)

View File

@@ -16,7 +16,7 @@ use sctk::reexports::calloop_wayland_source::WaylandSource;
use sctk::reexports::client::globals; use sctk::reexports::client::globals;
use sctk::reexports::client::{Connection, QueueHandle}; use sctk::reexports::client::{Connection, QueueHandle};
use crate::dpi::LogicalSize; use crate::dpi::{LogicalSize, PhysicalSize};
use crate::error::{EventLoopError, OsError as RootOsError}; use crate::error::{EventLoopError, OsError as RootOsError};
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent}; use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
use crate::event_loop::{ use crate::event_loop::{
@@ -34,7 +34,7 @@ use sink::EventSink;
use super::state::{WindowCompositorUpdate, WinitState}; use super::state::{WindowCompositorUpdate, WinitState};
use super::window::state::FrameCallbackState; use super::window::state::FrameCallbackState;
use super::{logical_to_physical_rounded, DeviceId, WaylandError, WindowId}; use super::{DeviceId, WaylandError, WindowId};
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>; type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
@@ -63,7 +63,7 @@ pub struct EventLoop<T: 'static> {
connection: Connection, connection: Connection,
/// Event loop window target. /// Event loop window target.
window_target: RootEventLoopWindowTarget, window_target: RootEventLoopWindowTarget<T>,
// XXX drop after everything else, just to be safe. // XXX drop after everything else, just to be safe.
/// Calloop's event loop. /// Calloop's event loop.
@@ -167,6 +167,7 @@ impl<T: 'static> EventLoop<T> {
control_flow: Cell::new(ControlFlow::default()), control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None), exit: Cell::new(None),
state: RefCell::new(winit_state), state: RefCell::new(winit_state),
_marker: PhantomData,
}; };
let event_loop = Self { let event_loop = Self {
@@ -190,8 +191,12 @@ impl<T: 'static> EventLoop<T> {
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
if self.loop_running {
return Err(EventLoopError::AlreadyRunning);
}
let exit = loop { let exit = loop {
match self.pump_events(None, &mut event_handler) { match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
@@ -217,7 +222,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
if !self.loop_running { if !self.loop_running {
self.loop_running = true; self.loop_running = true;
@@ -244,7 +249,7 @@ impl<T: 'static> EventLoop<T> {
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
let cause = loop { let cause = loop {
let start = Instant::now(); let start = Instant::now();
@@ -320,7 +325,7 @@ impl<T: 'static> EventLoop<T> {
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause) fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
// NOTE currently just indented to simplify the diff // NOTE currently just indented to simplify the diff
@@ -351,13 +356,15 @@ impl<T: 'static> EventLoop<T> {
for mut compositor_update in compositor_updates.drain(..) { for mut compositor_update in compositor_updates.drain(..) {
let window_id = compositor_update.window_id; let window_id = compositor_update.window_id;
if compositor_update.scale_changed { if let Some(scale_factor) = compositor_update.scale_factor {
let (physical_size, scale_factor) = self.with_state(|state| { let physical_size = self.with_state(|state| {
let windows = state.windows.get_mut(); let windows = state.windows.get_mut();
let window = windows.get(&window_id).unwrap().lock().unwrap(); let mut window = windows.get(&window_id).unwrap().lock().unwrap();
let scale_factor = window.scale_factor();
let size = logical_to_physical_rounded(window.inner_size(), scale_factor); // Set the new scale factor.
(size, scale_factor) window.set_scale_factor(scale_factor);
let window_size = compositor_update.size.unwrap_or(window.inner_size());
logical_to_physical_rounded(window_size, scale_factor)
}); });
// Stash the old window size. // Stash the old window size.
@@ -379,32 +386,30 @@ impl<T: 'static> EventLoop<T> {
let physical_size = *new_inner_size.lock().unwrap(); let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size); drop(new_inner_size);
let new_logical_size = physical_size.to_logical(scale_factor);
// Resize the window when user altered the size. // Resize the window when user altered the size.
if old_physical_size != physical_size { if old_physical_size != physical_size {
self.with_state(|state| { self.with_state(|state| {
let windows = state.windows.get_mut(); let windows = state.windows.get_mut();
let mut window = windows.get(&window_id).unwrap().lock().unwrap(); let mut window = windows.get(&window_id).unwrap().lock().unwrap();
let new_logical_size: LogicalSize<f64> =
physical_size.to_logical(scale_factor);
window.request_inner_size(new_logical_size.into()); window.request_inner_size(new_logical_size.into());
}); });
// Make it queue resize.
compositor_update.resized = true;
} }
// Make it queue resize.
compositor_update.size = Some(new_logical_size);
} }
// NOTE: Rescale changed the physical size which winit operates in, thus we should if let Some(size) = compositor_update.size.take() {
// resize.
if compositor_update.resized || compositor_update.scale_changed {
let physical_size = self.with_state(|state| { let physical_size = self.with_state(|state| {
let windows = state.windows.get_mut(); let windows = state.windows.get_mut();
let window = windows.get(&window_id).unwrap().lock().unwrap(); let window = windows.get(&window_id).unwrap().lock().unwrap();
let scale_factor = window.scale_factor(); let scale_factor = window.scale_factor();
let size = logical_to_physical_rounded(window.inner_size(), scale_factor); let physical_size = logical_to_physical_rounded(size, scale_factor);
// TODO could probably bring back size reporting optimization.
// Mark the window as needed a redraw. // Mark the window as needed a redraw.
state state
@@ -415,7 +420,7 @@ impl<T: 'static> EventLoop<T> {
.redraw_requested .redraw_requested
.store(true, Ordering::Relaxed); .store(true, Ordering::Relaxed);
size physical_size
}); });
callback( callback(
@@ -461,45 +466,45 @@ impl<T: 'static> EventLoop<T> {
window_ids.extend(state.window_requests.get_mut().keys()); window_ids.extend(state.window_requests.get_mut().keys());
}); });
for window_id in window_ids.iter() { for window_id in window_ids.drain(..) {
let event = self.with_state(|state| { let request_redraw = self.with_state(|state| {
let window_requests = state.window_requests.get_mut(); let window_requests = state.window_requests.get_mut();
if window_requests.get(window_id).unwrap().take_closed() { if window_requests.get(&window_id).unwrap().take_closed() {
mem::drop(window_requests.remove(window_id)); mem::drop(window_requests.remove(&window_id));
mem::drop(state.windows.get_mut().remove(window_id)); mem::drop(state.windows.get_mut().remove(&window_id));
return Some(WindowEvent::Destroyed); false
} else {
let mut window = state
.windows
.get_mut()
.get_mut(&window_id)
.unwrap()
.lock()
.unwrap();
if window.frame_callback_state() == FrameCallbackState::Requested {
false
} else {
// Reset the frame callbacks state.
window.frame_callback_reset();
let mut redraw_requested = window_requests
.get(&window_id)
.unwrap()
.take_redraw_requested();
// Redraw the frame while at it.
redraw_requested |= window.refresh_frame();
redraw_requested
}
} }
let mut window = state
.windows
.get_mut()
.get_mut(window_id)
.unwrap()
.lock()
.unwrap();
if window.frame_callback_state() == FrameCallbackState::Requested {
return None;
}
// Reset the frame callbacks state.
window.frame_callback_reset();
let mut redraw_requested = window_requests
.get(window_id)
.unwrap()
.take_redraw_requested();
// Redraw the frame while at it.
redraw_requested |= window.refresh_frame();
redraw_requested.then_some(WindowEvent::RedrawRequested)
}); });
if let Some(event) = event { if request_redraw {
callback( callback(
Event::WindowEvent { Event::WindowEvent {
window_id: crate::window::WindowId(*window_id), window_id: crate::window::WindowId(window_id),
event, event: WindowEvent::RedrawRequested,
}, },
&self.window_target, &self.window_target,
); );
@@ -514,42 +519,6 @@ impl<T: 'static> EventLoop<T> {
// This is always the last event we dispatch before poll again // This is always the last event we dispatch before poll again
callback(Event::AboutToWait, &self.window_target); callback(Event::AboutToWait, &self.window_target);
// Update the window frames and schedule redraws.
let mut wake_up = false;
for window_id in window_ids.drain(..) {
wake_up |= self.with_state(|state| match state.windows.get_mut().get_mut(&window_id) {
Some(window) => {
let refresh = window.lock().unwrap().refresh_frame();
if refresh {
state
.window_requests
.get_mut()
.get_mut(&window_id)
.unwrap()
.redraw_requested
.store(true, Ordering::Relaxed);
}
refresh
}
None => false,
});
}
// Wakeup event loop if needed.
//
// If the user draws from the `AboutToWait` this is likely not required, however
// we can't do much about it.
if wake_up {
match &self.window_target.p {
PlatformEventLoopWindowTarget::Wayland(window_target) => {
window_target.event_loop_awakener.ping();
}
#[cfg(x11_platform)]
PlatformEventLoopWindowTarget::X(_) => unreachable!(),
}
}
std::mem::swap(&mut self.compositor_updates, &mut compositor_updates); std::mem::swap(&mut self.compositor_updates, &mut compositor_updates);
std::mem::swap(&mut self.buffer_sink, &mut buffer_sink); std::mem::swap(&mut self.buffer_sink, &mut buffer_sink);
std::mem::swap(&mut self.window_ids, &mut window_ids); std::mem::swap(&mut self.window_ids, &mut window_ids);
@@ -561,7 +530,7 @@ impl<T: 'static> EventLoop<T> {
} }
#[inline] #[inline]
pub fn window_target(&self) -> &RootEventLoopWindowTarget { pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
&self.window_target &self.window_target
} }
@@ -583,7 +552,7 @@ impl<T: 'static> EventLoop<T> {
}; };
self.event_loop.dispatch(timeout, state).map_err(|error| { self.event_loop.dispatch(timeout, state).map_err(|error| {
log::error!("Error dispatching event loop: {}", error); error!("Error dispatching event loop: {}", error);
error.into() error.into()
}) })
} }
@@ -633,7 +602,7 @@ impl<T> AsRawFd for EventLoop<T> {
} }
} }
pub struct EventLoopWindowTarget { pub struct EventLoopWindowTarget<T> {
/// The event loop wakeup source. /// The event loop wakeup source.
pub event_loop_awakener: calloop::ping::Ping, pub event_loop_awakener: calloop::ping::Ping,
@@ -655,37 +624,11 @@ pub struct EventLoopWindowTarget {
/// Connection to the wayland server. /// Connection to the wayland server.
pub connection: Connection, pub connection: Connection,
_marker: std::marker::PhantomData<T>,
} }
impl EventLoopWindowTarget { impl<T> EventLoopWindowTarget<T> {
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow)
}
pub(crate) fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}
pub(crate) fn exit(&self) {
self.exit.set(Some(0))
}
pub(crate) fn clear_exit(&self) {
self.exit.set(None)
}
pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}
pub(crate) fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}
pub(crate) fn exit_code(&self) -> Option<i32> {
self.exit.get()
}
#[inline] #[inline]
pub fn listen_device_events(&self, _allowed: DeviceEvents) {} pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
@@ -713,3 +656,10 @@ impl EventLoopWindowTarget {
.into()) .into())
} }
} }
// The default routine does floor, but we need round on Wayland.
fn logical_to_physical_rounded(size: LogicalSize<u32>, scale_factor: f64) -> PhysicalSize<u32> {
let width = size.width as f64 * scale_factor;
let height = size.height as f64 * scale_factor;
(width.round(), height.round()).into()
}

View File

@@ -9,11 +9,9 @@ use sctk::reexports::client::globals::{BindError, GlobalError};
use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{self, ConnectError, DispatchError, Proxy}; use sctk::reexports::client::{self, ConnectError, DispatchError, Proxy};
pub(super) use crate::cursor::OnlyCursorImage as CustomCursor;
use crate::dpi::{LogicalSize, PhysicalSize};
pub use crate::platform_impl::platform::{OsError, WindowId}; pub use crate::platform_impl::platform::{OsError, WindowId};
pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
pub use output::{MonitorHandle, VideoModeHandle}; pub use output::{MonitorHandle, VideoMode};
pub use window::Window; pub use window::Window;
mod event_loop; mod event_loop;
@@ -78,10 +76,3 @@ impl DeviceId {
fn make_wid(surface: &WlSurface) -> WindowId { fn make_wid(surface: &WlSurface) -> WindowId {
WindowId(surface.id().as_ptr() as u64) WindowId(surface.id().as_ptr() as u64)
} }
/// The default routine does floor, but we need round on Wayland.
fn logical_to_physical_rounded(size: LogicalSize<u32>, scale_factor: f64) -> PhysicalSize<u32> {
let width = size.width as f64 * scale_factor;
let height = size.height as f64 * scale_factor;
(width.round(), height.round()).into()
}

View File

@@ -4,11 +4,12 @@ use sctk::reexports::client::Proxy;
use sctk::output::OutputData; use sctk::output::OutputData;
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::platform_impl::platform::VideoModeHandle as PlatformVideoModeHandle; use crate::event_loop::ControlFlow;
use crate::platform_impl::platform::VideoMode as PlatformVideoMode;
use super::event_loop::EventLoopWindowTarget; use super::event_loop::EventLoopWindowTarget;
impl EventLoopWindowTarget { impl<T> EventLoopWindowTarget<T> {
#[inline] #[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
self.state self.state
@@ -23,6 +24,30 @@ impl EventLoopWindowTarget {
// There's no primary monitor on Wayland. // There's no primary monitor on Wayland.
None None
} }
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow)
}
pub(crate) fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}
pub(crate) fn exit(&self) {
self.exit.set(Some(0))
}
pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}
pub(crate) fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}
pub(crate) fn exit_code(&self) -> Option<i32> {
self.exit.get()
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -98,14 +123,14 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn video_modes(&self) -> impl Iterator<Item = PlatformVideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = PlatformVideoMode> {
let output_data = self.proxy.data::<OutputData>().unwrap(); let output_data = self.proxy.data::<OutputData>().unwrap();
let modes = output_data.with_output_info(|info| info.modes.clone()); let modes = output_data.with_output_info(|info| info.modes.clone());
let monitor = self.clone(); let monitor = self.clone();
modes.into_iter().map(move |mode| { modes.into_iter().map(move |mode| {
PlatformVideoModeHandle::Wayland(VideoModeHandle { PlatformVideoMode::Wayland(VideoMode {
size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(), size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(),
refresh_rate_millihertz: mode.refresh_rate as u32, refresh_rate_millihertz: mode.refresh_rate as u32,
bit_depth: 32, bit_depth: 32,
@@ -142,14 +167,14 @@ impl std::hash::Hash for MonitorHandle {
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VideoModeHandle { pub struct VideoMode {
pub(crate) size: PhysicalSize<u32>, pub(crate) size: PhysicalSize<u32>,
pub(crate) bit_depth: u16, pub(crate) bit_depth: u16,
pub(crate) refresh_rate_millihertz: u32, pub(crate) refresh_rate_millihertz: u32,
pub(crate) monitor: MonitorHandle, pub(crate) monitor: MonitorHandle,
} }
impl VideoModeHandle { impl VideoMode {
#[inline] #[inline]
pub fn size(&self) -> PhysicalSize<u32> { pub fn size(&self) -> PhysicalSize<u32> {
self.size self.size

View File

@@ -61,13 +61,8 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
let window_id = wayland::make_wid(&surface); let window_id = wayland::make_wid(&surface);
// Mark the window as focused. // Mark the window as focused.
let was_unfocused = match state.windows.get_mut().get(&window_id) { match state.windows.get_mut().get(&window_id) {
Some(window) => { Some(window) => window.lock().unwrap().set_has_focus(true),
let mut window = window.lock().unwrap();
let was_unfocused = !window.has_focus();
window.add_seat_focus(data.seat.id());
was_unfocused
}
None => return, None => return,
}; };
@@ -78,14 +73,12 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
keyboard_state.loop_handle.remove(token); keyboard_state.loop_handle.remove(token);
} }
*data.window_id.lock().unwrap() = Some(window_id);
// The keyboard focus is considered as general focus. // The keyboard focus is considered as general focus.
if was_unfocused { state
state .events_sink
.events_sink .push_window_event(WindowEvent::Focused(true), window_id);
.push_window_event(WindowEvent::Focused(true), window_id);
} *data.window_id.lock().unwrap() = Some(window_id);
// HACK: this is just for GNOME not fixing their ordering issue of modifiers. // HACK: this is just for GNOME not fixing their ordering issue of modifiers.
if std::mem::take(&mut seat_state.modifiers_pending) { if std::mem::take(&mut seat_state.modifiers_pending) {
@@ -108,30 +101,24 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
// NOTE: The check whether the window exists is essential as we might get a // NOTE: The check whether the window exists is essential as we might get a
// nil surface, regardless of what protocol says. // nil surface, regardless of what protocol says.
let focused = match state.windows.get_mut().get(&window_id) { match state.windows.get_mut().get(&window_id) {
Some(window) => { Some(window) => window.lock().unwrap().set_has_focus(false),
let mut window = window.lock().unwrap();
window.remove_seat_focus(&data.seat.id());
window.has_focus()
}
None => return, None => return,
}; };
// Notify that no modifiers are being pressed.
state.events_sink.push_window_event(
WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
window_id,
);
// We don't need to update it above, because the next `Enter` will overwrite // We don't need to update it above, because the next `Enter` will overwrite
// anyway. // anyway.
*data.window_id.lock().unwrap() = None; *data.window_id.lock().unwrap() = None;
if !focused { state
// Notify that no modifiers are being pressed. .events_sink
state.events_sink.push_window_event( .push_window_event(WindowEvent::Focused(false), window_id);
WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
window_id,
);
state
.events_sink
.push_window_event(WindowEvent::Focused(false), window_id);
}
} }
WlKeyboardEvent::Key { WlKeyboardEvent::Key {
key, key,

View File

@@ -4,7 +4,6 @@ use std::sync::Arc;
use ahash::AHashMap; use ahash::AHashMap;
use sctk::reexports::client::backend::ObjectId;
use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_touch::WlTouch; use sctk::reexports::client::protocol::wl_touch::WlTouch;
use sctk::reexports::client::{Connection, Proxy, QueueHandle}; use sctk::reexports::client::{Connection, Proxy, QueueHandle};
@@ -14,7 +13,6 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
use sctk::seat::pointer::{ThemeSpec, ThemedPointer}; use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState}; use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
use crate::event::WindowEvent;
use crate::keyboard::ModifiersState; use crate::keyboard::ModifiersState;
use crate::platform_impl::wayland::state::WinitState; use crate::platform_impl::wayland::state::WinitState;
@@ -145,10 +143,6 @@ impl SeatHandler for WinitState {
) { ) {
let seat_state = self.seats.get_mut(&seat.id()).unwrap(); let seat_state = self.seats.get_mut(&seat.id()).unwrap();
if let Some(text_input) = seat_state.text_input.take() {
text_input.destroy();
}
match capability { match capability {
SeatCapability::Touch => { SeatCapability::Touch => {
if let Some(touch) = seat_state.touch.take() { if let Some(touch) = seat_state.touch.take() {
@@ -180,10 +174,13 @@ impl SeatHandler for WinitState {
} }
SeatCapability::Keyboard => { SeatCapability::Keyboard => {
seat_state.keyboard_state = None; seat_state.keyboard_state = None;
self.on_keyboard_destroy(&seat.id());
} }
_ => (), _ => (),
} }
if let Some(text_input) = seat_state.text_input.take() {
text_input.destroy();
}
} }
fn new_seat( fn new_seat(
@@ -202,21 +199,6 @@ impl SeatHandler for WinitState {
seat: WlSeat, seat: WlSeat,
) { ) {
let _ = self.seats.remove(&seat.id()); let _ = self.seats.remove(&seat.id());
self.on_keyboard_destroy(&seat.id());
}
}
impl WinitState {
fn on_keyboard_destroy(&mut self, seat: &ObjectId) {
for (window_id, window) in self.windows.get_mut() {
let mut window = window.lock().unwrap();
let had_focus = window.has_focus();
window.remove_seat_focus(seat);
if had_focus != window.has_focus() {
self.events_sink
.push_window_event(WindowEvent::Focused(false), *window_id);
}
}
} }
} }

View File

@@ -23,19 +23,21 @@ use sctk::shm::slot::SlotPool;
use sctk::shm::{Shm, ShmHandler}; use sctk::shm::{Shm, ShmHandler};
use sctk::subcompositor::SubcompositorState; use sctk::subcompositor::SubcompositorState;
use crate::platform_impl::wayland::event_loop::sink::EventSink; use crate::dpi::LogicalSize;
use crate::platform_impl::wayland::output::MonitorHandle; use crate::platform_impl::OsError;
use crate::platform_impl::wayland::seat::{
use super::event_loop::sink::EventSink;
use super::output::MonitorHandle;
use super::seat::{
PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData, PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData,
WinitPointerDataExt, WinitSeatState, WinitPointerDataExt, WinitSeatState,
}; };
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; use super::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScalingManager; use super::types::wp_fractional_scaling::FractionalScalingManager;
use crate::platform_impl::wayland::types::wp_viewporter::ViewporterState; use super::types::wp_viewporter::ViewporterState;
use crate::platform_impl::wayland::types::xdg_activation::XdgActivationState; use super::types::xdg_activation::XdgActivationState;
use crate::platform_impl::wayland::window::{WindowRequests, WindowState}; use super::window::{WindowRequests, WindowState};
use crate::platform_impl::wayland::{WaylandError, WindowId}; use super::{WaylandError, WindowId};
use crate::platform_impl::OsError;
/// Winit's Wayland state. /// Winit's Wayland state.
pub struct WinitState { pub struct WinitState {
@@ -133,7 +135,7 @@ impl WinitState {
) { ) {
Ok(c) => Some(c), Ok(c) => Some(c),
Err(e) => { Err(e) => {
log::warn!("Subcompositor protocol not available, ignoring CSD: {e:?}"); warn!("Subcompositor protocol not available, ignoring CSD: {e:?}");
None None
} }
}; };
@@ -225,7 +227,7 @@ impl WinitState {
// Update the scale factor right away. // Update the scale factor right away.
window.lock().unwrap().set_scale_factor(scale_factor); window.lock().unwrap().set_scale_factor(scale_factor);
self.window_compositor_updates[pos].scale_changed = true; self.window_compositor_updates[pos].scale_factor = Some(scale_factor);
} else if let Some(pointer) = self.pointer_surfaces.get(&surface.id()) { } else if let Some(pointer) = self.pointer_surfaces.get(&surface.id()) {
// Get the window, where the pointer resides right now. // Get the window, where the pointer resides right now.
let focused_window = match pointer.pointer().winit_data().focused_window() { let focused_window = match pointer.pointer().winit_data().focused_window() {
@@ -289,26 +291,27 @@ impl WindowHandler for WinitState {
}; };
// Populate the configure to the window. // Populate the configure to the window.
self.window_compositor_updates[pos].resized |= self //
// XXX the size on the window will be updated right before dispatching the size to the user.
let new_size = self
.windows .windows
.get_mut() .get_mut()
.get_mut(&window_id) .get_mut(&window_id)
.expect("got configure for dead window.") .expect("got configure for dead window.")
.lock() .lock()
.unwrap() .unwrap()
.configure(configure, &self.shm, &self.subcompositor_state); .configure(
configure,
&self.shm,
&self.subcompositor_state,
&mut self.events_sink,
);
// NOTE: configure demands wl_surface::commit, however winit doesn't commit on behalf of the // NOTE: Only update when the value is `Some` to not override consequent configures with
// users, since it can break a lot of things, thus it'll ask users to redraw instead. // the same sizes.
self.window_requests if new_size.is_some() {
.get_mut() self.window_compositor_updates[pos].size = new_size;
.get(&window_id) }
.unwrap()
.redraw_requested
.store(true, Ordering::Relaxed);
// Manually mark that we've got an event, since configure may not generate a resize.
self.dispatched_events = true;
} }
} }
@@ -402,10 +405,10 @@ pub struct WindowCompositorUpdate {
pub window_id: WindowId, pub window_id: WindowId,
/// New window size. /// New window size.
pub resized: bool, pub size: Option<LogicalSize<u32>>,
/// New scale factor. /// New scale factor.
pub scale_changed: bool, pub scale_factor: Option<f64>,
/// Close the window. /// Close the window.
pub close_window: bool, pub close_window: bool,
@@ -415,8 +418,8 @@ impl WindowCompositorUpdate {
fn new(window_id: WindowId) -> Self { fn new(window_id: WindowId) -> Self {
Self { Self {
window_id, window_id,
resized: false, size: None,
scale_changed: false, scale_factor: None,
close_window: false, close_window: false,
} }
} }

View File

@@ -37,15 +37,12 @@ impl CustomCursor {
) )
.unwrap(); .unwrap();
for (canvas_chunk, rgba) in canvas.chunks_exact_mut(4).zip(image.rgba.chunks_exact(4)) { for (canvas_chunk, rgba_chunk) in canvas.chunks_exact_mut(4).zip(image.rgba.chunks_exact(4))
// Alpha in buffer is premultiplied. {
let alpha = rgba[3] as f32 / 255.; canvas_chunk[0] = rgba_chunk[2];
let r = (rgba[0] as f32 * alpha) as u32; canvas_chunk[1] = rgba_chunk[1];
let g = (rgba[1] as f32 * alpha) as u32; canvas_chunk[2] = rgba_chunk[0];
let b = (rgba[2] as f32 * alpha) as u32; canvas_chunk[3] = rgba_chunk[3];
let color = ((rgba[3] as u32) << 24) + (r << 16) + (g << 8) + b;
let array: &mut [u8; 4] = canvas_chunk.try_into().unwrap();
*array = color.to_le_bytes();
} }
CustomCursor { CustomCursor {

View File

@@ -15,17 +15,16 @@ use sctk::shell::xdg::window::Window as SctkWindow;
use sctk::shell::xdg::window::WindowDecorations; use sctk::shell::xdg::window::WindowDecorations;
use sctk::shell::WaylandSurface; use sctk::shell::WaylandSurface;
use log::warn;
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError}; use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
use crate::event::{Ime, WindowEvent}; use crate::event::{Ime, WindowEvent};
use crate::event_loop::AsyncRequestSerial; use crate::event_loop::AsyncRequestSerial;
use crate::platform_impl::{ use crate::platform_impl::{
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon, Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor,
PlatformIcon, PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
}; };
use crate::window::{ use crate::window::{
Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowLevel, WindowAttributes, WindowButtons, WindowLevel,
}; };
@@ -80,9 +79,10 @@ pub struct Window {
} }
impl Window { impl Window {
pub(crate) fn new( pub(crate) fn new<T>(
event_loop_window_target: &EventLoopWindowTarget, event_loop_window_target: &EventLoopWindowTarget<T>,
attributes: WindowAttributes, attributes: WindowAttributes,
platform_attributes: PlatformAttributes,
) -> Result<Self, RootOsError> { ) -> Result<Self, RootOsError> {
let queue_handle = event_loop_window_target.queue_handle.clone(); let queue_handle = event_loop_window_target.queue_handle.clone();
let mut state = event_loop_window_target.state.borrow_mut(); let mut state = event_loop_window_target.state.borrow_mut();
@@ -132,7 +132,7 @@ impl Window {
window_state.set_decorate(attributes.decorations); window_state.set_decorate(attributes.decorations);
// Set the app_id. // Set the app_id.
if let Some(name) = attributes.platform_specific.name.map(|name| name.general) { if let Some(name) = platform_attributes.name.map(|name| name.general) {
window.set_app_id(name); window.set_app_id(name);
} }
@@ -150,7 +150,7 @@ impl Window {
window_state.set_resizable(attributes.resizable); window_state.set_resizable(attributes.resizable);
// Set startup mode. // Set startup mode.
match attributes.fullscreen.map(Into::into) { match attributes.fullscreen.0.map(Into::into) {
Some(Fullscreen::Exclusive(_)) => { Some(Fullscreen::Exclusive(_)) => {
warn!("`Fullscreen::Exclusive` is ignored on Wayland"); warn!("`Fullscreen::Exclusive` is ignored on Wayland");
} }
@@ -167,15 +167,10 @@ impl Window {
_ => (), _ => (),
}; };
match attributes.cursor {
Cursor::Icon(icon) => window_state.set_cursor(icon),
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
}
// Activate the window when the token is passed. // Activate the window when the token is passed.
if let (Some(xdg_activation), Some(token)) = ( if let (Some(xdg_activation), Some(token)) = (
xdg_activation.as_ref(), xdg_activation.as_ref(),
attributes.platform_specific.activation_token, platform_attributes.activation_token,
) { ) {
xdg_activation.activate(token._token, &surface); xdg_activation.activate(token._token, &surface);
} }
@@ -285,7 +280,7 @@ impl Window {
pub fn inner_size(&self) -> PhysicalSize<u32> { pub fn inner_size(&self) -> PhysicalSize<u32> {
let window_state = self.window_state.lock().unwrap(); let window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor(); let scale_factor = window_state.scale_factor();
super::logical_to_physical_rounded(window_state.inner_size(), scale_factor) window_state.inner_size().to_physical(scale_factor)
} }
#[inline] #[inline]
@@ -313,7 +308,7 @@ impl Window {
pub fn outer_size(&self) -> PhysicalSize<u32> { pub fn outer_size(&self) -> PhysicalSize<u32> {
let window_state = self.window_state.lock().unwrap(); let window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor(); let scale_factor = window_state.scale_factor();
super::logical_to_physical_rounded(window_state.outer_size(), scale_factor) window_state.outer_size().to_physical(scale_factor)
} }
#[inline] #[inline]
@@ -332,9 +327,7 @@ impl Window {
self.window_state self.window_state
.lock() .lock()
.unwrap() .unwrap()
.set_min_inner_size(min_size); .set_min_inner_size(min_size)
// NOTE: Requires commit to be applied.
self.request_redraw();
} }
/// Set the maximum inner size for the window. /// Set the maximum inner size for the window.
@@ -345,9 +338,7 @@ impl Window {
self.window_state self.window_state
.lock() .lock()
.unwrap() .unwrap()
.set_max_inner_size(max_size); .set_max_inner_size(max_size)
// NOTE: Requires commit to be applied.
self.request_redraw();
} }
#[inline] #[inline]
@@ -396,10 +387,7 @@ impl Window {
#[inline] #[inline]
pub fn set_resizable(&self, resizable: bool) { pub fn set_resizable(&self, resizable: bool) {
if self.window_state.lock().unwrap().set_resizable(resizable) { self.window_state.lock().unwrap().set_resizable(resizable);
// NOTE: Requires commit to be applied.
self.request_redraw();
}
} }
#[inline] #[inline]
@@ -514,13 +502,16 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_cursor(&self, cursor: Cursor) { pub fn set_cursor_icon(&self, cursor: CursorIcon) {
let window_state = &mut self.window_state.lock().unwrap(); self.window_state.lock().unwrap().set_cursor(cursor);
}
match cursor { #[inline]
Cursor::Icon(icon) => window_state.set_cursor(icon), pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor), self.window_state
} .lock()
.unwrap()
.set_custom_cursor(&cursor.0);
} }
#[inline] #[inline]

View File

@@ -4,10 +4,8 @@ use std::num::NonZeroU32;
use std::sync::{Arc, Mutex, Weak}; use std::sync::{Arc, Mutex, Weak};
use std::time::Duration; use std::time::Duration;
use ahash::HashSet;
use log::{info, warn}; use log::{info, warn};
use sctk::reexports::client::backend::ObjectId;
use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_shm::WlShm; use sctk::reexports::client::protocol::wl_shm::WlShm;
use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::protocol::wl_surface::WlSurface;
@@ -30,13 +28,15 @@ use sctk::shm::Shm;
use sctk::subcompositor::SubcompositorState; use sctk::subcompositor::SubcompositorState;
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
use crate::cursor::CustomCursor as RootCustomCursor; use crate::cursor::CursorImage;
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size}; use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
use crate::error::{ExternalError, NotSupportedError}; use crate::error::{ExternalError, NotSupportedError};
use crate::platform_impl::wayland::logical_to_physical_rounded; use crate::event::WindowEvent;
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::make_wid;
use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor}; use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor};
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::{PlatformCustomCursor, WindowId}; use crate::platform_impl::WindowId;
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme}; use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};
use crate::platform_impl::wayland::seat::{ use crate::platform_impl::wayland::seat::{
@@ -92,10 +92,8 @@ pub struct WindowState {
/// Whether the frame is resizable. /// Whether the frame is resizable.
resizable: bool, resizable: bool,
// NOTE: we can't use simple counter, since it's racy when seat getting destroyed and new /// Whether the window has focus.
// is created, since add/removed stuff could be delivered a bit out of order. has_focus: bool,
/// Seats that has keyboard focus on that window.
seat_focus: HashSet<ObjectId>,
/// The scale factor of the window. /// The scale factor of the window.
scale_factor: f64, scale_factor: f64,
@@ -191,7 +189,7 @@ impl WindowState {
fractional_scale, fractional_scale,
frame: None, frame: None,
frame_callback_state: FrameCallbackState::None, frame_callback_state: FrameCallbackState::None,
seat_focus: Default::default(), has_focus: false,
has_pending_move: None, has_pending_move: None,
ime_allowed: false, ime_allowed: false,
ime_purpose: ImePurpose::Normal, ime_purpose: ImePurpose::Normal,
@@ -263,7 +261,8 @@ impl WindowState {
configure: WindowConfigure, configure: WindowConfigure,
shm: &Shm, shm: &Shm,
subcompositor: &Option<Arc<SubcompositorState>>, subcompositor: &Option<Arc<SubcompositorState>>,
) -> bool { event_sink: &mut EventSink,
) -> Option<LogicalSize<u32>> {
// NOTE: when using fractional scaling or wl_compositor@v6 the scaling // NOTE: when using fractional scaling or wl_compositor@v6 the scaling
// should be delivered before the first configure, thus apply it to // should be delivered before the first configure, thus apply it to
// properly scale the physical sizes provided by the users. // properly scale the physical sizes provided by the users.
@@ -306,6 +305,19 @@ impl WindowState {
let stateless = Self::is_stateless(&configure); let stateless = Self::is_stateless(&configure);
// Emit `Occluded` event on suspension change.
let occluded = configure.state.contains(XdgWindowState::SUSPENDED);
if self
.last_configure
.as_ref()
.map(|c| c.state.contains(XdgWindowState::SUSPENDED))
.unwrap_or(false)
!= occluded
{
let window_id = make_wid(self.window.wl_surface());
event_sink.push_window_event(WindowEvent::Occluded(occluded), window_id);
}
let (mut new_size, constrain) = if let Some(frame) = self.frame.as_mut() { let (mut new_size, constrain) = if let Some(frame) = self.frame.as_mut() {
// Configure the window states. // Configure the window states.
frame.update_state(configure.state); frame.update_state(configure.state);
@@ -362,9 +374,9 @@ impl WindowState {
if state_change_requires_resize || new_size != self.inner_size() { if state_change_requires_resize || new_size != self.inner_size() {
self.resize(new_size); self.resize(new_size);
true Some(new_size)
} else { } else {
false None
} }
} }
@@ -504,12 +516,10 @@ impl WindowState {
} }
/// Set the resizable state on the window. /// Set the resizable state on the window.
///
/// Returns `true` when the state was applied.
#[inline] #[inline]
pub fn set_resizable(&mut self, resizable: bool) -> bool { pub fn set_resizable(&mut self, resizable: bool) {
if self.resizable == resizable { if self.resizable == resizable {
return false; return;
} }
self.resizable = resizable; self.resizable = resizable;
@@ -525,14 +535,12 @@ impl WindowState {
if let Some(frame) = self.frame.as_mut() { if let Some(frame) = self.frame.as_mut() {
frame.set_resizable(resizable); frame.set_resizable(resizable);
} }
true
} }
/// Whether the window is focused by any seat. /// Whether the window is focused.
#[inline] #[inline]
pub fn has_focus(&self) -> bool { pub fn has_focus(&self) -> bool {
!self.seat_focus.is_empty() self.has_focus
} }
/// Whether the IME is allowed. /// Whether the IME is allowed.
@@ -648,7 +656,7 @@ impl WindowState {
self.resize(inner_size.to_logical(self.scale_factor())) self.resize(inner_size.to_logical(self.scale_factor()))
} }
logical_to_physical_rounded(self.inner_size(), self.scale_factor()) self.inner_size().to_physical(self.scale_factor())
} }
/// Resize the window to the new inner size. /// Resize the window to the new inner size.
@@ -718,23 +726,10 @@ impl WindowState {
} }
/// Set the custom cursor icon. /// Set the custom cursor icon.
pub(crate) fn set_custom_cursor(&mut self, cursor: RootCustomCursor) { pub(crate) fn set_custom_cursor(&mut self, cursor: &CursorImage) {
let cursor = match cursor {
RootCustomCursor {
inner: PlatformCustomCursor::Wayland(cursor),
} => cursor.0,
#[cfg(x11_platform)]
RootCustomCursor {
inner: PlatformCustomCursor::X(_),
} => {
log::error!("passed a X11 cursor to Wayland backend");
return;
}
};
let cursor = { let cursor = {
let mut pool = self.custom_cursor_pool.lock().unwrap(); let mut pool = self.custom_cursor_pool.lock().unwrap();
CustomCursor::new(&mut pool, &cursor) CustomCursor::new(&mut pool, cursor)
}; };
if self.cursor_visible { if self.cursor_visible {
@@ -959,16 +954,12 @@ impl WindowState {
} }
} }
/// Add seat focus for the window. /// Mark that the window has focus.
///
/// Should be used from routine that sends focused event.
#[inline] #[inline]
pub fn add_seat_focus(&mut self, seat: ObjectId) { pub fn set_has_focus(&mut self, has_focus: bool) {
self.seat_focus.insert(seat); self.has_focus = has_focus;
}
/// Remove seat focus from the window.
#[inline]
pub fn remove_seat_focus(&mut self, seat: &ObjectId) {
self.seat_focus.remove(seat);
} }
/// Returns `true` if the requested state was applied. /// Returns `true` if the requested state was applied.

View File

@@ -6,7 +6,7 @@ macro_rules! atom_manager {
($($name:ident $(:$lit:literal)?),*) => { ($($name:ident $(:$lit:literal)?),*) => {
x11rb::atom_manager! { x11rb::atom_manager! {
/// The atoms used by `winit` /// The atoms used by `winit`
pub Atoms: AtomsCookie { pub(crate) Atoms: AtomsCookie {
$($name $(:$lit)?,)* $($name $(:$lit)?,)*
} }
} }
@@ -14,7 +14,7 @@ macro_rules! atom_manager {
/// Indices into the `Atoms` struct. /// Indices into the `Atoms` struct.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum AtomName { pub(crate) enum AtomName {
$($name,)* $($name,)*
} }
@@ -100,8 +100,7 @@ atom_manager! {
_NET_FRAME_EXTENTS, _NET_FRAME_EXTENTS,
_NET_SUPPORTED, _NET_SUPPORTED,
_NET_SUPPORTING_WM_CHECK, _NET_SUPPORTING_WM_CHECK,
_XEMBED, _XEMBED
_XSETTINGS_SETTINGS
} }
impl Index<AtomName> for Atoms { impl Index<AtomName> for Atoms {

View File

@@ -41,7 +41,7 @@ impl From<io::Error> for DndDataParseError {
} }
} }
pub struct Dnd { pub(crate) struct Dnd {
xconn: Arc<XConnection>, xconn: Arc<XConnection>,
// Populated by XdndEnter event handler // Populated by XdndEnter event handler
pub version: Option<c_long>, pub version: Option<c_long>,

File diff suppressed because it is too large Load Diff

View File

@@ -79,9 +79,9 @@ pub(crate) unsafe fn set_destroy_callback(
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
enum ReplaceImError { enum ReplaceImError {
// Boxed to prevent large error type // Boxed to prevent large error type
MethodOpenFailed(#[allow(dead_code)] Box<PotentialInputMethods>), MethodOpenFailed(Box<PotentialInputMethods>),
ContextCreationFailed(#[allow(dead_code)] ImeContextCreationError), ContextCreationFailed(ImeContextCreationError),
SetDestroyCallbackFailed(#[allow(dead_code)] XError), SetDestroyCallbackFailed(XError),
} }
// Attempt to replace current IM (which may or may not be presently valid) with a new one. This // Attempt to replace current IM (which may or may not be presently valid) with a new one. This

View File

@@ -86,7 +86,7 @@ extern "C" fn preedit_draw_callback(
let chg_range = let chg_range =
call_data.chg_first as usize..(call_data.chg_first + call_data.chg_length) as usize; call_data.chg_first as usize..(call_data.chg_first + call_data.chg_length) as usize;
if chg_range.start > client_data.text.len() || chg_range.end > client_data.text.len() { if chg_range.start > client_data.text.len() || chg_range.end > client_data.text.len() {
log::warn!( warn!(
"invalid chg range: buffer length={}, but chg_first={} chg_lengthg={}", "invalid chg range: buffer length={}, but chg_first={} chg_lengthg={}",
client_data.text.len(), client_data.text.len(),
call_data.chg_first, call_data.chg_first,

View File

@@ -159,9 +159,9 @@ impl InputMethodResult {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum GetXimServersError { enum GetXimServersError {
XError(#[allow(dead_code)] XError), XError(XError),
GetPropertyError(#[allow(dead_code)] util::GetPropertyError), GetPropertyError(util::GetPropertyError),
InvalidUtf8(#[allow(dead_code)] IntoStringError), InvalidUtf8(IntoStringError),
} }
impl From<util::GetPropertyError> for GetXimServersError { impl From<util::GetPropertyError> for GetXimServersError {

View File

@@ -10,10 +10,6 @@ use std::sync::{
Arc, Arc,
}; };
use log::debug;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::{ffi, util, XConnection, XError}; use super::{ffi, util, XConnection, XError};
pub use self::context::ImeContextCreationError; pub use self::context::ImeContextCreationError;
@@ -52,7 +48,7 @@ pub enum ImeRequest {
pub(crate) enum ImeCreationError { pub(crate) enum ImeCreationError {
// Boxed to prevent large error type // Boxed to prevent large error type
OpenFailure(Box<PotentialInputMethods>), OpenFailure(Box<PotentialInputMethods>),
SetDestroyCallbackFailed(#[allow(dead_code)] XError), SetDestroyCallbackFailed(XError),
} }
pub(crate) struct Ime { pub(crate) struct Ime {

View File

@@ -1,43 +1,5 @@
#![cfg(x11_platform)] #![cfg(x11_platform)]
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, HashSet};
use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::os::raw::*;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
use std::sync::{Arc, Weak};
use std::time::{Duration, Instant};
use std::{ptr, slice, str};
use calloop::generic::Generic;
use calloop::EventLoop as Loop;
use calloop::{ping::Ping, Readiness};
use libc::{self, setlocale, LC_CTYPE};
use log::warn;
use x11rb::connection::RequestConnection;
use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
use x11rb::protocol::xinput::{self, ConnectionExt as _};
use x11rb::protocol::xkb;
use x11rb::protocol::xproto::{self, ConnectionExt as _};
use x11rb::x11_utils::X11Error as LogicalError;
use x11rb::xcb_ffi::ReplyOrIdError;
use super::{common::xkb_state::KbdState, ControlFlow, OsError};
use crate::{
error::{EventLoopError, OsError as RootOsError},
event::{Event, StartCause, WindowEvent},
event_loop::{DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
platform::pump_events::PumpStatus,
platform_impl::platform::{min_timeout, WindowId},
window::WindowAttributes,
};
mod activation; mod activation;
mod atoms; mod atoms;
mod dnd; mod dnd;
@@ -45,20 +7,76 @@ mod event_processor;
pub mod ffi; pub mod ffi;
mod ime; mod ime;
mod monitor; mod monitor;
mod util; pub mod util;
mod window; mod window;
mod xdisplay; mod xdisplay;
mod xsettings;
pub(crate) use self::{
monitor::{MonitorHandle, VideoMode},
window::UnownedWindow,
xdisplay::XConnection,
};
pub use self::xdisplay::{XError, XNotSupported};
use calloop::generic::Generic;
use calloop::EventLoop as Loop;
use calloop::{ping::Ping, Readiness};
use std::{
cell::{Cell, RefCell},
collections::{HashMap, HashSet},
ffi::CStr,
fmt,
mem::MaybeUninit,
ops::Deref,
os::{
raw::*,
unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd},
},
ptr,
rc::Rc,
slice, str,
sync::mpsc::{Receiver, Sender, TryRecvError},
sync::{mpsc, Arc, Weak},
time::{Duration, Instant},
};
use libc::{self, setlocale, LC_CTYPE};
use atoms::*; use atoms::*;
use dnd::{Dnd, DndState};
use event_processor::EventProcessor;
use ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender};
pub(crate) use monitor::{MonitorHandle, VideoModeHandle};
use window::UnownedWindow;
pub(crate) use xdisplay::{XConnection, XError, XNotSupported};
pub use util::CustomCursor; use x11rb::x11_utils::X11Error as LogicalError;
use x11rb::{
connection::RequestConnection,
protocol::{
xinput::{self, ConnectionExt as _},
xkb,
xproto::{self, ConnectionExt as _},
},
};
use x11rb::{
errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError},
xcb_ffi::ReplyOrIdError,
};
use self::{
dnd::{Dnd, DndState},
event_processor::EventProcessor,
ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender},
};
use super::{common::xkb_state::KbdState, ControlFlow, OsError};
use crate::{
error::{EventLoopError, OsError as RootOsError},
event::{Event, StartCause, WindowEvent},
event_loop::{DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
platform::pump_events::PumpStatus,
platform_impl::{
platform::{min_timeout, WindowId},
PlatformSpecificWindowBuilderAttributes,
},
window::WindowAttributes,
};
// Xinput constants not defined in x11rb // Xinput constants not defined in x11rb
const ALL_DEVICES: u16 = 0; const ALL_DEVICES: u16 = 0;
@@ -125,7 +143,7 @@ impl<T> PeekableReceiver<T> {
} }
} }
pub struct EventLoopWindowTarget { pub struct EventLoopWindowTarget<T> {
xconn: Arc<XConnection>, xconn: Arc<XConnection>,
wm_delete_window: xproto::Atom, wm_delete_window: xproto::Atom,
net_wm_ping: xproto::Atom, net_wm_ping: xproto::Atom,
@@ -133,22 +151,24 @@ pub struct EventLoopWindowTarget {
control_flow: Cell<ControlFlow>, control_flow: Cell<ControlFlow>,
exit: Cell<Option<i32>>, exit: Cell<Option<i32>>,
root: xproto::Window, root: xproto::Window,
ime: Option<RefCell<Ime>>, ime: RefCell<Ime>,
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>, windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
redraw_sender: WakeSender<WindowId>, redraw_sender: WakeSender<WindowId>,
activation_sender: WakeSender<ActivationToken>, activation_sender: WakeSender<ActivationToken>,
device_events: Cell<DeviceEvents>, device_events: Cell<DeviceEvents>,
_marker: ::std::marker::PhantomData<T>,
} }
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
loop_running: bool, loop_running: bool,
event_loop: Loop<'static, EventLoopState>, event_loop: Loop<'static, EventLoopState>,
waker: calloop::ping::Ping, waker: calloop::ping::Ping,
event_processor: EventProcessor, event_processor: EventProcessor<T>,
redraw_receiver: PeekableReceiver<WindowId>, redraw_receiver: PeekableReceiver<WindowId>,
user_receiver: PeekableReceiver<T>, user_receiver: PeekableReceiver<T>,
activation_receiver: PeekableReceiver<ActivationToken>, activation_receiver: PeekableReceiver<ActivationToken>,
user_sender: Sender<T>, user_sender: Sender<T>,
target: Rc<RootELW<T>>,
/// The current state of the event loop. /// The current state of the event loop.
state: EventLoopState, state: EventLoopState,
@@ -209,15 +229,13 @@ impl<T: 'static> EventLoop<T> {
setlocale(LC_CTYPE, default_locale); setlocale(LC_CTYPE, default_locale);
} }
} }
let ime = RefCell::new({
let ime = Ime::new(Arc::clone(&xconn), ime_event_sender); let result = Ime::new(Arc::clone(&xconn), ime_event_sender);
if let Err(ImeCreationError::OpenFailure(state)) = ime.as_ref() { if let Err(ImeCreationError::OpenFailure(ref state)) = result {
warn!("Failed to open input method: {state:#?}"); panic!("Failed to open input method: {state:#?}");
} else if let Err(err) = ime.as_ref() { }
warn!("Failed to set input method destruction callback: {err:?}"); result.expect("Failed to set input method destruction callback")
} });
let ime = ime.ok().map(RefCell::new);
let randr_event_offset = xconn let randr_event_offset = xconn
.select_xrandr_input(root) .select_xrandr_input(root)
@@ -290,6 +308,7 @@ impl<T: 'static> EventLoop<T> {
control_flow: Cell::new(ControlFlow::default()), control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None), exit: Cell::new(None),
windows: Default::default(), windows: Default::default(),
_marker: ::std::marker::PhantomData,
ime_sender, ime_sender,
xconn, xconn,
wm_delete_window, wm_delete_window,
@@ -308,13 +327,13 @@ impl<T: 'static> EventLoop<T> {
// Set initial device event filter. // Set initial device event filter.
window_target.update_listen_device_events(true); window_target.update_listen_device_events(true);
let root_window_target = RootELW { let target = Rc::new(RootELW {
p: super::EventLoopWindowTarget::X(window_target), p: super::EventLoopWindowTarget::X(window_target),
_marker: PhantomData, _marker: ::std::marker::PhantomData,
}; });
let event_processor = EventProcessor { let event_processor = EventProcessor {
target: root_window_target, target: target.clone(),
dnd, dnd,
devices: Default::default(), devices: Default::default(),
randr_event_offset, randr_event_offset,
@@ -327,15 +346,13 @@ impl<T: 'static> EventLoop<T> {
held_key_press: None, held_key_press: None,
first_touch: None, first_touch: None,
active_window: None, active_window: None,
modifiers: Default::default(),
is_composing: false, is_composing: false,
}; };
// Register for device hotplug events // Register for device hotplug events
// (The request buffer is flushed during `init_device`) // (The request buffer is flushed during `init_device`)
let xconn = &EventProcessor::window_target(&event_processor.target).xconn; get_xtarget(&target)
.xconn
xconn
.select_xinput_events( .select_xinput_events(
root, root,
ALL_DEVICES, ALL_DEVICES,
@@ -343,12 +360,11 @@ impl<T: 'static> EventLoop<T> {
) )
.expect_then_ignore_error("Failed to register for XInput2 device hotplug events"); .expect_then_ignore_error("Failed to register for XInput2 device hotplug events");
xconn get_xtarget(&target)
.xconn
.select_xkb_events( .select_xkb_events(
0x100, // Use the "core keyboard device" 0x100, // Use the "core keyboard device"
xkb::EventType::NEW_KEYBOARD_NOTIFY xkb::EventType::NEW_KEYBOARD_NOTIFY | xkb::EventType::STATE_NOTIFY,
| xkb::EventType::MAP_NOTIFY
| xkb::EventType::STATE_NOTIFY,
) )
.unwrap(); .unwrap();
@@ -363,6 +379,7 @@ impl<T: 'static> EventLoop<T> {
activation_receiver: PeekableReceiver::from_recv(activation_token_channel), activation_receiver: PeekableReceiver::from_recv(activation_token_channel),
user_receiver: PeekableReceiver::from_recv(user_channel), user_receiver: PeekableReceiver::from_recv(user_channel),
user_sender, user_sender,
target,
state: EventLoopState { state: EventLoopState {
x11_readiness: Readiness::EMPTY, x11_readiness: Readiness::EMPTY,
}, },
@@ -378,14 +395,18 @@ impl<T: 'static> EventLoop<T> {
} }
} }
pub(crate) fn window_target(&self) -> &RootELW { pub(crate) fn window_target(&self) -> &RootELW<T> {
&self.event_processor.target &self.target
} }
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootELW), F: FnMut(Event<T>, &RootELW<T>),
{ {
if self.loop_running {
return Err(EventLoopError::AlreadyRunning);
}
let exit = loop { let exit = loop {
match self.pump_events(None, &mut event_handler) { match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
@@ -404,7 +425,7 @@ impl<T: 'static> EventLoop<T> {
// `run_on_demand` calls but if they have only just dropped their // `run_on_demand` calls but if they have only just dropped their
// windows we need to make sure those last requests are sent to the // windows we need to make sure those last requests are sent to the
// X Server. // X Server.
let wt = EventProcessor::window_target(&self.event_processor.target); let wt = get_xtarget(&self.target);
wt.x_connection().sync_with_server().map_err(|x_err| { wt.x_connection().sync_with_server().map_err(|x_err| {
EventLoopError::Os(os_error!(OsError::XError(Arc::new(X11Error::Xlib(x_err))))) EventLoopError::Os(os_error!(OsError::XError(Arc::new(X11Error::Xlib(x_err)))))
})?; })?;
@@ -414,7 +435,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where where
F: FnMut(Event<T>, &RootELW), F: FnMut(Event<T>, &RootELW<T>),
{ {
if !self.loop_running { if !self.loop_running {
self.loop_running = true; self.loop_running = true;
@@ -447,7 +468,7 @@ impl<T: 'static> EventLoop<T> {
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where where
F: FnMut(Event<T>, &RootELW), F: FnMut(Event<T>, &RootELW<T>),
{ {
let start = Instant::now(); let start = Instant::now();
@@ -525,14 +546,14 @@ impl<T: 'static> EventLoop<T> {
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause) fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
where where
F: FnMut(Event<T>, &RootELW), F: FnMut(Event<T>, &RootELW<T>),
{ {
callback(Event::NewEvents(cause), &self.event_processor.target); callback(crate::event::Event::NewEvents(cause), &self.target);
// NB: For consistency all platforms must emit a 'resumed' event even though X11 // NB: For consistency all platforms must emit a 'resumed' event even though X11
// applications don't themselves have a formal suspend/resume lifecycle. // applications don't themselves have a formal suspend/resume lifecycle.
if cause == StartCause::Init { if cause == StartCause::Init {
callback(Event::Resumed, &self.event_processor.target); callback(crate::event::Event::Resumed, &self.target);
} }
// Process all pending events // Process all pending events
@@ -547,16 +568,16 @@ impl<T: 'static> EventLoop<T> {
}); });
match token { match token {
Some(Ok(token)) => { Some(Ok(token)) => callback(
let event = Event::WindowEvent { crate::event::Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(window_id),
event: WindowEvent::ActivationTokenDone { event: crate::event::WindowEvent::ActivationTokenDone {
serial, serial,
token: crate::window::ActivationToken::_new(token), token: crate::window::ActivationToken::_new(token),
}, },
}; },
callback(event, &self.event_processor.target) &self.target,
} ),
Some(Err(e)) => { Some(Err(e)) => {
log::error!("Failed to get activation token: {}", e); log::error!("Failed to get activation token: {}", e);
} }
@@ -567,7 +588,7 @@ impl<T: 'static> EventLoop<T> {
// Empty the user event buffer // Empty the user event buffer
{ {
while let Ok(event) = self.user_receiver.try_recv() { while let Ok(event) = self.user_receiver.try_recv() {
callback(Event::UserEvent(event), &self.event_processor.target); callback(crate::event::Event::UserEvent(event), &self.target);
} }
} }
@@ -586,59 +607,55 @@ impl<T: 'static> EventLoop<T> {
window_id, window_id,
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}, },
&self.event_processor.target, &self.target,
); );
} }
} }
// This is always the last event we dispatch before poll again // This is always the last event we dispatch before poll again
{ {
callback(Event::AboutToWait, &self.event_processor.target); callback(crate::event::Event::AboutToWait, &self.target);
} }
} }
fn drain_events<F>(&mut self, callback: &mut F) fn drain_events<F>(&mut self, callback: &mut F)
where where
F: FnMut(Event<T>, &RootELW), F: FnMut(Event<T>, &RootELW<T>),
{ {
let target = &self.target;
let mut xev = MaybeUninit::uninit(); let mut xev = MaybeUninit::uninit();
let wt = get_xtarget(&self.target);
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
let mut xev = unsafe { xev.assume_init() }; let mut xev = unsafe { xev.assume_init() };
self.event_processor self.event_processor.process_event(&mut xev, |event| {
.process_event(&mut xev, |window_target, event| { if let Event::WindowEvent {
if let Event::WindowEvent { window_id: crate::window::WindowId(wid),
window_id: crate::window::WindowId(wid), event: WindowEvent::RedrawRequested,
event: WindowEvent::RedrawRequested, } = event
} = event {
{ wt.redraw_sender.send(wid).unwrap();
let window_target = EventProcessor::window_target(window_target); } else {
window_target.redraw_sender.send(wid).unwrap(); callback(event, target);
} else { }
callback(event, window_target); });
}
});
} }
} }
fn control_flow(&self) -> ControlFlow { fn control_flow(&self) -> ControlFlow {
let window_target = EventProcessor::window_target(&self.event_processor.target); self.target.p.control_flow()
window_target.control_flow()
} }
fn exiting(&self) -> bool { fn exiting(&self) -> bool {
let window_target = EventProcessor::window_target(&self.event_processor.target); self.target.p.exiting()
window_target.exiting()
} }
fn set_exit_code(&self, code: i32) { fn set_exit_code(&self, code: i32) {
let window_target = EventProcessor::window_target(&self.event_processor.target); self.target.p.set_exit_code(code)
window_target.set_exit_code(code);
} }
fn exit_code(&self) -> Option<i32> { fn exit_code(&self) -> Option<i32> {
let window_target = EventProcessor::window_target(&self.event_processor.target); self.target.p.exit_code()
window_target.exit_code()
} }
} }
@@ -654,7 +671,15 @@ impl<T> AsRawFd for EventLoop<T> {
} }
} }
impl EventLoopWindowTarget { pub(crate) fn get_xtarget<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> {
match target.p {
super::EventLoopWindowTarget::X(ref target) => target,
#[cfg(wayland_platform)]
_ => unreachable!(),
}
}
impl<T> EventLoopWindowTarget<T> {
/// Returns the `XConnection` of this events loop. /// Returns the `XConnection` of this events loop.
#[inline] #[inline]
pub(crate) fn x_connection(&self) -> &Arc<XConnection> { pub(crate) fn x_connection(&self) -> &Arc<XConnection> {
@@ -727,10 +752,6 @@ impl EventLoopWindowTarget {
self.exit.set(Some(0)) self.exit.set(Some(0))
} }
pub(crate) fn clear_exit(&self) {
self.exit.set(None)
}
pub(crate) fn exiting(&self) -> bool { pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some() self.exit.get().is_some()
} }
@@ -813,11 +834,12 @@ impl Deref for Window {
} }
impl Window { impl Window {
pub(crate) fn new( pub(crate) fn new<T>(
event_loop: &EventLoopWindowTarget, event_loop: &EventLoopWindowTarget<T>,
attribs: WindowAttributes, attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> { ) -> Result<Self, RootOsError> {
let window = Arc::new(UnownedWindow::new(event_loop, attribs)?); let window = Arc::new(UnownedWindow::new(event_loop, attribs, pl_attribs)?);
event_loop event_loop
.windows .windows
.borrow_mut() .borrow_mut()
@@ -869,9 +891,6 @@ pub enum X11Error {
/// Could not find a matching X11 visual for this visualid /// Could not find a matching X11 visual for this visualid
NoSuchVisual(xproto::Visualid), NoSuchVisual(xproto::Visualid),
/// Unable to parse xsettings.
XsettingsParse(xsettings::ParserError),
} }
impl fmt::Display for X11Error { impl fmt::Display for X11Error {
@@ -896,9 +915,6 @@ impl fmt::Display for X11Error {
visualid visualid
) )
} }
X11Error::XsettingsParse(err) => {
write!(f, "Failed to parse xsettings: {:?}", err)
}
} }
} }
} }
@@ -967,12 +983,6 @@ impl From<ReplyOrIdError> for X11Error {
} }
} }
impl From<xsettings::ParserError> for X11Error {
fn from(value: xsettings::ParserError) -> Self {
Self::XsettingsParse(value)
}
}
/// The underlying x11rb connection that we are using. /// The underlying x11rb connection that we are using.
type X11rbConnection = x11rb::xcb_ffi::XCBConnection; type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
@@ -1027,7 +1037,7 @@ fn mkdid(w: xinput::DeviceId) -> crate::event::DeviceId {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Device { struct Device {
_name: String, _name: String,
scroll_axes: Vec<(i32, ScrollAxis)>, scroll_axes: Vec<(i32, ScrollAxis)>,
// For master devices, this is the paired device (pointer <-> keyboard). // For master devices, this is the paired device (pointer <-> keyboard).

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