Compare commits

..

2 Commits

Author SHA1 Message Date
Kirill Chibisov
8d39dbf4a0 event_loop: group core API in EventLoopProvider
This helps with portability and defines some top-level structure around
the event loop, so in the future, backends can get an idea of what API
to use.

This also changes the API to be object safe by using `dyn` throughout.
2025-05-13 09:02:46 +09:00
Kirill Chibisov
8e3951636a ci/deny: allow scripts in zerocopy 2025-05-11 21:06:12 +09:00
278 changed files with 7071 additions and 6933 deletions

31
.github/CODEOWNERS vendored
View File

@@ -1,26 +1,31 @@
# Android
/winit-android @MarijnS95
/src/platform/android.rs @MarijnS95
/src/platform_impl/android @MarijnS95
# Apple (AppKit + UIKit)
/winit-appkit @madsmtm
/winit-uikit @madsmtm
/winit-common/src/core_foundation @madsmtm
/winit-common/src/event_handler.rs @madsmtm
/src/platform/ios.rs @madsmtm
/src/platform/macos.rs @madsmtm
/src/platform_impl/apple @madsmtm
# XKB
/winit-common/src/xkb @kchibisov
# Unix
/src/platform_impl/linux/mod.rs @kchibisov
# Wayland
/winit-wayland @kchibisov
/src/platform/wayland.rs @kchibisov
/src/platform_impl/linux/wayland @kchibisov
# X11
/winit-x11 @kchibisov
/src/platform/x11.rs @kchibisov @notgull
/src/platform_impl/linux/x11 @kchibisov @notgull
# Web
/winit-web @daxpedda
/src/platform/web.rs @daxpedda
/src/platform_impl/web @daxpedda
# Windows (Win32) (UNOWNED)
#/winit-win32
# Windows
/src/platform/windows.rs @notgull
/src/platform_impl/windows @notgull
# Orbital (Redox OS)
/winit-orbital @jackpot51
/src/platform/orbital.rs @jackpot51
/src/platform_impl/orbital @jackpot51

View File

@@ -66,7 +66,7 @@ jobs:
- { 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: '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: '--package winit --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: 'macOS x86_64', target: x86_64-apple-darwin, os: macos-latest, }
- { name: 'macOS Aarch64', target: aarch64-apple-darwin, os: macos-latest, }
@@ -89,7 +89,7 @@ jobs:
platform: { name: 'Redox OS' }
include:
- toolchain: '1.80'
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', target: wasm32-unknown-unknown, os: ubuntu-latest, test-options: -Zdoctest-xcompile }
- toolchain: 'nightly'
@@ -179,60 +179,13 @@ jobs:
- name: Build crate
run: cargo $CMD build $OPTIONS
- name: Test winit core
run: cargo test -p winit-core
- name: Test winit Android
if: contains(matrix.platform.target, 'android')
run: cargo $CMD test -p winit-android --features native-activity --no-run
- name: Test winit Common (EventHandler)
run: cargo $CMD test -p winit-common --features event-handler --no-run
- name: Test winit Common (CF)
if: contains(matrix.platform.target, 'apple')
run: cargo $CMD test -p winit-common --features core-foundation --no-run
- name: Test winit Common (XKB)
if: contains(matrix.platform.target, 'linux-gnu')
run: cargo $CMD test -p winit-common --features xkb,x11,wayland --no-run
- name: Test winit AppKit
if: contains(matrix.platform.target, 'macos')
run: cargo $CMD test -p winit-appkit $OPTIONS
- name: Test winit Orbital
if: contains(matrix.platform.target, 'redox')
run: cargo test -p winit-orbital
- name: Test winit UIKit
if: contains(matrix.platform.target, 'ios')
# TODO: Run on Simulator
run: cargo $CMD test -p winit-uikit $OPTIONS --no-run
- name: Test winit Web
if: contains(matrix.platform.target, 'wasm')
run: cargo $CMD test -p winit-web $OPTIONS --no-run
- name: Test winit Win32
if: contains(matrix.platform.target, 'windows')
run: cargo $CMD test -p winit-win32 $OPTIONS
- name: Test winit X11
if: contains(matrix.platform.target, 'linux-gnu')
run: cargo $CMD test -p winit-x11 --target=${{ matrix.platform.target }}
- name: Test winit Wayland
if: contains(matrix.platform.target, 'linux-gnu')
run: cargo $CMD test -p winit-wayland --target=${{ matrix.platform.target }}
# Test only on Linux x86_64, so we avoid spending unnecessary CI hours.
- name: Test dpi crate
if: >
contains(matrix.platform.name, 'Linux 64bit') &&
matrix.toolchain != '1.80'
run: cargo test -p dpi
- name: Check dpi crate (no_std)
if: >
contains(matrix.platform.name, 'Linux 64bit') &&
@@ -313,7 +266,6 @@ jobs:
with:
command: check
log-level: error
manifest-path: winit/Cargo.toml
arguments: --all-features --target ${{ matrix.platform.target }}
eslint:
@@ -322,7 +274,7 @@ jobs:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./winit-web/src/script
working-directory: ./src/platform_impl/web/script
steps:
- uses: taiki-e/checkout-action@v1
@@ -337,7 +289,7 @@ jobs:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./winit-web/src/script
working-directory: ./src/platform_impl/web/script
steps:
- uses: taiki-e/checkout-action@v1

View File

@@ -19,7 +19,7 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
@@ -41,7 +41,7 @@ jobs:
done
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
uses: actions/upload-pages-artifact@v3
with:
path: target/doc

View File

@@ -3,6 +3,6 @@ Changelog entries should be put in the [`changelog::unreleased`].
The changelog can also be viewed [on docs.rs][docs_rs] or [on the current
master docs][master_docs].
[`changelog::unreleased`]: winit/src/changelog/unreleased.md
[`changelog::unreleased`]: src/changelog/unreleased.md
[docs_rs]: https://docs.rs/winit/latest/winit/changelog/index.html
[master_docs]: https://rust-windowing.github.io/winit/winit/changelog/index.html

View File

@@ -13,13 +13,6 @@ To save your time it's wise to check already opened [pull requests][prs] and
accepted, however larger new API proposals should go into the issue first. When
in doubt contact us on [Matrix][matrix] or via opening an issue.
### Windows
To run the examples on Windows, you must have symlinks enabled, see
[this][git-windows-symlinks].
[git-windows-symlinks]: https://gitforwindows.org/symbolic-links.html
### Submitting your work and handling review
All patches have to be sent on Github as [pull requests][prs]. To simplify your

View File

@@ -1,6 +1,395 @@
[package]
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
categories = ["gui"]
description = "Cross-platform window creation library."
documentation = "https://docs.rs/winit"
edition.workspace = true
include = [
"/build.rs",
"/docs",
"/examples",
"/FEATURES.md",
"/LICENSE",
"/src",
"!/src/platform_impl/web/script",
"/src/platform_impl/web/script/**/*.min.js",
"/tests",
]
keywords = ["windowing"]
license.workspace = true
name = "winit"
readme = "README.md"
repository.workspace = true
rust-version.workspace = true
version = "0.30.10"
[package.metadata.docs.rs]
features = [
"serde",
"mint",
# Enabled to get docs to compile
"android-native-activity",
]
# These are all tested in CI
rustdoc-args = ["--cfg", "docsrs"]
targets = [
# Windows
"i686-pc-windows-msvc",
"x86_64-pc-windows-msvc",
# macOS
"aarch64-apple-darwin",
"x86_64-apple-darwin",
# Unix (X11 & Wayland)
"i686-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
# iOS
"aarch64-apple-ios",
# Android
"aarch64-linux-android",
# Web
"wasm32-unknown-unknown",
]
# Features are documented in either `lib.rs` or under `winit::platform`.
[features]
android-game-activity = ["android-activity/game-activity"]
android-native-activity = ["android-activity/native-activity"]
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
mint = ["dpi/mint"]
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde", "bitflags/serde"]
wayland = [
"wayland-client",
"wayland-backend",
"wayland-protocols",
"wayland-protocols-plasma",
"sctk",
"ahash",
"memmap2",
]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
wayland-dlopen = ["wayland-backend/dlopen"]
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
[build-dependencies]
cfg_aliases = "0.2.1"
[dependencies]
bitflags = "2"
cursor-icon = "1.1.0"
dpi = { version = "0.1.2", path = "dpi" }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"] }
serde = { workspace = true, optional = true }
smol_str = "0.3"
tracing = { version = "0.1.40", default-features = false }
[dev-dependencies]
image = { version = "0.25.0", default-features = false, features = ["png"] }
tracing = { version = "0.1.40", default-features = false, features = ["log"] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
[target.'cfg(not(target_os = "android"))'.dev-dependencies]
softbuffer = { version = "0.4.6", default-features = false, features = [
"x11",
"x11-dlopen",
"wayland",
"wayland-dlopen",
] }
# Android
[target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.6.0"
ndk = { version = "0.9.0", features = ["rwh_06"], default-features = false }
# AppKit or UIKit
[target.'cfg(target_vendor = "apple")'.dependencies]
block2 = "0.6.1"
dispatch2 = { version = "0.3.0", default-features = false, features = ["std", "objc2"] }
objc2 = "0.6.1"
# AppKit
[target.'cfg(target_os = "macos")'.dependencies]
objc2-app-kit = { version = "0.3.1", default-features = false, features = [
"std",
"objc2-core-foundation",
"NSAppearance",
"NSApplication",
"NSBitmapImageRep",
"NSButton",
"NSColor",
"NSControl",
"NSCursor",
"NSDragging",
"NSEvent",
"NSGraphics",
"NSGraphicsContext",
"NSImage",
"NSImageRep",
"NSMenu",
"NSMenuItem",
"NSOpenGLView",
"NSPanel",
"NSPasteboard",
"NSResponder",
"NSRunningApplication",
"NSScreen",
"NSTextInputClient",
"NSTextInputContext",
"NSToolbar",
"NSView",
"NSWindow",
"NSWindowScripting",
"NSWindowTabGroup",
] }
objc2-core-foundation = { version = "0.3.1", default-features = false, features = [
"std",
"block2",
"CFBase",
"CFCGTypes",
"CFData",
"CFRunLoop",
"CFString",
"CFUUID",
] }
objc2-core-graphics = { version = "0.3.1", default-features = false, features = [
"std",
"libc",
"CGDirectDisplay",
"CGDisplayConfiguration",
"CGDisplayFade",
"CGError",
"CGRemoteOperation",
"CGWindowLevel",
] }
objc2-core-video = { version = "0.3.1", default-features = false, features = [
"std",
"objc2-core-graphics",
"CVBase",
"CVReturn",
"CVDisplayLink",
] }
objc2-foundation = { version = "0.3.1", default-features = false, features = [
"std",
"block2",
"objc2-core-foundation",
"NSArray",
"NSAttributedString",
"NSData",
"NSDictionary",
"NSDistributedNotificationCenter",
"NSEnumerator",
"NSGeometry",
"NSKeyValueObserving",
"NSNotification",
"NSObjCRuntime",
"NSOperation",
"NSPathUtilities",
"NSProcessInfo",
"NSRunLoop",
"NSString",
"NSThread",
"NSValue",
] }
# UIKit
[target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dependencies]
objc2-core-foundation = { version = "0.3.1", default-features = false, features = [
"std",
"CFCGTypes",
"CFBase",
"CFRunLoop",
"CFString",
] }
objc2-foundation = { version = "0.3.1", default-features = false, features = [
"std",
"block2",
"objc2-core-foundation",
"NSArray",
"NSEnumerator",
"NSGeometry",
"NSObjCRuntime",
"NSOperation",
"NSString",
"NSThread",
"NSSet",
] }
objc2-ui-kit = { version = "0.3.1", default-features = false, features = [
"std",
"objc2-core-foundation",
"UIApplication",
"UIDevice",
"UIEvent",
"UIGeometry",
"UIGestureRecognizer",
"UITextInput",
"UITextInputTraits",
"UIOrientation",
"UIPanGestureRecognizer",
"UIPinchGestureRecognizer",
"UIResponder",
"UIRotationGestureRecognizer",
"UIScreen",
"UIScreenMode",
"UITapGestureRecognizer",
"UITouch",
"UITraitCollection",
"UIView",
"UIViewController",
"UIWindow",
] }
# Windows
[target.'cfg(target_os = "windows")'.dependencies]
unicode-segmentation = "1.7.1"
windows-sys = { version = "0.59.0", 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_Security",
"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",
] }
# Linux
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
ahash = { version = "0.8.7", features = ["no-rng"], optional = true }
bytemuck = { version = "1.13.1", default-features = false, optional = true }
calloop = "0.13.0"
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.19.2", default-features = false, features = [
"calloop",
], optional = true }
sctk-adwaita = { version = "0.10.1", default-features = false, optional = true }
wayland-backend = { version = "0.3.10", default-features = false, features = [
"client_system",
], optional = true }
wayland-client = { version = "0.31.10", optional = true }
wayland-protocols = { version = "0.32.8", features = ["staging"], optional = true }
wayland-protocols-plasma = { version = "0.3.8", features = ["client"], optional = true }
x11-dl = { version = "2.19.1", optional = true }
x11rb = { version = "0.13.0", default-features = false, features = [
"allow-unsafe-code",
"cursor",
"dl-libxcb",
"randr",
"resource_manager",
"sync",
"xinput",
"xkb",
], optional = true }
xkbcommon-dl = "0.4.2"
# Orbital
[target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.47", default-features = false }
redox_syscall = "0.5.7"
# Web
[target.'cfg(target_family = "wasm")'.dependencies]
js-sys = "0.3.70"
pin-project = "1"
wasm-bindgen = "0.2.93"
wasm-bindgen-futures = "0.4.43"
web-time = "1"
web_sys = { package = "web-sys", version = "0.3.70", features = [
"AbortController",
"AbortSignal",
"Blob",
"BlobPropertyBag",
"console",
"CssStyleDeclaration",
"Document",
"DomException",
"DomRect",
"DomRectReadOnly",
"Element",
"Event",
"EventTarget",
"FocusEvent",
"HtmlCanvasElement",
"HtmlElement",
"HtmlHtmlElement",
"HtmlImageElement",
"ImageBitmap",
"ImageBitmapOptions",
"ImageBitmapRenderingContext",
"ImageData",
"IntersectionObserver",
"IntersectionObserverEntry",
"KeyboardEvent",
"MediaQueryList",
"MessageChannel",
"MessagePort",
"Navigator",
"Node",
"OrientationLockType",
"OrientationType",
"PageTransitionEvent",
"Permissions",
"PermissionState",
"PermissionStatus",
"PointerEvent",
"PremultiplyAlpha",
"ResizeObserver",
"ResizeObserverBoxOptions",
"ResizeObserverEntry",
"ResizeObserverOptions",
"ResizeObserverSize",
"Screen",
"ScreenOrientation",
"Url",
"VisibilityState",
"WheelEvent",
"Window",
"Worker",
] }
[target.'cfg(all(target_family = "wasm", target_feature = "atomics"))'.dependencies]
atomic-waker = "1"
concurrent-queue = { version = "2", default-features = false }
[target.'cfg(target_family = "wasm")'.dev-dependencies]
console_error_panic_hook = "0.1"
tracing-web = "0.1"
wasm-bindgen-test = "0.3"
[[example]]
doc-scrape-examples = true
name = "window"
[[example]]
name = "child_window"
[workspace]
default-members = ["winit"]
members = ["dpi", "winit*"]
members = ["dpi"]
resolver = "2"
[workspace.package]
@@ -8,97 +397,7 @@ edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/rust-windowing/winit"
rust-version = "1.80"
version = "0.30.12"
[workspace.dependencies]
# Workspace dependencies.
# `winit` has no version here to allow using it in dev deps for docs.
winit = { path = "winit" }
winit-android = { version = "0.30.12", path = "winit-android" }
winit-appkit = { version = "0.30.12", path = "winit-appkit" }
winit-common = { version = "0.30.12", path = "winit-common" }
winit-core = { version = "0.30.12", path = "winit-core" }
winit-orbital = { version = "0.30.12", path = "winit-orbital" }
winit-uikit = { version = "0.30.12", path = "winit-uikit" }
winit-wayland = { version = "0.30.12", path = "winit-wayland", default-features = false }
winit-web = { version = "0.30.12", path = "winit-web" }
winit-win32 = { version = "0.30.12", path = "winit-win32" }
winit-x11 = { version = "0.30.12", path = "winit-x11" }
# Core dependencies.
bitflags = "2"
cfg_aliases = "0.2.1"
cursor-icon = "1.1.0"
dpi = { version = "0.1.2", path = "dpi" }
keyboard-types = "0.8.0"
mint = "0.5.6"
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"] }
serde = { version = "1", features = ["serde_derive"] }
smol_str = "0.3"
tracing = { version = "0.1.40", default-features = false }
# Dev dependencies.
image = { version = "0.25.0", default-features = false }
softbuffer = { version = "0.4.6", default-features = false, features = [
"x11",
"x11-dlopen",
"wayland",
"wayland-dlopen",
] }
tracing-subscriber = "0.3.18"
# Android dependencies.
android-activity = "0.6.0"
ndk = { version = "0.9.0", features = ["rwh_06"], default-features = false }
# Apple dependencies.
block2 = "0.6.1"
dispatch2 = { version = "0.3.0", default-features = false, features = ["std", "objc2"] }
objc2 = { version = "0.6.1", features = ["relax-sign-encoding"] }
objc2-app-kit = { version = "0.3.1", default-features = false }
objc2-core-foundation = { version = "0.3.1", default-features = false }
objc2-core-graphics = { version = "0.3.1", default-features = false }
objc2-core-video = { version = "0.3.1", default-features = false }
objc2-foundation = { version = "0.3.1", default-features = false }
objc2-ui-kit = { version = "0.3.1", default-features = false }
# Windows dependencies.
unicode-segmentation = "1.7.1"
windows-sys = "0.59.0"
# Linux dependencies.
ahash = { version = "0.8.7", features = ["no-rng"] }
bytemuck = { version = "1.13.1", default-features = false }
calloop = "0.13.0"
libc = "0.2.64"
memmap2 = "0.9.0"
percent-encoding = "2.0"
rustix = { version = "1.0.7", default-features = false }
sctk = { package = "smithay-client-toolkit", version = "0.19.2", default-features = false, features = [
"calloop",
] }
sctk-adwaita = { version = "0.10.1", default-features = false }
wayland-backend = { version = "0.3.10", default-features = false, features = ["client_system"] }
wayland-client = "0.31.10"
wayland-protocols = { version = "0.32.8", features = ["staging"] }
wayland-protocols-plasma = { version = "0.3.8", features = ["client"] }
x11-dl = "2.19.1"
x11rb = { version = "0.13.0", default-features = false }
xkbcommon-dl = "0.4.2"
# Orbital dependencies.
orbclient = { version = "0.3.47", default-features = false }
redox_syscall = "0.5.7"
# Web dependencies.
atomic-waker = "1"
concurrent-queue = { version = "2", default-features = false }
console_error_panic_hook = "0.1"
js-sys = "0.3.70"
pin-project = "1"
tracing-web = "0.1"
wasm-bindgen = "0.2.93"
wasm-bindgen-futures = "0.4.43"
wasm-bindgen-test = "0.3"
web-time = "1"
web_sys = { package = "web-sys", version = "0.3.70" }

View File

@@ -8,7 +8,7 @@
```toml
[dependencies]
winit = "0.30.12"
winit = "0.30.10"
```
## [Documentation](https://docs.rs/winit)

View File

@@ -1,17 +1,16 @@
# Using allow-invalid because this is platform-specific code
disallowed-methods = [
{ allow-invalid = true, path = "objc2_app_kit::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." },
{ allow-invalid = true, path = "objc2_app_kit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
{ allow-invalid = true, path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ allow-invalid = true, path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
{ allow-invalid = true, path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ allow-invalid = true, path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
{ allow-invalid = true, path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
{ allow-invalid = true, path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
{ allow-invalid = true, path = "web_sys::HtmlCanvasElement::width", reason = "Winit shouldn't touch the internal canvas size" },
{ allow-invalid = true, path = "web_sys::HtmlElement::style", reason = "cache this to reduce calls to JS" },
{ allow-invalid = true, path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" },
{ allow-invalid = true, path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" },
{ allow-invalid = true, path = "web_sys::Window::navigator", reason = "cache this to reduce calls to JS" },
{ allow-invalid = true, path = "web_sys::window", reason = "is not available in every context" },
{ path = "objc2_app_kit::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 = "objc2_app_kit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
{ 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::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
{ path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlCanvasElement::width", reason = "Winit shouldn't touch the internal canvas size" },
{ path = "web_sys::HtmlElement::style", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::Window::navigator", reason = "cache this to reduce calls to JS" },
{ path = "web_sys::window", reason = "is not available in every context" },
]

View File

@@ -38,12 +38,8 @@ private = { ignore = true }
[bans]
multiple-versions = "deny"
skip = [
{ crate = "bitflags@1", reason = "the ecosystem is in the process of migrating" },
{ crate = "rustix@0.38", reason = "the ecosystem is in the process of migrating" },
{ crate = "linux-raw-sys@0.4", reason = "the ecosystem is in the process of migrating" },
]
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
skip = [{ crate = "bitflags@1", reason = "the ecosystem is in the process of migrating" }]
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
[bans.build]
include-archives = true

18
docs/ATTRIBUTION.md Normal file
View File

@@ -0,0 +1,18 @@
# Image Attribution
These images are used in the documentation of `winit`.
## keyboard_*.svg
These files are a modified version of "[ANSI US QWERTY (Windows)](https://commons.wikimedia.org/wiki/File:ANSI_US_QWERTY_(Windows).svg)"
by [Tomiĉo] (https://commons.wikimedia.org/wiki/User:Tomi%C4%89o). It was
originally released under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)
License. Minor modifications have been made by [John Nunley](https://github.com/notgull),
which have been released under the same license as a derivative work.
## `coordinate-systems*`
These files are created by [Mads Marquart](https://github.com/madsmtm) using
[draw.io](https://draw.io/), and compressed using [svgomg.net](https://svgomg.net/).
They are licensed under the [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/) license.

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 73 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 73 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 73 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -7,44 +7,38 @@ use std::fmt::Debug;
use std::num::NonZeroU32;
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::Arc;
#[cfg(all(not(android_platform), not(web_platform)))]
use std::time::Instant;
use std::{cmp, fmt, mem};
use std::{fmt, mem};
use ::tracing::{error, info};
use cursor_icon::CursorIcon;
use dpi::LogicalPosition;
#[cfg(not(android_platform))]
use rwh_06::{DisplayHandle, HasDisplayHandle};
#[cfg(not(android_platform))]
use softbuffer::{Context, Surface};
#[cfg(all(web_platform, not(android_platform)))]
use web_time::Instant;
use winit::application::ApplicationHandler;
use winit::cursor::{Cursor, CustomCursor, CustomCursorSource};
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::error::RequestError;
use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::icon::{Icon, RgbaIcon};
use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProvider};
use winit::icon::RgbaIcon;
use winit::keyboard::{Key, ModifiersState};
use winit::monitor::Fullscreen;
#[cfg(macos_platform)]
use winit::platform::macos::{OptionAsAlt, WindowAttributesMacOS, WindowExtMacOS};
#[cfg(any(x11_platform, wayland_platform))]
use winit::platform::startup_notify::{self, EventLoopExtStartupNotify, WindowExtStartupNotify};
#[cfg(wayland_platform)]
use winit::platform::wayland::{ActiveEventLoopExtWayland, WindowAttributesWayland};
#[cfg(web_platform)]
use winit::platform::web::{ActiveEventLoopExtWeb, WindowAttributesWeb};
#[cfg(x11_platform)]
use winit::platform::x11::{ActiveEventLoopExtX11, WindowAttributesX11};
use winit::window::{
CursorGrabMode, ImeCapabilities, ImeEnableRequest, ImePurpose, ImeRequestData,
ImeSurroundingText, ResizeDirection, Theme, Window, WindowAttributes, WindowId,
use winit::platform::macos::{
ApplicationHandlerExtMacOS, OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS,
};
#[cfg(any(x11_platform, wayland_platform))]
use winit::platform::startup_notify::{
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
};
#[cfg(web_platform)]
use winit::platform::web::{ActiveEventLoopExtWeb, WindowAttributesExtWeb};
#[cfg(x11_platform)]
use winit::platform::x11::WindowAttributesExtX11;
use winit::window::{
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Icon, ResizeDirection, Theme, Window,
WindowAttributes, WindowId,
};
use winit_core::application::macos::ApplicationHandlerExtMacOS;
use winit_core::window::ImeRequest;
#[path = "util/tracing.rs"]
mod tracing;
@@ -54,7 +48,6 @@ mod fill;
/// The amount of points to around the window for drag resize direction calculations.
const BORDER_SIZE: f64 = 20.;
const IME_CURSOR_SIZE: PhysicalSize<u32> = PhysicalSize::new(20, 20);
fn main() -> Result<(), Box<dyn Error>> {
#[cfg(web_platform)]
@@ -156,29 +149,43 @@ impl Application {
.with_transparent(true)
.with_window_icon(Some(self.icon.clone()));
#[cfg(x11_platform)]
if event_loop.is_x11() {
window_attributes = window_attributes
.with_platform_attributes(Box::new(window_attributes_x11(event_loop)?));
#[cfg(any(x11_platform, wayland_platform))]
if let Some(token) = event_loop.read_token_from_env() {
startup_notify::reset_activation_token_env();
info!("Using token {:?} to activate a window", token);
window_attributes = window_attributes.with_activation_token(token);
}
#[cfg(wayland_platform)]
if event_loop.is_wayland() {
window_attributes = window_attributes
.with_platform_attributes(Box::new(window_attributes_wayland(event_loop)));
#[cfg(x11_platform)]
match std::env::var("X11_VISUAL_ID") {
Ok(visual_id_str) => {
info!("Using X11 visual id {visual_id_str}");
let visual_id = visual_id_str.parse()?;
window_attributes = window_attributes.with_x11_visual(visual_id);
},
Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"),
}
#[cfg(x11_platform)]
match std::env::var("X11_SCREEN_ID") {
Ok(screen_id_str) => {
info!("Placing the window on X11 screen {screen_id_str}");
let screen_id = screen_id_str.parse()?;
window_attributes = window_attributes.with_x11_screen(screen_id);
},
Err(_) => info!(
"Set the X11_SCREEN_ID env variable to place the window on non-default screen"
),
}
#[cfg(macos_platform)]
if let Some(tab_id) = _tab_id {
let window_attributes_macos =
Box::new(WindowAttributesMacOS::default().with_tabbing_identifier(&tab_id));
window_attributes = window_attributes.with_platform_attributes(window_attributes_macos);
window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
}
#[cfg(web_platform)]
{
window_attributes =
window_attributes.with_platform_attributes(Box::new(window_attributes_web()));
window_attributes = window_attributes.with_append(true);
}
let window = event_loop.create_window(window_attributes)?;
@@ -518,32 +525,7 @@ impl ApplicationHandler for Application {
info!("Preedit: {}, with caret at {:?}", text, caret_pos);
},
Ime::Commit(text) => {
window.text_field_contents.0.push_str(&text);
window.text_field_contents.1 += text.len();
info!("Committed: {}", text);
let request_data = window.get_ime_update();
window.window.request_ime_update(ImeRequest::Update(request_data)).unwrap();
},
Ime::DeleteSurrounding { before_bytes, after_bytes } => {
let (text, cursor) = &window.text_field_contents;
// To anyone copying this, keep in mind that this doesn't take text selection
// into account. The deletion happens *around* the pre-edit,
// and may remove the whole selection or a part of it.
let delete_start = cursor.saturating_sub(before_bytes);
let delete_end = cmp::min(cursor.saturating_add(after_bytes), text.len());
if text.is_char_boundary(delete_start) && text.is_char_boundary(delete_end) {
let new_text = {
let mut t = String::from(&text[..delete_start]);
t.push_str(&text[delete_end..]);
t
};
window.text_field_contents = (new_text, delete_start);
info!("IME deleted bytes: {before_bytes}, {after_bytes}");
} else {
error!("Buggy IME tried to delete with indices not on char boundary.");
}
},
Ime::Disabled => info!("IME disabled for Window={window_id:?}"),
},
@@ -611,6 +593,7 @@ impl ApplicationHandler for Application {
}
}
#[cfg(target_os = "macos")]
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
Some(self)
}
@@ -622,6 +605,7 @@ impl Drop for Application {
}
}
#[cfg(target_os = "macos")]
impl ApplicationHandlerExtMacOS for Application {
fn standard_key_binding(
&mut self,
@@ -635,10 +619,8 @@ impl ApplicationHandlerExtMacOS for Application {
/// State of the window.
struct WindowState {
ime_enabled: bool,
/// The contents of the emulated text field for IME purposes (not displayed).
/// (text, cursor position in bytes).
text_field_contents: (String, usize),
/// IME input.
ime: bool,
/// Render surface.
///
/// NOTE: This surface must be dropped before the `Window`.
@@ -652,7 +634,7 @@ struct WindowState {
animated_fill_color: bool,
/// The application start time. Used for color fill animation
#[cfg(not(android_platform))]
start_time: Instant,
start_time: std::time::Instant,
/// Redraw continuously
continuous_redraw: bool,
/// Cursor position over the window.
@@ -694,19 +676,8 @@ impl WindowState {
window.set_cursor(CURSORS[named_idx].into());
// Allow IME out of the box.
let request_data = ImeRequestData::default()
.with_purpose(ImePurpose::Normal)
.with_cursor_area(LogicalPosition { x: 0, y: 0 }.into(), IME_CURSOR_SIZE.into())
.with_surrounding_text(ImeSurroundingText::new(String::new(), 0, 0).unwrap());
let enable_request = ImeEnableRequest::new(
ImeCapabilities::new().with_purpose().with_cursor_area().with_surrounding_text(),
request_data,
)
.unwrap();
let enable_ime = ImeRequest::Enable(enable_request);
// Initial update
window.request_ime_update(enable_ime).unwrap();
let ime = true;
window.set_ime_allowed(ime);
let size = window.surface_size();
let mut state = Self {
@@ -722,9 +693,8 @@ impl WindowState {
animated_fill_color: false,
continuous_redraw: false,
#[cfg(not(android_platform))]
start_time: Instant::now(),
ime_enabled: true,
text_field_contents: (String::new(), 0),
start_time: std::time::Instant::now(),
ime,
cursor_position: Default::default(),
cursor_hidden: Default::default(),
modifiers: Default::default(),
@@ -738,48 +708,12 @@ impl WindowState {
Ok(state)
}
pub fn get_ime_update(&self) -> ImeRequestData {
let (text, cursor) = &self.text_field_contents;
// A rudimentary text field emulation: the caret moves right by a constant amount for each
// code point.
let text_before_caret = if text.is_char_boundary(*cursor) { &text[..*cursor] } else { "" };
let chars_before_caret = text_before_caret.chars().count();
let cursor_pos = LogicalPosition { x: 10 * chars_before_caret as u32, y: 0 }.into();
// Limit text field size
const MAX_BYTES: usize = ImeSurroundingText::MAX_TEXT_BYTES;
let minimal_offset = cursor / MAX_BYTES * MAX_BYTES;
let first_char_boundary =
(minimal_offset..*cursor).find(|off| text.is_char_boundary(*off)).unwrap_or(*cursor);
let last_char_boundary = (*cursor..(first_char_boundary + MAX_BYTES))
.rev()
.find(|off| text.is_char_boundary(*off))
.unwrap_or(*cursor);
let surrounding_text = &text[first_char_boundary..last_char_boundary];
let relative_cursor = cursor - first_char_boundary;
let surrounding_text =
ImeSurroundingText::new(surrounding_text.into(), relative_cursor, relative_cursor)
.expect("Bug in example: bad byte calculations");
ImeRequestData::default()
.with_purpose(ImePurpose::Normal)
.with_cursor_area(cursor_pos, IME_CURSOR_SIZE.into())
.with_surrounding_text(surrounding_text)
}
pub fn toggle_ime(&mut self) {
if self.ime_enabled {
self.window.request_ime_update(ImeRequest::Disable).expect("disable can not fail");
} else {
let enable_request = ImeEnableRequest::new(
ImeCapabilities::new().with_purpose().with_cursor_area().with_surrounding_text(),
self.get_ime_update(),
)
.unwrap();
self.window.request_ime_update(ImeRequest::Enable(enable_request)).unwrap();
};
self.ime_enabled = !self.ime_enabled;
self.ime = !self.ime;
self.window.set_ime_allowed(self.ime);
if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
self.window.set_ime_cursor_area(position.into(), PhysicalSize::new(20, 20).into());
}
}
pub fn minimize(&mut self) {
@@ -787,15 +721,9 @@ impl WindowState {
}
pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
// the IME really cares about the caret,
// but there's nothing else to demonstrate a position
self.cursor_position = Some(position);
if self.ime_enabled {
let request_data =
ImeRequestData::default().with_cursor_area(position.into(), IME_CURSOR_SIZE.into());
self.window
.request_ime_update(ImeRequest::Update(request_data))
.expect("A capability was not initially declared");
if self.ime {
self.window.set_ime_cursor_area(position.into(), PhysicalSize::new(20, 20).into());
}
}
@@ -1270,60 +1198,6 @@ fn mouse_button_to_string(button: MouseButton) -> &'static str {
}
}
#[cfg(web_platform)]
fn window_attributes_web() -> WindowAttributesWeb {
WindowAttributesWeb::default().with_append(true)
}
#[cfg(wayland_platform)]
fn window_attributes_wayland(event_loop: &dyn ActiveEventLoop) -> WindowAttributesWayland {
let mut window_attributes = WindowAttributesWayland::default();
if let Some(token) = event_loop.read_token_from_env() {
startup_notify::reset_activation_token_env();
info!("Using token {:?} to activate a window", token);
window_attributes = window_attributes.with_activation_token(token);
}
window_attributes
}
#[cfg(x11_platform)]
fn window_attributes_x11(
event_loop: &dyn ActiveEventLoop,
) -> Result<WindowAttributesX11, Box<dyn Error>> {
let mut window_attributes = WindowAttributesX11::default();
#[cfg(any(x11_platform, wayland_platform))]
if let Some(token) = event_loop.read_token_from_env() {
startup_notify::reset_activation_token_env();
info!("Using token {:?} to activate a window", token);
window_attributes = window_attributes.with_activation_token(token);
}
match std::env::var("X11_VISUAL_ID") {
Ok(visual_id_str) => {
info!("Using X11 visual id {visual_id_str}");
let visual_id = visual_id_str.parse()?;
window_attributes = window_attributes.with_x11_visual(visual_id);
},
Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"),
}
match std::env::var("X11_SCREEN_ID") {
Ok(screen_id_str) => {
info!("Placing the window on X11 screen {screen_id_str}");
let screen_id = screen_id_str.parse()?;
window_attributes = window_attributes.with_x11_screen(screen_id);
},
Err(_) => {
info!("Set the X11_SCREEN_ID env variable to place the window on non-default screen")
},
}
Ok(window_attributes)
}
/// Cursor list to cycle through.
const CURSORS: &[CursorIcon] = &[
CursorIcon::Default,

View File

@@ -6,7 +6,7 @@ fn main() -> Result<(), impl std::error::Error> {
use winit::application::ApplicationHandler;
use winit::dpi::{LogicalPosition, LogicalSize, Position};
use winit::event::{ElementState, KeyEvent, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProvider};
use winit::raw_window_handle::HasRawWindowHandle;
use winit::window::{Window, WindowAttributes, WindowId};

View File

@@ -9,7 +9,7 @@ use ::tracing::{info, warn};
use web_time as time;
use winit::application::ApplicationHandler;
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop, EventLoopProvider};
use winit::keyboard::{Key, NamedKey};
use winit::window::{Window, WindowAttributes, WindowId};

View File

@@ -2,7 +2,7 @@ use std::error::Error;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProvider};
use winit::window::{Window, WindowAttributes, WindowId};
#[path = "util/fill.rs"]
@@ -49,7 +49,7 @@ impl ApplicationHandler for Application {
| WindowEvent::DragEntered { .. }
| WindowEvent::DragMoved { .. }
| WindowEvent::DragDropped { .. } => {
println!("{event:?}");
println!("{:?}", event);
},
WindowEvent::RedrawRequested => {
let window = self.window.as_ref().unwrap();

View File

@@ -9,8 +9,8 @@ fn main() -> std::process::ExitCode {
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::pump_events::{EventLoopExtPumpEvents, PumpStatus};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus};
use winit::window::{Window, WindowAttributes, WindowId};
#[path = "util/fill.rs"]

View File

@@ -7,8 +7,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
use winit::window::{Window, WindowAttributes, WindowId};
#[path = "util/fill.rs"]

View File

@@ -23,12 +23,8 @@ mod platform {
use std::mem;
use std::mem::ManuallyDrop;
use std::num::NonZeroU32;
#[cfg(all(not(android_platform), not(web_platform)))]
use std::time::Instant;
use softbuffer::{Context, Surface};
#[cfg(all(web_platform, not(android_platform)))]
use web_time::Instant;
use winit::window::{Window, WindowId};
thread_local! {
@@ -109,7 +105,7 @@ mod platform {
}
#[allow(dead_code)]
pub fn fill_window_with_animated_color(window: &dyn Window, start: Instant) {
pub fn fill_window_with_animated_color(window: &dyn Window, start: std::time::Instant) {
let time = start.elapsed().as_secs_f32() * 1.5;
let blue = (time.sin() * 255.0) as u32;
let green = ((time.cos() * 255.0) as u32) << 8;

View File

@@ -4,9 +4,9 @@ use std::error::Error;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProvider};
#[cfg(web_platform)]
use winit::platform::web::WindowAttributesWeb;
use winit::platform::web::WindowAttributesExtWeb;
use winit::window::{Window, WindowAttributes, WindowId};
#[path = "util/fill.rs"]
@@ -24,8 +24,7 @@ impl ApplicationHandler for App {
#[cfg(not(web_platform))]
let window_attributes = WindowAttributes::default();
#[cfg(web_platform)]
let window_attributes = WindowAttributes::default()
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
let window_attributes = WindowAttributes::default().with_append(true);
self.window = match event_loop.create_window(window_attributes) {
Ok(window) => Some(window),
Err(err) => {

View File

@@ -5,8 +5,8 @@ use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::platform::x11::WindowAttributesX11;
use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProvider};
use winit::platform::x11::WindowAttributesExtX11;
use winit::window::{Window, WindowAttributes, WindowId};
#[path = "util/fill.rs"]
@@ -20,12 +20,10 @@ fn main() -> Result<(), Box<dyn Error>> {
impl ApplicationHandler for XEmbedDemo {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let mut window_attributes = WindowAttributes::default()
let window_attributes = WindowAttributes::default()
.with_title("An embedded window!")
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
let x11_attrs =
WindowAttributesX11::default().with_embed_parent_window(self.parent_window_id);
window_attributes = window_attributes.with_platform_attributes(Box::new(x11_attrs));
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(self.parent_window_id);
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}

View File

@@ -2,11 +2,21 @@
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
use crate::event_loop::ActiveEventLoop;
#[cfg(macos_platform)]
use crate::platform::macos::ApplicationHandlerExtMacOS;
use crate::window::WindowId;
pub mod macos;
/// The handler of application-level events.
///
/// See [the top-level docs] for example usage, and [`EventLoopProvider::run_app`] for an overview
/// of when events are delivered.
///
/// This is [dropped] when the event loop is shut down. Note that this only works if you're passing
/// the entire state to [`EventLoopProvider::run_app`] (passing `&mut app` won't work).
///
/// [the top-level docs]: crate
/// [`EventLoopProvider::run_app`]: crate::event_loop::EventLoopProvider::run_app
/// [dropped]: std::ops::Drop
pub trait ApplicationHandler {
/// Emitted when new events arrive from the OS to be processed.
///
@@ -125,9 +135,8 @@ pub trait ApplicationHandler {
/// use std::thread;
/// use std::time::Duration;
///
/// use winit::event_loop::EventLoop;
/// use winit_core::application::ApplicationHandler;
/// use winit_core::event_loop::ActiveEventLoop;
/// use winit::application::ApplicationHandler;
/// use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProvider};
///
/// struct MyApp {
/// receiver: mpsc::Receiver<u64>,
@@ -201,7 +210,9 @@ pub trait ApplicationHandler {
/// Emitted when the OS sends an event to a device.
///
/// Whether device events are delivered depends on the backend in use.
/// For this to be called, it must be enabled with [`EventLoopProvider::listen_device_events`].
///
/// [`EventLoopProvider::listen_device_events`]: crate::event_loop::EventLoopProvider::listen_device_events
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
@@ -339,8 +350,9 @@ pub trait ApplicationHandler {
/// The macOS-specific handler.
///
/// The return value from this should not change at runtime.
#[cfg(macos_platform)]
#[inline(always)]
fn macos_handler(&mut self) -> Option<&mut dyn macos::ApplicationHandlerExtMacOS> {
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
None
}
}
@@ -407,8 +419,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
(**self).memory_warning(event_loop);
}
#[cfg(macos_platform)]
#[inline]
fn macos_handler(&mut self) -> Option<&mut dyn macos::ApplicationHandlerExtMacOS> {
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
(**self).macos_handler()
}
}
@@ -475,8 +488,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
(**self).memory_warning(event_loop);
}
#[cfg(macos_platform)]
#[inline]
fn macos_handler(&mut self) -> Option<&mut dyn macos::ApplicationHandlerExtMacOS> {
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
(**self).macos_handler()
}
}

View File

@@ -61,7 +61,7 @@ changelog entry.
- Add `MonitorHandle::current_video_mode()`.
- Add `ApplicationHandlerExtMacOS` trait, and a `macos_handler` method to `ApplicationHandler` which returns a `dyn ApplicationHandlerExtMacOS` which allows for macOS specific extensions to winit.
- Add a `standard_key_binding` method to the `ApplicationHandlerExtMacOS` trait. This allows handling of standard keybindings such as "go to end of line" on macOS.
- On macOS, add `WindowExtMacOS::set_unified_titlebar` and `WindowAttributesMacOS::with_unified_titlebar`
- On macOS, add `WindowExtMacOS::set_unified_titlebar` and `WindowAttributesExtMacOS::with_unified_titlebar`
to use a larger style of titlebar.
- Add `WindowId::into_raw()` and `from_raw()`.
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId`, `primary` and `position` to all
@@ -77,13 +77,6 @@ changelog entry.
- Add `CustomCursorSource::Url`, `CustomCursorSource::from_animation`.
- Implement `CustomIconProvider` for `RgbaIcon`.
- Add `icon` module that exposes winit's icon API.
- `VideoMode::new` to create a `VideoMode`.
- `keyboard::ModifiersKey` to track which modifier is exactly pressed.
- `ActivationToken::as_raw` to get a ref to raw token.
- Each platform now has corresponding `WindowAttributes` struct instead of trait extension.
- On Wayland, added implementation for `Window::set_window_icon`
- Add `Window::request_ime_update` to atomically apply set of IME changes.
- Add `Ime::DeleteSurrounding` to let the input method delete text.
### Changed
@@ -174,7 +167,6 @@ changelog entry.
- Rename `VideoModeHandle` to `VideoMode`, now it only stores plain data.
- Make `Fullscreen::Exclusive` contain `(MonitorHandle, VideoMode)`.
- Reworked the file drag-and-drop API.
- On macOS, the default menu uses the bundle name or falls back to the process name as before.
The `WindowEvent::DroppedFile`, `WindowEvent::HoveredFile` and `WindowEvent::HoveredFileCancelled`
events have been removed, and replaced with `WindowEvent::DragEntered`, `WindowEvent::DragMoved`,
@@ -203,10 +195,10 @@ changelog entry.
- Renamed "super" key to "meta", to match the naming in the W3C specification.
`NamedKey::Super` still exists, but it's non-functional and deprecated, `NamedKey::Meta` should be used instead.
- Move `IconExtWindows` into `WinIcon`.
- Move `EventLoopExtPumpEvents` and `PumpStatus` from platform module to `winit::event_loop::pump_events`.
- Move `EventLoopExtRunOnDemand` from platform module to `winit::event_loop::run_on_demand`.
- Use `NamedKey`, `Code` and `Location` from the `keyboard-types` v0.8 crate.
- Deprecate `Window::set_ime_allowed`, `Window::set_ime_cursor_area`, and `Window::set_ime_purpose`.
- `run_app_on_demand`/`pump_app_events` now accept `&mut dyn ApplicationHandler` instead of generic.
- Moved common `EventLoop` methods like `run_app` into `EventLoopProvider` trait.
- Moved `event_loop::EventLoop` into `platform::event_loop::EventLoop` keeping the old re-export in place.
- `EventLoopProvider::run_app` now takes `Box<dyn ApplicationHandler` instead of owned generic.
### Removed
@@ -245,7 +237,6 @@ changelog entry.
the `Drop` impl on the application handler.
- Remove `NamedKey::Space`, match on `Key::Character(" ")` instead.
- Remove `PartialEq` impl for `WindowAttributes`.
- `WindowAttributesExt*` platform extensions; use `WindowAttributes*` instead.
### Fixed
@@ -254,9 +245,3 @@ changelog entry.
- On macOS, fixed the scancode conversion for audio volume keys.
- On macOS, fixed the scancode conversion for `IntlBackslash`.
- On macOS, fixed redundant `SurfaceResized` event at window creation.
- On macOS, don't panic on monitors with unknown bit-depths.
- On macOS, fixed crash when closing the window on macOS 26+.
- On Windows, account for mouse wheel lines per scroll setting for `WindowEvent::MouseWheel`.
- On Windows, `Window::theme` will return the correct theme after setting it through `Window::set_theme`.
- On Windows, `Window::set_theme` will change the title bar color immediately now.
- On Windows 11, prevent incorrect shifting when dragging window onto a monitor with different DPI.

View File

@@ -250,7 +250,7 @@
- On Web, fix some `WindowBuilder` methods doing nothing.
- On Web, fix some `Window` methods using incorrect HTML attributes instead of CSS properties.
- On Web, fix the bfcache by not using the `beforeunload` event and map bfcache loading/unloading to `Suspended`/`Resumed` events.
- On Web, fix touch input not gaining or losing focus.
- On Web, fix touch input not gaining or loosing focus.
- On Web, fix touch location to be as accurate as mouse position.
- On Web, handle coalesced pointer events, which increases the resolution of pointer inputs.
- On Web, implement `Window::focus_window()`.

View File

@@ -1,16 +1,3 @@
## 0.30.12
### Fixed
- On macOS, fix crash on macOS 26 by using objc2's relax-sign-encoding feature.
## 0.30.11
### Fixed
- On Windows, fixed crash in should_apps_use_dark_mode() for Windows versions < 17763.
- On Wayland, fixed `pump_events` driven loop deadlocking when loop was not drained before exit.
## 0.30.10
### Added

View File

@@ -5,10 +5,9 @@ use std::ops::Deref;
use std::sync::Arc;
use std::time::Duration;
#[doc(inline)]
pub use cursor_icon::CursorIcon;
use cursor_icon::CursorIcon;
use crate::as_any::{impl_dyn_casting, AsAny};
use crate::utils::{impl_dyn_casting, AsAny};
/// The maximum width and height for a cursor when using [`CustomCursorSource::from_rgba`].
pub const MAX_CURSOR_SIZE: u16 = 2048;
@@ -51,10 +50,10 @@ impl From<CustomCursor> for Cursor {
/// # Example
///
/// ```no_run
/// # use winit_core::event_loop::ActiveEventLoop;
/// # use winit_core::window::Window;
/// # use winit::event_loop::ActiveEventLoop;
/// # use winit::window::Window;
/// # fn scope(event_loop: &dyn ActiveEventLoop, window: &dyn Window) {
/// use winit_core::cursor::CustomCursorSource;
/// use winit::window::CustomCursorSource;
///
/// let w = 10;
/// let h = 10;
@@ -76,7 +75,7 @@ impl From<CustomCursor> for Cursor {
/// # }
/// ```
#[derive(Clone, Debug)]
pub struct CustomCursor(pub Arc<dyn CustomCursorProvider>);
pub struct CustomCursor(pub(crate) Arc<dyn CustomCursorProvider>);
pub trait CustomCursorProvider: AsAny + fmt::Debug + Send + Sync {
/// Whether a cursor was backed by animation.
@@ -236,6 +235,7 @@ impl fmt::Display for BadAnimation {
impl Error for BadAnimation {}
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
#[allow(dead_code)]
pub struct CursorImage {
pub(crate) rgba: Vec<u8>,
pub(crate) width: u16,
@@ -277,30 +277,6 @@ impl CursorImage {
Ok(CursorImage { rgba, width, height, hotspot_x, hotspot_y })
}
pub fn buffer(&self) -> &[u8] {
self.rgba.as_slice()
}
pub fn buffer_mut(&mut self) -> &mut [u8] {
self.rgba.as_mut_slice()
}
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
pub fn hotspot_x(&self) -> u16 {
self.hotspot_x
}
pub fn hotspot_y(&self) -> u16 {
self.hotspot_y
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -321,16 +297,4 @@ impl CursorAnimation {
Ok(Self { duration, cursors })
}
pub fn duration(&self) -> Duration {
self.duration
}
pub fn cursors(&self) -> &[CustomCursor] {
self.cursors.as_slice()
}
pub fn into_raw(self) -> (Duration, Vec<CustomCursor>) {
(self.duration, self.cursors)
}
}

View File

@@ -19,13 +19,7 @@ pub enum EventLoopError {
impl fmt::Display for EventLoopError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RecreationAttempt => {
write!(
f,
"EventLoop can't be recreated, only a single instance of it is supported (for \
cross-platform compatibility)"
)
},
Self::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
Self::Os(err) => err.fmt(f),
Self::ExitFailure(status) => write!(f, "Exit Failure: {status}"),
Self::NotSupported(err) => err.fmt(f),
@@ -106,7 +100,7 @@ pub struct NotSupportedError {
}
impl NotSupportedError {
pub fn new(reason: &'static str) -> Self {
pub(crate) fn new(reason: &'static str) -> Self {
Self { reason }
}
}
@@ -127,7 +121,8 @@ pub struct OsError {
}
impl OsError {
pub fn new(
#[allow(dead_code)]
pub(crate) fn new(
line: u32,
file: &'static str,
error: impl Into<Box<dyn Error + Send + Sync + 'static>>,

View File

@@ -1,19 +1,22 @@
//! The event enums and assorted supporting types.
use std::path::PathBuf;
use std::sync::{Mutex, Weak};
#[cfg(not(web_platform))]
use std::time::Instant;
use dpi::{PhysicalPosition, PhysicalSize};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
#[cfg(web_platform)]
use web_time::Instant;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::error::RequestError;
use crate::event_loop::AsyncRequestSerial;
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState};
#[cfg(doc)]
use crate::window::Window;
use crate::window::{ActivationToken, Theme};
use crate::Instant;
/// Describes the reason the event loop is resuming.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -43,6 +46,10 @@ pub enum StartCause {
#[derive(Debug, Clone, PartialEq)]
pub enum WindowEvent {
/// The activation token was delivered back and now could be used.
#[cfg_attr(not(any(x11_platform, wayland_platform)), allow(rustdoc::broken_intra_doc_links))]
/// Delivered in response to [`request_activation_token`].
///
/// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token
ActivationTokenDone { serial: AsyncRequestSerial, token: ActivationToken },
/// The size of the window's surface has changed.
@@ -556,14 +563,16 @@ impl DeviceId {
/// Convert the [`DeviceId`] into the underlying integer.
///
/// This is useful if you need to pass the ID across an FFI boundary, or store it in an atomic.
pub const fn into_raw(self) -> i64 {
#[allow(dead_code)]
pub(crate) const fn into_raw(self) -> i64 {
self.0
}
/// Construct a [`DeviceId`] from the underlying integer.
///
/// This should only be called with integers returned from [`DeviceId::into_raw`].
pub const fn from_raw(id: i64) -> Self {
#[allow(dead_code)]
pub(crate) const fn from_raw(id: i64) -> Self {
Self(id)
}
}
@@ -579,14 +588,16 @@ impl FingerId {
/// Convert the [`FingerId`] into the underlying integer.
///
/// This is useful if you need to pass the ID across an FFI boundary, or store it in an atomic.
pub const fn into_raw(self) -> usize {
#[allow(dead_code)]
pub(crate) const fn into_raw(self) -> usize {
self.0
}
/// Construct a [`FingerId`] from the underlying integer.
///
/// This should only be called with integers returned from [`FingerId::into_raw`].
pub const fn from_raw(id: usize) -> Self {
#[allow(dead_code)]
pub(crate) const fn from_raw(id: usize) -> Self {
Self(id)
}
}
@@ -611,8 +622,17 @@ pub enum DeviceEvent {
/// ## Platform-specific
///
/// **Web:** Only returns raw data, not OS accelerated, if [`CursorGrabMode::Locked`] is used
/// and browser support is available.
/// and browser support is available, see
#[cfg_attr(
web_platform,
doc = "[`ActiveEventLoopExtWeb::is_cursor_lock_raw()`][crate::platform::web::ActiveEventLoopExtWeb::is_cursor_lock_raw()]."
)]
#[cfg_attr(
not(web_platform),
doc = "`ActiveEventLoopExtWeb::is_cursor_lock_raw()`."
)]
///
#[rustfmt::skip]
/// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked
PointerMotion {
/// (x, y) change in position in unspecified units.
@@ -749,8 +769,8 @@ pub struct KeyEvent {
/// done by ignoring events where this property is set.
///
/// ```no_run
/// use winit_core::event::{ElementState, KeyEvent, WindowEvent};
/// use winit_core::keyboard::{KeyCode, PhysicalKey};
/// use winit::event::{ElementState, KeyEvent, WindowEvent};
/// use winit::keyboard::{KeyCode, PhysicalKey};
/// # let window_event = WindowEvent::RedrawRequested; // To make the example compile
/// match window_event {
/// WindowEvent::KeyboardInput {
@@ -805,59 +825,54 @@ pub struct KeyEvent {
pub struct Modifiers {
pub(crate) state: ModifiersState,
// NOTE: Currently active modifiers keys (logically, but not necessarily physically, pressed).
// NOTE: Currently pressed modifiers keys.
//
// The field providing a metadata, it shouldn't be used as a source of truth.
pub(crate) pressed_mods: ModifiersKeys,
}
impl Modifiers {
/// Create a new modifiers from state and pressed mods.
pub fn new(state: ModifiersState, pressed_mods: ModifiersKeys) -> Self {
Self { state, pressed_mods }
}
/// The logical state of the modifiers.
/// The state of the modifiers.
pub fn state(&self) -> ModifiersState {
self.state
}
/// The logical state of the left shift key.
/// The state of the left shift key.
pub fn lshift_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::LSHIFT)
}
/// The logical state of the right shift key.
/// The state of the right shift key.
pub fn rshift_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::RSHIFT)
}
/// The logical state of the left alt key.
/// The state of the left alt key.
pub fn lalt_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::LALT)
}
/// The logical state of the right alt key.
/// The state of the right alt key.
pub fn ralt_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::RALT)
}
/// The logical state of the left control key.
/// The state of the left control key.
pub fn lcontrol_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::LCONTROL)
}
/// The logical state of the right control key.
/// The state of the right control key.
pub fn rcontrol_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::RCONTROL)
}
/// The logical state of the left super key.
/// The state of the left super key.
pub fn lsuper_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::LMETA)
}
/// The logical state of the right super key.
/// The state of the right super key.
pub fn rsuper_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::RMETA)
}
@@ -879,8 +894,6 @@ impl From<ModifiersState> for Modifiers {
/// Describes [input method](https://en.wikipedia.org/wiki/Input_method) events.
///
/// The `Ime` events must be applied in the order they arrive.
///
/// This is also called a "composition event".
///
/// Most keypresses using a latin-like keyboard layout simply generate a
@@ -937,7 +950,7 @@ pub enum Ime {
/// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
/// this indicates that preedit was cleared.
///
/// The cursor position is byte-wise indexed, assuming UTF-8.
/// The cursor position is byte-wise indexed.
Preedit(String, Option<(usize, usize)>),
/// Notifies when text should be inserted into the editor widget.
@@ -945,20 +958,6 @@ pub enum Ime {
/// Right before this event winit will send empty [`Self::Preedit`] event.
Commit(String),
/// Delete text surrounding the cursor or selection.
///
/// This event does not affect either the pre-edit string.
/// This means that the application must first remove the pre-edit,
/// then execute the deletion, then insert the removed text back.
///
/// This event assumes text is stored in UTF-8.
DeleteSurrounding {
/// Bytes to remove before the selection
before_bytes: usize,
/// Bytes to remove after the selection
after_bytes: usize,
},
/// Notifies when the IME was disabled.
///
/// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or
@@ -1092,7 +1091,8 @@ pub struct SurfaceSizeWriter {
}
impl SurfaceSizeWriter {
pub fn new(new_surface_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
#[cfg(not(orbital_platform))]
pub(crate) fn new(new_surface_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
Self { new_surface_size }
}
@@ -1108,15 +1108,6 @@ impl SurfaceSizeWriter {
Err(RequestError::Ignored)
}
}
/// Get the currently stashed surface size.
pub fn surface_size(&self) -> Result<PhysicalSize<u32>, RequestError> {
if let Some(inner) = self.new_surface_size.upgrade() {
Ok(*inner.lock().unwrap())
} else {
Err(RequestError::Ignored)
}
}
}
impl PartialEq for SurfaceSizeWriter {
@@ -1131,8 +1122,7 @@ impl Eq for SurfaceSizeWriter {}
mod tests {
use std::collections::{BTreeSet, HashSet};
use dpi::PhysicalPosition;
use crate::dpi::PhysicalPosition;
use crate::event;
macro_rules! foreach_event {

View File

@@ -1,38 +1,132 @@
pub mod pump_events;
pub mod run_on_demand;
use std::fmt::{self, Debug};
//! The [`EventLoop`] struct and assorted supporting types, including
//! [`ControlFlow`].
//!
//! If you want to send custom events to the event loop, use
//! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its
//! [`wake_up`][EventLoopProxy::wake_up] method. Then during handling the wake up
//! you can poll your event sources.
//!
//! See the root-level documentation for information on how to create and use an event loop to
//! handle events.
use std::fmt;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::task::Waker;
use std::time::Duration;
#[cfg(not(web_platform))]
use std::time::{Duration, Instant};
use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
#[cfg(web_platform)]
use web_time::{Duration, Instant};
use crate::as_any::AsAny;
use crate::cursor::{CustomCursor, CustomCursorSource};
use crate::error::RequestError;
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, RequestError};
use crate::monitor::MonitorHandle;
use crate::window::{Theme, Window, WindowAttributes};
use crate::Instant;
pub use crate::platform::event_loop::*;
use crate::utils::{impl_dyn_casting, AsAny};
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes};
/// Common interface to describe event loop.
pub trait EventLoopProvider: AsAny + fmt::Debug {
/// Run the application with the event loop on the calling thread.
///
/// ## Event loop flow
///
/// This function internally handles the different parts of a traditional event-handling loop.
/// You can imagine this method as being implemented like this:
///
/// ```rust,ignore
/// let mut start_cause = StartCause::Init;
///
/// // Run the event loop.
/// while !event_loop.exiting() {
/// // Wake up.
/// app.new_events(event_loop, start_cause);
///
/// // Indicate that surfaces can now safely be created.
/// if start_cause == StartCause::Init {
/// app.can_create_surfaces(event_loop);
/// }
///
/// // Handle proxy wake-up event.
/// if event_loop.proxy_wake_up_set() {
/// event_loop.proxy_wake_up_clear();
/// app.proxy_wake_up(event_loop);
/// }
///
/// // Handle actions done by the user / system such as moving the cursor, resizing the
/// // window, changing the window theme, etc.
/// for event in event_loop.events() {
/// match event {
/// window event => app.window_event(event_loop, window_id, event),
/// device event => app.device_event(event_loop, device_id, event),
/// }
/// }
///
/// // Handle redraws.
/// for window_id in event_loop.pending_redraws() {
/// app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
/// }
///
/// // Done handling events, wait until we're woken up again.
/// app.about_to_wait(event_loop);
/// start_cause = event_loop.wait_if_necessary();
/// }
///
/// // Finished running, drop application state.
/// drop(app);
/// ```
///
/// This is of course a very coarse-grained overview, and leaves out timing details like
/// [`ControlFlow::WaitUntil`] and life-cycle methods like [`ApplicationHandler::resumed`], but
/// it should give you an idea of how things fit together.
///
/// ## Platform-specific
///
/// - **iOS:** Will never return to the caller and so values not passed to this function will
/// *not* be dropped before the process exits.
/// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript
/// exception (that Rust doesn't see) that will also mean that the rest of the function is
/// never executed and any values not passed to this function will *not* be dropped.
///
/// Web applications are recommended to use
#[cfg_attr(
web_platform,
doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
)]
#[cfg_attr(not(web_platform), doc = " `EventLoopExtWeb::spawn_app()`")]
/// [^1] instead of [`run_app()`] to avoid the need for the Javascript exception trick, and to
/// make it clearer that the event loop runs asynchronously (via the browser's own,
/// internal, event loop) and doesn't block the current thread of execution like it does
/// on other platforms.
///
/// This function won't be available with `target_feature = "exception-handling"`.
///
/// [^1]: `spawn_app()` is only available on the Web platform.
///
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`run_app()`]: Self::run_app()
fn run_app(self, app: impl ApplicationHandler) -> Result<(), EventLoopError>
where
Self: Sized;
pub trait ActiveEventLoop: AsAny + fmt::Debug {
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread.
fn create_proxy(&self) -> EventLoopProxy;
/// Create the window.
/// Gets a persistent reference to the underlying platform display.
///
/// Possible causes of error include denied permission, incompatible system, and lack of memory.
/// See the [`OwnedDisplayHandle`] type for more information.
fn owned_display_handle(&self) -> OwnedDisplayHandle;
/// Change if or when [`DeviceEvent`]s are captured.
///
/// ## Platform-specific
/// See [`ActiveEventLoop::listen_device_events`] for details.
///
/// - **Web:** The window is created but not inserted into the Web page automatically. Please
/// see the Web platform module for more information.
fn create_window(
&self,
window_attributes: WindowAttributes,
) -> Result<Box<dyn Window>, RequestError>;
/// [`DeviceEvent`]: crate::event::DeviceEvent
fn listen_device_events(&self, allowed: DeviceEvents);
/// Sets the [`ControlFlow`].
fn set_control_flow(&self, control_flow: ControlFlow);
/// Create custom cursor.
///
@@ -43,179 +137,9 @@ pub trait ActiveEventLoop: AsAny + fmt::Debug {
&self,
custom_cursor: CustomCursorSource,
) -> Result<CustomCursor, RequestError>;
/// Returns the list of all the monitors available on the system.
///
/// ## Platform-specific
///
/// **Web:** Only returns the current monitor without `detailed monitor permissions`.
fn available_monitors(&self) -> Box<dyn Iterator<Item = MonitorHandle>>;
/// Returns the primary monitor of the system.
///
/// Returns `None` if it can't identify any monitor as a primary one.
///
/// ## Platform-specific
///
/// - **Wayland:** Always returns `None`.
/// - **Web:** Always returns `None` without `detailed monitor permissions`.
fn primary_monitor(&self) -> Option<MonitorHandle>;
/// Change if or when [`DeviceEvent`]s are captured.
///
/// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit
/// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing
/// this at runtime to explicitly capture them again.
///
/// ## Platform-specific
///
/// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
///
/// [`DeviceEvent`]: crate::event::DeviceEvent
fn listen_device_events(&self, allowed: DeviceEvents);
/// Returns the current system theme.
///
/// Returns `None` if it cannot be determined on the current platform.
///
/// ## Platform-specific
///
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
fn system_theme(&self) -> Option<Theme>;
/// Sets the [`ControlFlow`].
fn set_control_flow(&self, control_flow: ControlFlow);
/// Gets the current [`ControlFlow`].
fn control_flow(&self) -> ControlFlow;
/// Stop the event loop.
///
/// ## Platform-specific
///
/// ### iOS
///
/// It is not possible to programmatically exit/quit an application on iOS, so this function is
/// a no-op there. See also [this technical Q&A][qa1561].
///
/// [qa1561]: https://developer.apple.com/library/archive/qa/qa1561/_index.html
fn exit(&self);
/// Returns whether the [`ActiveEventLoop`] is about to stop.
///
/// Set by [`exit()`][Self::exit].
fn exiting(&self) -> bool;
/// Gets a persistent reference to the underlying platform display.
///
/// See the [`OwnedDisplayHandle`] type for more information.
fn owned_display_handle(&self) -> OwnedDisplayHandle;
/// Get the raw-window-handle handle.
fn rwh_06_handle(&self) -> &dyn HasDisplayHandle;
}
impl HasDisplayHandle for dyn ActiveEventLoop + '_ {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.rwh_06_handle().display_handle()
}
}
impl_dyn_casting!(ActiveEventLoop);
/// Control the [`ActiveEventLoop`], possibly from a different thread, without referencing it
/// directly.
#[derive(Clone, Debug)]
pub struct EventLoopProxy {
pub(crate) proxy: Arc<dyn EventLoopProxyProvider>,
}
impl EventLoopProxy {
/// Wake up the [`ActiveEventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being
/// called.
///
/// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the
/// documentation on that for details.
///
/// If the event loop is no longer running, this is a no-op.
///
/// [`proxy_wake_up`]: crate::application::ApplicationHandler::proxy_wake_up
/// [`ApplicationHandler::proxy_wake_up()`]: crate::application::ApplicationHandler::proxy_wake_up
///
/// # Platform-specific
///
/// - **Windows**: The wake-up may be ignored under high contention, see [#3687].
///
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
pub fn wake_up(&self) {
self.proxy.wake_up();
}
/// Get a [`Waker`] that calls [`wake_up`] on the proxy when awoken.
///
/// This may be useful to `async` code or otherwise interoperating with `std`.
pub fn waker(&self) -> Waker {
self.proxy.waker()
}
pub fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self {
Self { proxy }
}
}
pub trait EventLoopProxyProvider: Send + Sync + Debug {
/// See [`EventLoopProxy::wake_up`] for details.
fn wake_up(&self);
/// See [`EventLoopProxy::waker`] for details.
fn waker(&self) -> Waker;
}
/// A proxy for the underlying display handle.
///
/// The purpose of this type is to provide a cheaply cloneable 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 underlying event loop type or the [`ActiveEventLoop`]
/// 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 {
pub(crate) handle: Arc<dyn HasDisplayHandle>,
}
impl OwnedDisplayHandle {
pub fn new(handle: Arc<dyn HasDisplayHandle>) -> Self {
Self { handle }
}
}
impl HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.handle.display_handle()
}
}
impl fmt::Debug for OwnedDisplayHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
}
}
impl PartialEq for OwnedDisplayHandle {
fn eq(&self, other: &Self) -> bool {
match (self.display_handle(), other.display_handle()) {
(Ok(lhs), Ok(rhs)) => lhs == rhs,
_ => false,
}
}
}
impl Eq for OwnedDisplayHandle {}
impl_dyn_casting!(EventLoopProvider);
/// Set through [`ActiveEventLoop::set_control_flow()`].
///
@@ -262,6 +186,205 @@ impl ControlFlow {
}
}
pub trait ActiveEventLoop: AsAny + fmt::Debug {
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread.
fn create_proxy(&self) -> EventLoopProxy;
/// Create the window.
///
/// Possible causes of error include denied permission, incompatible system, and lack of memory.
///
/// ## Platform-specific
///
/// - **Web:** The window is created but not inserted into the Web page automatically. Please
/// see the Web platform module for more information.
fn create_window(
&self,
window_attributes: WindowAttributes,
) -> Result<Box<dyn Window>, RequestError>;
/// Create custom cursor.
///
/// ## Platform-specific
///
/// **iOS / Android / Orbital:** Unsupported.
fn create_custom_cursor(
&self,
custom_cursor: CustomCursorSource,
) -> Result<CustomCursor, RequestError>;
/// Returns the list of all the monitors available on the system.
///
/// ## Platform-specific
///
/// **Web:** Only returns the current monitor without
#[cfg_attr(
web_platform,
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")]
fn available_monitors(&self) -> Box<dyn Iterator<Item = MonitorHandle>>;
/// Returns the primary monitor of the system.
///
/// Returns `None` if it can't identify any monitor as a primary one.
///
/// ## Platform-specific
///
/// - **Wayland:** Always returns `None`.
/// - **Web:** Always returns `None` without
#[cfg_attr(
web_platform,
doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(web_platform), doc = " detailed monitor permissions.")]
fn primary_monitor(&self) -> Option<MonitorHandle>;
/// Change if or when [`DeviceEvent`]s are captured.
///
/// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit
/// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing
/// this at runtime to explicitly capture them again.
///
/// ## Platform-specific
///
/// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
///
/// [`DeviceEvent`]: crate::event::DeviceEvent
fn listen_device_events(&self, allowed: DeviceEvents);
/// Returns the current system theme.
///
/// Returns `None` if it cannot be determined on the current platform.
///
/// ## Platform-specific
///
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
fn system_theme(&self) -> Option<Theme>;
/// Sets the [`ControlFlow`].
fn set_control_flow(&self, control_flow: ControlFlow);
/// Gets the current [`ControlFlow`].
fn control_flow(&self) -> ControlFlow;
/// Stop the event loop.
///
/// ## Platform-specific
///
/// ### iOS
///
/// It is not possible to programmatically exit/quit an application on iOS, so this function is
/// a no-op there. See also [this technical Q&A][qa1561].
///
/// [qa1561]: https://developer.apple.com/library/archive/qa/qa1561/_index.html
fn exit(&self);
/// Returns whether the [`EventLoop`] is about to stop.
///
/// Set by [`exit()`][Self::exit].
fn exiting(&self) -> bool;
/// Gets a persistent reference to the underlying platform display.
///
/// See the [`OwnedDisplayHandle`] type for more information.
fn owned_display_handle(&self) -> OwnedDisplayHandle;
/// Get the raw-window-handle handle.
fn rwh_06_handle(&self) -> &dyn HasDisplayHandle;
}
impl HasDisplayHandle for dyn ActiveEventLoop + '_ {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.rwh_06_handle().display_handle()
}
}
impl_dyn_casting!(ActiveEventLoop);
/// A proxy for the underlying display handle.
///
/// The purpose of this type is to provide a cheaply cloneable 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 [`ActiveEventLoop`]
/// 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 {
pub(crate) handle: Arc<dyn HasDisplayHandle>,
}
impl OwnedDisplayHandle {
pub(crate) fn new(handle: Arc<dyn HasDisplayHandle>) -> Self {
Self { handle }
}
}
impl HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.handle.display_handle()
}
}
impl fmt::Debug for OwnedDisplayHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
}
}
impl PartialEq for OwnedDisplayHandle {
fn eq(&self, other: &Self) -> bool {
match (self.display_handle(), other.display_handle()) {
(Ok(lhs), Ok(rhs)) => lhs == rhs,
_ => false,
}
}
}
impl Eq for OwnedDisplayHandle {}
pub(crate) trait EventLoopProxyProvider: Send + Sync + fmt::Debug {
/// See [`EventLoopProxy::wake_up`] for details.
fn wake_up(&self);
}
/// Control the [`EventLoop`], possibly from a different thread, without referencing it directly.
#[derive(Clone, Debug)]
pub struct EventLoopProxy {
pub(crate) proxy: Arc<dyn EventLoopProxyProvider>,
}
impl EventLoopProxy {
/// Wake up the [`EventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being
/// called.
///
/// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the
/// documentation on that for details.
///
/// If the event loop is no longer running, this is a no-op.
///
/// [`proxy_wake_up`]: ApplicationHandler::proxy_wake_up
///
/// # Platform-specific
///
/// - **Windows**: The wake-up may be ignored under high contention, see [#3687].
///
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
pub fn wake_up(&self) {
self.proxy.wake_up();
}
pub(crate) fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self {
Self { proxy }
}
}
/// Control when device events are captured.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@@ -290,7 +413,9 @@ pub struct AsyncRequestSerial {
}
impl AsyncRequestSerial {
pub fn get() -> Self {
// TODO(kchibisov): Remove `cfg` when the clipboard will be added.
#[allow(dead_code)]
pub(crate) fn get() -> Self {
static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0);
// NOTE: We rely on wrap around here, while the user may just request
// in the loop usize::MAX times that's issue is considered on them.

View File

@@ -1,29 +1,30 @@
use std::error::Error;
use std::ops::Deref;
use std::sync::Arc;
use std::{fmt, io, mem};
use crate::as_any::{impl_dyn_casting, AsAny};
use crate::utils::{impl_dyn_casting, AsAny};
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<u32>();
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
/// An icon used for the window titlebar, taskbar, etc.
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Icon(pub Arc<dyn IconProvider>);
pub struct Icon(pub(crate) Arc<dyn IconProvider>);
// TODO remove that once split.
pub trait IconProvider: AsAny + fmt::Debug + Send + Sync {}
impl Deref for Icon {
type Target = dyn IconProvider;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl_dyn_casting!(IconProvider);
#[repr(C)]
#[derive(Debug)]
pub(crate) struct Pixel {
pub(crate) r: u8,
pub(crate) g: u8,
pub(crate) b: u8,
pub(crate) a: u8,
}
#[derive(Debug)]
/// An error produced when using [`RgbaIcon::new`] with invalid arguments.
pub enum BadIcon {
@@ -60,7 +61,7 @@ impl fmt::Display for BadIcon {
impl Error for BadIcon {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone)]
pub struct RgbaIcon {
pub(crate) width: u32,
pub(crate) height: u32,

1767
src/keyboard.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -26,20 +26,28 @@
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`].
//!
//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will dispatch events
//! for every [`Window`] that was created with that particular [`EventLoop`].
//! You can retrieve events by calling [`EventLoopProvider::run_app()`]. This function will dispatch
//! events for every [`Window`] that was created with that particular [`EventLoop`].
//!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
//! model, since that can't be implemented properly on some platforms (e.g Web, iOS) and works
//! poorly on most other platforms. However, this model can be re-implemented to an extent with
//! [`EventLoopExtPumpEvents::pump_app_events()`] [^1]. See that method's documentation for more
//! reasons about why it's discouraged beyond compatibility reasons.
#![cfg_attr(
any(windows_platform, macos_platform, android_platform, x11_platform, wayland_platform),
doc = "[`EventLoopExtPumpEvents::pump_app_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_app_events()]"
)]
#![cfg_attr(
not(any(windows_platform, macos_platform, android_platform, x11_platform, wayland_platform)),
doc = "`EventLoopExtPumpEvents::pump_app_events()`"
)]
//! [^1]. See that method's documentation for more reasons about why
//! it's discouraged beyond compatibility reasons.
//!
//!
//! ```no_run
//! use winit::application::ApplicationHandler;
//! use winit::event::WindowEvent;
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop, EventLoopProvider};
//! use winit::window::{Window, WindowId, WindowAttributes};
//!
//! #[derive(Default)]
@@ -61,7 +69,7 @@
//! id: WindowId,
//! event: WindowEvent,
//! ) {
//! // Called by `EventLoop::run_app` when a new event happens on the window.
//! // Called by `EventLoopProvider::run_app` when a new event happens on the window.
//! match event {
//! WindowEvent::CloseRequested => {
//! println!("The close button was pressed; stopping");
@@ -257,7 +265,7 @@
//!
//! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app
//! [`EventLoopProvider::run_app()`]: event_loop::EventLoopProvider::run_app
//! [`exit()`]: event_loop::ActiveEventLoop::exit
//! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId
@@ -268,7 +276,6 @@
//! [`DeviceEvent`]: event::DeviceEvent
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
//! [`EventLoopExtPumpEvents::pump_app_events()`]: crate::event_loop::pump_events::EventLoopExtPumpEvents::pump_app_events()
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland.
#![deny(rust_2018_idioms)]
@@ -282,19 +289,26 @@
#![allow(clippy::missing_safety_doc)]
#![warn(clippy::uninlined_format_args)]
// TODO: wasm-binding needs to be updated for that to be resolved, for now just silence it.
#![cfg_attr(web_platform, allow(unknown_lints, renamed_and_removed_lints, wasm_c_abi))]
#![cfg_attr(web_platform, allow(unknown_lints, wasm_c_abi))]
// Re-export DPI types so that users don't have to put it in Cargo.toml.
#[doc(inline)]
pub use dpi;
pub use rwh_06 as raw_window_handle;
pub mod application;
#[cfg(any(doc, doctest, test))]
pub mod changelog;
pub mod event_loop;
pub use winit_core::{application, cursor, error, event, icon, keyboard, monitor, window};
#[macro_use]
mod os_error;
pub mod error;
mod cursor;
pub mod event;
pub mod event_loop;
pub mod icon;
pub mod keyboard;
pub mod monitor;
mod platform_impl;
mod utils;
pub mod window;
pub mod platform;

View File

@@ -11,9 +11,8 @@ use std::num::{NonZeroU16, NonZeroU32};
use std::ops::Deref;
use std::sync::Arc;
use dpi::{PhysicalPosition, PhysicalSize};
use crate::as_any::{impl_dyn_casting, AsAny};
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::utils::{impl_dyn_casting, AsAny};
/// Handle to a monitor.
///
@@ -29,13 +28,24 @@ use crate::as_any::{impl_dyn_casting, AsAny};
///
/// ## Platform-specific
///
/// **Web:** A [`MonitorHandle`] created without `detailed monitor permissions`
/// **Web:** A [`MonitorHandle`] created without
#[cfg_attr(
web_platform,
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")]
/// will always represent the current monitor the browser window is in instead of a specific
/// monitor.
/// monitor. See
#[cfg_attr(
web_platform,
doc = "[`MonitorHandleExtWeb::is_detailed()`][crate::platform::web::MonitorHandleExtWeb::is_detailed]"
)]
#[cfg_attr(not(web_platform), doc = "`MonitorHandleExtWeb::is_detailed()`")]
/// to check.
///
/// [`Window`]: crate::window::Window
#[derive(Debug, Clone)]
pub struct MonitorHandle(pub Arc<dyn MonitorHandleProvider>);
pub struct MonitorHandle(pub(crate) Arc<dyn MonitorHandleProvider>);
impl Deref for MonitorHandle {
type Target = dyn MonitorHandleProvider;
@@ -77,10 +87,14 @@ pub trait MonitorHandleProvider: AsAny + fmt::Debug + Send + Sync {
///
/// Returns `None` if the monitor doesn't exist anymore or the name couldn't be obtained.
///
///
/// ## Platform-specific
///
/// **Web:** Always returns [`None`] without `detailed monitor permissions`.
/// **Web:** Always returns [`None`] without
#[cfg_attr(
web_platform,
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")]
fn name(&self) -> Option<Cow<'_, str>>;
/// Returns the top-left corner position of the monitor in desktop coordinates.
@@ -91,7 +105,12 @@ pub trait MonitorHandleProvider: AsAny + fmt::Debug + Send + Sync {
///
/// ## Platform-specific
///
/// **Web:** Always returns [`None`] without `detailed monitor permissions`.
/// **Web:** Always returns [`None`] without
#[cfg_attr(
web_platform,
doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")]
fn position(&self) -> Option<PhysicalPosition<i32>>;
/// Returns the scale factor of the underlying monitor. To map logical pixels to physical
@@ -99,9 +118,19 @@ pub trait MonitorHandleProvider: AsAny + fmt::Debug + Send + Sync {
///
/// See the [`dpi`] module for more information.
///
/// - **Wayland:** May differ from [`Window::scale_factor`].
/// - **Web:** Always returns `0.0` without `detailed_monitor_permissions`.
/// ## Platform-specific
///
/// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
/// - **Wayland:** May differ from [`Window::scale_factor`].
/// - **Android:** Always returns 1.0.
/// - **Web:** Always returns `0.0` without
#[cfg_attr(
web_platform,
doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]."
)]
#[cfg_attr(not(web_platform), doc = " detailed monitor permissions.")]
///
#[rustfmt::skip]
/// [`Window::scale_factor`]: crate::window::Window::scale_factor
fn scale_factor(&self) -> f64;
@@ -132,14 +161,6 @@ pub struct VideoMode {
}
impl VideoMode {
pub fn new(
size: PhysicalSize<u32>,
bit_depth: Option<NonZeroU16>,
refresh_rate_millihertz: Option<NonZeroU32>,
) -> Self {
Self { size, bit_depth, refresh_rate_millihertz }
}
/// Returns the resolution of this video mode. This **must not** be used to create your
/// rendering surface. Use [`Window::surface_size()`] instead.
///
@@ -151,13 +172,6 @@ impl VideoMode {
/// Returns the bit depth of this video mode, as in how many bits you have
/// available per color. This is generally 24 bits or 32 bits on modern
/// systems, depending on whether the alpha channel is counted or not.
///
/// # Platform-specific
///
/// - **macOS**: Video modes do not control the bit depth of the monitor, so this often defaults
/// to 32.
/// - **iOS**: Always returns `None`.
/// - **Wayland**: Always returns `None`.
pub fn bit_depth(&self) -> Option<NonZeroU16> {
self.bit_depth
}

View File

@@ -62,26 +62,17 @@
//! 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`
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.12",
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.10",
//! 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).
//! 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your
//! event loop (as shown above).
#![cfg(target_os = "android")]
mod event_loop;
mod keycodes;
use winit_core::event_loop::ActiveEventLoop as CoreActiveEventLoop;
use winit_core::window::Window as CoreWindow;
use self::activity::{AndroidApp, ConfigurationRef, Rect};
pub use crate::event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes,
PlatformSpecificWindowAttributes, Window,
};
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
use crate::window::{Window, WindowAttributes};
/// Additional methods on [`EventLoop`] that are specific to Android.
pub trait EventLoopExtAndroid {
@@ -89,6 +80,12 @@ pub trait EventLoopExtAndroid {
fn android_app(&self) -> &AndroidApp;
}
impl EventLoopExtAndroid for EventLoop {
fn android_app(&self) -> &AndroidApp {
&self.event_loop.android_app
}
}
/// Additional methods on [`ActiveEventLoop`] that are specific to Android.
pub trait ActiveEventLoopExtAndroid {
/// Get the [`AndroidApp`] which was used to create this event loop.
@@ -102,25 +99,30 @@ pub trait WindowExtAndroid {
fn config(&self) -> ConfigurationRef;
}
impl WindowExtAndroid for dyn CoreWindow + '_ {
impl WindowExtAndroid for dyn Window + '_ {
fn content_rect(&self) -> Rect {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.content_rect()
}
fn config(&self) -> ConfigurationRef {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.config()
}
}
impl ActiveEventLoopExtAndroid for dyn CoreActiveEventLoop + '_ {
impl ActiveEventLoopExtAndroid for dyn ActiveEventLoop + '_ {
fn android_app(&self) -> &AndroidApp {
let event_loop = self.cast_ref::<ActiveEventLoop>().unwrap();
let event_loop = self.cast_ref::<crate::platform_impl::ActiveEventLoop>().unwrap();
&event_loop.app
}
}
/// Additional methods on [`WindowAttributes`] that are specific to Android.
pub trait WindowAttributesExtAndroid {}
impl WindowAttributesExtAndroid for WindowAttributes {}
pub trait EventLoopBuilderExtAndroid {
/// Associates the [`AndroidApp`] that was passed to `android_main()` with the event loop
///
@@ -133,6 +135,18 @@ pub trait EventLoopBuilderExtAndroid {
fn handle_volume_keys(&mut self) -> &mut Self;
}
impl EventLoopBuilderExtAndroid for EventLoopBuilder {
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self {
self.platform_specific.android_app = Some(app);
self
}
fn handle_volume_keys(&mut self) -> &mut Self {
self.platform_specific.ignore_volume_keys = false;
self
}
}
/// Re-export of the `android_activity` API
///
/// Winit re-exports the `android_activity` API for convenience so that most
@@ -161,5 +175,16 @@ pub mod activity {
// feature enabled, so we avoid inlining it so that they're forced to view
// it on the crate's own docs.rs page.
#[doc(no_inline)]
#[cfg(android_platform)]
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;
}

193
src/platform/event_loop.rs Normal file
View File

@@ -0,0 +1,193 @@
use std::marker::PhantomData;
#[cfg(any(x11_platform, wayland_platform))]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::{AtomicBool, Ordering};
use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
use crate::application::ApplicationHandler;
use crate::cursor::{CustomCursor, CustomCursorSource};
use crate::error::{EventLoopError, RequestError};
use crate::event_loop::{
ControlFlow, DeviceEvents, EventLoopProvider, EventLoopProxy, OwnedDisplayHandle,
};
use crate::platform_impl;
/// Provides a way to retrieve events from the system and from the windows that were registered to
/// the events loop.
///
/// An `EventLoop` can be seen more or less as a "context". Calling [`EventLoop::new`]
/// initializes everything that will be required to create windows. For example on Linux creating
/// an event loop opens a connection to the X or Wayland server.
///
/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs.
///
/// Note that this cannot be shared across threads (due to platform-dependant logic
/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access,
/// the [`Window`] created from this _can_ be sent to an other thread, and the
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
///
/// [`Window`]: crate::window::Window
#[derive(Debug)]
pub struct EventLoop {
pub(crate) event_loop: platform_impl::EventLoop,
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
}
impl EventLoop {
/// Create the event loop.
///
/// This is an alias of `EventLoop::builder().build()`.
#[inline]
pub fn new() -> Result<EventLoop, EventLoopError> {
Self::builder().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 {
EventLoopBuilder { platform_specific: Default::default() }
}
}
impl EventLoopProvider for EventLoop {
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
fn run_app(self, app: impl ApplicationHandler) -> Result<(), EventLoopError> {
self.event_loop.run_app(app)
}
fn create_proxy(&self) -> EventLoopProxy {
self.event_loop.window_target().create_proxy()
}
fn owned_display_handle(&self) -> OwnedDisplayHandle {
self.event_loop.window_target().owned_display_handle()
}
fn listen_device_events(&self, allowed: DeviceEvents) {
let _span = tracing::debug_span!(
"winit::EventLoop::listen_device_events",
allowed = ?allowed
)
.entered();
self.event_loop.window_target().listen_device_events(allowed)
}
fn set_control_flow(&self, control_flow: ControlFlow) {
self.event_loop.window_target().set_control_flow(control_flow);
}
fn create_custom_cursor(
&self,
custom_cursor: CustomCursorSource,
) -> Result<CustomCursor, RequestError> {
self.event_loop.window_target().create_custom_cursor(custom_cursor)
}
}
impl HasDisplayHandle for EventLoop {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())
}
}
#[cfg(any(x11_platform, wayland_platform))]
impl AsFd for EventLoop {
/// Get the underlying [EventLoop]'s `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_app_events`] API.
///
/// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio
/// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events
fn as_fd(&self) -> BorrowedFd<'_> {
self.event_loop.as_fd()
}
}
#[cfg(any(x11_platform, wayland_platform))]
impl AsRawFd for EventLoop {
/// Get the underlying [EventLoop]'s raw `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_app_events`] API.
///
/// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio
/// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events
fn as_raw_fd(&self) -> RawFd {
self.event_loop.as_raw_fd()
}
}
/// Object that allows building the event loop.
///
/// This is used to make specifying options that affect the whole application
/// easier. But note that constructing multiple event loops is not supported.
///
/// This can be created using [`EventLoop::builder`].
#[derive(Default, Debug, PartialEq, Eq, Hash)]
pub struct EventLoopBuilder {
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
}
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl EventLoopBuilder {
/// Builds a new event loop.
///
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
/// and only once per application.***
///
/// Calling this function will result in display backend initialisation.
///
/// ## Panics
///
/// Attempting to create the event loop off the main thread will panic. This
/// restriction isn't strictly necessary on all platforms, but is imposed to
/// eliminate any nasty surprises when porting to platforms that require it.
/// `EventLoopBuilderExt::with_any_thread` functions are exposed in the relevant
/// [`platform`] module if the target platform supports creating an event
/// loop on any thread.
///
/// ## Platform-specific
///
/// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY` or
/// `DISPLAY` respectively when building the event loop.
/// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling
/// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic.
///
/// [`platform`]: crate::platform
#[cfg_attr(
android_platform,
doc = "[`.with_android_app(app)`]: \
crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
)]
#[cfg_attr(
not(android_platform),
doc = "[`.with_android_app(app)`]: #only-available-on-android"
)]
#[inline]
pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
return Err(EventLoopError::RecreationAttempt);
}
// Certain platforms accept a mutable reference in their API.
#[allow(clippy::unnecessary_mut_passed)]
Ok(EventLoop {
event_loop: platform_impl::EventLoop::new(&mut self.platform_specific)?,
_marker: PhantomData,
})
}
#[cfg(web_platform)]
pub(crate) fn allow_event_loop_recreation() {
EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
}
}

View File

@@ -1,4 +1,4 @@
//! # Winit's UIKit (iOS/tvOS/visionOS) backend
//! # iOS / UIKit
//!
//! Winit has [the same iOS version requirements as `rustc`][rustc-ios-version], although it's
//! frequently only tested on newer iOS versions.
@@ -98,26 +98,15 @@
//! register an application delegate, so you can set up a custom one in a nib file instead.
//!
//! [app-delegate]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate?language=objc
#![cfg(target_vendor = "apple")] // TODO: Remove once `objc2` allows compiling on all platforms
mod app_state;
mod event_loop;
mod monitor;
mod notification_center;
mod view;
mod view_controller;
mod window;
use std::os::raw::c_void;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use winit_core::monitor::{MonitorHandle, VideoMode};
use winit_core::window::{PlatformWindowAttributes, Window};
pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
use self::monitor::MonitorHandle as UIKitMonitorHandle;
use self::window::Window as UIKitWindow;
use crate::monitor::{MonitorHandle, VideoMode};
use crate::platform_impl::MonitorHandle as IosMonitorHandle;
use crate::window::{Window, WindowAttributes};
/// Additional methods on [`Window`] that are specific to iOS.
pub trait WindowExtIOS {
@@ -222,25 +211,25 @@ pub trait WindowExtIOS {
impl WindowExtIOS for dyn Window + '_ {
#[inline]
fn set_scale_factor(&self, scale_factor: f64) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.set_scale_factor(scale_factor));
}
#[inline]
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.set_valid_orientations(valid_orientations));
}
#[inline]
fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden));
}
#[inline]
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| {
w.set_preferred_screen_edges_deferring_system_gestures(edges)
});
@@ -248,19 +237,19 @@ impl WindowExtIOS for dyn Window + '_ {
#[inline]
fn set_prefers_status_bar_hidden(&self, hidden: bool) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.set_prefers_status_bar_hidden(hidden));
}
#[inline]
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
}
#[inline]
fn recognize_pinch_gesture(&self, should_recognize: bool) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
}
@@ -271,7 +260,7 @@ impl WindowExtIOS for dyn Window + '_ {
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| {
w.recognize_pan_gesture(
should_recognize,
@@ -283,29 +272,19 @@ impl WindowExtIOS for dyn Window + '_ {
#[inline]
fn recognize_doubletap_gesture(&self, should_recognize: bool) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
}
#[inline]
fn recognize_rotation_gesture(&self, should_recognize: bool) {
let window = self.cast_ref::<UIKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
}
}
/// Ios specific window attributes.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct WindowAttributesIos {
pub(crate) scale_factor: Option<f64>,
pub(crate) valid_orientations: ValidOrientations,
pub(crate) prefers_home_indicator_hidden: bool,
pub(crate) prefers_status_bar_hidden: bool,
pub(crate) preferred_status_bar_style: StatusBarStyle,
pub(crate) preferred_screen_edges_deferring_system_gestures: ScreenEdge,
}
impl WindowAttributesIos {
/// Additional methods on [`WindowAttributes`] that are specific to iOS.
pub trait WindowAttributesExtIOS {
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
///
/// The default value is device dependent, and it's recommended GLES or Metal applications set
@@ -314,10 +293,7 @@ impl WindowAttributesIos {
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
/// [`MonitorHandleProvider::scale_factor()`]: crate::monitor::MonitorHandleProvider::scale_factor()
pub fn with_scale_factor(mut self, scale_factor: f64) -> Self {
self.scale_factor = Some(scale_factor);
self
}
fn with_scale_factor(self, scale_factor: f64) -> Self;
/// Sets the valid orientations for the [`Window`].
///
@@ -325,10 +301,7 @@ impl WindowAttributesIos {
///
/// This sets the initial value returned by
/// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc).
pub fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self {
self.valid_orientations = valid_orientations;
self
}
fn with_valid_orientations(self, valid_orientations: ValidOrientations) -> Self;
/// Sets whether the [`Window`] prefers the home indicator hidden.
///
@@ -338,10 +311,7 @@ impl WindowAttributesIos {
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc).
///
/// This only has an effect on iOS 11.0+.
pub fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self {
self.prefers_home_indicator_hidden = hidden;
self
}
fn with_prefers_home_indicator_hidden(self, hidden: bool) -> Self;
/// Sets the screen edges for which the system gestures will take a lower priority than the
/// application's touch handling.
@@ -350,13 +320,7 @@ impl WindowAttributesIos {
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc).
///
/// This only has an effect on iOS 11.0+.
pub fn with_preferred_screen_edges_deferring_system_gestures(
mut self,
edges: ScreenEdge,
) -> Self {
self.preferred_screen_edges_deferring_system_gestures = edges;
self
}
fn with_preferred_screen_edges_deferring_system_gestures(self, edges: ScreenEdge) -> Self;
/// Sets whether the [`Window`] prefers the status bar hidden.
///
@@ -364,10 +328,7 @@ impl WindowAttributesIos {
///
/// This sets the initial value returned by
/// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc).
pub fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self {
self.prefers_status_bar_hidden = hidden;
self
}
fn with_prefers_status_bar_hidden(self, hidden: bool) -> Self;
/// Sets the style of the [`Window`]'s status bar.
///
@@ -375,15 +336,44 @@ impl WindowAttributesIos {
///
/// This sets the initial value returned by
/// [`-[UIViewController preferredStatusBarStyle]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc),
pub fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self {
self.preferred_status_bar_style = status_bar_style;
self
}
fn with_preferred_status_bar_style(self, status_bar_style: StatusBarStyle) -> Self;
}
impl PlatformWindowAttributes for WindowAttributesIos {
fn box_clone(&self) -> Box<dyn PlatformWindowAttributes> {
Box::from(self.clone())
impl WindowAttributesExtIOS for WindowAttributes {
#[inline]
fn with_scale_factor(mut self, scale_factor: f64) -> Self {
self.platform_specific.scale_factor = Some(scale_factor);
self
}
#[inline]
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self {
self.platform_specific.valid_orientations = valid_orientations;
self
}
#[inline]
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self {
self.platform_specific.prefers_home_indicator_hidden = hidden;
self
}
#[inline]
fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
self.platform_specific.preferred_screen_edges_deferring_system_gestures = edges;
self
}
#[inline]
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self {
self.platform_specific.prefers_status_bar_hidden = hidden;
self
}
#[inline]
fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self {
self.platform_specific.preferred_status_bar_style = status_bar_style;
self
}
}
@@ -405,13 +395,13 @@ impl MonitorHandleExtIOS for MonitorHandle {
fn ui_screen(&self) -> *mut c_void {
// SAFETY: The marker is only used to get the pointer of the screen
let mtm = unsafe { objc2::MainThreadMarker::new_unchecked() };
let monitor = self.cast_ref::<UIKitMonitorHandle>().unwrap();
let monitor = self.cast_ref::<IosMonitorHandle>().unwrap();
objc2::rc::Retained::as_ptr(monitor.ui_screen(mtm)) as *mut c_void
}
#[inline]
fn preferred_video_mode(&self) -> VideoMode {
let monitor = self.cast_ref::<UIKitMonitorHandle>().unwrap();
let monitor = self.cast_ref::<IosMonitorHandle>().unwrap();
monitor.preferred_video_mode()
}
}

View File

@@ -17,12 +17,13 @@
//! Instead, Winit guarantees that it will not register an application delegate, so the solution is
//! to register your own application delegate, as outlined in the following example (see
//! `objc2-app-kit` for more detailed information).
//! ```
#![cfg_attr(target_os = "macos", doc = "```")]
#![cfg_attr(not(target_os = "macos"), doc = "```ignore")]
//! use objc2::rc::Retained;
//! use objc2::runtime::ProtocolObject;
//! use objc2::{define_class, msg_send, DefinedClass, MainThreadMarker, MainThreadOnly};
//! use objc2_app_kit::{NSApplication, NSApplicationDelegate};
//! use objc2_foundation::{NSArray, NSObject, NSObjectProtocol, NSURL};
//! use objc2_foundation::{NSArray, NSURL, NSObject, NSObjectProtocol};
//! use winit::event_loop::EventLoop;
//!
//! define_class!(
@@ -63,40 +64,17 @@
//! Ok(())
//! }
//! ```
#![cfg(target_vendor = "apple")] // TODO: Remove once `objc2` allows compiling on all platforms
#[macro_use]
mod util;
mod app;
mod app_state;
mod cursor;
mod event;
mod event_loop;
mod ffi;
mod menu;
mod monitor;
mod notification_center;
mod observer;
mod view;
mod window;
mod window_delegate;
use std::os::raw::c_void;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[doc(inline)]
pub use winit_core::application::macos::ApplicationHandlerExtMacOS;
use winit_core::event_loop::ActiveEventLoop;
use winit_core::monitor::MonitorHandle;
use winit_core::window::{PlatformWindowAttributes, Window};
pub use self::event::{physicalkey_to_scancode, scancode_to_physicalkey};
use self::event_loop::ActiveEventLoop as AppKitActiveEventLoop;
pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
use self::monitor::MonitorHandle as AppKitMonitorHandle;
use self::window::Window as AppKitWindow;
use crate::application::ApplicationHandler;
use crate::event_loop::{ActiveEventLoop, EventLoopBuilder};
use crate::monitor::MonitorHandle;
use crate::platform_impl::MonitorHandle as MacOsMonitorHandle;
use crate::window::{Window, WindowAttributes, WindowId};
/// Additional methods on [`Window`] that are specific to MacOS.
pub trait WindowExtMacOS {
@@ -192,109 +170,109 @@ pub trait WindowExtMacOS {
impl WindowExtMacOS for dyn Window + '_ {
#[inline]
fn simple_fullscreen(&self) -> bool {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.simple_fullscreen())
}
#[inline]
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
}
#[inline]
fn has_shadow(&self) -> bool {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.has_shadow())
}
#[inline]
fn set_has_shadow(&self, has_shadow: bool) {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.set_has_shadow(has_shadow));
}
#[inline]
fn set_tabbing_identifier(&self, identifier: &str) {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
}
#[inline]
fn tabbing_identifier(&self) -> String {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.tabbing_identifier())
}
#[inline]
fn select_next_tab(&self) {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.select_next_tab());
}
#[inline]
fn select_previous_tab(&self) {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.select_previous_tab());
}
#[inline]
fn select_tab_at_index(&self, index: usize) {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.select_tab_at_index(index));
}
#[inline]
fn num_tabs(&self) -> usize {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.num_tabs())
}
#[inline]
fn is_document_edited(&self) -> bool {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.is_document_edited())
}
#[inline]
fn set_document_edited(&self, edited: bool) {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.set_document_edited(edited));
}
#[inline]
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(move |w| w.set_option_as_alt(option_as_alt));
}
#[inline]
fn option_as_alt(&self) -> OptionAsAlt {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.option_as_alt())
}
#[inline]
fn set_borderless_game(&self, borderless_game: bool) {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game))
}
#[inline]
fn is_borderless_game(&self) -> bool {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.is_borderless_game())
}
#[inline]
fn set_unified_titlebar(&self, unified_titlebar: bool) {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.set_unified_titlebar(unified_titlebar))
}
#[inline]
fn unified_titlebar(&self) -> bool {
let window = self.cast_ref::<AppKitWindow>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.unified_titlebar())
}
}
@@ -314,7 +292,7 @@ pub enum ActivationPolicy {
Prohibited,
}
/// Window attributes that are specific to MacOS.
/// Additional methods on [`WindowAttributes`] that are specific to MacOS.
///
/// **Note:** Properties dealing with the titlebar will be overwritten by the
/// [`WindowAttributes::with_decorations`] method:
@@ -323,158 +301,127 @@ pub enum ActivationPolicy {
/// - `with_titlebar_hidden`
/// - `with_titlebar_buttons_hidden`
/// - `with_fullsize_content_view`
///
/// [`WindowAttributes::with_decorations`]: crate::window::WindowAttributes::with_decorations
#[derive(Clone, Debug, PartialEq)]
pub struct WindowAttributesMacOS {
pub(crate) movable_by_window_background: bool,
pub(crate) titlebar_transparent: bool,
pub(crate) title_hidden: bool,
pub(crate) titlebar_hidden: bool,
pub(crate) titlebar_buttons_hidden: bool,
pub(crate) fullsize_content_view: bool,
pub(crate) disallow_hidpi: bool,
pub(crate) has_shadow: bool,
pub(crate) accepts_first_mouse: bool,
pub(crate) tabbing_identifier: Option<String>,
pub(crate) option_as_alt: OptionAsAlt,
pub(crate) borderless_game: bool,
pub(crate) unified_titlebar: bool,
pub(crate) panel: bool,
}
impl WindowAttributesMacOS {
pub trait WindowAttributesExtMacOS {
/// Enables click-and-drag behavior for the entire window, not just the titlebar.
#[inline]
pub fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self {
self.movable_by_window_background = movable_by_window_background;
self
}
fn with_movable_by_window_background(self, movable_by_window_background: bool) -> Self;
/// Makes the titlebar transparent and allows the content to appear behind it.
#[inline]
pub fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self {
self.titlebar_transparent = titlebar_transparent;
self
}
/// Hides the window titlebar.
#[inline]
pub fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self {
self.titlebar_hidden = titlebar_hidden;
self
}
/// Hides the window titlebar buttons.
#[inline]
pub fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self {
self.titlebar_buttons_hidden = titlebar_buttons_hidden;
self
}
fn with_titlebar_transparent(self, titlebar_transparent: bool) -> Self;
/// Hides the window title.
#[inline]
pub fn with_title_hidden(mut self, title_hidden: bool) -> Self {
self.title_hidden = title_hidden;
self
}
fn with_title_hidden(self, title_hidden: bool) -> Self;
/// Hides the window titlebar.
fn with_titlebar_hidden(self, titlebar_hidden: bool) -> Self;
/// Hides the window titlebar buttons.
fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> Self;
/// Makes the window content appear behind the titlebar.
#[inline]
pub fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self {
self.fullsize_content_view = fullsize_content_view;
self
}
#[inline]
pub fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self {
self.disallow_hidpi = disallow_hidpi;
self
}
#[inline]
pub fn with_has_shadow(mut self, has_shadow: bool) -> Self {
self.has_shadow = has_shadow;
self
}
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> Self;
fn with_disallow_hidpi(self, disallow_hidpi: bool) -> Self;
fn with_has_shadow(self, has_shadow: bool) -> Self;
/// Window accepts click-through mouse events.
#[inline]
pub fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self {
self.accepts_first_mouse = accepts_first_mouse;
self
}
fn with_accepts_first_mouse(self, accepts_first_mouse: bool) -> Self;
/// Defines the window tabbing identifier.
///
/// <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
#[inline]
pub fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
self.tabbing_identifier.replace(tabbing_identifier.to_string());
self
}
fn with_tabbing_identifier(self, identifier: &str) -> Self;
/// Set how the <kbd>Option</kbd> keys are interpreted.
///
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
#[inline]
pub fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self {
self.option_as_alt = option_as_alt;
self
}
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
/// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
#[inline]
pub fn with_borderless_game(mut self, borderless_game: bool) -> Self {
self.borderless_game = borderless_game;
self
}
fn with_borderless_game(self, borderless_game: bool) -> Self;
/// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set.
#[inline]
pub fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self {
self.unified_titlebar = unified_titlebar;
self
}
fn with_unified_titlebar(self, unified_titlebar: bool) -> Self;
/// Use [`NSPanel`] window with [`NonactivatingPanel`] window style mask instead of
/// [`NSWindow`].
///
/// [`NSWindow`]: https://developer.apple.com/documentation/appkit/NSWindow?language=objc
/// [`NSPanel`]: https://developer.apple.com/documentation/appkit/NSPanel?language=objc
/// [`NonactivatingPanel`]: https://developer.apple.com/documentation/appkit/nswindow/stylemask-swift.struct/nonactivatingpanel?language=objc
fn with_panel(self, panel: bool) -> Self;
}
impl WindowAttributesExtMacOS for WindowAttributes {
#[inline]
pub fn with_panel(mut self, panel: bool) -> Self {
self.panel = panel;
fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self {
self.platform_specific.movable_by_window_background = movable_by_window_background;
self
}
}
impl Default for WindowAttributesMacOS {
#[inline]
fn default() -> Self {
Self {
movable_by_window_background: false,
titlebar_transparent: false,
title_hidden: false,
titlebar_hidden: false,
titlebar_buttons_hidden: false,
fullsize_content_view: false,
disallow_hidpi: false,
has_shadow: true,
accepts_first_mouse: true,
tabbing_identifier: None,
option_as_alt: Default::default(),
borderless_game: false,
unified_titlebar: false,
panel: false,
}
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self {
self.platform_specific.titlebar_transparent = titlebar_transparent;
self
}
}
impl PlatformWindowAttributes for WindowAttributesMacOS {
fn box_clone(&self) -> Box<dyn PlatformWindowAttributes> {
Box::from(self.clone())
#[inline]
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self {
self.platform_specific.titlebar_hidden = titlebar_hidden;
self
}
#[inline]
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self {
self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
self
}
#[inline]
fn with_title_hidden(mut self, title_hidden: bool) -> Self {
self.platform_specific.title_hidden = title_hidden;
self
}
#[inline]
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self {
self.platform_specific.fullsize_content_view = fullsize_content_view;
self
}
#[inline]
fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self {
self.platform_specific.disallow_hidpi = disallow_hidpi;
self
}
#[inline]
fn with_has_shadow(mut self, has_shadow: bool) -> Self {
self.platform_specific.has_shadow = has_shadow;
self
}
#[inline]
fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self {
self.platform_specific.accepts_first_mouse = accepts_first_mouse;
self
}
#[inline]
fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
self.platform_specific.tabbing_identifier.replace(tabbing_identifier.to_string());
self
}
#[inline]
fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self {
self.platform_specific.option_as_alt = option_as_alt;
self
}
#[inline]
fn with_borderless_game(mut self, borderless_game: bool) -> Self {
self.platform_specific.borderless_game = borderless_game;
self
}
#[inline]
fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self {
self.platform_specific.unified_titlebar = unified_titlebar;
self
}
#[inline]
fn with_panel(mut self, panel: bool) -> Self {
self.platform_specific.panel = panel;
self
}
}
@@ -533,6 +480,26 @@ pub trait EventLoopBuilderExtMacOS {
fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self;
}
impl EventLoopBuilderExtMacOS for EventLoopBuilder {
#[inline]
fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self {
self.platform_specific.activation_policy = Some(activation_policy);
self
}
#[inline]
fn with_default_menu(&mut self, enable: bool) -> &mut Self {
self.platform_specific.default_menu = enable;
self
}
#[inline]
fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self {
self.platform_specific.activate_ignoring_other_apps = ignore;
self
}
}
/// Additional methods on [`MonitorHandle`] that are specific to MacOS.
pub trait MonitorHandleExtMacOS {
/// Returns a pointer to the NSScreen representing this monitor.
@@ -541,7 +508,7 @@ pub trait MonitorHandleExtMacOS {
impl MonitorHandleExtMacOS for MonitorHandle {
fn ns_screen(&self) -> Option<*mut c_void> {
let monitor = self.cast_ref::<AppKitMonitorHandle>().unwrap();
let monitor = self.cast_ref::<MacOsMonitorHandle>().unwrap();
// SAFETY: We only use the marker to get a pointer
let mtm = unsafe { objc2::MainThreadMarker::new_unchecked() };
monitor.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _)
@@ -566,26 +533,30 @@ pub trait ActiveEventLoopExtMacOS {
impl ActiveEventLoopExtMacOS for dyn ActiveEventLoop + '_ {
fn hide_application(&self) {
let event_loop =
self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.hide_application()
}
fn hide_other_applications(&self) {
let event_loop =
self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.hide_other_applications()
}
fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
let event_loop =
self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.set_allows_automatic_window_tabbing(enabled);
}
fn allows_automatic_window_tabbing(&self) -> bool {
let event_loop =
self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non macOS event loop on macOS");
event_loop.allows_automatic_window_tabbing()
}
}
@@ -609,3 +580,52 @@ pub enum OptionAsAlt {
#[default]
None,
}
/// Additional events on [`ApplicationHandler`] that are specific to macOS.
///
/// This can be registered with [`ApplicationHandler::macos_handler`].
pub trait ApplicationHandlerExtMacOS: ApplicationHandler {
/// The system interpreted a keypress as a standard key binding command.
///
/// Examples include inserting tabs and newlines, or moving the insertion point, see
/// [`NSStandardKeyBindingResponding`] for the full list of key bindings. They are often text
/// editing related.
///
/// This corresponds to the [`doCommandBySelector:`] method on `NSTextInputClient`.
///
/// The `action` parameter contains the string representation of the selector. Examples include
/// `"insertBacktab:"`, `"indent:"` and `"noop:"`.
///
/// # Example
///
/// ```ignore
/// impl ApplicationHandlerExtMacOS for App {
/// fn standard_key_binding(
/// &mut self,
/// event_loop: &dyn ActiveEventLoop,
/// window_id: WindowId,
/// action: &str,
/// ) {
/// match action {
/// "moveBackward:" => self.cursor.position -= 1,
/// "moveForward:" => self.cursor.position += 1,
/// _ => {} // Ignore other actions
/// }
/// }
/// }
/// ```
///
/// [`NSStandardKeyBindingResponding`]: https://developer.apple.com/documentation/appkit/nsstandardkeybindingresponding?language=objc
/// [`doCommandBySelector:`]: https://developer.apple.com/documentation/appkit/nstextinputclient/1438256-docommandbyselector?language=objc
#[doc(alias = "doCommandBySelector:")]
fn standard_key_binding(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
action: &str,
) {
let _ = event_loop;
let _ = window_id;
let _ = action;
}
}

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

@@ -0,0 +1,48 @@
//! Contains traits with platform-specific methods in them.
//!
//! Only the modules 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(web_platform)]
pub mod web;
#[cfg(windows_platform)]
pub mod windows;
#[cfg(x11_platform)]
pub mod x11;
#[allow(unused_imports)]
#[cfg(any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform,
docsrs,
))]
pub mod run_on_demand;
#[cfg(any(
windows_platform,
macos_platform,
android_platform,
x11_platform,
wayland_platform,
docsrs,
))]
pub mod pump_events;
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, docsrs))]
pub mod scancode;
pub mod event_loop;

6
src/platform/orbital.rs Normal file
View File

@@ -0,0 +1,6 @@
//! # Orbital / Redox OS
//!
//! Redox OS has some functionality not yet present that will be implemented
//! when its orbital display server provides it.
// There are no Orbital specific traits yet.

View File

@@ -1,8 +1,8 @@
use std::time::Duration;
use crate::application::ApplicationHandler;
use crate::event_loop::EventLoop;
#[allow(rustdoc::broken_intra_doc_links)] // FIXME(madsmtm): Fix these.
/// Additional methods on [`EventLoop`] for pumping events within an external event loop
pub trait EventLoopExtPumpEvents {
/// Pump the `EventLoop` to check for and dispatch pending events.
@@ -99,11 +99,23 @@ pub trait EventLoopExtPumpEvents {
/// If you render outside of Winit you are likely to see window resizing artifacts
/// since MacOS expects applications to render synchronously during any `drawRect`
/// callback.
fn pump_app_events<A: ApplicationHandler>(
fn pump_app_events(
&mut self,
timeout: Option<Duration>,
app: A,
) -> PumpStatus;
app: impl ApplicationHandler,
) -> PumpStatus
where
Self: Sized;
}
impl EventLoopExtPumpEvents for EventLoop {
fn pump_app_events(
&mut self,
timeout: Option<Duration>,
app: impl ApplicationHandler,
) -> PumpStatus {
self.event_loop.pump_app_events(timeout, app)
}
}
/// The return status for `pump_events`

View File

@@ -1,18 +1,17 @@
use crate::application::ApplicationHandler;
use crate::error::EventLoopError;
use crate::event_loop::EventLoop;
#[cfg(doc)]
use crate::{
event_loop::{pump_events::EventLoopExtPumpEvents, ActiveEventLoop},
window::Window,
event_loop::ActiveEventLoop, platform::pump_events::EventLoopExtPumpEvents, window::Window,
};
#[allow(rustdoc::broken_intra_doc_links)] // FIXME(madsmtm): Fix these.
/// Additional methods on [`EventLoop`] to return control flow to the caller.
pub trait EventLoopExtRunOnDemand {
/// Run the application with the event loop on the calling thread.
///
/// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`)
/// closures and it is possible to return control back to the caller without
/// Unlike [`EventLoopProvider::run_app()`], this function accepts non-`'static` (i.e.
/// non-`move`) closures and it is possible to return control back to the caller without
/// consuming the `EventLoop` (by using [`exit()`]) and
/// so the event loop can be re-run after it has exit.
///
@@ -32,11 +31,17 @@ pub trait EventLoopExtRunOnDemand {
/// # Caveats
/// - This extension isn't available on all platforms, since it's not always possible to return
/// to the caller (specifically this is impossible on iOS and Web - though with the Web
/// backend it is possible to use `EventLoopExtWeb::spawn_app()`[^1] more than once instead).
/// backend it is possible to use
#[cfg_attr(
web_platform,
doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
)]
#[cfg_attr(not(web_platform), doc = " `EventLoopExtWeb::spawn_app()`")]
/// [^1] more than once instead).
/// - No [`Window`] state can be carried between separate runs of the event loop.
///
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you
/// specifically need the ability to re-run a single event loop more than once
/// You are strongly encouraged to use [`EventLoopProvider::run_app()`] for portability, unless
/// you specifically need the ability to re-run a single event loop more than once
///
/// # Supported Platforms
/// - Windows
@@ -55,5 +60,27 @@ pub trait EventLoopExtRunOnDemand {
///
/// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>;
/// [`EventLoopProvider::run_app()`]: crate::event_loop::EventLoopProvider::run_app
fn run_app_on_demand(&mut self, app: impl ApplicationHandler) -> Result<(), EventLoopError>
where
Self: Sized;
}
impl EventLoopExtRunOnDemand for EventLoop {
fn run_app_on_demand(&mut self, app: impl ApplicationHandler) -> Result<(), EventLoopError> {
self.event_loop.run_app_on_demand(app)
}
}
/// ```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

@@ -27,7 +27,7 @@ use crate::error::{NotSupportedError, RequestError};
use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial};
#[cfg(wayland_platform)]
use crate::platform::wayland::ActiveEventLoopExtWayland;
use crate::window::{ActivationToken, Window};
use crate::window::{ActivationToken, Window, WindowAttributes};
/// The variable which is used mostly on X11.
const X11_VAR: &str = "DESKTOP_STARTUP_ID";
@@ -80,7 +80,7 @@ impl WindowExtStartupNotify for dyn Window + '_ {
}
#[cfg(x11_platform)]
if let Some(window) = self.cast_ref::<crate::platform_impl::x11::Window>() {
if let Some(window) = self.cast_ref::<crate::platform_impl::x11::window::Window>() {
return window.request_activation_token();
}
@@ -88,6 +88,13 @@ impl WindowExtStartupNotify for dyn Window + '_ {
}
}
impl WindowAttributesExtStartupNotify for WindowAttributes {
fn with_activation_token(mut self, token: ActivationToken) -> Self {
self.platform_specific.activation_token = Some(token);
self
}
}
/// Remove the activation environment variables from the current process.
///
/// This is wise to do before running child processes,
@@ -101,7 +108,6 @@ pub fn reset_activation_token_env() {
///
/// This could be used before running daemon processes.
pub fn set_activation_token_env(token: ActivationToken) {
let token = token.into_raw();
env::set_var(X11_VAR, &token);
env::set_var(WAYLAND_VAR, token);
env::set_var(X11_VAR, &token.token);
env::set_var(WAYLAND_VAR, token.token);
}

111
src/platform/wayland.rs Normal file
View File

@@ -0,0 +1,111 @@
//! # Wayland
//!
//! **Note:** Windows don't appear on Wayland until you draw/present to them.
//!
//! By default, Winit loads system libraries using `dlopen`. This can be
//! disabled by disabling the `"wayland-dlopen"` cargo feature.
//!
//! ## Client-side decorations
//!
//! Winit provides client-side decorations by default, but the behaviour can
//! be controlled with the following feature flags:
//!
//! * `wayland-csd-adwaita` (default).
//! * `wayland-csd-adwaita-crossfont`.
//! * `wayland-csd-adwaita-notitle`.
use std::ffi::c_void;
use std::ptr::NonNull;
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
use crate::platform_impl::wayland::Window;
pub use crate::window::Theme;
use crate::window::{Window as CoreWindow, WindowAttributes};
/// Additional methods on [`ActiveEventLoop`] that are specific to Wayland.
pub trait ActiveEventLoopExtWayland {
/// True if the [`ActiveEventLoop`] uses Wayland.
fn is_wayland(&self) -> bool;
}
impl ActiveEventLoopExtWayland for dyn ActiveEventLoop + '_ {
#[inline]
fn is_wayland(&self) -> bool {
self.cast_ref::<crate::platform_impl::wayland::ActiveEventLoop>().is_some()
}
}
/// Additional methods on [`EventLoop`] that are specific to Wayland.
pub trait EventLoopExtWayland {
/// True if the [`EventLoop`] uses Wayland.
fn is_wayland(&self) -> bool;
}
impl EventLoopExtWayland for EventLoop {
#[inline]
fn is_wayland(&self) -> bool {
self.event_loop.is_wayland()
}
}
/// Additional methods on [`EventLoopBuilder`] that are specific to Wayland.
pub trait EventLoopBuilderExtWayland {
/// Force using Wayland.
fn with_wayland(&mut self) -> &mut Self;
/// Whether to allow the event loop to be created off of the main thread.
///
/// By default, the window is only allowed to be created on the main
/// thread, to make platform compatibility easier.
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
}
impl EventLoopBuilderExtWayland for EventLoopBuilder {
#[inline]
fn with_wayland(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland);
self
}
#[inline]
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
self.platform_specific.any_thread = any_thread;
self
}
}
/// Additional methods on [`Window`] that are specific to Wayland.
///
/// [`Window`]: crate::window::Window
pub trait WindowExtWayland {
/// Returns `xdg_toplevel` of the window or [`None`] if the window is X11 window.
fn xdg_toplevel(&self) -> Option<NonNull<c_void>>;
}
impl WindowExtWayland for dyn CoreWindow + '_ {
#[inline]
fn xdg_toplevel(&self) -> Option<NonNull<c_void>> {
self.cast_ref::<Window>()?.xdg_toplevel()
}
}
/// Additional methods on [`WindowAttributes`] that are specific to Wayland.
pub trait WindowAttributesExtWayland {
/// Build window with the given name.
///
/// The `general` name sets an application ID, which should match the `.desktop`
/// file distributed with your program. The `instance` is a `no-op`.
///
/// 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)
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;
}
impl WindowAttributesExtWayland for WindowAttributes {
#[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.platform_specific.name =
Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into()));
self
}
}

View File

@@ -13,10 +13,10 @@
//! yourself.
//!
//! [canvas]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement
//! [with_canvas]: WindowAttributesWeb::with_canvas
//! [with_canvas]: WindowAttributesExtWeb::with_canvas
//! [get]: WindowExtWeb::canvas
//! [insert]: WindowAttributesWeb::with_append
//! [wasm_bindgen]: https://docs.rs/wasm-bindgen
//! [insert]: WindowAttributesExtWeb::with_append
#![cfg_attr(not(web_platform), doc = "[wasm_bindgen]: https://docs.rs/wasm-bindgen")]
//! [Rust and WebAssembly book]: https://rustwasm.github.io/book
//!
//! ## CSS properties
@@ -41,41 +41,6 @@
//! [`WindowEvent::PointerLeft`]: crate::event::WindowEvent::PointerLeft
//! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position
// Brief introduction to the internals of the Web backend:
// The Web backend used to support both wasm-bindgen and stdweb as methods of binding to the
// environment. Because they are both supporting the same underlying APIs, the actual Web bindings
// are cordoned off into backend abstractions, which present the thinnest unifying layer possible.
//
// When adding support for new events or interactions with the browser, first consult trusted
// documentation (such as MDN) to ensure it is well-standardised and supported across many browsers.
// Once you have decided on the relevant Web APIs, add support to both backends.
//
// The backend is used by the rest of the module to implement Winit's business logic, which forms
// the rest of the code. 'device', 'error', 'monitor', and 'window' define Web-specific structures
// for winit's cross-platform structures. They are all relatively simple translations.
//
// The event_loop module handles listening for and processing events. 'Proxy' implements
// EventLoopProxy and 'WindowTarget' implements ActiveEventLoop. WindowTarget also handles
// registering the event handlers. The 'Execution' struct in the 'runner' module handles taking
// incoming events (from the registered handlers) and ensuring they are passed to the user in a
// compliant way.
macro_rules! os_error {
($error:expr) => {{
winit_core::error::OsError::new(line!(), file!(), $error)
}};
}
mod r#async;
mod cursor;
mod event;
pub(crate) mod event_loop;
mod lock;
pub(crate) mod main_thread;
mod monitor;
pub(crate) mod web_sys;
pub(crate) mod window;
use std::cell::Ref;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
@@ -84,27 +49,29 @@ use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use ::web_sys::HtmlCanvasElement;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor, CustomCursorSource};
use winit_core::error::NotSupportedError;
use winit_core::event_loop::ActiveEventLoop;
use winit_core::monitor::MonitorHandleProvider;
use winit_core::window::{PlatformWindowAttributes, Window};
#[cfg(web_platform)]
use web_sys::HtmlCanvasElement;
pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
use self::web_sys as backend;
use self::window::Window as WebWindow;
use crate::cursor::CustomCursorFuture as PlatformCustomCursorFuture;
use crate::event_loop::ActiveEventLoop as WebActiveEventLoop;
use crate::main_thread::{MainThreadMarker, MainThreadSafe};
use crate::monitor::{
use crate::application::ApplicationHandler;
use crate::cursor::CustomCursorSource;
use crate::error::NotSupportedError;
use crate::event_loop::{ActiveEventLoop, EventLoop};
use crate::monitor::MonitorHandleProvider;
use crate::platform_impl::MonitorHandle as WebMonitorHandle;
#[cfg(web_platform)]
use crate::platform_impl::{
CustomCursorFuture as PlatformCustomCursorFuture,
HasMonitorPermissionFuture as PlatformHasMonitorPermissionFuture,
MonitorHandle as WebMonitorHandle, MonitorPermissionFuture as PlatformMonitorPermissionFuture,
MonitorPermissionFuture as PlatformMonitorPermissionFuture,
OrientationLockFuture as PlatformOrientationLockFuture,
};
use crate::window::{CustomCursor, Window, WindowAttributes};
#[cfg(not(web_platform))]
#[doc(hidden)]
pub struct HtmlCanvasElement;
pub trait WindowExtWeb {
/// Only returns the canvas if called from inside the window context (the
@@ -138,51 +105,37 @@ pub trait WindowExtWeb {
impl WindowExtWeb for dyn Window + '_ {
#[inline]
fn canvas(&self) -> Option<Ref<'_, HtmlCanvasElement>> {
self.cast_ref::<WebWindow>().expect("non Web window on Web").canvas()
self.cast_ref::<crate::platform_impl::Window>().expect("non Web window on Web").canvas()
}
fn prevent_default(&self) -> bool {
self.cast_ref::<WebWindow>().expect("non Web window on Web").prevent_default()
self.cast_ref::<crate::platform_impl::Window>()
.expect("non Web window on Web")
.prevent_default()
}
fn set_prevent_default(&self, prevent_default: bool) {
self.cast_ref::<WebWindow>()
self.cast_ref::<crate::platform_impl::Window>()
.expect("non Web window on Web")
.set_prevent_default(prevent_default)
}
fn is_cursor_lock_raw(&self) -> bool {
self.cast_ref::<WebWindow>().expect("non Web window on Web").is_cursor_lock_raw()
self.cast_ref::<crate::platform_impl::Window>()
.expect("non Web window on Web")
.is_cursor_lock_raw()
}
}
#[derive(Clone, Debug)]
pub struct WindowAttributesWeb {
pub(crate) canvas: Option<Arc<MainThreadSafe<backend::RawCanvasType>>>,
pub(crate) prevent_default: bool,
pub(crate) focusable: bool,
pub(crate) append: bool,
}
impl WindowAttributesWeb {
pub trait WindowAttributesExtWeb {
/// Pass an [`HtmlCanvasElement`] to be used for this [`Window`]. If [`None`],
/// the default one will be created.
/// [`WindowAttributes::default()`] will create one.
///
/// In any case, the canvas won't be automatically inserted into the Web page.
///
/// [`None`] by default.
pub fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
match canvas {
Some(canvas) => {
let main_thread = MainThreadMarker::new()
.expect("received a `HtmlCanvasElement` outside the window context");
self.canvas = Some(Arc::new(MainThreadSafe::new(main_thread, canvas)));
},
None => self.canvas = None,
}
self
}
#[cfg_attr(not(web_platform), doc = "", doc = "[`HtmlCanvasElement`]: #only-available-on-wasm")]
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
/// Sets whether `event.preventDefault()` should be called on events on the
/// canvas that have side effects.
@@ -190,50 +143,39 @@ impl WindowAttributesWeb {
/// See [`WindowExtWeb::set_prevent_default()`] for more details.
///
/// Enabled by default.
pub fn with_prevent_default(mut self, prevent_default: bool) -> Self {
self.prevent_default = prevent_default;
self
}
fn with_prevent_default(self, prevent_default: bool) -> Self;
/// Whether the canvas should be focusable using the tab key. This is necessary to capture
/// canvas keyboard events.
///
/// Enabled by default.
pub fn with_focusable(mut self, focusable: bool) -> Self {
self.focusable = focusable;
self
}
fn with_focusable(self, focusable: bool) -> Self;
/// On window creation, append the canvas element to the Web page if it isn't already.
///
/// Disabled by default.
pub fn with_append(mut self, append: bool) -> Self {
self.append = append;
fn with_append(self, append: bool) -> Self;
}
impl WindowAttributesExtWeb for WindowAttributes {
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
self.platform_specific.set_canvas(canvas);
self
}
}
impl PlatformWindowAttributes for WindowAttributesWeb {
fn box_clone(&self) -> Box<dyn PlatformWindowAttributes> {
Box::from(self.clone())
fn with_prevent_default(mut self, prevent_default: bool) -> Self {
self.platform_specific.prevent_default = prevent_default;
self
}
}
impl PartialEq for WindowAttributesWeb {
fn eq(&self, other: &Self) -> bool {
(match (&self.canvas, &other.canvas) {
(Some(this), Some(other)) => Arc::ptr_eq(this, other),
(None, None) => true,
_ => false,
}) && self.prevent_default.eq(&other.prevent_default)
&& self.focusable.eq(&other.focusable)
&& self.append.eq(&other.append)
fn with_focusable(mut self, focusable: bool) -> Self {
self.platform_specific.focusable = focusable;
self
}
}
impl Default for WindowAttributesWeb {
fn default() -> Self {
Self { canvas: None, prevent_default: true, focusable: true, append: false }
fn with_append(mut self, append: bool) -> Self {
self.platform_specific.append = append;
self
}
}
@@ -242,9 +184,9 @@ pub trait EventLoopExtWeb {
/// Initializes the winit event loop.
///
/// Unlike
#[cfg_attr(target_feature = "exception-handling", doc = "`run_app()`")]
#[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run_app()`")]
#[cfg_attr(
not(target_feature = "exception-handling"),
not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run_app()`]"
)]
/// [^1], this returns immediately, and doesn't throw an exception in order to
@@ -257,8 +199,8 @@ pub trait EventLoopExtWeb {
#[rustfmt::skip]
///
#[cfg_attr(
not(target_feature = "exception-handling"),
doc = "[`run_app()`]: EventLoop::run_app()"
not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run_app()`]: crate::event_loop::EventLoopProvider::run_app()"
)]
/// [^1]: `run_app()` is _not_ available on Wasm when the target supports `exception-handling`.
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A);
@@ -320,6 +262,40 @@ pub trait EventLoopExtWeb {
fn has_detailed_monitor_permission(&self) -> HasMonitorPermissionFuture;
}
impl EventLoopExtWeb for EventLoop {
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A) {
self.event_loop.spawn_app(app);
}
fn set_poll_strategy(&self, strategy: PollStrategy) {
self.event_loop.set_poll_strategy(strategy);
}
fn poll_strategy(&self) -> PollStrategy {
self.event_loop.poll_strategy()
}
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
self.event_loop.set_wait_until_strategy(strategy);
}
fn wait_until_strategy(&self) -> WaitUntilStrategy {
self.event_loop.wait_until_strategy()
}
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError> {
self.event_loop.has_multiple_screens()
}
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
MonitorPermissionFuture(self.event_loop.request_detailed_monitor_permission())
}
fn has_detailed_monitor_permission(&self) -> HasMonitorPermissionFuture {
HasMonitorPermissionFuture(self.event_loop.has_detailed_monitor_permission())
}
}
pub trait ActiveEventLoopExtWeb {
/// Sets the strategy for [`ControlFlow::Poll`].
///
@@ -388,55 +364,73 @@ pub trait ActiveEventLoopExtWeb {
impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ {
#[inline]
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture {
let event_loop = self.cast_ref::<WebActiveEventLoop>().expect("non Web event loop on Web");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.create_custom_cursor_async(source)
}
#[inline]
fn set_poll_strategy(&self, strategy: PollStrategy) {
let event_loop = self.cast_ref::<WebActiveEventLoop>().expect("non Web event loop on Web");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.set_poll_strategy(strategy);
}
#[inline]
fn poll_strategy(&self) -> PollStrategy {
let event_loop = self.cast_ref::<WebActiveEventLoop>().expect("non Web event loop on Web");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.poll_strategy()
}
#[inline]
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
let event_loop = self.cast_ref::<WebActiveEventLoop>().expect("non Web event loop on Web");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.set_wait_until_strategy(strategy);
}
#[inline]
fn wait_until_strategy(&self) -> WaitUntilStrategy {
let event_loop = self.cast_ref::<WebActiveEventLoop>().expect("non Web event loop on Web");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.wait_until_strategy()
}
#[inline]
fn is_cursor_lock_raw(&self) -> bool {
let event_loop = self.cast_ref::<WebActiveEventLoop>().expect("non Web event loop on Web");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.is_cursor_lock_raw()
}
#[inline]
fn has_multiple_screens(&self) -> Result<bool, NotSupportedError> {
let event_loop = self.cast_ref::<WebActiveEventLoop>().expect("non Web event loop on Web");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.has_multiple_screens()
}
#[inline]
fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
let event_loop = self.cast_ref::<WebActiveEventLoop>().expect("non Web event loop on Web");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
MonitorPermissionFuture(event_loop.request_detailed_monitor_permission())
}
#[inline]
fn has_detailed_monitor_permission(&self) -> bool {
let event_loop = self.cast_ref::<WebActiveEventLoop>().expect("non Web event loop on Web");
let event_loop = self
.cast_ref::<crate::platform_impl::ActiveEventLoop>()
.expect("non Web event loop on Web");
event_loop.has_detailed_monitor_permission()
}
}
@@ -492,6 +486,9 @@ pub enum WaitUntilStrategy {
Worker,
}
#[cfg(not(web_platform))]
struct PlatformCustomCursorFuture;
#[derive(Debug)]
pub struct CustomCursorFuture(pub(crate) PlatformCustomCursorFuture);
@@ -521,6 +518,9 @@ impl Display for CustomCursorError {
impl Error for CustomCursorError {}
#[cfg(not(web_platform))]
struct PlatformMonitorPermissionFuture;
/// Can be dropped without aborting the request for detailed monitor permissions.
#[derive(Debug)]
pub struct MonitorPermissionFuture(pub(crate) PlatformMonitorPermissionFuture);
@@ -563,6 +563,9 @@ impl Display for MonitorPermissionError {
impl Error for MonitorPermissionError {}
#[cfg(not(web_platform))]
struct PlatformHasMonitorPermissionFuture;
#[derive(Debug)]
pub struct HasMonitorPermissionFuture(PlatformHasMonitorPermissionFuture);
@@ -678,6 +681,9 @@ pub enum OrientationLock {
},
}
#[cfg(not(web_platform))]
struct PlatformOrientationLockFuture;
/// Can be dropped without aborting the request to lock the screen.
#[derive(Debug)]
pub struct OrientationLockFuture(PlatformOrientationLockFuture);

View File

@@ -1,44 +1,24 @@
//! # Winit Win32 / Windows backend
//! # Windows
//!
//! The supported OS version is Windows 7 or higher, though Windows 10 is
//! tested regularly.
#![cfg(target_os = "windows")] // FIXME(madsmtm): Allow compiling on all platforms.
#[macro_use]
mod util;
mod dark_mode;
mod definitions;
mod dpi;
mod drop_handler;
mod event_loop;
mod icon;
mod ime;
mod keyboard;
mod keyboard_layout;
mod monitor;
mod raw_input;
mod window;
mod window_state;
use std::borrow::Borrow;
use std::ffi::c_void;
use std::ops::Deref;
use std::path::Path;
use std::sync::Arc;
use ::dpi::PhysicalSize;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(windows_platform)]
use windows_sys::Win32::Foundation::HANDLE;
use winit_core::event::DeviceId;
use winit_core::icon::{BadIcon, Icon};
use winit_core::window::{PlatformWindowAttributes, Window as CoreWindow};
pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
use self::icon::{RaiiIcon, SelectedCursor};
pub use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey};
pub use self::monitor::{MonitorHandle, VideoModeHandle};
pub use self::window::Window;
use crate::dpi::PhysicalSize;
use crate::event::DeviceId;
use crate::event_loop::EventLoopBuilder;
use crate::icon::BadIcon;
use crate::platform_impl::RaiiIcon;
use crate::window::{Icon, Window, WindowAttributes};
/// Window Handle type used by Win32 API
pub type HWND = *mut c_void;
@@ -140,24 +120,24 @@ pub enum CornerPreference {
///
/// See [`WindowBorrowExtWindows::any_thread`] for more information.
#[derive(Clone, Debug)]
pub struct AnyThread<W: CoreWindow>(W);
pub struct AnyThread<W: Window>(W);
impl<W: CoreWindow> AnyThread<W> {
impl<W: Window> AnyThread<W> {
/// Get a reference to the inner window.
#[inline]
pub fn get_ref(&self) -> &dyn CoreWindow {
pub fn get_ref(&self) -> &dyn Window {
&self.0
}
}
impl<W: CoreWindow> Deref for AnyThread<W> {
impl<W: Window> Deref for AnyThread<W> {
type Target = W;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<W: CoreWindow> rwh_06::HasWindowHandle for AnyThread<W> {
impl<W: Window> rwh_06::HasWindowHandle for AnyThread<W> {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
// SAFETY: The top level user has asserted this is only used safely.
unsafe { self.get_ref().window_handle_any_thread() }
@@ -232,13 +212,36 @@ pub trait EventLoopBuilderExtWindows {
F: FnMut(*const c_void) -> bool + 'static;
}
impl EventLoopBuilderExtWindows for EventLoopBuilder {
#[inline]
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
self.platform_specific.any_thread = any_thread;
self
}
#[inline]
fn with_dpi_aware(&mut self, dpi_aware: bool) -> &mut Self {
self.platform_specific.dpi_aware = dpi_aware;
self
}
#[inline]
fn with_msg_hook<F>(&mut self, callback: F) -> &mut Self
where
F: FnMut(*const c_void) -> bool + 'static,
{
self.platform_specific.msg_hook = Some(Box::new(callback));
self
}
}
/// Additional methods on `Window` that are specific to Windows.
pub trait WindowExtWindows {
/// Enables or disables mouse and keyboard input to the specified window.
///
/// A window must be enabled before it can be activated.
/// If an application has create a modal dialog box by disabling its owner window
/// (as described in [`WindowAttributesWindows::with_owner_window`]), the application must
/// (as described in [`WindowAttributesExtWindows::with_owner_window`]), the application must
/// enable the owner window before destroying the dialog box.
/// Otherwise, another window will receive the keyboard focus and be activated.
///
@@ -285,15 +288,6 @@ pub trait WindowExtWindows {
/// Supported starting with Windows 11 Build 22000.
fn set_corner_preference(&self, preference: CornerPreference);
/// Sets if the reported [`winit_core::event::WindowEvent::MouseWheel`] event
/// should account for scroll speed system settings.
///
/// The default scroll speed on Windows is 3 lines/characters per scroll,
/// this will be 1 if you set it to false.
///
/// The default is `true`.
fn set_use_system_scroll_speed(&self, should_use: bool);
/// Get the raw window handle for this [`Window`] without checking for thread affinity.
///
/// Window handles in Win32 have a property called "thread affinity" that ties them to their
@@ -349,40 +343,40 @@ pub trait WindowExtWindows {
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError>;
}
impl WindowExtWindows for dyn CoreWindow + '_ {
impl WindowExtWindows for dyn Window + '_ {
#[inline]
fn set_enable(&self, enabled: bool) {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.set_enable(enabled)
}
#[inline]
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.set_taskbar_icon(taskbar_icon)
}
#[inline]
fn set_skip_taskbar(&self, skip: bool) {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.set_skip_taskbar(skip)
}
#[inline]
fn set_undecorated_shadow(&self, shadow: bool) {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.set_undecorated_shadow(shadow)
}
#[inline]
fn set_system_backdrop(&self, backdrop_type: BackdropType) {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.set_system_backdrop(backdrop_type)
}
#[inline]
fn set_border_color(&self, color: Option<Color>) {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.set_border_color(color.unwrap_or(Color::NONE))
}
@@ -391,31 +385,26 @@ impl WindowExtWindows for dyn CoreWindow + '_ {
// 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"
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.set_title_background_color(color.unwrap_or(Color::NONE))
}
#[inline]
fn set_title_text_color(&self, color: Color) {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.set_title_text_color(color)
}
#[inline]
fn set_corner_preference(&self, preference: CornerPreference) {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
window.set_corner_preference(preference)
}
fn set_use_system_scroll_speed(&self, should_use: bool) {
let window = self.cast_ref::<Window>().unwrap();
window.set_use_system_scroll_speed(should_use)
}
unsafe fn window_handle_any_thread(
&self,
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let window = self.cast_ref::<Window>().unwrap();
let window = self.cast_ref::<crate::platform_impl::Window>().unwrap();
unsafe {
let handle = window.rwh_06_no_thread_check()?;
@@ -428,7 +417,7 @@ impl WindowExtWindows for dyn CoreWindow + '_ {
/// Additional methods for anything that dereference to [`Window`].
///
/// [`Window`]: crate::window::Window
pub trait WindowBorrowExtWindows: Borrow<dyn CoreWindow> + Sized {
pub trait WindowBorrowExtWindows: Borrow<dyn Window> + Sized {
/// Create an object that allows accessing the inner window handle in a thread-unsafe way.
///
/// It is possible to call [`window_handle_any_thread`] to get around Windows's thread
@@ -449,59 +438,17 @@ pub trait WindowBorrowExtWindows: Borrow<dyn CoreWindow> + Sized {
/// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread
unsafe fn any_thread(self) -> AnyThread<Self>
where
Self: CoreWindow,
Self: Window,
{
AnyThread(self)
}
}
impl<W: Borrow<dyn CoreWindow> + Sized> WindowBorrowExtWindows for W {}
impl<W: Borrow<dyn Window> + Sized> WindowBorrowExtWindows for W {}
#[derive(Clone, Debug)]
pub struct WindowAttributesWindows {
pub(crate) owner: Option<HWND>,
pub(crate) menu: Option<HMENU>,
pub(crate) taskbar_icon: Option<Icon>,
pub(crate) no_redirection_bitmap: bool,
pub(crate) drag_and_drop: bool,
pub(crate) skip_taskbar: bool,
pub(crate) class_name: String,
pub(crate) decoration_shadow: bool,
pub(crate) backdrop_type: BackdropType,
pub(crate) clip_children: bool,
pub(crate) border_color: Option<Color>,
pub(crate) title_background_color: Option<Color>,
pub(crate) title_text_color: Option<Color>,
pub(crate) corner_preference: Option<CornerPreference>,
pub(crate) use_system_wheel_speed: bool,
}
impl Default for WindowAttributesWindows {
fn default() -> Self {
Self {
owner: None,
menu: None,
taskbar_icon: None,
no_redirection_bitmap: false,
drag_and_drop: true,
skip_taskbar: false,
class_name: "Window Class".to_string(),
decoration_shadow: false,
backdrop_type: BackdropType::default(),
clip_children: true,
border_color: None,
title_background_color: None,
title_text_color: None,
corner_preference: None,
use_system_wheel_speed: true,
}
}
}
unsafe impl Send for WindowAttributesWindows {}
unsafe impl Sync for WindowAttributesWindows {}
impl WindowAttributesWindows {
/// Additional methods on `WindowAttributes` that are specific to Windows.
#[allow(rustdoc::broken_intra_doc_links)]
pub trait WindowAttributesExtWindows {
/// Set an owner to the window to be created. Can be used to create a dialog box, for example.
/// This only works when [`WindowAttributes::with_parent_window`] isn't called or set to `None`.
/// Can be used in combination with
@@ -514,12 +461,7 @@ impl WindowAttributesWindows {
/// - An owned window is hidden when its owner is minimized.
///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
///
/// [`WindowAttributes::with_parent_window`]: winit_core::window::WindowAttributes::with_parent_window
pub fn with_owner_window(mut self, parent: HWND) -> Self {
self.owner = Some(parent);
self
}
fn with_owner_window(self, parent: HWND) -> Self;
/// Sets a menu on the window to be created.
///
@@ -530,24 +472,18 @@ impl WindowAttributesWindows {
/// 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.
///
/// [`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
pub fn with_menu(mut self, menu: HMENU) -> Self {
self.menu = Some(menu);
self
}
#[cfg_attr(
windows_platform,
doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
)]
#[cfg_attr(not(windows_platform), doc = "[`CreateMenu`]: #only-available-on-windows")]
fn with_menu(self, menu: HMENU) -> Self;
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
pub fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self {
self.taskbar_icon = taskbar_icon;
self
}
fn with_taskbar_icon(self, taskbar_icon: Option<Icon>) -> Self;
/// This sets `WS_EX_NOREDIRECTIONBITMAP`.
pub fn with_no_redirection_bitmap(mut self, flag: bool) -> Self {
self.no_redirection_bitmap = flag;
self
}
fn with_no_redirection_bitmap(self, flag: bool) -> Self;
/// Enables or disables drag and drop support (enabled by default). Will interfere with other
/// crates that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED`
@@ -555,94 +491,132 @@ impl WindowAttributesWindows {
/// attempt to initialize COM API regardless of this option. Currently only fullscreen mode
/// does that, but there may be more in the future. If you need COM API with
/// `COINIT_MULTITHREADED` you must initialize it before calling any winit functions. See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information.
pub fn with_drag_and_drop(mut self, flag: bool) -> Self {
self.drag_and_drop = flag;
self
}
fn with_drag_and_drop(self, flag: bool) -> Self;
/// Whether show or hide the window icon in the taskbar.
pub fn with_skip_taskbar(mut self, skip: bool) -> Self {
self.skip_taskbar = skip;
self
}
fn with_skip_taskbar(self, skip: bool) -> Self;
/// Customize the window class name.
pub fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self {
self.class_name = class_name.into();
self
}
fn with_class_name<S: Into<String>>(self, class_name: S) -> Self;
/// Shows or hides the background drop shadow for undecorated windows.
///
/// The shadow is hidden by default.
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
pub fn with_undecorated_shadow(mut self, shadow: bool) -> Self {
self.decoration_shadow = shadow;
self
}
fn with_undecorated_shadow(self, shadow: bool) -> Self;
/// Sets system-drawn backdrop type.
///
/// Requires Windows 11 build 22523+.
pub fn with_system_backdrop(mut self, backdrop_type: BackdropType) -> Self {
self.backdrop_type = backdrop_type;
self
}
fn with_system_backdrop(self, backdrop_type: BackdropType) -> Self;
/// This sets or removes `WS_CLIPCHILDREN` style.
pub fn with_clip_children(mut self, flag: bool) -> Self {
self.clip_children = flag;
self
}
fn with_clip_children(self, flag: bool) -> Self;
/// Sets the color of the window border.
///
/// Supported starting with Windows 11 Build 22000.
pub fn with_border_color(mut self, color: Option<Color>) -> Self {
self.border_color = Some(color.unwrap_or(Color::NONE));
self
}
fn with_border_color(self, color: Option<Color>) -> Self;
/// Sets the background color of the title bar.
///
/// Supported starting with Windows 11 Build 22000.
pub fn with_title_background_color(mut self, color: Option<Color>) -> Self {
self.title_background_color = Some(color.unwrap_or(Color::NONE));
self
}
fn with_title_background_color(self, color: Option<Color>) -> Self;
/// Sets the color of the window title.
///
/// Supported starting with Windows 11 Build 22000.
pub fn with_title_text_color(mut self, color: Color) -> Self {
self.title_text_color = Some(color);
self
}
fn with_title_text_color(self, color: Color) -> Self;
/// Sets the preferred style of the window corners.
///
/// Supported starting with Windows 11 Build 22000.
pub fn with_corner_preference(mut self, corners: CornerPreference) -> Self {
self.corner_preference = Some(corners);
self
}
/// Sets if the reported [`winit_core::event::WindowEvent::MouseWheel`] event
/// should account for scroll speed system settings.
///
/// The default scroll speed on Windows is 3 lines/characters per scroll,
/// this will be 1 if you set it to false.
///
/// The default is `true`.
pub fn with_use_system_scroll_speed(mut self, should_use: bool) -> Self {
self.use_system_wheel_speed = should_use;
self
}
fn with_corner_preference(self, corners: CornerPreference) -> Self;
}
impl PlatformWindowAttributes for WindowAttributesWindows {
fn box_clone(&self) -> Box<dyn PlatformWindowAttributes> {
Box::from(self.clone())
impl WindowAttributesExtWindows for WindowAttributes {
#[inline]
fn with_owner_window(mut self, parent: HWND) -> Self {
self.platform_specific.owner = Some(parent);
self
}
#[inline]
fn with_menu(mut self, menu: HMENU) -> Self {
self.platform_specific.menu = Some(menu);
self
}
#[inline]
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> Self {
self.platform_specific.taskbar_icon = taskbar_icon;
self
}
#[inline]
fn with_no_redirection_bitmap(mut self, flag: bool) -> Self {
self.platform_specific.no_redirection_bitmap = flag;
self
}
#[inline]
fn with_drag_and_drop(mut self, flag: bool) -> Self {
self.platform_specific.drag_and_drop = flag;
self
}
#[inline]
fn with_skip_taskbar(mut self, skip: bool) -> Self {
self.platform_specific.skip_taskbar = skip;
self
}
#[inline]
fn with_class_name<S: Into<String>>(mut self, class_name: S) -> Self {
self.platform_specific.class_name = class_name.into();
self
}
#[inline]
fn with_undecorated_shadow(mut self, shadow: bool) -> Self {
self.platform_specific.decoration_shadow = shadow;
self
}
#[inline]
fn with_system_backdrop(mut self, backdrop_type: BackdropType) -> Self {
self.platform_specific.backdrop_type = backdrop_type;
self
}
#[inline]
fn with_clip_children(mut self, flag: bool) -> Self {
self.platform_specific.clip_children = flag;
self
}
#[inline]
fn with_border_color(mut self, color: Option<Color>) -> Self {
self.platform_specific.border_color = Some(color.unwrap_or(Color::NONE));
self
}
#[inline]
fn with_title_background_color(mut self, color: Option<Color>) -> Self {
self.platform_specific.title_background_color = Some(color.unwrap_or(Color::NONE));
self
}
#[inline]
fn with_title_text_color(mut self, color: Color) -> Self {
self.platform_specific.title_text_color = Some(color);
self
}
#[inline]
fn with_corner_preference(mut self, corners: CornerPreference) -> Self {
self.platform_specific.corner_preference = Some(corners);
self
}
}
@@ -654,11 +628,12 @@ pub trait DeviceIdExtWindows {
fn persistent_identifier(&self) -> Option<String>;
}
#[cfg(windows_platform)]
impl DeviceIdExtWindows for DeviceId {
fn persistent_identifier(&self) -> Option<String> {
let raw_id = self.into_raw();
if raw_id != 0 {
raw_input::get_raw_input_device_name(raw_id as HANDLE)
crate::platform_impl::raw_input::get_raw_input_device_name(raw_id as HANDLE)
} else {
None
}
@@ -758,7 +733,7 @@ impl WinIcon {
///
/// ```rust,no_run
/// # use winit::platform::windows::WinIcon;
/// # use winit::icon::Icon;
/// # use winit::window::Icon;
/// assert!(WinIcon::from_resource_name("0", None).is_ok());
/// assert!(WinIcon::from_resource(0, None).is_err());
/// ```

View File

@@ -1,32 +1,10 @@
//! # X11
use dpi::Size;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use winit_core::event_loop::ActiveEventLoop as CoreActiveEventLoop;
use winit_core::window::{ActivationToken, PlatformWindowAttributes, Window as CoreWindow};
pub use crate::event_loop::{ActiveEventLoop, EventLoop};
pub use crate::window::Window;
macro_rules! os_error {
($error:expr) => {{
winit_core::error::OsError::new(line!(), file!(), $error)
}};
}
mod activation;
mod atoms;
mod dnd;
mod event_loop;
mod event_processor;
pub mod ffi;
mod ime;
mod monitor;
mod util;
mod window;
mod xdisplay;
mod xsettings;
use crate::dpi::Size;
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
use crate::window::{Window as CoreWindow, WindowAttributes};
/// X window type. Maps directly to
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
@@ -101,21 +79,19 @@ pub type XWindow = u32;
#[inline]
pub fn register_xlib_error_hook(hook: XlibErrorHook) {
// Append new hook.
crate::event_loop::XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
crate::platform_impl::x11::XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
}
/// Additional methods on [`ActiveEventLoop`] that are specific to X11.
///
/// [`ActiveEventLoop`]: winit_core::event_loop::ActiveEventLoop
pub trait ActiveEventLoopExtX11 {
/// True if the event loop uses X11.
/// True if the [`ActiveEventLoop`] uses X11.
fn is_x11(&self) -> bool;
}
impl ActiveEventLoopExtX11 for dyn CoreActiveEventLoop + '_ {
impl ActiveEventLoopExtX11 for dyn ActiveEventLoop + '_ {
#[inline]
fn is_x11(&self) -> bool {
self.cast_ref::<ActiveEventLoop>().is_some()
self.cast_ref::<crate::platform_impl::x11::ActiveEventLoop>().is_some()
}
}
@@ -125,6 +101,13 @@ pub trait EventLoopExtX11 {
fn is_x11(&self) -> bool;
}
impl EventLoopExtX11 for EventLoop {
#[inline]
fn is_x11(&self) -> bool {
!self.event_loop.is_wayland()
}
}
/// Additional methods on [`EventLoopBuilder`] that are specific to X11.
pub trait EventLoopBuilderExtX11 {
/// Force using X11.
@@ -137,6 +120,20 @@ pub trait EventLoopBuilderExtX11 {
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
}
impl EventLoopBuilderExtX11 for EventLoopBuilder {
#[inline]
fn with_x11(&mut self) -> &mut Self {
self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X);
self
}
#[inline]
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
self.platform_specific.any_thread = any_thread;
self
}
}
/// Additional methods on [`Window`] that are specific to X11.
///
/// [`Window`]: crate::window::Window
@@ -144,52 +141,12 @@ pub trait WindowExtX11 {}
impl WindowExtX11 for dyn CoreWindow {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ApplicationName {
pub(crate) general: String,
pub(crate) instance: String,
}
#[derive(Clone, Debug)]
pub struct WindowAttributesX11 {
pub(crate) name: Option<ApplicationName>,
pub(crate) activation_token: Option<ActivationToken>,
pub(crate) visual_id: Option<XVisualID>,
pub(crate) screen_id: Option<i32>,
pub(crate) base_size: Option<Size>,
pub(crate) override_redirect: bool,
pub(crate) x11_window_types: Vec<WindowType>,
/// The parent window to embed this window into.
pub(crate) embed_window: Option<XWindow>,
}
impl Default for WindowAttributesX11 {
fn default() -> Self {
Self {
name: None,
activation_token: None,
visual_id: None,
screen_id: None,
base_size: None,
override_redirect: false,
x11_window_types: vec![WindowType::Normal],
embed_window: None,
}
}
}
impl WindowAttributesX11 {
/// Additional methods on [`WindowAttributes`] that are specific to X11.
pub trait WindowAttributesExtX11 {
/// Create this window with a specific X11 visual.
pub fn with_x11_visual(mut self, visual_id: XVisualID) -> Self {
self.visual_id = Some(visual_id);
self
}
fn with_x11_visual(self, visual_id: XVisualID) -> Self;
pub fn with_x11_screen(mut self, screen_id: i32) -> Self {
self.screen_id = Some(screen_id);
self
}
fn with_x11_screen(self, screen_id: i32) -> Self;
/// Build window with the given `general` and `instance` names.
///
@@ -199,39 +156,27 @@ impl WindowAttributesX11 {
///
/// 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)
pub fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.name = Some(ApplicationName { general: general.into(), instance: instance.into() });
self
}
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;
/// Build window with override-redirect flag; defaults to false.
pub fn with_override_redirect(mut self, override_redirect: bool) -> Self {
self.override_redirect = override_redirect;
self
}
fn with_override_redirect(self, override_redirect: bool) -> Self;
/// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`.
pub fn with_x11_window_type(mut self, x11_window_types: Vec<WindowType>) -> Self {
self.x11_window_types = x11_window_types;
self
}
fn with_x11_window_type(self, x11_window_type: Vec<WindowType>) -> Self;
/// Build window with base size hint.
///
/// ```
/// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::window::{Window, WindowAttributes};
/// # use winit::platform::x11::WindowAttributesX11;
/// # use winit::platform::x11::WindowAttributesExtX11;
/// // Specify the size in logical dimensions like this:
/// WindowAttributesX11::default().with_base_size(LogicalSize::new(400.0, 200.0));
/// WindowAttributes::default().with_base_size(LogicalSize::new(400.0, 200.0));
///
/// // Or specify the size in physical dimensions like this:
/// WindowAttributesX11::default().with_base_size(PhysicalSize::new(400, 200));
/// WindowAttributes::default().with_base_size(PhysicalSize::new(400, 200));
/// ```
pub fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
self.base_size = Some(base_size.into());
self
}
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
/// Embed this window into another parent window.
///
@@ -240,28 +185,57 @@ impl WindowAttributesX11 {
/// ```no_run
/// use winit::window::{Window, WindowAttributes};
/// use winit::event_loop::ActiveEventLoop;
/// use winit::platform::x11::{XWindow, WindowAttributesX11};
/// use winit::platform::x11::{XWindow, WindowAttributesExtX11};
/// # fn create_window(event_loop: &dyn ActiveEventLoop) -> Result<(), Box<dyn std::error::Error>> {
/// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?;
/// let window_x11_attributes = WindowAttributesX11::default().with_embed_parent_window(parent_window_id);
/// let window_attributes = WindowAttributes::default().with_platform_attributes(Box::new(window_x11_attributes));
/// let window_attributes = WindowAttributes::default().with_embed_parent_window(parent_window_id);
/// let window = event_loop.create_window(window_attributes)?;
/// # Ok(()) }
/// ```
pub fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self {
self.embed_window = Some(parent_window_id);
fn with_embed_parent_window(self, parent_window_id: XWindow) -> Self;
}
impl WindowAttributesExtX11 for WindowAttributes {
#[inline]
fn with_x11_visual(mut self, visual_id: XVisualID) -> Self {
self.platform_specific.x11.visual_id = Some(visual_id);
self
}
#[inline]
pub fn with_activation_token(mut self, token: ActivationToken) -> Self {
self.activation_token = Some(token);
fn with_x11_screen(mut self, screen_id: i32) -> Self {
self.platform_specific.x11.screen_id = Some(screen_id);
self
}
#[inline]
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
self.platform_specific.name =
Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into()));
self
}
#[inline]
fn with_override_redirect(mut self, override_redirect: bool) -> Self {
self.platform_specific.x11.override_redirect = override_redirect;
self
}
#[inline]
fn with_x11_window_type(mut self, x11_window_types: Vec<WindowType>) -> Self {
self.platform_specific.x11.x11_window_types = x11_window_types;
self
}
#[inline]
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
self.platform_specific.x11.base_size = Some(base_size.into());
self
}
#[inline]
fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self {
self.platform_specific.x11.embed_window = Some(parent_window_id);
self
}
}
impl PlatformWindowAttributes for WindowAttributesX11 {
fn box_clone(&self) -> Box<dyn PlatformWindowAttributes> {
Box::from(self.clone())
}
}

View File

@@ -1,8 +1,7 @@
use android_activity::input::{KeyAction, KeyEvent, KeyMapChar, Keycode};
use android_activity::AndroidApp;
use winit_core::keyboard::{
Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey,
};
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey};
pub fn to_physical_key(keycode: Keycode) -> PhysicalKey {
PhysicalKey::Code(match keycode {

View File

@@ -1,5 +1,4 @@
use std::cell::Cell;
use std::fmt;
use std::hash::Hash;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
@@ -9,26 +8,26 @@ use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
use android_activity::{
AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect,
};
use dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use tracing::{debug, trace, warn};
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{Cursor, CustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
use winit_core::event::{self, DeviceId, FingerId, Force, StartCause, SurfaceSizeWriter};
use winit_core::event_loop::pump_events::PumpStatus;
use winit_core::event_loop::{
use crate::application::ApplicationHandler;
use crate::cursor::Cursor;
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event::{self, DeviceId, FingerId, Force, StartCause, SurfaceSizeWriter};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use winit_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
use winit_core::window::{
self, CursorGrabMode, ImeCapabilities, ImePurpose, ImeRequest, ImeRequestError,
ResizeDirection, Theme, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId,
WindowLevel,
use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
use crate::platform::pump_events::PumpStatus;
use crate::window::{
self, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose, ResizeDirection, Theme,
Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel,
};
use crate::keycodes;
mod keycodes;
static HAS_FOCUS: AtomicBool = AtomicBool::new(true);
@@ -44,7 +43,7 @@ struct SharedFlagSetter {
flag: Arc<AtomicBool>,
}
impl SharedFlagSetter {
fn set(&self) -> bool {
pub fn set(&self) -> bool {
self.flag.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed).is_ok()
}
}
@@ -59,21 +58,21 @@ struct SharedFlag {
// we just need to know at the start of a main loop iteration if a redraw
// was queued and be able to read and clear the state atomically)
impl SharedFlag {
fn new() -> Self {
pub fn new() -> Self {
Self { flag: Arc::new(AtomicBool::new(false)) }
}
fn setter(&self) -> SharedFlagSetter {
pub fn setter(&self) -> SharedFlagSetter {
SharedFlagSetter { flag: self.flag.clone() }
}
fn get_and_reset(&self) -> bool {
pub fn get_and_reset(&self) -> bool {
self.flag.swap(false, std::sync::atomic::Ordering::AcqRel)
}
}
#[derive(Clone)]
struct RedrawRequester {
pub struct RedrawRequester {
flag: SharedFlagSetter,
waker: AndroidAppWaker,
}
@@ -89,7 +88,7 @@ impl RedrawRequester {
RedrawRequester { flag: flag.setter(), waker }
}
fn request_redraw(&self) {
pub fn request_redraw(&self) {
if self.flag.set() {
// Only explicitly try to wake up the main loop when the flag
// value changes
@@ -100,7 +99,7 @@ impl RedrawRequester {
#[derive(Debug)]
pub struct EventLoop {
pub android_app: AndroidApp,
pub(crate) android_app: AndroidApp,
window_target: ActiveEventLoop,
redraw_flag: SharedFlag,
loop_running: bool, // Dispatched `NewEvents<Init>`
@@ -113,9 +112,9 @@ pub struct EventLoop {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PlatformSpecificEventLoopAttributes {
pub android_app: Option<AndroidApp>,
pub ignore_volume_keys: bool,
pub(crate) struct PlatformSpecificEventLoopAttributes {
pub(crate) android_app: Option<AndroidApp>,
pub(crate) ignore_volume_keys: bool,
}
impl Default for PlatformSpecificEventLoopAttributes {
@@ -128,13 +127,9 @@ impl Default for PlatformSpecificEventLoopAttributes {
const GLOBAL_WINDOW: WindowId = WindowId::from_raw(0);
impl EventLoop {
pub fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
// For better cross-platformness.
return Err(EventLoopError::RecreationAttempt);
}
pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes,
) -> Result<Self, EventLoopError> {
let android_app = attributes.android_app.as_ref().expect(
"An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on \
Android",
@@ -164,7 +159,7 @@ impl EventLoop {
})
}
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
pub(crate) fn window_target(&self) -> &dyn RootActiveEventLoop {
&self.window_target
}
@@ -757,9 +752,8 @@ impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
pub struct PlatformSpecificWindowAttributes;
#[derive(Debug)]
pub struct Window {
pub(crate) struct Window {
app: AndroidApp,
ime_capabilities: Mutex<Option<ImeCapabilities>>,
redraw_requester: RedrawRequester,
}
@@ -770,18 +764,14 @@ impl Window {
) -> Result<Self, RequestError> {
// FIXME this ignores requested window attributes
Ok(Self {
app: el.app.clone(),
ime_capabilities: Default::default(),
redraw_requester: el.redraw_requester.clone(),
})
Ok(Self { app: el.app.clone(), redraw_requester: el.redraw_requester.clone() })
}
pub(crate) fn config(&self) -> ConfigurationRef {
pub fn config(&self) -> ConfigurationRef {
self.app.config()
}
pub(crate) fn content_rect(&self) -> Rect {
pub fn content_rect(&self) -> Rect {
self.app.content_rect()
}
@@ -938,37 +928,16 @@ impl CoreWindow for Window {
fn set_window_level(&self, _level: WindowLevel) {}
fn set_window_icon(&self, _window_icon: Option<winit_core::icon::Icon>) {}
fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
fn request_ime_update(&self, request: ImeRequest) -> Result<(), ImeRequestError> {
let mut current_caps = self.ime_capabilities.lock().unwrap();
match request {
ImeRequest::Enable(enable) => {
let (capabilities, _) = enable.into_raw();
if current_caps.is_some() {
return Err(ImeRequestError::AlreadyEnabled);
}
*current_caps = Some(capabilities);
self.app.show_soft_input(true);
},
ImeRequest::Update(_) => {
if current_caps.is_none() {
return Err(ImeRequestError::NotEnabled);
}
},
ImeRequest::Disable => {
*current_caps = None;
self.app.hide_soft_input(true);
},
fn set_ime_allowed(&self, allowed: bool) {
if allowed {
self.app.show_soft_input(true);
} else {
self.app.hide_soft_input(true);
}
Ok(())
}
fn ime_capabilities(&self) -> Option<ImeCapabilities> {
*self.ime_capabilities.lock().unwrap()
}
fn set_ime_purpose(&self, _purpose: ImePurpose) {}
@@ -1031,6 +1000,16 @@ impl CoreWindow for Window {
}
}
#[derive(Default, Clone, Debug)]
pub struct OsError;
use std::fmt::{self, Display, Formatter};
impl Display for OsError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(fmt, "Android OS Error")
}
}
fn screen_size(app: &AndroidApp) -> PhysicalSize<u32> {
if let Some(native_window) = app.native_window() {
PhysicalSize::new(native_window.width() as _, native_window.height() as _)

View File

@@ -9,9 +9,9 @@ use objc2::runtime::{Imp, Sel};
use objc2::sel;
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType};
use objc2_foundation::MainThreadMarker;
use winit_core::event::{DeviceEvent, ElementState};
use super::app_state::AppState;
use crate::event::{DeviceEvent, ElementState};
type SendEvent = extern "C-unwind" fn(&NSApplication, Sel, &NSEvent);

View File

@@ -1,6 +1,6 @@
use std::cell::{Cell, OnceCell, RefCell};
use std::mem;
use std::rc::Rc;
use std::rc::{Rc, Weak};
use std::sync::Arc;
use std::time::Instant;
@@ -8,16 +8,16 @@ use dispatch2::MainThreadBound;
use objc2::MainThreadMarker;
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningApplication};
use objc2_foundation::NSNotification;
use winit_common::core_foundation::EventLoopProxy;
use winit_common::event_handler::EventHandler;
use winit_core::application::ApplicationHandler;
use winit_core::event::{StartCause, WindowEvent};
use winit_core::event_loop::ControlFlow;
use winit_core::window::WindowId;
use super::event_loop::{notify_windows_of_exit, stop_app_immediately, ActiveEventLoop};
use super::super::event_handler::EventHandler;
use super::super::event_loop_proxy::EventLoopProxy;
use super::event_loop::{notify_windows_of_exit, stop_app_immediately, ActiveEventLoop, PanicInfo};
use super::menu;
use super::observer::{EventLoopWaker, RunLoop};
use crate::application::ApplicationHandler;
use crate::event::{StartCause, WindowEvent};
use crate::event_loop::ControlFlow;
use crate::window::WindowId;
#[derive(Debug)]
pub(super) struct AppState {
@@ -58,7 +58,7 @@ impl AppState {
activation_policy: Option<NSApplicationActivationPolicy>,
default_menu: bool,
activate_ignoring_other_apps: bool,
) -> Option<Rc<Self>> {
) -> Rc<Self> {
let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || {
Self::get(mtm).with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
}));
@@ -85,7 +85,8 @@ impl AppState {
pending_redraw: RefCell::new(vec![]),
});
GLOBAL.get(mtm).set(this.clone()).ok().and(Some(this))
GLOBAL.get(mtm).set(this.clone()).expect("application state can only be set once");
this
}
pub fn get(mtm: MainThreadMarker) -> Rc<Self> {
@@ -308,9 +309,13 @@ impl AppState {
}
// Called by RunLoopObserver after finishing waiting for new events
pub fn wakeup(self: &Rc<Self>) {
pub fn wakeup(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
let panic_info = panic_info
.upgrade()
.expect("The panic info must exist here. This failure indicates a developer error.");
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
if !self.event_handler.ready() || !self.is_running() {
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
return;
}
@@ -336,11 +341,15 @@ impl AppState {
}
// Called by RunLoopObserver before waiting for new events
pub fn cleared(self: &Rc<Self>) {
pub fn cleared(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
let panic_info = panic_info
.upgrade()
.expect("The panic info must exist here. This failure indicates a developer error.");
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
// XXX: how does it make sense that `event_handler.ready()` can ever return `false` here if
// we're about to return to the `CFRunLoop` to poll for new events?
if !self.event_handler.ready() || !self.is_running() {
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
return;
}

View File

@@ -9,8 +9,10 @@ use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
use objc2_foundation::{
ns_string, NSData, NSDictionary, NSNumber, NSObject, NSPoint, NSSize, NSString,
};
use winit_core::cursor::{CursorIcon, CursorImage, CustomCursorProvider, CustomCursorSource};
use winit_core::error::{NotSupportedError, RequestError};
use crate::cursor::{CursorImage, CustomCursorProvider, CustomCursorSource};
use crate::error::{NotSupportedError, RequestError};
use crate::window::CursorIcon;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CustomCursor(pub(crate) Retained<NSCursor>);
@@ -40,8 +42,8 @@ impl CustomCursor {
}
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result<Retained<NSCursor>, RequestError> {
let width = cursor.width();
let height = cursor.height();
let width = cursor.width;
let height = cursor.height;
let bitmap = unsafe {
NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel(
@@ -58,16 +60,15 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result<Retained<NSCurso
32,
)
}.ok_or_else(|| os_error!("parent view should be installed in a window"))?;
let bitmap_data =
unsafe { slice::from_raw_parts_mut(bitmap.bitmapData(), cursor.buffer().len()) };
bitmap_data.copy_from_slice(cursor.buffer());
let bitmap_data = unsafe { slice::from_raw_parts_mut(bitmap.bitmapData(), cursor.rgba.len()) };
bitmap_data.copy_from_slice(&cursor.rgba);
let image = unsafe {
NSImage::initWithSize(NSImage::alloc(), NSSize::new(width.into(), height.into()))
};
unsafe { image.addRepresentation(&bitmap) };
let hotspot = NSPoint::new(cursor.hotspot_x() as f64, cursor.hotspot_y() as f64);
let hotspot = NSPoint::new(cursor.hotspot_x as f64, cursor.hotspot_y as f64);
Ok(NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot))
}

View File

@@ -6,14 +6,14 @@ use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSEventType};
use objc2_core_foundation::{CFData, CFRetained};
use objc2_foundation::NSPoint;
use smol_str::SmolStr;
use winit_core::event::{ElementState, KeyEvent, Modifiers};
use winit_core::keyboard::{
use super::ffi;
use crate::event::{ElementState, KeyEvent, Modifiers};
use crate::keyboard::{
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
PhysicalKey,
};
use super::ffi;
/// Ignores ALL modifiers.
pub fn get_modifierless_char(scancode: u16) -> Key {
let Some(ptr) = NonNull::new(unsafe { ffi::TISCopyCurrentKeyboardLayoutInputSource() }) else {
@@ -330,7 +330,7 @@ pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
pressed_mods.set(ModifiersKeys::LMETA, flags.contains(NX_DEVICELCMDKEYMASK));
pressed_mods.set(ModifiersKeys::RMETA, flags.contains(NX_DEVICERCMDKEYMASK));
Modifiers::new(state, pressed_mods)
Modifiers { state, pressed_mods }
}
pub(super) fn dummy_event() -> Option<Retained<NSEvent>> {
@@ -349,7 +349,7 @@ pub(super) fn dummy_event() -> Option<Retained<NSEvent>> {
}
}
pub fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32> {
pub(crate) fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32> {
let code = match physical_key {
PhysicalKey::Code(code) => code,
PhysicalKey::Unidentified(_) => return None,
@@ -481,7 +481,7 @@ pub fn physicalkey_to_scancode(physical_key: PhysicalKey) -> Option<u32> {
}
}
pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
// Follows what Chromium and Firefox do:
// https://chromium.googlesource.com/chromium/src.git/+/3e1a26c44c024d97dc9a4c09bbc6a2365398ca2c/ui/events/keycodes/dom/dom_code_data.inc
// https://searchfox.org/mozilla-central/rev/c597e9c789ad36af84a0370d395be066b7dc94f4/widget/NativeKeyToDOMCodeName.h

View File

@@ -1,4 +1,8 @@
use std::rc::Rc;
use std::any::Any;
use std::cell::Cell;
use std::fmt;
use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe};
use std::rc::{Rc, Weak};
use std::sync::Arc;
use std::time::{Duration, Instant};
@@ -11,26 +15,61 @@ use objc2_app_kit::{
};
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
use rwh_06::HasDisplayHandle;
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, RequestError};
use winit_core::event_loop::pump_events::PumpStatus;
use winit_core::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use winit_core::monitor::MonitorHandle as CoreMonitorHandle;
use winit_core::window::Theme;
use super::super::notification_center::create_observer;
use super::app::override_send_event;
use super::app_state::AppState;
use super::cursor::CustomCursor;
use super::event::dummy_event;
use super::monitor;
use super::notification_center::create_observer;
use super::observer::setup_control_flow_observers;
use crate::window::Window;
use crate::ActivationPolicy;
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, RequestError};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::monitor::MonitorHandle as CoreMonitorHandle;
use crate::platform::macos::ActivationPolicy;
use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::Window;
use crate::window::{CustomCursor as CoreCustomCursor, CustomCursorSource, Theme};
#[derive(Default)]
pub struct PanicInfo {
inner: Cell<Option<Box<dyn Any + Send + 'static>>>,
}
impl fmt::Debug for PanicInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PanicInfo").finish_non_exhaustive()
}
}
// WARNING:
// As long as this struct is used through its `impl`, it is UnwindSafe.
// (If `get_mut` is called on `inner`, unwind safety may get broken.)
impl UnwindSafe for PanicInfo {}
impl RefUnwindSafe for PanicInfo {}
impl PanicInfo {
pub fn is_panicking(&self) -> bool {
let inner = self.inner.take();
let result = inner.is_some();
self.inner.set(inner);
result
}
/// Overwrites the current state if the current state is not panicking
pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) {
if !self.is_panicking() {
self.inner.set(Some(p));
}
}
pub fn take(&self) -> Option<Box<dyn Any + Send + 'static>> {
self.inner.take()
}
}
#[derive(Debug)]
pub struct ActiveEventLoop {
@@ -63,8 +102,8 @@ impl RootActiveEventLoop for ActiveEventLoop {
fn create_window(
&self,
window_attributes: winit_core::window::WindowAttributes,
) -> Result<Box<dyn winit_core::window::Window>, RequestError> {
window_attributes: crate::window::WindowAttributes,
) -> Result<Box<dyn crate::window::Window>, RequestError> {
Ok(Box::new(Window::new(self, window_attributes)?))
}
@@ -83,7 +122,7 @@ impl RootActiveEventLoop for ActiveEventLoop {
)
}
fn primary_monitor(&self) -> Option<winit_core::monitor::MonitorHandle> {
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
let monitor = monitor::primary_monitor();
Some(CoreMonitorHandle(Arc::new(monitor)))
}
@@ -143,6 +182,7 @@ pub struct EventLoop {
app_state: Rc<AppState>,
window_target: ActiveEventLoop,
panic_info: Rc<PanicInfo>,
// Since macOS 10.11, we no longer need to remove the observers before they are deallocated;
// the system instead cleans it up next time it would have posted a notification to it.
@@ -153,10 +193,10 @@ pub struct EventLoop {
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct PlatformSpecificEventLoopAttributes {
pub activation_policy: Option<ActivationPolicy>,
pub default_menu: bool,
pub activate_ignoring_other_apps: bool,
pub(crate) struct PlatformSpecificEventLoopAttributes {
pub(crate) activation_policy: Option<ActivationPolicy>,
pub(crate) default_menu: bool,
pub(crate) activate_ignoring_other_apps: bool,
}
impl Default for PlatformSpecificEventLoopAttributes {
@@ -166,7 +206,9 @@ impl Default for PlatformSpecificEventLoopAttributes {
}
impl EventLoop {
pub fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes,
) -> Result<Self, EventLoopError> {
let mtm = MainThreadMarker::new()
.expect("on macOS, `EventLoop` must be created on the main thread!");
@@ -182,8 +224,7 @@ impl EventLoop {
activation_policy,
attributes.default_menu,
attributes.activate_ignoring_other_apps,
)
.ok_or_else(|| EventLoopError::RecreationAttempt)?;
);
// Initialize the application (if it has not already been).
let app = NSApplication::sharedApplication(mtm);
@@ -217,12 +258,14 @@ impl EventLoop {
},
);
setup_control_flow_observers(mtm);
let panic_info: Rc<PanicInfo> = Default::default();
setup_control_flow_observers(mtm, Rc::downgrade(&panic_info));
Ok(EventLoop {
app,
app_state: app_state.clone(),
window_target: ActiveEventLoop { app_state, mtm },
panic_info,
_did_finish_launching_observer,
_will_terminate_observer,
})
@@ -262,6 +305,15 @@ impl EventLoop {
// NOTE: Make sure to not run the application re-entrantly, as that'd be confusing.
self.app.run();
// While the app is running it's possible that we catch a panic
// to avoid unwinding across an objective-c ffi boundary, which
// will lead to us stopping the `NSApplication` and saving the
// `PanicInfo` so that we can resume the unwind at a controlled,
// safe point in time.
if let Some(panic) = self.panic_info.take() {
resume_unwind(panic);
}
self.app_state.internal_exit()
})
});
@@ -317,6 +369,15 @@ impl EventLoop {
self.app.run();
}
// While the app is running it's possible that we catch a panic
// to avoid unwinding across an objective-c ffi boundary, which
// will lead to us stopping the application and saving the
// `PanicInfo` so that we can resume the unwind at a controlled,
// safe point in time.
if let Some(panic) = self.panic_info.take() {
resume_unwind(panic);
}
if self.app_state.exiting() {
self.app_state.internal_exit();
PumpStatus::Exit(0)
@@ -361,3 +422,29 @@ pub(super) fn notify_windows_of_exit(app: &NSApplication) {
window.close();
}
}
/// Catches panics that happen inside `f` and when a panic
/// happens, stops the `sharedApplication`
#[inline]
pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
mtm: MainThreadMarker,
panic_info: Weak<PanicInfo>,
f: F,
) -> Option<R> {
match catch_unwind(f) {
Ok(r) => Some(r),
Err(e) => {
// It's important that we set the panic before requesting a `stop`
// because some callback are still called during the `stop` message
// and we need to know in those callbacks if the application is currently
// panicking
{
let panic_info = panic_info.upgrade().unwrap();
panic_info.set_panic(e);
}
let app = NSApplication::sharedApplication(mtm);
stop_app_immediately(&app);
None
},
}
}

View File

@@ -11,8 +11,8 @@ use objc2_core_graphics::CGDirectDisplayID;
pub const IO16BitDirectPixels: &str = "-RRRRRGGGGGBBBBB";
pub const IO32BitDirectPixels: &str = "--------RRRRRRRRGGGGGGGGBBBBBBBB";
pub const kIO30BitDirectPixels: &str = "--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB";
pub const kIO64BitDirectPixels: &str = "-16R16G16B16";
// `CGDisplayCreateUUIDFromDisplayID` comes from the `ColorSync` framework.
// However, that framework was only introduced "publicly" in macOS 10.13.

View File

@@ -2,7 +2,7 @@ use objc2::rc::Retained;
use objc2::runtime::Sel;
use objc2::{sel, MainThreadMarker};
use objc2_app_kit::{NSApplication, NSEventModifierFlags, NSMenu, NSMenuItem};
use objc2_foundation::{ns_string, NSBundle, NSProcessInfo, NSString};
use objc2_foundation::{ns_string, NSProcessInfo, NSString};
struct KeyEquivalent<'a> {
key: &'a NSString,
@@ -16,10 +16,7 @@ pub fn initialize(app: &NSApplication) {
menubar.addItem(&app_menu_item);
let app_menu = NSMenu::new(mtm);
let process_name = match NSBundle::mainBundle().name() {
Some(bundle_name) => bundle_name,
None => NSProcessInfo::processInfo().processName(),
};
let process_name = NSProcessInfo::processInfo().processName();
// About menu item
let about_item_title = ns_string!("About ").stringByAppendingString(&process_name);

View File

@@ -0,0 +1,23 @@
#[macro_use]
mod util;
mod app;
mod app_state;
mod cursor;
mod event;
mod event_loop;
mod ffi;
mod menu;
mod monitor;
mod observer;
mod view;
mod window;
mod window_delegate;
pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey};
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::MonitorHandle;
pub(crate) use self::window::Window;
pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;

View File

@@ -6,7 +6,6 @@ use std::ptr::NonNull;
use std::{fmt, ptr};
use dispatch2::run_on_main;
use dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use objc2::rc::Retained;
use objc2::MainThreadMarker;
use objc2_app_kit::NSScreen;
@@ -18,10 +17,11 @@ use objc2_core_graphics::{
use objc2_core_video::{kCVReturnSuccess, CVDisplayLink, CVTimeFlags};
use objc2_foundation::{ns_string, NSNumber, NSPoint, NSRect};
use tracing::warn;
use winit_core::monitor::{MonitorHandleProvider, VideoMode};
use super::ffi;
use super::util::cgerr;
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::monitor::{MonitorHandleProvider, VideoMode};
#[derive(Clone)]
pub struct VideoModeHandle {
@@ -66,31 +66,27 @@ impl VideoModeHandle {
refresh_rate_millihertz: Option<NonZeroU32>,
) -> Self {
unsafe {
// The bit-depth is basically always 32 since macOS 10.12.
#[allow(deprecated)]
let pixel_encoding =
CGDisplayMode::pixel_encoding(Some(&native_mode.0)).unwrap().to_string();
let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) {
NonZeroU16::new(32)
32
} else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) {
NonZeroU16::new(16)
16
} else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO30BitDirectPixels) {
NonZeroU16::new(30)
} else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO64BitDirectPixels) {
NonZeroU16::new(64)
30
} else {
warn!(?pixel_encoding, "unknown bit depth");
None
unimplemented!()
};
let mode = VideoMode::new(
PhysicalSize::new(
let mode = VideoMode {
size: PhysicalSize::new(
CGDisplayMode::pixel_width(Some(&native_mode.0)) as u32,
CGDisplayMode::pixel_height(Some(&native_mode.0)) as u32,
),
bit_depth,
refresh_rate_millihertz,
);
bit_depth: NonZeroU16::new(bit_depth),
};
VideoModeHandle { mode, monitor: monitor.clone(), native_mode }
}

View File

@@ -4,7 +4,9 @@
//! <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html>
use std::cell::Cell;
use std::ffi::c_void;
use std::panic::{AssertUnwindSafe, UnwindSafe};
use std::ptr;
use std::rc::Weak;
use std::time::Instant;
use objc2::MainThreadMarker;
@@ -16,18 +18,47 @@ use objc2_core_foundation::{
use tracing::error;
use super::app_state::AppState;
use super::event_loop::{stop_app_on_panic, PanicInfo};
unsafe fn control_flow_handler<F>(panic_info: *mut c_void, f: F)
where
F: FnOnce(Weak<PanicInfo>) + UnwindSafe,
{
let info_from_raw = unsafe { Weak::from_raw(panic_info as *mut PanicInfo) };
// Asserting unwind safety on this type should be fine because `PanicInfo` is
// `RefUnwindSafe` and `Rc<T>` is `UnwindSafe` if `T` is `RefUnwindSafe`.
let panic_info = AssertUnwindSafe(Weak::clone(&info_from_raw));
// `from_raw` takes ownership of the data behind the pointer.
// But if this scope takes ownership of the weak pointer, then
// the weak pointer will get free'd at the end of the scope.
// However we want to keep that weak reference around after the function.
std::mem::forget(info_from_raw);
let mtm = MainThreadMarker::new().unwrap();
stop_app_on_panic(mtm, Weak::clone(&panic_info), move || {
let _ = &panic_info;
f(panic_info.0)
});
}
// begin is queued with the highest priority to ensure it is processed before other observers
extern "C-unwind" fn control_flow_begin_handler(
_: *mut CFRunLoopObserver,
activity: CFRunLoopActivity,
_info: *mut c_void,
panic_info: *mut c_void,
) {
match activity {
CFRunLoopActivity::AfterWaiting => {
AppState::get(MainThreadMarker::new().unwrap()).wakeup();
},
_ => unreachable!(),
unsafe {
control_flow_handler(panic_info, |panic_info| {
#[allow(non_upper_case_globals)]
match activity {
CFRunLoopActivity::AfterWaiting => {
// trace!("Triggered `CFRunLoopAfterWaiting`");
AppState::get(MainThreadMarker::new().unwrap()).wakeup(panic_info);
// trace!("Completed `CFRunLoopAfterWaiting`");
},
_ => unreachable!(),
}
});
}
}
@@ -36,14 +67,21 @@ extern "C-unwind" fn control_flow_begin_handler(
extern "C-unwind" fn control_flow_end_handler(
_: *mut CFRunLoopObserver,
activity: CFRunLoopActivity,
_info: *mut c_void,
panic_info: *mut c_void,
) {
match activity {
CFRunLoopActivity::BeforeWaiting => {
AppState::get(MainThreadMarker::new().unwrap()).cleared();
},
CFRunLoopActivity::Exit => (), // unimplemented!(), // not expected to ever happen
_ => unreachable!(),
unsafe {
control_flow_handler(panic_info, |panic_info| {
#[allow(non_upper_case_globals)]
match activity {
CFRunLoopActivity::BeforeWaiting => {
// trace!("Triggered `CFRunLoopBeforeWaiting`");
AppState::get(MainThreadMarker::new().unwrap()).cleared(panic_info);
// trace!("Completed `CFRunLoopBeforeWaiting`");
},
CFRunLoopActivity::Exit => (), /* unimplemented!(), // not expected to ever happen */
_ => unreachable!(),
}
});
}
}
@@ -142,11 +180,11 @@ impl RunLoop {
}
}
pub fn setup_control_flow_observers(mtm: MainThreadMarker) {
pub fn setup_control_flow_observers(mtm: MainThreadMarker, panic_info: Weak<PanicInfo>) {
let run_loop = RunLoop::main(mtm);
unsafe {
let mut context = CFRunLoopObserverContext {
info: ptr::null_mut(),
info: Weak::into_raw(panic_info) as *mut _,
version: 0,
retain: None,
release: None,

View File

@@ -1,16 +1,12 @@
use objc2_core_graphics::CGError;
use tracing::trace;
use winit_core::error::OsError;
macro_rules! os_error {
($error:expr) => {{
winit_core::error::OsError::new(line!(), file!(), $error)
}};
}
use crate::error::OsError;
macro_rules! trace_scope {
($s:literal) => {
let _crate = $crate::util::TraceGuard::new(module_path!(), $s);
let _crate =
$crate::platform_impl::platform::appkit::util::TraceGuard::new(module_path!(), $s);
};
}

View File

@@ -4,7 +4,6 @@ use std::collections::{HashMap, VecDeque};
use std::ptr;
use std::rc::Rc;
use dpi::{LogicalPosition, LogicalSize};
use objc2::rc::Retained;
use objc2::runtime::{AnyObject, Sel};
use objc2::{define_class, msg_send, DefinedClass, MainThreadMarker};
@@ -12,17 +11,10 @@ use objc2_app_kit::{
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient,
NSTrackingRectTag, NSView, NSWindow,
};
use objc2_core_foundation::CGRect;
use objc2_foundation::{
NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString,
NSNotFound, NSObject, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
};
use winit_core::event::{
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
PointerKind, PointerSource, TouchPhase, WindowEvent,
};
use winit_core::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey};
use winit_core::window::ImeCapabilities;
use super::app_state::AppState;
use super::cursor::{default_cursor, invisible_cursor};
@@ -31,7 +23,13 @@ use super::event::{
scancode_to_physicalkey,
};
use super::window::window_id;
use crate::OptionAsAlt;
use crate::dpi::{LogicalPosition, LogicalSize};
use crate::event::{
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
PointerKind, PointerSource, TouchPhase, WindowEvent,
};
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey};
use crate::platform::macos::OptionAsAlt;
#[derive(Debug)]
struct CursorState {
@@ -126,7 +124,7 @@ pub struct ViewState {
/// True iff the application wants IME events.
///
/// Can be set using `set_ime_allowed`
ime_capabilities: Cell<Option<ImeCapabilities>>,
ime_allowed: Cell<bool>,
/// True if the current key event should be forwarded
/// to the application, even during IME
@@ -362,16 +360,9 @@ define_class!(
_actual_range: *mut NSRange,
) -> NSRect {
trace_scope!("firstRectForCharacterRange:actualRange:");
// Guard when the view is no longer in a window during teardown.
let Some(window) = (**self).window() else {
return CGRect::ZERO;
};
// Return value is expected to be in screen coordinates, so we need a conversion
let rect = NSRect::new(self.ivars().ime_position.get(), self.ivars().ime_size.get());
let view_rect = self.convertRect_toView(rect, None);
window.convertRectToScreen(view_rect)
// Return value is expected to be in screen coordinates, so we need a conversion here
self.window().convertRectToScreen(self.convertRect_toView(rect, None))
}
#[unsafe(method(insertText:replacementRange:))]
@@ -465,7 +456,7 @@ define_class!(
// we must send the `KeyboardInput` event during IME if it triggered
// `doCommandBySelector`. (doCommandBySelector means that the keyboard input
// is not handled by IME and should be handled by the application)
if self.ivars().ime_capabilities.get().is_some() {
if self.ivars().ime_allowed.get() {
let events_for_nsview = NSArray::from_slice(&[&*event]);
unsafe { self.interpretKeyEvents(&events_for_nsview) };
@@ -806,7 +797,7 @@ impl WinitView {
tracking_rect: Default::default(),
ime_state: Default::default(),
input_source: Default::default(),
ime_capabilities: Default::default(),
ime_allowed: Default::default(),
forward_key_to_app: Default::default(),
marked_text: Default::default(),
accepts_first_mouse,
@@ -868,13 +859,12 @@ impl WinitView {
}
}
pub(super) fn set_ime_allowed(&self, capabilities: Option<ImeCapabilities>) {
if self.ivars().ime_capabilities.get().is_some() {
pub(super) fn set_ime_allowed(&self, ime_allowed: bool) {
if self.ivars().ime_allowed.get() == ime_allowed {
return;
}
self.ivars().ime_capabilities.set(capabilities);
if capabilities.is_some() {
self.ivars().ime_allowed.set(ime_allowed);
if self.ivars().ime_allowed.get() {
return;
}
@@ -887,10 +877,6 @@ impl WinitView {
}
}
pub(super) fn ime_capabilities(&self) -> Option<ImeCapabilities> {
self.ivars().ime_capabilities.get()
}
pub(super) fn set_ime_cursor_area(&self, position: NSPoint, size: NSSize) {
self.ivars().ime_position.set(position);
self.ivars().ime_size.set(size);
@@ -1110,7 +1096,7 @@ fn mouse_button(event: &NSEvent) -> MouseButton {
// we're getting from the operating system, which makes it
// impossible to provide such events as extra in `KeyEvent`.
fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Retained<NSEvent> {
let ev_mods = event_mods(event).state();
let ev_mods = event_mods(event).state;
let ignore_alt_characters = match option_as_alt {
OptionAsAlt::OnlyLeft if lalt_pressed(event) => true,
OptionAsAlt::OnlyRight if ralt_pressed(event) => true,

View File

@@ -8,17 +8,15 @@ use objc2::rc::{autoreleasepool, Retained};
use objc2::{define_class, MainThreadMarker, Message};
use objc2_app_kit::{NSPanel, NSResponder, NSWindow};
use objc2_foundation::NSObject;
use winit_core::cursor::Cursor;
use winit_core::error::RequestError;
use winit_core::icon::Icon;
use winit_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
use winit_core::window::{
ImeCapabilities, ImeRequest, ImeRequestError, Theme, UserAttentionType, Window as CoreWindow,
WindowAttributes, WindowButtons, WindowId, WindowLevel,
};
use super::event_loop::ActiveEventLoop;
use super::window_delegate::WindowDelegate;
use crate::error::RequestError;
use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
use crate::window::{
Cursor, Icon, ImePurpose, Theme, UserAttentionType, Window as CoreWindow, WindowAttributes,
WindowButtons, WindowId, WindowLevel,
};
#[derive(Debug)]
pub(crate) struct Window {
@@ -93,7 +91,7 @@ impl rwh_06::HasWindowHandle for Window {
}
impl CoreWindow for Window {
fn id(&self) -> winit_core::window::WindowId {
fn id(&self) -> crate::window::WindowId {
self.maybe_wait_on_main(|delegate| delegate.id())
}
@@ -233,12 +231,16 @@ impl CoreWindow for Window {
self.maybe_wait_on_main(|delegate| delegate.set_window_icon(window_icon));
}
fn request_ime_update(&self, request: ImeRequest) -> Result<(), ImeRequestError> {
self.maybe_wait_on_main(|delegate| delegate.request_ime_update(request))
fn set_ime_cursor_area(&self, position: Position, size: Size) {
self.maybe_wait_on_main(|delegate| delegate.set_ime_cursor_area(position, size));
}
fn ime_capabilities(&self) -> Option<ImeCapabilities> {
self.maybe_wait_on_main(|delegate| delegate.ime_capabilities())
fn set_ime_allowed(&self, allowed: bool) {
self.maybe_wait_on_main(|delegate| delegate.set_ime_allowed(allowed));
}
fn set_ime_purpose(&self, purpose: ImePurpose) {
self.maybe_wait_on_main(|delegate| delegate.set_ime_purpose(purpose));
}
fn focus_window(&self) {
@@ -277,10 +279,7 @@ impl CoreWindow for Window {
self.maybe_wait_on_main(|delegate| delegate.set_cursor_position(position))
}
fn set_cursor_grab(
&self,
mode: winit_core::window::CursorGrabMode,
) -> Result<(), RequestError> {
fn set_cursor_grab(&self, mode: crate::window::CursorGrabMode) -> Result<(), RequestError> {
self.maybe_wait_on_main(|delegate| delegate.set_cursor_grab(mode))
}
@@ -294,7 +293,7 @@ impl CoreWindow for Window {
fn drag_resize_window(
&self,
direction: winit_core::window::ResizeDirection,
direction: crate::window::ResizeDirection,
) -> Result<(), RequestError> {
Ok(self.maybe_wait_on_main(|delegate| delegate.drag_resize_window(direction))?)
}

View File

@@ -6,10 +6,6 @@ use std::ptr;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use dpi::{
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
Position, Size,
};
use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::{AnyObject, ProtocolObject};
use objc2::{
@@ -40,25 +36,67 @@ use objc2_foundation::{
NSRect, NSSize, NSString,
};
use tracing::{trace, warn};
use winit_core::cursor::Cursor;
use winit_core::error::{NotSupportedError, RequestError};
use winit_core::event::{SurfaceSizeWriter, WindowEvent};
use winit_core::icon::Icon;
use winit_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider};
use winit_core::window::{
CursorGrabMode, ImeCapabilities, ImeRequest, ImeRequestError, ResizeDirection, Theme,
UserAttentionType, WindowAttributes, WindowButtons, WindowId, WindowLevel,
};
use super::app_state::AppState;
use super::cursor::{cursor_from_icon, CustomCursor};
use super::ffi;
use super::monitor::{self, flip_window_screen_coordinates, get_display_id, MonitorHandle};
use super::monitor::{self, flip_window_screen_coordinates, get_display_id};
use super::observer::RunLoop;
use super::util::cgerr;
use super::view::WinitView;
use super::window::{window_id, WinitPanel, WinitWindow};
use crate::{OptionAsAlt, WindowAttributesMacOS, WindowExtMacOS};
use super::{ffi, MonitorHandle};
use crate::dpi::{
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
Position, Size,
};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{SurfaceSizeWriter, WindowEvent};
use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider};
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
use crate::window::{
Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowId, WindowLevel,
};
#[derive(Clone, Debug, PartialEq)]
pub struct PlatformSpecificWindowAttributes {
pub movable_by_window_background: bool,
pub titlebar_transparent: bool,
pub title_hidden: bool,
pub titlebar_hidden: bool,
pub titlebar_buttons_hidden: bool,
pub fullsize_content_view: bool,
pub disallow_hidpi: bool,
pub has_shadow: bool,
pub accepts_first_mouse: bool,
pub tabbing_identifier: Option<String>,
pub option_as_alt: OptionAsAlt,
pub borderless_game: bool,
pub unified_titlebar: bool,
pub panel: bool,
}
impl Default for PlatformSpecificWindowAttributes {
#[inline]
fn default() -> Self {
Self {
movable_by_window_background: false,
titlebar_transparent: false,
title_hidden: false,
titlebar_hidden: false,
titlebar_buttons_hidden: false,
fullsize_content_view: false,
disallow_hidpi: false,
has_shadow: true,
accepts_first_mouse: true,
tabbing_identifier: None,
option_as_alt: Default::default(),
borderless_game: false,
unified_titlebar: false,
panel: false,
}
}
}
#[derive(Debug)]
pub(crate) struct State {
@@ -514,7 +552,6 @@ impl Drop for WindowDelegate {
fn new_window(
app_state: &Rc<AppState>,
attrs: &WindowAttributes,
macos_attrs: &WindowAttributesMacOS,
mtm: MainThreadMarker,
) -> Option<Retained<NSWindow>> {
autoreleasepool(|_| {
@@ -555,7 +592,9 @@ fn new_window(
},
};
let mut masks = if (!attrs.decorations && screen.is_none()) || macos_attrs.titlebar_hidden {
let mut masks = if (!attrs.decorations && screen.is_none())
|| attrs.platform_specific.titlebar_hidden
{
// Resizable without a titlebar or borders
// if decorations is set to false, ignore pl_attrs
//
@@ -583,7 +622,7 @@ fn new_window(
masks &= !NSWindowStyleMask::Closable;
}
if macos_attrs.fullsize_content_view {
if attrs.platform_specific.fullsize_content_view {
// NOTE: If we decide to add an option to change this at runtime, we must emit a
// `SurfaceResized` event to let applications know that the safe area changed.
//
@@ -598,7 +637,7 @@ fn new_window(
// confusing issues with the window not being properly activated.
//
// Winit ensures this by not allowing access to `ActiveEventLoop` before handling events.
let window: Retained<NSWindow> = if macos_attrs.panel {
let window: Retained<NSWindow> = if attrs.platform_specific.panel {
masks |= NSWindowStyleMask::NonactivatingPanel;
let window: Option<Retained<WinitPanel>> = unsafe {
@@ -634,7 +673,7 @@ fn new_window(
window.setTitle(&NSString::from_str(&attrs.title));
window.setAcceptsMouseMovedEvents(true);
if let Some(identifier) = &macos_attrs.tabbing_identifier {
if let Some(identifier) = &attrs.platform_specific.tabbing_identifier {
window.setTabbingIdentifier(&NSString::from_str(identifier));
window.setTabbingMode(NSWindowTabbingMode::Preferred);
}
@@ -643,13 +682,13 @@ fn new_window(
window.setSharingType(NSWindowSharingType::None);
}
if macos_attrs.titlebar_transparent {
if attrs.platform_specific.titlebar_transparent {
window.setTitlebarAppearsTransparent(true);
}
if macos_attrs.title_hidden {
if attrs.platform_specific.title_hidden {
window.setTitleVisibility(NSWindowTitleVisibility::Hidden);
}
if macos_attrs.titlebar_buttons_hidden {
if attrs.platform_specific.titlebar_buttons_hidden {
for titlebar_button in &[
#[allow(deprecated)]
NSWindowFullScreenButton,
@@ -662,10 +701,10 @@ fn new_window(
}
}
}
if macos_attrs.movable_by_window_background {
if attrs.platform_specific.movable_by_window_background {
window.setMovableByWindowBackground(true);
}
if macos_attrs.unified_titlebar {
if attrs.platform_specific.unified_titlebar {
unsafe {
// The toolbar style is ignored if there is no toolbar, so it is
// necessary to add one.
@@ -680,7 +719,7 @@ fn new_window(
}
}
if !macos_attrs.has_shadow {
if !attrs.platform_specific.has_shadow {
window.setHasShadow(false);
}
if attrs.position.is_none() {
@@ -689,8 +728,8 @@ fn new_window(
let view = WinitView::new(
app_state,
macos_attrs.accepts_first_mouse,
macos_attrs.option_as_alt,
attrs.platform_specific.accepts_first_mouse,
attrs.platform_specific.option_as_alt,
mtm,
);
@@ -698,7 +737,7 @@ fn new_window(
// macos 10.14 and `true` after 10.15, we should set it to `YES` or `NO` to avoid
// always the default system value in favour of the user's code
#[allow(deprecated)]
view.setWantsBestResolutionOpenGLSurface(!macos_attrs.disallow_hidpi);
view.setWantsBestResolutionOpenGLSurface(!attrs.platform_specific.disallow_hidpi);
// On Mojave, views automatically become layer-backed shortly after being added to
// a window. Changing the layer-backedness of a view breaks the association between
@@ -746,19 +785,13 @@ fn new_window(
impl WindowDelegate {
pub(super) fn new(
app_state: &Rc<AppState>,
mut attrs: WindowAttributes,
attrs: WindowAttributes,
mtm: MainThreadMarker,
) -> Result<Retained<Self>, RequestError> {
let macos_attrs = attrs
.platform
.take()
.and_then(|attrs| attrs.cast::<WindowAttributesMacOS>().ok())
.unwrap_or_default();
let window = new_window(app_state, &attrs, &macos_attrs, mtm)
let window = new_window(app_state, &attrs, mtm)
.ok_or_else(|| os_error!("couldn't create `NSWindow`"))?;
match attrs.parent_window() {
match attrs.parent_window.map(|handle| handle.0) {
Some(rwh_06::RawWindowHandle::AppKit(handle)) => {
// SAFETY: Caller ensures the pointer is valid or NULL
// Unwrap is fine, since the pointer comes from `NonNull`.
@@ -810,7 +843,7 @@ impl WindowDelegate {
standard_frame: Cell::new(None),
is_simple_fullscreen: Cell::new(false),
saved_style: Cell::new(None),
is_borderless_game: Cell::new(macos_attrs.borderless_game),
is_borderless_game: Cell::new(attrs.platform_specific.borderless_game),
});
let delegate: Retained<WindowDelegate> = unsafe { msg_send![super(delegate), init] };
@@ -1674,50 +1707,26 @@ impl WindowDelegate {
// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/WinPanel/Tasks/SettingWindowTitle.html
}
pub fn request_ime_update(&self, request: ImeRequest) -> Result<(), ImeRequestError> {
let current_caps = self.view().ime_capabilities();
let request_data = match request {
ImeRequest::Enable(enable) => {
let (capabilities, request_data) = enable.into_raw();
if current_caps.is_some() {
return Err(ImeRequestError::AlreadyEnabled);
}
self.view().set_ime_allowed(Some(capabilities));
request_data
},
ImeRequest::Update(request_data) => {
if current_caps.is_none() {
return Err(ImeRequestError::NotEnabled);
}
request_data
},
ImeRequest::Disable => {
self.view().set_ime_allowed(None);
return Ok(());
},
};
#[inline]
pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
let scale_factor = self.scale_factor();
let logical_spot = spot.to_logical(scale_factor);
let logical_spot = NSPoint::new(logical_spot.x, logical_spot.y);
if let Some((spot, size)) = request_data.cursor_area {
if self.view().ime_capabilities().unwrap().cursor_area() {
let scale_factor = self.scale_factor();
let logical_spot = spot.to_logical(scale_factor);
let logical_spot = NSPoint::new(logical_spot.x, logical_spot.y);
let size = size.to_logical(scale_factor);
let size = NSSize::new(size.width, size.height);
let size = size.to_logical(scale_factor);
let size = NSSize::new(size.width, size.height);
self.view().set_ime_cursor_area(logical_spot, size);
} else {
warn!("discarding IME cursor area update without capability enabled.");
}
}
Ok(())
self.view().set_ime_cursor_area(logical_spot, size);
}
pub fn ime_capabilities(&self) -> Option<ImeCapabilities> {
self.view().ime_capabilities()
#[inline]
pub fn set_ime_allowed(&self, allowed: bool) {
self.view().set_ime_allowed(allowed);
}
#[inline]
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}
#[inline]
pub fn focus_window(&self) {
let mtm = MainThreadMarker::from(self);

View File

@@ -1,12 +1,12 @@
use std::cell::RefCell;
use std::{fmt, mem};
use winit_core::application::ApplicationHandler;
use crate::application::ApplicationHandler;
/// A helper type for storing a reference to `ApplicationHandler`, allowing interior mutable access
/// to it within the execution of a closure.
#[derive(Default)]
pub struct EventHandler {
pub(crate) struct EventHandler {
/// This can be in the following states:
/// - Not registered by the event loop, or terminated (None).
/// - Present (Some(handler)).
@@ -26,7 +26,7 @@ impl fmt::Debug for EventHandler {
}
impl EventHandler {
pub fn new() -> Self {
pub(crate) fn new() -> Self {
Self { inner: RefCell::new(None) }
}
@@ -35,7 +35,7 @@ impl EventHandler {
/// This is similar to using the `scoped-tls` or `scoped-tls-hkt` crates
/// to store the handler in a thread local, such that it can be accessed
/// from within the closure.
pub fn set<'handler, R>(
pub(crate) fn set<'handler, R>(
&self,
app: Box<dyn ApplicationHandler + 'handler>,
closure: impl FnOnce() -> R,
@@ -104,15 +104,16 @@ impl EventHandler {
// soundness.
}
pub fn in_use(&self) -> bool {
#[cfg(target_os = "macos")]
pub(crate) fn in_use(&self) -> bool {
self.inner.try_borrow().is_err()
}
pub fn ready(&self) -> bool {
pub(crate) fn ready(&self) -> bool {
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
}
pub fn handle(&self, callback: impl FnOnce(&mut (dyn ApplicationHandler + '_))) {
pub(crate) fn handle(&self, callback: impl FnOnce(&mut (dyn ApplicationHandler + '_))) {
match self.inner.try_borrow_mut().as_deref_mut() {
Ok(Some(ref mut user_app)) => {
// It is important that we keep the reference borrowed here,
@@ -136,7 +137,7 @@ impl EventHandler {
}
}
pub fn terminate(&self) {
pub(crate) fn terminate(&self) {
match self.inner.try_borrow_mut().as_deref_mut() {
Ok(data @ Some(_)) => {
let handler = data.take();

View File

@@ -1,14 +1,12 @@
use std::os::raw::c_void;
use std::ptr::NonNull;
use std::sync::Arc;
use std::task::{RawWaker, RawWakerVTable, Waker};
use objc2::MainThreadMarker;
use objc2_core_foundation::{
kCFRunLoopCommonModes, CFIndex, CFRetained, CFRunLoop, CFRunLoopSource, CFRunLoopSourceContext,
Type,
};
use winit_core::event_loop::EventLoopProxyProvider;
use crate::event_loop::EventLoopProxyProvider;
/// A waker that signals a `CFRunLoopSource` on the main thread.
///
@@ -17,7 +15,7 @@ use winit_core::event_loop::EventLoopProxyProvider;
///
/// See <https://developer.apple.com/documentation/corefoundation/cfrunloopsource?language=objc>.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EventLoopProxy {
pub(crate) struct EventLoopProxy {
source: CFRetained<CFRunLoopSource>,
/// Cached value of `CFRunLoopGetMain`.
main_loop: CFRetained<CFRunLoop>,
@@ -31,7 +29,7 @@ impl EventLoopProxy {
/// Create a new proxy, registering it to be performed on the main thread.
///
/// The provided closure should call `proxy_wake_up` on the application.
pub fn new<F: Fn() + 'static>(mtm: MainThreadMarker, signaller: F) -> Self {
pub(crate) fn new<F: Fn() + 'static>(mtm: MainThreadMarker, signaller: F) -> Self {
// We use an `Arc` here to make sure that the reference-counting of the signal container is
// atomic (`Retained`/`CFRetained` would be valid alternatives too).
let signaller = Arc::new(signaller);
@@ -102,7 +100,8 @@ impl EventLoopProxy {
// FIXME(madsmtm): Use this on macOS too.
// More difficult there, since the user can re-start the event loop.
pub fn invalidate(&self) {
#[cfg_attr(target_os = "macos", allow(dead_code))]
pub(crate) fn invalidate(&self) {
// NOTE: We do NOT fire this on `Drop`, since we want the proxy to be cloneable, such that
// we only need to register a single source even if there's multiple proxies in use.
self.source.invalidate();
@@ -122,54 +121,4 @@ impl EventLoopProxyProvider for EventLoopProxy {
// main loop may be sleeping (and `CFRunLoopSourceSignal` won't wake it).
self.main_loop.wake_up();
}
fn waker(&self) -> Waker {
const VTABLE: RawWakerVTable =
RawWakerVTable::new(clone_waker, wake, wake_by_ref, drop_waker);
unsafe fn clone_waker(data: *const ()) -> RawWaker {
// SAFETY: The poiner came from `CFRunLoopSource` and is valid and non-null.
let source = unsafe { &*data.cast::<CFRunLoopSource>() };
// Increment reference count.
let source = source.retain();
// Pass ownership to the raw waker.
let data: *const CFRunLoopSource = CFRetained::into_raw(source).as_ptr();
RawWaker::new(data.cast(), &VTABLE)
}
unsafe fn wake(data: *const ()) {
unsafe { wake_by_ref(data) };
unsafe { drop_waker(data) };
}
unsafe fn wake_by_ref(data: *const ()) {
// SAFETY: The poiner came from `CFRunLoopSource` and is valid and non-null.
let source = unsafe { &*data.cast::<CFRunLoopSource>() };
// Signal the source, which ends up later invoking `perform` on the main thread.
//
// Multiple signals in quick succession are automatically coalesced into a single
// signal.
source.signal();
let main_loop = CFRunLoop::main().unwrap();
// Let the main thread know there's a new event.
//
// This is required since we may be (probably are) running on a different thread, and
// the main loop may be sleeping (and `CFRunLoopSourceSignal` won't wake it).
main_loop.wake_up();
}
unsafe fn drop_waker(data: *const ()) {
let source = data.cast::<CFRunLoopSource>().cast_mut();
// SAFETY: The poiner came from `CFRunLoopSource` and is valid and non-null.
// We take ownership of a retain count here.
let _source = unsafe { CFRetained::from_raw(NonNull::new_unchecked(source)) };
}
let data: *const CFRunLoopSource = CFRetained::into_raw(self.source.clone()).as_ptr();
unsafe { Waker::from_raw(RawWaker::new(data.cast(), &VTABLE)) }
}
}

View File

@@ -0,0 +1,16 @@
//! Apple/Darwin-specific implementations
#[cfg(target_os = "macos")]
mod appkit;
mod event_handler;
mod event_loop_proxy;
mod notification_center;
#[cfg(not(target_os = "macos"))]
mod uikit;
#[allow(unused_imports)]
#[cfg(target_os = "macos")]
pub use self::appkit::*;
#[allow(unused_imports)]
#[cfg(not(target_os = "macos"))]
pub use self::uikit::*;

View File

@@ -1,4 +1,3 @@
// NOTE: This is symlinked to be contained in both the AppKit and UIKit implementations.
use std::ptr::NonNull;
use block2::RcBlock;
@@ -12,7 +11,7 @@ use objc2_foundation::{
///
/// This is used in Winit as an alternative to declaring an application delegate, as we want to
/// give the user full control over those.
pub(crate) fn create_observer(
pub fn create_observer(
center: &NSNotificationCenter,
name: &NSNotificationName,
handler: impl Fn(&NSNotification) + 'static,

View File

@@ -1,14 +1,13 @@
#![deny(unused_results)]
use std::cell::{Cell, OnceCell};
use std::cell::{OnceCell, RefCell, RefMut};
use std::collections::HashSet;
use std::os::raw::c_void;
use std::sync::{Arc, Mutex};
use std::time::Instant;
use std::{fmt, ptr};
use std::{mem, ptr};
use dispatch2::MainThreadBound;
use dpi::PhysicalSize;
use objc2::rc::Retained;
use objc2::MainThreadMarker;
use objc2_core_foundation::{
@@ -16,15 +15,16 @@ use objc2_core_foundation::{
CGSize,
};
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView};
use winit_common::core_foundation::EventLoopProxy;
use winit_common::event_handler::EventHandler;
use winit_core::application::ApplicationHandler;
use winit_core::event::{StartCause, SurfaceSizeWriter, WindowEvent};
use winit_core::event_loop::ControlFlow;
use winit_core::window::WindowId;
use crate::event_loop::ActiveEventLoop;
use crate::window::WinitUIWindow;
use super::super::event_handler::EventHandler;
use super::super::event_loop_proxy::EventLoopProxy;
use super::window::WinitUIWindow;
use super::ActiveEventLoop;
use crate::application::ApplicationHandler;
use crate::dpi::PhysicalSize;
use crate::event::{StartCause, SurfaceSizeWriter, WindowEvent};
use crate::event_loop::ControlFlow;
use crate::window::WindowId;
macro_rules! bug {
($($msg:tt)*) => {
@@ -32,6 +32,12 @@ macro_rules! bug {
};
}
macro_rules! bug_assert {
($test:expr, $($msg:tt)*) => {
assert!($test, "winit iOS bug, file an issue: {}", format!($($msg)*))
};
}
/// Get the global event handler for the application.
///
/// This is stored separately from AppState, since AppState needs to be accessible while the handler
@@ -65,90 +71,131 @@ impl EventWrapper {
}
// this is the state machine for the app lifecycle
#[derive(Clone, Copy, Debug)]
#[derive(Debug)]
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
enum AppStateImpl {
Initial,
ProcessingEvents { active_control_flow: ControlFlow },
ProcessingRedraws { active_control_flow: ControlFlow },
Waiting { start: Instant },
Initial {
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
},
ProcessingEvents {
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
active_control_flow: ControlFlow,
},
ProcessingRedraws {
active_control_flow: ControlFlow,
},
Waiting {
start: Instant,
},
PollFinished,
Terminated,
}
pub(crate) struct AppState {
state: Cell<AppStateImpl>,
control_flow: Cell<ControlFlow>,
// This should never be `None`, except for briefly during a state transition.
app_state: Option<AppStateImpl>,
control_flow: ControlFlow,
waker: EventLoopWaker,
event_loop_proxy: Arc<EventLoopProxy>,
queued_events: Cell<Vec<EventWrapper>>,
queued_gpu_redraws: Cell<HashSet<Retained<WinitUIWindow>>>,
queued_events: Vec<EventWrapper>,
}
impl fmt::Debug for AppState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AppState")
.field("control_flow", &self.control_flow)
.field("waker", &self.waker)
.field("event_loop_proxy", &self.event_loop_proxy)
.field("queued_events", &"Cell<...>")
.field("queued_gpu_redraws", &"Cell<...>")
.finish_non_exhaustive()
}
}
// SAFETY: Creating `MainThreadBound` in a `const` context,
// where there is no concept of the main thread.
static GLOBAL: MainThreadBound<OnceCell<AppState>> =
MainThreadBound::new(OnceCell::new(), unsafe { MainThreadMarker::new_unchecked() });
impl AppState {
pub(crate) fn setup_global(mtm: MainThreadMarker) -> bool {
let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || {
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
}));
GLOBAL
.get(mtm)
.set(Self {
state: Cell::new(AppStateImpl::Initial),
control_flow: Cell::new(ControlFlow::default()),
waker: EventLoopWaker::new(CFRunLoop::main().unwrap()),
event_loop_proxy,
queued_events: Cell::new(Vec::new()),
queued_gpu_redraws: Cell::new(HashSet::new()),
})
.is_ok()
pub(crate) fn get_mut(mtm: MainThreadMarker) -> RefMut<'static, AppState> {
// basically everything in UIKit requires the main thread, so it's pointless to use the
// std::sync APIs.
// must be mut because plain `static` requires `Sync`
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
#[allow(unknown_lints)] // New lint below
#[allow(static_mut_refs)] // TODO: Use `MainThreadBound` instead.
let mut guard = unsafe { APP_STATE.borrow_mut() };
if guard.is_none() {
#[inline(never)]
#[cold]
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>, mtm: MainThreadMarker) {
let waker = EventLoopWaker::new(CFRunLoop::main().unwrap());
let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || {
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
}));
**guard = Some(AppState {
app_state: Some(AppStateImpl::Initial { queued_gpu_redraws: HashSet::new() }),
control_flow: ControlFlow::default(),
waker,
event_loop_proxy,
queued_events: Vec::new(),
});
}
init_guard(&mut guard, mtm);
}
RefMut::map(guard, |state| state.as_mut().unwrap())
}
pub(crate) fn get(mtm: MainThreadMarker) -> &'static Self {
GLOBAL.get(mtm).get().expect("tried to get application state before it was registered")
fn state(&self) -> &AppStateImpl {
match &self.app_state {
Some(ref state) => state,
None => bug!("`AppState` previously failed a state transition"),
}
}
fn state_mut(&mut self) -> &mut AppStateImpl {
match &mut self.app_state {
Some(ref mut state) => state,
None => bug!("`AppState` previously failed a state transition"),
}
}
fn take_state(&mut self) -> AppStateImpl {
match self.app_state.take() {
Some(state) => state,
None => bug!("`AppState` previously failed a state transition"),
}
}
fn set_state(&mut self, new_state: AppStateImpl) {
bug_assert!(
self.app_state.is_none(),
"attempted to set an `AppState` without calling `take_state` first {:?}",
self.app_state
);
self.app_state = Some(new_state)
}
fn replace_state(&mut self, new_state: AppStateImpl) -> AppStateImpl {
match &mut self.app_state {
Some(ref mut state) => mem::replace(state, new_state),
None => bug!("`AppState` previously failed a state transition"),
}
}
fn has_launched(&self) -> bool {
!matches!(self.state.get(), AppStateImpl::Initial)
!matches!(self.state(), AppStateImpl::Initial { .. })
}
fn has_terminated(&self) -> bool {
matches!(self.state.get(), AppStateImpl::Terminated)
matches!(self.state(), AppStateImpl::Terminated)
}
fn did_finish_launching_transition(&self) {
match self.state.get() {
AppStateImpl::Initial => {},
fn did_finish_launching_transition(&mut self) {
let queued_gpu_redraws = match self.take_state() {
AppStateImpl::Initial { queued_gpu_redraws } => queued_gpu_redraws,
s => bug!("unexpected state {:?}", s),
}
self.state
.set(AppStateImpl::ProcessingEvents { active_control_flow: self.control_flow.get() });
};
self.set_state(AppStateImpl::ProcessingEvents {
active_control_flow: self.control_flow,
queued_gpu_redraws,
});
}
fn wakeup_transition(&self) -> Option<StartCause> {
fn wakeup_transition(&mut self) -> Option<StartCause> {
// before `AppState::did_finish_launching` is called, pretend there is no running
// event loop.
if !self.has_launched() || self.has_terminated() {
return None;
}
let start_cause = match (self.control_flow.get(), self.state.get()) {
let start_cause = match (self.control_flow, self.take_state()) {
(ControlFlow::Poll, AppStateImpl::PollFinished) => StartCause::Poll,
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
StartCause::WaitCancelled { start, requested_resume: None }
@@ -163,61 +210,66 @@ impl AppState {
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
};
self.state
.set(AppStateImpl::ProcessingEvents { active_control_flow: self.control_flow.get() });
self.set_state(AppStateImpl::ProcessingEvents {
queued_gpu_redraws: Default::default(),
active_control_flow: self.control_flow,
});
Some(start_cause)
}
fn main_events_cleared_transition(&self) {
let active_control_flow = match self.state.get() {
AppStateImpl::ProcessingEvents { active_control_flow } => active_control_flow,
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
let (queued_gpu_redraws, active_control_flow) = match self.take_state() {
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
(queued_gpu_redraws, active_control_flow)
},
s => bug!("unexpected state {:?}", s),
};
self.state.set(AppStateImpl::ProcessingRedraws { active_control_flow });
self.set_state(AppStateImpl::ProcessingRedraws { active_control_flow });
queued_gpu_redraws
}
fn events_cleared_transition(&self) {
fn events_cleared_transition(&mut self) {
if !self.has_launched() || self.has_terminated() {
return;
}
let old = match self.state.get() {
let old = match self.take_state() {
AppStateImpl::ProcessingRedraws { active_control_flow } => active_control_flow,
s => bug!("unexpected state {:?}", s),
};
let new = self.control_flow.get();
let new = self.control_flow;
match (old, new) {
(ControlFlow::Wait, ControlFlow::Wait) => {
let start = Instant::now();
self.state.set(AppStateImpl::Waiting { start });
self.set_state(AppStateImpl::Waiting { start });
self.waker.stop()
},
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
if old_instant == new_instant =>
{
let start = Instant::now();
self.state.set(AppStateImpl::Waiting { start });
self.set_state(AppStateImpl::Waiting { start });
},
(_, ControlFlow::Wait) => {
let start = Instant::now();
self.state.set(AppStateImpl::Waiting { start });
self.set_state(AppStateImpl::Waiting { start });
self.waker.stop()
},
(_, ControlFlow::WaitUntil(new_instant)) => {
let start = Instant::now();
self.state.set(AppStateImpl::Waiting { start });
self.set_state(AppStateImpl::Waiting { start });
self.waker.start_at(new_instant)
},
// Unlike on macOS, handle Poll to Poll transition here to call the waker
(_, ControlFlow::Poll) => {
self.state.set(AppStateImpl::PollFinished);
self.set_state(AppStateImpl::PollFinished);
self.waker.start()
},
}
}
fn terminated_transition(&self) {
match self.state.replace(AppStateImpl::Terminated) {
fn terminated_transition(&mut self) {
match self.replace_state(AppStateImpl::Terminated) {
AppStateImpl::ProcessingEvents { .. } => {},
s => bug!("terminated while not processing events {:?}", s),
}
@@ -227,27 +279,26 @@ impl AppState {
&self.event_loop_proxy
}
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow);
pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) {
self.control_flow = control_flow;
}
pub(crate) fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
self.control_flow
}
}
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) {
let this = AppState::get(mtm);
match this.state.get() {
AppStateImpl::Initial | AppStateImpl::ProcessingEvents { .. } => {
let mut queued_gpu_redraws = this.queued_gpu_redraws.take();
let mut this = AppState::get_mut(mtm);
match this.state_mut() {
&mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. }
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. } => {
let _ = queued_gpu_redraws.insert(window);
this.queued_gpu_redraws.set(queued_gpu_redraws);
},
s @ AppStateImpl::ProcessingRedraws { .. }
| s @ AppStateImpl::Waiting { .. }
| s @ AppStateImpl::PollFinished => bug!("unexpected state {:?}", s),
AppStateImpl::Terminated => {
s @ &mut AppStateImpl::ProcessingRedraws { .. }
| s @ &mut AppStateImpl::Waiting { .. }
| s @ &mut AppStateImpl::PollFinished => bug!("unexpected state {:?}", s),
&mut AppStateImpl::Terminated => {
panic!("Attempt to create a `Window` after the app has terminated")
},
}
@@ -262,10 +313,14 @@ pub(crate) fn launch<R>(
}
pub fn did_finish_launching(mtm: MainThreadMarker) {
let this = AppState::get(mtm);
let mut this = AppState::get_mut(mtm);
this.waker.start();
this.did_finish_launching_transition();
// have to drop RefMut because the window setup code below can trigger new events
drop(this);
AppState::get_mut(mtm).did_finish_launching_transition();
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, StartCause::Init));
get_handler(mtm).handle(|app| app.can_create_surfaces(&ActiveEventLoop { mtm }));
@@ -274,11 +329,12 @@ pub fn did_finish_launching(mtm: MainThreadMarker) {
// AppState::did_finish_launching handles the special transition `Init`
pub fn handle_wakeup_transition(mtm: MainThreadMarker) {
let this = AppState::get(mtm);
let mut this = AppState::get_mut(mtm);
let cause = match this.wakeup_transition() {
None => return,
Some(cause) => cause,
};
drop(this);
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, cause));
handle_nonuser_events(mtm, []);
@@ -292,20 +348,19 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
mtm: MainThreadMarker,
events: I,
) {
let this = AppState::get(mtm);
let mut this = AppState::get_mut(mtm);
if this.has_terminated() {
return;
}
if !get_handler(mtm).ready() {
// Prevent re-entrancy; queue the events up for once we're done handling the event instead.
let mut queued_events = this.queued_events.take();
queued_events.extend(events);
this.queued_events.set(queued_events);
this.queued_events.extend(events);
return;
}
let processing_redraws = matches!(this.state.get(), AppStateImpl::ProcessingRedraws { .. });
let processing_redraws = matches!(this.state(), AppStateImpl::ProcessingRedraws { .. });
drop(this);
for event in events {
if !processing_redraws && event.is_redraw() {
@@ -320,10 +375,12 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
}
loop {
let queued_events = this.queued_events.take();
let mut this = AppState::get_mut(mtm);
let queued_events = mem::take(&mut this.queued_events);
if queued_events.is_empty() {
break;
}
drop(this);
for event in queued_events {
if !processing_redraws && event.is_redraw() {
@@ -340,16 +397,19 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
}
fn handle_user_events(mtm: MainThreadMarker) {
let this = AppState::get(mtm);
if matches!(this.state.get(), AppStateImpl::ProcessingRedraws { .. }) {
let this = AppState::get_mut(mtm);
if matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }) {
bug!("user events attempted to be sent out while `ProcessingRedraws`");
}
drop(this);
loop {
let queued_events = this.queued_events.take();
let mut this = AppState::get_mut(mtm);
let queued_events = mem::take(&mut this.queued_events);
if queued_events.is_empty() {
break;
}
drop(this);
for event in queued_events {
handle_wrapped_event(mtm, event);
@@ -374,23 +434,28 @@ pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, o
}
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
let this = AppState::get(mtm);
let mut this = AppState::get_mut(mtm);
if !this.has_launched() || this.has_terminated() {
return;
}
match this.state.get() {
match this.state_mut() {
AppStateImpl::ProcessingEvents { .. } => {},
_ => bug!("`ProcessingRedraws` happened unexpectedly"),
};
drop(this);
handle_user_events(mtm);
this.main_events_cleared_transition();
let queued_gpu_redraws = this.queued_gpu_redraws.take();
let redraw_events = queued_gpu_redraws.into_iter().map(|window| EventWrapper::Window {
window_id: window.id(),
event: WindowEvent::RedrawRequested,
});
let mut this = AppState::get_mut(mtm);
let redraw_events: Vec<EventWrapper> = this
.main_events_cleared_transition()
.into_iter()
.map(|window| EventWrapper::Window {
window_id: window.id(),
event: WindowEvent::RedrawRequested,
})
.collect();
drop(this);
handle_nonuser_events(mtm, redraw_events);
get_handler(mtm).handle(|app| app.about_to_wait(&ActiveEventLoop { mtm }));
@@ -398,7 +463,7 @@ pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
}
pub fn handle_events_cleared(mtm: MainThreadMarker) {
AppState::get(mtm).events_cleared_transition();
AppState::get_mut(mtm).events_cleared_transition();
}
pub(crate) fn handle_resumed(mtm: MainThreadMarker) {
@@ -431,10 +496,11 @@ pub(crate) fn terminated(application: &UIApplication) {
}
handle_nonuser_events(mtm, events);
let this = AppState::get(mtm);
let mut this = AppState::get_mut(mtm);
this.terminated_transition();
// Prevent EventLoopProxy from firing again.
this.event_loop_proxy.invalidate();
drop(this);
get_handler(mtm).terminate();
}
@@ -475,7 +541,6 @@ fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Retained<UIView>, CGRec
(view, screen_frame)
}
#[derive(Debug)]
struct EventLoopWaker {
timer: CFRetained<CFRunLoopTimer>,
}
@@ -509,15 +574,15 @@ impl EventLoopWaker {
}
}
fn stop(&self) {
fn stop(&mut self) {
self.timer.set_next_fire_date(f64::MAX);
}
fn start(&self) {
fn start(&mut self) {
self.timer.set_next_fire_date(f64::MIN);
}
fn start_at(&self, instant: Instant) {
fn start_at(&mut self, instant: Instant) {
let now = Instant::now();
if now >= instant {
self.start();

View File

@@ -16,35 +16,33 @@ use objc2_ui_kit::{
UIApplicationWillResignActiveNotification, UIApplicationWillTerminateNotification, UIScreen,
};
use rwh_06::HasDisplayHandle;
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
use winit_core::event_loop::{
use super::super::notification_center::create_observer;
use super::app_state::{send_occluded_event_for_all_windows, AppState};
use super::{app_state, monitor, MonitorHandle};
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use winit_core::monitor::MonitorHandle as CoreMonitorHandle;
use winit_core::window::{Theme, Window as CoreWindow};
use super::app_state::{send_occluded_event_for_all_windows, AppState};
use super::notification_center::create_observer;
use crate::monitor::MonitorHandle;
use crate::window::Window;
use crate::{app_state, monitor};
use crate::monitor::MonitorHandle as CoreMonitorHandle;
use crate::platform_impl::Window;
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window as CoreWindow};
#[derive(Debug)]
pub struct ActiveEventLoop {
pub(crate) struct ActiveEventLoop {
pub(super) mtm: MainThreadMarker,
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy {
CoreEventLoopProxy::new(AppState::get(self.mtm).event_loop_proxy().clone())
CoreEventLoopProxy::new(AppState::get_mut(self.mtm).event_loop_proxy().clone())
}
fn create_window(
&self,
window_attributes: winit_core::window::WindowAttributes,
window_attributes: crate::window::WindowAttributes,
) -> Result<Box<dyn CoreWindow>, RequestError> {
Ok(Box::new(Window::new(self, window_attributes)?))
}
@@ -64,7 +62,7 @@ impl RootActiveEventLoop for ActiveEventLoop {
)
}
fn primary_monitor(&self) -> Option<winit_core::monitor::MonitorHandle> {
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
#[allow(deprecated)]
let monitor = MonitorHandle::new(UIScreen::mainScreen(self.mtm));
Some(CoreMonitorHandle(Arc::new(monitor)))
@@ -73,7 +71,7 @@ impl RootActiveEventLoop for ActiveEventLoop {
fn listen_device_events(&self, _allowed: DeviceEvents) {}
fn set_control_flow(&self, control_flow: ControlFlow) {
AppState::get(self.mtm).set_control_flow(control_flow)
AppState::get_mut(self.mtm).set_control_flow(control_flow)
}
fn system_theme(&self) -> Option<Theme> {
@@ -81,7 +79,7 @@ impl RootActiveEventLoop for ActiveEventLoop {
}
fn control_flow(&self) -> ControlFlow {
AppState::get(self.mtm).control_flow()
AppState::get_mut(self.mtm).control_flow()
}
fn exit(&self) {
@@ -139,16 +137,22 @@ pub struct EventLoop {
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct PlatformSpecificEventLoopAttributes {}
pub(crate) struct PlatformSpecificEventLoopAttributes {}
impl EventLoop {
pub fn new(_: &PlatformSpecificEventLoopAttributes) -> Result<EventLoop, EventLoopError> {
pub(crate) fn new(
_: &PlatformSpecificEventLoopAttributes,
) -> Result<EventLoop, EventLoopError> {
let mtm = MainThreadMarker::new()
.expect("On iOS, `EventLoop` must be created on the main thread");
if !AppState::setup_global(mtm) {
// Required, AppState is global state, and event loop can only be run once.
return Err(EventLoopError::RecreationAttempt);
static mut SINGLETON_INIT: bool = false;
unsafe {
assert!(
!SINGLETON_INIT,
"Only one `EventLoop` is supported on iOS. `EventLoopProxy` might be helpful"
);
SINGLETON_INIT = true;
}
// this line sets up the main run loop before `UIApplicationMain`

View File

@@ -0,0 +1,25 @@
#![allow(clippy::let_unit_value)]
mod app_state;
mod event_loop;
mod monitor;
mod view;
mod view_controller;
mod window;
use std::fmt;
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::MonitorHandle;
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
#[derive(Debug)]
pub enum OsError {}
impl fmt::Display for OsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "os error")
}
}

View File

@@ -5,12 +5,13 @@ use std::num::NonZeroU32;
use std::{fmt, hash, ptr};
use dispatch2::{run_on_main, MainThreadBound};
use dpi::PhysicalPosition;
use objc2::rc::Retained;
use objc2::{available, MainThreadMarker, Message};
use objc2_foundation::NSInteger;
use objc2_ui_kit::{UIScreen, UIScreenMode};
use winit_core::monitor::{MonitorHandleProvider, VideoMode};
use crate::dpi::PhysicalPosition;
use crate::monitor::{MonitorHandleProvider, VideoMode};
// Workaround for `MainThreadBound` implementing almost no traits
#[derive(Debug)]
@@ -54,11 +55,11 @@ impl VideoModeHandle {
) -> VideoModeHandle {
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
let size = screen_mode.size();
let mode = VideoMode::new(
(size.width as u32, size.height as u32).into(),
None,
let mode = VideoMode {
size: (size.width as u32, size.height as u32).into(),
bit_depth: None,
refresh_rate_millihertz,
);
};
VideoModeHandle {
mode,

View File

@@ -1,7 +1,6 @@
#![allow(clippy::unnecessary_cast)]
use std::cell::{Cell, RefCell};
use dpi::PhysicalPosition;
use objc2::rc::Retained;
use objc2::runtime::{NSObjectProtocol, ProtocolObject};
use objc2::{available, define_class, msg_send, sel, DefinedClass, MainThreadMarker};
@@ -14,14 +13,16 @@ use objc2_ui_kit::{
UITouchPhase, UITouchType, UITraitEnvironment, UIView,
};
use tracing::debug;
use winit_core::event::{
ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource, TouchPhase,
WindowEvent,
};
use winit_core::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
use super::app_state::{self, EventWrapper};
use super::window::WinitUIWindow;
use crate::dpi::PhysicalPosition;
use crate::event::{
ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource, TouchPhase,
WindowEvent,
};
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
use crate::window::WindowAttributes;
pub struct WinitViewState {
pinch_gesture_recognizer: RefCell<Option<Retained<UIPinchGestureRecognizer>>>,
@@ -64,7 +65,7 @@ define_class!(
let frame = self.frame();
let scale_factor = self.contentScaleFactor() as f64;
let size = dpi::LogicalSize {
let size = crate::dpi::LogicalSize {
width: frame.size.width as f64,
height: frame.size.height as f64,
}
@@ -103,7 +104,7 @@ define_class!(
);
let scale_factor = scale_factor as f64;
let frame = self.frame();
let size = dpi::LogicalSize {
let size = crate::dpi::LogicalSize {
width: frame.size.width as f64,
height: frame.size.height as f64,
};
@@ -336,7 +337,7 @@ define_class!(
impl WinitView {
pub(crate) fn new(
mtm: MainThreadMarker,
scale_factor: Option<f64>,
window_attributes: &WindowAttributes,
frame: CGRect,
) -> Retained<Self> {
let this = mtm.alloc().set_ivars(WinitViewState {
@@ -356,7 +357,7 @@ impl WinitView {
this.setMultipleTouchEnabled(true);
if let Some(scale_factor) = scale_factor {
if let Some(scale_factor) = window_attributes.platform_specific.scale_factor {
this.setContentScaleFactor(scale_factor as _);
}

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