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
244 changed files with 8401 additions and 10374 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

@@ -35,7 +35,7 @@ jobs:
- { name: 'Linux 64bit', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, } - { name: 'Linux 64bit', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
- { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' } - { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' } - { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--features=android-native-activity', cmd: 'apk --' } - { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
- { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, } - { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, }
- { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, } - { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, }
- { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, } - { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, }
@@ -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 }}
@@ -117,21 +109,19 @@ 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 -p winit $OPTIONS run: cargo $CMD build $OPTIONS
- name: Build tests - name: Build tests
if: > if: >
!contains(matrix.platform.target, 'redox') && !contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.70.0' matrix.toolchain != '1.70.0'
run: cargo $CMD test -p winit --no-run $OPTIONS run: cargo $CMD test --no-run $OPTIONS
- name: Run tests - name: Run tests
if: > if: >
@@ -150,7 +140,7 @@ jobs:
if: > if: >
!contains(matrix.platform.target, 'redox') && !contains(matrix.platform.target, 'redox') &&
matrix.toolchain != '1.70.0' matrix.toolchain != '1.70.0'
run: cargo $CMD test --no-run $OPTIONS -p winit --features serde run: cargo $CMD test --no-run $OPTIONS --features serde
- name: Run tests with serde enabled - name: Run tests with serde enabled
if: > if: >
@@ -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,84 +11,18 @@ Unreleased` header.
# Unreleased # Unreleased
- 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.
- **Breaking:** Move some types to the `winit-core` crate. Most types are
re-exported verbatim; however:
- `Event`, `WindowEvent` and `KeyEvent` now take a generic that `winit`
implements.
- `ActivationToken` and `InnerSizeWriter` expose new methods to make them
useful.
- `InnerSizeWriter::request_inner_size` returns an `InnerSizeIgnored` error
instead of an `ExternalError`.
# 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
@@ -113,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,16 +1,255 @@
[package]
name = "winit"
version = "0.29.5"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
edition = "2021"
keywords = ["windowing"]
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/rust-windowing/winit"
documentation = "https://docs.rs/winit"
categories = ["gui"]
rust-version = "1.70.0"
[package.metadata.docs.rs]
features = [
"rwh_04",
"rwh_05",
"rwh_06",
"serde",
# Enabled to get docs to compile
"android-native-activity",
]
default-target = "x86_64-unknown-linux-gnu"
# These are all tested in CI
targets = [
# Windows
"i686-pc-windows-msvc",
"x86_64-pc-windows-msvc",
# macOS
"x86_64-apple-darwin",
# Unix (X11 & Wayland)
"i686-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
# iOS
"x86_64-apple-ios",
# Android
"aarch64-linux-android",
# WebAssembly
"wasm32-unknown-unknown",
]
rustdoc-args = ["--cfg", "docsrs"]
[features]
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"]
wayland-dlopen = ["wayland-backend/dlopen"]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
android-native-activity = ["android-activity/native-activity"]
android-game-activity = ["android-activity/game-activity"]
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"]
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
[build-dependencies]
cfg_aliases = "0.1.1"
[dependencies]
bitflags = "2"
cursor-icon = "1.1.0"
log = "0.4"
mint = { version = "0.5.6", optional = true }
once_cell = "1.12"
rwh_04 = { package = "raw-window-handle", version = "0.4", 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 }
serde = { version = "1", optional = true, features = ["serde_derive"] }
smol_str = "0.2.0"
[dev-dependencies]
image = { version = "0.24.0", default-features = false, features = ["png"] }
simple_logger = { version = "4.2.0", default_features = false }
winit = { path = ".", features = ["rwh_05"] }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
softbuffer = "0.3.0"
[target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.5.0"
ndk = { version = "0.8.0", default-features = false }
ndk-sys = "0.5.0"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
core-foundation = "0.9.3"
objc2 = "0.5.0"
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.23.1"
[target.'cfg(target_os = "macos")'.dependencies.icrate]
version = "0.1.0"
features = [
"dispatch",
"Foundation",
"Foundation_NSArray",
"Foundation_NSAttributedString",
"Foundation_NSMutableAttributedString",
"Foundation_NSData",
"Foundation_NSDictionary",
"Foundation_NSString",
"Foundation_NSProcessInfo",
"Foundation_NSThread",
"Foundation_NSNumber",
"AppKit",
"AppKit_NSAppearance",
"AppKit_NSApplication",
"AppKit_NSBitmapImageRep",
"AppKit_NSButton",
"AppKit_NSColor",
"AppKit_NSControl",
"AppKit_NSCursor",
"AppKit_NSEvent",
"AppKit_NSGraphicsContext",
"AppKit_NSImage",
"AppKit_NSImageRep",
"AppKit_NSMenu",
"AppKit_NSMenuItem",
"AppKit_NSPasteboard",
"AppKit_NSResponder",
"AppKit_NSScreen",
"AppKit_NSTextInputContext",
"AppKit_NSView",
"AppKit_NSWindow",
"AppKit_NSWindowTabGroup",
]
[target.'cfg(target_os = "ios")'.dependencies.icrate]
version = "0.1.0"
features = [
"dispatch",
"Foundation",
"Foundation_NSArray",
"Foundation_NSString",
"Foundation_NSProcessInfo",
"Foundation_NSThread",
"Foundation_NSSet",
]
[target.'cfg(target_os = "windows")'.dependencies]
unicode-segmentation = "1.7.1"
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.48"
features = [
"Win32_Devices_HumanInterfaceDevice",
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_Media",
"Win32_System_Com_StructuredStorage",
"Win32_System_Com",
"Win32_System_LibraryLoader",
"Win32_System_Ole",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_System_WindowsProgramming",
"Win32_UI_Accessibility",
"Win32_UI_Controls",
"Win32_UI_HiDpi",
"Win32_UI_Input_Ime",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Input_Pointer",
"Win32_UI_Input_Touch",
"Win32_UI_Shell",
"Win32_UI_TextServices",
"Win32_UI_WindowsAndMessaging",
]
[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.3", features = ["no-rng"], optional = true }
bytemuck = { version = "1.13.1", default-features = false, optional = true }
calloop = "0.12.3"
libc = "0.2.64"
memmap2 = { version = "0.9.0", optional = true }
percent-encoding = { version = "2.0", optional = true }
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
sctk-adwaita = { version = "0.8.0", default_features = false, optional = true }
wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
wayland-client = { version = "0.31.1", optional = true }
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
x11-dl = { version = "2.18.5", optional = true }
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
xkbcommon-dl = "0.4.0"
[target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.42", default-features = false }
redox_syscall = "0.3"
[target.'cfg(target_family = "wasm")'.dependencies.web_sys]
package = "web-sys"
version = "0.3.64"
features = [
'AbortController',
'AbortSignal',
'Blob',
'console',
'CssStyleDeclaration',
'Document',
'DomRect',
'DomRectReadOnly',
'Element',
'Event',
'EventTarget',
'FocusEvent',
'HtmlCanvasElement',
'HtmlElement',
'HtmlImageElement',
'ImageBitmap',
'ImageBitmapOptions',
'ImageBitmapRenderingContext',
'ImageData',
'IntersectionObserver',
'IntersectionObserverEntry',
'KeyboardEvent',
'MediaQueryList',
'MessageChannel',
'MessagePort',
'Node',
'PageTransitionEvent',
'PointerEvent',
'PremultiplyAlpha',
'ResizeObserver',
'ResizeObserverBoxOptions',
'ResizeObserverEntry',
'ResizeObserverOptions',
'ResizeObserverSize',
'VisibilityState',
'Window',
'WheelEvent',
'Url',
]
[target.'cfg(target_family = "wasm")'.dependencies]
atomic-waker = "1"
js-sys = "0.3.64"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-time = "0.2"
[target.'cfg(target_family = "wasm")'.dev-dependencies]
console_log = "1"
web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
[workspace] [workspace]
members = [ members = [
"run-wasm", "run-wasm",
"winit",
"winit-core"
] ]
resolver = "2"
[workspace.dependencies]
bitflags = "2"
cfg_aliases = "0.2.0"
cursor-icon = "1.1.0"
serde = { version = "1", features = ["serde_derive"] }
smol_str = "0.2.0"
web-time = "1"
winit-core = { path = "./winit-core", default-features = false, features = ["std"] }

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 14. 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,10 +34,9 @@ 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 = []
{ name = "run-wasm", version = "0.1.0" }
]
[licenses] [licenses]

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

@@ -0,0 +1,94 @@
#![allow(clippy::single_match, clippy::disallowed_methods)]
#[cfg(not(wasm_platform))]
use simple_logger::SimpleLogger;
use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget},
keyboard::Key,
window::{CustomCursor, WindowBuilder},
};
fn decode_cursor<T>(bytes: &[u8], window_target: &EventLoopWindowTarget<T>) -> CustomCursor {
let img = image::load_from_memory(bytes).unwrap().to_rgba8();
let samples = img.into_flat_samples();
let (_, w, h) = samples.extents();
let (w, h) = (w as u16, h as u16);
let builder = CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap();
builder.build(window_target)
}
#[cfg(not(wasm_platform))]
#[path = "util/fill.rs"]
mod fill;
fn main() -> Result<(), impl std::error::Error> {
#[cfg(not(wasm_platform))]
SimpleLogger::new()
.with_level(log::LevelFilter::Info)
.init()
.unwrap();
#[cfg(wasm_platform)]
console_log::init_with_level(log::Level::Debug).unwrap();
let event_loop = EventLoop::new().unwrap();
let builder = WindowBuilder::new().with_title("A fantastic window!");
#[cfg(wasm_platform)]
let builder = {
use winit::platform::web::WindowBuilderExtWebSys;
builder.with_append(true)
};
let window = builder.build(&event_loop).unwrap();
let mut cursor_idx = 0;
let mut cursor_visible = true;
let custom_cursors = [
decode_cursor(include_bytes!("data/cross.png"), &event_loop),
decode_cursor(include_bytes!("data/cross2.png"), &event_loop),
];
event_loop.run(move |event, _elwt| match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
logical_key: key,
..
},
..
} => match key.as_ref() {
Key::Character("1") => {
log::debug!("Setting cursor to {:?}", cursor_idx);
window.set_custom_cursor(&custom_cursors[cursor_idx]);
cursor_idx = (cursor_idx + 1) % 2;
}
Key::Character("2") => {
log::debug!("Setting cursor icon to default");
window.set_cursor_icon(Default::default());
}
Key::Character("3") => {
cursor_visible = !cursor_visible;
log::debug!("Setting cursor visibility to {:?}", cursor_visible);
window.set_cursor_visible(cursor_visible);
}
_ => {}
},
WindowEvent::RedrawRequested => {
#[cfg(not(wasm_platform))]
fill::fill_window(&window);
}
WindowEvent::CloseRequested => {
#[cfg(not(wasm_platform))]
_elwt.exit();
}
_ => (),
},
Event::AboutToWait => {
window.request_redraw();
}
_ => {}
})
}

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.");
} }

View File

Before

Width:  |  Height:  |  Size: 159 B

After

Width:  |  Height:  |  Size: 159 B

View File

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 129 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

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

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)

89
examples/util/fill.rs Normal file
View File

@@ -0,0 +1,89 @@
//! Fill the window buffer with a solid color.
//!
//! Launching a window without drawing to it has unpredictable results varying from platform to
//! platform. In order to have well-defined examples, this module provides an easy way to
//! fill the window buffer with a solid color.
//!
//! 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.
use winit::window::Window;
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
pub(super) fn fill_window(window: &Window) {
use softbuffer::{Context, Surface};
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::ManuallyDrop;
use std::num::NonZeroU32;
use winit::window::WindowId;
/// The graphics context used to draw to a window.
struct GraphicsContext {
/// The global softbuffer context.
context: Context,
/// The hash map of window IDs to surfaces.
surfaces: HashMap<WindowId, Surface>,
}
impl GraphicsContext {
fn new(w: &Window) -> Self {
Self {
context: unsafe { Context::new(w) }.expect("Failed to create a softbuffer context"),
surfaces: HashMap::new(),
}
}
fn surface(&mut self, w: &Window) -> &mut Surface {
self.surfaces.entry(w.id()).or_insert_with(|| {
unsafe { Surface::new(&self.context, w) }
.expect("Failed to create a softbuffer surface")
})
}
}
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));
}
GC.with(|gc| {
let size = window.inner_size();
let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
else {
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")))))]
pub(super) fn fill_window(_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

@@ -2,7 +2,8 @@
name = "run-wasm" name = "run-wasm"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
cargo-run-wasm = "0.2.0" cargo-run-wasm = "0.2.0"

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

@@ -97,16 +97,13 @@
//! [points]: https://en.wikipedia.org/wiki/Point_(typography) //! [points]: https://en.wikipedia.org/wiki/Point_(typography)
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography) //! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
//! [`ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged //! [`ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged
//! [`window.scale_factor()`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.scale_factor //! [`window.scale_factor()`]: crate::window::Window::scale_factor
//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows //! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html //! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html
//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/ //! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/
//! [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

@@ -1,8 +1,6 @@
use crate::platform_impl;
use std::{error, fmt}; use std::{error, fmt};
#[doc(inline)] use crate::platform_impl;
pub use winit_core::error::NotSupportedError;
// TODO: Rename // TODO: Rename
/// An error that may be generated when requesting Winit state /// An error that may be generated when requesting Winit state
@@ -16,10 +14,10 @@ pub enum ExternalError {
Os(OsError), Os(OsError),
} }
impl From<winit_core::event::InnerSizeIgnored> for ExternalError { /// The error type for when the requested operation is not supported by the backend.
fn from(_: winit_core::event::InnerSizeIgnored) -> Self { #[derive(Clone)]
Self::Ignored pub struct NotSupportedError {
} _marker: (),
} }
/// The error type for when the OS cannot perform the requested operation. /// The error type for when the OS cannot perform the requested operation.
@@ -37,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.
@@ -49,6 +49,14 @@ impl From<OsError> for EventLoopError {
} }
} }
impl NotSupportedError {
#[inline]
#[allow(dead_code)]
pub(crate) fn new() -> NotSupportedError {
NotSupportedError { _marker: () }
}
}
impl OsError { impl OsError {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError { pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError {
@@ -82,9 +90,22 @@ impl fmt::Display for ExternalError {
} }
} }
impl fmt::Debug for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("NotSupportedError").finish()
}
}
impl fmt::Display for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.pad("the requested operation is not supported by Winit")
}
}
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),
@@ -95,6 +116,7 @@ impl fmt::Display for EventLoopError {
impl error::Error for OsError {} impl error::Error for OsError {}
impl error::Error for ExternalError {} impl error::Error for ExternalError {}
impl error::Error for NotSupportedError {}
impl error::Error for EventLoopError {} impl error::Error for EventLoopError {}
#[cfg(test)] #[cfg(test)]
@@ -106,6 +128,11 @@ mod tests {
// Eat attributes for testing // Eat attributes for testing
#[test] #[test]
fn ensure_fmt_does_not_panic() { fn ensure_fmt_does_not_panic() {
let _ = format!(
"{:?}, {}",
NotSupportedError::new(),
NotSupportedError::new().clone()
);
let _ = format!( let _ = format!(
"{:?}, {}", "{:?}, {}",
ExternalError::NotSupported(NotSupportedError::new()), ExternalError::NotSupported(NotSupportedError::new()),

View File

@@ -1,64 +1,62 @@
//! Incoming notifications from the GUI system. //! The [`Event`] enum and assorted supporting types.
//!
use crate::dpi::{PhysicalPosition, PhysicalSize}; //! These are sent to the closure given to [`EventLoop::run(...)`], where they get
use crate::event_loop::AsyncRequestSerial; //! processed and used to modify the program state. For more details, see the root-level documentation.
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState}; //!
use crate::window::{ActivationToken, Theme, WindowId}; //! Some of these events represent different "parts" of a traditional event-handling loop. You could
//! approximate the basic ordering loop of [`EventLoop::run(...)`] like this:
use std::fmt; //!
//! ```rust,ignore
//! let mut start_cause = StartCause::Init;
//!
//! while !elwt.exiting() {
//! event_handler(NewEvents(start_cause), elwt);
//!
//! for e in (window events, user events, device events) {
//! event_handler(e, elwt);
//! }
//!
//! for w in (redraw windows) {
//! event_handler(RedrawRequested(w), elwt);
//! }
//!
//! event_handler(AboutToWait, elwt);
//! start_cause = wait_if_necessary();
//! }
//!
//! event_handler(LoopExiting, elwt);
//! ```
//!
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
//! describes what happens in what order.
//!
//! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run
//! [`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(wasm_platform))]
#[cfg(not(web_platform))]
use std::time::Instant; use std::time::Instant;
#[cfg(web_platform)]
use web_time::Instant;
#[cfg(feature = "serde")]
pub use serde::{Deserialize, Serialize};
use smol_str::SmolStr; use smol_str::SmolStr;
#[cfg(wasm_platform)]
use web_time::Instant;
/// Identifier of an input device. use crate::error::ExternalError;
/// #[cfg(doc)]
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which use crate::window::Window;
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or use crate::{
/// physical. Virtual devices typically aggregate inputs from multiple physical devices. dpi::{PhysicalPosition, PhysicalSize},
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] event_loop::AsyncRequestSerial,
pub struct DeviceId(u64); keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState},
platform_impl,
impl DeviceId { window::{ActivationToken, Theme, WindowId},
/// Returns a dummy id, useful for unit testing. };
///
/// # Safety
///
/// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `DeviceId`.
///
/// **Passing this into a winit function will result in undefined behavior.**
pub const unsafe fn dummy() -> Self {
DeviceId(0)
}
}
impl From<u64> for DeviceId {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<DeviceId> for u64 {
fn from(value: DeviceId) -> Self {
value.0
}
}
/// Describes a generic event. /// Describes a generic event.
/// ///
/// See the module-level docs for more information on the event loop manages each event. /// See the module-level docs for more information on the event loop manages each event.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Event<T: 'static, KeyExtra> { pub enum Event<T: 'static> {
/// Emitted when new events arrive from the OS to be processed. /// Emitted when new events arrive from the OS to be processed.
/// ///
/// This event type is useful as a place to put code that should be done before you start /// This event type is useful as a place to put code that should be done before you start
@@ -70,7 +68,7 @@ pub enum Event<T: 'static, KeyExtra> {
/// Emitted when the OS sends an event to a winit window. /// Emitted when the OS sends an event to a winit window.
WindowEvent { WindowEvent {
window_id: WindowId, window_id: WindowId,
event: WindowEvent<KeyExtra>, event: WindowEvent,
}, },
/// Emitted when the OS sends an event to a device. /// Emitted when the OS sends an event to a device.
@@ -79,7 +77,7 @@ pub enum Event<T: 'static, KeyExtra> {
event: DeviceEvent, event: DeviceEvent,
}, },
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](https://docs.rs/winit/latest/winit/event_loop/struct.EventLoopProxy.html#method.send_event) /// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event)
UserEvent(T), UserEvent(T),
/// Emitted when the application has been suspended. /// Emitted when the application has been suspended.
@@ -255,9 +253,9 @@ pub enum Event<T: 'static, KeyExtra> {
MemoryWarning, MemoryWarning,
} }
impl<T, Extra> Event<T, Extra> { impl<T> Event<T> {
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
pub fn map_nonuser_event<U>(self) -> Result<Event<U, Extra>, Event<T, Extra>> { pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
use self::Event::*; use self::Event::*;
match self { match self {
UserEvent(_) => Err(self), UserEvent(_) => Err(self),
@@ -304,10 +302,8 @@ pub enum StartCause {
} }
/// Describes an event from a [`Window`]. /// Describes an event from a [`Window`].
///
/// [`Window`]: https://docs.rs/winit/latest/winit/window/struct.Window.html
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum WindowEvent<KeyExtra> { pub enum WindowEvent {
/// The activation token was delivered back and now could be used. /// The activation token was delivered back and now could be used.
/// ///
#[cfg_attr( #[cfg_attr(
@@ -369,7 +365,7 @@ pub enum WindowEvent<KeyExtra> {
/// events which are not marked as `is_synthetic`. /// events which are not marked as `is_synthetic`.
KeyboardInput { KeyboardInput {
device_id: DeviceId, device_id: DeviceId,
event: KeyEvent<KeyExtra>, event: KeyEvent,
/// If `true`, the event was generated synthetically by winit /// If `true`, the event was generated synthetically by winit
/// in one of the following circumstances: /// in one of the following circumstances:
@@ -393,8 +389,6 @@ pub enum WindowEvent<KeyExtra> {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web / Orbital:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
///
/// [`Window::set_ime_allowed`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_allowed
Ime(Ime), Ime(Ime),
/// The cursor has moved on the window. /// The cursor has moved on the window.
@@ -451,23 +445,21 @@ pub enum WindowEvent<KeyExtra> {
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
@@ -483,20 +475,18 @@ pub enum WindowEvent<KeyExtra> {
/// ///
/// ## 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,
@@ -584,7 +574,7 @@ pub enum WindowEvent<KeyExtra> {
/// ### 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
@@ -600,11 +590,33 @@ pub enum WindowEvent<KeyExtra> {
/// ///
/// Winit will aggregate duplicate redraw requests into a single event, to /// Winit will aggregate duplicate redraw requests into a single event, to
/// help avoid duplicating rendering work. /// help avoid duplicating rendering work.
///
/// [`Window::request_redraw`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.request_redraw
RedrawRequested, RedrawRequested,
} }
/// Identifier of an input device.
///
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
/// physical. Virtual devices typically aggregate inputs from multiple physical devices.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(pub(crate) platform_impl::DeviceId);
impl DeviceId {
/// Returns a dummy id, useful for unit testing.
///
/// # Safety
///
/// The only guarantee made about the return value of this function is that
/// it will always be equal to itself and to future values returned by this function.
/// No other guarantees are made. This may be equal to a real `DeviceId`.
///
/// **Passing this into a winit function will result in undefined behavior.**
pub const unsafe fn dummy() -> Self {
#[allow(unused_unsafe)]
DeviceId(unsafe { platform_impl::DeviceId::dummy() })
}
}
/// Represents raw hardware events that are not associated with any particular window. /// Represents raw hardware events that are not associated with any particular window.
/// ///
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person /// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person
@@ -665,7 +677,7 @@ pub struct RawKeyEvent {
/// Describes a keyboard input targeting a window. /// Describes a keyboard input targeting a window.
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeyEvent<Extra> { pub struct KeyEvent {
/// Represents the position of a key independent of the currently active layout. /// Represents the position of a key independent of the currently active layout.
/// ///
/// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode).
@@ -715,7 +727,7 @@ pub struct KeyEvent<Extra> {
/// - **Web:** Dead keys might be reported as the real key instead /// - **Web:** Dead keys might be reported as the real key instead
/// of `Dead` depending on the browser/OS. /// of `Dead` depending on the browser/OS.
/// ///
/// [`key_without_modifiers`]: https://docs.rs/winit/latest/winit/platform/modifier_supplement/trait.KeyEventExtModifierSupplement.html#tymethod.key_without_modifiers /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
pub logical_key: keyboard::Key, pub logical_key: keyboard::Key,
/// Contains the text produced by this keypress. /// Contains the text produced by this keypress.
@@ -772,7 +784,7 @@ pub struct KeyEvent<Extra> {
/// modifiers applied. /// modifiers applied.
/// ///
/// On Android, iOS, Redox and Web, this type is a no-op. /// On Android, iOS, Redox and Web, this type is a no-op.
pub extra: Extra, pub(crate) platform_specific: platform_impl::KeyEventExtra,
} }
/// Describes keyboard modifiers event. /// Describes keyboard modifiers event.
@@ -787,15 +799,6 @@ pub struct Modifiers {
} }
impl Modifiers { impl Modifiers {
/// Only `winit` should instantiate this!
#[doc(hidden)]
pub fn new(state: ModifiersState, pressed_mods: ModifiersKeys) -> Self {
Self {
state,
pressed_mods,
}
}
/// The state of the modifiers. /// The state of the modifiers.
pub fn state(&self) -> ModifiersState { pub fn state(&self) -> ModifiersState {
self.state self.state
@@ -895,8 +898,6 @@ impl From<ModifiersState> for Modifiers {
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. /// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
/// Ime::Commit("啊不") /// Ime::Commit("啊不")
/// ``` /// ```
///
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Ime { pub enum Ime {
@@ -905,8 +906,6 @@ pub enum Ime {
/// After getting this event you could receive [`Preedit`](Self::Preedit) and /// After getting this event you could receive [`Preedit`](Self::Preedit) and
/// [`Commit`](Self::Commit) events. You should also start performing IME related requests /// [`Commit`](Self::Commit) events. You should also start performing IME related requests
/// like [`Window::set_ime_cursor_area`]. /// like [`Window::set_ime_cursor_area`].
///
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
Enabled, Enabled,
/// Notifies when a new composing text should be set at the cursor position. /// Notifies when a new composing text should be set at the cursor position.
@@ -929,8 +928,6 @@ pub enum Ime {
/// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You should /// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You should
/// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear pending /// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear pending
/// preedit text. /// preedit text.
///
/// [`Window::set_ime_cursor_area`]: https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_cursor_area
Disabled, Disabled,
} }
@@ -1119,7 +1116,7 @@ pub struct InnerSizeWriter {
impl InnerSizeWriter { impl InnerSizeWriter {
#[cfg(not(orbital_platform))] #[cfg(not(orbital_platform))]
pub fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self { pub(crate) fn new(new_inner_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
Self { new_inner_size } Self { new_inner_size }
} }
@@ -1127,19 +1124,14 @@ impl InnerSizeWriter {
pub fn request_inner_size( pub fn request_inner_size(
&mut self, &mut self,
new_inner_size: PhysicalSize<u32>, new_inner_size: PhysicalSize<u32>,
) -> Result<(), InnerSizeIgnored> { ) -> Result<(), ExternalError> {
if let Some(inner) = self.new_inner_size.upgrade() { if let Some(inner) = self.new_inner_size.upgrade() {
*inner.lock().unwrap() = new_inner_size; *inner.lock().unwrap() = new_inner_size;
Ok(()) Ok(())
} else { } else {
Err(InnerSizeIgnored { _private: () }) Err(ExternalError::Ignored)
} }
} }
/// Get the underlying size.
pub fn get(&self) -> PhysicalSize<u32> {
*self.new_inner_size.upgrade().unwrap().lock().unwrap()
}
} }
impl PartialEq for InnerSizeWriter { impl PartialEq for InnerSizeWriter {
@@ -1148,25 +1140,6 @@ impl PartialEq for InnerSizeWriter {
} }
} }
/// Could not write the inner size.
pub struct InnerSizeIgnored {
_private: (),
}
impl fmt::Debug for InnerSizeIgnored {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InnerSizeIgnored").finish_non_exhaustive()
}
}
impl fmt::Display for InnerSizeIgnored {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("tried to write inner size after")
}
}
impl std::error::Error for InnerSizeIgnored {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::event; use crate::event;
@@ -1226,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,
@@ -1292,7 +1265,7 @@ mod tests {
#[allow(clippy::redundant_clone)] #[allow(clippy::redundant_clone)]
#[test] #[test]
fn test_event_clone() { fn test_event_clone() {
foreach_event!(|event: event::Event<(), ()>| { foreach_event!(|event: event::Event<()>| {
let event2 = event.clone(); let event2 = event.clone();
assert_eq!(event, event2); assert_eq!(event, event2);
}) })
@@ -1300,7 +1273,7 @@ mod tests {
#[test] #[test]
fn test_map_nonuser_event() { fn test_map_nonuser_event() {
foreach_event!(|event: event::Event<(), ()>| { foreach_event!(|event: event::Event<()>| {
let is_user = matches!(event, event::Event::UserEvent(())); let is_user = matches!(event, event::Event::UserEvent(()));
let event2 = event.map_nonuser_event::<()>(); let event2 = event.map_nonuser_event::<()>();
if is_user { if is_user {
@@ -1334,7 +1307,7 @@ mod tests {
#[allow(clippy::clone_on_copy)] #[allow(clippy::clone_on_copy)]
#[test] #[test]
fn ensure_attrs_do_not_panic() { fn ensure_attrs_do_not_panic() {
foreach_event!(|event: event::Event<(), ()>| { foreach_event!(|event: event::Event<()>| {
let _ = format!("{:?}", event); let _ = format!("{:?}", event);
}); });
let _ = event::StartCause::Init.clone(); let _ = event::StartCause::Init.clone();

View File

@@ -11,15 +11,17 @@ 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, Ordering}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::{error, fmt}; use std::{error, fmt};
#[cfg(not(wasm_platform))]
use std::time::{Duration, Instant};
#[cfg(wasm_platform)]
use web_time::{Duration, Instant};
use crate::error::EventLoopError; use crate::error::EventLoopError;
use crate::{event::Event, monitor::MonitorHandle, platform_impl}; use crate::{event::Event, monitor::MonitorHandle, platform_impl};
#[doc(inline)]
pub use winit_core::event_loop::{AsyncRequestSerial, ControlFlow};
/// Provides a way to retrieve events from the system and from the windows that were registered to /// Provides a way to retrieve events from the system and from the windows that were registered to
/// the events loop. /// the events loop.
/// ///
@@ -46,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
} }
@@ -55,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,
@@ -121,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);
} }
@@ -133,45 +142,78 @@ 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 { .. }")
} }
} }
impl EventLoop<()> { /// Set through [`EventLoopWindowTarget::set_control_flow()`].
/// Create the event loop. ///
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
///
/// Defaults to [`Wait`].
///
/// [`Wait`]: Self::Wait
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum ControlFlow {
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process.
Poll,
/// When the current loop iteration finishes, suspend the thread until another event arrives.
#[default]
Wait,
/// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the given time is reached.
/// ///
/// This is an alias of `EventLoop::builder().build()`. /// Useful for implementing efficient timers. Applications which want to render at the display's
/// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
/// to reduce odds of missed frames.
///
/// [`Poll`]: Self::Poll
WaitUntil(Instant),
}
impl ControlFlow {
/// Creates a [`ControlFlow`] that waits until a timeout has expired.
///
/// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
/// instead set to [`Wait`].
///
/// [`WaitUntil`]: Self::WaitUntil
/// [`Wait`]: Self::Wait
pub fn wait_duration(timeout: Duration) -> Self {
match Instant::now().checked_add(timeout) {
Some(instant) => Self::WaitUntil(instant),
None => Self::Wait,
}
}
}
impl EventLoop<()> {
/// Alias for [`EventLoopBuilder::new().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
@@ -184,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
@@ -197,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)
} }
@@ -259,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> {
@@ -328,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.
@@ -349,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>,
@@ -460,3 +447,29 @@ pub enum DeviceEvents {
/// Never capture device events. /// Never capture device events.
Never, Never,
} }
/// A unique identifier of the winit's async request.
///
/// This could be used to identify the async request once it's done
/// and a specific action must be taken.
///
/// One of the handling scenarious could be to maintain a working list
/// containing [`AsyncRequestSerial`] and some closure associated with it.
/// Then once event is arriving the working list is being traversed and a job
/// executed and removed from the list.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AsyncRequestSerial {
serial: u64,
}
impl AsyncRequestSerial {
// TODO(kchibisov) remove `cfg` when the clipboard will be added.
#[allow(dead_code)]
pub(crate) fn get() -> Self {
static CURRENT_SERIAL: AtomicU64 = AtomicU64::new(0);
// NOTE: we rely on wrap around here, while the user may just request
// in the loop u64::MAX times that's issue is considered on them.
let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
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
@@ -1454,29 +1451,6 @@ pub enum NamedKey {
F35, F35,
} }
// NOTE: the exact modifier key is not used to represent modifiers state in the
// first place due to a fact that modifiers state could be changed without any
// key being pressed and on some platforms like Wayland/X11 which key resulted
// in modifiers change is hidden, also, not that it really matters.
//
// The reason this API is even exposed is mostly to provide a way for users
// to treat modifiers differently based on their position, which is required
// on macOS due to their AltGr/Option situation.
bitflags! {
#[doc(hidden)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModifiersKeys: u8 {
const LSHIFT = 0b0000_0001;
const RSHIFT = 0b0000_0010;
const LCONTROL = 0b0000_0100;
const RCONTROL = 0b0000_1000;
const LALT = 0b0001_0000;
const RALT = 0b0010_0000;
const LSUPER = 0b0100_0000;
const RSUPER = 0b1000_0000;
}
}
/// Key represents the meaning of a keypress. /// Key represents the meaning of a keypress.
/// ///
/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with /// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with
@@ -1588,7 +1562,7 @@ impl NamedKey {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use winit_core::keyboard::NamedKey; /// use winit::keyboard::NamedKey;
/// ///
/// assert_eq!(NamedKey::Enter.to_text(), Some("\r")); /// assert_eq!(NamedKey::Enter.to_text(), Some("\r"));
/// assert_eq!(NamedKey::F20.to_text(), None); /// assert_eq!(NamedKey::F20.to_text(), None);
@@ -1611,7 +1585,7 @@ impl Key {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use winit_core::keyboard::{NamedKey, Key}; /// use winit::keyboard::{NamedKey, Key};
/// ///
/// assert_eq!(Key::Character("a".into()).to_text(), Some("a")); /// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
/// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r")); /// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
@@ -1750,6 +1724,28 @@ pub enum ModifiersKeyState {
Unknown, Unknown,
} }
// NOTE: the exact modifier key is not used to represent modifiers state in the
// first place due to a fact that modifiers state could be changed without any
// key being pressed and on some platforms like Wayland/X11 which key resulted
// in modifiers change is hidden, also, not that it really matters.
//
// The reason this API is even exposed is mostly to provide a way for users
// to treat modifiers differently based on their position, which is required
// on macOS due to their AltGr/Option situation.
bitflags! {
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct ModifiersKeys: u8 {
const LSHIFT = 0b0000_0001;
const RSHIFT = 0b0000_0010;
const LCONTROL = 0b0000_0100;
const RCONTROL = 0b0000_1000;
const LALT = 0b0001_0000;
const RALT = 0b0010_0000;
const LSUPER = 0b0100_0000;
const RSUPER = 0b1000_0000;
}
}
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
mod modifiers_serde { mod modifiers_serde {
use super::ModifiersState; use super::ModifiersState;

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,27 +154,46 @@
#![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;
#[doc(inline)] #[allow(unused_imports)]
pub use winit_core::{dpi, keyboard}; #[macro_use]
extern crate log;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[macro_use]
extern crate bitflags;
pub mod dpi;
#[macro_use] #[macro_use]
pub mod error; pub mod error;
mod cursor; mod cursor;
pub mod event; pub mod event;
pub mod event_loop; pub mod event_loop;
mod icon; mod icon;
pub mod keyboard;
pub mod monitor; pub mod monitor;
mod platform_impl; 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()
} }

57
src/platform/mod.rs Normal file
View File

@@ -0,0 +1,57 @@
//! Contains traits with platform-specific methods in them.
//!
//! 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(android_platform)]
pub mod android;
#[cfg(ios_platform)]
pub mod ios;
#[cfg(macos_platform)]
pub mod macos;
#[cfg(orbital_platform)]
pub mod orbital;
#[cfg(any(x11_platform, wayland_platform))]
pub mod startup_notify;
#[cfg(wayland_platform)]
pub mod wayland;
#[cfg(wasm_platform)]
pub mod web;
#[cfg(windows_platform)]
pub mod windows;
#[cfg(x11_platform)]
pub mod x11;
#[cfg(any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
))]
pub mod run_on_demand;
#[cfg(any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform
))]
pub mod pump_events;
pub mod modifier_supplement;
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,19 +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.extra
.extra
.text_with_all_modifiers
.as_ref()
.map(|s| s.as_str())
}
#[inline]
fn key_without_modifiers(&self) -> Key {
self.extra.extra.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)]
@@ -64,7 +64,7 @@ impl EventLoopExtStartupNotify for EventLoopWindowTarget {
crate::platform_impl::EventLoopWindowTarget::X(_) => env::var(X11_VAR), crate::platform_impl::EventLoopWindowTarget::X(_) => env::var(X11_VAR),
} }
.ok() .ok()
.map(ActivationToken::new) .map(ActivationToken::_new)
} }
} }
@@ -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
} }
} }
@@ -94,6 +94,6 @@ pub fn reset_activation_token_env() {
/// ///
/// This could be used before running daemon processes. /// This could be used before running daemon processes.
pub fn set_activation_token_env(token: ActivationToken) { pub fn set_activation_token_env(token: ActivationToken) {
env::set_var(X11_VAR, token.token()); env::set_var(X11_VAR, &token._token);
env::set_var(WAYLAND_VAR, token.token()); env::set_var(WAYLAND_VAR, token._token);
} }

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
} }
} }
@@ -504,7 +297,7 @@ pub trait DeviceIdExtWindows {
impl DeviceIdExtWindows for DeviceId { impl DeviceIdExtWindows for DeviceId {
#[inline] #[inline]
fn persistent_identifier(&self) -> Option<String> { fn persistent_identifier(&self) -> Option<String> {
crate::platform_impl::persistent_identifier(*self) self.0.persistent_identifier()
} }
} }
@@ -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,19 +15,16 @@ 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},
event_loop::{self, ControlFlow, DeviceEvents, EventLoopWindowTarget as RootELW}, event_loop::{self, ControlFlow, DeviceEvents, EventLoopWindowTarget as RootELW},
platform::pump_events::PumpStatus, platform::pump_events::PumpStatus,
window::{ window::{
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowId, self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
WindowLevel,
}, },
}; };
use crate::{error::EventLoopError, platform_impl::Fullscreen}; use crate::{error::EventLoopError, platform_impl::Fullscreen};
@@ -36,7 +32,6 @@ use crate::{error::EventLoopError, platform_impl::Fullscreen};
mod keycodes; mod keycodes;
static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true)); static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));
const WINDOW_ID: u64 = 0;
/// 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
@@ -143,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
@@ -190,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,
@@ -207,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");
@@ -236,7 +232,7 @@ impl<T: 'static> EventLoop<T> {
*HAS_FOCUS.write().unwrap() = true; *HAS_FOCUS.write().unwrap() = true;
callback( callback(
event::Event::WindowEvent { event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID), window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(true), event: event::WindowEvent::Focused(true),
}, },
self.window_target(), self.window_target(),
@@ -246,7 +242,7 @@ impl<T: 'static> EventLoop<T> {
*HAS_FOCUS.write().unwrap() = false; *HAS_FOCUS.write().unwrap() = false;
callback( callback(
event::Event::WindowEvent { event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID), window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(false), event: event::WindowEvent::Focused(false),
}, },
self.window_target(), self.window_target(),
@@ -261,7 +257,7 @@ impl<T: 'static> EventLoop<T> {
MonitorHandle::new(self.android_app.clone()).size(), MonitorHandle::new(self.android_app.clone()).size(),
)); ));
let event = event::Event::WindowEvent { let event = event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID), window_id: window::WindowId(WindowId),
event: event::WindowEvent::ScaleFactorChanged { event: event::WindowEvent::ScaleFactorChanged {
inner_size_writer: InnerSizeWriter::new(Arc::downgrade( inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
&new_inner_size, &new_inner_size,
@@ -349,7 +345,7 @@ impl<T: 'static> EventLoop<T> {
PhysicalSize::new(0, 0) PhysicalSize::new(0, 0)
}; };
let event = event::Event::WindowEvent { let event = event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID), window_id: window::WindowId(WindowId),
event: event::WindowEvent::Resized(size), event: event::WindowEvent::Resized(size),
}; };
callback(event, self.window_target()); callback(event, self.window_target());
@@ -359,7 +355,7 @@ impl<T: 'static> EventLoop<T> {
if pending_redraw { if pending_redraw {
pending_redraw = false; pending_redraw = false;
let event = event::Event::WindowEvent { let event = event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID), window_id: window::WindowId(WindowId),
event: event::WindowEvent::RedrawRequested, event: event::WindowEvent::RedrawRequested,
}; };
callback(event, self.window_target()); callback(event, self.window_target());
@@ -379,13 +375,13 @@ 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 {
InputEvent::MotionEvent(motion_event) => { InputEvent::MotionEvent(motion_event) => {
let window_id = WindowId::from(WINDOW_ID); let window_id = window::WindowId(WindowId);
let device_id = event::DeviceId::from(motion_event.device_id() as u64); let device_id = event::DeviceId(DeviceId(motion_event.device_id()));
let phase = match motion_event.action() { let phase = match motion_event.action() {
MotionAction::Down | MotionAction::PointerDown => { MotionAction::Down | MotionAction::PointerDown => {
@@ -455,9 +451,9 @@ impl<T: 'static> EventLoop<T> {
); );
let event = event::Event::WindowEvent { let event = event::Event::WindowEvent {
window_id: WindowId::from(WINDOW_ID), window_id: window::WindowId(WindowId),
event: event::WindowEvent::KeyboardInput { event: event::WindowEvent::KeyboardInput {
device_id: event::DeviceId::from(key.device_id() as u64), device_id: event::DeviceId(DeviceId(key.device_id())),
event: event::KeyEvent { event: event::KeyEvent {
state, state,
physical_key: keycodes::to_physical_key(keycode), physical_key: keycodes::to_physical_key(keycode),
@@ -465,9 +461,7 @@ impl<T: 'static> EventLoop<T> {
location: keycodes::to_location(keycode), location: keycodes::to_location(keycode),
repeat: key.repeat_count() > 0, repeat: key.repeat_count() > 0,
text: None, text: None,
extra: event::KeyExtra { platform_specific: KeyEventExtra {},
extra: KeyEventExtra {},
},
}, },
is_synthetic: false, is_synthetic: false,
}, },
@@ -486,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) => {
@@ -512,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;
@@ -545,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();
@@ -621,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
} }
@@ -665,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()))
} }
@@ -714,50 +713,66 @@ 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 { #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
OwnedDisplayHandle pub(crate) struct WindowId;
impl WindowId {
pub const fn dummy() -> Self {
WindowId
} }
} }
#[derive(Clone)] impl From<WindowId> for u64 {
pub(crate) struct OwnedDisplayHandle; fn from(_: WindowId) -> Self {
0
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")] impl From<u64> for WindowId {
#[inline] fn from(_: u64) -> Self {
pub fn raw_display_handle_rwh_06( Self
&self, }
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> { }
Ok(rwh_06::AndroidDisplayHandle::new().into())
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DeviceId(i32);
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId(0)
} }
} }
#[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
@@ -776,7 +791,7 @@ impl Window {
} }
pub fn id(&self) -> WindowId { pub fn id(&self) -> WindowId {
WindowId::from(WINDOW_ID) WindowId
} }
pub fn primary_monitor(&self) -> Option<MonitorHandle> { pub fn primary_monitor(&self) -> Option<MonitorHandle> {
@@ -901,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(
@@ -1081,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,
@@ -1095,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,14 @@ 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,
}; };
macro_rules! bug { macro_rules! bug {
@@ -45,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),
} }
@@ -83,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,
}, },
@@ -92,14 +70,16 @@ enum UserCallbackTransitionResult<'a> {
}, },
} }
fn is_redraw(event: &Event<HandlePendingUserEvents>) -> bool { impl Event<Never> {
matches!( fn is_redraw(&self) -> bool {
event, matches!(
Event::WindowEvent { self,
event: WindowEvent::RedrawRequested, Event::WindowEvent {
.. event: WindowEvent::RedrawRequested,
} ..
) }
)
}
} }
// this is the state machine for the app lifecycle // this is the state machine for the app lifecycle
@@ -114,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,
}, },
@@ -128,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,
} }
@@ -224,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,
@@ -236,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,
}); });
@@ -271,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,
@@ -292,7 +277,7 @@ impl AppState {
( (
ControlFlow::WaitUntil(requested_resume), ControlFlow::WaitUntil(requested_resume),
AppStateImpl::Waiting { AppStateImpl::Waiting {
waiting_handler, waiting_event_handler,
start, start,
}, },
) => { ) => {
@@ -307,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,
}); });
@@ -358,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!(),
@@ -381,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
@@ -407,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),
}; };
@@ -420,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,
}); });
} }
@@ -429,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()
@@ -444,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),
} }
} }
@@ -524,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) {
@@ -602,34 +594,36 @@ 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);
for wrapper in events { for wrapper in events {
match wrapper { match wrapper {
EventWrapper::StaticEvent(event) => { EventWrapper::StaticEvent(event) => {
if !processing_redraws && is_redraw(&event) { if !processing_redraws && event.is_redraw() {
log::info!("processing `RedrawRequested` during the main event loop"); log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !is_redraw(&event) { } else if processing_redraws && !event.is_redraw() {
log::warn!( log::warn!(
"processing non `RedrawRequested` event after the main event loop: {:#?}", "processing non `RedrawRequested` event after the main event loop: {:#?}",
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),
} }
} }
@@ -656,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,
} }
@@ -673,17 +667,19 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
for wrapper in queued_events { for wrapper in queued_events {
match wrapper { match wrapper {
EventWrapper::StaticEvent(event) => { EventWrapper::StaticEvent(event) => {
if !processing_redraws && is_redraw(&event) { if !processing_redraws && event.is_redraw() {
log::info!("processing `RedrawRequested` during the main event loop"); log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !is_redraw(&event) { } else if processing_redraws && !event.is_redraw() {
log::warn!( log::warn!(
"processing non-`RedrawRequested` event after the main event loop: {:#?}", "processing non-`RedrawRequested` event after the main event loop: {:#?}",
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),
} }
} }
} }
@@ -691,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);
@@ -727,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,
}); });
@@ -737,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));
} }
} }
@@ -765,7 +762,7 @@ pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
.into_iter() .into_iter()
.map(|window| { .map(|window| {
EventWrapper::StaticEvent(Event::WindowEvent { EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: RootWindowId(window.id()),
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}) })
}) })
@@ -782,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,
@@ -796,13 +793,13 @@ fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged)
} = event; } = event;
let new_inner_size = Arc::new(Mutex::new(suggested_size)); let new_inner_size = Arc::new(Mutex::new(suggested_size));
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id: window.id(), window_id: RootWindowId(window.id()),
event: WindowEvent::ScaleFactorChanged { event: WindowEvent::ScaleFactorChanged {
scale_factor, scale_factor,
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,22 +68,35 @@ mod window;
use std::fmt; use std::fmt;
use crate::event::DeviceId;
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}, 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;
pub(crate) const DEVICE_ID: DeviceId = unsafe { DeviceId::dummy() }; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId {
uiscreen: *const UIScreen,
}
impl DeviceId {
pub const unsafe fn dummy() -> Self {
DeviceId {
uiscreen: std::ptr::null(),
}
}
}
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

@@ -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,37 +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 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}, 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;
@@ -42,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:)]
@@ -54,7 +47,7 @@ declare_class!(
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::StaticEvent(Event::WindowEvent { EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: RootWindowId(window.id()),
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}), }),
); );
@@ -89,7 +82,7 @@ declare_class!(
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::StaticEvent(Event::WindowEvent { EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: RootWindowId(window.id()),
event: WindowEvent::Resized(size), event: WindowEvent::Resized(size),
}), }),
); );
@@ -128,7 +121,7 @@ declare_class!(
width: screen_frame.size.width as f64, width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64, height: screen_frame.size.height as f64,
}; };
let window_id = window.id(); let window_id = RootWindowId(window.id());
app_state::handle_nonuser_events( app_state::handle_nonuser_events(
mtm, mtm,
std::iter::once(EventWrapper::ScaleFactorChanged( std::iter::once(EventWrapper::ScaleFactorChanged(
@@ -166,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: 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: 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: window.id(),
event: WindowEvent::RotationGesture {
device_id: DEVICE_ID,
delta,
phase,
},
});
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event);
}
} }
); );
@@ -262,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 {
@@ -379,9 +250,11 @@ impl WinitView {
) )
}; };
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: 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,
@@ -512,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.
@@ -525,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(),
); );
@@ -582,7 +441,7 @@ declare_class!(
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::StaticEvent(Event::WindowEvent { EventWrapper::StaticEvent(Event::WindowEvent {
window_id: self.id(), window_id: RootWindowId(self.id()),
event: WindowEvent::Focused(true), event: WindowEvent::Focused(true),
}), }),
); );
@@ -595,7 +454,7 @@ declare_class!(
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::StaticEvent(Event::WindowEvent { EventWrapper::StaticEvent(Event::WindowEvent {
window_id: self.id(), window_id: RootWindowId(self.id()),
event: WindowEvent::Focused(false), event: WindowEvent::Focused(false),
}), }),
); );
@@ -608,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> {
@@ -615,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);
@@ -690,7 +550,7 @@ declare_class!(
&*ptr &*ptr
}; };
events.push(EventWrapper::StaticEvent(Event::WindowEvent { events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: RootWindowId(window.id()),
event: WindowEvent::Destroyed, event: WindowEvent::Destroyed,
})); }));
} }
@@ -720,7 +580,7 @@ impl WinitApplicationDelegate {
&*ptr &*ptr
}; };
events.push(EventWrapper::StaticEvent(Event::WindowEvent { events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: window.id(), window_id: RootWindowId(window.id()),
event: WindowEvent::Occluded(occluded), event: WindowEvent::Occluded(occluded),
})); }));
} }

View File

@@ -3,29 +3,41 @@
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::{class, msg_send}; use objc2::{class, msg_send};
use super::app_state::EventWrapper; 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, 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>,
@@ -174,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> {
@@ -366,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
@@ -397,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;
@@ -413,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),
@@ -437,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();
@@ -446,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);
@@ -464,7 +497,7 @@ impl Window {
width: screen_frame.size.width as f64, width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64, height: screen_frame.size.height as f64,
}; };
let window_id = window.id(); let window_id = RootWindowId(window.id());
app_state::handle_nonuser_events( app_state::handle_nonuser_events(
mtm, mtm,
std::iter::once(EventWrapper::ScaleFactorChanged( std::iter::once(EventWrapper::ScaleFactorChanged(
@@ -514,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
@@ -562,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 {
@@ -638,7 +649,45 @@ impl Inner {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId {
window: *mut WinitUIWindow,
}
impl WindowId {
pub const unsafe fn dummy() -> Self {
WindowId {
window: std::ptr::null_mut(),
}
}
}
impl From<WindowId> for u64 {
fn from(window_id: WindowId) -> Self {
window_id.window as u64
}
}
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self {
window: raw_id as _,
}
}
}
unsafe impl Send for WindowId {}
unsafe impl Sync for WindowId {}
impl From<&AnyObject> for WindowId {
fn from(window: &AnyObject) -> WindowId {
WindowId {
window: window as *const _ as _,
}
}
}
#[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,

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