mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
86 Commits
notgull/ia
...
rwh-send-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e66eba38f5 | ||
|
|
e9a25a4c91 | ||
|
|
674657efb6 | ||
|
|
7d5bee767c | ||
|
|
745cfaab2c | ||
|
|
a8f49dc8ef | ||
|
|
e5310ade08 | ||
|
|
37946e0a3a | ||
|
|
86b737f5e7 | ||
|
|
e37585e5bc | ||
|
|
4aeeb24745 | ||
|
|
8cd3aaa8a2 | ||
|
|
2c15de7cf9 | ||
|
|
0a7ea61834 | ||
|
|
4ee11018c2 | ||
|
|
4f669ebbd2 | ||
|
|
7761b2b16c | ||
|
|
ae41e3265f | ||
|
|
8702a09333 | ||
|
|
8b5c84f404 | ||
|
|
a676d0018b | ||
|
|
04ca85a909 | ||
|
|
cc33212479 | ||
|
|
f2c5127f27 | ||
|
|
af93167237 | ||
|
|
7f6b16a6af | ||
|
|
bf5806a9b2 | ||
|
|
2a9c593e01 | ||
|
|
becdd0dbd2 | ||
|
|
3eea505440 | ||
|
|
b863283c38 | ||
|
|
73718c9f2f | ||
|
|
f735f028a1 | ||
|
|
da947992ac | ||
|
|
e9784127df | ||
|
|
0be2bb0a8c | ||
|
|
075996b1fa | ||
|
|
a7241b3db3 | ||
|
|
17296e9878 | ||
|
|
b3c87caa7c | ||
|
|
81a1d9c396 | ||
|
|
5612626944 | ||
|
|
d3ca685b77 | ||
|
|
7bed5eecfd | ||
|
|
14140607d1 | ||
|
|
eab982c402 | ||
|
|
21701a33de | ||
|
|
c89e6df758 | ||
|
|
e9210555c1 | ||
|
|
0994b5ceb8 | ||
|
|
bcce5134e1 | ||
|
|
d333dd8664 | ||
|
|
52af1b4a77 | ||
|
|
3c9f9da19e | ||
|
|
075dfcea19 | ||
|
|
92b7dcccc1 | ||
|
|
5a3be586f4 | ||
|
|
12dbbf8012 | ||
|
|
53ca5af48f | ||
|
|
c235bd154a | ||
|
|
f4e71a1d9c | ||
|
|
62ed51a138 | ||
|
|
b2a2ec91ae | ||
|
|
d37d1a03b2 | ||
|
|
772b21ce09 | ||
|
|
2edcd09704 | ||
|
|
d35c3bea42 | ||
|
|
89a184ed84 | ||
|
|
36d4907da8 | ||
|
|
98b3508aca | ||
|
|
c0db53a516 | ||
|
|
52b7205b75 | ||
|
|
41dbbc27a0 | ||
|
|
c346fb7e61 | ||
|
|
6a041f84ba | ||
|
|
acfeff5327 | ||
|
|
b9e1e96eaa | ||
|
|
880238a24f | ||
|
|
f5b4d6938f | ||
|
|
c65e2247a1 | ||
|
|
801fddbfcf | ||
|
|
3ad64fb811 | ||
|
|
9bf4493a21 | ||
|
|
48f6582eb4 | ||
|
|
ef34692148 | ||
|
|
c48116a8fd |
72
.github/workflows/ci.yml
vendored
72
.github/workflows/ci.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain: [stable, nightly, '1.65.0']
|
||||
toolchain: [stable, nightly, '1.70.0']
|
||||
platform:
|
||||
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
|
||||
- { name: 'Windows 64bit MSVC', target: x86_64-pc-windows-msvc, os: windows-latest, }
|
||||
@@ -35,20 +35,19 @@ 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: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, }
|
||||
- { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, }
|
||||
- { name: 'iOS Aarch64', target: aarch64-apple-ios, os: macos-latest, }
|
||||
- { name: 'web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
|
||||
include:
|
||||
exclude:
|
||||
# Android is tested on stable-3
|
||||
- toolchain: '1.69.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity' }
|
||||
- toolchain: 'stable'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity' }
|
||||
- toolchain: 'nightly'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity' }
|
||||
|
||||
- toolchain: '1.70.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
include:
|
||||
- toolchain: '1.70.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
|
||||
env:
|
||||
# Set more verbose terminal output
|
||||
@@ -60,6 +59,7 @@ jobs:
|
||||
RUSTDOCFLAGS: '--deny=warnings'
|
||||
|
||||
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
|
||||
CMD: ${{ matrix.platform.cmd }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -87,11 +87,23 @@ jobs:
|
||||
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
||||
run: sudo apt-get update && sudo apt-get install gcc-multilib
|
||||
|
||||
- name: Install xbuild
|
||||
uses: taiki-e/install-action@v2
|
||||
if: contains(matrix.platform.target, 'android') || contains(matrix.platform.target, 'ios')
|
||||
- name: Cache cargo-apk
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
id: cargo-apk-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
tool: xbuild@0.2.0
|
||||
path: ~/.cargo/bin/cargo-apk
|
||||
# Change this key if we update the required cargo-apk version
|
||||
key: cargo-apk-v0-9-7
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install cargo-apk
|
||||
if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
|
||||
run: cargo install cargo-apk --version=^0.9.7 --locked
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -99,30 +111,17 @@ jobs:
|
||||
targets: ${{ matrix.platform.target }}
|
||||
components: clippy
|
||||
|
||||
- name: Install LLVM tools for Android
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
run: sudo apt-get update && sudo apt-get install lld llvm
|
||||
|
||||
- name: Check documentation
|
||||
run: cargo doc --no-deps $OPTIONS --document-private-items
|
||||
|
||||
- name: Build crate
|
||||
run: cargo build $OPTIONS
|
||||
|
||||
- name: Build and package crate for Android
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
run: x build -p android-xbuild-target --platform android --arch arm64
|
||||
|
||||
- name: Build and package crate for iOS
|
||||
if: contains(matrix.platform.target, 'ios')
|
||||
run: x build -p ios-xbuild-target --platform ios --arch arm64
|
||||
- name: Build crate
|
||||
run: cargo $CMD build $OPTIONS
|
||||
|
||||
- name: Build tests
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo test --no-run $OPTIONS
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS
|
||||
|
||||
- name: Run tests
|
||||
if: >
|
||||
@@ -130,8 +129,8 @@ jobs:
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo test $OPTIONS
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test $OPTIONS
|
||||
|
||||
- name: Lint with clippy
|
||||
if: (matrix.toolchain == 'stable') && !contains(matrix.platform.options, '--no-default-features')
|
||||
@@ -140,9 +139,8 @@ jobs:
|
||||
- name: Build tests with serde enabled
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo test --no-run $OPTIONS --features serde
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS --features serde
|
||||
|
||||
- name: Run tests with serde enabled
|
||||
if: >
|
||||
@@ -150,8 +148,8 @@ jobs:
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo test $OPTIONS --features serde
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo $CMD test $OPTIONS --features serde
|
||||
|
||||
# See restore step above
|
||||
- name: Save cache of cargo folder
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "deps/apk-builder"]
|
||||
path = deps/apk-builder
|
||||
url = https://github.com/rust-windowing/android-rs-glue
|
||||
348
CHANGELOG.md
348
CHANGELOG.md
@@ -2,49 +2,130 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
Please keep one empty line before and after all headers. (This is required for `git` to produce a conflict when a release is made while a PR is open and the PR's changelog entry would go into the wrong section).
|
||||
Please keep one empty line before and after all headers. (This is required for
|
||||
`git` to produce a conflict when a release is made while a PR is open and the
|
||||
PR's changelog entry would go into the wrong section).
|
||||
|
||||
And please only add new entries to the top of this list, right below the `# Unreleased` header.
|
||||
And please only add new entries to the top of this list, right below the `#
|
||||
Unreleased` header.
|
||||
|
||||
# Unreleased
|
||||
|
||||
- Renamed `EventLoopExtRunOnDemand` / `run_ondemand` to `EventLoopExtRunOnDemand` / `run_on_demand`.
|
||||
- Make iOS `MonitorHandle` and `VideoMode` usable from other threads.
|
||||
- On Web, `ControlFlow::WaitUntil` now uses the Prioritized Task Scheduling API. `setTimeout()`, with a trick to circumvent throttling to 4ms, is used as a fallback.
|
||||
- On Web, never return a `MonitorHandle`.
|
||||
- **Breaking:** Move `Event::RedrawRequested` to `WindowEvent::RedrawRequested`.
|
||||
- On macOS, fix crash in `window.set_minimized(false)`.
|
||||
- On Web, enable event propagation and let `DeviceEvent`s appear after `WindowEvent`s.
|
||||
- On Web, take all transient activations on the canvas and window into account to queue a fullscreen request.
|
||||
- On Web, remove any fullscreen requests from the queue when an external fullscreen activation was detected.
|
||||
- On Wayland, fix `TouchPhase::Canceled` being sent for moved events.
|
||||
- Mark `startup_notify` unsafe functions as safe.
|
||||
- Fix a bug where Wayland would be chosen on Linux even if the user specified `with_x11`. (#3058)
|
||||
- **Breaking:** Moved `ControlFlow` to `EventLoopWindowTarget::set_control_flow()` and `EventLoopWindowTarget::control_flow()`.
|
||||
- **Breaking:** Moved `ControlFlow::Exit` to `EventLoopWindowTarget::exit()` and `EventLoopWindowTarget::exiting()` and removed `ControlFlow::ExitWithCode(_)` entirely.
|
||||
- On Web, add `EventLoopWindowTargetExtWebSys` and `PollStrategy`, which allows to set different strategies for `ControlFlow::Poll`. By default the Prioritized Task Scheduling API is used, but an option to use `Window.requestIdleCallback` is available as well. Both use `setTimeout()`, with a trick to circumvent throttling to 4ms, as a fallback.
|
||||
- Implement `PartialOrd` and `Ord` for `MouseButton`.
|
||||
- On X11, fix event loop not waking up on `ControlFlow::Poll` and `ControlFlow::WaitUntil`.
|
||||
- On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example.
|
||||
- Add `Window::set_custom_cursor`
|
||||
- Add `CustomCursor`
|
||||
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
|
||||
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
|
||||
- On macOS, add services menu.
|
||||
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
|
||||
- On Web, fix setting cursor icon overriding cursor visibility.
|
||||
- On Web, fix context menu not being disabled by `with_prevent_default(true)`.
|
||||
- **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
|
||||
- **Breaking:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available.
|
||||
- **Breaking:** Bump MSRV from `1.65` to `1.70`.
|
||||
|
||||
# 0.29.5
|
||||
|
||||
- On macOS, remove spurious error logging when handling `Fn`.
|
||||
- On X11, fix an issue where floating point data from the server is
|
||||
misinterpreted during a drag and drop operation.
|
||||
- On X11, fix a bug where focusing the window would panic.
|
||||
- On macOS, fix `refresh_rate_millihertz`.
|
||||
- On Wayland, disable Client Side Decorations when `wl_subcompositor` is not supported.
|
||||
- On X11, fix `Xft.dpi` detection from Xresources.
|
||||
- On Windows, fix consecutive calls to `window.set_fullscreen(Some(Fullscreen::Borderless(None)))` resulting in losing previous window state when eventually exiting fullscreen using `window.set_fullscreen(None)`.
|
||||
- On Wayland, fix resize being sent on focus change.
|
||||
- On Windows, fix `set_ime_cursor_area`.
|
||||
|
||||
# 0.29.4
|
||||
|
||||
- Fix crash when running iOS app on macOS.
|
||||
- On X11, check common alternative cursor names when loading cursor.
|
||||
- On X11, reload the DPI after a property change event.
|
||||
- On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread.
|
||||
- On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account.
|
||||
- On macOS, send a `Resized` event after each `ScaleFactorChanged` event.
|
||||
- On Wayland, fix `wl_surface` being destroyed before associated objects.
|
||||
- On macOS, fix assertion when pressing `Fn` key.
|
||||
|
||||
# 0.29.3
|
||||
|
||||
- On Wayland, apply correct scale to `PhysicalSize` passed in `WindowBuilder::with_inner_size` when possible.
|
||||
- On Wayland, fix `RedrawRequsted` being always sent without decorations and `sctk-adwaita` feature.
|
||||
- On Wayland, ignore resize requests when the window is fully tiled.
|
||||
- On Wayland, use `configure_bounds` to constrain `with_inner_size` when compositor wants users to pick size.
|
||||
- On Windows, fix deadlock when accessing the state during `Cursor{Enter,Leave}`.
|
||||
- On Windows, add support for `Window::set_transparent`.
|
||||
- On macOS, fix deadlock when entering a nested event loop from an event handler.
|
||||
- On macOS, add support for `Window::set_blur`.
|
||||
|
||||
# 0.29.2
|
||||
|
||||
- **Breaking:** Bump MSRV from `1.60` to `1.65`.
|
||||
- **Breaking:** Add `Event::MemoryWarning`; implemented on iOS/Android.
|
||||
- **Breaking:** Bump `ndk` version to `0.8.0`, ndk-sys to `0.5.0`, `android-activity` to `0.5.0`.
|
||||
- **Breaking:** Change default `ControlFlow` from `Poll` to `Wait`.
|
||||
- **Breaking:** remove `DeviceEvent::Text`.
|
||||
- On Android, fix `DeviceId` to contain device id's.
|
||||
- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now.
|
||||
- On Web, fix `ControlFlow::WaitUntil` to never wake up **before** the given time.
|
||||
- Add `Window::show_window_menu` to request a titlebar/system menu; implemented on Wayland/Windows for now.
|
||||
- On iOS, send events `WindowEvent::Occluded(false)`, `WindowEvent::Occluded(true)` when application enters/leaves foreground.
|
||||
- **Breaking** add `Event::MemoryWarning`; implemented on iOS/Android.
|
||||
- On Wayland, support `Occluded` event with xdg-shell v6
|
||||
|
||||
# 0.29.1-beta
|
||||
|
||||
- **Breaking:** Bump `ndk` version to `0.8.0-beta.0`, ndk-sys to `v0.5.0-beta.0`, `android-activity` to `0.5.0-beta.1`.
|
||||
- **Breaking:** Bump MSRV from `1.64` to `1.65`.
|
||||
- Make iOS windows usable from other threads.
|
||||
- Reexport `raw-window-handle` in `window` module.
|
||||
- **Breaking:** `WINIT_UNIX_BACKEND` was removed in favor of standard `WAYLAND_DISPLAY` and `DISPLAY` variables.
|
||||
- **Breaking:** Move `Event::RedrawRequested` to `WindowEvent::RedrawRequested`.
|
||||
- **Breaking:** Moved `ControlFlow::Exit` to `EventLoopWindowTarget::exit()` and `EventLoopWindowTarget::exiting()` and removed `ControlFlow::ExitWithCode(_)` entirely.
|
||||
- **Breaking:** Moved `ControlFlow` to `EventLoopWindowTarget::set_control_flow()` and `EventLoopWindowTarget::control_flow()`.
|
||||
- **Breaking:** `EventLoop::new` and `EventLoopBuilder::build` now return `Result<Self, EventLoopError>`
|
||||
- On X11, set `visual_id` in returned `raw-window-handle`.
|
||||
- **Breaking:** on Wayland, dispatching user created wayland queue won't wake up the loop unless winit has event to send back.
|
||||
- **Breaking:** `WINIT_UNIX_BACKEND` was removed in favor of standard `WAYLAND_DISPLAY` and `DISPLAY` variables.
|
||||
- **Breaking:** on Wayland, dispatching user created Wayland queue won't wake up the loop unless winit has event to send back.
|
||||
- **Breaking:** remove `DeviceEvent::Text`.
|
||||
- **Breaking:** Remove lifetime parameter from `Event` and `WindowEvent`.
|
||||
- **Breaking:** Rename `Window::set_inner_size` to `Window::request_inner_size` and indicate if the size was applied immediately.
|
||||
- **Breaking:** `ActivationTokenDone` event which could be requested with the new `startup_notify` module, see its docs for more.
|
||||
- **Breaking:** `ScaleFactorChanged` now contains a writer instead of a reference to update inner size.
|
||||
- **Breaking** `run() -> !` has been replaced by `run() -> Result<(), EventLoopError>` for returning errors without calling `std::process::exit()` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
|
||||
- **Breaking** Removed `EventLoopExtRunReturn` / `run_return` in favor of `EventLoopExtPumpEvents` / `pump_events` and `EventLoopExtRunOnDemand` / `run_on_demand` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
|
||||
- `RedrawRequested` is no longer guaranteed to be emitted after `MainEventsCleared`, it is now platform-specific when the event is emitted after being requested via `redraw_request()`.
|
||||
- On Windows, `RedrawRequested` is now driven by `WM_PAINT` messages which are requested via `redraw_request()`
|
||||
- **Breaking** `LoopDestroyed` renamed to `LoopExiting` ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||
- **Breaking** `RedrawEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||
- **Breaking** `MainEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||
- **Breaking:** Remove all deprecated `modifiers` fields.
|
||||
- **Breaking:** Rename `DeviceEventFilter` to `DeviceEvents` reversing the behavior of variants.
|
||||
- **Breaking** Add `AboutToWait` event which is emitted when the event loop is about to block and wait for new events ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||
- **Breaking:** Rename `EventLoopWindowTarget::set_device_event_filter` to `listen_device_events`.
|
||||
- **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone.
|
||||
- **Breaking:** `with_x11_visual` now takes the visual ID instead of the bare pointer.
|
||||
- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events on Wayland, X11, Windows, macOS and Web.
|
||||
- **Breaking:** On Web, `instant` is now replaced by `web_time`.
|
||||
- **Breaking:** On Web, dropped support for Safari versions below 13.1.
|
||||
- **Breaking:** On Web, the canvas output bitmap size is no longer adjusted.
|
||||
- **Breaking:** On Web, the canvas size is not controlled by Winit anymore and external changes to the canvas size will be reported through `WindowEvent::Resized`.
|
||||
- **Breaking:** Updated `bitflags` crate version to `2`, which changes the API on exposed types.
|
||||
- **Breaking:** `CursorIcon::Arrow` was removed.
|
||||
- **Breaking:** `CursorIcon::Hand` is now named `CursorIcon::Pointer`.
|
||||
- **Breaking:** `CursorIcon` is now used from the `cursor-icon` crate.
|
||||
- **Breaking:** `WindowExtWebSys::canvas()` now returns an `Option`.
|
||||
- **Breaking:** Overhaul keyboard input handling.
|
||||
- Replace `KeyboardInput` with `KeyEvent` and `RawKeyEvent`.
|
||||
- Change `WindowEvent::KeyboardInput` to contain a `KeyEvent`.
|
||||
- Change `Event::Key` to contain a `RawKeyEvent`.
|
||||
- Remove `Event::ReceivedCharacter`. In its place, you should use
|
||||
`KeyEvent.text` in combination with `WindowEvent::Ime`.
|
||||
- Replace `VirtualKeyCode` with the `Key` enum.
|
||||
- Replace `ScanCode` with the `KeyCode` enum.
|
||||
- Rename `ModifiersState::LOGO` to `SUPER` and `ModifiersState::CTRL` to `CONTROL`.
|
||||
- Add `PhysicalKey` wrapping `KeyCode` and `NativeKeyCode`.
|
||||
- Add `KeyCode` to refer to keys (roughly) by their physical location.
|
||||
- Add `NativeKeyCode` to represent raw `KeyCode`s which Winit doesn't
|
||||
understand.
|
||||
- Add `Key` to represent the keys after they've been interpreted by the
|
||||
active (software) keyboard layout.
|
||||
- Add `NamedKey` to represent the categorized keys.
|
||||
- Add `NativeKey` to represent raw `Key`s which Winit doesn't understand.
|
||||
- Add `KeyLocation` to tell apart `Key`s which usually "mean" the same thing,
|
||||
but can appear simultaneously in different spots on the same keyboard
|
||||
layout.
|
||||
- Add `Window::reset_dead_keys` to enable application-controlled cancellation
|
||||
of dead key sequences.
|
||||
- Add `KeyEventExtModifierSupplement` to expose additional (and less
|
||||
portable) interpretations of a given key-press.
|
||||
- Add `PhysicalKeyExtScancode`, which lets you convert between scancodes and
|
||||
`PhysicalKey`.
|
||||
- `ModifiersChanged` now uses dedicated `Modifiers` struct.
|
||||
- Removed platform-specific extensions that should be retrieved through `raw-window-handle` trait implementations instead:
|
||||
- `platform::windows::HINSTANCE`.
|
||||
- `WindowExtWindows::hinstance`.
|
||||
@@ -61,126 +142,89 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||
- `WindowExtX11::xlib_display`.
|
||||
- `WindowExtX11::xlib_screen_id`.
|
||||
- `WindowExtX11::xcb_connection`.
|
||||
- On Web, use `Window.requestAnimationFrame()` to throttle `RedrawRequested` events.
|
||||
- On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor.
|
||||
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
|
||||
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.
|
||||
- **Breaking:** Remove lifetime parameter from `Event` and `WindowEvent`.
|
||||
- **Breaking:** `ScaleFactorChanged` now contains a writer instead of a reference to update inner size.
|
||||
- On iOS, always wake the event loop when transitioning from `ControlFlow::Poll` to `ControlFlow::Poll`.
|
||||
- **Breaking:** `ActivationTokenDone` event which could be requested with the new `startup_notify` module, see its docs for more.
|
||||
- On Wayland, make double clicking and moving the CSD frame more reliable.
|
||||
- On macOS, add tabbing APIs on `WindowExtMacOS` and `EventLoopWindowTargetExtMacOS`.
|
||||
- **Breaking:** Rename `Window::set_inner_size` to `Window::request_inner_size` and indicate if the size was applied immediately.
|
||||
- On X11, fix false positive flagging of key repeats when pressing different keys with no release between presses.
|
||||
- Implement `PartialOrd` and `Ord` for `Key`, `KeyCode`, `NativeKey`, and `NativeKeyCode`.
|
||||
- Reexport `raw-window-handle` in `window` module.
|
||||
- Add `ElementState::is_pressed`.
|
||||
- On Web, implement `WindowEvent::Occluded`.
|
||||
- On Web, fix touch location to be as accurate as mouse position.
|
||||
- On Web, account for CSS `padding`, `border`, and `margin` when getting or setting the canvas position.
|
||||
- On Web, add Fullscreen API compatibility for Safari.
|
||||
- On Web, implement `Window::set_(min|max)_inner_size()`.
|
||||
- On Web, fix some `Window` methods using incorrect HTML attributes instead of CSS properties.
|
||||
- On Web, fix some `WindowBuilder` methods doing nothing.
|
||||
- On Web, implement `Window::focus_window()`.
|
||||
- On Web, remove unnecessary `Window::is_dark_mode()`, which was replaced with `Window::theme()`.
|
||||
- On Web, add `WindowBuilderExtWebSys::with_append()` to append the canvas element to the web page on creation.
|
||||
- On Windows, add `drag_resize_window` method support.
|
||||
- **Breaking** `run() ->!` has been replaced by `run() -> Result<(), EventLoopError>` for returning errors without calling `std::process::exit()` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
|
||||
- **Breaking** Removed `EventLoopExtRunReturn` / `run_return` in favor of `EventLoopExtPumpEvents` / `pump_events` and `EventLoopExtRunOnDemand` / `run_ondemand` ([#2767](https://github.com/rust-windowing/winit/pull/2767))
|
||||
- `RedrawRequested` is no longer guaranteed to be emitted after `MainEventsCleared`, it is now platform-specific when the event is emitted after being requested via `redraw_request()`.
|
||||
- On Windows, `RedrawRequested` is now driven by `WM_PAINT` messages which are requested via `redraw_request()`
|
||||
- **Breaking** `LoopDestroyed` renamed to `LoopExiting` ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||
- **Breaking** `RedrawEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||
- **Breaking** `MainEventsCleared` removed ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||
- Added `AboutToWait` event which is emitted when the event loop is about to block and wait for new events ([#2900](https://github.com/rust-windowing/winit/issues/2900))
|
||||
- **Breaking:** `with_x11_visual` now takes the visual ID instead of the bare pointer.
|
||||
- On X11, add a `with_embedded_parent_window` function to the window builder to allow embedding a window into another window.
|
||||
- On iOS, add force data to touch events when using the Apple Pencil.
|
||||
- On Android, add force data to touch events.
|
||||
|
||||
# 0.29.0-beta.0
|
||||
|
||||
- On Web, allow event loops to be recreated with `spawn`.
|
||||
- **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone.
|
||||
- On Android, changed default behavior of Android to ignore volume keys letting the operating system handle them.
|
||||
- On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually.
|
||||
- **Breaking:** Rename `DeviceEventFilter` to `DeviceEvents` reversing the behavior of variants.
|
||||
- **Breaking:** Rename `EventLoopWindowTarget::set_device_event_filter` to `listen_device_events`.
|
||||
- On X11, fix `EventLoopWindowTarget::listen_device_events` effect being reversed.
|
||||
- **Breaking:** Remove all deprecated `modifiers` fields.
|
||||
- **Breaking:** Overhaul keyboard input handling.
|
||||
- Replace `KeyboardInput` with `KeyEvent` and `RawKeyEvent`.
|
||||
- Change `WindowEvent::KeyboardInput` to contain a `KeyEvent`.
|
||||
- Change `Event::Key` to contain a `RawKeyEvent`.
|
||||
- Remove `Event::ReceivedCharacter`. In its place, you should use
|
||||
`KeyEvent.text` in combination with `WindowEvent::Ime`.
|
||||
- Replace `VirtualKeyCode` with the `Key` enum.
|
||||
- Replace `ScanCode` with the `KeyCode` enum.
|
||||
- Rename `ModifiersState::LOGO` to `SUPER` and `ModifiersState::CTRL` to `CONTROL`.
|
||||
- Add `KeyCode` to refer to keys (roughly) by their physical location.
|
||||
- Add `NativeKeyCode` to represent raw `KeyCode`s which Winit doesn't
|
||||
understand.
|
||||
- Add `Key` to represent the keys after they've been interpreted by the
|
||||
active (software) keyboard layout.
|
||||
- Add `NativeKey` to represent raw `Key`s which Winit doesn't understand.
|
||||
- Add `KeyLocation` to tell apart `Key`s which usually "mean" the same thing,
|
||||
but can appear simultaneously in different spots on the same keyboard
|
||||
layout.
|
||||
- Add `Window::reset_dead_keys` to enable application-controlled cancellation
|
||||
of dead key sequences.
|
||||
- Add `KeyEventExtModifierSupplement` to expose additional (and less
|
||||
portable) interpretations of a given key-press.
|
||||
- Add `KeyCodeExtScancode`, which lets you convert between raw keycodes and
|
||||
`KeyCode`.
|
||||
- `ModifiersChanged` now uses dedicated `Modifiers` struct.
|
||||
- On Orbital, fix `ModifiersChanged` not being sent.
|
||||
- **Breaking:** `CursorIcon` is now used from the `cursor-icon` crate.
|
||||
- **Breaking:** `CursorIcon::Hand` is now named `CursorIcon::Pointer`.
|
||||
- **Breaking:** `CursorIcon::Arrow` was removed.
|
||||
- On Wayland, fix maximized startup not taking full size on GNOME.
|
||||
- On Wayland, fix initial window size not restored for maximized/fullscreened on startup window.
|
||||
- On Wayland, `Window::outer_size` now accounts for **client side** decorations.
|
||||
- On Wayland, fix window not checking that it actually got initial configure event.
|
||||
- On Wayland, fix maximized window creation and window geometry handling.
|
||||
- On Wayland, fix forward compatibility issues.
|
||||
- On Wayland, add `Window::drag_resize_window` method.
|
||||
- On Wayland, drop `WINIT_WAYLAND_CSD_THEME` variable.
|
||||
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
|
||||
- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now.
|
||||
- Add `Window::show_window_menu` to request a titlebar/system menu; implemented on Wayland/Windows for now.
|
||||
- Implement `AsFd`/`AsRawFd` for `EventLoop<T>` on X11 and Wayland.
|
||||
- Implement `PartialOrd` and `Ord` for `MouseButton`.
|
||||
- Implement `PartialOrd` and `Ord` on types in the `dpi` module.
|
||||
- **Breaking:** Bump MSRV from `1.60` to `1.64`.
|
||||
- **Breaking:** On Web, the canvas output bitmap size is no longer adjusted.
|
||||
- On Web: fix `Window::request_redraw` not waking the event loop when called from outside the loop.
|
||||
- On Web: fix position of touch events to be relative to the canvas.
|
||||
- On Web, fix `Window:::set_fullscreen` doing nothing when called outside the event loop but during
|
||||
a transient activation.
|
||||
- On Web, fix pointer button events not being processed when a buttons is already pressed.
|
||||
- **Breaking:** Updated `bitflags` crate version to `2`, which changes the API on exposed types.
|
||||
- On Web, handle coalesced pointer events, which increases the resolution of pointer inputs.
|
||||
- **Breaking:** On Web, `instant` is now replaced by `web_time`.
|
||||
- On Windows, port to `windows-sys` version 0.48.0.
|
||||
- On Web, fix pen treated as mouse input.
|
||||
- On Web, send mouse position on button release as well.
|
||||
- On Web, fix touch input not gaining or loosing focus.
|
||||
- **Breaking:** On Web, dropped support for Safari versions below 13.1.
|
||||
- On Web, prevent clicks on the canvas to select text.
|
||||
- Make `WindowBuilder` `Send + Sync`.
|
||||
- Make iOS `MonitorHandle` and `VideoMode` usable from other threads.
|
||||
- Make iOS windows usable from other threads.
|
||||
- On Android, add force data to touch events.
|
||||
- On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually.
|
||||
- On Android, fix `DeviceId` to contain device id's.
|
||||
- On Orbital, fix `ModifiersChanged` not being sent.
|
||||
- On Wayland, `Window::outer_size` now accounts for **client side** decorations.
|
||||
- On Wayland, add `Window::drag_resize_window` method.
|
||||
- On Wayland, remove `WINIT_WAYLAND_CSD_THEME` variable.
|
||||
- On Wayland, fix `TouchPhase::Canceled` being sent for moved events.
|
||||
- On Wayland, fix forward compatibility issues.
|
||||
- On Wayland, fix initial window size not restored for maximized/fullscreened on startup window.
|
||||
- On Wayland, fix maximized startup not taking full size on GNOME.
|
||||
- On Wayland, fix maximized window creation and window geometry handling.
|
||||
- On Wayland, fix window not checking that it actually got initial configure event.
|
||||
- On Wayland, make double clicking and moving the CSD frame more reliable.
|
||||
- On Wayland, support `Occluded` event with xdg-shell v6
|
||||
- On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor.
|
||||
- On Web, `ControlFlow::WaitUntil` now uses the Prioritized Task Scheduling API. `setTimeout()`, with a trick to circumvent throttling to 4ms, is used as a fallback.
|
||||
- On Web, `EventLoopProxy` now implements `Send`.
|
||||
- On Web, `Window` now implements `Send` and `Sync`.
|
||||
- **Breaking:** `WindowExtWebSys::canvas()` now returns an `Option`.
|
||||
- On Web, use the correct canvas size when calculating the new size during scale factor change,
|
||||
instead of using the output bitmap size.
|
||||
- On Web, scale factor and dark mode detection are now more robust.
|
||||
- On Web, fix the bfcache by not using the `beforeunload` event and map bfcache loading/unloading to `Suspended`/`Resumed` events.
|
||||
- On Web, fix scale factor resize suggestion always overwriting the canvas size.
|
||||
- On macOS, fix crash when dropping `Window`.
|
||||
- On Web, use `Window.requestIdleCallback()` for `ControlFlow::Poll` when available.
|
||||
- **Breaking:** On Web, the canvas size is not controlled by Winit anymore and external changes to
|
||||
the canvas size will be reported through `WindowEvent::Resized`.
|
||||
- On Web, respect `EventLoopWindowTarget::listen_device_events()` settings.
|
||||
- On Web, account for CSS `padding`, `border`, and `margin` when getting or setting the canvas position.
|
||||
- On Web, add Fullscreen API compatibility for Safari.
|
||||
- On Web, add `DeviceEvent::Motion`, `DeviceEvent::MouseWheel`, `DeviceEvent::Button` and `DeviceEvent::Key` support.
|
||||
- On Web, add `EventLoopWindowTargetExtWebSys` and `PollStrategy`, which allows to set different strategies for `ControlFlow::Poll`. By default the Prioritized Task Scheduling API is used, but an option to use `Window.requestIdleCallback` is available as well. Both use `setTimeout()`, with a trick to circumvent throttling to 4ms, as a fallback.
|
||||
- On Web, add `WindowBuilderExtWebSys::with_append()` to append the canvas element to the web page on creation.
|
||||
- On Web, allow event loops to be recreated with `spawn`.
|
||||
- On Web, enable event propagation.
|
||||
- On Web, fix `ControlFlow::WaitUntil` to never wake up **before** the given time.
|
||||
- On Web, fix `DeviceEvent::MouseMotion` only being emitted for each canvas instead of the whole window.
|
||||
- On Web, add `DeviceEvent::Motion`, `DeviceEvent::MouseWheel`, `DeviceEvent::Button` and
|
||||
`DeviceEvent::Key` support.
|
||||
- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events
|
||||
on Wayland, X11, Windows, macOS and Web.
|
||||
- On Web, fix `Window:::set_fullscreen` doing nothing when called outside the event loop but during transient activation.
|
||||
- On Web, fix pen treated as mouse input.
|
||||
- On Web, fix pointer button events not being processed when a buttons is already pressed.
|
||||
- On Web, fix scale factor resize suggestion always overwriting the canvas size.
|
||||
- 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 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()`.
|
||||
- On Web, implement `Window::set_(min|max)_inner_size()`.
|
||||
- On Web, implement `WindowEvent::Occluded`.
|
||||
- On Web, never return a `MonitorHandle`.
|
||||
- On Web, prevent clicks on the canvas to select text.
|
||||
- On Web, remove any fullscreen requests from the queue when an external fullscreen activation was detected.
|
||||
- On Web, remove unnecessary `Window::is_dark_mode()`, which was replaced with `Window::theme()`.
|
||||
- On Web, respect `EventLoopWindowTarget::listen_device_events()` settings.
|
||||
- On Web, scale factor and dark mode detection are now more robust.
|
||||
- On Web, send mouse position on button release as well.
|
||||
- On Web, take all transient activations on the canvas and window into account to queue a fullscreen request.
|
||||
- On Web, use `Window.requestAnimationFrame()` to throttle `RedrawRequested` events.
|
||||
- On Web, use the correct canvas size when calculating the new size during scale factor change, instead of using the output bitmap size.
|
||||
- On Web: fix `Window::request_redraw` not waking the event loop when called from outside the loop.
|
||||
- On Web: fix position of touch events to be relative to the canvas.
|
||||
- On Windows, add `drag_resize_window` method support.
|
||||
- On Windows, add horizontal MouseWheel `DeviceEvent`.
|
||||
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.
|
||||
- On Windows, fix IME APIs not working when from non event loop thread.
|
||||
- On Windows, fix `CursorEnter/Left` not being sent when grabbing the mouse.
|
||||
- On Windows, fix `RedrawRequested` not being delivered when calling `Window::request_redraw` from `RedrawRequested`.
|
||||
- On Windows, port to `windows-sys` version 0.48.0.
|
||||
- On X11, add a `with_embedded_parent_window` function to the window builder to allow embedding a window into another window.
|
||||
- On X11, fix event loop not waking up on `ControlFlow::Poll` and `ControlFlow::WaitUntil`.
|
||||
- On X11, fix false positive flagging of key repeats when pressing different keys with no release between presses.
|
||||
- On X11, set `visual_id` in returned `raw-window-handle`.
|
||||
- On iOS, add ability to change the status bar style.
|
||||
- On iOS, add force data to touch events when using the Apple Pencil.
|
||||
- On iOS, always wake the event loop when transitioning from `ControlFlow::Poll` to `ControlFlow::Poll`.
|
||||
- On iOS, send events `WindowEvent::Occluded(false)`, `WindowEvent::Occluded(true)` when application enters/leaves foreground.
|
||||
- On macOS, add tabbing APIs on `WindowExtMacOS` and `EventLoopWindowTargetExtMacOS`.
|
||||
- On macOS, fix assertion when pressing `Globe` key.
|
||||
- On macOS, fix crash in `window.set_minimized(false)`.
|
||||
- On macOS, fix crash when dropping `Window`.
|
||||
|
||||
# 0.28.7
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ may be worth creating a separate crate that extends Winit's API to add that func
|
||||
When reporting an issue, in order to help the maintainers understand what the problem is, please make
|
||||
your description of the issue as detailed as possible:
|
||||
|
||||
- if it is a bug, please provide clear explanation of what happens, what should happen, and how to
|
||||
- if it is a bug, please provide a clear explanation of what happens, what should happen, and how to
|
||||
reproduce the issue, ideally by providing a minimal program exhibiting the problem
|
||||
- if it is a feature request, please provide a clear argumentation about why you believe this feature
|
||||
should be supported by winit
|
||||
@@ -20,8 +20,8 @@ your description of the issue as detailed as possible:
|
||||
|
||||
When making a code contribution to winit, before opening your pull request, please make sure that:
|
||||
|
||||
- your patch builds with Winit's minimal supported rust version - Rust 1.65.
|
||||
- you tested your modifications on all the platforms impacted, or if not possible detail which platforms
|
||||
- your patch builds with Winit's minimal supported rust version - Rust 1.70.
|
||||
- you tested your modifications on all the platforms impacted, or if not possible, detail which platforms
|
||||
were not tested, and what should be tested, so that a maintainer or another contributor can test them
|
||||
- you updated any relevant documentation in winit
|
||||
- you left comments in your code explaining any part that is not straightforward, so that the
|
||||
@@ -34,7 +34,7 @@ When making a code contribution to winit, before opening your pull request, plea
|
||||
relevant sections in [`FEATURES.md`](https://github.com/rust-windowing/winit/blob/master/FEATURES.md#features)
|
||||
should be updated.
|
||||
|
||||
Once your PR is open, you can ask for review by a maintainer of your platform. Winit's merging policy
|
||||
Once your PR is open, you can ask for a review by a maintainer of your platform. Winit's merging policy
|
||||
is that a PR must be approved by at least two maintainers of winit before being merged, including
|
||||
at least a maintainer of the platform (a maintainer making a PR themselves counts as approving it).
|
||||
|
||||
@@ -46,27 +46,26 @@ Once your PR is deemed ready, the merging maintainer will take care of resolving
|
||||
|
||||
The current maintainers are listed in the [CODEOWNERS](.github/CODEOWNERS) file.
|
||||
|
||||
If you are interested in being pinged when testing is needed for a certain platform, please add yourself to the [Testers and Contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors) table!
|
||||
If you are interested in being pinged when testing is needed for a specific platform, please add yourself to the [Testers and Contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors) table!
|
||||
|
||||
## Release process
|
||||
|
||||
Given that winit is a widely used library we should be able to make a patch
|
||||
Given that winit is a widely used library, we should be able to make a patch
|
||||
releases at any time we want without blocking the development of new features.
|
||||
|
||||
To achieve these goals, a new branch is created for every new release. Releases
|
||||
and later patch releases are committed and tagged in this branch.
|
||||
To achieve these goals, a new branch is created for every new release. Releases and later patch releases are committed and tagged in this branch.
|
||||
|
||||
The exact steps for an exemplary `0.2.0` release might look like this:
|
||||
1. Initially the version on the latest master is `0.1.0`
|
||||
1. Initially, the version on the latest master is `0.1.0`
|
||||
2. A new `v0.2.x` branch is created for the release
|
||||
3. In the branch, the version is bumped to `v0.2.0`
|
||||
4. The new commit in the branch is tagged `v0.2.0`
|
||||
5. The version is pushed to crates.io
|
||||
6. A GitHub release is created for the `v0.2.0` tag
|
||||
7. On master, the version is bumped to `0.2.0` and the CHANGELOG is updated
|
||||
7. On master, the version is bumped to `0.2.0`, and the CHANGELOG is updated
|
||||
|
||||
When doing a patch release the process is similar:
|
||||
1. Initially the version of the latest release is `0.2.0`
|
||||
When doing a patch release, the process is similar:
|
||||
1. Initially, the version of the latest release is `0.2.0`
|
||||
2. Checkout the `v0.2.x` branch
|
||||
3. Cherry-pick the required non-breaking changes into the `v0.2.x`
|
||||
4. Follow steps 3-7 of the regular release example
|
||||
|
||||
71
Cargo.toml
71
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.29.1-beta"
|
||||
version = "0.29.5"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2021"
|
||||
@@ -10,10 +10,17 @@ readme = "README.md"
|
||||
repository = "https://github.com/rust-windowing/winit"
|
||||
documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
rust-version = "1.65.0"
|
||||
rust-version = "1.70.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["rwh_04", "rwh_05", "rwh_06", "serde"]
|
||||
features = [
|
||||
"rwh_04",
|
||||
"rwh_05",
|
||||
"rwh_06",
|
||||
"serde",
|
||||
# Enabled to get docs to compile
|
||||
"android-native-activity",
|
||||
]
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
# These are all tested in CI
|
||||
targets = [
|
||||
@@ -45,13 +52,16 @@ wayland-csd-adwaita-notitle = ["sctk-adwaita"]
|
||||
android-native-activity = ["android-activity/native-activity"]
|
||||
android-game-activity = ["android-activity/game-activity"]
|
||||
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"]
|
||||
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
|
||||
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
|
||||
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases = "0.1.1"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2"
|
||||
cursor-icon = "1.0.0"
|
||||
cursor-icon = "1.1.0"
|
||||
log = "0.4"
|
||||
mint = { version = "0.5.6", optional = true }
|
||||
once_cell = "1.12"
|
||||
@@ -63,26 +73,26 @@ smol_str = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
image = { version = "0.24.0", default-features = false, features = ["png"] }
|
||||
simple_logger = { version = "2.1.0", default_features = false }
|
||||
simple_logger = { version = "4.2.0", default_features = false }
|
||||
winit = { path = ".", features = ["rwh_05"] }
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
|
||||
softbuffer = "0.3.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
# Coordinate the next winit release android-activity 0.5 release
|
||||
android-activity = "=0.5.0-beta.1"
|
||||
ndk = "=0.8.0-beta.0"
|
||||
ndk-sys = "=0.5.0-beta.0"
|
||||
android-activity = "0.5.0"
|
||||
ndk = { version = "0.8.0", default-features = false }
|
||||
ndk-sys = "0.5.0"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = "0.4.1"
|
||||
objc2 = "0.5.0"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-graphics = "0.23.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.icrate]
|
||||
version = "0.0.4"
|
||||
version = "0.1.0"
|
||||
features = [
|
||||
"dispatch",
|
||||
"Foundation",
|
||||
@@ -95,10 +105,31 @@ features = [
|
||||
"Foundation_NSProcessInfo",
|
||||
"Foundation_NSThread",
|
||||
"Foundation_NSNumber",
|
||||
"AppKit",
|
||||
"AppKit_NSAppearance",
|
||||
"AppKit_NSApplication",
|
||||
"AppKit_NSBitmapImageRep",
|
||||
"AppKit_NSButton",
|
||||
"AppKit_NSColor",
|
||||
"AppKit_NSControl",
|
||||
"AppKit_NSCursor",
|
||||
"AppKit_NSEvent",
|
||||
"AppKit_NSGraphicsContext",
|
||||
"AppKit_NSImage",
|
||||
"AppKit_NSImageRep",
|
||||
"AppKit_NSMenu",
|
||||
"AppKit_NSMenuItem",
|
||||
"AppKit_NSPasteboard",
|
||||
"AppKit_NSResponder",
|
||||
"AppKit_NSScreen",
|
||||
"AppKit_NSTextInputContext",
|
||||
"AppKit_NSView",
|
||||
"AppKit_NSWindow",
|
||||
"AppKit_NSWindowTabGroup",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies.icrate]
|
||||
version = "0.0.4"
|
||||
version = "0.1.0"
|
||||
features = [
|
||||
"dispatch",
|
||||
"Foundation",
|
||||
@@ -150,13 +181,13 @@ memmap2 = { version = "0.9.0", optional = true }
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
|
||||
sctk-adwaita = { version = "0.7.0", default_features = false, optional = true }
|
||||
sctk-adwaita = { version = "0.8.0", default_features = false, optional = true }
|
||||
wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
|
||||
wayland-client = { version = "0.31.1", optional = true }
|
||||
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
|
||||
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
x11rb = { version = "0.12.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
|
||||
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
|
||||
xkbcommon-dl = "0.4.0"
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
@@ -169,6 +200,7 @@ version = "0.3.64"
|
||||
features = [
|
||||
'AbortController',
|
||||
'AbortSignal',
|
||||
'Blob',
|
||||
'console',
|
||||
'CssStyleDeclaration',
|
||||
'Document',
|
||||
@@ -180,6 +212,11 @@ features = [
|
||||
'FocusEvent',
|
||||
'HtmlCanvasElement',
|
||||
'HtmlElement',
|
||||
'HtmlImageElement',
|
||||
'ImageBitmap',
|
||||
'ImageBitmapOptions',
|
||||
'ImageBitmapRenderingContext',
|
||||
'ImageData',
|
||||
'IntersectionObserver',
|
||||
'IntersectionObserverEntry',
|
||||
'KeyboardEvent',
|
||||
@@ -189,6 +226,7 @@ features = [
|
||||
'Node',
|
||||
'PageTransitionEvent',
|
||||
'PointerEvent',
|
||||
'PremultiplyAlpha',
|
||||
'ResizeObserver',
|
||||
'ResizeObserverBoxOptions',
|
||||
'ResizeObserverEntry',
|
||||
@@ -196,7 +234,8 @@ features = [
|
||||
'ResizeObserverSize',
|
||||
'VisibilityState',
|
||||
'Window',
|
||||
'WheelEvent'
|
||||
'WheelEvent',
|
||||
'Url',
|
||||
]
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
@@ -212,7 +251,5 @@ web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"xbuild/android-target",
|
||||
"xbuild/ios-target",
|
||||
"run-wasm",
|
||||
]
|
||||
|
||||
20
FEATURES.md
20
FEATURES.md
@@ -1,6 +1,6 @@
|
||||
# Winit Scope
|
||||
|
||||
Winit aims to expose an interface that abstracts over window creation and input handling, and can
|
||||
Winit aims to expose an interface that abstracts over window creation and input handling and can
|
||||
be used to create both games and applications. It supports the following main graphical platforms:
|
||||
- Desktop
|
||||
- Windows 7+ (10+ is tested regularly)
|
||||
@@ -45,10 +45,10 @@ be released and the library will enter maintenance mode. For the most part, new
|
||||
be added past this point. New platform features may be accepted and exposed through point releases.
|
||||
|
||||
### Tier upgrades
|
||||
Some platform features could in theory be exposed across multiple platforms, but have not gone
|
||||
Some platform features could, in theory, be exposed across multiple platforms, but have not gone
|
||||
through the implementation work necessary to function on all platforms. When one of these features
|
||||
gets implemented across all platforms, a PR can be opened to upgrade the feature to a core feature.
|
||||
If that gets accepted, the platform-specific functions gets deprecated and become permanently
|
||||
If that gets accepted, the platform-specific functions get deprecated and become permanently
|
||||
exposed through the core, cross-platform API.
|
||||
|
||||
# Features
|
||||
@@ -88,7 +88,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
- **Fullscreen toggle**: The windows created by winit can be switched to and from fullscreen after
|
||||
creation.
|
||||
- **Exclusive fullscreen**: Winit allows changing the video mode of the monitor
|
||||
for fullscreen windows, and if applicable, captures the monitor for exclusive
|
||||
for fullscreen windows and, if applicable, captures the monitor for exclusive
|
||||
use by this application.
|
||||
- **HiDPI support**: Winit assists developers in appropriately scaling HiDPI content.
|
||||
- **Popup / modal windows**: Windows can be created relative to the client area of other windows, and parent
|
||||
@@ -105,7 +105,8 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
- **Mouse set location**: Forcibly changing the location of the pointer.
|
||||
- **Cursor locking**: Locking the cursor inside the window so it cannot move.
|
||||
- **Cursor confining**: Confining the cursor to the window bounds so it cannot leave them.
|
||||
- **Cursor icon**: Changing the cursor icon, or hiding the cursor.
|
||||
- **Cursor icon**: Changing the cursor icon or hiding the cursor.
|
||||
- **Cursor image**: Changing the cursor to your own image.
|
||||
- **Cursor hittest**: Handle or ignore mouse events for a window.
|
||||
- **Touch events**: Single-touch events.
|
||||
- **Touch pressure**: Touch events contain information about the amount of force being applied.
|
||||
@@ -150,13 +151,13 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* Setting the `UIView` hidpi factor
|
||||
* Valid orientations
|
||||
* Home indicator visibility
|
||||
* Status bar visibility
|
||||
* Deferrring system gestures
|
||||
* Status bar visibility and style
|
||||
* Deferring system gestures
|
||||
* Getting the device idiom
|
||||
* Getting the preferred video mode
|
||||
|
||||
### Web
|
||||
* Get if systems preferred color scheme is "dark"
|
||||
* Get if the systems preferred color scheme is "dark"
|
||||
|
||||
## Usability
|
||||
* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial)
|
||||
@@ -166,7 +167,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
Legend:
|
||||
|
||||
- ✔️: Works as intended
|
||||
- ▢: Mostly works but some bugs are known
|
||||
- ▢: Mostly works, but some bugs are known
|
||||
- ❌: Missing feature or large bugs making it unusable
|
||||
- **N/A**: Not applicable for this platform
|
||||
- ❓: Unknown status
|
||||
@@ -206,6 +207,7 @@ Legend:
|
||||
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |❌ |
|
||||
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|
||||
|Cursor image |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|
||||
|Cursor hittest |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A** |
|
||||
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |
|
||||
|
||||
@@ -2,21 +2,20 @@
|
||||
|
||||
The winit maintainers would like to recognize the following former winit
|
||||
contributors, without whom winit would not exist in its current form. We thank
|
||||
them deeply for their time and efforts, and wish them best of luck in their
|
||||
them deeply for their time and efforts and wish them the best of luck in their
|
||||
future endeavors:
|
||||
|
||||
* [@tomaka]: For creating the winit project and guiding it through its early
|
||||
years of existence.
|
||||
* [@vberger]: For diligently creating the Wayland backend, and being its
|
||||
* [@vberger]: For diligently creating the Wayland backend and being its
|
||||
extremely helpful and benevolent maintainer for years.
|
||||
* [@francesca64]: For taking over the responsibility of maintaining almost every
|
||||
winit backend, and standardizing HiDPI support across all of them.
|
||||
* [@Osspial]: For heroically landing EventLoop 2.0, and valiantly ushering in a
|
||||
winit backend and standardizing HiDPI support across all of them.
|
||||
* [@Osspial]: For heroically landing EventLoop 2.0 and valiantly ushering in a
|
||||
vastly more sustainable era of winit.
|
||||
* [@goddessfreya]: For selflessly taking over maintainership of glutin, and her
|
||||
* [@goddessfreya]: For selflessly taking over maintainership of glutin and her
|
||||
stellar dedication to improving both winit and glutin.
|
||||
* [@ArturKovacs]: For consistently maintaining the macOS backend, and his
|
||||
immense involvement in designing and implementing the new keyboard API.
|
||||
* [@ArturKovacs]: For consistently maintaining the macOS backend and for his immense involvement in designing and implementing the new keyboard API.
|
||||
|
||||
[@tomaka]: https://github.com/tomaka
|
||||
[@vberger]: https://github.com/vberger
|
||||
|
||||
88
README.md
88
README.md
@@ -6,7 +6,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.29.1-beta"
|
||||
winit = "0.29.5"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -26,38 +26,12 @@ Join us in any of these:
|
||||
|
||||
Winit is a window creation and management library. It can create windows and lets you handle
|
||||
events (for example: the window being resized, a key being pressed, a mouse movement, etc.)
|
||||
produced by window.
|
||||
produced by the window.
|
||||
|
||||
Winit is designed to be a low-level brick in a hierarchy of libraries. Consequently, in order to
|
||||
show something on the window you need to use the platform-specific getters provided by winit, or
|
||||
another library.
|
||||
|
||||
```rust
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let event_loop = EventLoop::new();
|
||||
event_loop.set_control_flow(ControlFlow::Wait);
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Winit is only officially supported on the latest stable version of the Rust compiler.
|
||||
|
||||
### Cargo Features
|
||||
|
||||
Winit provides the following features, which can be enabled in your `Cargo.toml` file:
|
||||
@@ -68,7 +42,7 @@ Winit provides the following features, which can be enabled in your `Cargo.toml`
|
||||
|
||||
## MSRV Policy
|
||||
|
||||
The Minimum Supported Rust Version (MSRV) of this crate is **1.65**. Changes to
|
||||
This crate's Minimum Supported Rust Version (MSRV) is **1.70**. Changes to
|
||||
the MSRV will be accompanied by a minor version bump.
|
||||
|
||||
As a **tentative** policy, the upper bound of the MSRV is given by the following
|
||||
@@ -79,12 +53,11 @@ min(sid, stable - 3)
|
||||
```
|
||||
|
||||
Where `sid` is the current version of `rustc` provided by [Debian Sid], and
|
||||
`stable` is the latest stable version of Rust. This bound may be broken in the
|
||||
event of a major ecosystem shift or a security vulnerability.
|
||||
`stable` is the latest stable version of Rust. This bound may be broken in case of a major ecosystem shift or a security vulnerability.
|
||||
|
||||
[Debian Sid]: https://packages.debian.org/sid/rustc
|
||||
|
||||
The exception to this is for the Android platform, where a higher Rust version
|
||||
The exception is for the Android platform, where a higher Rust version
|
||||
must be used for certain Android features. In this case, the MSRV will be
|
||||
capped at the latest stable version of Rust minus three. This inconsistency is
|
||||
not reflected in Cargo metadata, as it is not powerful enough to expose this
|
||||
@@ -112,7 +85,7 @@ either [provide Winit with a `<canvas>` element][web with_canvas], or [let Winit
|
||||
create a `<canvas>` element which you can then retrieve][web canvas getter] and
|
||||
insert it into the DOM yourself.
|
||||
|
||||
For example code using Winit with WebAssembly, check out the [web example]. For
|
||||
For the example code using Winit with WebAssembly, check out the [web example]. For
|
||||
information on using Rust on WebAssembly, check out the [Rust and WebAssembly
|
||||
book].
|
||||
|
||||
@@ -135,14 +108,14 @@ glue crate (prior to `0.28` it used
|
||||
|
||||
The version of the glue crate that your application depends on _must_ match the
|
||||
version that Winit depends on because the glue crate is responsible for your
|
||||
application's main entrypoint. If Cargo resolves multiple versions they will
|
||||
application's main entry point. If Cargo resolves multiple versions, they will
|
||||
clash.
|
||||
|
||||
`winit` glue compatibility table:
|
||||
|
||||
| winit | ndk-glue |
|
||||
| :---: | :--------------------------: |
|
||||
| 0.29.1-beta | `android-activity = "0.5.0-beta.1"` |
|
||||
| 0.29 | `android-activity = "0.5"` |
|
||||
| 0.28 | `android-activity = "0.4"` |
|
||||
| 0.27 | `ndk-glue = "0.7"` |
|
||||
| 0.26 | `ndk-glue = "0.5"` |
|
||||
@@ -153,7 +126,7 @@ The recommended way to avoid a conflict with the glue version is to avoid explic
|
||||
depending on the `android-activity` crate, and instead consume the API that
|
||||
is re-exported by Winit under `winit::platform::android::activity::*`
|
||||
|
||||
Running on an Android device needs a dynamic system library, add this to Cargo.toml:
|
||||
Running on an Android device needs a dynamic system library. Add this to Cargo.toml:
|
||||
|
||||
```toml
|
||||
[lib]
|
||||
@@ -161,14 +134,14 @@ name = "main"
|
||||
crate-type = ["cdylib"]
|
||||
```
|
||||
|
||||
All Android applications are based on an `Activity` subclass and the
|
||||
All Android applications are based on an `Activity` subclass, and the
|
||||
`android-activity` crate is designed to support different choices for this base
|
||||
class. Your application _must_ specify the base class it needs via a feature flag:
|
||||
|
||||
| Base Class | Feature Flag | Notes |
|
||||
| :--------------: | :---------------: | :-----: |
|
||||
| `NativeActivity` | `android-native-activity` | Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass `NativeActivity` to access some platform features. It does not derive from the [`AndroidAppCompat`] base class.|
|
||||
| [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`] which is a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
|
||||
| [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`], a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
|
||||
|
||||
[`GameActivity`]: https://developer.android.com/games/agdk/game-activity
|
||||
[`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input
|
||||
@@ -177,40 +150,13 @@ class. Your application _must_ specify the base class it needs via a feature fla
|
||||
[agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
|
||||
[Gradle]: https://developer.android.com/studio/build
|
||||
|
||||
For example, add this to Cargo.toml:
|
||||
```toml
|
||||
winit = { version = "0.29.1-beta", features = [ "android-native-activity" ] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_logger = "0.11.0"
|
||||
```
|
||||
|
||||
And, for example, define an entry point for your library like this:
|
||||
```rust
|
||||
#[cfg(target_os = "android")]
|
||||
use winit::platform::android::activity::AndroidApp;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[no_mangle]
|
||||
fn android_main(app: AndroidApp) {
|
||||
use winit::platform::android::EventLoopBuilderExtAndroid;
|
||||
|
||||
android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Trace));
|
||||
|
||||
let event_loop = EventLoopBuilder::with_user_event()
|
||||
.with_android_app(app)
|
||||
.build();
|
||||
_main(event_loop);
|
||||
}
|
||||
```
|
||||
|
||||
For more details, refer to these `android-activity` [example applications](https://github.com/rib/android-activity/tree/main/examples).
|
||||
|
||||
##### Converting from `ndk-glue` to `android-activity`
|
||||
|
||||
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk` then the minimal changes would be:
|
||||
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
|
||||
1. Remove `ndk-glue` from your `Cargo.toml`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.1-beta", features = [ "android-native-activity" ] }`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.5", features = [ "android-native-activity" ] }`
|
||||
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
|
||||
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
|
||||
|
||||
@@ -221,13 +167,13 @@ doing anything; this includes creating windows, fetching monitors, drawing,
|
||||
and so on, see issues [#2238], [#2051] and [#2087].
|
||||
|
||||
If you encounter problems, you should try doing your initialization inside
|
||||
`Event::NewEvents(StartCause::Init)`.
|
||||
`Event::Resumed`.
|
||||
|
||||
#### iOS
|
||||
|
||||
Similar to macOS, iOS's main `UIApplicationMain` does some init work that's required
|
||||
by all UI related code, see issue [#1705]. You should consider creating your windows
|
||||
inside `Event::NewEvents(StartCause::Init)`.
|
||||
by all UI-related code (see issue [#1705]). It would be best to consider creating your windows
|
||||
inside `Event::Resumed`.
|
||||
|
||||
|
||||
[#2238]: https://github.com/rust-windowing/winit/issues/2238
|
||||
@@ -237,5 +183,5 @@ inside `Event::NewEvents(StartCause::Init)`.
|
||||
|
||||
#### Redox OS
|
||||
|
||||
Redox OS has some functionality not present yet, that will be implemented when
|
||||
Redox OS has some functionality not yet present that will be implemented when
|
||||
its orbital display server provides it.
|
||||
|
||||
@@ -6,6 +6,7 @@ disallowed-methods = [
|
||||
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ 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::HtmlElement::style", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
|
||||
|
||||
@@ -34,13 +34,7 @@ skip = [
|
||||
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this
|
||||
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
|
||||
{ name = "libloading" }, # x11rb uses a different version until the next update
|
||||
{ name = "syn" }, # https://github.com/rust-mobile/ndk/issues/392
|
||||
{ name = "num_enum"}, # See above ^, waiting for release
|
||||
{ name = "num_enum_derive"}, # See above ^, waiting for release
|
||||
{ name = "miniz_oxide"}, # https://github.com/rust-lang/flate2-rs/issues/340
|
||||
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
|
||||
{ name = "foreign-types-shared" }, # https://github.com/servo/core-foundation-rs/issues/634
|
||||
{ name = "foreign-types" }, # See above ^, waiting for release
|
||||
]
|
||||
skip-tree = []
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ 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 is
|
||||
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.
|
||||
|
||||
@@ -17,7 +17,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
dpi::{LogicalPosition, LogicalSize, Position},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
raw_window_handle::HasRawWindowHandle,
|
||||
raw_window_handle::HasWindowHandle,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
@@ -26,14 +26,13 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event_loop: &EventLoopWindowTarget<()>,
|
||||
windows: &mut HashMap<WindowId, Window>,
|
||||
) {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let parent = parent.window_handle().unwrap();
|
||||
let mut builder = WindowBuilder::new()
|
||||
.with_title("child window")
|
||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_visible(true);
|
||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||
builder = unsafe { builder.with_parent_window(Some(parent)) };
|
||||
builder = builder.with_parent_window(Some(parent));
|
||||
let child_window = builder.build(event_loop).unwrap();
|
||||
|
||||
let id = child_window.id();
|
||||
|
||||
@@ -10,7 +10,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
keyboard::Key,
|
||||
keyboard::{Key, NamedKey},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
@@ -88,7 +88,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
request_redraw = !request_redraw;
|
||||
println!("\nrequest_redraw: {request_redraw}\n");
|
||||
}
|
||||
Key::Escape => {
|
||||
Key::Named(NamedKey::Escape) => {
|
||||
close_requested = true;
|
||||
}
|
||||
_ => (),
|
||||
|
||||
@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState},
|
||||
keyboard::{Key, ModifiersState, NamedKey},
|
||||
window::{CursorGrabMode, WindowBuilder},
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
..
|
||||
} => {
|
||||
let result = match key {
|
||||
Key::Escape => {
|
||||
Key::Named(NamedKey::Escape) => {
|
||||
elwt.exit();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
94
examples/custom_cursors.rs
Normal file
94
examples/custom_cursors.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
#![allow(clippy::single_match, clippy::disallowed_methods)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
keyboard::Key,
|
||||
window::{CustomCursor, WindowBuilder},
|
||||
};
|
||||
|
||||
fn decode_cursor<T>(bytes: &[u8], window_target: &EventLoopWindowTarget<T>) -> CustomCursor {
|
||||
let img = image::load_from_memory(bytes).unwrap().to_rgba8();
|
||||
let samples = img.into_flat_samples();
|
||||
let (_, w, h) = samples.extents();
|
||||
let (w, h) = (w as u16, h as u16);
|
||||
let builder = CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap();
|
||||
|
||||
builder.build(window_target)
|
||||
}
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
#[cfg(not(wasm_platform))]
|
||||
SimpleLogger::new()
|
||||
.with_level(log::LevelFilter::Info)
|
||||
.init()
|
||||
.unwrap();
|
||||
#[cfg(wasm_platform)]
|
||||
console_log::init_with_level(log::Level::Debug).unwrap();
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let builder = WindowBuilder::new().with_title("A fantastic window!");
|
||||
#[cfg(wasm_platform)]
|
||||
let builder = {
|
||||
use winit::platform::web::WindowBuilderExtWebSys;
|
||||
builder.with_append(true)
|
||||
};
|
||||
let window = builder.build(&event_loop).unwrap();
|
||||
|
||||
let mut cursor_idx = 0;
|
||||
let mut cursor_visible = true;
|
||||
|
||||
let custom_cursors = [
|
||||
decode_cursor(include_bytes!("data/cross.png"), &event_loop),
|
||||
decode_cursor(include_bytes!("data/cross2.png"), &event_loop),
|
||||
];
|
||||
|
||||
event_loop.run(move |event, _elwt| match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
logical_key: key,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("1") => {
|
||||
log::debug!("Setting cursor to {:?}", cursor_idx);
|
||||
window.set_custom_cursor(&custom_cursors[cursor_idx]);
|
||||
cursor_idx = (cursor_idx + 1) % 2;
|
||||
}
|
||||
Key::Character("2") => {
|
||||
log::debug!("Setting cursor icon to default");
|
||||
window.set_cursor_icon(Default::default());
|
||||
}
|
||||
Key::Character("3") => {
|
||||
cursor_visible = !cursor_visible;
|
||||
log::debug!("Setting cursor visibility to {:?}", cursor_visible);
|
||||
window.set_cursor_visible(cursor_visible);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
#[cfg(not(wasm_platform))]
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
#[cfg(not(wasm_platform))]
|
||||
_elwt.exit();
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
}
|
||||
BIN
examples/data/cross.png
Normal file
BIN
examples/data/cross.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 B |
BIN
examples/data/cross2.png
Normal file
BIN
examples/data/cross2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 129 B |
56
examples/focus.rs
Normal file
56
examples/focus.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
//! Example for focusing a window.
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time;
|
||||
#[cfg(wasm_platform)]
|
||||
use web_time as time;
|
||||
use winit::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut deadline = time::Instant::now() + time::Duration::from_secs(3);
|
||||
event_loop.run(move |event, elwt| {
|
||||
match event {
|
||||
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
|
||||
// Timeout reached; focus the window.
|
||||
println!("Re-focusing the window.");
|
||||
deadline += time::Duration::from_secs(3);
|
||||
window.focus_window();
|
||||
}
|
||||
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Notify the windowing system that we'll be presenting to the window.
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
elwt.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(deadline));
|
||||
})
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::dpi::PhysicalSize;
|
||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::keyboard::Key;
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::{Fullscreen, WindowBuilder};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -65,7 +65,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
},
|
||||
..
|
||||
} => match key {
|
||||
Key::Escape => elwt.exit(),
|
||||
Key::Named(NamedKey::Escape) => elwt.exit(),
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
|
||||
@@ -6,7 +6,7 @@ use winit::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
event::{ElementState, Event, Ime, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, KeyCode},
|
||||
keyboard::NamedKey,
|
||||
window::{ImePurpose, WindowBuilder},
|
||||
};
|
||||
|
||||
@@ -69,12 +69,12 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
println!("key: {event:?}");
|
||||
|
||||
if event.state == ElementState::Pressed && event.physical_key == KeyCode::F2 {
|
||||
if event.state == ElementState::Pressed && event.logical_key == NamedKey::F2 {
|
||||
ime_allowed = !ime_allowed;
|
||||
window.set_ime_allowed(ime_allowed);
|
||||
println!("\nIME allowed: {ime_allowed}\n");
|
||||
}
|
||||
if event.state == ElementState::Pressed && event.logical_key == Key::F3 {
|
||||
if event.state == ElementState::Pressed && event.logical_key == NamedKey::F3 {
|
||||
ime_purpose = match ime_purpose {
|
||||
ImePurpose::Normal => ImePurpose::Password,
|
||||
ImePurpose::Password => ImePurpose::Terminal,
|
||||
|
||||
@@ -9,7 +9,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState},
|
||||
keyboard::{Key, ModifiersState, NamedKey},
|
||||
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel},
|
||||
};
|
||||
|
||||
@@ -65,17 +65,17 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
},
|
||||
..
|
||||
} => {
|
||||
use Key::{ArrowLeft, ArrowRight};
|
||||
use NamedKey::{ArrowLeft, ArrowRight};
|
||||
window.set_title(&format!("{key:?}"));
|
||||
let state = !modifiers.shift_key();
|
||||
match key {
|
||||
// Cycle through video modes
|
||||
Key::ArrowRight | Key::ArrowLeft => {
|
||||
video_mode_id = match key {
|
||||
ArrowLeft => video_mode_id.saturating_sub(1),
|
||||
ArrowRight => (video_modes.len() - 1).min(video_mode_id + 1),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Key::Named(ArrowRight) | Key::Named(ArrowLeft) => {
|
||||
if key == ArrowLeft {
|
||||
video_mode_id = video_mode_id.saturating_sub(1);
|
||||
} else if key == ArrowRight {
|
||||
video_mode_id = (video_modes.len() - 1).min(video_mode_id + 1);
|
||||
}
|
||||
println!("Picking video mode: {}", video_modes[video_mode_id]);
|
||||
}
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
@@ -185,7 +185,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Released,
|
||||
logical_key: Key::Escape,
|
||||
logical_key: Key::Named(NamedKey::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::collections::HashMap;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
keyboard::{Key, NamedKey},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
@@ -40,19 +40,18 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
logical_key: Key::Character(c),
|
||||
..
|
||||
},
|
||||
event,
|
||||
is_synthetic: false,
|
||||
..
|
||||
} if matches!(c.as_ref(), "n" | "N") => {
|
||||
let window = Window::new(elwt).unwrap();
|
||||
println!("Opened a new window: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
} if event.state == ElementState::Pressed => match event.logical_key {
|
||||
Key::Named(NamedKey::Escape) => elwt.exit(),
|
||||
Key::Character(c) if c == "n" || c == "N" => {
|
||||
let window = Window::new(elwt).unwrap();
|
||||
println!("Opened a new window: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
|
||||
@@ -5,7 +5,7 @@ use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::KeyCode,
|
||||
keyboard::{KeyCode, PhysicalKey},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
physical_key: KeyCode::Space,
|
||||
physical_key: PhysicalKey::Code(KeyCode::Space),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
|
||||
@@ -11,7 +11,6 @@ mod example {
|
||||
|
||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::keyboard::Key;
|
||||
use winit::platform::startup_notify::{
|
||||
EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify,
|
||||
};
|
||||
@@ -46,7 +45,7 @@ mod example {
|
||||
},
|
||||
..
|
||||
} => {
|
||||
if logical_key == Key::Character("n".into()) {
|
||||
if logical_key == "n" {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
// Request a new activation token on this window.
|
||||
// Once we get it we will use it to create a window.
|
||||
|
||||
@@ -53,6 +53,13 @@ pub(super) fn fill_window(window: &Window) {
|
||||
}
|
||||
|
||||
GC.with(|gc| {
|
||||
let size = window.inner_size();
|
||||
let (Some(width), Some(height)) =
|
||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Either get the last context used or create a new one.
|
||||
let mut gc = gc.borrow_mut();
|
||||
let surface = gc
|
||||
@@ -61,13 +68,9 @@ pub(super) fn fill_window(window: &Window) {
|
||||
|
||||
// Fill a buffer with a solid color.
|
||||
const DARK_GRAY: u32 = 0xFF181818;
|
||||
let size = window.inner_size();
|
||||
|
||||
surface
|
||||
.resize(
|
||||
NonZeroU32::new(size.width).expect("Width must be greater than zero"),
|
||||
NonZeroU32::new(size.height).expect("Height must be greater than zero"),
|
||||
)
|
||||
.resize(width, height)
|
||||
.expect("Failed to resize the softbuffer surface");
|
||||
|
||||
let mut buffer = surface
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::KeyCode,
|
||||
keyboard::Key,
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
@@ -39,13 +39,13 @@ pub fn main() -> Result<(), impl std::error::Error> {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
physical_key: KeyCode::KeyF,
|
||||
logical_key: Key::Character(c),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
} if window_id == window.id() => {
|
||||
} if window_id == window.id() && c == "f" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,7 @@ use winit::{
|
||||
dpi::{LogicalSize, PhysicalSize},
|
||||
event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent},
|
||||
event_loop::{DeviceEvents, EventLoop},
|
||||
keyboard::{Key, KeyCode},
|
||||
keyboard::{Key, KeyCode, PhysicalKey},
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
@@ -51,14 +51,14 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}),
|
||||
..
|
||||
} => match physical_key {
|
||||
KeyCode::KeyM => {
|
||||
PhysicalKey::Code(KeyCode::KeyM) => {
|
||||
if minimized {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
window.focus_window();
|
||||
}
|
||||
}
|
||||
KeyCode::KeyV => {
|
||||
PhysicalKey::Code(KeyCode::KeyV) => {
|
||||
if !visible {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
|
||||
@@ -2,9 +2,9 @@ use log::debug;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
keyboard::NamedKey,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
@@ -27,15 +27,10 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Space,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
WindowEvent::KeyboardInput { event, .. }
|
||||
if event.logical_key == NamedKey::Space
|
||||
&& event.state == ElementState::Released =>
|
||||
{
|
||||
has_increments = !has_increments;
|
||||
|
||||
let new_increments = match window.resize_increments() {
|
||||
|
||||
@@ -9,7 +9,7 @@ use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
keyboard::{Key, NamedKey},
|
||||
platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
@@ -70,10 +70,10 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
Key::Character("w") => {
|
||||
let _ = windows.remove(&window_id);
|
||||
}
|
||||
Key::ArrowRight => {
|
||||
Key::Named(NamedKey::ArrowRight) => {
|
||||
windows.get(&window_id).unwrap().select_next_tab();
|
||||
}
|
||||
Key::ArrowLeft => {
|
||||
Key::Named(NamedKey::ArrowLeft) => {
|
||||
windows.get(&window_id).unwrap().select_previous_tab();
|
||||
}
|
||||
Key::Character(ch) => {
|
||||
|
||||
278
src/cursor.rs
Normal file
278
src/cursor.rs
Normal file
@@ -0,0 +1,278 @@
|
||||
use core::fmt;
|
||||
use std::hash::Hasher;
|
||||
use std::sync::Arc;
|
||||
use std::{error::Error, hash::Hash};
|
||||
|
||||
use crate::event_loop::EventLoopWindowTarget;
|
||||
use crate::platform_impl::{self, PlatformCustomCursor, PlatformCustomCursorBuilder};
|
||||
|
||||
/// The maximum width and height for a cursor when using [`CustomCursor::from_rgba`].
|
||||
pub const MAX_CURSOR_SIZE: u16 = 2048;
|
||||
|
||||
const PIXEL_SIZE: usize = 4;
|
||||
|
||||
/// Use a custom image as a cursor (mouse pointer).
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **Web**: Some browsers have limits on cursor sizes usually at 128x128.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winit::{
|
||||
/// event::{Event, WindowEvent},
|
||||
/// event_loop::{ControlFlow, EventLoop},
|
||||
/// window::{CustomCursor, Window},
|
||||
/// };
|
||||
///
|
||||
/// let mut event_loop = EventLoop::new().unwrap();
|
||||
///
|
||||
/// let w = 10;
|
||||
/// let h = 10;
|
||||
/// let rgba = vec![255; (w * h * 4) as usize];
|
||||
///
|
||||
/// #[cfg(not(target_family = "wasm"))]
|
||||
/// let builder = CustomCursor::from_rgba(rgba, w, h, w / 2, h / 2).unwrap();
|
||||
///
|
||||
/// #[cfg(target_family = "wasm")]
|
||||
/// let builder = {
|
||||
/// use winit::platform::web::CustomCursorExtWebSys;
|
||||
/// CustomCursor::from_url(String::from("http://localhost:3000/cursor.png"), 0, 0)
|
||||
/// };
|
||||
///
|
||||
/// let custom_cursor = builder.build(&event_loop);
|
||||
///
|
||||
/// let window = Window::new(&event_loop).unwrap();
|
||||
/// window.set_custom_cursor(&custom_cursor);
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct CustomCursor {
|
||||
/// Platforms should make sure this is cheap to clone.
|
||||
pub(crate) inner: PlatformCustomCursor,
|
||||
}
|
||||
|
||||
impl CustomCursor {
|
||||
/// Creates a new cursor from an rgba buffer.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Setting cursor could be delayed due to the creation of `Blob` objects,
|
||||
/// which are async by nature.
|
||||
pub fn from_rgba(
|
||||
rgba: impl Into<Vec<u8>>,
|
||||
width: u16,
|
||||
height: u16,
|
||||
hotspot_x: u16,
|
||||
hotspot_y: u16,
|
||||
) -> Result<CustomCursorBuilder, BadImage> {
|
||||
Ok(CustomCursorBuilder {
|
||||
inner: PlatformCustomCursorBuilder::from_rgba(
|
||||
rgba.into(),
|
||||
width,
|
||||
height,
|
||||
hotspot_x,
|
||||
hotspot_y,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a [`CustomCursor`].
|
||||
///
|
||||
/// See [`CustomCursor`] for more details.
|
||||
#[derive(Debug)]
|
||||
pub struct CustomCursorBuilder {
|
||||
pub(crate) inner: PlatformCustomCursorBuilder,
|
||||
}
|
||||
|
||||
impl CustomCursorBuilder {
|
||||
pub fn build<T>(self, window_target: &EventLoopWindowTarget<T>) -> CustomCursor {
|
||||
CustomCursor {
|
||||
inner: PlatformCustomCursor::build(self.inner, &window_target.p),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BadImage {
|
||||
/// Produced when the image dimensions are larger than [`MAX_CURSOR_SIZE`]. This doesn't
|
||||
/// guarantee that the cursor will work, but should avoid many platform and device specific
|
||||
/// limits.
|
||||
TooLarge { width: u16, height: u16 },
|
||||
/// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
|
||||
/// safely interpreted as 32bpp RGBA pixels.
|
||||
ByteCountNotDivisibleBy4 { byte_count: usize },
|
||||
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
|
||||
/// At least one of your arguments is incorrect.
|
||||
DimensionsVsPixelCount {
|
||||
width: u16,
|
||||
height: u16,
|
||||
width_x_height: u64,
|
||||
pixel_count: u64,
|
||||
},
|
||||
/// Produced when the hotspot is outside the image bounds
|
||||
HotspotOutOfBounds {
|
||||
width: u16,
|
||||
height: u16,
|
||||
hotspot_x: u16,
|
||||
hotspot_y: u16,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for BadImage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
BadImage::TooLarge { width, height } => write!(f,
|
||||
"The specified dimensions ({width:?}x{height:?}) are too large. The maximum is {MAX_CURSOR_SIZE:?}x{MAX_CURSOR_SIZE:?}.",
|
||||
),
|
||||
BadImage::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
|
||||
"The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
|
||||
),
|
||||
BadImage::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height,
|
||||
pixel_count,
|
||||
} => write!(f,
|
||||
"The specified dimensions ({width:?}x{height:?}) don't match the number of pixels supplied by the `rgba` argument ({pixel_count:?}). For those dimensions, the expected pixel count is {width_x_height:?}.",
|
||||
),
|
||||
BadImage::HotspotOutOfBounds {
|
||||
width,
|
||||
height,
|
||||
hotspot_x,
|
||||
hotspot_y,
|
||||
} => write!(f,
|
||||
"The specified hotspot ({hotspot_x:?}, {hotspot_y:?}) is outside the image bounds ({width:?}x{height:?}).",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BadImage {}
|
||||
|
||||
/// Platforms export this directly as `PlatformCustomCursorBuilder` if they need to only work with images.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct OnlyCursorImageBuilder(pub(crate) CursorImage);
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl OnlyCursorImageBuilder {
|
||||
pub(crate) fn from_rgba(
|
||||
rgba: Vec<u8>,
|
||||
width: u16,
|
||||
height: u16,
|
||||
hotspot_x: u16,
|
||||
hotspot_y: u16,
|
||||
) -> Result<Self, BadImage> {
|
||||
CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Platforms export this directly as `PlatformCustomCursor` if they don't implement caching.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct OnlyCursorImage(pub(crate) Arc<CursorImage>);
|
||||
|
||||
impl Hash for OnlyCursorImage {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
Arc::as_ptr(&self.0).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for OnlyCursorImage {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for OnlyCursorImage {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl OnlyCursorImage {
|
||||
fn build<T>(
|
||||
builder: OnlyCursorImageBuilder,
|
||||
_: &platform_impl::EventLoopWindowTarget<T>,
|
||||
) -> Self {
|
||||
Self(Arc::new(builder.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct CursorImage {
|
||||
pub(crate) rgba: Vec<u8>,
|
||||
pub(crate) width: u16,
|
||||
pub(crate) height: u16,
|
||||
pub(crate) hotspot_x: u16,
|
||||
pub(crate) hotspot_y: u16,
|
||||
}
|
||||
|
||||
impl CursorImage {
|
||||
pub(crate) fn from_rgba(
|
||||
rgba: Vec<u8>,
|
||||
width: u16,
|
||||
height: u16,
|
||||
hotspot_x: u16,
|
||||
hotspot_y: u16,
|
||||
) -> Result<Self, BadImage> {
|
||||
if width > MAX_CURSOR_SIZE || height > MAX_CURSOR_SIZE {
|
||||
return Err(BadImage::TooLarge { width, height });
|
||||
}
|
||||
|
||||
if rgba.len() % PIXEL_SIZE != 0 {
|
||||
return Err(BadImage::ByteCountNotDivisibleBy4 {
|
||||
byte_count: rgba.len(),
|
||||
});
|
||||
}
|
||||
|
||||
let pixel_count = (rgba.len() / PIXEL_SIZE) as u64;
|
||||
let width_x_height = width as u64 * height as u64;
|
||||
if pixel_count != width_x_height {
|
||||
return Err(BadImage::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height,
|
||||
pixel_count,
|
||||
});
|
||||
}
|
||||
|
||||
if hotspot_x >= width || hotspot_y >= height {
|
||||
return Err(BadImage::HotspotOutOfBounds {
|
||||
width,
|
||||
height,
|
||||
hotspot_x,
|
||||
hotspot_y,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(CursorImage {
|
||||
rgba,
|
||||
width,
|
||||
height,
|
||||
hotspot_x,
|
||||
hotspot_y,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Platforms that don't support cursors will export this as `PlatformCustomCursor`.
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub(crate) struct NoCustomCursor;
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl NoCustomCursor {
|
||||
pub(crate) fn from_rgba(
|
||||
rgba: Vec<u8>,
|
||||
width: u16,
|
||||
height: u16,
|
||||
hotspot_x: u16,
|
||||
hotspot_y: u16,
|
||||
) -> Result<Self, BadImage> {
|
||||
CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y)?;
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
fn build<T>(self, _: &platform_impl::EventLoopWindowTarget<T>) -> NoCustomCursor {
|
||||
self
|
||||
}
|
||||
}
|
||||
39
src/dpi.rs
39
src/dpi.rs
@@ -4,13 +4,13 @@
|
||||
//!
|
||||
//! Modern computer screens don't have a consistent relationship between resolution and size.
|
||||
//! 1920x1080 is a common resolution for both desktop and mobile screens, despite mobile screens
|
||||
//! normally being less than a quarter the size of their desktop counterparts. What's more, neither
|
||||
//! desktop nor mobile screens are consistent resolutions within their own size classes - common
|
||||
//! typically being less than a quarter the size of their desktop counterparts. Moreover, neither
|
||||
//! desktop nor mobile screens have consistent resolutions within their own size classes - common
|
||||
//! mobile screens range from below 720p to above 1440p, and desktop screens range from 720p to 5K
|
||||
//! and beyond.
|
||||
//!
|
||||
//! Given that, it's a mistake to assume that 2D content will only be displayed on screens with
|
||||
//! a consistent pixel density. If you were to render a 96-pixel-square image on a 1080p screen,
|
||||
//! a consistent pixel density. If you were to render a 96-pixel-square image on a 1080p screen and
|
||||
//! then render the same image on a similarly-sized 4K screen, the 4K rendition would only take up
|
||||
//! about a quarter of the physical space as it did on the 1080p screen. That issue is especially
|
||||
//! problematic with text rendering, where quarter-sized text becomes a significant legibility
|
||||
@@ -25,12 +25,12 @@
|
||||
//!
|
||||
//! The solution to this problem is to account for the device's *scale factor*. The scale factor is
|
||||
//! the factor UI elements should be scaled by to be consistent with the rest of the user's system -
|
||||
//! for example, a button that's normally 50 pixels across would be 100 pixels across on a device
|
||||
//! for example, a button that's usually 50 pixels across would be 100 pixels across on a device
|
||||
//! with a scale factor of `2.0`, or 75 pixels across with a scale factor of `1.5`.
|
||||
//!
|
||||
//! Many UI systems, such as CSS, expose DPI-dependent units like [points] or [picas]. That's
|
||||
//! usually a mistake, since there's no consistent mapping between the scale factor and the screen's
|
||||
//! actual DPI. Unless you're printing to a physical medium, you should work in scaled pixels rather
|
||||
//! usually a mistake since there's no consistent mapping between the scale factor and the screen's
|
||||
//! actual DPI. Unless printing to a physical medium, you should work in scaled pixels rather
|
||||
//! than any DPI-dependent units.
|
||||
//!
|
||||
//! ### Position and Size types
|
||||
@@ -42,11 +42,11 @@
|
||||
//! coordinates as input, allowing you to use the most convenient coordinate system for your
|
||||
//! particular application.
|
||||
//!
|
||||
//! Winit's position and size types types are generic over their exact pixel type, `P`, to allow the
|
||||
//! Winit's position and size types are generic over their exact pixel type, `P`, to allow the
|
||||
//! API to have integer precision where appropriate (e.g. most window manipulation functions) and
|
||||
//! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch
|
||||
//! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so
|
||||
//! will truncate the fractional part of the float, rather than properly round to the nearest
|
||||
//! will truncate the fractional part of the float rather than properly round to the nearest
|
||||
//! integer. Use the provided `cast` function or [`From`]/[`Into`] conversions, which handle the
|
||||
//! rounding properly. Note that precision loss will still occur when rounding from a float to an
|
||||
//! int, although rounding lessens the problem.
|
||||
@@ -55,34 +55,35 @@
|
||||
//!
|
||||
//! Winit will dispatch a [`ScaleFactorChanged`] event whenever a window's scale factor has changed.
|
||||
//! This can happen if the user drags their window from a standard-resolution monitor to a high-DPI
|
||||
//! monitor, or if the user changes their DPI settings. This gives you a chance to rescale your
|
||||
//! application's UI elements and adjust how the platform changes the window's size to reflect the new
|
||||
//! scale factor. If a window hasn't received a [`ScaleFactorChanged`] event, then its scale factor
|
||||
//! monitor or if the user changes their DPI settings. This allows you to rescale your application's
|
||||
//! UI elements and adjust how the platform changes the window's size to reflect the new scale
|
||||
//! factor. If a window hasn't received a [`ScaleFactorChanged`] event, its scale factor
|
||||
//! can be found by calling [`window.scale_factor()`].
|
||||
//!
|
||||
//! ## How is the scale factor calculated?
|
||||
//!
|
||||
//! Scale factor is calculated differently on different platforms:
|
||||
//! The scale factor is calculated differently on different platforms:
|
||||
//!
|
||||
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the
|
||||
//! display settings. While users are free to select any option they want, they're only given a
|
||||
//! selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7, the scale factor is
|
||||
//! selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7. The scale factor is
|
||||
//! global and changing it requires logging out. See [this article][windows_1] for technical
|
||||
//! details.
|
||||
//! - **macOS:** Recent versions of macOS allow the user to change the scaling factor for certain
|
||||
//! displays. When this is available, the user may pick a per-monitor scaling factor from a set
|
||||
//! of pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default but
|
||||
//! the specific value varies across devices.
|
||||
//! - **macOS:** Recent macOS versions allow the user to change the scaling factor for specific
|
||||
//! displays. When available, the user may pick a per-monitor scaling factor from a set of
|
||||
//! pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default,
|
||||
//! but the specific value varies across devices.
|
||||
//! - **X11:** Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit
|
||||
//! currently uses a three-pronged approach:
|
||||
//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable, if present.
|
||||
//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable if present.
|
||||
//! + If not present, use the value set in `Xft.dpi` in Xresources.
|
||||
//! + Otherwise, calculate the scale factor based on the millimeter monitor dimensions provided by XRandR.
|
||||
//!
|
||||
//! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the
|
||||
//! XRandR scaling method. Generally speaking, you should try to configure the standard system
|
||||
//! variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`.
|
||||
//! - **Wayland:** Scale factor is suggested by the the compositor.
|
||||
//! - **Wayland:** The scale factor is suggested by the compositor for each window individually. The
|
||||
//! monitor scale factor may differ from the window scale factor.
|
||||
//! - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range
|
||||
//! from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more
|
||||
//! information.
|
||||
|
||||
@@ -671,7 +671,7 @@ pub enum DeviceEvent {
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct RawKeyEvent {
|
||||
pub physical_key: keyboard::KeyCode,
|
||||
pub physical_key: keyboard::PhysicalKey,
|
||||
pub state: ElementState,
|
||||
}
|
||||
|
||||
@@ -703,7 +703,7 @@ pub struct KeyEvent {
|
||||
/// `Fn` and `FnLock` key events are *exceedingly unlikely* to be emitted by Winit. These keys
|
||||
/// are usually handled at the hardware or OS level, and aren't surfaced to applications. If
|
||||
/// you somehow see this in the wild, we'd like to know :)
|
||||
pub physical_key: keyboard::KeyCode,
|
||||
pub physical_key: keyboard::PhysicalKey,
|
||||
|
||||
// Allowing `broken_intra_doc_links` for `logical_key`, because
|
||||
// `key_without_modifiers` is not available on all platforms
|
||||
@@ -743,7 +743,7 @@ pub struct KeyEvent {
|
||||
/// An additional difference from `logical_key` is that
|
||||
/// this field stores the text representation of any key
|
||||
/// that has such a representation. For example when
|
||||
/// `logical_key` is `Key::Enter`, this field is `Some("\r")`.
|
||||
/// `logical_key` is `Key::Named(NamedKey::Enter)`, this field is `Some("\r")`.
|
||||
///
|
||||
/// This is `None` if the current keypress cannot
|
||||
/// be interpreted as text.
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
//! handle events.
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::{error, fmt};
|
||||
|
||||
@@ -222,14 +224,22 @@ impl<T> EventLoop<T> {
|
||||
/// (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 `spawn()` instead of `run()` to avoid the need
|
||||
/// Web applications are recommended to use
|
||||
#[cfg_attr(
|
||||
wasm_platform,
|
||||
doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]"
|
||||
)]
|
||||
#[cfg_attr(not(wasm_platform), doc = "`EventLoopExtWebSys::spawn()`")]
|
||||
/// [^1] instead of [`run()`] 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"`.
|
||||
///
|
||||
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow
|
||||
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow()
|
||||
/// [`run()`]: Self::run()
|
||||
/// [^1]: `EventLoopExtWebSys::spawn()` is only available on WASM.
|
||||
#[inline]
|
||||
#[cfg(not(all(wasm_platform, target_feature = "exception-handling")))]
|
||||
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
|
||||
@@ -256,12 +266,40 @@ impl<T> rwh_06::HasDisplayHandle for EventLoop<T> {
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
|
||||
/// Returns a [`raw_window_handle::RawDisplayHandle`] for the event loop.
|
||||
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::HasRawDisplayHandle::raw_display_handle(&**self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
/// 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_events`] API.
|
||||
///
|
||||
/// [`calloop`]: https://crates.io/crates/calloop
|
||||
/// [`mio`]: https://crates.io/crates/mio
|
||||
/// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
self.event_loop.as_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
/// 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_events`] API.
|
||||
///
|
||||
/// [`calloop`]: https://crates.io/crates/calloop
|
||||
/// [`mio`]: https://crates.io/crates/mio
|
||||
/// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.event_loop.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for EventLoop<T> {
|
||||
type Target = EventLoopWindowTarget<T>;
|
||||
fn deref(&self) -> &EventLoopWindowTarget<T> {
|
||||
@@ -345,7 +383,7 @@ impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> {
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoopWindowTarget<T> {
|
||||
/// Returns a [`raw_window_handle::RawDisplayHandle`] for the event loop.
|
||||
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
self.p.raw_display_handle_rwh_05()
|
||||
}
|
||||
|
||||
@@ -49,11 +49,7 @@ impl fmt::Display for BadIcon {
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BadIcon {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
impl Error for BadIcon {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct RgbaIcon {
|
||||
|
||||
328
src/keyboard.rs
328
src/keyboard.rs
@@ -185,26 +185,112 @@ impl std::fmt::Debug for NativeKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NativeKeyCode> for NativeKey {
|
||||
#[inline]
|
||||
fn from(code: NativeKeyCode) -> Self {
|
||||
match code {
|
||||
NativeKeyCode::Unidentified => NativeKey::Unidentified,
|
||||
NativeKeyCode::Android(x) => NativeKey::Android(x),
|
||||
NativeKeyCode::MacOS(x) => NativeKey::MacOS(x),
|
||||
NativeKeyCode::Windows(x) => NativeKey::Windows(x),
|
||||
NativeKeyCode::Xkb(x) => NativeKey::Xkb(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<NativeKey> for NativeKeyCode {
|
||||
#[allow(clippy::cmp_owned)] // uses less code than direct match; target is stack allocated
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &NativeKey) -> bool {
|
||||
NativeKey::from(*self) == *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<NativeKeyCode> for NativeKey {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &NativeKeyCode) -> bool {
|
||||
rhs == self
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the location of a physical key.
|
||||
///
|
||||
/// This type is a superset of [`KeyCode`], including an [`Unidentified`](Self::Unidentified)
|
||||
/// variant.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum PhysicalKey {
|
||||
/// A known key code
|
||||
Code(KeyCode),
|
||||
/// This variant is used when the key cannot be translated to a [`KeyCode`]
|
||||
///
|
||||
/// The native keycode is provided (if available) so you're able to more reliably match
|
||||
/// key-press and key-release events by hashing the [`PhysicalKey`]. It is also possible to use
|
||||
/// this for keybinds for non-standard keys, but such keybinds are tied to a given platform.
|
||||
Unidentified(NativeKeyCode),
|
||||
}
|
||||
|
||||
impl From<KeyCode> for PhysicalKey {
|
||||
#[inline]
|
||||
fn from(code: KeyCode) -> Self {
|
||||
PhysicalKey::Code(code)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NativeKeyCode> for PhysicalKey {
|
||||
#[inline]
|
||||
fn from(code: NativeKeyCode) -> Self {
|
||||
PhysicalKey::Unidentified(code)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<KeyCode> for PhysicalKey {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &KeyCode) -> bool {
|
||||
match self {
|
||||
PhysicalKey::Code(ref code) => code == rhs,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<PhysicalKey> for KeyCode {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &PhysicalKey) -> bool {
|
||||
rhs == self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<NativeKeyCode> for PhysicalKey {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &NativeKeyCode) -> bool {
|
||||
match self {
|
||||
PhysicalKey::Unidentified(ref code) => code == rhs,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<PhysicalKey> for NativeKeyCode {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &PhysicalKey) -> bool {
|
||||
rhs == self
|
||||
}
|
||||
}
|
||||
|
||||
/// Code representing the location of a physical key
|
||||
///
|
||||
/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.code`] with a few
|
||||
/// exceptions:
|
||||
/// - The keys that the specification calls "MetaLeft" and "MetaRight" are named "SuperLeft" and
|
||||
/// "SuperRight" here.
|
||||
/// - The key that the specification calls "Super" is reported as `Unidentified` here.
|
||||
/// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`.
|
||||
///
|
||||
/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum KeyCode {
|
||||
/// This variant is used when the key cannot be translated to any other variant.
|
||||
///
|
||||
/// The native keycode is provided (if available) so you're able to more reliably match
|
||||
/// key-press and key-release events by hashing the [`KeyCode`]. It is also possible to use
|
||||
/// this for keybinds for non-standard keys, but such keybinds are tied to a given platform.
|
||||
Unidentified(NativeKeyCode),
|
||||
/// <kbd>`</kbd> on a US keyboard. This is also called a backtick or grave.
|
||||
/// This is the <kbd>半角</kbd>/<kbd>全角</kbd>/<kbd>漢字</kbd>
|
||||
/// (hankaku/zenkaku/kanji) key on Japanese keyboards
|
||||
@@ -648,7 +734,7 @@ pub enum KeyCode {
|
||||
F35,
|
||||
}
|
||||
|
||||
/// Key represents the meaning of a keypress.
|
||||
/// A [`Key::Named`] value
|
||||
///
|
||||
/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few
|
||||
/// exceptions:
|
||||
@@ -656,32 +742,12 @@ pub enum KeyCode {
|
||||
/// another key which the specification calls `Super`. That does not exist here.)
|
||||
/// - The `Space` variant here, can be identified by the character it generates in the
|
||||
/// specificaiton.
|
||||
/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`.
|
||||
/// - The `Dead` variant here, can specify the character which is inserted when pressing the
|
||||
/// dead-key twice.
|
||||
///
|
||||
/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Key<Str = SmolStr> {
|
||||
/// A key string that corresponds to the character typed by the user, taking into account the
|
||||
/// user’s current locale setting, and any system-level keyboard mapping overrides that are in
|
||||
/// effect.
|
||||
Character(Str),
|
||||
|
||||
/// This variant is used when the key cannot be translated to any other variant.
|
||||
///
|
||||
/// The native key is provided (if available) in order to allow the user to specify keybindings
|
||||
/// for keys which are not defined by this API, mainly through some sort of UI.
|
||||
Unidentified(NativeKey),
|
||||
|
||||
/// Contains the text representation of the dead-key when available.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Web:** Always contains `None`
|
||||
Dead(Option<char>),
|
||||
|
||||
pub enum NamedKey {
|
||||
/// The `Alt` (Alternative) key.
|
||||
///
|
||||
/// This key enables the alternate modifier function for interpreting concurrent or subsequent
|
||||
@@ -1385,83 +1451,131 @@ pub enum Key<Str = SmolStr> {
|
||||
F35,
|
||||
}
|
||||
|
||||
macro_rules! map_match {
|
||||
(
|
||||
$to_match:expr,
|
||||
// Custom match arms
|
||||
{ $( $from:pat => $to:expr ),* },
|
||||
// The enum's name
|
||||
$prefix:path,
|
||||
// Trivial match arms for unit variants
|
||||
{ $( $t:tt ),* }) => {
|
||||
match $to_match {
|
||||
$( $from => $to, )*
|
||||
$( Key::$t => Key::$t, )*
|
||||
/// Key represents the meaning of a keypress.
|
||||
///
|
||||
/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with
|
||||
/// additions:
|
||||
/// - All simple variants are wrapped under the `Named` variant
|
||||
/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`.
|
||||
/// - The `Dead` variant here, can specify the character which is inserted when pressing the
|
||||
/// dead-key twice.
|
||||
///
|
||||
/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Key<Str = SmolStr> {
|
||||
/// A simple (unparameterised) action
|
||||
Named(NamedKey),
|
||||
|
||||
/// A key string that corresponds to the character typed by the user, taking into account the
|
||||
/// user’s current locale setting, and any system-level keyboard mapping overrides that are in
|
||||
/// effect.
|
||||
Character(Str),
|
||||
|
||||
/// This variant is used when the key cannot be translated to any other variant.
|
||||
///
|
||||
/// The native key is provided (if available) in order to allow the user to specify keybindings
|
||||
/// for keys which are not defined by this API, mainly through some sort of UI.
|
||||
Unidentified(NativeKey),
|
||||
|
||||
/// Contains the text representation of the dead-key when available.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Web:** Always contains `None`
|
||||
Dead(Option<char>),
|
||||
}
|
||||
|
||||
impl From<NamedKey> for Key {
|
||||
#[inline]
|
||||
fn from(action: NamedKey) -> Self {
|
||||
Key::Named(action)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NativeKey> for Key {
|
||||
#[inline]
|
||||
fn from(code: NativeKey) -> Self {
|
||||
Key::Unidentified(code)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Str> PartialEq<NamedKey> for Key<Str> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &NamedKey) -> bool {
|
||||
match self {
|
||||
Key::Named(ref a) => a == rhs,
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<Str: PartialEq<str>> PartialEq<str> for Key<Str> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &str) -> bool {
|
||||
match self {
|
||||
Key::Character(ref s) => s == rhs,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Str: PartialEq<str>> PartialEq<&str> for Key<Str> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &&str) -> bool {
|
||||
self == *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<Str> PartialEq<NativeKey> for Key<Str> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &NativeKey) -> bool {
|
||||
match self {
|
||||
Key::Unidentified(ref code) => code == rhs,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Str> PartialEq<Key<Str>> for NativeKey {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &Key<Str>) -> bool {
|
||||
rhs == self
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<SmolStr> {
|
||||
/// Convert `Key::Character(SmolStr)` to `Key::Character(&str)` so you can more easily match on
|
||||
/// `Key`. All other variants remain unchanged.
|
||||
pub fn as_ref(&self) -> Key<&str> {
|
||||
map_match!(
|
||||
self,
|
||||
{
|
||||
Key::Character(ch) => Key::Character(ch.as_str()),
|
||||
Key::Dead(d) => Key::Dead(*d),
|
||||
Key::Unidentified(u) => Key::Unidentified(u.clone())
|
||||
},
|
||||
Key,
|
||||
{
|
||||
Alt, AltGraph, CapsLock, Control, Fn, FnLock, NumLock, ScrollLock, Shift, Symbol,
|
||||
SymbolLock, Meta, Hyper, Super, Enter, Tab, Space, ArrowDown, ArrowLeft,
|
||||
ArrowRight, ArrowUp, End, Home, PageDown, PageUp, Backspace, Clear, Copy, CrSel,
|
||||
Cut, Delete, EraseEof, ExSel, Insert, Paste, Redo, Undo, Accept, Again, Attn,
|
||||
Cancel, ContextMenu, Escape, Execute, Find, Help, Pause, Play, Props, Select,
|
||||
ZoomIn, ZoomOut, BrightnessDown, BrightnessUp, Eject, LogOff, Power, PowerOff,
|
||||
PrintScreen, Hibernate, Standby, WakeUp, AllCandidates, Alphanumeric, CodeInput,
|
||||
Compose, Convert, FinalMode, GroupFirst, GroupLast, GroupNext, GroupPrevious,
|
||||
ModeChange, NextCandidate, NonConvert, PreviousCandidate, Process, SingleCandidate,
|
||||
HangulMode, HanjaMode, JunjaMode, Eisu, Hankaku, Hiragana, HiraganaKatakana,
|
||||
KanaMode, KanjiMode, Katakana, Romaji, Zenkaku, ZenkakuHankaku, Soft1, Soft2,
|
||||
Soft3, Soft4, ChannelDown, ChannelUp, Close, MailForward, MailReply, MailSend,
|
||||
MediaClose, MediaFastForward, MediaPause, MediaPlay, MediaPlayPause, MediaRecord,
|
||||
MediaRewind, MediaStop, MediaTrackNext, MediaTrackPrevious, New, Open, Print, Save,
|
||||
SpellCheck, Key11, Key12, AudioBalanceLeft, AudioBalanceRight, AudioBassBoostDown,
|
||||
AudioBassBoostToggle, AudioBassBoostUp, AudioFaderFront, AudioFaderRear,
|
||||
AudioSurroundModeNext, AudioTrebleDown, AudioTrebleUp, AudioVolumeDown,
|
||||
AudioVolumeUp, AudioVolumeMute, MicrophoneToggle, MicrophoneVolumeDown,
|
||||
MicrophoneVolumeUp, MicrophoneVolumeMute, SpeechCorrectionList, SpeechInputToggle,
|
||||
LaunchApplication1, LaunchApplication2, LaunchCalendar, LaunchContacts, LaunchMail,
|
||||
LaunchMediaPlayer, LaunchMusicPlayer, LaunchPhone, LaunchScreenSaver,
|
||||
LaunchSpreadsheet, LaunchWebBrowser, LaunchWebCam, LaunchWordProcessor,
|
||||
BrowserBack, BrowserFavorites, BrowserForward, BrowserHome, BrowserRefresh,
|
||||
BrowserSearch, BrowserStop, AppSwitch, Call, Camera, CameraFocus, EndCall, GoBack,
|
||||
GoHome, HeadsetHook, LastNumberRedial, Notification, MannerMode, VoiceDial, TV,
|
||||
TV3DMode, TVAntennaCable, TVAudioDescription, TVAudioDescriptionMixDown,
|
||||
TVAudioDescriptionMixUp, TVContentsMenu, TVDataService, TVInput, TVInputComponent1,
|
||||
TVInputComponent2, TVInputComposite1, TVInputComposite2, TVInputHDMI1,
|
||||
TVInputHDMI2, TVInputHDMI3, TVInputHDMI4, TVInputVGA1, TVMediaContext, TVNetwork,
|
||||
TVNumberEntry, TVPower, TVRadioService, TVSatellite, TVSatelliteBS, TVSatelliteCS,
|
||||
TVSatelliteToggle, TVTerrestrialAnalog, TVTerrestrialDigital, TVTimer, AVRInput,
|
||||
AVRPower, ColorF0Red, ColorF1Green, ColorF2Yellow, ColorF3Blue, ColorF4Grey,
|
||||
ColorF5Brown, ClosedCaptionToggle, Dimmer, DisplaySwap, DVR, Exit, FavoriteClear0,
|
||||
FavoriteClear1, FavoriteClear2, FavoriteClear3, FavoriteRecall0, FavoriteRecall1,
|
||||
FavoriteRecall2, FavoriteRecall3, FavoriteStore0, FavoriteStore1, FavoriteStore2,
|
||||
FavoriteStore3, Guide, GuideNextDay, GuidePreviousDay, Info, InstantReplay, Link,
|
||||
ListProgram, LiveContent, Lock, MediaApps, MediaAudioTrack, MediaLast,
|
||||
MediaSkipBackward, MediaSkipForward, MediaStepBackward, MediaStepForward,
|
||||
MediaTopMenu, NavigateIn, NavigateNext, NavigateOut, NavigatePrevious,
|
||||
NextFavoriteChannel, NextUserProfile, OnDemand, Pairing, PinPDown, PinPMove,
|
||||
PinPToggle, PinPUp, PlaySpeedDown, PlaySpeedReset, PlaySpeedUp, RandomToggle,
|
||||
RcLowBattery, RecordSpeedNext, RfBypass, ScanChannelsToggle, ScreenModeNext,
|
||||
Settings, SplitScreenToggle, STBInput, STBPower, Subtitle, Teletext, VideoModeNext,
|
||||
Wink, ZoomToggle, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,
|
||||
F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31,
|
||||
F32, F33, F34, F35
|
||||
}
|
||||
)
|
||||
match self {
|
||||
Key::Named(a) => Key::Named(*a),
|
||||
Key::Character(ch) => Key::Character(ch.as_str()),
|
||||
Key::Dead(d) => Key::Dead(*d),
|
||||
Key::Unidentified(u) => Key::Unidentified(u.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NamedKey {
|
||||
/// Convert an action to its approximate textual equivalent.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use winit::keyboard::NamedKey;
|
||||
///
|
||||
/// assert_eq!(NamedKey::Enter.to_text(), Some("\r"));
|
||||
/// assert_eq!(NamedKey::F20.to_text(), None);
|
||||
/// ```
|
||||
pub fn to_text(&self) -> Option<&str> {
|
||||
match self {
|
||||
NamedKey::Enter => Some("\r"),
|
||||
NamedKey::Backspace => Some("\x08"),
|
||||
NamedKey::Tab => Some("\t"),
|
||||
NamedKey::Space => Some(" "),
|
||||
NamedKey::Escape => Some("\x1b"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1471,20 +1585,16 @@ impl Key {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use winit::keyboard::Key;
|
||||
/// use winit::keyboard::{NamedKey, Key};
|
||||
///
|
||||
/// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
|
||||
/// assert_eq!(Key::Enter.to_text(), Some("\r"));
|
||||
/// assert_eq!(Key::F20.to_text(), None);
|
||||
/// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
|
||||
/// assert_eq!(Key::Named(NamedKey::F20).to_text(), None);
|
||||
/// ```
|
||||
pub fn to_text(&self) -> Option<&str> {
|
||||
match self {
|
||||
Key::Named(action) => action.to_text(),
|
||||
Key::Character(ch) => Some(ch.as_str()),
|
||||
Key::Enter => Some("\r"),
|
||||
Key::Backspace => Some("\x08"),
|
||||
Key::Tab => Some("\t"),
|
||||
Key::Space => Some(" "),
|
||||
Key::Escape => Some("\x1b"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1572,7 +1682,7 @@ bitflags! {
|
||||
/// Represents the current state of the keyboard modifiers
|
||||
///
|
||||
/// Each flag represents a modifier and is set if this modifier is active.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ModifiersState: u32 {
|
||||
/// The "shift" key.
|
||||
const SHIFT = 0b100;
|
||||
|
||||
74
src/lib.rs
74
src/lib.rs
@@ -10,12 +10,12 @@
|
||||
//! let event_loop = EventLoop::new().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! Once this is done there are two ways to create a [`Window`]:
|
||||
//! Once this is done, there are two ways to create a [`Window`]:
|
||||
//!
|
||||
//! - Calling [`Window::new(&event_loop)`][window_new].
|
||||
//! - Calling [`let builder = WindowBuilder::new()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build].
|
||||
//!
|
||||
//! The first method is the simplest, and will give you default values for everything. The second
|
||||
//! The first method is the simplest and will give you default values for everything. The second
|
||||
//! method allows you to customize the way your [`Window`] will look and behave by modifying the
|
||||
//! fields of the [`WindowBuilder`] object before you create the [`Window`].
|
||||
//!
|
||||
@@ -26,17 +26,37 @@
|
||||
//! window or a key getting pressed while the window is focused. Devices can generate
|
||||
//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window.
|
||||
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
|
||||
//! [`DeviceEvent`]. You can also create and handle your own custom [`UserEvent`]s, if desired.
|
||||
//! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if desired.
|
||||
//!
|
||||
//! You can retrieve events by calling [`EventLoop::run`][event_loop_run]. This function will
|
||||
//! You can retrieve events by calling [`EventLoop::run()`]. This function will
|
||||
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
|
||||
//! will run until [`exit()`] is used, at which point [`Event`]`::`[`LoopExiting`].
|
||||
//! will run until [`exit()`] is used, at which point [`Event::LoopExiting`].
|
||||
//!
|
||||
//! 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_events`]. 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_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_events()]"
|
||||
)]
|
||||
#![cfg_attr(
|
||||
not(any(
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
android_platform,
|
||||
x11_platform,
|
||||
wayland_platform
|
||||
)),
|
||||
doc = "`EventLoopExtPumpEvents::pump_events()`"
|
||||
)]
|
||||
//! [^1]. See that method's documentation for more reasons about why
|
||||
//! it's discouraged beyond compatibility reasons.
|
||||
//!
|
||||
//!
|
||||
//! ```no_run
|
||||
@@ -72,9 +92,9 @@
|
||||
//!
|
||||
//! // Queue a RedrawRequested event.
|
||||
//! //
|
||||
//! // You only need to call this if you've determined that you need to redraw, in
|
||||
//! // You only need to call this if you've determined that you need to redraw in
|
||||
//! // applications which do not always need to. Applications that redraw continuously
|
||||
//! // can just render here instead.
|
||||
//! // can render here instead.
|
||||
//! window.request_redraw();
|
||||
//! },
|
||||
//! Event::WindowEvent {
|
||||
@@ -92,16 +112,16 @@
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! [`Event`]`::`[`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
|
||||
//! compared to the value returned by [`Window::id()`][window_id_fn] to determine which [`Window`]
|
||||
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
|
||||
//! compared to the value returned by [`Window::id()`] to determine which [`Window`]
|
||||
//! dispatched the event.
|
||||
//!
|
||||
//! # Drawing on the window
|
||||
//!
|
||||
//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However it allows you to
|
||||
//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However, it allows you to
|
||||
//! retrieve the raw handle of the window and display (see the [`platform`] module and/or the
|
||||
//! [`raw_window_handle`] and [`raw_display_handle`] methods), which in turn allows
|
||||
//! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
|
||||
//! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
|
||||
//!
|
||||
//! Note that many platforms will display garbage data in the window's client area if the
|
||||
//! application doesn't render anything to the window by the time the desktop compositor is ready to
|
||||
@@ -110,9 +130,8 @@
|
||||
//! window visible only once you're ready to render into it.
|
||||
//!
|
||||
//! [`EventLoop`]: event_loop::EventLoop
|
||||
//! [`EventLoopExtPumpEvents::pump_events`]: ./platform/pump_events/trait.EventLoopExtPumpEvents.html#tymethod.pump_events
|
||||
//! [`EventLoop::new()`]: event_loop::EventLoop::new
|
||||
//! [event_loop_run]: event_loop::EventLoop::run
|
||||
//! [`EventLoop::run()`]: event_loop::EventLoop::run
|
||||
//! [`exit()`]: event_loop::EventLoopWindowTarget::exit
|
||||
//! [`Window`]: window::Window
|
||||
//! [`WindowId`]: window::WindowId
|
||||
@@ -120,15 +139,14 @@
|
||||
//! [window_new]: window::Window::new
|
||||
//! [window_builder_new]: window::WindowBuilder::new
|
||||
//! [window_builder_build]: window::WindowBuilder::build
|
||||
//! [window_id_fn]: window::Window::id
|
||||
//! [`Event`]: event::Event
|
||||
//! [`Window::id()`]: window::Window::id
|
||||
//! [`WindowEvent`]: event::WindowEvent
|
||||
//! [`DeviceEvent`]: event::DeviceEvent
|
||||
//! [`UserEvent`]: event::Event::UserEvent
|
||||
//! [`LoopExiting`]: event::Event::LoopExiting
|
||||
//! [`platform`]: platform
|
||||
//! [`Event::UserEvent`]: event::Event::UserEvent
|
||||
//! [`Event::LoopExiting`]: event::Event::LoopExiting
|
||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
|
||||
//! [^1]: `EventLoopExtPumpEvents::pump_events()` is only available on Windows, macOS, Android, X11 and Wayland.
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
@@ -154,6 +172,7 @@ extern crate bitflags;
|
||||
pub mod dpi;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
mod cursor;
|
||||
pub mod event;
|
||||
pub mod event_loop;
|
||||
mod icon;
|
||||
@@ -163,3 +182,18 @@ mod platform_impl;
|
||||
pub mod window;
|
||||
|
||||
pub mod platform;
|
||||
|
||||
/// Wrapper for objects which winit will access on the main thread so they are effectively `Send`
|
||||
/// and `Sync`, since they always execute on a single thread.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Winit can run only one event loop at a time, and the event loop itself is tied to some thread.
|
||||
/// The objects could be sent across the threads, but once passed to winit, they execute on the
|
||||
/// main thread if the platform demands it. Thus, marking such objects as `Send + Sync` is safe.
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct SendSyncWrapper<T>(pub(crate) T);
|
||||
|
||||
unsafe impl<T> Send for SendSyncWrapper<T> {}
|
||||
unsafe impl<T> Sync for SendSyncWrapper<T> {}
|
||||
|
||||
@@ -138,14 +138,18 @@ impl MonitorHandle {
|
||||
self.inner.refresh_rate_millihertz()
|
||||
}
|
||||
|
||||
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||
/// Returns the scale factor of the underlying monitor. To map logical pixels to physical
|
||||
/// pixels and vice versa, use [`Window::scale_factor`].
|
||||
///
|
||||
/// See the [`dpi`](crate::dpi) module for more information.
|
||||
///
|
||||
/// ## 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.
|
||||
///
|
||||
/// [`Window::scale_factor`]: crate::window::Window::scale_factor
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.inner.scale_factor()
|
||||
|
||||
@@ -84,5 +84,10 @@ impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
|
||||
/// use winit::platform::android::activity::AndroidApp;
|
||||
/// ```
|
||||
pub mod activity {
|
||||
// We enable the `"native-activity"` feature just so that we can build the
|
||||
// docs, but it'll be very confusing for users to see the docs with that
|
||||
// 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)]
|
||||
pub use android_activity::*;
|
||||
}
|
||||
|
||||
@@ -69,11 +69,25 @@ pub trait WindowExtIOS {
|
||||
///
|
||||
/// The default is to prefer showing the status bar.
|
||||
///
|
||||
/// This changes the value returned by
|
||||
/// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc),
|
||||
/// and then calls
|
||||
/// [`-[UIViewController setNeedsStatusBarAppearanceUpdate]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc).
|
||||
/// This sets the value of the
|
||||
/// [`prefersStatusBarHidden`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc)
|
||||
/// property.
|
||||
///
|
||||
/// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc)
|
||||
/// is also called for you.
|
||||
fn set_prefers_status_bar_hidden(&self, hidden: bool);
|
||||
|
||||
/// Sets the preferred status bar style for the [`Window`].
|
||||
///
|
||||
/// The default is system-defined.
|
||||
///
|
||||
/// This sets the value of the
|
||||
/// [`preferredStatusBarStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc)
|
||||
/// property.
|
||||
///
|
||||
/// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc)
|
||||
/// is also called for you.
|
||||
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle);
|
||||
}
|
||||
|
||||
impl WindowExtIOS for Window {
|
||||
@@ -107,6 +121,12 @@ impl WindowExtIOS for Window {
|
||||
self.window
|
||||
.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
|
||||
self.window
|
||||
.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`WindowBuilder`] that are specific to iOS.
|
||||
@@ -154,6 +174,14 @@ pub trait WindowBuilderExtIOS {
|
||||
/// This sets the initial value returned by
|
||||
/// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc).
|
||||
fn with_prefers_status_bar_hidden(self, hidden: bool) -> Self;
|
||||
|
||||
/// Sets the style of the [`Window`]'s status bar.
|
||||
///
|
||||
/// The default is system-defined.
|
||||
///
|
||||
/// This sets the initial value returned by
|
||||
/// [`-[UIViewController preferredStatusBarStyle]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc),
|
||||
fn with_preferred_status_bar_style(self, status_bar_style: StatusBarStyle) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtIOS for WindowBuilder {
|
||||
@@ -187,6 +215,12 @@ impl WindowBuilderExtIOS for WindowBuilder {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`MonitorHandle`] that are specific to iOS.
|
||||
@@ -264,3 +298,11 @@ bitflags! {
|
||||
| ScreenEdge::BOTTOM.bits() | ScreenEdge::RIGHT.bits();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum StatusBarStyle {
|
||||
#[default]
|
||||
Default,
|
||||
LightContent,
|
||||
DarkContent,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
use objc2::rc::Id;
|
||||
|
||||
use crate::{
|
||||
@@ -365,7 +366,9 @@ impl MonitorHandleExtMacOS for MonitorHandle {
|
||||
}
|
||||
|
||||
fn ns_screen(&self) -> Option<*mut c_void> {
|
||||
self.inner.ns_screen().map(|s| Id::as_ptr(&s) as _)
|
||||
// SAFETY: We only use the marker to get a pointer
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
self.inner.ns_screen(mtm).map(|s| Id::as_ptr(&s) as _)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,19 +155,19 @@ pub trait EventLoopExtPumpEvents {
|
||||
/// - **Windows**: The implementation will use `PeekMessage` when checking for
|
||||
/// window messages to avoid blocking your external event loop.
|
||||
///
|
||||
/// - **MacOS**: The implementation works in terms of stopping the global `NSApp`
|
||||
/// - **MacOS**: The implementation works in terms of stopping the global application
|
||||
/// whenever the application `RunLoop` indicates that it is preparing to block
|
||||
/// and wait for new events.
|
||||
///
|
||||
/// This is very different to the polling APIs that are available on other
|
||||
/// platforms (the lower level polling primitives on MacOS are private
|
||||
/// implementation details for `NSApp` which aren't accessible to application
|
||||
/// developers)
|
||||
/// implementation details for `NSApplication` which aren't accessible to
|
||||
/// application developers)
|
||||
///
|
||||
/// It's likely this will be less efficient than polling on other OSs and
|
||||
/// it also means the `NSApp` is stopped while outside of the Winit
|
||||
/// it also means the `NSApplication` is stopped while outside of the Winit
|
||||
/// event loop - and that's observable (for example to crates like `rfd`)
|
||||
/// because the `NSApp` is global state.
|
||||
/// because the `NSApplication` is global state.
|
||||
///
|
||||
/// If you render outside of Winit you are likely to see window resizing artifacts
|
||||
/// since MacOS expects applications to render synchronously during any `drawRect`
|
||||
|
||||
@@ -35,9 +35,9 @@ pub trait EventLoopExtRunOnDemand {
|
||||
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
|
||||
///
|
||||
/// # 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 `spawn()` more than once instead).
|
||||
/// - 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 `EventLoopExtWebSys::spawn()`[^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()`] for portability, unless you specifically need
|
||||
@@ -57,8 +57,13 @@ pub trait EventLoopExtRunOnDemand {
|
||||
/// on an event loop that is internal to the browser itself.
|
||||
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS.
|
||||
///
|
||||
/// [`exit()`]: EventLoopWindowTarget::exit
|
||||
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow
|
||||
#[cfg_attr(
|
||||
not(wasm_platform),
|
||||
doc = "[^1]: `spawn()` is only available on `wasm` platforms."
|
||||
)]
|
||||
///
|
||||
/// [`exit()`]: EventLoopWindowTarget::exit()
|
||||
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow()
|
||||
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
|
||||
|
||||
use crate::keyboard::KeyCode;
|
||||
use crate::keyboard::{KeyCode, PhysicalKey};
|
||||
|
||||
// TODO: Describe what this value contains for each platform
|
||||
|
||||
/// Additional methods for the [`KeyCode`] type that allow the user to access the platform-specific
|
||||
/// Additional methods for the [`PhysicalKey`] type that allow the user to access the platform-specific
|
||||
/// scancode.
|
||||
///
|
||||
/// [`KeyCode`]: crate::keyboard::KeyCode
|
||||
pub trait KeyCodeExtScancode {
|
||||
/// [`PhysicalKey`]: crate::keyboard::PhysicalKey
|
||||
pub trait PhysicalKeyExtScancode {
|
||||
/// The raw value of the platform-specific physical key identifier.
|
||||
///
|
||||
/// Returns `Some(key_id)` if the conversion was succesful; returns `None` otherwise.
|
||||
@@ -18,13 +18,28 @@ pub trait KeyCodeExtScancode {
|
||||
/// - **Wayland/X11**: A 32-bit linux scancode, which is X11/Wayland keycode subtracted by 8.
|
||||
fn to_scancode(self) -> Option<u32>;
|
||||
|
||||
/// Constructs a `KeyCode` from a platform-specific physical key identifier.
|
||||
/// Constructs a `PhysicalKey` from a platform-specific physical key identifier.
|
||||
///
|
||||
/// Note that this conversion may be lossy, i.e. converting the returned `KeyCode` back
|
||||
/// Note that this conversion may be lossy, i.e. converting the returned `PhysicalKey` back
|
||||
/// using `to_scancode` might not yield the original value.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Wayland/X11**: A 32-bit linux scancode. When building from X11/Wayland keycode subtract
|
||||
/// `8` to get the value you wanted.
|
||||
fn from_scancode(scancode: u32) -> KeyCode;
|
||||
fn from_scancode(scancode: u32) -> PhysicalKey;
|
||||
}
|
||||
|
||||
impl PhysicalKeyExtScancode for KeyCode
|
||||
where
|
||||
PhysicalKey: PhysicalKeyExtScancode,
|
||||
{
|
||||
#[inline]
|
||||
fn from_scancode(scancode: u32) -> PhysicalKey {
|
||||
<PhysicalKey as PhysicalKeyExtScancode>::from_scancode(scancode)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_scancode(self) -> Option<u32> {
|
||||
<PhysicalKey as PhysicalKeyExtScancode>::to_scancode(PhysicalKey::Code(self))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,14 @@
|
||||
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||
|
||||
use crate::cursor::CustomCursorBuilder;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::EventLoop;
|
||||
use crate::event_loop::EventLoopWindowTarget;
|
||||
use crate::platform_impl::PlatformCustomCursorBuilder;
|
||||
use crate::window::CustomCursor;
|
||||
use crate::window::{Window, WindowBuilder};
|
||||
use crate::SendSyncWrapper;
|
||||
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
@@ -81,26 +85,22 @@ pub trait WindowBuilderExtWebSys {
|
||||
|
||||
impl WindowBuilderExtWebSys for WindowBuilder {
|
||||
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
|
||||
self.platform_specific.canvas = canvas;
|
||||
|
||||
self.platform_specific.canvas = SendSyncWrapper(canvas);
|
||||
self
|
||||
}
|
||||
|
||||
fn with_prevent_default(mut self, prevent_default: bool) -> Self {
|
||||
self.platform_specific.prevent_default = prevent_default;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn with_focusable(mut self, focusable: bool) -> Self {
|
||||
self.platform_specific.focusable = focusable;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn with_append(mut self, append: bool) -> Self {
|
||||
self.platform_specific.append = append;
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -112,13 +112,28 @@ pub trait EventLoopExtWebSys {
|
||||
|
||||
/// Initializes the winit event loop.
|
||||
///
|
||||
/// Unlike `run`, this returns immediately, and doesn't throw an exception in order to
|
||||
/// satisfy its `!` return type.
|
||||
/// Unlike
|
||||
#[cfg_attr(
|
||||
all(wasm_platform, target_feature = "exception-handling"),
|
||||
doc = "`run()`"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(all(wasm_platform, target_feature = "exception-handling")),
|
||||
doc = "[`run()`]"
|
||||
)]
|
||||
/// [^1], this returns immediately, and doesn't throw an exception in order to
|
||||
/// satisfy its [`!`] return type.
|
||||
///
|
||||
/// Once the event loop has been destroyed, it's possible to reinitialize another event loop
|
||||
/// by calling this function again. This can be useful if you want to recreate the event loop
|
||||
/// while the WebAssembly module is still loaded. For example, this can be used to recreate the
|
||||
/// event loop when switching between tabs on a single page application.
|
||||
///
|
||||
#[cfg_attr(
|
||||
not(all(wasm_platform, target_feature = "exception-handling")),
|
||||
doc = "[`run()`]: EventLoop::run()"
|
||||
)]
|
||||
/// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`.
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
|
||||
@@ -188,3 +203,24 @@ pub enum PollStrategy {
|
||||
#[default]
|
||||
Scheduler,
|
||||
}
|
||||
|
||||
pub trait CustomCursorExtWebSys {
|
||||
/// Creates a new cursor from a URL pointing to an image.
|
||||
/// It uses the [url css function](https://developer.mozilla.org/en-US/docs/Web/CSS/url),
|
||||
/// but browser support for image formats is inconsistent. Using [PNG] is recommended.
|
||||
///
|
||||
/// [PNG]: https://en.wikipedia.org/wiki/PNG
|
||||
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder;
|
||||
}
|
||||
|
||||
impl CustomCursorExtWebSys for CustomCursor {
|
||||
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder {
|
||||
CustomCursorBuilder {
|
||||
inner: PlatformCustomCursorBuilder::Url {
|
||||
url,
|
||||
hotspot_x,
|
||||
hotspot_y,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ use android_activity::{
|
||||
AndroidApp,
|
||||
};
|
||||
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKey, NativeKeyCode};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey};
|
||||
|
||||
pub fn to_physical_keycode(keycode: Keycode) -> KeyCode {
|
||||
match keycode {
|
||||
pub fn to_physical_key(keycode: Keycode) -> PhysicalKey {
|
||||
PhysicalKey::Code(match keycode {
|
||||
Keycode::A => KeyCode::KeyA,
|
||||
Keycode::B => KeyCode::KeyB,
|
||||
Keycode::C => KeyCode::KeyC,
|
||||
@@ -155,8 +155,8 @@ pub fn to_physical_keycode(keycode: Keycode) -> KeyCode {
|
||||
Keycode::Sleep => KeyCode::Sleep, // what about SoftSleep?
|
||||
Keycode::Wakeup => KeyCode::WakeUp,
|
||||
|
||||
keycode => KeyCode::Unidentified(NativeKeyCode::Android(keycode.into())),
|
||||
}
|
||||
keycode => return PhysicalKey::Unidentified(NativeKeyCode::Android(keycode.into())),
|
||||
})
|
||||
}
|
||||
|
||||
/// Tries to map the `key_event` to a `KeyMapChar` containing a unicode character or dead key accent
|
||||
@@ -231,10 +231,10 @@ pub fn to_logical(key_char: Option<KeyMapChar>, keycode: Keycode) -> Key {
|
||||
None | Some(KeyMapChar::None) => match keycode {
|
||||
// Using `BrowserHome` instead of `GoHome` according to
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
||||
Home => Key::BrowserHome,
|
||||
Back => Key::BrowserBack,
|
||||
Call => Key::Call,
|
||||
Endcall => Key::EndCall,
|
||||
Home => Key::Named(NamedKey::BrowserHome),
|
||||
Back => Key::Named(NamedKey::BrowserBack),
|
||||
Call => Key::Named(NamedKey::Call),
|
||||
Endcall => Key::Named(NamedKey::EndCall),
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// These should be redundant because they should have already been matched
|
||||
@@ -291,81 +291,81 @@ pub fn to_logical(key_char: Option<KeyMapChar>, keycode: Keycode) -> Key {
|
||||
At => Key::Character("@".into()),
|
||||
Plus => Key::Character("+".into()),
|
||||
//-------------------------------------------------------------------------------
|
||||
DpadUp => Key::ArrowUp,
|
||||
DpadDown => Key::ArrowDown,
|
||||
DpadLeft => Key::ArrowLeft,
|
||||
DpadRight => Key::ArrowRight,
|
||||
DpadCenter => Key::Enter,
|
||||
DpadUp => Key::Named(NamedKey::ArrowUp),
|
||||
DpadDown => Key::Named(NamedKey::ArrowDown),
|
||||
DpadLeft => Key::Named(NamedKey::ArrowLeft),
|
||||
DpadRight => Key::Named(NamedKey::ArrowRight),
|
||||
DpadCenter => Key::Named(NamedKey::Enter),
|
||||
|
||||
VolumeUp => Key::AudioVolumeUp,
|
||||
VolumeDown => Key::AudioVolumeDown,
|
||||
Power => Key::Power,
|
||||
Camera => Key::Camera,
|
||||
Clear => Key::Clear,
|
||||
VolumeUp => Key::Named(NamedKey::AudioVolumeUp),
|
||||
VolumeDown => Key::Named(NamedKey::AudioVolumeDown),
|
||||
Power => Key::Named(NamedKey::Power),
|
||||
Camera => Key::Named(NamedKey::Camera),
|
||||
Clear => Key::Named(NamedKey::Clear),
|
||||
|
||||
AltLeft => Key::Alt,
|
||||
AltRight => Key::Alt,
|
||||
ShiftLeft => Key::Shift,
|
||||
ShiftRight => Key::Shift,
|
||||
Tab => Key::Tab,
|
||||
Space => Key::Space,
|
||||
Sym => Key::Symbol,
|
||||
Explorer => Key::LaunchWebBrowser,
|
||||
Envelope => Key::LaunchMail,
|
||||
Enter => Key::Enter,
|
||||
Del => Key::Backspace,
|
||||
AltLeft => Key::Named(NamedKey::Alt),
|
||||
AltRight => Key::Named(NamedKey::Alt),
|
||||
ShiftLeft => Key::Named(NamedKey::Shift),
|
||||
ShiftRight => Key::Named(NamedKey::Shift),
|
||||
Tab => Key::Named(NamedKey::Tab),
|
||||
Space => Key::Named(NamedKey::Space),
|
||||
Sym => Key::Named(NamedKey::Symbol),
|
||||
Explorer => Key::Named(NamedKey::LaunchWebBrowser),
|
||||
Envelope => Key::Named(NamedKey::LaunchMail),
|
||||
Enter => Key::Named(NamedKey::Enter),
|
||||
Del => Key::Named(NamedKey::Backspace),
|
||||
|
||||
// According to https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_NUM
|
||||
Num => Key::Alt,
|
||||
Num => Key::Named(NamedKey::Alt),
|
||||
|
||||
Headsethook => Key::HeadsetHook,
|
||||
Focus => Key::CameraFocus,
|
||||
Headsethook => Key::Named(NamedKey::HeadsetHook),
|
||||
Focus => Key::Named(NamedKey::CameraFocus),
|
||||
|
||||
Notification => Key::Notification,
|
||||
Search => Key::BrowserSearch,
|
||||
MediaPlayPause => Key::MediaPlayPause,
|
||||
MediaStop => Key::MediaStop,
|
||||
MediaNext => Key::MediaTrackNext,
|
||||
MediaPrevious => Key::MediaTrackPrevious,
|
||||
MediaRewind => Key::MediaRewind,
|
||||
MediaFastForward => Key::MediaFastForward,
|
||||
Mute => Key::MicrophoneVolumeMute,
|
||||
PageUp => Key::PageUp,
|
||||
PageDown => Key::PageDown,
|
||||
Notification => Key::Named(NamedKey::Notification),
|
||||
Search => Key::Named(NamedKey::BrowserSearch),
|
||||
MediaPlayPause => Key::Named(NamedKey::MediaPlayPause),
|
||||
MediaStop => Key::Named(NamedKey::MediaStop),
|
||||
MediaNext => Key::Named(NamedKey::MediaTrackNext),
|
||||
MediaPrevious => Key::Named(NamedKey::MediaTrackPrevious),
|
||||
MediaRewind => Key::Named(NamedKey::MediaRewind),
|
||||
MediaFastForward => Key::Named(NamedKey::MediaFastForward),
|
||||
Mute => Key::Named(NamedKey::MicrophoneVolumeMute),
|
||||
PageUp => Key::Named(NamedKey::PageUp),
|
||||
PageDown => Key::Named(NamedKey::PageDown),
|
||||
|
||||
Escape => Key::Escape,
|
||||
ForwardDel => Key::Delete,
|
||||
CtrlLeft => Key::Control,
|
||||
CtrlRight => Key::Control,
|
||||
CapsLock => Key::CapsLock,
|
||||
ScrollLock => Key::ScrollLock,
|
||||
MetaLeft => Key::Super,
|
||||
MetaRight => Key::Super,
|
||||
Function => Key::Fn,
|
||||
Sysrq => Key::PrintScreen,
|
||||
Break => Key::Pause,
|
||||
MoveHome => Key::Home,
|
||||
MoveEnd => Key::End,
|
||||
Insert => Key::Insert,
|
||||
Forward => Key::BrowserForward,
|
||||
MediaPlay => Key::MediaPlay,
|
||||
MediaPause => Key::MediaPause,
|
||||
MediaClose => Key::MediaClose,
|
||||
MediaEject => Key::Eject,
|
||||
MediaRecord => Key::MediaRecord,
|
||||
F1 => Key::F1,
|
||||
F2 => Key::F2,
|
||||
F3 => Key::F3,
|
||||
F4 => Key::F4,
|
||||
F5 => Key::F5,
|
||||
F6 => Key::F6,
|
||||
F7 => Key::F7,
|
||||
F8 => Key::F8,
|
||||
F9 => Key::F9,
|
||||
F10 => Key::F10,
|
||||
F11 => Key::F11,
|
||||
F12 => Key::F12,
|
||||
NumLock => Key::NumLock,
|
||||
Escape => Key::Named(NamedKey::Escape),
|
||||
ForwardDel => Key::Named(NamedKey::Delete),
|
||||
CtrlLeft => Key::Named(NamedKey::Control),
|
||||
CtrlRight => Key::Named(NamedKey::Control),
|
||||
CapsLock => Key::Named(NamedKey::CapsLock),
|
||||
ScrollLock => Key::Named(NamedKey::ScrollLock),
|
||||
MetaLeft => Key::Named(NamedKey::Super),
|
||||
MetaRight => Key::Named(NamedKey::Super),
|
||||
Function => Key::Named(NamedKey::Fn),
|
||||
Sysrq => Key::Named(NamedKey::PrintScreen),
|
||||
Break => Key::Named(NamedKey::Pause),
|
||||
MoveHome => Key::Named(NamedKey::Home),
|
||||
MoveEnd => Key::Named(NamedKey::End),
|
||||
Insert => Key::Named(NamedKey::Insert),
|
||||
Forward => Key::Named(NamedKey::BrowserForward),
|
||||
MediaPlay => Key::Named(NamedKey::MediaPlay),
|
||||
MediaPause => Key::Named(NamedKey::MediaPause),
|
||||
MediaClose => Key::Named(NamedKey::MediaClose),
|
||||
MediaEject => Key::Named(NamedKey::Eject),
|
||||
MediaRecord => Key::Named(NamedKey::MediaRecord),
|
||||
F1 => Key::Named(NamedKey::F1),
|
||||
F2 => Key::Named(NamedKey::F2),
|
||||
F3 => Key::Named(NamedKey::F3),
|
||||
F4 => Key::Named(NamedKey::F4),
|
||||
F5 => Key::Named(NamedKey::F5),
|
||||
F6 => Key::Named(NamedKey::F6),
|
||||
F7 => Key::Named(NamedKey::F7),
|
||||
F8 => Key::Named(NamedKey::F8),
|
||||
F9 => Key::Named(NamedKey::F9),
|
||||
F10 => Key::Named(NamedKey::F10),
|
||||
F11 => Key::Named(NamedKey::F11),
|
||||
F12 => Key::Named(NamedKey::F12),
|
||||
NumLock => Key::Named(NamedKey::NumLock),
|
||||
Numpad0 => Key::Character("0".into()),
|
||||
Numpad1 => Key::Character("1".into()),
|
||||
Numpad2 => Key::Character("2".into()),
|
||||
@@ -382,97 +382,97 @@ pub fn to_logical(key_char: Option<KeyMapChar>, keycode: Keycode) -> Key {
|
||||
NumpadAdd => Key::Character("+".into()),
|
||||
NumpadDot => Key::Character(".".into()),
|
||||
NumpadComma => Key::Character(",".into()),
|
||||
NumpadEnter => Key::Enter,
|
||||
NumpadEnter => Key::Named(NamedKey::Enter),
|
||||
NumpadEquals => Key::Character("=".into()),
|
||||
NumpadLeftParen => Key::Character("(".into()),
|
||||
NumpadRightParen => Key::Character(")".into()),
|
||||
|
||||
VolumeMute => Key::AudioVolumeMute,
|
||||
Info => Key::Info,
|
||||
ChannelUp => Key::ChannelUp,
|
||||
ChannelDown => Key::ChannelDown,
|
||||
ZoomIn => Key::ZoomIn,
|
||||
ZoomOut => Key::ZoomOut,
|
||||
Tv => Key::TV,
|
||||
Guide => Key::Guide,
|
||||
Dvr => Key::DVR,
|
||||
Bookmark => Key::BrowserFavorites,
|
||||
Captions => Key::ClosedCaptionToggle,
|
||||
Settings => Key::Settings,
|
||||
TvPower => Key::TVPower,
|
||||
TvInput => Key::TVInput,
|
||||
StbPower => Key::STBPower,
|
||||
StbInput => Key::STBInput,
|
||||
AvrPower => Key::AVRPower,
|
||||
AvrInput => Key::AVRInput,
|
||||
ProgRed => Key::ColorF0Red,
|
||||
ProgGreen => Key::ColorF1Green,
|
||||
ProgYellow => Key::ColorF2Yellow,
|
||||
ProgBlue => Key::ColorF3Blue,
|
||||
AppSwitch => Key::AppSwitch,
|
||||
LanguageSwitch => Key::GroupNext,
|
||||
MannerMode => Key::MannerMode,
|
||||
Keycode3dMode => Key::TV3DMode,
|
||||
Contacts => Key::LaunchContacts,
|
||||
Calendar => Key::LaunchCalendar,
|
||||
Music => Key::LaunchMusicPlayer,
|
||||
Calculator => Key::LaunchApplication2,
|
||||
ZenkakuHankaku => Key::ZenkakuHankaku,
|
||||
Eisu => Key::Eisu,
|
||||
Muhenkan => Key::NonConvert,
|
||||
Henkan => Key::Convert,
|
||||
KatakanaHiragana => Key::HiraganaKatakana,
|
||||
Kana => Key::KanjiMode,
|
||||
BrightnessDown => Key::BrightnessDown,
|
||||
BrightnessUp => Key::BrightnessUp,
|
||||
MediaAudioTrack => Key::MediaAudioTrack,
|
||||
Sleep => Key::Standby,
|
||||
Wakeup => Key::WakeUp,
|
||||
Pairing => Key::Pairing,
|
||||
MediaTopMenu => Key::MediaTopMenu,
|
||||
LastChannel => Key::MediaLast,
|
||||
TvDataService => Key::TVDataService,
|
||||
VoiceAssist => Key::VoiceDial,
|
||||
TvRadioService => Key::TVRadioService,
|
||||
TvTeletext => Key::Teletext,
|
||||
TvNumberEntry => Key::TVNumberEntry,
|
||||
TvTerrestrialAnalog => Key::TVTerrestrialAnalog,
|
||||
TvTerrestrialDigital => Key::TVTerrestrialDigital,
|
||||
TvSatellite => Key::TVSatellite,
|
||||
TvSatelliteBs => Key::TVSatelliteBS,
|
||||
TvSatelliteCs => Key::TVSatelliteCS,
|
||||
TvSatelliteService => Key::TVSatelliteToggle,
|
||||
TvNetwork => Key::TVNetwork,
|
||||
TvAntennaCable => Key::TVAntennaCable,
|
||||
TvInputHdmi1 => Key::TVInputHDMI1,
|
||||
TvInputHdmi2 => Key::TVInputHDMI2,
|
||||
TvInputHdmi3 => Key::TVInputHDMI3,
|
||||
TvInputHdmi4 => Key::TVInputHDMI4,
|
||||
TvInputComposite1 => Key::TVInputComposite1,
|
||||
TvInputComposite2 => Key::TVInputComposite2,
|
||||
TvInputComponent1 => Key::TVInputComponent1,
|
||||
TvInputComponent2 => Key::TVInputComponent2,
|
||||
TvInputVga1 => Key::TVInputVGA1,
|
||||
TvAudioDescription => Key::TVAudioDescription,
|
||||
TvAudioDescriptionMixUp => Key::TVAudioDescriptionMixUp,
|
||||
TvAudioDescriptionMixDown => Key::TVAudioDescriptionMixDown,
|
||||
TvZoomMode => Key::ZoomToggle,
|
||||
TvContentsMenu => Key::TVContentsMenu,
|
||||
TvMediaContextMenu => Key::TVMediaContext,
|
||||
TvTimerProgramming => Key::TVTimer,
|
||||
Help => Key::Help,
|
||||
NavigatePrevious => Key::NavigatePrevious,
|
||||
NavigateNext => Key::NavigateNext,
|
||||
NavigateIn => Key::NavigateIn,
|
||||
NavigateOut => Key::NavigateOut,
|
||||
MediaSkipForward => Key::MediaSkipForward,
|
||||
MediaSkipBackward => Key::MediaSkipBackward,
|
||||
MediaStepForward => Key::MediaStepForward,
|
||||
MediaStepBackward => Key::MediaStepBackward,
|
||||
Cut => Key::Cut,
|
||||
Copy => Key::Copy,
|
||||
Paste => Key::Paste,
|
||||
Refresh => Key::BrowserRefresh,
|
||||
VolumeMute => Key::Named(NamedKey::AudioVolumeMute),
|
||||
Info => Key::Named(NamedKey::Info),
|
||||
ChannelUp => Key::Named(NamedKey::ChannelUp),
|
||||
ChannelDown => Key::Named(NamedKey::ChannelDown),
|
||||
ZoomIn => Key::Named(NamedKey::ZoomIn),
|
||||
ZoomOut => Key::Named(NamedKey::ZoomOut),
|
||||
Tv => Key::Named(NamedKey::TV),
|
||||
Guide => Key::Named(NamedKey::Guide),
|
||||
Dvr => Key::Named(NamedKey::DVR),
|
||||
Bookmark => Key::Named(NamedKey::BrowserFavorites),
|
||||
Captions => Key::Named(NamedKey::ClosedCaptionToggle),
|
||||
Settings => Key::Named(NamedKey::Settings),
|
||||
TvPower => Key::Named(NamedKey::TVPower),
|
||||
TvInput => Key::Named(NamedKey::TVInput),
|
||||
StbPower => Key::Named(NamedKey::STBPower),
|
||||
StbInput => Key::Named(NamedKey::STBInput),
|
||||
AvrPower => Key::Named(NamedKey::AVRPower),
|
||||
AvrInput => Key::Named(NamedKey::AVRInput),
|
||||
ProgRed => Key::Named(NamedKey::ColorF0Red),
|
||||
ProgGreen => Key::Named(NamedKey::ColorF1Green),
|
||||
ProgYellow => Key::Named(NamedKey::ColorF2Yellow),
|
||||
ProgBlue => Key::Named(NamedKey::ColorF3Blue),
|
||||
AppSwitch => Key::Named(NamedKey::AppSwitch),
|
||||
LanguageSwitch => Key::Named(NamedKey::GroupNext),
|
||||
MannerMode => Key::Named(NamedKey::MannerMode),
|
||||
Keycode3dMode => Key::Named(NamedKey::TV3DMode),
|
||||
Contacts => Key::Named(NamedKey::LaunchContacts),
|
||||
Calendar => Key::Named(NamedKey::LaunchCalendar),
|
||||
Music => Key::Named(NamedKey::LaunchMusicPlayer),
|
||||
Calculator => Key::Named(NamedKey::LaunchApplication2),
|
||||
ZenkakuHankaku => Key::Named(NamedKey::ZenkakuHankaku),
|
||||
Eisu => Key::Named(NamedKey::Eisu),
|
||||
Muhenkan => Key::Named(NamedKey::NonConvert),
|
||||
Henkan => Key::Named(NamedKey::Convert),
|
||||
KatakanaHiragana => Key::Named(NamedKey::HiraganaKatakana),
|
||||
Kana => Key::Named(NamedKey::KanjiMode),
|
||||
BrightnessDown => Key::Named(NamedKey::BrightnessDown),
|
||||
BrightnessUp => Key::Named(NamedKey::BrightnessUp),
|
||||
MediaAudioTrack => Key::Named(NamedKey::MediaAudioTrack),
|
||||
Sleep => Key::Named(NamedKey::Standby),
|
||||
Wakeup => Key::Named(NamedKey::WakeUp),
|
||||
Pairing => Key::Named(NamedKey::Pairing),
|
||||
MediaTopMenu => Key::Named(NamedKey::MediaTopMenu),
|
||||
LastChannel => Key::Named(NamedKey::MediaLast),
|
||||
TvDataService => Key::Named(NamedKey::TVDataService),
|
||||
VoiceAssist => Key::Named(NamedKey::VoiceDial),
|
||||
TvRadioService => Key::Named(NamedKey::TVRadioService),
|
||||
TvTeletext => Key::Named(NamedKey::Teletext),
|
||||
TvNumberEntry => Key::Named(NamedKey::TVNumberEntry),
|
||||
TvTerrestrialAnalog => Key::Named(NamedKey::TVTerrestrialAnalog),
|
||||
TvTerrestrialDigital => Key::Named(NamedKey::TVTerrestrialDigital),
|
||||
TvSatellite => Key::Named(NamedKey::TVSatellite),
|
||||
TvSatelliteBs => Key::Named(NamedKey::TVSatelliteBS),
|
||||
TvSatelliteCs => Key::Named(NamedKey::TVSatelliteCS),
|
||||
TvSatelliteService => Key::Named(NamedKey::TVSatelliteToggle),
|
||||
TvNetwork => Key::Named(NamedKey::TVNetwork),
|
||||
TvAntennaCable => Key::Named(NamedKey::TVAntennaCable),
|
||||
TvInputHdmi1 => Key::Named(NamedKey::TVInputHDMI1),
|
||||
TvInputHdmi2 => Key::Named(NamedKey::TVInputHDMI2),
|
||||
TvInputHdmi3 => Key::Named(NamedKey::TVInputHDMI3),
|
||||
TvInputHdmi4 => Key::Named(NamedKey::TVInputHDMI4),
|
||||
TvInputComposite1 => Key::Named(NamedKey::TVInputComposite1),
|
||||
TvInputComposite2 => Key::Named(NamedKey::TVInputComposite2),
|
||||
TvInputComponent1 => Key::Named(NamedKey::TVInputComponent1),
|
||||
TvInputComponent2 => Key::Named(NamedKey::TVInputComponent2),
|
||||
TvInputVga1 => Key::Named(NamedKey::TVInputVGA1),
|
||||
TvAudioDescription => Key::Named(NamedKey::TVAudioDescription),
|
||||
TvAudioDescriptionMixUp => Key::Named(NamedKey::TVAudioDescriptionMixUp),
|
||||
TvAudioDescriptionMixDown => Key::Named(NamedKey::TVAudioDescriptionMixDown),
|
||||
TvZoomMode => Key::Named(NamedKey::ZoomToggle),
|
||||
TvContentsMenu => Key::Named(NamedKey::TVContentsMenu),
|
||||
TvMediaContextMenu => Key::Named(NamedKey::TVMediaContext),
|
||||
TvTimerProgramming => Key::Named(NamedKey::TVTimer),
|
||||
Help => Key::Named(NamedKey::Help),
|
||||
NavigatePrevious => Key::Named(NamedKey::NavigatePrevious),
|
||||
NavigateNext => Key::Named(NamedKey::NavigateNext),
|
||||
NavigateIn => Key::Named(NamedKey::NavigateIn),
|
||||
NavigateOut => Key::Named(NamedKey::NavigateOut),
|
||||
MediaSkipForward => Key::Named(NamedKey::MediaSkipForward),
|
||||
MediaSkipBackward => Key::Named(NamedKey::MediaSkipBackward),
|
||||
MediaStepForward => Key::Named(NamedKey::MediaStepForward),
|
||||
MediaStepBackward => Key::Named(NamedKey::MediaStepBackward),
|
||||
Cut => Key::Named(NamedKey::Cut),
|
||||
Copy => Key::Named(NamedKey::Copy),
|
||||
Paste => Key::Named(NamedKey::Paste),
|
||||
Refresh => Key::Named(NamedKey::BrowserRefresh),
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Keycodes that don't have a logical Key mapping
|
||||
@@ -555,6 +555,10 @@ pub fn to_logical(key_char: Option<KeyMapChar>, keycode: Keycode) -> Key {
|
||||
ThumbsUp => Key::Unidentified(native),
|
||||
ThumbsDown => Key::Unidentified(native),
|
||||
ProfileSwitch => Key::Unidentified(native),
|
||||
|
||||
// It's always possible that new versions of Android could introduce
|
||||
// key codes we can't know about at compile time.
|
||||
_ => Key::Unidentified(native),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
device_id: event::DeviceId(DeviceId(key.device_id())),
|
||||
event: event::KeyEvent {
|
||||
state,
|
||||
physical_key: keycodes::to_physical_keycode(keycode),
|
||||
physical_key: keycodes::to_physical_key(keycode),
|
||||
logical_key: keycodes::to_logical(key_char, keycode),
|
||||
location: keycodes::to_location(keycode),
|
||||
repeat: key.repeat_count() > 0,
|
||||
@@ -751,6 +751,18 @@ impl DeviceId {
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct OwnedWindowHandle {}
|
||||
|
||||
impl OwnedWindowHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
|
||||
// Parent windows are currently unsupported, though owned window
|
||||
// handles would be implementable.
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Window {
|
||||
app: AndroidApp,
|
||||
redraw_requester: RedrawRequester,
|
||||
@@ -906,6 +918,8 @@ impl Window {
|
||||
|
||||
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
|
||||
|
||||
pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {}
|
||||
|
||||
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(
|
||||
error::NotSupportedError::new(),
|
||||
@@ -946,10 +960,10 @@ impl Window {
|
||||
|
||||
#[cfg(feature = "rwh_04")]
|
||||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||
use rwh_04::HasRawWindowHandle;
|
||||
|
||||
if let Some(native_window) = self.app.native_window().as_ref() {
|
||||
let mut handle = rwh_04::AndroidNdkHandle::empty();
|
||||
handle.a_native_window = native_window.ptr().as_ptr() as *mut _;
|
||||
rwh_04::RawWindowHandle::AndroidNdk(handle)
|
||||
native_window.raw_window_handle()
|
||||
} else {
|
||||
panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
|
||||
}
|
||||
@@ -972,10 +986,13 @@ impl Window {
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
// Allow the usage of HasRawWindowHandle inside this function
|
||||
#[allow(deprecated)]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
use rwh_06::HasRawWindowHandle;
|
||||
|
||||
if let Some(native_window) = self.app.native_window().as_ref() {
|
||||
let handle = rwh_06::AndroidNdkWindowHandle::new(native_window.ptr().cast());
|
||||
Ok(rwh_06::RawWindowHandle::AndroidNdk(handle))
|
||||
native_window.raw_window_handle()
|
||||
} else {
|
||||
log::error!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
|
||||
Err(rwh_06::HandleError::Unavailable)
|
||||
@@ -1028,6 +1045,8 @@ impl Display for OsError {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
|
||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
|
||||
@@ -200,6 +200,10 @@ impl AppState {
|
||||
)
|
||||
}
|
||||
|
||||
fn has_terminated(&self) -> bool {
|
||||
matches!(self.state(), AppStateImpl::Terminated)
|
||||
}
|
||||
|
||||
fn will_launch_transition(&mut self, queued_event_handler: Box<dyn EventHandler>) {
|
||||
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::NotLaunched {
|
||||
@@ -243,7 +247,7 @@ impl AppState {
|
||||
fn wakeup_transition(&mut self) -> Option<EventWrapper> {
|
||||
// before `AppState::did_finish_launching` is called, pretend there is no running
|
||||
// event loop.
|
||||
if !self.has_launched() {
|
||||
if !self.has_launched() || self.has_terminated() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -390,7 +394,7 @@ impl AppState {
|
||||
}
|
||||
|
||||
fn events_cleared_transition(&mut self) {
|
||||
if !self.has_launched() {
|
||||
if !self.has_launched() || self.has_terminated() {
|
||||
return;
|
||||
}
|
||||
let (waiting_event_handler, old) = match self.take_state() {
|
||||
@@ -586,6 +590,10 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
events: I,
|
||||
) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
if this.has_terminated() {
|
||||
return;
|
||||
}
|
||||
|
||||
let (mut event_handler, active_control_flow, processing_redraws) =
|
||||
match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
||||
@@ -737,7 +745,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||
|
||||
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
if !this.has_launched() {
|
||||
if !this.has_launched() || this.has_terminated() {
|
||||
return;
|
||||
}
|
||||
match this.state_mut() {
|
||||
|
||||
@@ -289,7 +289,7 @@ fn setup_control_flow_observers() {
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm),
|
||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||
kCFRunLoopExit => {} // may happen when running on macOS
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -304,7 +304,7 @@ fn setup_control_flow_observers() {
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm),
|
||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||
kCFRunLoopExit => {} // may happen when running on macOS
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,10 +73,12 @@ pub(crate) use self::{
|
||||
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
|
||||
},
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
|
||||
window::{OwnedWindowHandle, PlatformSpecificWindowBuilderAttributes, Window, WindowId},
|
||||
};
|
||||
|
||||
use self::uikit::UIScreen;
|
||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
|
||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::{
|
||||
use icrate::Foundation::{MainThreadBound, MainThreadMarker, NSInteger};
|
||||
use objc2::mutability::IsRetainable;
|
||||
use objc2::rc::Id;
|
||||
use objc2::Message;
|
||||
|
||||
use super::uikit::{UIScreen, UIScreenMode};
|
||||
use crate::{
|
||||
@@ -20,16 +21,15 @@ use crate::{
|
||||
#[derive(Debug)]
|
||||
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Id<T>>);
|
||||
|
||||
impl<T: IsRetainable> Clone for MainThreadBoundDelegateImpls<T> {
|
||||
impl<T: IsRetainable + Message> Clone for MainThreadBoundDelegateImpls<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(
|
||||
self.0
|
||||
.get_on_main(|inner, mtm| MainThreadBound::new(Id::clone(inner), mtm)),
|
||||
)
|
||||
Self(MainThreadMarker::run_on_main(|mtm| {
|
||||
MainThreadBound::new(Id::clone(self.0.get(mtm)), mtm)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IsRetainable> hash::Hash for MainThreadBoundDelegateImpls<T> {
|
||||
impl<T: IsRetainable + Message> hash::Hash for MainThreadBoundDelegateImpls<T> {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
// SAFETY: Marker only used to get the pointer
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
@@ -37,7 +37,7 @@ impl<T: IsRetainable> hash::Hash for MainThreadBoundDelegateImpls<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IsRetainable> PartialEq for MainThreadBoundDelegateImpls<T> {
|
||||
impl<T: IsRetainable + Message> PartialEq for MainThreadBoundDelegateImpls<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// SAFETY: Marker only used to get the pointer
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
@@ -45,7 +45,7 @@ impl<T: IsRetainable> PartialEq for MainThreadBoundDelegateImpls<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IsRetainable> Eq for MainThreadBoundDelegateImpls<T> {}
|
||||
impl<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub struct VideoMode {
|
||||
@@ -100,11 +100,9 @@ pub struct MonitorHandle {
|
||||
|
||||
impl Clone for MonitorHandle {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
ui_screen: self
|
||||
.ui_screen
|
||||
.get_on_main(|inner, mtm| MainThreadBound::new(inner.clone(), mtm)),
|
||||
}
|
||||
MainThreadMarker::run_on_main(|mtm| Self {
|
||||
ui_screen: MainThreadBound::new(self.ui_screen.get(mtm).clone(), mtm),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,16 +166,16 @@ impl MonitorHandle {
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<String> {
|
||||
self.ui_screen.get_on_main(|ui_screen, mtm| {
|
||||
MainThreadMarker::run_on_main(|mtm| {
|
||||
let main = UIScreen::main(mtm);
|
||||
if *ui_screen == main {
|
||||
if *self.ui_screen(mtm) == main {
|
||||
Some("Primary".to_string())
|
||||
} else if *ui_screen == main.mirroredScreen() {
|
||||
} else if *self.ui_screen(mtm) == main.mirroredScreen() {
|
||||
Some("Mirrored".to_string())
|
||||
} else {
|
||||
UIScreen::screens(mtm)
|
||||
.iter()
|
||||
.position(|rhs| rhs == &**ui_screen)
|
||||
.position(|rhs| rhs == &**self.ui_screen(mtm))
|
||||
.map(|idx| idx.to_string())
|
||||
}
|
||||
})
|
||||
@@ -186,31 +184,32 @@ impl MonitorHandle {
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
let bounds = self
|
||||
.ui_screen
|
||||
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
|
||||
.get_on_main(|ui_screen| ui_screen.nativeBounds());
|
||||
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
|
||||
}
|
||||
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
let bounds = self
|
||||
.ui_screen
|
||||
.get_on_main(|ui_screen, _| ui_screen.nativeBounds());
|
||||
.get_on_main(|ui_screen| ui_screen.nativeBounds());
|
||||
(bounds.origin.x as f64, bounds.origin.y as f64).into()
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.ui_screen
|
||||
.get_on_main(|ui_screen, _| ui_screen.nativeScale()) as f64
|
||||
.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64
|
||||
}
|
||||
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
Some(
|
||||
self.ui_screen
|
||||
.get_on_main(|ui_screen, _| refresh_rate_millihertz(ui_screen)),
|
||||
.get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
||||
self.ui_screen.get_on_main(|ui_screen, mtm| {
|
||||
MainThreadMarker::run_on_main(|mtm| {
|
||||
let ui_screen = self.ui_screen(mtm);
|
||||
// Use Ord impl of RootVideoMode
|
||||
|
||||
let modes: BTreeSet<_> = ui_screen
|
||||
@@ -230,8 +229,12 @@ impl MonitorHandle {
|
||||
}
|
||||
|
||||
pub fn preferred_video_mode(&self) -> VideoMode {
|
||||
self.ui_screen.get_on_main(|ui_screen, mtm| {
|
||||
VideoMode::new(ui_screen.clone(), ui_screen.preferredMode().unwrap(), mtm)
|
||||
MainThreadMarker::run_on_main(|mtm| {
|
||||
VideoMode::new(
|
||||
self.ui_screen(mtm).clone(),
|
||||
self.ui_screen(mtm).preferredMode().unwrap(),
|
||||
mtm,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ mod event;
|
||||
mod responder;
|
||||
mod screen;
|
||||
mod screen_mode;
|
||||
mod status_bar_style;
|
||||
mod touch;
|
||||
mod trait_collection;
|
||||
mod view;
|
||||
@@ -25,6 +26,7 @@ pub(crate) use self::event::UIEvent;
|
||||
pub(crate) use self::responder::UIResponder;
|
||||
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};
|
||||
pub(crate) use self::screen_mode::UIScreenMode;
|
||||
pub(crate) use self::status_bar_style::UIStatusBarStyle;
|
||||
pub(crate) use self::touch::{UITouch, UITouchPhase, UITouchType};
|
||||
pub(crate) use self::trait_collection::{UIForceTouchCapability, UITraitCollection};
|
||||
#[allow(unused_imports)]
|
||||
|
||||
27
src/platform_impl/ios/uikit/status_bar_style.rs
Normal file
27
src/platform_impl/ios/uikit/status_bar_style.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use crate::platform::ios::StatusBarStyle;
|
||||
use icrate::Foundation::NSInteger;
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
pub enum UIStatusBarStyle {
|
||||
#[default]
|
||||
Default = 0,
|
||||
LightContent = 1,
|
||||
DarkContent = 3,
|
||||
}
|
||||
|
||||
impl From<StatusBarStyle> for UIStatusBarStyle {
|
||||
fn from(value: StatusBarStyle) -> Self {
|
||||
match value {
|
||||
StatusBarStyle::Default => Self::Default,
|
||||
StatusBarStyle::LightContent => Self::LightContent,
|
||||
StatusBarStyle::DarkContent => Self::DarkContent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for UIStatusBarStyle {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
use std::cell::Cell;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet};
|
||||
use objc2::declare::{Ivar, IvarDrop};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyClass;
|
||||
use objc2::{declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType};
|
||||
use objc2::{
|
||||
declare_class, extern_methods, msg_send, msg_send_id, mutability, ClassType, DeclaredClass,
|
||||
};
|
||||
|
||||
use super::app_state::{self, EventWrapper};
|
||||
use super::uikit::{
|
||||
UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIInterfaceOrientationMask,
|
||||
UIResponder, UITouch, UITouchPhase, UITouchType, UITraitCollection, UIView, UIViewController,
|
||||
UIWindow,
|
||||
UIResponder, UIStatusBarStyle, UITouch, UITouchPhase, UITouchType, UITraitCollection, UIView,
|
||||
UIViewController, UIWindow,
|
||||
};
|
||||
use super::window::WindowId;
|
||||
use crate::{
|
||||
@@ -37,6 +37,8 @@ declare_class!(
|
||||
const NAME: &'static str = "WinitUIView";
|
||||
}
|
||||
|
||||
impl DeclaredClass for WinitView {}
|
||||
|
||||
unsafe impl WinitView {
|
||||
#[method(drawRect:)]
|
||||
fn draw_rect(&self, rect: CGRect) {
|
||||
@@ -267,17 +269,14 @@ impl WinitView {
|
||||
|
||||
pub struct ViewControllerState {
|
||||
prefers_status_bar_hidden: Cell<bool>,
|
||||
preferred_status_bar_style: Cell<UIStatusBarStyle>,
|
||||
prefers_home_indicator_auto_hidden: Cell<bool>,
|
||||
supported_orientations: Cell<UIInterfaceOrientationMask>,
|
||||
preferred_screen_edges_deferring_system_gestures: Cell<UIRectEdge>,
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
pub(crate) struct WinitViewController {
|
||||
state: IvarDrop<Box<ViewControllerState>, "_state">,
|
||||
}
|
||||
|
||||
mod view_controller_ivars;
|
||||
pub(crate) struct WinitViewController;
|
||||
|
||||
unsafe impl ClassType for WinitViewController {
|
||||
#[inherits(UIResponder, NSObject)]
|
||||
@@ -286,27 +285,8 @@ declare_class!(
|
||||
const NAME: &'static str = "WinitUIViewController";
|
||||
}
|
||||
|
||||
unsafe impl WinitViewController {
|
||||
#[method(init)]
|
||||
unsafe fn init(this: *mut Self) -> Option<NonNull<Self>> {
|
||||
let this: Option<&mut Self> = msg_send![super(this), init];
|
||||
this.map(|this| {
|
||||
// These are set in WinitViewController::new, it's just to set them
|
||||
// to _something_.
|
||||
Ivar::write(
|
||||
&mut this.state,
|
||||
Box::new(ViewControllerState {
|
||||
prefers_status_bar_hidden: Cell::new(false),
|
||||
prefers_home_indicator_auto_hidden: Cell::new(false),
|
||||
supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
|
||||
preferred_screen_edges_deferring_system_gestures: Cell::new(
|
||||
UIRectEdge::NONE,
|
||||
),
|
||||
}),
|
||||
);
|
||||
NonNull::from(this)
|
||||
})
|
||||
}
|
||||
impl DeclaredClass for WinitViewController {
|
||||
type Ivars = ViewControllerState;
|
||||
}
|
||||
|
||||
unsafe impl WinitViewController {
|
||||
@@ -317,22 +297,27 @@ declare_class!(
|
||||
|
||||
#[method(prefersStatusBarHidden)]
|
||||
fn prefers_status_bar_hidden(&self) -> bool {
|
||||
self.state.prefers_status_bar_hidden.get()
|
||||
self.ivars().prefers_status_bar_hidden.get()
|
||||
}
|
||||
|
||||
#[method(preferredStatusBarStyle)]
|
||||
fn preferred_status_bar_style(&self) -> UIStatusBarStyle {
|
||||
self.ivars().preferred_status_bar_style.get()
|
||||
}
|
||||
|
||||
#[method(prefersHomeIndicatorAutoHidden)]
|
||||
fn prefers_home_indicator_auto_hidden(&self) -> bool {
|
||||
self.state.prefers_home_indicator_auto_hidden.get()
|
||||
self.ivars().prefers_home_indicator_auto_hidden.get()
|
||||
}
|
||||
|
||||
#[method(supportedInterfaceOrientations)]
|
||||
fn supported_orientations(&self) -> UIInterfaceOrientationMask {
|
||||
self.state.supported_orientations.get()
|
||||
self.ivars().supported_orientations.get()
|
||||
}
|
||||
|
||||
#[method(preferredScreenEdgesDeferringSystemGestures)]
|
||||
fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
|
||||
self.state
|
||||
self.ivars()
|
||||
.preferred_screen_edges_deferring_system_gestures
|
||||
.get()
|
||||
}
|
||||
@@ -341,12 +326,17 @@ declare_class!(
|
||||
|
||||
impl WinitViewController {
|
||||
pub(crate) fn set_prefers_status_bar_hidden(&self, val: bool) {
|
||||
self.state.prefers_status_bar_hidden.set(val);
|
||||
self.ivars().prefers_status_bar_hidden.set(val);
|
||||
self.setNeedsStatusBarAppearanceUpdate();
|
||||
}
|
||||
|
||||
pub(crate) fn set_preferred_status_bar_style(&self, val: UIStatusBarStyle) {
|
||||
self.ivars().preferred_status_bar_style.set(val);
|
||||
self.setNeedsStatusBarAppearanceUpdate();
|
||||
}
|
||||
|
||||
pub(crate) fn set_prefers_home_indicator_auto_hidden(&self, val: bool) {
|
||||
self.state.prefers_home_indicator_auto_hidden.set(val);
|
||||
self.ivars().prefers_home_indicator_auto_hidden.set(val);
|
||||
let os_capabilities = app_state::os_capabilities();
|
||||
if os_capabilities.home_indicator_hidden {
|
||||
self.setNeedsUpdateOfHomeIndicatorAutoHidden();
|
||||
@@ -356,7 +346,7 @@ impl WinitViewController {
|
||||
}
|
||||
|
||||
pub(crate) fn set_preferred_screen_edges_deferring_system_gestures(&self, val: UIRectEdge) {
|
||||
self.state
|
||||
self.ivars()
|
||||
.preferred_screen_edges_deferring_system_gestures
|
||||
.set(val);
|
||||
let os_capabilities = app_state::os_capabilities();
|
||||
@@ -389,7 +379,7 @@ impl WinitViewController {
|
||||
| UIInterfaceOrientationMask::PortraitUpsideDown
|
||||
}
|
||||
};
|
||||
self.state.supported_orientations.set(mask);
|
||||
self.ivars().supported_orientations.set(mask);
|
||||
UIViewController::attemptRotationToDeviceOrientation();
|
||||
}
|
||||
|
||||
@@ -399,10 +389,20 @@ impl WinitViewController {
|
||||
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
||||
view: &UIView,
|
||||
) -> Id<Self> {
|
||||
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), init] };
|
||||
// These are set properly below, we just to set them to something in the meantime.
|
||||
let this = Self::alloc().set_ivars(ViewControllerState {
|
||||
prefers_status_bar_hidden: Cell::new(false),
|
||||
preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default),
|
||||
prefers_home_indicator_auto_hidden: Cell::new(false),
|
||||
supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
|
||||
preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::NONE),
|
||||
});
|
||||
let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
|
||||
|
||||
this.set_prefers_status_bar_hidden(platform_attributes.prefers_status_bar_hidden);
|
||||
|
||||
this.set_preferred_status_bar_style(platform_attributes.preferred_status_bar_style.into());
|
||||
|
||||
this.set_supported_interface_orientations(mtm, platform_attributes.valid_orientations);
|
||||
|
||||
this.set_prefers_home_indicator_auto_hidden(
|
||||
@@ -432,6 +432,8 @@ declare_class!(
|
||||
const NAME: &'static str = "WinitUIWindow";
|
||||
}
|
||||
|
||||
impl DeclaredClass for WinitUIWindow {}
|
||||
|
||||
unsafe impl WinitUIWindow {
|
||||
#[method(becomeKeyWindow)]
|
||||
fn become_key_window(&self) {
|
||||
@@ -473,7 +475,7 @@ impl WinitUIWindow {
|
||||
|
||||
this.setRootViewController(Some(view_controller));
|
||||
|
||||
match window_attributes.fullscreen.clone().map(Into::into) {
|
||||
match window_attributes.fullscreen.0.clone().map(Into::into) {
|
||||
Some(Fullscreen::Exclusive(ref video_mode)) => {
|
||||
let monitor = video_mode.monitor();
|
||||
let screen = monitor.ui_screen(mtm);
|
||||
@@ -504,6 +506,8 @@ declare_class!(
|
||||
const NAME: &'static str = "WinitApplicationDelegate";
|
||||
}
|
||||
|
||||
impl DeclaredClass for WinitApplicationDelegate {}
|
||||
|
||||
// UIApplicationDelegate protocol
|
||||
unsafe impl WinitApplicationDelegate {
|
||||
#[method(application:didFinishLaunchingWithOptions:)]
|
||||
|
||||
@@ -15,9 +15,9 @@ use crate::{
|
||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
event::{Event, WindowEvent},
|
||||
icon::Icon,
|
||||
platform::ios::{ScreenEdge, ValidOrientations},
|
||||
platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations},
|
||||
platform_impl::platform::{
|
||||
app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
|
||||
app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle, PlatformCustomCursor,
|
||||
},
|
||||
window::{
|
||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
@@ -25,6 +25,19 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct OwnedWindowHandle {}
|
||||
|
||||
impl OwnedWindowHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
|
||||
// Parent windows are currently unsupported, though owned window
|
||||
// handles would be implementable (would work similar to macOS).
|
||||
warn!("parent windows are unsupported on iOS");
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Inner {
|
||||
window: Id<WinitUIWindow>,
|
||||
view_controller: Id<WinitViewController>,
|
||||
@@ -177,6 +190,10 @@ impl Inner {
|
||||
debug!("`Window::set_cursor_icon` ignored on iOS")
|
||||
}
|
||||
|
||||
pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {
|
||||
debug!("`Window::set_custom_cursor` ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
@@ -355,14 +372,14 @@ impl Inner {
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
|
||||
let mut window_handle = rwh_06::UiKitWindowHandle::new({
|
||||
let ui_view = Id::as_ptr(&self.view) as _;
|
||||
std::ptr::NonNull::new(ui_view).expect("Id<T> should never be null")
|
||||
});
|
||||
window_handle.ui_view_controller =
|
||||
std::ptr::NonNull::new(Id::as_ptr(&self.view_controller) as _);
|
||||
Ok(rwh_06::RawWindowHandle::UiKit(window_handle))
|
||||
rwh_06::RawWindowHandle::UiKit(window_handle)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
@@ -422,7 +439,7 @@ impl Window {
|
||||
// TODO: transparency, visible
|
||||
|
||||
let main_screen = UIScreen::main(mtm);
|
||||
let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
|
||||
let fullscreen = window_attributes.fullscreen.0.clone().map(Into::into);
|
||||
let screen = match fullscreen {
|
||||
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm),
|
||||
Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm),
|
||||
@@ -516,7 +533,19 @@ impl Window {
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Inner) -> R + Send) -> R {
|
||||
self.inner.get_on_main(|inner, _mtm| f(inner))
|
||||
self.inner.get_on_main(|inner| f(inner))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
#[inline]
|
||||
pub(crate) fn raw_window_handle_rwh_06(
|
||||
&self,
|
||||
) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
if let Some(mtm) = MainThreadMarker::new() {
|
||||
Ok(self.inner.get(mtm).raw_window_handle_rwh_06())
|
||||
} else {
|
||||
Err(rwh_06::HandleError::Unavailable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,6 +580,11 @@ impl Inner {
|
||||
pub fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
||||
self.view_controller.set_prefers_status_bar_hidden(hidden);
|
||||
}
|
||||
|
||||
pub fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
|
||||
self.view_controller
|
||||
.set_preferred_status_bar_style(status_bar_style.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
@@ -659,5 +693,6 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub valid_orientations: ValidOrientations,
|
||||
pub prefers_home_indicator_hidden: bool,
|
||||
pub prefers_status_bar_hidden: bool,
|
||||
pub preferred_status_bar_style: StatusBarStyle,
|
||||
pub preferred_screen_edges_deferring_system_gestures: ScreenEdge,
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
//! Convert XKB keys to Winit keys.
|
||||
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKey, NativeKeyCode};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey};
|
||||
|
||||
/// Map the raw X11-style keycode to the `KeyCode` enum.
|
||||
///
|
||||
/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses.
|
||||
pub fn raw_keycode_to_keycode(keycode: u32) -> KeyCode {
|
||||
pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey {
|
||||
scancode_to_keycode(keycode.saturating_sub(8))
|
||||
}
|
||||
|
||||
/// Map the linux scancode to Keycode.
|
||||
///
|
||||
/// Both X11 and Wayland use keys with `+ 8` offset to linux scancode.
|
||||
pub fn scancode_to_keycode(scancode: u32) -> KeyCode {
|
||||
pub fn scancode_to_keycode(scancode: u32) -> PhysicalKey {
|
||||
// The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as
|
||||
// libxkbcommon's documentation seems to suggest that the keycode values we're interested in
|
||||
// are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes,
|
||||
@@ -21,8 +21,8 @@ pub fn scancode_to_keycode(scancode: u32) -> KeyCode {
|
||||
// Some of the keycodes are likely superfluous for our purposes, and some are ones which are
|
||||
// difficult to test the correctness of, or discover the purpose of. Because of this, they've
|
||||
// either been commented out here, or not included at all.
|
||||
match scancode {
|
||||
0 => KeyCode::Unidentified(NativeKeyCode::Xkb(0)),
|
||||
PhysicalKey::Code(match scancode {
|
||||
0 => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(0)),
|
||||
1 => KeyCode::Escape,
|
||||
2 => KeyCode::Digit1,
|
||||
3 => KeyCode::Digit2,
|
||||
@@ -256,7 +256,7 @@ pub fn scancode_to_keycode(scancode: u32) -> KeyCode {
|
||||
// 237 => KeyCode::BLUETOOTH,
|
||||
// 238 => KeyCode::WLAN,
|
||||
// 239 => KeyCode::UWB,
|
||||
240 => KeyCode::Unidentified(NativeKeyCode::Unidentified),
|
||||
240 => return PhysicalKey::Unidentified(NativeKeyCode::Unidentified),
|
||||
// 241 => KeyCode::VIDEO_NEXT,
|
||||
// 242 => KeyCode::VIDEO_PREV,
|
||||
// 243 => KeyCode::BRIGHTNESS_CYCLE,
|
||||
@@ -265,14 +265,23 @@ pub fn scancode_to_keycode(scancode: u32) -> KeyCode {
|
||||
// 246 => KeyCode::WWAN,
|
||||
// 247 => KeyCode::RFKILL,
|
||||
// 248 => KeyCode::KEY_MICMUTE,
|
||||
_ => KeyCode::Unidentified(NativeKeyCode::Xkb(scancode)),
|
||||
}
|
||||
_ => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(scancode)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn keycode_to_scancode(keycode: KeyCode) -> Option<u32> {
|
||||
match keycode {
|
||||
KeyCode::Unidentified(NativeKeyCode::Unidentified) => Some(240),
|
||||
KeyCode::Unidentified(NativeKeyCode::Xkb(raw)) => Some(raw),
|
||||
pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option<u32> {
|
||||
let code = match key {
|
||||
PhysicalKey::Code(code) => code,
|
||||
PhysicalKey::Unidentified(code) => {
|
||||
return match code {
|
||||
NativeKeyCode::Unidentified => Some(240),
|
||||
NativeKeyCode::Xkb(raw) => Some(raw),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
match code {
|
||||
KeyCode::Escape => Some(1),
|
||||
KeyCode::Digit1 => Some(2),
|
||||
KeyCode::Digit2 => Some(3),
|
||||
@@ -415,213 +424,213 @@ pub fn keycode_to_scancode(keycode: KeyCode) -> Option<u32> {
|
||||
|
||||
pub fn keysym_to_key(keysym: u32) -> Key {
|
||||
use xkbcommon_dl::keysyms;
|
||||
match keysym {
|
||||
Key::Named(match keysym {
|
||||
// TTY function keys
|
||||
keysyms::BackSpace => Key::Backspace,
|
||||
keysyms::Tab => Key::Tab,
|
||||
// keysyms::Linefeed => Key::Linefeed,
|
||||
keysyms::Clear => Key::Clear,
|
||||
keysyms::Return => Key::Enter,
|
||||
keysyms::Pause => Key::Pause,
|
||||
keysyms::Scroll_Lock => Key::ScrollLock,
|
||||
keysyms::Sys_Req => Key::PrintScreen,
|
||||
keysyms::Escape => Key::Escape,
|
||||
keysyms::Delete => Key::Delete,
|
||||
keysyms::BackSpace => NamedKey::Backspace,
|
||||
keysyms::Tab => NamedKey::Tab,
|
||||
// keysyms::Linefeed => NamedKey::Linefeed,
|
||||
keysyms::Clear => NamedKey::Clear,
|
||||
keysyms::Return => NamedKey::Enter,
|
||||
keysyms::Pause => NamedKey::Pause,
|
||||
keysyms::Scroll_Lock => NamedKey::ScrollLock,
|
||||
keysyms::Sys_Req => NamedKey::PrintScreen,
|
||||
keysyms::Escape => NamedKey::Escape,
|
||||
keysyms::Delete => NamedKey::Delete,
|
||||
|
||||
// IME keys
|
||||
keysyms::Multi_key => Key::Compose,
|
||||
keysyms::Codeinput => Key::CodeInput,
|
||||
keysyms::SingleCandidate => Key::SingleCandidate,
|
||||
keysyms::MultipleCandidate => Key::AllCandidates,
|
||||
keysyms::PreviousCandidate => Key::PreviousCandidate,
|
||||
keysyms::Multi_key => NamedKey::Compose,
|
||||
keysyms::Codeinput => NamedKey::CodeInput,
|
||||
keysyms::SingleCandidate => NamedKey::SingleCandidate,
|
||||
keysyms::MultipleCandidate => NamedKey::AllCandidates,
|
||||
keysyms::PreviousCandidate => NamedKey::PreviousCandidate,
|
||||
|
||||
// Japanese keys
|
||||
keysyms::Kanji => Key::KanjiMode,
|
||||
keysyms::Muhenkan => Key::NonConvert,
|
||||
keysyms::Henkan_Mode => Key::Convert,
|
||||
keysyms::Romaji => Key::Romaji,
|
||||
keysyms::Hiragana => Key::Hiragana,
|
||||
keysyms::Hiragana_Katakana => Key::HiraganaKatakana,
|
||||
keysyms::Zenkaku => Key::Zenkaku,
|
||||
keysyms::Hankaku => Key::Hankaku,
|
||||
keysyms::Zenkaku_Hankaku => Key::ZenkakuHankaku,
|
||||
// keysyms::Touroku => Key::Touroku,
|
||||
// keysyms::Massyo => Key::Massyo,
|
||||
keysyms::Kana_Lock => Key::KanaMode,
|
||||
keysyms::Kana_Shift => Key::KanaMode,
|
||||
keysyms::Eisu_Shift => Key::Alphanumeric,
|
||||
keysyms::Eisu_toggle => Key::Alphanumeric,
|
||||
keysyms::Kanji => NamedKey::KanjiMode,
|
||||
keysyms::Muhenkan => NamedKey::NonConvert,
|
||||
keysyms::Henkan_Mode => NamedKey::Convert,
|
||||
keysyms::Romaji => NamedKey::Romaji,
|
||||
keysyms::Hiragana => NamedKey::Hiragana,
|
||||
keysyms::Hiragana_Katakana => NamedKey::HiraganaKatakana,
|
||||
keysyms::Zenkaku => NamedKey::Zenkaku,
|
||||
keysyms::Hankaku => NamedKey::Hankaku,
|
||||
keysyms::Zenkaku_Hankaku => NamedKey::ZenkakuHankaku,
|
||||
// keysyms::Touroku => NamedKey::Touroku,
|
||||
// keysyms::Massyo => NamedKey::Massyo,
|
||||
keysyms::Kana_Lock => NamedKey::KanaMode,
|
||||
keysyms::Kana_Shift => NamedKey::KanaMode,
|
||||
keysyms::Eisu_Shift => NamedKey::Alphanumeric,
|
||||
keysyms::Eisu_toggle => NamedKey::Alphanumeric,
|
||||
// NOTE: The next three items are aliases for values we've already mapped.
|
||||
// keysyms::Kanji_Bangou => Key::CodeInput,
|
||||
// keysyms::Zen_Koho => Key::AllCandidates,
|
||||
// keysyms::Mae_Koho => Key::PreviousCandidate,
|
||||
// keysyms::Kanji_Bangou => NamedKey::CodeInput,
|
||||
// keysyms::Zen_Koho => NamedKey::AllCandidates,
|
||||
// keysyms::Mae_Koho => NamedKey::PreviousCandidate,
|
||||
|
||||
// Cursor control & motion
|
||||
keysyms::Home => Key::Home,
|
||||
keysyms::Left => Key::ArrowLeft,
|
||||
keysyms::Up => Key::ArrowUp,
|
||||
keysyms::Right => Key::ArrowRight,
|
||||
keysyms::Down => Key::ArrowDown,
|
||||
// keysyms::Prior => Key::PageUp,
|
||||
keysyms::Page_Up => Key::PageUp,
|
||||
// keysyms::Next => Key::PageDown,
|
||||
keysyms::Page_Down => Key::PageDown,
|
||||
keysyms::End => Key::End,
|
||||
// keysyms::Begin => Key::Begin,
|
||||
keysyms::Home => NamedKey::Home,
|
||||
keysyms::Left => NamedKey::ArrowLeft,
|
||||
keysyms::Up => NamedKey::ArrowUp,
|
||||
keysyms::Right => NamedKey::ArrowRight,
|
||||
keysyms::Down => NamedKey::ArrowDown,
|
||||
// keysyms::Prior => NamedKey::PageUp,
|
||||
keysyms::Page_Up => NamedKey::PageUp,
|
||||
// keysyms::Next => NamedKey::PageDown,
|
||||
keysyms::Page_Down => NamedKey::PageDown,
|
||||
keysyms::End => NamedKey::End,
|
||||
// keysyms::Begin => NamedKey::Begin,
|
||||
|
||||
// Misc. functions
|
||||
keysyms::Select => Key::Select,
|
||||
keysyms::Print => Key::PrintScreen,
|
||||
keysyms::Execute => Key::Execute,
|
||||
keysyms::Insert => Key::Insert,
|
||||
keysyms::Undo => Key::Undo,
|
||||
keysyms::Redo => Key::Redo,
|
||||
keysyms::Menu => Key::ContextMenu,
|
||||
keysyms::Find => Key::Find,
|
||||
keysyms::Cancel => Key::Cancel,
|
||||
keysyms::Help => Key::Help,
|
||||
keysyms::Break => Key::Pause,
|
||||
keysyms::Mode_switch => Key::ModeChange,
|
||||
// keysyms::script_switch => Key::ModeChange,
|
||||
keysyms::Num_Lock => Key::NumLock,
|
||||
keysyms::Select => NamedKey::Select,
|
||||
keysyms::Print => NamedKey::PrintScreen,
|
||||
keysyms::Execute => NamedKey::Execute,
|
||||
keysyms::Insert => NamedKey::Insert,
|
||||
keysyms::Undo => NamedKey::Undo,
|
||||
keysyms::Redo => NamedKey::Redo,
|
||||
keysyms::Menu => NamedKey::ContextMenu,
|
||||
keysyms::Find => NamedKey::Find,
|
||||
keysyms::Cancel => NamedKey::Cancel,
|
||||
keysyms::Help => NamedKey::Help,
|
||||
keysyms::Break => NamedKey::Pause,
|
||||
keysyms::Mode_switch => NamedKey::ModeChange,
|
||||
// keysyms::script_switch => NamedKey::ModeChange,
|
||||
keysyms::Num_Lock => NamedKey::NumLock,
|
||||
|
||||
// Keypad keys
|
||||
// keysyms::KP_Space => Key::Character(" "),
|
||||
keysyms::KP_Tab => Key::Tab,
|
||||
keysyms::KP_Enter => Key::Enter,
|
||||
keysyms::KP_F1 => Key::F1,
|
||||
keysyms::KP_F2 => Key::F2,
|
||||
keysyms::KP_F3 => Key::F3,
|
||||
keysyms::KP_F4 => Key::F4,
|
||||
keysyms::KP_Home => Key::Home,
|
||||
keysyms::KP_Left => Key::ArrowLeft,
|
||||
keysyms::KP_Up => Key::ArrowLeft,
|
||||
keysyms::KP_Right => Key::ArrowRight,
|
||||
keysyms::KP_Down => Key::ArrowDown,
|
||||
// keysyms::KP_Prior => Key::PageUp,
|
||||
keysyms::KP_Page_Up => Key::PageUp,
|
||||
// keysyms::KP_Next => Key::PageDown,
|
||||
keysyms::KP_Page_Down => Key::PageDown,
|
||||
keysyms::KP_End => Key::End,
|
||||
// keysyms::KP_Space => return Key::Character(" "),
|
||||
keysyms::KP_Tab => NamedKey::Tab,
|
||||
keysyms::KP_Enter => NamedKey::Enter,
|
||||
keysyms::KP_F1 => NamedKey::F1,
|
||||
keysyms::KP_F2 => NamedKey::F2,
|
||||
keysyms::KP_F3 => NamedKey::F3,
|
||||
keysyms::KP_F4 => NamedKey::F4,
|
||||
keysyms::KP_Home => NamedKey::Home,
|
||||
keysyms::KP_Left => NamedKey::ArrowLeft,
|
||||
keysyms::KP_Up => NamedKey::ArrowLeft,
|
||||
keysyms::KP_Right => NamedKey::ArrowRight,
|
||||
keysyms::KP_Down => NamedKey::ArrowDown,
|
||||
// keysyms::KP_Prior => NamedKey::PageUp,
|
||||
keysyms::KP_Page_Up => NamedKey::PageUp,
|
||||
// keysyms::KP_Next => NamedKey::PageDown,
|
||||
keysyms::KP_Page_Down => NamedKey::PageDown,
|
||||
keysyms::KP_End => NamedKey::End,
|
||||
// This is the key labeled "5" on the numpad when NumLock is off.
|
||||
// keysyms::KP_Begin => Key::Begin,
|
||||
keysyms::KP_Insert => Key::Insert,
|
||||
keysyms::KP_Delete => Key::Delete,
|
||||
// keysyms::KP_Equal => Key::Equal,
|
||||
// keysyms::KP_Multiply => Key::Multiply,
|
||||
// keysyms::KP_Add => Key::Add,
|
||||
// keysyms::KP_Separator => Key::Separator,
|
||||
// keysyms::KP_Subtract => Key::Subtract,
|
||||
// keysyms::KP_Decimal => Key::Decimal,
|
||||
// keysyms::KP_Divide => Key::Divide,
|
||||
// keysyms::KP_Begin => NamedKey::Begin,
|
||||
keysyms::KP_Insert => NamedKey::Insert,
|
||||
keysyms::KP_Delete => NamedKey::Delete,
|
||||
// keysyms::KP_Equal => NamedKey::Equal,
|
||||
// keysyms::KP_Multiply => NamedKey::Multiply,
|
||||
// keysyms::KP_Add => NamedKey::Add,
|
||||
// keysyms::KP_Separator => NamedKey::Separator,
|
||||
// keysyms::KP_Subtract => NamedKey::Subtract,
|
||||
// keysyms::KP_Decimal => NamedKey::Decimal,
|
||||
// keysyms::KP_Divide => NamedKey::Divide,
|
||||
|
||||
// keysyms::KP_0 => Key::Character("0"),
|
||||
// keysyms::KP_1 => Key::Character("1"),
|
||||
// keysyms::KP_2 => Key::Character("2"),
|
||||
// keysyms::KP_3 => Key::Character("3"),
|
||||
// keysyms::KP_4 => Key::Character("4"),
|
||||
// keysyms::KP_5 => Key::Character("5"),
|
||||
// keysyms::KP_6 => Key::Character("6"),
|
||||
// keysyms::KP_7 => Key::Character("7"),
|
||||
// keysyms::KP_8 => Key::Character("8"),
|
||||
// keysyms::KP_9 => Key::Character("9"),
|
||||
// keysyms::KP_0 => return Key::Character("0"),
|
||||
// keysyms::KP_1 => return Key::Character("1"),
|
||||
// keysyms::KP_2 => return Key::Character("2"),
|
||||
// keysyms::KP_3 => return Key::Character("3"),
|
||||
// keysyms::KP_4 => return Key::Character("4"),
|
||||
// keysyms::KP_5 => return Key::Character("5"),
|
||||
// keysyms::KP_6 => return Key::Character("6"),
|
||||
// keysyms::KP_7 => return Key::Character("7"),
|
||||
// keysyms::KP_8 => return Key::Character("8"),
|
||||
// keysyms::KP_9 => return Key::Character("9"),
|
||||
|
||||
// Function keys
|
||||
keysyms::F1 => Key::F1,
|
||||
keysyms::F2 => Key::F2,
|
||||
keysyms::F3 => Key::F3,
|
||||
keysyms::F4 => Key::F4,
|
||||
keysyms::F5 => Key::F5,
|
||||
keysyms::F6 => Key::F6,
|
||||
keysyms::F7 => Key::F7,
|
||||
keysyms::F8 => Key::F8,
|
||||
keysyms::F9 => Key::F9,
|
||||
keysyms::F10 => Key::F10,
|
||||
keysyms::F11 => Key::F11,
|
||||
keysyms::F12 => Key::F12,
|
||||
keysyms::F13 => Key::F13,
|
||||
keysyms::F14 => Key::F14,
|
||||
keysyms::F15 => Key::F15,
|
||||
keysyms::F16 => Key::F16,
|
||||
keysyms::F17 => Key::F17,
|
||||
keysyms::F18 => Key::F18,
|
||||
keysyms::F19 => Key::F19,
|
||||
keysyms::F20 => Key::F20,
|
||||
keysyms::F21 => Key::F21,
|
||||
keysyms::F22 => Key::F22,
|
||||
keysyms::F23 => Key::F23,
|
||||
keysyms::F24 => Key::F24,
|
||||
keysyms::F25 => Key::F25,
|
||||
keysyms::F26 => Key::F26,
|
||||
keysyms::F27 => Key::F27,
|
||||
keysyms::F28 => Key::F28,
|
||||
keysyms::F29 => Key::F29,
|
||||
keysyms::F30 => Key::F30,
|
||||
keysyms::F31 => Key::F31,
|
||||
keysyms::F32 => Key::F32,
|
||||
keysyms::F33 => Key::F33,
|
||||
keysyms::F34 => Key::F34,
|
||||
keysyms::F35 => Key::F35,
|
||||
keysyms::F1 => NamedKey::F1,
|
||||
keysyms::F2 => NamedKey::F2,
|
||||
keysyms::F3 => NamedKey::F3,
|
||||
keysyms::F4 => NamedKey::F4,
|
||||
keysyms::F5 => NamedKey::F5,
|
||||
keysyms::F6 => NamedKey::F6,
|
||||
keysyms::F7 => NamedKey::F7,
|
||||
keysyms::F8 => NamedKey::F8,
|
||||
keysyms::F9 => NamedKey::F9,
|
||||
keysyms::F10 => NamedKey::F10,
|
||||
keysyms::F11 => NamedKey::F11,
|
||||
keysyms::F12 => NamedKey::F12,
|
||||
keysyms::F13 => NamedKey::F13,
|
||||
keysyms::F14 => NamedKey::F14,
|
||||
keysyms::F15 => NamedKey::F15,
|
||||
keysyms::F16 => NamedKey::F16,
|
||||
keysyms::F17 => NamedKey::F17,
|
||||
keysyms::F18 => NamedKey::F18,
|
||||
keysyms::F19 => NamedKey::F19,
|
||||
keysyms::F20 => NamedKey::F20,
|
||||
keysyms::F21 => NamedKey::F21,
|
||||
keysyms::F22 => NamedKey::F22,
|
||||
keysyms::F23 => NamedKey::F23,
|
||||
keysyms::F24 => NamedKey::F24,
|
||||
keysyms::F25 => NamedKey::F25,
|
||||
keysyms::F26 => NamedKey::F26,
|
||||
keysyms::F27 => NamedKey::F27,
|
||||
keysyms::F28 => NamedKey::F28,
|
||||
keysyms::F29 => NamedKey::F29,
|
||||
keysyms::F30 => NamedKey::F30,
|
||||
keysyms::F31 => NamedKey::F31,
|
||||
keysyms::F32 => NamedKey::F32,
|
||||
keysyms::F33 => NamedKey::F33,
|
||||
keysyms::F34 => NamedKey::F34,
|
||||
keysyms::F35 => NamedKey::F35,
|
||||
|
||||
// Modifiers
|
||||
keysyms::Shift_L => Key::Shift,
|
||||
keysyms::Shift_R => Key::Shift,
|
||||
keysyms::Control_L => Key::Control,
|
||||
keysyms::Control_R => Key::Control,
|
||||
keysyms::Caps_Lock => Key::CapsLock,
|
||||
// keysyms::Shift_Lock => Key::ShiftLock,
|
||||
keysyms::Shift_L => NamedKey::Shift,
|
||||
keysyms::Shift_R => NamedKey::Shift,
|
||||
keysyms::Control_L => NamedKey::Control,
|
||||
keysyms::Control_R => NamedKey::Control,
|
||||
keysyms::Caps_Lock => NamedKey::CapsLock,
|
||||
// keysyms::Shift_Lock => NamedKey::ShiftLock,
|
||||
|
||||
// keysyms::Meta_L => Key::Meta,
|
||||
// keysyms::Meta_R => Key::Meta,
|
||||
keysyms::Alt_L => Key::Alt,
|
||||
keysyms::Alt_R => Key::Alt,
|
||||
keysyms::Super_L => Key::Super,
|
||||
keysyms::Super_R => Key::Super,
|
||||
keysyms::Hyper_L => Key::Hyper,
|
||||
keysyms::Hyper_R => Key::Hyper,
|
||||
// keysyms::Meta_L => NamedKey::Meta,
|
||||
// keysyms::Meta_R => NamedKey::Meta,
|
||||
keysyms::Alt_L => NamedKey::Alt,
|
||||
keysyms::Alt_R => NamedKey::Alt,
|
||||
keysyms::Super_L => NamedKey::Super,
|
||||
keysyms::Super_R => NamedKey::Super,
|
||||
keysyms::Hyper_L => NamedKey::Hyper,
|
||||
keysyms::Hyper_R => NamedKey::Hyper,
|
||||
|
||||
// XKB function and modifier keys
|
||||
// keysyms::ISO_Lock => Key::IsoLock,
|
||||
// keysyms::ISO_Level2_Latch => Key::IsoLevel2Latch,
|
||||
keysyms::ISO_Level3_Shift => Key::AltGraph,
|
||||
keysyms::ISO_Level3_Latch => Key::AltGraph,
|
||||
keysyms::ISO_Level3_Lock => Key::AltGraph,
|
||||
// keysyms::ISO_Level5_Shift => Key::IsoLevel5Shift,
|
||||
// keysyms::ISO_Level5_Latch => Key::IsoLevel5Latch,
|
||||
// keysyms::ISO_Level5_Lock => Key::IsoLevel5Lock,
|
||||
// keysyms::ISO_Group_Shift => Key::IsoGroupShift,
|
||||
// keysyms::ISO_Group_Latch => Key::IsoGroupLatch,
|
||||
// keysyms::ISO_Group_Lock => Key::IsoGroupLock,
|
||||
keysyms::ISO_Next_Group => Key::GroupNext,
|
||||
// keysyms::ISO_Next_Group_Lock => Key::GroupNextLock,
|
||||
keysyms::ISO_Prev_Group => Key::GroupPrevious,
|
||||
// keysyms::ISO_Prev_Group_Lock => Key::GroupPreviousLock,
|
||||
keysyms::ISO_First_Group => Key::GroupFirst,
|
||||
// keysyms::ISO_First_Group_Lock => Key::GroupFirstLock,
|
||||
keysyms::ISO_Last_Group => Key::GroupLast,
|
||||
// keysyms::ISO_Last_Group_Lock => Key::GroupLastLock,
|
||||
// keysyms::ISO_Lock => NamedKey::IsoLock,
|
||||
// keysyms::ISO_Level2_Latch => NamedKey::IsoLevel2Latch,
|
||||
keysyms::ISO_Level3_Shift => NamedKey::AltGraph,
|
||||
keysyms::ISO_Level3_Latch => NamedKey::AltGraph,
|
||||
keysyms::ISO_Level3_Lock => NamedKey::AltGraph,
|
||||
// keysyms::ISO_Level5_Shift => NamedKey::IsoLevel5Shift,
|
||||
// keysyms::ISO_Level5_Latch => NamedKey::IsoLevel5Latch,
|
||||
// keysyms::ISO_Level5_Lock => NamedKey::IsoLevel5Lock,
|
||||
// keysyms::ISO_Group_Shift => NamedKey::IsoGroupShift,
|
||||
// keysyms::ISO_Group_Latch => NamedKey::IsoGroupLatch,
|
||||
// keysyms::ISO_Group_Lock => NamedKey::IsoGroupLock,
|
||||
keysyms::ISO_Next_Group => NamedKey::GroupNext,
|
||||
// keysyms::ISO_Next_Group_Lock => NamedKey::GroupNextLock,
|
||||
keysyms::ISO_Prev_Group => NamedKey::GroupPrevious,
|
||||
// keysyms::ISO_Prev_Group_Lock => NamedKey::GroupPreviousLock,
|
||||
keysyms::ISO_First_Group => NamedKey::GroupFirst,
|
||||
// keysyms::ISO_First_Group_Lock => NamedKey::GroupFirstLock,
|
||||
keysyms::ISO_Last_Group => NamedKey::GroupLast,
|
||||
// keysyms::ISO_Last_Group_Lock => NamedKey::GroupLastLock,
|
||||
//
|
||||
keysyms::ISO_Left_Tab => Key::Tab,
|
||||
// keysyms::ISO_Move_Line_Up => Key::IsoMoveLineUp,
|
||||
// keysyms::ISO_Move_Line_Down => Key::IsoMoveLineDown,
|
||||
// keysyms::ISO_Partial_Line_Up => Key::IsoPartialLineUp,
|
||||
// keysyms::ISO_Partial_Line_Down => Key::IsoPartialLineDown,
|
||||
// keysyms::ISO_Partial_Space_Left => Key::IsoPartialSpaceLeft,
|
||||
// keysyms::ISO_Partial_Space_Right => Key::IsoPartialSpaceRight,
|
||||
// keysyms::ISO_Set_Margin_Left => Key::IsoSetMarginLeft,
|
||||
// keysyms::ISO_Set_Margin_Right => Key::IsoSetMarginRight,
|
||||
// keysyms::ISO_Release_Margin_Left => Key::IsoReleaseMarginLeft,
|
||||
// keysyms::ISO_Release_Margin_Right => Key::IsoReleaseMarginRight,
|
||||
// keysyms::ISO_Release_Both_Margins => Key::IsoReleaseBothMargins,
|
||||
// keysyms::ISO_Fast_Cursor_Left => Key::IsoFastCursorLeft,
|
||||
// keysyms::ISO_Fast_Cursor_Right => Key::IsoFastCursorRight,
|
||||
// keysyms::ISO_Fast_Cursor_Up => Key::IsoFastCursorUp,
|
||||
// keysyms::ISO_Fast_Cursor_Down => Key::IsoFastCursorDown,
|
||||
// keysyms::ISO_Continuous_Underline => Key::IsoContinuousUnderline,
|
||||
// keysyms::ISO_Discontinuous_Underline => Key::IsoDiscontinuousUnderline,
|
||||
// keysyms::ISO_Emphasize => Key::IsoEmphasize,
|
||||
// keysyms::ISO_Center_Object => Key::IsoCenterObject,
|
||||
keysyms::ISO_Enter => Key::Enter,
|
||||
keysyms::ISO_Left_Tab => NamedKey::Tab,
|
||||
// keysyms::ISO_Move_Line_Up => NamedKey::IsoMoveLineUp,
|
||||
// keysyms::ISO_Move_Line_Down => NamedKey::IsoMoveLineDown,
|
||||
// keysyms::ISO_Partial_Line_Up => NamedKey::IsoPartialLineUp,
|
||||
// keysyms::ISO_Partial_Line_Down => NamedKey::IsoPartialLineDown,
|
||||
// keysyms::ISO_Partial_Space_Left => NamedKey::IsoPartialSpaceLeft,
|
||||
// keysyms::ISO_Partial_Space_Right => NamedKey::IsoPartialSpaceRight,
|
||||
// keysyms::ISO_Set_Margin_Left => NamedKey::IsoSetMarginLeft,
|
||||
// keysyms::ISO_Set_Margin_Right => NamedKey::IsoSetMarginRight,
|
||||
// keysyms::ISO_Release_Margin_Left => NamedKey::IsoReleaseMarginLeft,
|
||||
// keysyms::ISO_Release_Margin_Right => NamedKey::IsoReleaseMarginRight,
|
||||
// keysyms::ISO_Release_Both_Margins => NamedKey::IsoReleaseBothMargins,
|
||||
// keysyms::ISO_Fast_Cursor_Left => NamedKey::IsoFastCursorLeft,
|
||||
// keysyms::ISO_Fast_Cursor_Right => NamedKey::IsoFastCursorRight,
|
||||
// keysyms::ISO_Fast_Cursor_Up => NamedKey::IsoFastCursorUp,
|
||||
// keysyms::ISO_Fast_Cursor_Down => NamedKey::IsoFastCursorDown,
|
||||
// keysyms::ISO_Continuous_Underline => NamedKey::IsoContinuousUnderline,
|
||||
// keysyms::ISO_Discontinuous_Underline => NamedKey::IsoDiscontinuousUnderline,
|
||||
// keysyms::ISO_Emphasize => NamedKey::IsoEmphasize,
|
||||
// keysyms::ISO_Center_Object => NamedKey::IsoCenterObject,
|
||||
keysyms::ISO_Enter => NamedKey::Enter,
|
||||
|
||||
// dead_grave..dead_currency
|
||||
|
||||
@@ -642,194 +651,194 @@ pub fn keysym_to_key(keysym: u32) -> Key {
|
||||
// ch..C_H
|
||||
|
||||
// 3270 terminal keys
|
||||
// keysyms::3270_Duplicate => Key::Duplicate,
|
||||
// keysyms::3270_FieldMark => Key::FieldMark,
|
||||
// keysyms::3270_Right2 => Key::Right2,
|
||||
// keysyms::3270_Left2 => Key::Left2,
|
||||
// keysyms::3270_BackTab => Key::BackTab,
|
||||
keysyms::_3270_EraseEOF => Key::EraseEof,
|
||||
// keysyms::3270_EraseInput => Key::EraseInput,
|
||||
// keysyms::3270_Reset => Key::Reset,
|
||||
// keysyms::3270_Quit => Key::Quit,
|
||||
// keysyms::3270_PA1 => Key::Pa1,
|
||||
// keysyms::3270_PA2 => Key::Pa2,
|
||||
// keysyms::3270_PA3 => Key::Pa3,
|
||||
// keysyms::3270_Test => Key::Test,
|
||||
keysyms::_3270_Attn => Key::Attn,
|
||||
// keysyms::3270_CursorBlink => Key::CursorBlink,
|
||||
// keysyms::3270_AltCursor => Key::AltCursor,
|
||||
// keysyms::3270_KeyClick => Key::KeyClick,
|
||||
// keysyms::3270_Jump => Key::Jump,
|
||||
// keysyms::3270_Ident => Key::Ident,
|
||||
// keysyms::3270_Rule => Key::Rule,
|
||||
// keysyms::3270_Copy => Key::Copy,
|
||||
keysyms::_3270_Play => Key::Play,
|
||||
// keysyms::3270_Setup => Key::Setup,
|
||||
// keysyms::3270_Record => Key::Record,
|
||||
// keysyms::3270_ChangeScreen => Key::ChangeScreen,
|
||||
// keysyms::3270_DeleteWord => Key::DeleteWord,
|
||||
keysyms::_3270_ExSelect => Key::ExSel,
|
||||
keysyms::_3270_CursorSelect => Key::CrSel,
|
||||
keysyms::_3270_PrintScreen => Key::PrintScreen,
|
||||
keysyms::_3270_Enter => Key::Enter,
|
||||
// keysyms::3270_Duplicate => NamedKey::Duplicate,
|
||||
// keysyms::3270_FieldMark => NamedKey::FieldMark,
|
||||
// keysyms::3270_Right2 => NamedKey::Right2,
|
||||
// keysyms::3270_Left2 => NamedKey::Left2,
|
||||
// keysyms::3270_BackTab => NamedKey::BackTab,
|
||||
keysyms::_3270_EraseEOF => NamedKey::EraseEof,
|
||||
// keysyms::3270_EraseInput => NamedKey::EraseInput,
|
||||
// keysyms::3270_Reset => NamedKey::Reset,
|
||||
// keysyms::3270_Quit => NamedKey::Quit,
|
||||
// keysyms::3270_PA1 => NamedKey::Pa1,
|
||||
// keysyms::3270_PA2 => NamedKey::Pa2,
|
||||
// keysyms::3270_PA3 => NamedKey::Pa3,
|
||||
// keysyms::3270_Test => NamedKey::Test,
|
||||
keysyms::_3270_Attn => NamedKey::Attn,
|
||||
// keysyms::3270_CursorBlink => NamedKey::CursorBlink,
|
||||
// keysyms::3270_AltCursor => NamedKey::AltCursor,
|
||||
// keysyms::3270_KeyClick => NamedKey::KeyClick,
|
||||
// keysyms::3270_Jump => NamedKey::Jump,
|
||||
// keysyms::3270_Ident => NamedKey::Ident,
|
||||
// keysyms::3270_Rule => NamedKey::Rule,
|
||||
// keysyms::3270_Copy => NamedKey::Copy,
|
||||
keysyms::_3270_Play => NamedKey::Play,
|
||||
// keysyms::3270_Setup => NamedKey::Setup,
|
||||
// keysyms::3270_Record => NamedKey::Record,
|
||||
// keysyms::3270_ChangeScreen => NamedKey::ChangeScreen,
|
||||
// keysyms::3270_DeleteWord => NamedKey::DeleteWord,
|
||||
keysyms::_3270_ExSelect => NamedKey::ExSel,
|
||||
keysyms::_3270_CursorSelect => NamedKey::CrSel,
|
||||
keysyms::_3270_PrintScreen => NamedKey::PrintScreen,
|
||||
keysyms::_3270_Enter => NamedKey::Enter,
|
||||
|
||||
keysyms::space => Key::Space,
|
||||
keysyms::space => NamedKey::Space,
|
||||
// exclam..Sinh_kunddaliya
|
||||
|
||||
// XFree86
|
||||
// keysyms::XF86_ModeLock => Key::ModeLock,
|
||||
// keysyms::XF86_ModeLock => NamedKey::ModeLock,
|
||||
|
||||
// XFree86 - Backlight controls
|
||||
keysyms::XF86_MonBrightnessUp => Key::BrightnessUp,
|
||||
keysyms::XF86_MonBrightnessDown => Key::BrightnessDown,
|
||||
// keysyms::XF86_KbdLightOnOff => Key::LightOnOff,
|
||||
// keysyms::XF86_KbdBrightnessUp => Key::KeyboardBrightnessUp,
|
||||
// keysyms::XF86_KbdBrightnessDown => Key::KeyboardBrightnessDown,
|
||||
keysyms::XF86_MonBrightnessUp => NamedKey::BrightnessUp,
|
||||
keysyms::XF86_MonBrightnessDown => NamedKey::BrightnessDown,
|
||||
// keysyms::XF86_KbdLightOnOff => NamedKey::LightOnOff,
|
||||
// keysyms::XF86_KbdBrightnessUp => NamedKey::KeyboardBrightnessUp,
|
||||
// keysyms::XF86_KbdBrightnessDown => NamedKey::KeyboardBrightnessDown,
|
||||
|
||||
// XFree86 - "Internet"
|
||||
keysyms::XF86_Standby => Key::Standby,
|
||||
keysyms::XF86_AudioLowerVolume => Key::AudioVolumeDown,
|
||||
keysyms::XF86_AudioRaiseVolume => Key::AudioVolumeUp,
|
||||
keysyms::XF86_AudioPlay => Key::MediaPlay,
|
||||
keysyms::XF86_AudioStop => Key::MediaStop,
|
||||
keysyms::XF86_AudioPrev => Key::MediaTrackPrevious,
|
||||
keysyms::XF86_AudioNext => Key::MediaTrackNext,
|
||||
keysyms::XF86_HomePage => Key::BrowserHome,
|
||||
keysyms::XF86_Mail => Key::LaunchMail,
|
||||
// keysyms::XF86_Start => Key::Start,
|
||||
keysyms::XF86_Search => Key::BrowserSearch,
|
||||
keysyms::XF86_AudioRecord => Key::MediaRecord,
|
||||
keysyms::XF86_Standby => NamedKey::Standby,
|
||||
keysyms::XF86_AudioLowerVolume => NamedKey::AudioVolumeDown,
|
||||
keysyms::XF86_AudioRaiseVolume => NamedKey::AudioVolumeUp,
|
||||
keysyms::XF86_AudioPlay => NamedKey::MediaPlay,
|
||||
keysyms::XF86_AudioStop => NamedKey::MediaStop,
|
||||
keysyms::XF86_AudioPrev => NamedKey::MediaTrackPrevious,
|
||||
keysyms::XF86_AudioNext => NamedKey::MediaTrackNext,
|
||||
keysyms::XF86_HomePage => NamedKey::BrowserHome,
|
||||
keysyms::XF86_Mail => NamedKey::LaunchMail,
|
||||
// keysyms::XF86_Start => NamedKey::Start,
|
||||
keysyms::XF86_Search => NamedKey::BrowserSearch,
|
||||
keysyms::XF86_AudioRecord => NamedKey::MediaRecord,
|
||||
|
||||
// XFree86 - PDA
|
||||
keysyms::XF86_Calculator => Key::LaunchApplication2,
|
||||
// keysyms::XF86_Memo => Key::Memo,
|
||||
// keysyms::XF86_ToDoList => Key::ToDoList,
|
||||
keysyms::XF86_Calendar => Key::LaunchCalendar,
|
||||
keysyms::XF86_PowerDown => Key::Power,
|
||||
// keysyms::XF86_ContrastAdjust => Key::AdjustContrast,
|
||||
// keysyms::XF86_RockerUp => Key::RockerUp,
|
||||
// keysyms::XF86_RockerDown => Key::RockerDown,
|
||||
// keysyms::XF86_RockerEnter => Key::RockerEnter,
|
||||
keysyms::XF86_Calculator => NamedKey::LaunchApplication2,
|
||||
// keysyms::XF86_Memo => NamedKey::Memo,
|
||||
// keysyms::XF86_ToDoList => NamedKey::ToDoList,
|
||||
keysyms::XF86_Calendar => NamedKey::LaunchCalendar,
|
||||
keysyms::XF86_PowerDown => NamedKey::Power,
|
||||
// keysyms::XF86_ContrastAdjust => NamedKey::AdjustContrast,
|
||||
// keysyms::XF86_RockerUp => NamedKey::RockerUp,
|
||||
// keysyms::XF86_RockerDown => NamedKey::RockerDown,
|
||||
// keysyms::XF86_RockerEnter => NamedKey::RockerEnter,
|
||||
|
||||
// XFree86 - More "Internet"
|
||||
keysyms::XF86_Back => Key::BrowserBack,
|
||||
keysyms::XF86_Forward => Key::BrowserForward,
|
||||
// keysyms::XF86_Stop => Key::Stop,
|
||||
keysyms::XF86_Refresh => Key::BrowserRefresh,
|
||||
keysyms::XF86_PowerOff => Key::Power,
|
||||
keysyms::XF86_WakeUp => Key::WakeUp,
|
||||
keysyms::XF86_Eject => Key::Eject,
|
||||
keysyms::XF86_ScreenSaver => Key::LaunchScreenSaver,
|
||||
keysyms::XF86_WWW => Key::LaunchWebBrowser,
|
||||
keysyms::XF86_Sleep => Key::Standby,
|
||||
keysyms::XF86_Favorites => Key::BrowserFavorites,
|
||||
keysyms::XF86_AudioPause => Key::MediaPause,
|
||||
// keysyms::XF86_AudioMedia => Key::AudioMedia,
|
||||
keysyms::XF86_MyComputer => Key::LaunchApplication1,
|
||||
// keysyms::XF86_VendorHome => Key::VendorHome,
|
||||
// keysyms::XF86_LightBulb => Key::LightBulb,
|
||||
// keysyms::XF86_Shop => Key::BrowserShop,
|
||||
// keysyms::XF86_History => Key::BrowserHistory,
|
||||
// keysyms::XF86_OpenURL => Key::OpenUrl,
|
||||
// keysyms::XF86_AddFavorite => Key::AddFavorite,
|
||||
// keysyms::XF86_HotLinks => Key::HotLinks,
|
||||
// keysyms::XF86_BrightnessAdjust => Key::BrightnessAdjust,
|
||||
// keysyms::XF86_Finance => Key::BrowserFinance,
|
||||
// keysyms::XF86_Community => Key::BrowserCommunity,
|
||||
keysyms::XF86_AudioRewind => Key::MediaRewind,
|
||||
keysyms::XF86_Back => NamedKey::BrowserBack,
|
||||
keysyms::XF86_Forward => NamedKey::BrowserForward,
|
||||
// keysyms::XF86_Stop => NamedKey::Stop,
|
||||
keysyms::XF86_Refresh => NamedKey::BrowserRefresh,
|
||||
keysyms::XF86_PowerOff => NamedKey::Power,
|
||||
keysyms::XF86_WakeUp => NamedKey::WakeUp,
|
||||
keysyms::XF86_Eject => NamedKey::Eject,
|
||||
keysyms::XF86_ScreenSaver => NamedKey::LaunchScreenSaver,
|
||||
keysyms::XF86_WWW => NamedKey::LaunchWebBrowser,
|
||||
keysyms::XF86_Sleep => NamedKey::Standby,
|
||||
keysyms::XF86_Favorites => NamedKey::BrowserFavorites,
|
||||
keysyms::XF86_AudioPause => NamedKey::MediaPause,
|
||||
// keysyms::XF86_AudioMedia => NamedKey::AudioMedia,
|
||||
keysyms::XF86_MyComputer => NamedKey::LaunchApplication1,
|
||||
// keysyms::XF86_VendorHome => NamedKey::VendorHome,
|
||||
// keysyms::XF86_LightBulb => NamedKey::LightBulb,
|
||||
// keysyms::XF86_Shop => NamedKey::BrowserShop,
|
||||
// keysyms::XF86_History => NamedKey::BrowserHistory,
|
||||
// keysyms::XF86_OpenURL => NamedKey::OpenUrl,
|
||||
// keysyms::XF86_AddFavorite => NamedKey::AddFavorite,
|
||||
// keysyms::XF86_HotLinks => NamedKey::HotLinks,
|
||||
// keysyms::XF86_BrightnessAdjust => NamedKey::BrightnessAdjust,
|
||||
// keysyms::XF86_Finance => NamedKey::BrowserFinance,
|
||||
// keysyms::XF86_Community => NamedKey::BrowserCommunity,
|
||||
keysyms::XF86_AudioRewind => NamedKey::MediaRewind,
|
||||
// keysyms::XF86_BackForward => Key::???,
|
||||
// XF86_Launch0..XF86_LaunchF
|
||||
|
||||
// XF86_ApplicationLeft..XF86_CD
|
||||
keysyms::XF86_Calculater => Key::LaunchApplication2, // Nice typo, libxkbcommon :)
|
||||
keysyms::XF86_Calculater => NamedKey::LaunchApplication2, // Nice typo, libxkbcommon :)
|
||||
// XF86_Clear
|
||||
keysyms::XF86_Close => Key::Close,
|
||||
keysyms::XF86_Copy => Key::Copy,
|
||||
keysyms::XF86_Cut => Key::Cut,
|
||||
keysyms::XF86_Close => NamedKey::Close,
|
||||
keysyms::XF86_Copy => NamedKey::Copy,
|
||||
keysyms::XF86_Cut => NamedKey::Cut,
|
||||
// XF86_Display..XF86_Documents
|
||||
keysyms::XF86_Excel => Key::LaunchSpreadsheet,
|
||||
keysyms::XF86_Excel => NamedKey::LaunchSpreadsheet,
|
||||
// XF86_Explorer..XF86iTouch
|
||||
keysyms::XF86_LogOff => Key::LogOff,
|
||||
keysyms::XF86_LogOff => NamedKey::LogOff,
|
||||
// XF86_Market..XF86_MenuPB
|
||||
keysyms::XF86_MySites => Key::BrowserFavorites,
|
||||
keysyms::XF86_New => Key::New,
|
||||
keysyms::XF86_MySites => NamedKey::BrowserFavorites,
|
||||
keysyms::XF86_New => NamedKey::New,
|
||||
// XF86_News..XF86_OfficeHome
|
||||
keysyms::XF86_Open => Key::Open,
|
||||
keysyms::XF86_Open => NamedKey::Open,
|
||||
// XF86_Option
|
||||
keysyms::XF86_Paste => Key::Paste,
|
||||
keysyms::XF86_Phone => Key::LaunchPhone,
|
||||
keysyms::XF86_Paste => NamedKey::Paste,
|
||||
keysyms::XF86_Phone => NamedKey::LaunchPhone,
|
||||
// XF86_Q
|
||||
keysyms::XF86_Reply => Key::MailReply,
|
||||
keysyms::XF86_Reload => Key::BrowserRefresh,
|
||||
keysyms::XF86_Reply => NamedKey::MailReply,
|
||||
keysyms::XF86_Reload => NamedKey::BrowserRefresh,
|
||||
// XF86_RotateWindows..XF86_RotationKB
|
||||
keysyms::XF86_Save => Key::Save,
|
||||
keysyms::XF86_Save => NamedKey::Save,
|
||||
// XF86_ScrollUp..XF86_ScrollClick
|
||||
keysyms::XF86_Send => Key::MailSend,
|
||||
keysyms::XF86_Spell => Key::SpellCheck,
|
||||
keysyms::XF86_SplitScreen => Key::SplitScreenToggle,
|
||||
keysyms::XF86_Send => NamedKey::MailSend,
|
||||
keysyms::XF86_Spell => NamedKey::SpellCheck,
|
||||
keysyms::XF86_SplitScreen => NamedKey::SplitScreenToggle,
|
||||
// XF86_Support..XF86_User2KB
|
||||
keysyms::XF86_Video => Key::LaunchMediaPlayer,
|
||||
keysyms::XF86_Video => NamedKey::LaunchMediaPlayer,
|
||||
// XF86_WheelButton
|
||||
keysyms::XF86_Word => Key::LaunchWordProcessor,
|
||||
keysyms::XF86_Word => NamedKey::LaunchWordProcessor,
|
||||
// XF86_Xfer
|
||||
keysyms::XF86_ZoomIn => Key::ZoomIn,
|
||||
keysyms::XF86_ZoomOut => Key::ZoomOut,
|
||||
keysyms::XF86_ZoomIn => NamedKey::ZoomIn,
|
||||
keysyms::XF86_ZoomOut => NamedKey::ZoomOut,
|
||||
|
||||
// XF86_Away..XF86_Messenger
|
||||
keysyms::XF86_WebCam => Key::LaunchWebCam,
|
||||
keysyms::XF86_MailForward => Key::MailForward,
|
||||
keysyms::XF86_WebCam => NamedKey::LaunchWebCam,
|
||||
keysyms::XF86_MailForward => NamedKey::MailForward,
|
||||
// XF86_Pictures
|
||||
keysyms::XF86_Music => Key::LaunchMusicPlayer,
|
||||
keysyms::XF86_Music => NamedKey::LaunchMusicPlayer,
|
||||
|
||||
// XF86_Battery..XF86_UWB
|
||||
//
|
||||
keysyms::XF86_AudioForward => Key::MediaFastForward,
|
||||
keysyms::XF86_AudioForward => NamedKey::MediaFastForward,
|
||||
// XF86_AudioRepeat
|
||||
keysyms::XF86_AudioRandomPlay => Key::RandomToggle,
|
||||
keysyms::XF86_Subtitle => Key::Subtitle,
|
||||
keysyms::XF86_AudioCycleTrack => Key::MediaAudioTrack,
|
||||
keysyms::XF86_AudioRandomPlay => NamedKey::RandomToggle,
|
||||
keysyms::XF86_Subtitle => NamedKey::Subtitle,
|
||||
keysyms::XF86_AudioCycleTrack => NamedKey::MediaAudioTrack,
|
||||
// XF86_CycleAngle..XF86_Blue
|
||||
//
|
||||
keysyms::XF86_Suspend => Key::Standby,
|
||||
keysyms::XF86_Hibernate => Key::Hibernate,
|
||||
keysyms::XF86_Suspend => NamedKey::Standby,
|
||||
keysyms::XF86_Hibernate => NamedKey::Hibernate,
|
||||
// XF86_TouchpadToggle..XF86_TouchpadOff
|
||||
//
|
||||
keysyms::XF86_AudioMute => Key::AudioVolumeMute,
|
||||
keysyms::XF86_AudioMute => NamedKey::AudioVolumeMute,
|
||||
|
||||
// XF86_Switch_VT_1..XF86_Switch_VT_12
|
||||
|
||||
// XF86_Ungrab..XF86_ClearGrab
|
||||
keysyms::XF86_Next_VMode => Key::VideoModeNext,
|
||||
// keysyms::XF86_Prev_VMode => Key::VideoModePrevious,
|
||||
keysyms::XF86_Next_VMode => NamedKey::VideoModeNext,
|
||||
// keysyms::XF86_Prev_VMode => NamedKey::VideoModePrevious,
|
||||
// XF86_LogWindowTree..XF86_LogGrabInfo
|
||||
|
||||
// SunFA_Grave..SunFA_Cedilla
|
||||
|
||||
// keysyms::SunF36 => Key::F36 | Key::F11,
|
||||
// keysyms::SunF37 => Key::F37 | Key::F12,
|
||||
// keysyms::SunF36 => NamedKey::F36 | NamedKey::F11,
|
||||
// keysyms::SunF37 => NamedKey::F37 | NamedKey::F12,
|
||||
|
||||
// keysyms::SunSys_Req => Key::PrintScreen,
|
||||
// keysyms::SunSys_Req => NamedKey::PrintScreen,
|
||||
// The next couple of xkb (until SunStop) are already handled.
|
||||
// SunPrint_Screen..SunPageDown
|
||||
|
||||
// SunUndo..SunFront
|
||||
keysyms::SUN_Copy => Key::Copy,
|
||||
keysyms::SUN_Open => Key::Open,
|
||||
keysyms::SUN_Paste => Key::Paste,
|
||||
keysyms::SUN_Cut => Key::Cut,
|
||||
keysyms::SUN_Copy => NamedKey::Copy,
|
||||
keysyms::SUN_Open => NamedKey::Open,
|
||||
keysyms::SUN_Paste => NamedKey::Paste,
|
||||
keysyms::SUN_Cut => NamedKey::Cut,
|
||||
|
||||
// SunPowerSwitch
|
||||
keysyms::SUN_AudioLowerVolume => Key::AudioVolumeDown,
|
||||
keysyms::SUN_AudioMute => Key::AudioVolumeMute,
|
||||
keysyms::SUN_AudioRaiseVolume => Key::AudioVolumeUp,
|
||||
keysyms::SUN_AudioLowerVolume => NamedKey::AudioVolumeDown,
|
||||
keysyms::SUN_AudioMute => NamedKey::AudioVolumeMute,
|
||||
keysyms::SUN_AudioRaiseVolume => NamedKey::AudioVolumeUp,
|
||||
// SUN_VideoDegauss
|
||||
keysyms::SUN_VideoLowerBrightness => Key::BrightnessDown,
|
||||
keysyms::SUN_VideoRaiseBrightness => Key::BrightnessUp,
|
||||
keysyms::SUN_VideoLowerBrightness => NamedKey::BrightnessDown,
|
||||
keysyms::SUN_VideoRaiseBrightness => NamedKey::BrightnessUp,
|
||||
// SunPowerSwitchShift
|
||||
//
|
||||
0 => Key::Unidentified(NativeKey::Unidentified),
|
||||
_ => Key::Unidentified(NativeKey::Xkb(keysym)),
|
||||
}
|
||||
0 => return Key::Unidentified(NativeKey::Unidentified),
|
||||
_ => return Key::Unidentified(NativeKey::Xkb(keysym)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn keysym_location(keysym: u32) -> KeyLocation {
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::platform_impl::common::keymap;
|
||||
use crate::platform_impl::KeyEventExtra;
|
||||
use crate::{
|
||||
event::ElementState,
|
||||
keyboard::{Key, KeyCode, KeyLocation},
|
||||
keyboard::{Key, KeyLocation, PhysicalKey},
|
||||
};
|
||||
|
||||
// TODO: Wire this up without using a static `AtomicBool`.
|
||||
@@ -391,7 +391,7 @@ impl KbdState {
|
||||
) -> KeyEvent {
|
||||
let mut event =
|
||||
KeyEventResults::new(self, keycode, !repeat && state == ElementState::Pressed);
|
||||
let physical_key = event.keycode();
|
||||
let physical_key = event.physical_key();
|
||||
let (logical_key, location) = event.key();
|
||||
let text = event.text();
|
||||
let (key_without_modifiers, _) = event.key_without_modifiers();
|
||||
@@ -498,8 +498,8 @@ impl<'a> KeyEventResults<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn keycode(&mut self) -> KeyCode {
|
||||
keymap::raw_keycode_to_keycode(self.keycode)
|
||||
fn physical_key(&mut self) -> PhysicalKey {
|
||||
keymap::raw_keycode_to_physicalkey(self.keycode)
|
||||
}
|
||||
|
||||
pub fn key(&mut self) -> (Key, KeyLocation) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#[cfg(all(not(x11_platform), not(wayland_platform)))]
|
||||
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
||||
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{collections::VecDeque, env, fmt};
|
||||
@@ -24,10 +25,10 @@ use crate::{
|
||||
EventLoopWindowTarget as RootELW,
|
||||
},
|
||||
icon::Icon,
|
||||
keyboard::{Key, KeyCode},
|
||||
keyboard::{Key, PhysicalKey},
|
||||
platform::{
|
||||
modifier_supplement::KeyEventExtModifierSupplement, pump_events::PumpStatus,
|
||||
scancode::KeyCodeExtScancode,
|
||||
scancode::PhysicalKeyExtScancode,
|
||||
},
|
||||
window::{
|
||||
ActivationToken, CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme,
|
||||
@@ -39,6 +40,8 @@ pub use x11::XNotSupported;
|
||||
#[cfg(x11_platform)]
|
||||
use x11::{util::WindowType as XWindowType, X11Error, XConnection, XError};
|
||||
|
||||
pub(crate) use crate::cursor::OnlyCursorImage as PlatformCustomCursor;
|
||||
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
|
||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
|
||||
@@ -138,6 +141,43 @@ impl fmt::Display for OsError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum OwnedWindowHandle {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11rb::protocol::xproto::Window),
|
||||
#[cfg(wayland_platform)]
|
||||
Wayland,
|
||||
}
|
||||
|
||||
impl OwnedWindowHandle {
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub(crate) fn new_parent_window(handle: rwh_06::WindowHandle<'_>) -> Self {
|
||||
// TODO: Do we need to do something extra to extend the lifetime of
|
||||
// the window lives beyond the passed-in handle?
|
||||
match handle.as_raw() {
|
||||
#[cfg(x11_platform)]
|
||||
rwh_06::RawWindowHandle::Xlib(handle) => {
|
||||
Self::X(handle.window as x11rb::protocol::xproto::Window)
|
||||
}
|
||||
#[cfg(x11_platform)]
|
||||
rwh_06::RawWindowHandle::Xcb(handle) => Self::X(handle.window.get()),
|
||||
#[cfg(wayland_platform)]
|
||||
rwh_06::RawWindowHandle::Wayland(_handle) => {
|
||||
// Wayland does not currently support parent windows, but it
|
||||
// could support owned handles.
|
||||
Self::Wayland
|
||||
}
|
||||
#[cfg(not(x11_platform))]
|
||||
handle => panic!("invalid window handle {handle:?} on Wayland"),
|
||||
#[cfg(not(wayland_platform))]
|
||||
handle => panic!("invalid window handle {handle:?} on X11"),
|
||||
#[cfg(all(x11_platform, wayland_platform))]
|
||||
handle => panic!("invalid window handle {handle:?} on X11 or Wayland"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum Window {
|
||||
#[cfg(x11_platform)]
|
||||
X(x11::Window),
|
||||
@@ -423,6 +463,11 @@ impl Window {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_cursor_icon(cursor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_custom_cursor(cursor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(window) => window.set_cursor_grab(mode))
|
||||
@@ -656,13 +701,13 @@ impl KeyEventExtModifierSupplement for KeyEvent {
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyCodeExtScancode for KeyCode {
|
||||
fn from_scancode(scancode: u32) -> KeyCode {
|
||||
impl PhysicalKeyExtScancode for PhysicalKey {
|
||||
fn from_scancode(scancode: u32) -> PhysicalKey {
|
||||
common::keymap::scancode_to_keycode(scancode)
|
||||
}
|
||||
|
||||
fn to_scancode(self) -> Option<u32> {
|
||||
common::keymap::keycode_to_scancode(self)
|
||||
common::keymap::physicalkey_to_scancode(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,11 +796,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: Wayland first because of X11 could be present under wayland as well.
|
||||
// NOTE: Wayland first because of X11 could be present under Wayland as well. Empty
|
||||
// variables are also treated as not set.
|
||||
let backend = match (
|
||||
attributes.forced_backend,
|
||||
env::var("WAYLAND_DISPLAY").is_ok(),
|
||||
env::var("DISPLAY").is_ok(),
|
||||
env::var("WAYLAND_DISPLAY")
|
||||
.map(|var| !var.is_empty())
|
||||
.unwrap_or(false),
|
||||
env::var("DISPLAY")
|
||||
.map(|var| !var.is_empty())
|
||||
.unwrap_or(false),
|
||||
) {
|
||||
// User is forcing a backend.
|
||||
(Some(backend), _, _) => backend,
|
||||
@@ -827,6 +877,18 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_fd())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.send_event(event))
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::cell::{Cell, RefCell};
|
||||
use std::io::Result as IOResult;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -392,7 +393,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
self.with_state(|state| {
|
||||
let windows = state.windows.get_mut();
|
||||
let mut window = windows.get(&window_id).unwrap().lock().unwrap();
|
||||
window.resize(new_logical_size);
|
||||
window.request_inner_size(new_logical_size.into());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -589,6 +590,18 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
self.event_loop.as_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.event_loop.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopWindowTarget<T> {
|
||||
/// The event loop wakeup source.
|
||||
pub event_loop_awakener: calloop::ping::Ping,
|
||||
|
||||
@@ -19,6 +19,7 @@ use sctk::seat::SeatState;
|
||||
use sctk::shell::xdg::window::{Window, WindowConfigure, WindowHandler};
|
||||
use sctk::shell::xdg::XdgShell;
|
||||
use sctk::shell::WaylandSurface;
|
||||
use sctk::shm::slot::SlotPool;
|
||||
use sctk::shm::{Shm, ShmHandler};
|
||||
use sctk::subcompositor::SubcompositorState;
|
||||
|
||||
@@ -50,7 +51,7 @@ pub struct WinitState {
|
||||
pub compositor_state: Arc<CompositorState>,
|
||||
|
||||
/// The state of the subcompositor.
|
||||
pub subcompositor_state: Arc<SubcompositorState>,
|
||||
pub subcompositor_state: Option<Arc<SubcompositorState>>,
|
||||
|
||||
/// The seat state responsible for all sorts of input.
|
||||
pub seat_state: SeatState,
|
||||
@@ -58,6 +59,9 @@ pub struct WinitState {
|
||||
/// The shm for software buffers, such as cursors.
|
||||
pub shm: Shm,
|
||||
|
||||
/// The pool where custom cursors are allocated.
|
||||
pub custom_cursor_pool: Arc<Mutex<SlotPool>>,
|
||||
|
||||
/// The XDG shell that is used for widnows.
|
||||
pub xdg_shell: XdgShell,
|
||||
|
||||
@@ -124,12 +128,17 @@ impl WinitState {
|
||||
let registry_state = RegistryState::new(globals);
|
||||
let compositor_state =
|
||||
CompositorState::bind(globals, queue_handle).map_err(WaylandError::Bind)?;
|
||||
let subcompositor_state = SubcompositorState::bind(
|
||||
let subcompositor_state = match SubcompositorState::bind(
|
||||
compositor_state.wl_compositor().clone(),
|
||||
globals,
|
||||
queue_handle,
|
||||
)
|
||||
.map_err(WaylandError::Bind)?;
|
||||
) {
|
||||
Ok(c) => Some(c),
|
||||
Err(e) => {
|
||||
warn!("Subcompositor protocol not available, ignoring CSD: {e:?}");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let output_state = OutputState::new(globals, queue_handle);
|
||||
let monitors = output_state.outputs().map(MonitorHandle::new).collect();
|
||||
@@ -148,13 +157,17 @@ impl WinitState {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let shm = Shm::bind(globals, queue_handle).map_err(WaylandError::Bind)?;
|
||||
let custom_cursor_pool = Arc::new(Mutex::new(SlotPool::new(2, &shm).unwrap()));
|
||||
|
||||
Ok(Self {
|
||||
registry_state,
|
||||
compositor_state: Arc::new(compositor_state),
|
||||
subcompositor_state: Arc::new(subcompositor_state),
|
||||
subcompositor_state: subcompositor_state.map(Arc::new),
|
||||
output_state,
|
||||
seat_state,
|
||||
shm: Shm::bind(globals, queue_handle).map_err(WaylandError::Bind)?,
|
||||
shm,
|
||||
custom_cursor_pool,
|
||||
|
||||
xdg_shell: XdgShell::bind(globals, queue_handle).map_err(WaylandError::Bind)?,
|
||||
xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(),
|
||||
@@ -294,7 +307,11 @@ impl WindowHandler for WinitState {
|
||||
&mut self.events_sink,
|
||||
);
|
||||
|
||||
self.window_compositor_updates[pos].size = Some(new_size);
|
||||
// NOTE: Only update when the value is `Some` to not override consequent configures with
|
||||
// the same sizes.
|
||||
if new_size.is_some() {
|
||||
self.window_compositor_updates[pos].size = new_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
56
src/platform_impl/linux/wayland/types/cursor.rs
Normal file
56
src/platform_impl/linux/wayland/types/cursor.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use cursor_icon::CursorIcon;
|
||||
|
||||
use sctk::reexports::client::protocol::wl_shm::Format;
|
||||
use sctk::shm::slot::{Buffer, SlotPool};
|
||||
|
||||
use crate::cursor::CursorImage;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SelectedCursor {
|
||||
Named(CursorIcon),
|
||||
Custom(CustomCursor),
|
||||
}
|
||||
|
||||
impl Default for SelectedCursor {
|
||||
fn default() -> Self {
|
||||
Self::Named(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CustomCursor {
|
||||
pub buffer: Buffer,
|
||||
pub w: i32,
|
||||
pub h: i32,
|
||||
pub hotspot_x: i32,
|
||||
pub hotspot_y: i32,
|
||||
}
|
||||
|
||||
impl CustomCursor {
|
||||
pub(crate) fn new(pool: &mut SlotPool, image: &CursorImage) -> Self {
|
||||
let (buffer, canvas) = pool
|
||||
.create_buffer(
|
||||
image.width as i32,
|
||||
image.height as i32,
|
||||
4 * (image.width as i32),
|
||||
Format::Argb8888,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for (canvas_chunk, rgba_chunk) in canvas.chunks_exact_mut(4).zip(image.rgba.chunks_exact(4))
|
||||
{
|
||||
canvas_chunk[0] = rgba_chunk[2];
|
||||
canvas_chunk[1] = rgba_chunk[1];
|
||||
canvas_chunk[2] = rgba_chunk[0];
|
||||
canvas_chunk[3] = rgba_chunk[3];
|
||||
}
|
||||
|
||||
CustomCursor {
|
||||
buffer,
|
||||
w: image.width as i32,
|
||||
h: image.height as i32,
|
||||
hotspot_x: image.hotspot_x as i32,
|
||||
hotspot_y: image.hotspot_y as i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Wayland protocol implementation boilerplate.
|
||||
|
||||
pub mod cursor;
|
||||
pub mod kwin_blur;
|
||||
pub mod wp_fractional_scaling;
|
||||
pub mod wp_viewporter;
|
||||
|
||||
@@ -20,8 +20,8 @@ use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event::{Ime, WindowEvent};
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::platform_impl::{
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
|
||||
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor,
|
||||
PlatformIcon, PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
||||
};
|
||||
use crate::window::{
|
||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
@@ -97,11 +97,9 @@ impl Window {
|
||||
.map(|activation_state| activation_state.global().clone());
|
||||
let display = event_loop_window_target.connection.display();
|
||||
|
||||
// XXX The initial scale factor must be 1, but it might cause sizing issues on HiDPI.
|
||||
let size: LogicalSize<u32> = attributes
|
||||
let size: Size = attributes
|
||||
.inner_size
|
||||
.map(|size| size.to_logical::<u32>(1.))
|
||||
.unwrap_or((800, 600).into());
|
||||
.unwrap_or(LogicalSize::new(800., 600.).into());
|
||||
|
||||
// We prefer server side decorations, however to not have decorations we ask for client
|
||||
// side decorations instead.
|
||||
@@ -141,7 +139,8 @@ impl Window {
|
||||
// Set the window title.
|
||||
window_state.set_title(attributes.title);
|
||||
|
||||
// Set the min and max sizes.
|
||||
// Set the min and max sizes. We must set the hints upon creating a window, so
|
||||
// we use the default `1.` scaling...
|
||||
let min_size = attributes.min_inner_size.map(|size| size.to_logical(1.));
|
||||
let max_size = attributes.max_inner_size.map(|size| size.to_logical(1.));
|
||||
window_state.set_min_inner_size(min_size);
|
||||
@@ -151,7 +150,7 @@ impl Window {
|
||||
window_state.set_resizable(attributes.resizable);
|
||||
|
||||
// Set startup mode.
|
||||
match attributes.fullscreen.map(Into::into) {
|
||||
match attributes.fullscreen.0.map(Into::into) {
|
||||
Some(Fullscreen::Exclusive(_)) => {
|
||||
warn!("`Fullscreen::Exclusive` is ignored on Wayland");
|
||||
}
|
||||
@@ -315,12 +314,9 @@ impl Window {
|
||||
#[inline]
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
let scale_factor = window_state.scale_factor();
|
||||
window_state.resize(size.to_logical::<u32>(scale_factor));
|
||||
|
||||
let new_size = window_state.request_inner_size(size);
|
||||
self.request_redraw();
|
||||
|
||||
Some(window_state.inner_size().to_physical(scale_factor))
|
||||
Some(new_size)
|
||||
}
|
||||
|
||||
/// Set the minimum inner size for the window.
|
||||
@@ -510,6 +506,14 @@ impl Window {
|
||||
self.window_state.lock().unwrap().set_cursor(cursor);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
|
||||
self.window_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_custom_cursor(&cursor.0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
self.window_state
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//! The state of the window, which is shared with the event-loop.
|
||||
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
use log::{info, warn};
|
||||
@@ -19,20 +18,23 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
|
||||
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
||||
use sctk::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge as XdgResizeEdge;
|
||||
|
||||
use sctk::compositor::{CompositorState, Region};
|
||||
use sctk::seat::pointer::ThemedPointer;
|
||||
use sctk::compositor::{CompositorState, Region, SurfaceData, SurfaceDataExt};
|
||||
use sctk::seat::pointer::{PointerDataExt, ThemedPointer};
|
||||
use sctk::shell::xdg::window::{DecorationMode, Window, WindowConfigure};
|
||||
use sctk::shell::xdg::XdgSurface;
|
||||
use sctk::shell::WaylandSurface;
|
||||
use sctk::shm::slot::SlotPool;
|
||||
use sctk::shm::Shm;
|
||||
use sctk::subcompositor::SubcompositorState;
|
||||
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
||||
|
||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||
use crate::cursor::CursorImage;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
|
||||
use crate::error::{ExternalError, NotSupportedError};
|
||||
use crate::event::WindowEvent;
|
||||
use crate::platform_impl::wayland::event_loop::sink::EventSink;
|
||||
use crate::platform_impl::wayland::make_wid;
|
||||
use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor};
|
||||
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
|
||||
use crate::platform_impl::WindowId;
|
||||
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};
|
||||
@@ -55,23 +57,22 @@ pub struct WindowState {
|
||||
/// The connection to Wayland server.
|
||||
pub connection: Connection,
|
||||
|
||||
/// The underlying SCTK window.
|
||||
pub window: ManuallyDrop<Window>,
|
||||
|
||||
/// The window frame, which is created from the configure request.
|
||||
frame: Option<WinitFrame>,
|
||||
|
||||
/// The `Shm` to set cursor.
|
||||
pub shm: WlShm,
|
||||
|
||||
// A shared pool where to allocate custom cursors.
|
||||
custom_cursor_pool: Arc<Mutex<SlotPool>>,
|
||||
|
||||
/// The last received configure.
|
||||
pub last_configure: Option<WindowConfigure>,
|
||||
|
||||
/// The pointers observed on the window.
|
||||
pub pointers: Vec<Weak<ThemedPointer<WinitPointerData>>>,
|
||||
|
||||
/// Cursor icon.
|
||||
pub cursor_icon: CursorIcon,
|
||||
selected_cursor: SelectedCursor,
|
||||
|
||||
/// Wether the cursor is visible.
|
||||
pub cursor_visible: bool,
|
||||
@@ -133,6 +134,10 @@ pub struct WindowState {
|
||||
/// sends `None` for the new size in the configure.
|
||||
stateless_size: LogicalSize<u32>,
|
||||
|
||||
/// Initial window size provided by the user. Removed on the first
|
||||
/// configure.
|
||||
initial_size: Option<Size>,
|
||||
|
||||
/// The state of the frame callback.
|
||||
frame_callback_state: FrameCallbackState,
|
||||
|
||||
@@ -145,6 +150,9 @@ pub struct WindowState {
|
||||
///
|
||||
/// The value is the serial of the event triggered moved.
|
||||
has_pending_move: Option<u32>,
|
||||
|
||||
/// The underlying SCTK window.
|
||||
pub window: Window,
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
@@ -153,7 +161,7 @@ impl WindowState {
|
||||
connection: Connection,
|
||||
queue_handle: &QueueHandle<WinitState>,
|
||||
winit_state: &WinitState,
|
||||
size: LogicalSize<u32>,
|
||||
initial_size: Size,
|
||||
window: Window,
|
||||
theme: Option<Theme>,
|
||||
) -> Self {
|
||||
@@ -175,7 +183,7 @@ impl WindowState {
|
||||
connection,
|
||||
csd_fails: false,
|
||||
cursor_grab_mode: GrabState::new(),
|
||||
cursor_icon: CursorIcon::Default,
|
||||
selected_cursor: Default::default(),
|
||||
cursor_visible: true,
|
||||
decorate: true,
|
||||
fractional_scale,
|
||||
@@ -194,14 +202,16 @@ impl WindowState {
|
||||
resizable: true,
|
||||
scale_factor: 1.,
|
||||
shm: winit_state.shm.wl_shm().clone(),
|
||||
size,
|
||||
stateless_size: size,
|
||||
custom_cursor_pool: winit_state.custom_cursor_pool.clone(),
|
||||
size: initial_size.to_logical(1.),
|
||||
stateless_size: initial_size.to_logical(1.),
|
||||
initial_size: Some(initial_size),
|
||||
text_inputs: Vec::new(),
|
||||
theme,
|
||||
title: String::default(),
|
||||
transparent: false,
|
||||
viewport,
|
||||
window: ManuallyDrop::new(window),
|
||||
window,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,16 +260,27 @@ impl WindowState {
|
||||
&mut self,
|
||||
configure: WindowConfigure,
|
||||
shm: &Shm,
|
||||
subcompositor: &Arc<SubcompositorState>,
|
||||
subcompositor: &Option<Arc<SubcompositorState>>,
|
||||
event_sink: &mut EventSink,
|
||||
) -> LogicalSize<u32> {
|
||||
if configure.decoration_mode == DecorationMode::Client
|
||||
&& self.frame.is_none()
|
||||
&& !self.csd_fails
|
||||
{
|
||||
) -> Option<LogicalSize<u32>> {
|
||||
// NOTE: when using fractional scaling or wl_compositor@v6 the scaling
|
||||
// should be delivered before the first configure, thus apply it to
|
||||
// properly scale the physical sizes provided by the users.
|
||||
if let Some(initial_size) = self.initial_size.take() {
|
||||
self.size = initial_size.to_logical(self.scale_factor());
|
||||
self.stateless_size = self.size;
|
||||
}
|
||||
|
||||
if let Some(subcompositor) = subcompositor.as_ref().filter(|_| {
|
||||
configure.decoration_mode == DecorationMode::Client
|
||||
&& self.frame.is_none()
|
||||
&& !self.csd_fails
|
||||
}) {
|
||||
match WinitFrame::new(
|
||||
&*self.window,
|
||||
&self.window,
|
||||
shm,
|
||||
#[cfg(feature = "sctk-adwaita")]
|
||||
self.compositor.clone(),
|
||||
subcompositor.clone(),
|
||||
self.queue_handle.clone(),
|
||||
#[cfg(feature = "sctk-adwaita")]
|
||||
@@ -297,37 +318,90 @@ impl WindowState {
|
||||
event_sink.push_window_event(WindowEvent::Occluded(occluded), window_id);
|
||||
}
|
||||
|
||||
let new_size = if let Some(frame) = self.frame.as_mut() {
|
||||
let (mut new_size, constrain) = if let Some(frame) = self.frame.as_mut() {
|
||||
// Configure the window states.
|
||||
frame.update_state(configure.state);
|
||||
|
||||
match configure.new_size {
|
||||
(Some(width), Some(height)) => {
|
||||
let (width, height) = frame.subtract_borders(width, height);
|
||||
(
|
||||
width.map(|w| w.get()).unwrap_or(1),
|
||||
height.map(|h| h.get()).unwrap_or(1),
|
||||
)
|
||||
.into()
|
||||
let width = width.map(|w| w.get()).unwrap_or(1);
|
||||
let height = height.map(|h| h.get()).unwrap_or(1);
|
||||
((width, height).into(), false)
|
||||
}
|
||||
(_, _) if stateless => self.stateless_size,
|
||||
_ => self.size,
|
||||
(_, _) if stateless => (self.stateless_size, true),
|
||||
_ => (self.size, true),
|
||||
}
|
||||
} else {
|
||||
match configure.new_size {
|
||||
(Some(width), Some(height)) => (width.get(), height.get()).into(),
|
||||
_ if stateless => self.stateless_size,
|
||||
_ => self.size,
|
||||
(Some(width), Some(height)) => ((width.get(), height.get()).into(), false),
|
||||
_ if stateless => (self.stateless_size, true),
|
||||
_ => (self.size, true),
|
||||
}
|
||||
};
|
||||
|
||||
// XXX Set the configure before doing a resize.
|
||||
// Apply configure bounds only when compositor let the user decide what size to pick.
|
||||
if constrain {
|
||||
let bounds = self.inner_size_bounds(&configure);
|
||||
new_size.width = bounds
|
||||
.0
|
||||
.map(|bound_w| new_size.width.min(bound_w.get()))
|
||||
.unwrap_or(new_size.width);
|
||||
new_size.height = bounds
|
||||
.1
|
||||
.map(|bound_h| new_size.height.min(bound_h.get()))
|
||||
.unwrap_or(new_size.height);
|
||||
}
|
||||
|
||||
let new_state = configure.state;
|
||||
let old_state = self
|
||||
.last_configure
|
||||
.as_ref()
|
||||
.map(|configure| configure.state);
|
||||
|
||||
let state_change_requires_resize = old_state
|
||||
.map(|old_state| {
|
||||
!old_state
|
||||
.symmetric_difference(new_state)
|
||||
.difference(XdgWindowState::ACTIVATED | XdgWindowState::SUSPENDED)
|
||||
.is_empty()
|
||||
})
|
||||
// NOTE: `None` is present for the initial configure, thus we must always resize.
|
||||
.unwrap_or(true);
|
||||
|
||||
// NOTE: Set the configure before doing a resize, since we query it during it.
|
||||
self.last_configure = Some(configure);
|
||||
|
||||
// XXX Update the new size right away.
|
||||
self.resize(new_size);
|
||||
if state_change_requires_resize || new_size != self.inner_size() {
|
||||
self.resize(new_size);
|
||||
Some(new_size)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
new_size
|
||||
/// Compute the bounds for the inner size of the surface.
|
||||
fn inner_size_bounds(
|
||||
&self,
|
||||
configure: &WindowConfigure,
|
||||
) -> (Option<NonZeroU32>, Option<NonZeroU32>) {
|
||||
let configure_bounds = match configure.suggested_bounds {
|
||||
Some((width, height)) => (NonZeroU32::new(width), NonZeroU32::new(height)),
|
||||
None => (None, None),
|
||||
};
|
||||
|
||||
if let Some(frame) = self.frame.as_ref() {
|
||||
let (width, height) = frame.subtract_borders(
|
||||
configure_bounds.0.unwrap_or(NonZeroU32::new(1).unwrap()),
|
||||
configure_bounds.1.unwrap_or(NonZeroU32::new(1).unwrap()),
|
||||
);
|
||||
(
|
||||
configure_bounds.0.and(width),
|
||||
configure_bounds.1.and(height),
|
||||
)
|
||||
} else {
|
||||
configure_bounds
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -537,7 +611,7 @@ impl WindowState {
|
||||
/// Refresh the decorations frame if it's present returning whether the client should redraw.
|
||||
pub fn refresh_frame(&mut self) -> bool {
|
||||
if let Some(frame) = self.frame.as_mut() {
|
||||
if frame.is_dirty() {
|
||||
if !frame.is_hidden() && frame.is_dirty() {
|
||||
return frame.draw();
|
||||
}
|
||||
}
|
||||
@@ -548,7 +622,10 @@ impl WindowState {
|
||||
/// Reload the cursor style on the given window.
|
||||
pub fn reload_cursor_style(&mut self) {
|
||||
if self.cursor_visible {
|
||||
self.set_cursor(self.cursor_icon);
|
||||
match &self.selected_cursor {
|
||||
SelectedCursor::Named(icon) => self.set_cursor(*icon),
|
||||
SelectedCursor::Custom(cursor) => self.apply_custom_cursor(cursor),
|
||||
}
|
||||
} else {
|
||||
self.set_cursor_visible(self.cursor_visible);
|
||||
}
|
||||
@@ -568,8 +645,22 @@ impl WindowState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to resize the window when the user can do so.
|
||||
pub fn request_inner_size(&mut self, inner_size: Size) -> PhysicalSize<u32> {
|
||||
if self
|
||||
.last_configure
|
||||
.as_ref()
|
||||
.map(Self::is_stateless)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
self.resize(inner_size.to_logical(self.scale_factor()))
|
||||
}
|
||||
|
||||
self.inner_size().to_physical(self.scale_factor())
|
||||
}
|
||||
|
||||
/// Resize the window to the new inner size.
|
||||
pub fn resize(&mut self, inner_size: LogicalSize<u32>) {
|
||||
fn resize(&mut self, inner_size: LogicalSize<u32>) {
|
||||
self.size = inner_size;
|
||||
|
||||
// Update the stateless size.
|
||||
@@ -620,10 +711,8 @@ impl WindowState {
|
||||
}
|
||||
|
||||
/// Set the cursor icon.
|
||||
///
|
||||
/// Providing `None` will hide the cursor.
|
||||
pub fn set_cursor(&mut self, cursor_icon: CursorIcon) {
|
||||
self.cursor_icon = cursor_icon;
|
||||
self.selected_cursor = SelectedCursor::Named(cursor_icon);
|
||||
|
||||
if !self.cursor_visible {
|
||||
return;
|
||||
@@ -636,6 +725,54 @@ impl WindowState {
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the custom cursor icon.
|
||||
pub(crate) fn set_custom_cursor(&mut self, cursor: &CursorImage) {
|
||||
let cursor = {
|
||||
let mut pool = self.custom_cursor_pool.lock().unwrap();
|
||||
CustomCursor::new(&mut pool, cursor)
|
||||
};
|
||||
|
||||
if self.cursor_visible {
|
||||
self.apply_custom_cursor(&cursor);
|
||||
}
|
||||
|
||||
self.selected_cursor = SelectedCursor::Custom(cursor);
|
||||
}
|
||||
|
||||
fn apply_custom_cursor(&self, cursor: &CustomCursor) {
|
||||
self.apply_on_poiner(|pointer, _| {
|
||||
let surface = pointer.surface();
|
||||
|
||||
let scale = surface
|
||||
.data::<SurfaceData>()
|
||||
.unwrap()
|
||||
.surface_data()
|
||||
.scale_factor();
|
||||
|
||||
surface.set_buffer_scale(scale);
|
||||
surface.attach(Some(cursor.buffer.wl_buffer()), 0, 0);
|
||||
if surface.version() >= 4 {
|
||||
surface.damage_buffer(0, 0, cursor.w, cursor.h);
|
||||
} else {
|
||||
surface.damage(0, 0, cursor.w / scale, cursor.h / scale);
|
||||
}
|
||||
surface.commit();
|
||||
|
||||
let serial = pointer
|
||||
.pointer()
|
||||
.data::<WinitPointerData>()
|
||||
.and_then(|data| data.pointer_data().latest_enter_serial())
|
||||
.unwrap();
|
||||
|
||||
pointer.pointer().set_cursor(
|
||||
serial,
|
||||
Some(surface),
|
||||
cursor.hotspot_x / scale,
|
||||
cursor.hotspot_y / scale,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Set maximum inner window size.
|
||||
pub fn set_min_inner_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
// Ensure that the window has the right minimum size.
|
||||
@@ -770,7 +907,10 @@ impl WindowState {
|
||||
self.cursor_visible = cursor_visible;
|
||||
|
||||
if self.cursor_visible {
|
||||
self.set_cursor(self.cursor_icon);
|
||||
match &self.selected_cursor {
|
||||
SelectedCursor::Named(icon) => self.set_cursor(*icon),
|
||||
SelectedCursor::Custom(cursor) => self.apply_custom_cursor(cursor),
|
||||
}
|
||||
} else {
|
||||
for pointer in self.pointers.iter().filter_map(|pointer| pointer.upgrade()) {
|
||||
let latest_enter_serial = pointer.pointer().winit_data().latest_enter_serial();
|
||||
@@ -843,7 +983,7 @@ impl WindowState {
|
||||
|
||||
/// Set the IME position.
|
||||
pub fn set_ime_cursor_area(&self, position: LogicalPosition<u32>, size: LogicalSize<u32>) {
|
||||
// XXX This won't fly unless user will have a way to request IME window per seat, since
|
||||
// FIXME: This won't fly unless user will have a way to request IME window per seat, since
|
||||
// the ime windows will be overlapping, but winit doesn't expose API to specify for
|
||||
// which seat we're setting IME position.
|
||||
let (x, y) = (position.x as i32, position.y as i32);
|
||||
@@ -874,7 +1014,7 @@ impl WindowState {
|
||||
pub fn set_scale_factor(&mut self, scale_factor: f64) {
|
||||
self.scale_factor = scale_factor;
|
||||
|
||||
// XXX when fractional scaling is not used update the buffer scale.
|
||||
// NOTE: When fractional scaling is not used update the buffer scale.
|
||||
if self.fractional_scale.is_none() {
|
||||
let _ = self.window.set_buffer_scale(self.scale_factor as _);
|
||||
}
|
||||
@@ -959,13 +1099,6 @@ impl WindowState {
|
||||
|
||||
impl Drop for WindowState {
|
||||
fn drop(&mut self) {
|
||||
let surface = self.window.wl_surface().clone();
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut self.window);
|
||||
}
|
||||
|
||||
// Cleanup objects.
|
||||
|
||||
if let Some(blur) = self.blur.take() {
|
||||
blur.release();
|
||||
}
|
||||
@@ -978,7 +1111,8 @@ impl Drop for WindowState {
|
||||
viewport.destroy();
|
||||
}
|
||||
|
||||
surface.destroy();
|
||||
// NOTE: the wl_surface used by the window is being cleaned up when
|
||||
// dropping SCTK `Window`.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1028,7 +1162,7 @@ impl From<ResizeDirection> for XdgResizeEdge {
|
||||
}
|
||||
}
|
||||
|
||||
// XXX rust doesn't allow from `Option`.
|
||||
// NOTE: Rust doesn't allow `From<Option<Theme>>`.
|
||||
#[cfg(feature = "sctk-adwaita")]
|
||||
fn into_sctk_adwaita_config(theme: Option<Theme>) -> sctk_adwaita::FrameConfig {
|
||||
match theme {
|
||||
|
||||
@@ -484,6 +484,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
}
|
||||
|
||||
let mut shared_state_lock = window.shared_state_lock();
|
||||
let hittest = shared_state_lock.cursor_hittest;
|
||||
|
||||
// This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
|
||||
// doesn't need this, but Xfwm does. The hack should not be run on other WMs, since tiling
|
||||
@@ -501,6 +502,11 @@ impl<T: 'static> EventProcessor<T> {
|
||||
// Unlock shared state to prevent deadlock in callback below
|
||||
drop(shared_state_lock);
|
||||
|
||||
// Reload hittest.
|
||||
if hittest.unwrap_or(false) {
|
||||
let _ = window.set_cursor_hittest(true);
|
||||
}
|
||||
|
||||
if resized {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
@@ -564,6 +570,14 @@ impl<T: 'static> EventProcessor<T> {
|
||||
event: WindowEvent::Destroyed,
|
||||
});
|
||||
}
|
||||
ffi::PropertyNotify => {
|
||||
let xev: &ffi::XPropertyEvent = xev.as_ref();
|
||||
let atom = xev.atom as xproto::Atom;
|
||||
|
||||
if atom == xproto::Atom::from(xproto::AtomEnum::RESOURCE_MANAGER) {
|
||||
self.process_dpi_change(&mut callback);
|
||||
}
|
||||
}
|
||||
|
||||
ffi::VisibilityNotify => {
|
||||
let xev: &ffi::XVisibilityEvent = xev.as_ref();
|
||||
@@ -1201,7 +1215,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
if keycode < KEYCODE_OFFSET as u32 {
|
||||
return;
|
||||
}
|
||||
let physical_key = keymap::raw_keycode_to_keycode(keycode);
|
||||
let physical_key = keymap::raw_keycode_to_physicalkey(keycode);
|
||||
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
@@ -1300,72 +1314,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
}
|
||||
}
|
||||
if event_type == self.randr_event_offset as c_int {
|
||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||
let prev_list = wt.xconn.invalidate_cached_monitor_list();
|
||||
if let Some(prev_list) = prev_list {
|
||||
let new_list = wt
|
||||
.xconn
|
||||
.available_monitors()
|
||||
.expect("Failed to get monitor list");
|
||||
for new_monitor in new_list {
|
||||
// Previous list may be empty, in case of disconnecting and
|
||||
// reconnecting the only one monitor. We still need to emit events in
|
||||
// this case.
|
||||
let maybe_prev_scale_factor = prev_list
|
||||
.iter()
|
||||
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
||||
.map(|prev_monitor| prev_monitor.scale_factor);
|
||||
if Some(new_monitor.scale_factor) != maybe_prev_scale_factor {
|
||||
for (window_id, window) in wt.windows.borrow().iter() {
|
||||
if let Some(window) = window.upgrade() {
|
||||
// Check if the window is on this monitor
|
||||
let monitor =
|
||||
window.shared_state_lock().last_monitor.clone();
|
||||
if monitor.name == new_monitor.name {
|
||||
let (width, height) = window.inner_size_physical();
|
||||
let (new_width, new_height) = window.adjust_for_dpi(
|
||||
// If we couldn't determine the previous scale
|
||||
// factor (e.g., because all monitors were closed
|
||||
// before), just pick whatever the current monitor
|
||||
// has set as a baseline.
|
||||
maybe_prev_scale_factor
|
||||
.unwrap_or(monitor.scale_factor),
|
||||
new_monitor.scale_factor,
|
||||
width,
|
||||
height,
|
||||
&window.shared_state_lock(),
|
||||
);
|
||||
|
||||
let window_id = crate::window::WindowId(*window_id);
|
||||
let old_inner_size = PhysicalSize::new(width, height);
|
||||
let inner_size = Arc::new(Mutex::new(
|
||||
PhysicalSize::new(new_width, new_height),
|
||||
));
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor: new_monitor.scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(
|
||||
Arc::downgrade(&inner_size),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
let new_inner_size = *inner_size.lock().unwrap();
|
||||
drop(inner_size);
|
||||
|
||||
if new_inner_size != old_inner_size {
|
||||
let (new_width, new_height) = new_inner_size.into();
|
||||
window.request_inner_size_physical(
|
||||
new_width, new_height,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.process_dpi_change(&mut callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1458,6 +1407,45 @@ impl<T: 'static> EventProcessor<T> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn process_dpi_change<F>(&self, callback: &mut F)
|
||||
where
|
||||
F: FnMut(Event<T>),
|
||||
{
|
||||
let wt = get_xtarget(&self.target);
|
||||
|
||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||
let prev_list = {
|
||||
let prev_list = wt.xconn.invalidate_cached_monitor_list();
|
||||
match prev_list {
|
||||
Some(prev_list) => prev_list,
|
||||
None => return,
|
||||
}
|
||||
};
|
||||
|
||||
let new_list = wt
|
||||
.xconn
|
||||
.available_monitors()
|
||||
.expect("Failed to get monitor list");
|
||||
for new_monitor in new_list {
|
||||
// Previous list may be empty, in case of disconnecting and
|
||||
// reconnecting the only one monitor. We still need to emit events in
|
||||
// this case.
|
||||
let maybe_prev_scale_factor = prev_list
|
||||
.iter()
|
||||
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
||||
.map(|prev_monitor| prev_monitor.scale_factor);
|
||||
if Some(new_monitor.scale_factor) != maybe_prev_scale_factor {
|
||||
for window in wt.windows.borrow().iter().filter_map(|(_, w)| w.upgrade()) {
|
||||
window.refresh_dpi_for_monitor(
|
||||
&new_monitor,
|
||||
maybe_prev_scale_factor,
|
||||
&mut *callback,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_first_touch(first: &mut Option<u64>, num: &mut u32, id: u64, phase: TouchPhase) -> bool {
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
use x11_dl::xmd::CARD32;
|
||||
pub use x11_dl::{
|
||||
error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*,
|
||||
};
|
||||
|
||||
// Isn't defined by x11_dl
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const IconicState: CARD32 = 3;
|
||||
pub use x11_dl::{error::OpenError, xcursor::*, xinput2::*, xlib::*, xlib_xcb::*};
|
||||
|
||||
@@ -32,7 +32,7 @@ use std::{
|
||||
ops::Deref,
|
||||
os::{
|
||||
raw::*,
|
||||
unix::io::{AsRawFd, BorrowedFd},
|
||||
unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd},
|
||||
},
|
||||
ptr,
|
||||
rc::Rc,
|
||||
@@ -81,6 +81,7 @@ use crate::{
|
||||
// Xinput constants not defined in x11rb
|
||||
const ALL_DEVICES: u16 = 0;
|
||||
const ALL_MASTER_DEVICES: u16 = 1;
|
||||
const ICONIC_STATE: u32 = 3;
|
||||
|
||||
type X11Source = Generic<BorrowedFd<'static>>;
|
||||
|
||||
@@ -658,6 +659,18 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
self.event_loop.as_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.event_loop.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_xtarget<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> {
|
||||
match target.p {
|
||||
super::EventLoopWindowTarget::X(ref target) => target,
|
||||
@@ -1116,3 +1129,9 @@ impl Device {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the raw X11 representation for a 32-bit floating point to a double.
|
||||
#[inline]
|
||||
fn xinput_fp1616_to_float(fp: xinput::Fp1616) -> f64 {
|
||||
(fp as f64) / ((1 << 16) as f64)
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ impl XConnection {
|
||||
return Ok(MonitorHandle::dummy());
|
||||
}
|
||||
|
||||
let default = monitors.get(0).unwrap();
|
||||
let default = monitors.first().unwrap();
|
||||
|
||||
let window_rect = match window_rect {
|
||||
Some(rect) => rect,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::ffi::CString;
|
||||
use std::{ffi::CString, iter, slice, sync::Arc};
|
||||
|
||||
use x11rb::connection::Connection;
|
||||
|
||||
use crate::window::CursorIcon;
|
||||
use crate::{cursor::CursorImage, window::CursorIcon};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -19,6 +19,11 @@ impl XConnection {
|
||||
.expect("Failed to set cursor");
|
||||
}
|
||||
|
||||
pub(crate) fn set_custom_cursor(&self, window: xproto::Window, cursor: &CustomCursor) {
|
||||
self.update_cursor(window, cursor.inner.cursor)
|
||||
.expect("Failed to set cursor");
|
||||
}
|
||||
|
||||
fn create_empty_cursor(&self) -> ffi::Cursor {
|
||||
let data = 0;
|
||||
let pixmap = unsafe {
|
||||
@@ -56,10 +61,22 @@ impl XConnection {
|
||||
None => return self.create_empty_cursor(),
|
||||
};
|
||||
|
||||
let name = CString::new(cursor.name()).unwrap();
|
||||
unsafe {
|
||||
(self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char)
|
||||
let mut xcursor = 0;
|
||||
for &name in iter::once(&cursor.name()).chain(cursor.alt_names().iter()) {
|
||||
let name = CString::new(name).unwrap();
|
||||
xcursor = unsafe {
|
||||
(self.xcursor.XcursorLibraryLoadCursor)(
|
||||
self.display,
|
||||
name.as_ptr() as *const c_char,
|
||||
)
|
||||
};
|
||||
|
||||
if xcursor != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
xcursor
|
||||
}
|
||||
|
||||
fn update_cursor(&self, window: xproto::Window, cursor: ffi::Cursor) -> Result<(), X11Error> {
|
||||
@@ -74,3 +91,74 @@ impl XConnection {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SelectedCursor {
|
||||
Custom(CustomCursor),
|
||||
Named(CursorIcon),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CustomCursor {
|
||||
inner: Arc<CustomCursorInner>,
|
||||
}
|
||||
|
||||
impl CustomCursor {
|
||||
pub(crate) unsafe fn new(xconn: &Arc<XConnection>, image: &CursorImage) -> Self {
|
||||
unsafe {
|
||||
let ximage =
|
||||
(xconn.xcursor.XcursorImageCreate)(image.width as i32, image.height as i32);
|
||||
if ximage.is_null() {
|
||||
panic!("failed to allocate cursor image");
|
||||
}
|
||||
(*ximage).xhot = image.hotspot_x as u32;
|
||||
(*ximage).yhot = image.hotspot_y as u32;
|
||||
(*ximage).delay = 0;
|
||||
|
||||
let dst = slice::from_raw_parts_mut((*ximage).pixels, image.rgba.len() / 4);
|
||||
for (dst, chunk) in dst.iter_mut().zip(image.rgba.chunks_exact(4)) {
|
||||
*dst = (chunk[0] as u32) << 16
|
||||
| (chunk[1] as u32) << 8
|
||||
| (chunk[2] as u32)
|
||||
| (chunk[3] as u32) << 24;
|
||||
}
|
||||
|
||||
let cursor = (xconn.xcursor.XcursorImageLoadCursor)(xconn.display, ximage);
|
||||
(xconn.xcursor.XcursorImageDestroy)(ximage);
|
||||
Self {
|
||||
inner: Arc::new(CustomCursorInner {
|
||||
xconn: xconn.clone(),
|
||||
cursor,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CustomCursorInner {
|
||||
xconn: Arc<XConnection>,
|
||||
cursor: ffi::Cursor,
|
||||
}
|
||||
|
||||
impl Drop for CustomCursorInner {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFreeCursor)(self.xconn.display, self.cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for CustomCursorInner {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cursor == other.cursor
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for CustomCursorInner {}
|
||||
|
||||
impl Default for SelectedCursor {
|
||||
fn default() -> Self {
|
||||
SelectedCursor::Named(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,7 @@ mod randr;
|
||||
mod window_property;
|
||||
mod wm;
|
||||
|
||||
pub use self::{
|
||||
client_msg::*, geometry::*, hint::*, icon::*, input::*, randr::*, window_property::*, wm::*,
|
||||
};
|
||||
pub use self::{cursor::*, geometry::*, hint::*, input::*, window_property::*, wm::*};
|
||||
|
||||
use std::{
|
||||
mem::{self, MaybeUninit},
|
||||
|
||||
@@ -38,7 +38,7 @@ impl XConnection {
|
||||
// Retrieve DPI from Xft.dpi property
|
||||
pub fn get_xft_dpi(&self) -> Option<f64> {
|
||||
self.database()
|
||||
.get_string("Xfi.dpi", "")
|
||||
.get_string("Xft.dpi", "")
|
||||
.and_then(|s| f64::from_str(s).ok())
|
||||
}
|
||||
pub fn get_output_info(
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::{
|
||||
sync::{Arc, Mutex, MutexGuard},
|
||||
};
|
||||
|
||||
use cursor_icon::CursorIcon;
|
||||
use x11rb::{
|
||||
connection::Connection,
|
||||
properties::{WmHints, WmHintsState, WmSizeHints, WmSizeHintsSpecification},
|
||||
@@ -22,20 +23,27 @@ use x11rb::{
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
event::{Event, InnerSizeWriter, WindowEvent},
|
||||
event_loop::AsyncRequestSerial,
|
||||
platform_impl::{
|
||||
x11::{atoms::*, MonitorHandle as X11MonitorHandle, WakeSender, X11Error},
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
|
||||
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
|
||||
x11::{
|
||||
atoms::*, xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender,
|
||||
X11Error,
|
||||
},
|
||||
OwnedWindowHandle as PlatformOwnedWindowHandle,
|
||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor,
|
||||
PlatformIcon, PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
|
||||
},
|
||||
window::{
|
||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
|
||||
WindowButtons, WindowLevel,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
ffi, util, CookieResultExt, EventLoopWindowTarget, ImeRequest, ImeSender, VoidCookie, WindowId,
|
||||
ffi,
|
||||
util::{self, CustomCursor, SelectedCursor},
|
||||
CookieResultExt, EventLoopWindowTarget, ImeRequest, ImeSender, VoidCookie, WindowId,
|
||||
XConnection,
|
||||
};
|
||||
|
||||
@@ -64,7 +72,8 @@ pub struct SharedState {
|
||||
pub base_size: Option<Size>,
|
||||
pub visibility: Visibility,
|
||||
pub has_focus: bool,
|
||||
pub cursor_hittest: bool,
|
||||
// Use `Option` to not apply hittest logic when it was never requested.
|
||||
pub cursor_hittest: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
@@ -105,7 +114,7 @@ impl SharedState {
|
||||
resize_increments: None,
|
||||
base_size: None,
|
||||
has_focus: false,
|
||||
cursor_hittest: true,
|
||||
cursor_hittest: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -121,7 +130,7 @@ pub(crate) struct UnownedWindow {
|
||||
root: xproto::Window, // never changes
|
||||
#[allow(dead_code)]
|
||||
screen_id: i32, // never changes
|
||||
cursor: Mutex<CursorIcon>,
|
||||
selected_cursor: Mutex<SelectedCursor>,
|
||||
cursor_grabbed_mode: Mutex<CursorGrabMode>,
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
cursor_visible: Mutex<bool>,
|
||||
@@ -149,15 +158,12 @@ impl UnownedWindow {
|
||||
) -> Result<UnownedWindow, RootOsError> {
|
||||
let xconn = &event_loop.xconn;
|
||||
let atoms = xconn.atoms();
|
||||
#[cfg(feature = "rwh_06")]
|
||||
let root = match window_attrs.parent_window {
|
||||
Some(rwh_06::RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window,
|
||||
Some(rwh_06::RawWindowHandle::Xcb(handle)) => handle.window.get(),
|
||||
Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"),
|
||||
Some(PlatformOwnedWindowHandle::X(handle)) => handle,
|
||||
#[cfg(wayland_platform)]
|
||||
Some(handle) => panic!("invalid window handle {handle:?} on X11"),
|
||||
None => event_loop.root,
|
||||
};
|
||||
#[cfg(not(feature = "rwh_06"))]
|
||||
let root = event_loop.root;
|
||||
|
||||
let mut monitors = leap!(xconn.available_monitors());
|
||||
let guessed_monitor = if monitors.is_empty() {
|
||||
@@ -275,7 +281,8 @@ impl UnownedWindow {
|
||||
| EventMask::KEYMAP_STATE
|
||||
| EventMask::BUTTON_PRESS
|
||||
| EventMask::BUTTON_RELEASE
|
||||
| EventMask::POINTER_MOTION;
|
||||
| EventMask::POINTER_MOTION
|
||||
| EventMask::PROPERTY_CHANGE;
|
||||
|
||||
aux = aux.event_mask(event_mask).border_pixel(0);
|
||||
|
||||
@@ -349,7 +356,7 @@ impl UnownedWindow {
|
||||
visual,
|
||||
root,
|
||||
screen_id,
|
||||
cursor: Default::default(),
|
||||
selected_cursor: Default::default(),
|
||||
cursor_grabbed_mode: Mutex::new(CursorGrabMode::None),
|
||||
cursor_visible: Mutex::new(true),
|
||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||
@@ -547,10 +554,10 @@ impl UnownedWindow {
|
||||
if window_attrs.maximized {
|
||||
leap!(window.set_maximized_inner(window_attrs.maximized)).ignore_error();
|
||||
}
|
||||
if window_attrs.fullscreen.is_some() {
|
||||
if window_attrs.fullscreen.0.is_some() {
|
||||
if let Some(flusher) =
|
||||
leap!(window
|
||||
.set_fullscreen_inner(window_attrs.fullscreen.clone().map(Into::into)))
|
||||
.set_fullscreen_inner(window_attrs.fullscreen.0.clone().map(Into::into)))
|
||||
{
|
||||
flusher.ignore_error()
|
||||
}
|
||||
@@ -922,6 +929,51 @@ impl UnownedWindow {
|
||||
})
|
||||
}
|
||||
|
||||
/// Refresh the API for the given monitor.
|
||||
#[inline]
|
||||
pub(super) fn refresh_dpi_for_monitor<T: 'static>(
|
||||
&self,
|
||||
new_monitor: &X11MonitorHandle,
|
||||
maybe_prev_scale_factor: Option<f64>,
|
||||
mut callback: impl FnMut(Event<T>),
|
||||
) {
|
||||
// Check if the self is on this monitor
|
||||
let monitor = self.shared_state_lock().last_monitor.clone();
|
||||
if monitor.name == new_monitor.name {
|
||||
let (width, height) = self.inner_size_physical();
|
||||
let (new_width, new_height) = self.adjust_for_dpi(
|
||||
// If we couldn't determine the previous scale
|
||||
// factor (e.g., because all monitors were closed
|
||||
// before), just pick whatever the current monitor
|
||||
// has set as a baseline.
|
||||
maybe_prev_scale_factor.unwrap_or(monitor.scale_factor),
|
||||
new_monitor.scale_factor,
|
||||
width,
|
||||
height,
|
||||
&self.shared_state_lock(),
|
||||
);
|
||||
|
||||
let window_id = crate::window::WindowId(self.id());
|
||||
let old_inner_size = PhysicalSize::new(width, height);
|
||||
let inner_size = Arc::new(Mutex::new(PhysicalSize::new(new_width, new_height)));
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor: new_monitor.scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&inner_size)),
|
||||
},
|
||||
});
|
||||
|
||||
let new_inner_size = *inner_size.lock().unwrap();
|
||||
drop(inner_size);
|
||||
|
||||
if new_inner_size != old_inner_size {
|
||||
let (new_width, new_height) = new_inner_size.into();
|
||||
self.request_inner_size_physical(new_width, new_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_minimized_inner(&self, minimized: bool) -> Result<VoidCookie<'_>, X11Error> {
|
||||
let atoms = self.xconn.atoms();
|
||||
|
||||
@@ -1295,8 +1347,8 @@ impl UnownedWindow {
|
||||
self.xconn
|
||||
.flush_requests()
|
||||
.expect("Failed to call XResizeWindow");
|
||||
// cursor_hittest needs to be reapplied after window resize
|
||||
if self.shared_state_lock().cursor_hittest {
|
||||
// cursor_hittest needs to be reapplied after each window resize.
|
||||
if self.shared_state_lock().cursor_hittest.unwrap_or(false) {
|
||||
let _ = self.set_cursor_hittest(true);
|
||||
}
|
||||
}
|
||||
@@ -1326,7 +1378,8 @@ impl UnownedWindow {
|
||||
self.xwindow as xproto::Window,
|
||||
xproto::AtomEnum::WM_NORMAL_HINTS,
|
||||
)?
|
||||
.reply()?;
|
||||
.reply()?
|
||||
.unwrap_or_default();
|
||||
callback(&mut normal_hints);
|
||||
normal_hints
|
||||
.set(
|
||||
@@ -1379,6 +1432,7 @@ impl UnownedWindow {
|
||||
)
|
||||
.ok()
|
||||
.and_then(|cookie| cookie.reply().ok())
|
||||
.flatten()
|
||||
.and_then(|hints| hints.size_increment)
|
||||
.map(|(width, height)| (width as u32, height as u32).into())
|
||||
}
|
||||
@@ -1482,13 +1536,29 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
||||
let old_cursor = replace(&mut *self.cursor.lock().unwrap(), cursor);
|
||||
let old_cursor = replace(
|
||||
&mut *self.selected_cursor.lock().unwrap(),
|
||||
SelectedCursor::Named(cursor),
|
||||
);
|
||||
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
if cursor != old_cursor && *self.cursor_visible.lock().unwrap() {
|
||||
if SelectedCursor::Named(cursor) != old_cursor && *self.cursor_visible.lock().unwrap() {
|
||||
self.xconn.set_cursor_icon(self.xwindow, Some(cursor));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) {
|
||||
let new_cursor = unsafe { CustomCursor::new(&self.xconn, &cursor.0) };
|
||||
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
if *self.cursor_visible.lock().unwrap() {
|
||||
self.xconn.set_custom_cursor(self.xwindow, &new_cursor);
|
||||
}
|
||||
|
||||
*self.selected_cursor.lock().unwrap() = SelectedCursor::Custom(new_cursor);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
let mut grabbed_lock = self.cursor_grabbed_mode.lock().unwrap();
|
||||
@@ -1575,13 +1645,23 @@ impl UnownedWindow {
|
||||
return;
|
||||
}
|
||||
let cursor = if visible {
|
||||
Some(*self.cursor.lock().unwrap())
|
||||
Some((*self.selected_cursor.lock().unwrap()).clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
*visible_lock = visible;
|
||||
drop(visible_lock);
|
||||
self.xconn.set_cursor_icon(self.xwindow, cursor);
|
||||
match cursor {
|
||||
Some(SelectedCursor::Custom(cursor)) => {
|
||||
self.xconn.set_custom_cursor(self.xwindow, &cursor);
|
||||
}
|
||||
Some(SelectedCursor::Named(cursor)) => {
|
||||
self.xconn.set_cursor_icon(self.xwindow, Some(cursor));
|
||||
}
|
||||
None => {
|
||||
self.xconn.set_cursor_icon(self.xwindow, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1627,7 +1707,7 @@ impl UnownedWindow {
|
||||
.xcb_connection()
|
||||
.xfixes_set_window_shape_region(self.xwindow, SK::INPUT, 0, 0, region.region())
|
||||
.map_err(|_e| ExternalError::Ignored)?;
|
||||
self.shared_state_lock().cursor_hittest = hittest;
|
||||
self.shared_state_lock().cursor_hittest = Some(hittest);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1691,8 +1771,8 @@ impl UnownedWindow {
|
||||
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
|
||||
),
|
||||
[
|
||||
(window.x as u32 + pointer.win_x as u32),
|
||||
(window.y as u32 + pointer.win_y as u32),
|
||||
(window.x as u32 + xinput_fp1616_to_float(pointer.win_x) as u32),
|
||||
(window.y as u32 + xinput_fp1616_to_float(pointer.win_y) as u32),
|
||||
action.try_into().unwrap(),
|
||||
1, // Button 1
|
||||
1,
|
||||
@@ -1734,9 +1814,9 @@ impl UnownedWindow {
|
||||
let state_type_atom = atoms[CARD32];
|
||||
let is_minimized = if let Ok(state) =
|
||||
self.xconn
|
||||
.get_property(self.xwindow, state_atom, state_type_atom)
|
||||
.get_property::<u32>(self.xwindow, state_atom, state_type_atom)
|
||||
{
|
||||
state.contains(&(ffi::IconicState as c_ulong))
|
||||
state.contains(&super::ICONIC_STATE)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
@@ -1773,6 +1853,7 @@ impl UnownedWindow {
|
||||
WmHints::get(self.xconn.xcb_connection(), self.xwindow as xproto::Window)
|
||||
.ok()
|
||||
.and_then(|cookie| cookie.reply().ok())
|
||||
.flatten()
|
||||
.unwrap_or_default();
|
||||
|
||||
wm_hints.urgent = request_type.is_some();
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use icrate::AppKit::{
|
||||
NSApplication, NSEvent, NSEventModifierFlagCommand, NSEventTypeKeyUp, NSEventTypeLeftMouseDown,
|
||||
NSEventTypeLeftMouseDragged, NSEventTypeLeftMouseUp, NSEventTypeMouseMoved,
|
||||
NSEventTypeOtherMouseDown, NSEventTypeOtherMouseDragged, NSEventTypeOtherMouseUp,
|
||||
NSEventTypeRightMouseDown, NSEventTypeRightMouseDragged, NSEventTypeRightMouseUp, NSResponder,
|
||||
};
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::{declare_class, msg_send, mutability, ClassType};
|
||||
use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
|
||||
|
||||
use super::appkit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
||||
use super::event::flags_contains;
|
||||
use super::{app_state::AppState, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, ElementState, Event};
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(super) struct WinitApplication;
|
||||
|
||||
unsafe impl ClassType for WinitApplication {
|
||||
#[inherits(NSResponder, NSObject)]
|
||||
type Super = NSApplication;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitApplication";
|
||||
}
|
||||
|
||||
impl DeclaredClass for WinitApplication {}
|
||||
|
||||
unsafe impl WinitApplication {
|
||||
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
|
||||
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
|
||||
@@ -27,13 +34,13 @@ declare_class!(
|
||||
// For posterity, there are some undocumented event types
|
||||
// (https://github.com/servo/cocoa-rs/issues/155)
|
||||
// but that doesn't really matter here.
|
||||
let event_type = event.type_();
|
||||
let modifier_flags = event.modifierFlags();
|
||||
if event_type == NSEventType::NSKeyUp
|
||||
&& modifier_flags.contains(NSEventModifierFlags::NSCommandKeyMask)
|
||||
let event_type = unsafe { event.r#type() };
|
||||
let modifier_flags = unsafe { event.modifierFlags() };
|
||||
if event_type == NSEventTypeKeyUp
|
||||
&& flags_contains(modifier_flags, NSEventModifierFlagCommand)
|
||||
{
|
||||
if let Some(key_window) = self.keyWindow() {
|
||||
unsafe { key_window.sendEvent(event) };
|
||||
key_window.sendEvent(event);
|
||||
}
|
||||
} else {
|
||||
maybe_dispatch_device_event(event);
|
||||
@@ -44,14 +51,15 @@ declare_class!(
|
||||
);
|
||||
|
||||
fn maybe_dispatch_device_event(event: &NSEvent) {
|
||||
let event_type = event.type_();
|
||||
let event_type = unsafe { event.r#type() };
|
||||
#[allow(non_upper_case_globals)]
|
||||
match event_type {
|
||||
NSEventType::NSMouseMoved
|
||||
| NSEventType::NSLeftMouseDragged
|
||||
| NSEventType::NSOtherMouseDragged
|
||||
| NSEventType::NSRightMouseDragged => {
|
||||
let delta_x = event.deltaX() as f64;
|
||||
let delta_y = event.deltaY() as f64;
|
||||
NSEventTypeMouseMoved
|
||||
| NSEventTypeLeftMouseDragged
|
||||
| NSEventTypeOtherMouseDragged
|
||||
| NSEventTypeRightMouseDragged => {
|
||||
let delta_x = unsafe { event.deltaX() } as f64;
|
||||
let delta_y = unsafe { event.deltaY() } as f64;
|
||||
|
||||
if delta_x != 0.0 {
|
||||
queue_device_event(DeviceEvent::Motion {
|
||||
@@ -73,17 +81,15 @@ fn maybe_dispatch_device_event(event: &NSEvent) {
|
||||
});
|
||||
}
|
||||
}
|
||||
NSEventType::NSLeftMouseDown
|
||||
| NSEventType::NSRightMouseDown
|
||||
| NSEventType::NSOtherMouseDown => {
|
||||
NSEventTypeLeftMouseDown | NSEventTypeRightMouseDown | NSEventTypeOtherMouseDown => {
|
||||
queue_device_event(DeviceEvent::Button {
|
||||
button: event.buttonNumber() as u32,
|
||||
button: unsafe { event.buttonNumber() } as u32,
|
||||
state: ElementState::Pressed,
|
||||
});
|
||||
}
|
||||
NSEventType::NSLeftMouseUp | NSEventType::NSRightMouseUp | NSEventType::NSOtherMouseUp => {
|
||||
NSEventTypeLeftMouseUp | NSEventTypeRightMouseUp | NSEventTypeOtherMouseUp => {
|
||||
queue_device_event(DeviceEvent::Button {
|
||||
button: event.buttonNumber() as u32,
|
||||
button: unsafe { event.buttonNumber() } as u32,
|
||||
state: ElementState::Released,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,54 +1,41 @@
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::declare::{IvarBool, IvarEncode};
|
||||
use icrate::AppKit::{NSApplicationActivationPolicy, NSApplicationDelegate};
|
||||
use icrate::Foundation::{MainThreadMarker, NSObject, NSObjectProtocol};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType};
|
||||
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||
|
||||
use super::app_state::AppState;
|
||||
use super::appkit::NSApplicationActivationPolicy;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct State {
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ApplicationDelegate {
|
||||
activation_policy: IvarEncode<NSApplicationActivationPolicy, "_activation_policy">,
|
||||
default_menu: IvarBool<"_default_menu">,
|
||||
activate_ignoring_other_apps: IvarBool<"_activate_ignoring_other_apps">,
|
||||
}
|
||||
|
||||
mod ivars;
|
||||
pub(super) struct ApplicationDelegate;
|
||||
|
||||
unsafe impl ClassType for ApplicationDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitApplicationDelegate";
|
||||
}
|
||||
|
||||
unsafe impl ApplicationDelegate {
|
||||
#[method(initWithActivationPolicy:defaultMenu:activateIgnoringOtherApps:)]
|
||||
unsafe fn init(
|
||||
this: *mut Self,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) -> Option<NonNull<Self>> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![super(this), init] };
|
||||
this.map(|this| {
|
||||
*this.activation_policy = activation_policy;
|
||||
*this.default_menu = default_menu;
|
||||
*this.activate_ignoring_other_apps = activate_ignoring_other_apps;
|
||||
NonNull::from(this)
|
||||
})
|
||||
}
|
||||
impl DeclaredClass for ApplicationDelegate {
|
||||
type Ivars = State;
|
||||
}
|
||||
|
||||
unsafe impl NSObjectProtocol for ApplicationDelegate {}
|
||||
|
||||
unsafe impl NSApplicationDelegate for ApplicationDelegate {
|
||||
#[method(applicationDidFinishLaunching:)]
|
||||
fn did_finish_launching(&self, _sender: Option<&AnyObject>) {
|
||||
trace_scope!("applicationDidFinishLaunching:");
|
||||
AppState::launched(
|
||||
*self.activation_policy,
|
||||
*self.default_menu,
|
||||
*self.activate_ignoring_other_apps,
|
||||
self.ivars().activation_policy,
|
||||
self.ivars().default_menu,
|
||||
self.ivars().activate_ignoring_other_apps,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,17 +50,16 @@ declare_class!(
|
||||
|
||||
impl ApplicationDelegate {
|
||||
pub(super) fn new(
|
||||
mtm: MainThreadMarker,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) -> Id<Self> {
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
Self::alloc(),
|
||||
initWithActivationPolicy: activation_policy,
|
||||
defaultMenu: default_menu,
|
||||
activateIgnoringOtherApps: activate_ignoring_other_apps,
|
||||
]
|
||||
}
|
||||
let this = mtm.alloc().set_ivars(State {
|
||||
activation_policy,
|
||||
default_menu,
|
||||
activate_ignoring_other_apps,
|
||||
});
|
||||
unsafe { msg_send_id![super(this), init] }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,14 @@ use std::{
|
||||
};
|
||||
|
||||
use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp};
|
||||
use icrate::Foundation::{is_main_thread, NSSize};
|
||||
use icrate::AppKit::{NSApplication, NSApplicationActivationPolicy};
|
||||
use icrate::Foundation::{is_main_thread, MainThreadMarker, NSSize};
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent};
|
||||
use super::{
|
||||
event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never, window::WinitWindow,
|
||||
event::dummy_event, event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never,
|
||||
window::WinitWindow,
|
||||
};
|
||||
use crate::{
|
||||
dpi::PhysicalSize,
|
||||
@@ -57,14 +58,14 @@ impl<T> EventLoopHandler<T> {
|
||||
where
|
||||
F: FnOnce(&mut EventLoopHandler<T>, RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>)>),
|
||||
{
|
||||
// The `NSApp` and our `HANDLER` are global state and so it's possible that
|
||||
// we could get a delegate callback after the application has exit an
|
||||
// `NSApplication` and our `HANDLER` are global state and so it's possible
|
||||
// that we could get a delegate callback after the application has exit an
|
||||
// `EventLoop`. If the loop has been exit then our weak `self.callback`
|
||||
// will fail to upgrade.
|
||||
//
|
||||
// We don't want to panic or output any verbose logging if we fail to
|
||||
// upgrade the weak reference since it might be valid that the application
|
||||
// re-starts the `NSApp` after exiting a Winit `EventLoop`
|
||||
// re-starts the `NSApplication` after exiting a Winit `EventLoop`
|
||||
if let Some(callback) = self.callback.upgrade() {
|
||||
let callback = callback.borrow_mut();
|
||||
(f)(self, callback);
|
||||
@@ -144,9 +145,9 @@ impl Handler {
|
||||
|
||||
/// `true` after `ApplicationDelegate::applicationDidFinishLaunching` called
|
||||
///
|
||||
/// NB: This is global / `NSApp` state and since the app will only be launched
|
||||
/// once but an `EventLoop` may be run more than once then only the first
|
||||
/// `EventLoop` will observe the `NSApp` before it is launched.
|
||||
/// NB: This is global / `NSApplication` state and since the app will only
|
||||
/// be launched once but an `EventLoop` may be run more than once then only
|
||||
/// the first `EventLoop` will observe the application before it is launched.
|
||||
fn is_launched(&self) -> bool {
|
||||
self.launched.load(Ordering::Acquire)
|
||||
}
|
||||
@@ -158,8 +159,8 @@ impl Handler {
|
||||
|
||||
/// `true` if an `EventLoop` is currently running
|
||||
///
|
||||
/// NB: This is global / `NSApp` state and may persist beyond the lifetime of
|
||||
/// a running `EventLoop`.
|
||||
/// NB: This is global / `NSApplication` state and may persist beyond the
|
||||
/// lifetime of a running `EventLoop`.
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
@@ -167,7 +168,7 @@ impl Handler {
|
||||
self.running.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Set when an `EventLoop` starts running, after the `NSApp` is launched
|
||||
/// Set when an `EventLoop` starts running, after the `NSApplication` is launched
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
@@ -180,8 +181,8 @@ impl Handler {
|
||||
/// Since an `EventLoop` may be run more than once we need make sure to reset the
|
||||
/// `control_flow` state back to `Poll` each time the loop exits.
|
||||
///
|
||||
/// Note: that if the `NSApp` has been launched then that state is preserved, and we won't
|
||||
/// need to re-launch the app if subsequent EventLoops are run.
|
||||
/// Note: that if the `NSApplication` has been launched then that state is preserved,
|
||||
/// and we won't need to re-launch the app if subsequent EventLoops are run.
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
@@ -329,7 +330,7 @@ impl Handler {
|
||||
) {
|
||||
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
let event = Event::WindowEvent {
|
||||
let scale_factor_changed_event = Event::WindowEvent {
|
||||
window_id: WindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
@@ -337,13 +338,19 @@ impl Handler {
|
||||
},
|
||||
};
|
||||
|
||||
callback.handle_nonuser_event(event);
|
||||
callback.handle_nonuser_event(scale_factor_changed_event);
|
||||
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
|
||||
let resized_event = Event::WindowEvent {
|
||||
window_id: WindowId(window.id()),
|
||||
event: WindowEvent::Resized(physical_size),
|
||||
};
|
||||
callback.handle_nonuser_event(resized_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,7 +393,7 @@ impl AppState {
|
||||
}
|
||||
|
||||
// If `pump_events` is called to progress the event loop then we bootstrap the event
|
||||
// loop via `[NSApp run]` but will use `CFRunLoopRunInMode` for subsequent calls to
|
||||
// loop via `-[NSAppplication run]` but will use `CFRunLoopRunInMode` for subsequent calls to
|
||||
// `pump_events`
|
||||
pub fn request_stop_on_launch() {
|
||||
HANDLER.request_stop_app_on_launch();
|
||||
@@ -453,13 +460,15 @@ impl AppState {
|
||||
create_default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) {
|
||||
let app = NSApp();
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
app.setActivationPolicy(activation_policy);
|
||||
|
||||
window_activation_hack(&app);
|
||||
#[allow(deprecated)]
|
||||
app.activateIgnoringOtherApps(activate_ignoring_other_apps);
|
||||
|
||||
HANDLER.set_launched();
|
||||
@@ -467,21 +476,22 @@ impl AppState {
|
||||
if create_default_menu {
|
||||
// The menubar initialization should be before the `NewEvents` event, to allow
|
||||
// overriding of the default menu even if it's created
|
||||
menu::initialize();
|
||||
menu::initialize(&app);
|
||||
}
|
||||
|
||||
Self::start_running();
|
||||
|
||||
// If the `NSApp` is being launched via `EventLoop::pump_events()` then we'll
|
||||
// If the application is being launched via `EventLoop::pump_events()` then we'll
|
||||
// want to stop the app once it is launched (and return to the external loop)
|
||||
//
|
||||
// In this case we still want to consider Winit's `EventLoop` to be "running",
|
||||
// so we call `start_running()` above.
|
||||
if HANDLER.should_stop_app_on_launch() {
|
||||
// Note: the original idea had been to only stop the underlying `RunLoop`
|
||||
// for the app but that didn't work as expected (`[NSApp run]` effectively
|
||||
// ignored the attempt to stop the RunLoop and re-started it.). So we
|
||||
// return from `pump_events` by stopping the `NSApp`
|
||||
// for the app but that didn't work as expected (`-[NSApplication run]`
|
||||
// effectively ignored the attempt to stop the RunLoop and re-started it).
|
||||
//
|
||||
// So we return from `pump_events` by stopping the application.
|
||||
Self::stop();
|
||||
}
|
||||
}
|
||||
@@ -494,9 +504,9 @@ impl AppState {
|
||||
|
||||
// Return when in callback due to https://github.com/rust-windowing/winit/issues/1779
|
||||
if panic_info.is_panicking()
|
||||
|| HANDLER.get_in_callback()
|
||||
|| !HANDLER.have_callback()
|
||||
|| !HANDLER.is_running()
|
||||
|| HANDLER.get_in_callback()
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -583,11 +593,12 @@ impl AppState {
|
||||
}
|
||||
|
||||
pub fn stop() {
|
||||
let app = NSApp();
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
autoreleasepool(|_| {
|
||||
app.stop(None);
|
||||
// To stop event loop immediately, we need to post some event here.
|
||||
app.postEvent_atStart(&NSEvent::dummy(), true);
|
||||
app.postEvent_atStart(&dummy_event().unwrap(), true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -601,9 +612,9 @@ impl AppState {
|
||||
// XXX: how does it make sense that `get_in_callback()` can ever return `true` here if we're
|
||||
// about to return to the `CFRunLoop` to poll for new events?
|
||||
if panic_info.is_panicking()
|
||||
|| HANDLER.get_in_callback()
|
||||
|| !HANDLER.have_callback()
|
||||
|| !HANDLER.is_running()
|
||||
|| HANDLER.get_in_callback()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
use icrate::Foundation::{NSArray, NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSAppearance;
|
||||
|
||||
unsafe impl ClassType for NSAppearance {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
type NSAppearanceName = NSString;
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSAppearance {
|
||||
#[method_id(appearanceNamed:)]
|
||||
pub fn appearanceNamed(name: &NSAppearanceName) -> Id<Self>;
|
||||
|
||||
#[method_id(bestMatchFromAppearancesWithNames:)]
|
||||
pub fn bestMatchFromAppearancesWithNames(
|
||||
&self,
|
||||
appearances: &NSArray<NSAppearanceName>,
|
||||
) -> Id<NSAppearanceName>;
|
||||
}
|
||||
);
|
||||
@@ -1,140 +0,0 @@
|
||||
use icrate::Foundation::{MainThreadMarker, NSArray, NSInteger, NSObject, NSUInteger};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||
use objc2::{Encode, Encoding};
|
||||
|
||||
use super::{NSAppearance, NSEvent, NSMenu, NSResponder, NSWindow};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSApplication;
|
||||
|
||||
unsafe impl ClassType for NSApplication {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
pub(crate) fn NSApp() -> Id<NSApplication> {
|
||||
// TODO: Only allow access from main thread
|
||||
NSApplication::shared(unsafe { MainThreadMarker::new_unchecked() })
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSApplication {
|
||||
/// This can only be called on the main thread since it may initialize
|
||||
/// the application and since it's parameters may be changed by the main
|
||||
/// thread at any time (hence it is only safe to access on the main thread).
|
||||
pub fn shared(_mtm: MainThreadMarker) -> Id<Self> {
|
||||
let app: Option<_> = unsafe { msg_send_id![Self::class(), sharedApplication] };
|
||||
// SAFETY: `sharedApplication` always initializes the app if it isn't already
|
||||
unsafe { app.unwrap_unchecked() }
|
||||
}
|
||||
|
||||
#[method_id(currentEvent)]
|
||||
pub fn currentEvent(&self) -> Option<Id<NSEvent>>;
|
||||
|
||||
#[method(postEvent:atStart:)]
|
||||
pub fn postEvent_atStart(&self, event: &NSEvent, front_of_queue: bool);
|
||||
|
||||
#[method(presentationOptions)]
|
||||
pub fn presentationOptions(&self) -> NSApplicationPresentationOptions;
|
||||
|
||||
#[method_id(windows)]
|
||||
pub fn windows(&self) -> Id<NSArray<NSWindow>>;
|
||||
|
||||
#[method_id(keyWindow)]
|
||||
pub fn keyWindow(&self) -> Option<Id<NSWindow>>;
|
||||
|
||||
// TODO: NSApplicationDelegate
|
||||
#[method(setDelegate:)]
|
||||
pub fn setDelegate(&self, delegate: &AnyObject);
|
||||
|
||||
#[method(setPresentationOptions:)]
|
||||
pub fn setPresentationOptions(&self, options: NSApplicationPresentationOptions);
|
||||
|
||||
#[method(hide:)]
|
||||
pub fn hide(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(orderFrontCharacterPalette:)]
|
||||
#[allow(dead_code)]
|
||||
pub fn orderFrontCharacterPalette(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(hideOtherApplications:)]
|
||||
pub fn hideOtherApplications(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(stop:)]
|
||||
pub fn stop(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(activateIgnoringOtherApps:)]
|
||||
pub fn activateIgnoringOtherApps(&self, ignore: bool);
|
||||
|
||||
#[method(requestUserAttention:)]
|
||||
pub fn requestUserAttention(&self, type_: NSRequestUserAttentionType) -> NSInteger;
|
||||
|
||||
#[method(setActivationPolicy:)]
|
||||
pub fn setActivationPolicy(&self, policy: NSApplicationActivationPolicy) -> bool;
|
||||
|
||||
#[method(setMainMenu:)]
|
||||
pub fn setMainMenu(&self, menu: &NSMenu);
|
||||
|
||||
#[method_id(effectiveAppearance)]
|
||||
pub fn effectiveAppearance(&self) -> Id<NSAppearance>;
|
||||
|
||||
#[method(setAppearance:)]
|
||||
pub fn setAppearance(&self, appearance: Option<&NSAppearance>);
|
||||
|
||||
#[method(run)]
|
||||
pub unsafe fn run(&self);
|
||||
}
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSApplicationActivationPolicy {
|
||||
NSApplicationActivationPolicyRegular = 0,
|
||||
NSApplicationActivationPolicyAccessory = 1,
|
||||
NSApplicationActivationPolicyProhibited = 2,
|
||||
NSApplicationActivationPolicyERROR = -1,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSApplicationActivationPolicy {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NSApplicationPresentationOptions: NSUInteger {
|
||||
const NSApplicationPresentationDefault = 0;
|
||||
const NSApplicationPresentationAutoHideDock = 1 << 0;
|
||||
const NSApplicationPresentationHideDock = 1 << 1;
|
||||
const NSApplicationPresentationAutoHideMenuBar = 1 << 2;
|
||||
const NSApplicationPresentationHideMenuBar = 1 << 3;
|
||||
const NSApplicationPresentationDisableAppleMenu = 1 << 4;
|
||||
const NSApplicationPresentationDisableProcessSwitching = 1 << 5;
|
||||
const NSApplicationPresentationDisableForceQuit = 1 << 6;
|
||||
const NSApplicationPresentationDisableSessionTermination = 1 << 7;
|
||||
const NSApplicationPresentationDisableHideApplication = 1 << 8;
|
||||
const NSApplicationPresentationDisableMenuBarTransparency = 1 << 9;
|
||||
const NSApplicationPresentationFullScreen = 1 << 10;
|
||||
const NSApplicationPresentationAutoHideToolbar = 1 << 11;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSApplicationPresentationOptions {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSRequestUserAttentionType {
|
||||
NSCriticalRequest = 0,
|
||||
NSInformationalRequest = 10,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSRequestUserAttentionType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::{extern_class, mutability, ClassType};
|
||||
|
||||
use super::{NSControl, NSResponder, NSView};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSButton;
|
||||
|
||||
unsafe impl ClassType for NSButton {
|
||||
#[inherits(NSView, NSResponder, NSObject)]
|
||||
type Super = NSControl;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
@@ -1,28 +0,0 @@
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// An object that stores color data and sometimes opacity (alpha value).
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nscolor?language=objc>
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSColor;
|
||||
|
||||
unsafe impl ClassType for NSColor {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Documentation clearly states:
|
||||
// > Color objects are immutable and thread-safe
|
||||
unsafe impl Send for NSColor {}
|
||||
unsafe impl Sync for NSColor {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSColor {
|
||||
#[method_id(clearColor)]
|
||||
pub fn clear() -> Id<Self>;
|
||||
}
|
||||
);
|
||||
@@ -1,25 +0,0 @@
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::{NSResponder, NSView};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSControl;
|
||||
|
||||
unsafe impl ClassType for NSControl {
|
||||
#[inherits(NSResponder, NSObject)]
|
||||
type Super = NSView;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSControl {
|
||||
#[method(setEnabled:)]
|
||||
pub fn setEnabled(&self, enabled: bool);
|
||||
|
||||
#[method(isEnabled)]
|
||||
pub fn isEnabled(&self) -> bool;
|
||||
}
|
||||
);
|
||||
@@ -1,241 +0,0 @@
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use icrate::ns_string;
|
||||
use icrate::Foundation::{
|
||||
NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSString,
|
||||
};
|
||||
use objc2::rc::{DefaultId, Id};
|
||||
use objc2::runtime::Sel;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, sel, ClassType};
|
||||
|
||||
use super::NSImage;
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/appkit/nscursor?language=objc>
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSCursor;
|
||||
|
||||
unsafe impl ClassType for NSCursor {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: NSCursor is immutable, stated here:
|
||||
// https://developer.apple.com/documentation/appkit/nscursor/1527062-image?language=objc
|
||||
unsafe impl Send for NSCursor {}
|
||||
unsafe impl Sync for NSCursor {}
|
||||
|
||||
macro_rules! def_cursor {
|
||||
{$(
|
||||
$(#[$($m:meta)*])*
|
||||
pub fn $name:ident();
|
||||
)*} => {$(
|
||||
$(#[$($m)*])*
|
||||
pub fn $name() -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::class(), $name] }
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
macro_rules! def_undocumented_cursor {
|
||||
{$(
|
||||
$(#[$($m:meta)*])*
|
||||
pub fn $name:ident();
|
||||
)*} => {$(
|
||||
$(#[$($m)*])*
|
||||
pub fn $name() -> Id<Self> {
|
||||
unsafe { Self::from_selector(sel!($name)).unwrap_or_else(|| Default::default()) }
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
/// Documented cursors
|
||||
unsafe impl NSCursor {
|
||||
def_cursor!(
|
||||
pub fn arrowCursor();
|
||||
pub fn pointingHandCursor();
|
||||
pub fn openHandCursor();
|
||||
pub fn closedHandCursor();
|
||||
pub fn IBeamCursor();
|
||||
pub fn IBeamCursorForVerticalLayout();
|
||||
pub fn dragCopyCursor();
|
||||
pub fn dragLinkCursor();
|
||||
pub fn operationNotAllowedCursor();
|
||||
pub fn contextualMenuCursor();
|
||||
pub fn crosshairCursor();
|
||||
pub fn resizeRightCursor();
|
||||
pub fn resizeUpCursor();
|
||||
pub fn resizeLeftCursor();
|
||||
pub fn resizeDownCursor();
|
||||
pub fn resizeLeftRightCursor();
|
||||
pub fn resizeUpDownCursor();
|
||||
);
|
||||
|
||||
// Creating cursors should be thread-safe, though using them for anything probably isn't.
|
||||
pub fn new(image: &NSImage, hotSpot: NSPoint) -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::alloc(), initWithImage: image, hotSpot: hotSpot] }
|
||||
}
|
||||
|
||||
pub fn invisible() -> Id<Self> {
|
||||
// 16x16 GIF data for invisible cursor
|
||||
// You can reproduce this via ImageMagick.
|
||||
// $ convert -size 16x16 xc:none cursor.gif
|
||||
static CURSOR_BYTES: &[u8] = &[
|
||||
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x84, 0x8F, 0xA9,
|
||||
0xCB, 0xED, 0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B,
|
||||
];
|
||||
|
||||
static CURSOR: Lazy<Id<NSCursor>> = Lazy::new(|| {
|
||||
// TODO: Consider using `dataWithBytesNoCopy:`
|
||||
let data = NSData::with_bytes(CURSOR_BYTES);
|
||||
let image = NSImage::new_with_data(&data);
|
||||
NSCursor::new(&image, NSPoint::new(0.0, 0.0))
|
||||
});
|
||||
|
||||
CURSOR.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Undocumented cursors
|
||||
unsafe impl NSCursor {
|
||||
#[method(respondsToSelector:)]
|
||||
fn class_responds_to(sel: Sel) -> bool;
|
||||
|
||||
#[method_id(performSelector:)]
|
||||
unsafe fn from_selector_unchecked(sel: Sel) -> Id<Self>;
|
||||
|
||||
unsafe fn from_selector(sel: Sel) -> Option<Id<Self>> {
|
||||
if Self::class_responds_to(sel) {
|
||||
Some(unsafe { Self::from_selector_unchecked(sel) })
|
||||
} else {
|
||||
warn!("Cursor `{:?}` appears to be invalid", sel);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def_undocumented_cursor!(
|
||||
// Undocumented cursors: https://stackoverflow.com/a/46635398/5435443
|
||||
pub fn _helpCursor();
|
||||
pub fn _zoomInCursor();
|
||||
pub fn _zoomOutCursor();
|
||||
pub fn _windowResizeNorthEastCursor();
|
||||
pub fn _windowResizeNorthWestCursor();
|
||||
pub fn _windowResizeSouthEastCursor();
|
||||
pub fn _windowResizeSouthWestCursor();
|
||||
pub fn _windowResizeNorthEastSouthWestCursor();
|
||||
pub fn _windowResizeNorthWestSouthEastCursor();
|
||||
|
||||
// While these two are available, the former just loads a white arrow,
|
||||
// and the latter loads an ugly deflated beachball!
|
||||
// pub fn _moveCursor();
|
||||
// pub fn _waitCursor();
|
||||
|
||||
// An even more undocumented cursor...
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=522349
|
||||
pub fn busyButClickableCursor();
|
||||
);
|
||||
}
|
||||
|
||||
/// Webkit cursors
|
||||
unsafe impl NSCursor {
|
||||
// Note that loading `busybutclickable` with this code won't animate
|
||||
// the frames; instead you'll just get them all in a column.
|
||||
unsafe fn load_webkit_cursor(name: &NSString) -> Id<Self> {
|
||||
// Snatch a cursor from WebKit; They fit the style of the native
|
||||
// cursors, and will seem completely standard to macOS users.
|
||||
//
|
||||
// https://stackoverflow.com/a/21786835/5435443
|
||||
let root = ns_string!("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors");
|
||||
let cursor_path = root.stringByAppendingPathComponent(name);
|
||||
|
||||
let pdf_path = cursor_path.stringByAppendingPathComponent(ns_string!("cursor.pdf"));
|
||||
let image = NSImage::new_by_referencing_file(&pdf_path);
|
||||
|
||||
// TODO: Handle PLists better
|
||||
let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist"));
|
||||
let info: Id<NSDictionary<NSObject, NSObject>> = unsafe {
|
||||
msg_send_id![
|
||||
<NSDictionary<NSObject, NSObject>>::class(),
|
||||
dictionaryWithContentsOfFile: &*info_path,
|
||||
]
|
||||
};
|
||||
let mut x = 0.0;
|
||||
if let Some(n) = info.get(&*ns_string!("hotx")) {
|
||||
if n.is_kind_of::<NSNumber>() {
|
||||
let ptr: *const NSObject = n;
|
||||
let ptr: *const NSNumber = ptr.cast();
|
||||
x = unsafe { &*ptr }.as_cgfloat()
|
||||
}
|
||||
}
|
||||
let mut y = 0.0;
|
||||
if let Some(n) = info.get(&*ns_string!("hotx")) {
|
||||
if n.is_kind_of::<NSNumber>() {
|
||||
let ptr: *const NSObject = n;
|
||||
let ptr: *const NSNumber = ptr.cast();
|
||||
y = unsafe { &*ptr }.as_cgfloat()
|
||||
}
|
||||
}
|
||||
|
||||
let hotspot = NSPoint::new(x, y);
|
||||
Self::new(&image, hotspot)
|
||||
}
|
||||
|
||||
pub fn moveCursor() -> Id<Self> {
|
||||
unsafe { Self::load_webkit_cursor(ns_string!("move")) }
|
||||
}
|
||||
|
||||
pub fn cellCursor() -> Id<Self> {
|
||||
unsafe { Self::load_webkit_cursor(ns_string!("cell")) }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl NSCursor {
|
||||
pub fn from_icon(icon: CursorIcon) -> Id<Self> {
|
||||
match icon {
|
||||
CursorIcon::Default => Default::default(),
|
||||
CursorIcon::Pointer => Self::pointingHandCursor(),
|
||||
CursorIcon::Grab => Self::openHandCursor(),
|
||||
CursorIcon::Grabbing => Self::closedHandCursor(),
|
||||
CursorIcon::Text => Self::IBeamCursor(),
|
||||
CursorIcon::VerticalText => Self::IBeamCursorForVerticalLayout(),
|
||||
CursorIcon::Copy => Self::dragCopyCursor(),
|
||||
CursorIcon::Alias => Self::dragLinkCursor(),
|
||||
CursorIcon::NotAllowed | CursorIcon::NoDrop => Self::operationNotAllowedCursor(),
|
||||
CursorIcon::ContextMenu => Self::contextualMenuCursor(),
|
||||
CursorIcon::Crosshair => Self::crosshairCursor(),
|
||||
CursorIcon::EResize => Self::resizeRightCursor(),
|
||||
CursorIcon::NResize => Self::resizeUpCursor(),
|
||||
CursorIcon::WResize => Self::resizeLeftCursor(),
|
||||
CursorIcon::SResize => Self::resizeDownCursor(),
|
||||
CursorIcon::EwResize | CursorIcon::ColResize => Self::resizeLeftRightCursor(),
|
||||
CursorIcon::NsResize | CursorIcon::RowResize => Self::resizeUpDownCursor(),
|
||||
CursorIcon::Help => Self::_helpCursor(),
|
||||
CursorIcon::ZoomIn => Self::_zoomInCursor(),
|
||||
CursorIcon::ZoomOut => Self::_zoomOutCursor(),
|
||||
CursorIcon::NeResize => Self::_windowResizeNorthEastCursor(),
|
||||
CursorIcon::NwResize => Self::_windowResizeNorthWestCursor(),
|
||||
CursorIcon::SeResize => Self::_windowResizeSouthEastCursor(),
|
||||
CursorIcon::SwResize => Self::_windowResizeSouthWestCursor(),
|
||||
CursorIcon::NeswResize => Self::_windowResizeNorthEastSouthWestCursor(),
|
||||
CursorIcon::NwseResize => Self::_windowResizeNorthWestSouthEastCursor(),
|
||||
// This is the wrong semantics for `Wait`, but it's the same as
|
||||
// what's used in Safari and Chrome.
|
||||
CursorIcon::Wait | CursorIcon::Progress => Self::busyButClickableCursor(),
|
||||
CursorIcon::Move | CursorIcon::AllScroll => Self::moveCursor(),
|
||||
CursorIcon::Cell => Self::cellCursor(),
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultId for NSCursor {
|
||||
fn default_id() -> Id<Self> {
|
||||
Self::arrowCursor()
|
||||
}
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
use std::os::raw::c_ushort;
|
||||
|
||||
use icrate::Foundation::{
|
||||
CGFloat, NSCopying, NSInteger, NSObject, NSPoint, NSString, NSTimeInterval, NSUInteger,
|
||||
};
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSEvent;
|
||||
|
||||
unsafe impl ClassType for NSEvent {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// > Safely handled only on the same thread, whether that be the main thread
|
||||
// > or a secondary thread; otherwise you run the risk of having events get
|
||||
// > out of sequence.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123383>
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSEvent {
|
||||
#[method_id(
|
||||
otherEventWithType:
|
||||
location:
|
||||
modifierFlags:
|
||||
timestamp:
|
||||
windowNumber:
|
||||
context:
|
||||
subtype:
|
||||
data1:
|
||||
data2:
|
||||
)]
|
||||
unsafe fn otherEventWithType(
|
||||
type_: NSEventType,
|
||||
location: NSPoint,
|
||||
flags: NSEventModifierFlags,
|
||||
time: NSTimeInterval,
|
||||
window_num: NSInteger,
|
||||
context: Option<&NSObject>, // NSGraphicsContext
|
||||
subtype: NSEventSubtype,
|
||||
data1: NSInteger,
|
||||
data2: NSInteger,
|
||||
) -> Id<Self>;
|
||||
|
||||
pub fn dummy() -> Id<Self> {
|
||||
unsafe {
|
||||
Self::otherEventWithType(
|
||||
NSEventType::NSApplicationDefined,
|
||||
NSPoint::new(0.0, 0.0),
|
||||
NSEventModifierFlags::empty(),
|
||||
0.0,
|
||||
0,
|
||||
None,
|
||||
NSEventSubtype::NSWindowExposedEventType,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[method_id(
|
||||
keyEventWithType:
|
||||
location:
|
||||
modifierFlags:
|
||||
timestamp:
|
||||
windowNumber:
|
||||
context:
|
||||
characters:
|
||||
charactersIgnoringModifiers:
|
||||
isARepeat:
|
||||
keyCode:
|
||||
)]
|
||||
pub fn keyEventWithType(
|
||||
type_: NSEventType,
|
||||
location: NSPoint,
|
||||
modifier_flags: NSEventModifierFlags,
|
||||
timestamp: NSTimeInterval,
|
||||
window_num: NSInteger,
|
||||
context: Option<&NSObject>,
|
||||
characters: &NSString,
|
||||
characters_ignoring_modifiers: &NSString,
|
||||
is_a_repeat: bool,
|
||||
scancode: c_ushort,
|
||||
) -> Id<Self>;
|
||||
|
||||
#[method(locationInWindow)]
|
||||
pub fn locationInWindow(&self) -> NSPoint;
|
||||
|
||||
// TODO: MainThreadMarker
|
||||
#[method(pressedMouseButtons)]
|
||||
pub fn pressedMouseButtons() -> NSUInteger;
|
||||
|
||||
#[method(modifierFlags)]
|
||||
pub fn modifierFlags(&self) -> NSEventModifierFlags;
|
||||
|
||||
#[method(type)]
|
||||
pub fn type_(&self) -> NSEventType;
|
||||
|
||||
#[method(keyCode)]
|
||||
pub fn key_code(&self) -> c_ushort;
|
||||
|
||||
#[method(magnification)]
|
||||
pub fn magnification(&self) -> CGFloat;
|
||||
|
||||
#[method(phase)]
|
||||
pub fn phase(&self) -> NSEventPhase;
|
||||
|
||||
#[method(momentumPhase)]
|
||||
pub fn momentumPhase(&self) -> NSEventPhase;
|
||||
|
||||
#[method(deltaX)]
|
||||
pub fn deltaX(&self) -> CGFloat;
|
||||
|
||||
#[method(deltaY)]
|
||||
pub fn deltaY(&self) -> CGFloat;
|
||||
|
||||
#[method(buttonNumber)]
|
||||
pub fn buttonNumber(&self) -> NSInteger;
|
||||
|
||||
#[method(scrollingDeltaX)]
|
||||
pub fn scrollingDeltaX(&self) -> CGFloat;
|
||||
|
||||
#[method(scrollingDeltaY)]
|
||||
pub fn scrollingDeltaY(&self) -> CGFloat;
|
||||
|
||||
#[method(hasPreciseScrollingDeltas)]
|
||||
pub fn hasPreciseScrollingDeltas(&self) -> bool;
|
||||
|
||||
#[method(rotation)]
|
||||
pub fn rotation(&self) -> f32;
|
||||
|
||||
#[method(pressure)]
|
||||
pub fn pressure(&self) -> f32;
|
||||
|
||||
#[method(stage)]
|
||||
pub fn stage(&self) -> NSInteger;
|
||||
|
||||
#[method(isARepeat)]
|
||||
pub fn is_a_repeat(&self) -> bool;
|
||||
|
||||
#[method(windowNumber)]
|
||||
pub fn window_number(&self) -> NSInteger;
|
||||
|
||||
#[method(timestamp)]
|
||||
pub fn timestamp(&self) -> NSTimeInterval;
|
||||
|
||||
#[method_id(characters)]
|
||||
pub fn characters(&self) -> Option<Id<NSString>>;
|
||||
|
||||
#[method_id(charactersIgnoringModifiers)]
|
||||
pub fn charactersIgnoringModifiers(&self) -> Option<Id<NSString>>;
|
||||
|
||||
pub fn lshift_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELSHIFTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn rshift_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERSHIFTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn lctrl_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELCTLKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn rctrl_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERCTLKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn lalt_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELALTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn ralt_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERALTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn lcmd_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELCMDKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn rcmd_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERCMDKEYMASK != 0
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl NSCopying for NSEvent {}
|
||||
|
||||
// The values are from the https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h#L258-L259
|
||||
const NX_DEVICELCTLKEYMASK: u32 = 0x00000001;
|
||||
const NX_DEVICELSHIFTKEYMASK: u32 = 0x00000002;
|
||||
const NX_DEVICERSHIFTKEYMASK: u32 = 0x00000004;
|
||||
const NX_DEVICELCMDKEYMASK: u32 = 0x00000008;
|
||||
const NX_DEVICERCMDKEYMASK: u32 = 0x00000010;
|
||||
const NX_DEVICELALTKEYMASK: u32 = 0x00000020;
|
||||
const NX_DEVICERALTKEYMASK: u32 = 0x00000040;
|
||||
const NX_DEVICERCTLKEYMASK: u32 = 0x00002000;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct NSEventModifierFlags: NSUInteger {
|
||||
const NSAlphaShiftKeyMask = 1 << 16;
|
||||
const NSShiftKeyMask = 1 << 17;
|
||||
const NSControlKeyMask = 1 << 18;
|
||||
const NSAlternateKeyMask = 1 << 19;
|
||||
const NSCommandKeyMask = 1 << 20;
|
||||
const NSNumericPadKeyMask = 1 << 21;
|
||||
const NSHelpKeyMask = 1 << 22;
|
||||
const NSFunctionKeyMask = 1 << 23;
|
||||
const NSDeviceIndependentModifierFlagsMask = 0xffff0000;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSEventModifierFlags {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct NSEventPhase: NSUInteger {
|
||||
const NSEventPhaseNone = 0;
|
||||
const NSEventPhaseBegan = 0x1 << 0;
|
||||
const NSEventPhaseStationary = 0x1 << 1;
|
||||
const NSEventPhaseChanged = 0x1 << 2;
|
||||
const NSEventPhaseEnded = 0x1 << 3;
|
||||
const NSEventPhaseCancelled = 0x1 << 4;
|
||||
const NSEventPhaseMayBegin = 0x1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSEventPhase {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(i16)] // short
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSEventSubtype {
|
||||
// TODO: Not sure what these values are
|
||||
// NSMouseEventSubtype = NX_SUBTYPE_DEFAULT,
|
||||
// NSTabletPointEventSubtype = NX_SUBTYPE_TABLET_POINT,
|
||||
// NSTabletProximityEventSubtype = NX_SUBTYPE_TABLET_PROXIMITY
|
||||
// NSTouchEventSubtype = NX_SUBTYPE_MOUSE_TOUCH,
|
||||
NSWindowExposedEventType = 0,
|
||||
NSApplicationActivatedEventType = 1,
|
||||
NSApplicationDeactivatedEventType = 2,
|
||||
NSWindowMovedEventType = 4,
|
||||
NSScreenChangedEventType = 8,
|
||||
NSAWTEventType = 16,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSEventSubtype {
|
||||
const ENCODING: Encoding = i16::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
pub enum NSEventType {
|
||||
NSLeftMouseDown = 1,
|
||||
NSLeftMouseUp = 2,
|
||||
NSRightMouseDown = 3,
|
||||
NSRightMouseUp = 4,
|
||||
NSMouseMoved = 5,
|
||||
NSLeftMouseDragged = 6,
|
||||
NSRightMouseDragged = 7,
|
||||
NSMouseEntered = 8,
|
||||
NSMouseExited = 9,
|
||||
NSKeyDown = 10,
|
||||
NSKeyUp = 11,
|
||||
NSFlagsChanged = 12,
|
||||
NSAppKitDefined = 13,
|
||||
NSSystemDefined = 14,
|
||||
NSApplicationDefined = 15,
|
||||
NSPeriodic = 16,
|
||||
NSCursorUpdate = 17,
|
||||
NSScrollWheel = 22,
|
||||
NSTabletPoint = 23,
|
||||
NSTabletProximity = 24,
|
||||
NSOtherMouseDown = 25,
|
||||
NSOtherMouseUp = 26,
|
||||
NSOtherMouseDragged = 27,
|
||||
NSEventTypeGesture = 29,
|
||||
NSEventTypeMagnify = 30,
|
||||
NSEventTypeSwipe = 31,
|
||||
NSEventTypeRotate = 18,
|
||||
NSEventTypeBeginGesture = 19,
|
||||
NSEventTypeEndGesture = 20,
|
||||
NSEventTypePressure = 34,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSEventType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use icrate::Foundation::{NSData, NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
// TODO: Can this be mutable?
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSImage;
|
||||
|
||||
unsafe impl ClassType for NSImage {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// Documented Thread-Unsafe, but:
|
||||
// > One thread can create an NSImage object, draw to the image buffer,
|
||||
// > and pass it off to the main thread for drawing. The underlying image
|
||||
// > cache is shared among all threads.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-126728>
|
||||
//
|
||||
// So really only unsafe to mutate on several threads.
|
||||
unsafe impl Send for NSImage {}
|
||||
unsafe impl Sync for NSImage {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSImage {
|
||||
pub fn new_by_referencing_file(path: &NSString) -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::alloc(), initByReferencingFile: path] }
|
||||
}
|
||||
|
||||
pub fn new_with_data(data: &NSData) -> Id<Self> {
|
||||
unsafe { msg_send_id![Self::alloc(), initWithData: data] }
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,25 +0,0 @@
|
||||
use icrate::Foundation::NSObject;
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::NSMenuItem;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSMenu;
|
||||
|
||||
unsafe impl ClassType for NSMenu {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSMenu {
|
||||
#[method_id(new)]
|
||||
pub fn new() -> Id<Self>;
|
||||
|
||||
#[method(addItem:)]
|
||||
pub fn addItem(&self, item: &NSMenuItem);
|
||||
}
|
||||
);
|
||||
@@ -1,43 +0,0 @@
|
||||
use icrate::Foundation::{NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::Sel;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||
|
||||
use super::{NSEventModifierFlags, NSMenu};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSMenuItem;
|
||||
|
||||
unsafe impl ClassType for NSMenuItem {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSMenuItem {
|
||||
#[method_id(new)]
|
||||
pub fn new() -> Id<Self>;
|
||||
|
||||
pub fn newWithTitle(title: &NSString, action: Sel, key_equivalent: &NSString) -> Id<Self> {
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
Self::alloc(),
|
||||
initWithTitle: title,
|
||||
action: action,
|
||||
keyEquivalent: key_equivalent,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[method_id(separatorItem)]
|
||||
pub fn separatorItem() -> Id<Self>;
|
||||
|
||||
#[method(setKeyEquivalentModifierMask:)]
|
||||
pub fn setKeyEquivalentModifierMask(&self, mask: NSEventModifierFlags);
|
||||
|
||||
#[method(setSubmenu:)]
|
||||
pub fn setSubmenu(&self, submenu: &NSMenu);
|
||||
}
|
||||
);
|
||||
@@ -1,66 +0,0 @@
|
||||
//! Safe bindings for the AppKit framework.
|
||||
//!
|
||||
//! These are split out from the rest of `winit` to make safety easier to review.
|
||||
//! In the future, these should probably live in another crate like `cacao`.
|
||||
//!
|
||||
//! TODO: Main thread safety.
|
||||
// Objective-C methods have different conventions, and it's much easier to
|
||||
// understand if we just use the same names
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::enum_variant_names)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
mod appearance;
|
||||
mod application;
|
||||
mod button;
|
||||
mod color;
|
||||
mod control;
|
||||
mod cursor;
|
||||
mod event;
|
||||
mod image;
|
||||
mod menu;
|
||||
mod menu_item;
|
||||
mod pasteboard;
|
||||
mod responder;
|
||||
mod screen;
|
||||
mod tab_group;
|
||||
mod text_input_client;
|
||||
mod text_input_context;
|
||||
mod version;
|
||||
mod view;
|
||||
mod window;
|
||||
|
||||
pub(crate) use self::appearance::NSAppearance;
|
||||
pub(crate) use self::application::{
|
||||
NSApp, NSApplication, NSApplicationActivationPolicy, NSApplicationPresentationOptions,
|
||||
NSRequestUserAttentionType,
|
||||
};
|
||||
pub(crate) use self::button::NSButton;
|
||||
pub(crate) use self::color::NSColor;
|
||||
pub(crate) use self::control::NSControl;
|
||||
pub(crate) use self::cursor::NSCursor;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use self::event::{
|
||||
NSEvent, NSEventModifierFlags, NSEventPhase, NSEventSubtype, NSEventType,
|
||||
};
|
||||
pub(crate) use self::image::NSImage;
|
||||
pub(crate) use self::menu::NSMenu;
|
||||
pub(crate) use self::menu_item::NSMenuItem;
|
||||
pub(crate) use self::pasteboard::{NSFilenamesPboardType, NSPasteboard, NSPasteboardType};
|
||||
pub(crate) use self::responder::NSResponder;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use self::screen::{NSDeviceDescriptionKey, NSScreen};
|
||||
pub(crate) use self::tab_group::NSWindowTabGroup;
|
||||
pub(crate) use self::text_input_client::NSTextInputClient;
|
||||
pub(crate) use self::text_input_context::NSTextInputContext;
|
||||
pub(crate) use self::version::NSAppKitVersion;
|
||||
pub(crate) use self::view::{NSTrackingRectTag, NSView};
|
||||
pub(crate) use self::window::{
|
||||
NSBackingStoreType, NSWindow, NSWindowButton, NSWindowLevel, NSWindowOcclusionState,
|
||||
NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode,
|
||||
NSWindowTitleVisibility,
|
||||
};
|
||||
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
extern "C" {}
|
||||
@@ -1,26 +0,0 @@
|
||||
use icrate::Foundation::{NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSPasteboard;
|
||||
|
||||
unsafe impl ClassType for NSPasteboard {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSPasteboard {
|
||||
#[method_id(propertyListForType:)]
|
||||
pub fn propertyListForType(&self, type_: &NSPasteboardType) -> Id<NSObject>;
|
||||
}
|
||||
);
|
||||
|
||||
pub type NSPasteboardType = NSString;
|
||||
|
||||
extern "C" {
|
||||
pub static NSFilenamesPboardType: &'static NSPasteboardType;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
use icrate::Foundation::{NSArray, NSObject};
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::NSEvent;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NSResponder;
|
||||
|
||||
unsafe impl ClassType for NSResponder {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// Documented as "Thread-Unsafe".
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSResponder {
|
||||
#[method(interpretKeyEvents:)]
|
||||
pub(crate) unsafe fn interpretKeyEvents(&self, events: &NSArray<NSEvent>);
|
||||
}
|
||||
);
|
||||
@@ -1,65 +0,0 @@
|
||||
use icrate::ns_string;
|
||||
use icrate::Foundation::{CGFloat, NSArray, NSDictionary, NSNumber, NSObject, NSRect, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSScreen;
|
||||
|
||||
unsafe impl ClassType for NSScreen {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: Main thread marker!
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSScreen {
|
||||
/// The application object must have been created.
|
||||
#[method_id(mainScreen)]
|
||||
pub fn main() -> Option<Id<Self>>;
|
||||
|
||||
/// The application object must have been created.
|
||||
#[method_id(screens)]
|
||||
pub fn screens() -> Id<NSArray<Self>>;
|
||||
|
||||
#[method(frame)]
|
||||
pub fn frame(&self) -> NSRect;
|
||||
|
||||
#[method(visibleFrame)]
|
||||
pub fn visibleFrame(&self) -> NSRect;
|
||||
|
||||
#[method_id(deviceDescription)]
|
||||
pub fn deviceDescription(&self) -> Id<NSDictionary<NSDeviceDescriptionKey, AnyObject>>;
|
||||
|
||||
pub fn display_id(&self) -> u32 {
|
||||
let key = ns_string!("NSScreenNumber");
|
||||
|
||||
objc2::rc::autoreleasepool(|_| {
|
||||
let device_description = self.deviceDescription();
|
||||
|
||||
// Retrieve the CGDirectDisplayID associated with this screen
|
||||
//
|
||||
// SAFETY: The value from @"NSScreenNumber" in deviceDescription is guaranteed
|
||||
// to be an NSNumber. See documentation for `deviceDescription` for details:
|
||||
// <https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription?language=objc>
|
||||
let obj = device_description
|
||||
.get(key)
|
||||
.expect("failed getting screen display id from device description");
|
||||
let obj: *const AnyObject = obj;
|
||||
let obj: *const NSNumber = obj.cast();
|
||||
let obj: &NSNumber = unsafe { &*obj };
|
||||
|
||||
obj.as_u32()
|
||||
})
|
||||
}
|
||||
|
||||
#[method(backingScaleFactor)]
|
||||
pub fn backingScaleFactor(&self) -> CGFloat;
|
||||
}
|
||||
);
|
||||
|
||||
pub type NSDeviceDescriptionKey = NSString;
|
||||
@@ -1,31 +0,0 @@
|
||||
use icrate::Foundation::{NSArray, NSObject};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::NSWindow;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSWindowTabGroup;
|
||||
|
||||
unsafe impl ClassType for NSWindowTabGroup {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSWindowTabGroup {
|
||||
#[method(selectNextTab)]
|
||||
pub fn selectNextTab(&self);
|
||||
|
||||
#[method(selectPreviousTab)]
|
||||
pub fn selectPreviousTab(&self);
|
||||
|
||||
#[method_id(windows)]
|
||||
pub fn tabbedWindows(&self) -> Id<NSArray<NSWindow>>;
|
||||
|
||||
#[method(setSelectedWindow:)]
|
||||
pub fn setSelectedWindow(&self, window: &NSWindow);
|
||||
}
|
||||
);
|
||||
@@ -1,9 +0,0 @@
|
||||
use objc2::{extern_protocol, ProtocolType};
|
||||
|
||||
extern_protocol!(
|
||||
pub(crate) unsafe trait NSTextInputClient {
|
||||
// TODO: Methods
|
||||
}
|
||||
|
||||
unsafe impl ProtocolType for dyn NSTextInputClient {}
|
||||
);
|
||||
@@ -1,29 +0,0 @@
|
||||
use icrate::Foundation::{NSObject, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
type NSTextInputSourceIdentifier = NSString;
|
||||
|
||||
extern_class!(
|
||||
/// Main-Thread-Only!
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSTextInputContext;
|
||||
|
||||
unsafe impl ClassType for NSTextInputContext {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSTextInputContext {
|
||||
#[method(invalidateCharacterCoordinates)]
|
||||
pub fn invalidateCharacterCoordinates(&self);
|
||||
|
||||
#[method(discardMarkedText)]
|
||||
pub fn discardMarkedText(&self);
|
||||
|
||||
#[method_id(selectedKeyboardInputSource)]
|
||||
pub fn selectedKeyboardInputSource(&self) -> Option<Id<NSTextInputSourceIdentifier>>;
|
||||
}
|
||||
);
|
||||
@@ -1,62 +0,0 @@
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
|
||||
pub struct NSAppKitVersion(f64);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
impl NSAppKitVersion {
|
||||
pub fn current() -> Self {
|
||||
extern "C" {
|
||||
static NSAppKitVersionNumber: NSAppKitVersion;
|
||||
}
|
||||
|
||||
unsafe { NSAppKitVersionNumber }
|
||||
}
|
||||
|
||||
pub fn floor(self) -> Self {
|
||||
Self(self.0.floor())
|
||||
}
|
||||
|
||||
pub const NSAppKitVersionNumber10_0: Self = Self(577.0);
|
||||
pub const NSAppKitVersionNumber10_1: Self = Self(620.0);
|
||||
pub const NSAppKitVersionNumber10_2: Self = Self(663.0);
|
||||
pub const NSAppKitVersionNumber10_2_3: Self = Self(663.6);
|
||||
pub const NSAppKitVersionNumber10_3: Self = Self(743.0);
|
||||
pub const NSAppKitVersionNumber10_3_2: Self = Self(743.14);
|
||||
pub const NSAppKitVersionNumber10_3_3: Self = Self(743.2);
|
||||
pub const NSAppKitVersionNumber10_3_5: Self = Self(743.24);
|
||||
pub const NSAppKitVersionNumber10_3_7: Self = Self(743.33);
|
||||
pub const NSAppKitVersionNumber10_3_9: Self = Self(743.36);
|
||||
pub const NSAppKitVersionNumber10_4: Self = Self(824.0);
|
||||
pub const NSAppKitVersionNumber10_4_1: Self = Self(824.1);
|
||||
pub const NSAppKitVersionNumber10_4_3: Self = Self(824.23);
|
||||
pub const NSAppKitVersionNumber10_4_4: Self = Self(824.33);
|
||||
pub const NSAppKitVersionNumber10_4_7: Self = Self(824.41);
|
||||
pub const NSAppKitVersionNumber10_5: Self = Self(949.0);
|
||||
pub const NSAppKitVersionNumber10_5_2: Self = Self(949.27);
|
||||
pub const NSAppKitVersionNumber10_5_3: Self = Self(949.33);
|
||||
pub const NSAppKitVersionNumber10_6: Self = Self(1038.0);
|
||||
pub const NSAppKitVersionNumber10_7: Self = Self(1138.0);
|
||||
pub const NSAppKitVersionNumber10_7_2: Self = Self(1138.23);
|
||||
pub const NSAppKitVersionNumber10_7_3: Self = Self(1138.32);
|
||||
pub const NSAppKitVersionNumber10_7_4: Self = Self(1138.47);
|
||||
pub const NSAppKitVersionNumber10_8: Self = Self(1187.0);
|
||||
pub const NSAppKitVersionNumber10_9: Self = Self(1265.0);
|
||||
pub const NSAppKitVersionNumber10_10: Self = Self(1343.0);
|
||||
pub const NSAppKitVersionNumber10_10_2: Self = Self(1344.0);
|
||||
pub const NSAppKitVersionNumber10_10_3: Self = Self(1347.0);
|
||||
pub const NSAppKitVersionNumber10_10_4: Self = Self(1348.0);
|
||||
pub const NSAppKitVersionNumber10_10_5: Self = Self(1348.0);
|
||||
pub const NSAppKitVersionNumber10_10_Max: Self = Self(1349.0);
|
||||
pub const NSAppKitVersionNumber10_11: Self = Self(1404.0);
|
||||
pub const NSAppKitVersionNumber10_11_1: Self = Self(1404.13);
|
||||
pub const NSAppKitVersionNumber10_11_2: Self = Self(1404.34);
|
||||
pub const NSAppKitVersionNumber10_11_3: Self = Self(1404.34);
|
||||
pub const NSAppKitVersionNumber10_12: Self = Self(1504.0);
|
||||
pub const NSAppKitVersionNumber10_12_1: Self = Self(1504.60);
|
||||
pub const NSAppKitVersionNumber10_12_2: Self = Self(1504.76);
|
||||
pub const NSAppKitVersionNumber10_13: Self = Self(1561.0);
|
||||
pub const NSAppKitVersionNumber10_13_1: Self = Self(1561.1);
|
||||
pub const NSAppKitVersionNumber10_13_2: Self = Self(1561.2);
|
||||
pub const NSAppKitVersionNumber10_13_4: Self = Self(1561.4);
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
use std::ffi::c_void;
|
||||
use std::num::NonZeroIsize;
|
||||
use std::ptr;
|
||||
|
||||
use icrate::Foundation::{NSObject, NSPoint, NSRect};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::{NSCursor, NSResponder, NSTextInputContext, NSWindow};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSView;
|
||||
|
||||
unsafe impl ClassType for NSView {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// Documented as "Main Thread Only".
|
||||
// > generally thread safe, although operations on views such as creating,
|
||||
// > resizing, and moving should happen on the main thread.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
|
||||
//
|
||||
// > If you want to use a thread to draw to a view, bracket all drawing code
|
||||
// > between the lockFocusIfCanDraw and unlockFocus methods of NSView.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123351-BBCFIIEB>
|
||||
|
||||
extern_methods!(
|
||||
/// Getter methods
|
||||
unsafe impl NSView {
|
||||
#[method(frame)]
|
||||
pub fn frame(&self) -> NSRect;
|
||||
|
||||
#[method(bounds)]
|
||||
pub fn bounds(&self) -> NSRect;
|
||||
|
||||
#[method_id(inputContext)]
|
||||
pub fn inputContext(
|
||||
&self,
|
||||
// _mtm: MainThreadMarker,
|
||||
) -> Option<Id<NSTextInputContext>>;
|
||||
|
||||
#[method(hasMarkedText)]
|
||||
pub fn hasMarkedText(&self) -> bool;
|
||||
|
||||
#[method(convertPoint:fromView:)]
|
||||
pub fn convertPoint_fromView(&self, point: NSPoint, view: Option<&NSView>) -> NSPoint;
|
||||
|
||||
#[method_id(window)]
|
||||
pub fn window(&self) -> Option<Id<NSWindow>>;
|
||||
}
|
||||
|
||||
unsafe impl NSView {
|
||||
#[method(setWantsBestResolutionOpenGLSurface:)]
|
||||
pub fn setWantsBestResolutionOpenGLSurface(&self, value: bool);
|
||||
|
||||
#[method(setWantsLayer:)]
|
||||
pub fn setWantsLayer(&self, wants_layer: bool);
|
||||
|
||||
#[method(setPostsFrameChangedNotifications:)]
|
||||
pub fn setPostsFrameChangedNotifications(&self, value: bool);
|
||||
|
||||
#[method(removeTrackingRect:)]
|
||||
pub fn removeTrackingRect(&self, tag: NSTrackingRectTag);
|
||||
|
||||
#[method(addTrackingRect:owner:userData:assumeInside:)]
|
||||
unsafe fn inner_addTrackingRect(
|
||||
&self,
|
||||
rect: NSRect,
|
||||
owner: &AnyObject,
|
||||
user_data: *mut c_void,
|
||||
assume_inside: bool,
|
||||
) -> Option<NSTrackingRectTag>;
|
||||
|
||||
pub fn add_tracking_rect(&self, rect: NSRect, assume_inside: bool) -> NSTrackingRectTag {
|
||||
// SAFETY: The user data is NULL, so it is valid
|
||||
unsafe { self.inner_addTrackingRect(rect, self, ptr::null_mut(), assume_inside) }
|
||||
.expect("failed creating tracking rect")
|
||||
}
|
||||
|
||||
#[method(addCursorRect:cursor:)]
|
||||
// NSCursor safe to take by shared reference since it is already immutable
|
||||
pub fn addCursorRect(&self, rect: NSRect, cursor: &NSCursor);
|
||||
|
||||
#[method(setHidden:)]
|
||||
pub fn setHidden(&self, hidden: bool);
|
||||
}
|
||||
);
|
||||
|
||||
/// <https://developer.apple.com/documentation/appkit/nstrackingrecttag?language=objc>
|
||||
pub type NSTrackingRectTag = NonZeroIsize; // NSInteger, but non-zero!
|
||||
@@ -1,437 +0,0 @@
|
||||
use icrate::Foundation::{
|
||||
CGFloat, NSArray, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, NSUInteger,
|
||||
};
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::{
|
||||
NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView, NSWindowTabGroup,
|
||||
};
|
||||
|
||||
extern_class!(
|
||||
/// Main-Thread-Only!
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NSWindow;
|
||||
|
||||
unsafe impl ClassType for NSWindow {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
// Documented as "Main Thread Only", but:
|
||||
// > Thread safe in that you can create and manage them on a secondary thread.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123364>
|
||||
//
|
||||
// So could in theory be `Send`, and perhaps also `Sync` - but we would like
|
||||
// interior mutability on windows, since that's just much easier, and in that
|
||||
// case, they can't be!
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSWindow {
|
||||
#[method(frame)]
|
||||
pub(crate) fn frame(&self) -> NSRect;
|
||||
|
||||
#[method(backingScaleFactor)]
|
||||
pub(crate) fn backingScaleFactor(&self) -> CGFloat;
|
||||
|
||||
#[method_id(contentView)]
|
||||
pub(crate) fn contentView(&self) -> Id<NSView>;
|
||||
|
||||
#[method(setContentView:)]
|
||||
pub(crate) fn setContentView(&self, view: &NSView);
|
||||
|
||||
#[method(setInitialFirstResponder:)]
|
||||
pub(crate) fn setInitialFirstResponder(&self, view: &NSView);
|
||||
|
||||
#[method(makeFirstResponder:)]
|
||||
#[must_use]
|
||||
pub(crate) fn makeFirstResponder(&self, responder: Option<&NSResponder>) -> bool;
|
||||
|
||||
#[method(contentRectForFrameRect:)]
|
||||
pub(crate) fn contentRectForFrameRect(&self, windowFrame: NSRect) -> NSRect;
|
||||
|
||||
#[method_id(screen)]
|
||||
pub(crate) fn screen(&self) -> Option<Id<NSScreen>>;
|
||||
|
||||
#[method(setContentSize:)]
|
||||
pub(crate) fn setContentSize(&self, contentSize: NSSize);
|
||||
|
||||
#[method(setFrameTopLeftPoint:)]
|
||||
pub(crate) fn setFrameTopLeftPoint(&self, point: NSPoint);
|
||||
|
||||
#[method(setMinSize:)]
|
||||
pub(crate) fn setMinSize(&self, minSize: NSSize);
|
||||
|
||||
#[method(setMaxSize:)]
|
||||
pub(crate) fn setMaxSize(&self, maxSize: NSSize);
|
||||
|
||||
#[method(setResizeIncrements:)]
|
||||
pub(crate) fn setResizeIncrements(&self, increments: NSSize);
|
||||
|
||||
#[method(contentResizeIncrements)]
|
||||
pub(crate) fn contentResizeIncrements(&self) -> NSSize;
|
||||
|
||||
#[method(setContentResizeIncrements:)]
|
||||
pub(crate) fn setContentResizeIncrements(&self, increments: NSSize);
|
||||
|
||||
#[method(setFrame:display:)]
|
||||
pub(crate) fn setFrame_display(&self, frameRect: NSRect, flag: bool);
|
||||
|
||||
#[method(setMovable:)]
|
||||
pub(crate) fn setMovable(&self, movable: bool);
|
||||
|
||||
#[method(setSharingType:)]
|
||||
pub(crate) fn setSharingType(&self, sharingType: NSWindowSharingType);
|
||||
|
||||
#[method(setTabbingMode:)]
|
||||
pub(crate) fn setTabbingMode(&self, tabbingMode: NSWindowTabbingMode);
|
||||
|
||||
#[method(setOpaque:)]
|
||||
pub(crate) fn setOpaque(&self, opaque: bool);
|
||||
|
||||
#[method(hasShadow)]
|
||||
pub(crate) fn hasShadow(&self) -> bool;
|
||||
|
||||
#[method(setHasShadow:)]
|
||||
pub(crate) fn setHasShadow(&self, has_shadow: bool);
|
||||
|
||||
#[method(setIgnoresMouseEvents:)]
|
||||
pub(crate) fn setIgnoresMouseEvents(&self, ignores: bool);
|
||||
|
||||
#[method(setBackgroundColor:)]
|
||||
pub(crate) fn setBackgroundColor(&self, color: &NSColor);
|
||||
|
||||
#[method(styleMask)]
|
||||
pub(crate) fn styleMask(&self) -> NSWindowStyleMask;
|
||||
|
||||
#[method(setStyleMask:)]
|
||||
pub(crate) fn setStyleMask(&self, mask: NSWindowStyleMask);
|
||||
|
||||
#[method(registerForDraggedTypes:)]
|
||||
pub(crate) fn registerForDraggedTypes(&self, types: &NSArray<NSPasteboardType>);
|
||||
|
||||
#[method(makeKeyAndOrderFront:)]
|
||||
pub(crate) fn makeKeyAndOrderFront(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(orderFront:)]
|
||||
pub(crate) fn orderFront(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(miniaturize:)]
|
||||
pub(crate) fn miniaturize(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(deminiaturize:)]
|
||||
pub(crate) fn deminiaturize(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(toggleFullScreen:)]
|
||||
pub(crate) fn toggleFullScreen(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(orderOut:)]
|
||||
pub(crate) fn orderOut(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(zoom:)]
|
||||
pub(crate) fn zoom(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(selectNextKeyView:)]
|
||||
pub(crate) fn selectNextKeyView(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(selectPreviousKeyView:)]
|
||||
pub(crate) fn selectPreviousKeyView(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method_id(firstResponder)]
|
||||
pub(crate) fn firstResponder(&self) -> Option<Id<NSResponder>>;
|
||||
|
||||
#[method_id(standardWindowButton:)]
|
||||
pub(crate) fn standardWindowButton(&self, kind: NSWindowButton) -> Option<Id<NSButton>>;
|
||||
|
||||
#[method(setTitle:)]
|
||||
pub(crate) fn setTitle(&self, title: &NSString);
|
||||
|
||||
#[method_id(title)]
|
||||
pub(crate) fn title_(&self) -> Id<NSString>;
|
||||
|
||||
#[method(setReleasedWhenClosed:)]
|
||||
pub(crate) fn setReleasedWhenClosed(&self, val: bool);
|
||||
|
||||
#[method(setAcceptsMouseMovedEvents:)]
|
||||
pub(crate) fn setAcceptsMouseMovedEvents(&self, val: bool);
|
||||
|
||||
#[method(setTitlebarAppearsTransparent:)]
|
||||
pub(crate) fn setTitlebarAppearsTransparent(&self, val: bool);
|
||||
|
||||
#[method(setTitleVisibility:)]
|
||||
pub(crate) fn setTitleVisibility(&self, visibility: NSWindowTitleVisibility);
|
||||
|
||||
#[method(setMovableByWindowBackground:)]
|
||||
pub(crate) fn setMovableByWindowBackground(&self, val: bool);
|
||||
|
||||
#[method(setLevel:)]
|
||||
pub(crate) fn setLevel(&self, level: NSWindowLevel);
|
||||
|
||||
#[method(setAllowsAutomaticWindowTabbing:)]
|
||||
pub(crate) fn setAllowsAutomaticWindowTabbing(val: bool);
|
||||
|
||||
#[method(setTabbingIdentifier:)]
|
||||
pub(crate) fn setTabbingIdentifier(&self, identifier: &NSString);
|
||||
|
||||
#[method(setDocumentEdited:)]
|
||||
pub(crate) fn setDocumentEdited(&self, val: bool);
|
||||
|
||||
#[method(occlusionState)]
|
||||
pub(crate) fn occlusionState(&self) -> NSWindowOcclusionState;
|
||||
|
||||
#[method(center)]
|
||||
pub(crate) fn center(&self);
|
||||
|
||||
#[method(isResizable)]
|
||||
pub(crate) fn isResizable(&self) -> bool;
|
||||
|
||||
#[method(isMiniaturizable)]
|
||||
pub(crate) fn isMiniaturizable(&self) -> bool;
|
||||
|
||||
#[method(hasCloseBox)]
|
||||
pub(crate) fn hasCloseBox(&self) -> bool;
|
||||
|
||||
#[method(isMiniaturized)]
|
||||
pub(crate) fn isMiniaturized(&self) -> bool;
|
||||
|
||||
#[method(isVisible)]
|
||||
pub(crate) fn isVisible(&self) -> bool;
|
||||
|
||||
#[method(isKeyWindow)]
|
||||
pub(crate) fn isKeyWindow(&self) -> bool;
|
||||
|
||||
#[method(isZoomed)]
|
||||
pub(crate) fn isZoomed(&self) -> bool;
|
||||
|
||||
#[method(allowsAutomaticWindowTabbing)]
|
||||
pub(crate) fn allowsAutomaticWindowTabbing() -> bool;
|
||||
|
||||
#[method(selectNextTab)]
|
||||
pub(crate) fn selectNextTab(&self);
|
||||
|
||||
#[method_id(tabbingIdentifier)]
|
||||
pub(crate) fn tabbingIdentifier(&self) -> Id<NSString>;
|
||||
|
||||
#[method_id(tabGroup)]
|
||||
pub(crate) fn tabGroup(&self) -> Id<NSWindowTabGroup>;
|
||||
|
||||
#[method(isDocumentEdited)]
|
||||
pub(crate) fn isDocumentEdited(&self) -> bool;
|
||||
|
||||
#[method(close)]
|
||||
pub(crate) fn close(&self);
|
||||
|
||||
#[method(performWindowDragWithEvent:)]
|
||||
// TODO: Can this actually accept NULL?
|
||||
pub(crate) fn performWindowDragWithEvent(&self, event: Option<&NSEvent>);
|
||||
|
||||
#[method(invalidateCursorRectsForView:)]
|
||||
pub(crate) fn invalidateCursorRectsForView(&self, view: &NSView);
|
||||
|
||||
#[method(setDelegate:)]
|
||||
pub(crate) fn setDelegate(&self, delegate: Option<&NSObject>);
|
||||
|
||||
#[method(sendEvent:)]
|
||||
pub(crate) unsafe fn sendEvent(&self, event: &NSEvent);
|
||||
|
||||
#[method(addChildWindow:ordered:)]
|
||||
pub(crate) unsafe fn addChildWindow(&self, child: &NSWindow, ordered: NSWindowOrderingMode);
|
||||
}
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowTitleVisibility {
|
||||
#[doc(alias = "NSWindowTitleVisible")]
|
||||
Visible = 0,
|
||||
#[doc(alias = "NSWindowTitleHidden")]
|
||||
Hidden = 1,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowTitleVisibility {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowButton {
|
||||
#[doc(alias = "NSWindowCloseButton")]
|
||||
Close = 0,
|
||||
#[doc(alias = "NSWindowMiniaturizeButton")]
|
||||
Miniaturize = 1,
|
||||
#[doc(alias = "NSWindowZoomButton")]
|
||||
Zoom = 2,
|
||||
#[doc(alias = "NSWindowToolbarButton")]
|
||||
Toolbar = 3,
|
||||
#[doc(alias = "NSWindowDocumentIconButton")]
|
||||
DocumentIcon = 4,
|
||||
#[doc(alias = "NSWindowDocumentVersionsButton")]
|
||||
DocumentVersions = 6,
|
||||
#[doc(alias = "NSWindowFullScreenButton")]
|
||||
#[deprecated = "Deprecated since macOS 10.12"]
|
||||
FullScreen = 7,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowButton {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
// CGWindowLevel.h
|
||||
//
|
||||
// Note: There are two different things at play in this header:
|
||||
// `CGWindowLevel` and `CGWindowLevelKey`.
|
||||
//
|
||||
// It seems like there was a push towards using "key" values instead of the
|
||||
// raw window level values, and then you were supposed to use
|
||||
// `CGWindowLevelForKey` to get the actual level.
|
||||
//
|
||||
// But the values that `NSWindowLevel` has are compiled in, and as such has
|
||||
// to remain ABI compatible, so they're safe for us to hardcode as well.
|
||||
#[allow(dead_code)]
|
||||
mod window_level {
|
||||
const kCGNumReservedWindowLevels: i32 = 16;
|
||||
const kCGNumReservedBaseWindowLevels: i32 = 5;
|
||||
|
||||
pub const kCGBaseWindowLevel: i32 = i32::MIN;
|
||||
pub const kCGMinimumWindowLevel: i32 = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels;
|
||||
pub const kCGMaximumWindowLevel: i32 = i32::MAX - kCGNumReservedWindowLevels;
|
||||
|
||||
pub const kCGDesktopWindowLevel: i32 = kCGMinimumWindowLevel + 20;
|
||||
pub const kCGDesktopIconWindowLevel: i32 = kCGDesktopWindowLevel + 20;
|
||||
pub const kCGBackstopMenuLevel: i32 = -20;
|
||||
pub const kCGNormalWindowLevel: i32 = 0;
|
||||
pub const kCGFloatingWindowLevel: i32 = 3;
|
||||
pub const kCGTornOffMenuWindowLevel: i32 = 3;
|
||||
pub const kCGModalPanelWindowLevel: i32 = 8;
|
||||
pub const kCGUtilityWindowLevel: i32 = 19;
|
||||
pub const kCGDockWindowLevel: i32 = 20;
|
||||
pub const kCGMainMenuWindowLevel: i32 = 24;
|
||||
pub const kCGStatusWindowLevel: i32 = 25;
|
||||
pub const kCGPopUpMenuWindowLevel: i32 = 101;
|
||||
pub const kCGOverlayWindowLevel: i32 = 102;
|
||||
pub const kCGHelpWindowLevel: i32 = 200;
|
||||
pub const kCGDraggingWindowLevel: i32 = 500;
|
||||
pub const kCGScreenSaverWindowLevel: i32 = 1000;
|
||||
pub const kCGAssistiveTechHighWindowLevel: i32 = 1500;
|
||||
pub const kCGCursorWindowLevel: i32 = kCGMaximumWindowLevel - 1;
|
||||
}
|
||||
use window_level::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct NSWindowLevel(pub NSInteger);
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl NSWindowLevel {
|
||||
#[doc(alias = "BelowNormalWindowLevel")]
|
||||
pub const BELOW_NORMAL: Self = Self((kCGNormalWindowLevel - 1) as _);
|
||||
#[doc(alias = "NSNormalWindowLevel")]
|
||||
pub const Normal: Self = Self(kCGNormalWindowLevel as _);
|
||||
#[doc(alias = "NSFloatingWindowLevel")]
|
||||
pub const Floating: Self = Self(kCGFloatingWindowLevel as _);
|
||||
#[doc(alias = "NSTornOffMenuWindowLevel")]
|
||||
pub const TornOffMenu: Self = Self(kCGTornOffMenuWindowLevel as _);
|
||||
#[doc(alias = "NSModalPanelWindowLevel")]
|
||||
pub const ModalPanel: Self = Self(kCGModalPanelWindowLevel as _);
|
||||
#[doc(alias = "NSMainMenuWindowLevel")]
|
||||
pub const MainMenu: Self = Self(kCGMainMenuWindowLevel as _);
|
||||
#[doc(alias = "NSStatusWindowLevel")]
|
||||
pub const Status: Self = Self(kCGStatusWindowLevel as _);
|
||||
#[doc(alias = "NSPopUpMenuWindowLevel")]
|
||||
pub const PopUpMenu: Self = Self(kCGPopUpMenuWindowLevel as _);
|
||||
#[doc(alias = "NSScreenSaverWindowLevel")]
|
||||
pub const ScreenSaver: Self = Self(kCGScreenSaverWindowLevel as _);
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowLevel {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct NSWindowOcclusionState: NSUInteger {
|
||||
const NSWindowOcclusionStateVisible = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowOcclusionState {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NSWindowStyleMask: NSUInteger {
|
||||
const NSBorderlessWindowMask = 0;
|
||||
const NSTitledWindowMask = 1 << 0;
|
||||
const NSClosableWindowMask = 1 << 1;
|
||||
const NSMiniaturizableWindowMask = 1 << 2;
|
||||
const NSResizableWindowMask = 1 << 3;
|
||||
const NSTexturedBackgroundWindowMask = 1 << 8;
|
||||
const NSUnifiedTitleAndToolbarWindowMask = 1 << 12;
|
||||
const NSFullScreenWindowMask = 1 << 14;
|
||||
const NSFullSizeContentViewWindowMask = 1 << 15;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowStyleMask {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSBackingStoreType {
|
||||
NSBackingStoreRetained = 0,
|
||||
NSBackingStoreNonretained = 1,
|
||||
NSBackingStoreBuffered = 2,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSBackingStoreType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowSharingType {
|
||||
NSWindowSharingNone = 0,
|
||||
NSWindowSharingReadOnly = 1,
|
||||
NSWindowSharingReadWrite = 2,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowSharingType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowOrderingMode {
|
||||
NSWindowAbove = 1,
|
||||
NSWindowBelow = -1,
|
||||
NSWindowOut = 0,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowOrderingMode {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowTabbingMode {
|
||||
NSWindowTabbingModeAutomatic = 0,
|
||||
NSWindowTabbingModeDisallowed = 2,
|
||||
NSWindowTabbingModePreferred = 1,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowTabbingMode {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
228
src/platform_impl/macos/cursor.rs
Normal file
228
src/platform_impl/macos/cursor.rs
Normal file
@@ -0,0 +1,228 @@
|
||||
use icrate::AppKit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
|
||||
use icrate::Foundation::{
|
||||
ns_string, NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSSize,
|
||||
NSString,
|
||||
};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::Sel;
|
||||
use objc2::{msg_send_id, sel, ClassType};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::ffi::c_uchar;
|
||||
use std::slice;
|
||||
|
||||
use super::EventLoopWindowTarget;
|
||||
use crate::cursor::CursorImage;
|
||||
use crate::cursor::OnlyCursorImageBuilder;
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct CustomCursor(pub(crate) Id<NSCursor>);
|
||||
|
||||
// SAFETY: NSCursor is immutable and thread-safe
|
||||
// TODO(madsmtm): Put this logic in icrate itself
|
||||
unsafe impl Send for CustomCursor {}
|
||||
unsafe impl Sync for CustomCursor {}
|
||||
|
||||
impl CustomCursor {
|
||||
pub(crate) fn build<T>(
|
||||
cursor: OnlyCursorImageBuilder,
|
||||
_: &EventLoopWindowTarget<T>,
|
||||
) -> CustomCursor {
|
||||
Self(cursor_from_image(&cursor.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Id<NSCursor> {
|
||||
let width = cursor.width;
|
||||
let height = cursor.height;
|
||||
|
||||
let bitmap = unsafe {
|
||||
NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel(
|
||||
NSBitmapImageRep::alloc(),
|
||||
std::ptr::null_mut::<*mut c_uchar>(),
|
||||
width as isize,
|
||||
height as isize,
|
||||
8,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
NSDeviceRGBColorSpace,
|
||||
width as isize * 4,
|
||||
32,
|
||||
).unwrap()
|
||||
};
|
||||
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);
|
||||
|
||||
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
|
||||
}
|
||||
|
||||
pub(crate) fn default_cursor() -> Id<NSCursor> {
|
||||
NSCursor::arrowCursor()
|
||||
}
|
||||
|
||||
unsafe fn try_cursor_from_selector(sel: Sel) -> Option<Id<NSCursor>> {
|
||||
let cls = NSCursor::class();
|
||||
if cls.responds_to(sel) {
|
||||
let cursor: Id<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] };
|
||||
Some(cursor)
|
||||
} else {
|
||||
warn!("cursor `{sel}` appears to be invalid");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! def_undocumented_cursor {
|
||||
{$(
|
||||
$(#[$($m:meta)*])*
|
||||
fn $name:ident();
|
||||
)*} => {$(
|
||||
$(#[$($m)*])*
|
||||
#[allow(non_snake_case)]
|
||||
fn $name() -> Id<NSCursor> {
|
||||
unsafe { try_cursor_from_selector(sel!($name)).unwrap_or_else(|| default_cursor()) }
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
def_undocumented_cursor!(
|
||||
// Undocumented cursors: https://stackoverflow.com/a/46635398/5435443
|
||||
fn _helpCursor();
|
||||
fn _zoomInCursor();
|
||||
fn _zoomOutCursor();
|
||||
fn _windowResizeNorthEastCursor();
|
||||
fn _windowResizeNorthWestCursor();
|
||||
fn _windowResizeSouthEastCursor();
|
||||
fn _windowResizeSouthWestCursor();
|
||||
fn _windowResizeNorthEastSouthWestCursor();
|
||||
fn _windowResizeNorthWestSouthEastCursor();
|
||||
|
||||
// While these two are available, the former just loads a white arrow,
|
||||
// and the latter loads an ugly deflated beachball!
|
||||
// pub fn _moveCursor();
|
||||
// pub fn _waitCursor();
|
||||
|
||||
// An even more undocumented cursor...
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=522349
|
||||
fn busyButClickableCursor();
|
||||
);
|
||||
|
||||
// Note that loading `busybutclickable` with this code won't animate
|
||||
// the frames; instead you'll just get them all in a column.
|
||||
unsafe fn load_webkit_cursor(name: &NSString) -> Id<NSCursor> {
|
||||
// Snatch a cursor from WebKit; They fit the style of the native
|
||||
// cursors, and will seem completely standard to macOS users.
|
||||
//
|
||||
// https://stackoverflow.com/a/21786835/5435443
|
||||
let root = ns_string!("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors");
|
||||
let cursor_path = root.stringByAppendingPathComponent(name);
|
||||
|
||||
let pdf_path = cursor_path.stringByAppendingPathComponent(ns_string!("cursor.pdf"));
|
||||
let image = NSImage::initByReferencingFile(NSImage::alloc(), &pdf_path).unwrap();
|
||||
|
||||
// TODO: Handle PLists better
|
||||
let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist"));
|
||||
let info: Id<NSDictionary<NSObject, NSObject>> = unsafe {
|
||||
msg_send_id![
|
||||
<NSDictionary<NSObject, NSObject>>::class(),
|
||||
dictionaryWithContentsOfFile: &*info_path,
|
||||
]
|
||||
};
|
||||
let mut x = 0.0;
|
||||
if let Some(n) = info.get(&*ns_string!("hotx")) {
|
||||
if n.is_kind_of::<NSNumber>() {
|
||||
let ptr: *const NSObject = n;
|
||||
let ptr: *const NSNumber = ptr.cast();
|
||||
x = unsafe { &*ptr }.as_cgfloat()
|
||||
}
|
||||
}
|
||||
let mut y = 0.0;
|
||||
if let Some(n) = info.get(&*ns_string!("hotx")) {
|
||||
if n.is_kind_of::<NSNumber>() {
|
||||
let ptr: *const NSObject = n;
|
||||
let ptr: *const NSNumber = ptr.cast();
|
||||
y = unsafe { &*ptr }.as_cgfloat()
|
||||
}
|
||||
}
|
||||
|
||||
let hotspot = NSPoint::new(x, y);
|
||||
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
|
||||
}
|
||||
|
||||
fn webkit_move() -> Id<NSCursor> {
|
||||
unsafe { load_webkit_cursor(ns_string!("move")) }
|
||||
}
|
||||
|
||||
fn webkit_cell() -> Id<NSCursor> {
|
||||
unsafe { load_webkit_cursor(ns_string!("cell")) }
|
||||
}
|
||||
|
||||
pub(crate) fn invisible_cursor() -> Id<NSCursor> {
|
||||
// 16x16 GIF data for invisible cursor
|
||||
// You can reproduce this via ImageMagick.
|
||||
// $ convert -size 16x16 xc:none cursor.gif
|
||||
static CURSOR_BYTES: &[u8] = &[
|
||||
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00,
|
||||
0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x84, 0x8F, 0xA9, 0xCB, 0xED, 0x0F,
|
||||
0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B,
|
||||
];
|
||||
|
||||
static CURSOR: Lazy<CustomCursor> = Lazy::new(|| {
|
||||
// TODO: Consider using `dataWithBytesNoCopy:`
|
||||
let data = NSData::with_bytes(CURSOR_BYTES);
|
||||
let image = NSImage::initWithData(NSImage::alloc(), &data).unwrap();
|
||||
let hotspot = NSPoint::new(0.0, 0.0);
|
||||
CustomCursor(NSCursor::initWithImage_hotSpot(
|
||||
NSCursor::alloc(),
|
||||
&image,
|
||||
hotspot,
|
||||
))
|
||||
});
|
||||
|
||||
CURSOR.0.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Id<NSCursor> {
|
||||
match icon {
|
||||
CursorIcon::Default => default_cursor(),
|
||||
CursorIcon::Pointer => NSCursor::pointingHandCursor(),
|
||||
CursorIcon::Grab => NSCursor::openHandCursor(),
|
||||
CursorIcon::Grabbing => NSCursor::closedHandCursor(),
|
||||
CursorIcon::Text => NSCursor::IBeamCursor(),
|
||||
CursorIcon::VerticalText => NSCursor::IBeamCursorForVerticalLayout(),
|
||||
CursorIcon::Copy => NSCursor::dragCopyCursor(),
|
||||
CursorIcon::Alias => NSCursor::dragLinkCursor(),
|
||||
CursorIcon::NotAllowed | CursorIcon::NoDrop => NSCursor::operationNotAllowedCursor(),
|
||||
CursorIcon::ContextMenu => NSCursor::contextualMenuCursor(),
|
||||
CursorIcon::Crosshair => NSCursor::crosshairCursor(),
|
||||
CursorIcon::EResize => NSCursor::resizeRightCursor(),
|
||||
CursorIcon::NResize => NSCursor::resizeUpCursor(),
|
||||
CursorIcon::WResize => NSCursor::resizeLeftCursor(),
|
||||
CursorIcon::SResize => NSCursor::resizeDownCursor(),
|
||||
CursorIcon::EwResize | CursorIcon::ColResize => NSCursor::resizeLeftRightCursor(),
|
||||
CursorIcon::NsResize | CursorIcon::RowResize => NSCursor::resizeUpDownCursor(),
|
||||
CursorIcon::Help => _helpCursor(),
|
||||
CursorIcon::ZoomIn => _zoomInCursor(),
|
||||
CursorIcon::ZoomOut => _zoomOutCursor(),
|
||||
CursorIcon::NeResize => _windowResizeNorthEastCursor(),
|
||||
CursorIcon::NwResize => _windowResizeNorthWestCursor(),
|
||||
CursorIcon::SeResize => _windowResizeSouthEastCursor(),
|
||||
CursorIcon::SwResize => _windowResizeSouthWestCursor(),
|
||||
CursorIcon::NeswResize => _windowResizeNorthEastSouthWestCursor(),
|
||||
CursorIcon::NwseResize => _windowResizeNorthWestSouthEastCursor(),
|
||||
// This is the wrong semantics for `Wait`, but it's the same as
|
||||
// what's used in Safari and Chrome.
|
||||
CursorIcon::Wait | CursorIcon::Progress => busyButClickableCursor(),
|
||||
CursorIcon::Move | CursorIcon::AllScroll => webkit_move(),
|
||||
CursorIcon::Cell => webkit_cell(),
|
||||
_ => default_cursor(),
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,24 @@ use core_foundation::{
|
||||
base::CFRelease,
|
||||
data::{CFDataGetBytePtr, CFDataRef},
|
||||
};
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
use icrate::AppKit::{
|
||||
NSEvent, NSEventModifierFlagCommand, NSEventModifierFlagControl, NSEventModifierFlagOption,
|
||||
NSEventModifierFlagShift, NSEventModifierFlags, NSEventSubtypeWindowExposed,
|
||||
NSEventTypeApplicationDefined,
|
||||
};
|
||||
use icrate::Foundation::{MainThreadMarker, NSPoint};
|
||||
use objc2::rc::Id;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use super::appkit::{NSEvent, NSEventModifierFlags};
|
||||
use crate::{
|
||||
event::{ElementState, KeyEvent, Modifiers},
|
||||
keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NativeKey, NativeKeyCode,
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey,
|
||||
NativeKeyCode, PhysicalKey,
|
||||
},
|
||||
platform::{
|
||||
modifier_supplement::KeyEventExtModifierSupplement, scancode::PhysicalKeyExtScancode,
|
||||
},
|
||||
platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode},
|
||||
platform_impl::platform::ffi,
|
||||
};
|
||||
|
||||
@@ -85,7 +93,9 @@ pub fn get_modifierless_char(scancode: u16) -> Key {
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
if result_len == 0 {
|
||||
log::error!("`UCKeyTranslate` was succesful but gave a string of 0 length.");
|
||||
// This is fine - not all keys have text representation.
|
||||
// For instance, users that have mapped the `Fn` key to toggle
|
||||
// keyboard layouts will hit this code path.
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
let chars = String::from_utf16_lossy(&string[0..result_len as usize]);
|
||||
@@ -93,8 +103,7 @@ pub fn get_modifierless_char(scancode: u16) -> Key {
|
||||
}
|
||||
|
||||
fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
|
||||
let string = ns_event
|
||||
.charactersIgnoringModifiers()
|
||||
let string = unsafe { ns_event.charactersIgnoringModifiers() }
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_default();
|
||||
if string.is_empty() {
|
||||
@@ -112,25 +121,25 @@ pub(crate) fn create_key_event(
|
||||
ns_event: &NSEvent,
|
||||
is_press: bool,
|
||||
is_repeat: bool,
|
||||
key_override: Option<KeyCode>,
|
||||
key_override: Option<PhysicalKey>,
|
||||
) -> KeyEvent {
|
||||
use ElementState::{Pressed, Released};
|
||||
let state = if is_press { Pressed } else { Released };
|
||||
|
||||
let scancode = ns_event.key_code();
|
||||
let mut physical_key = key_override.unwrap_or_else(|| KeyCode::from_scancode(scancode as u32));
|
||||
let scancode = unsafe { ns_event.keyCode() };
|
||||
let mut physical_key =
|
||||
key_override.unwrap_or_else(|| PhysicalKey::from_scancode(scancode as u32));
|
||||
|
||||
let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() {
|
||||
None
|
||||
} else {
|
||||
let characters = ns_event
|
||||
.characters()
|
||||
let characters = unsafe { ns_event.characters() }
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_default();
|
||||
if characters.is_empty() {
|
||||
None
|
||||
} else {
|
||||
if matches!(physical_key, KeyCode::Unidentified(_)) {
|
||||
if matches!(physical_key, PhysicalKey::Unidentified(_)) {
|
||||
// The key may be one of the funky function keys
|
||||
physical_key = extra_function_key_to_code(scancode, &characters);
|
||||
}
|
||||
@@ -142,8 +151,8 @@ pub(crate) fn create_key_event(
|
||||
let (logical_key, key_without_modifiers) = if matches!(key_from_code, Key::Unidentified(_)) {
|
||||
let key_without_modifiers = get_modifierless_char(scancode);
|
||||
|
||||
let modifiers = NSEvent::modifierFlags(ns_event);
|
||||
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
|
||||
let modifiers = unsafe { ns_event.modifierFlags() };
|
||||
let has_ctrl = flags_contains(modifiers, NSEventModifierFlagControl);
|
||||
|
||||
let logical_key = match text_with_all_modifiers.as_ref() {
|
||||
// Only checking for ctrl here, not checking for alt because we DO want to
|
||||
@@ -152,13 +161,11 @@ pub(crate) fn create_key_event(
|
||||
// Also not checking if this is a release event because then this issue would
|
||||
// still affect the key release.
|
||||
Some(text) if !has_ctrl => Key::Character(text.clone()),
|
||||
_ => {
|
||||
let modifierless_chars = match key_without_modifiers.as_ref() {
|
||||
Key::Character(ch) => ch,
|
||||
_ => "",
|
||||
};
|
||||
get_logical_key_char(ns_event, modifierless_chars)
|
||||
}
|
||||
_ => match key_without_modifiers.as_ref() {
|
||||
Key::Character(ch) => get_logical_key_char(ns_event, ch),
|
||||
// Don't try to get text for events which likely don't have it.
|
||||
_ => key_without_modifiers.clone(),
|
||||
},
|
||||
};
|
||||
|
||||
(logical_key, key_without_modifiers)
|
||||
@@ -188,65 +195,75 @@ pub(crate) fn create_key_event(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code_to_key(code: KeyCode, scancode: u16) -> Key {
|
||||
match code {
|
||||
KeyCode::Enter => Key::Enter,
|
||||
KeyCode::Tab => Key::Tab,
|
||||
KeyCode::Space => Key::Space,
|
||||
KeyCode::Backspace => Key::Backspace,
|
||||
KeyCode::Escape => Key::Escape,
|
||||
KeyCode::SuperRight => Key::Super,
|
||||
KeyCode::SuperLeft => Key::Super,
|
||||
KeyCode::ShiftLeft => Key::Shift,
|
||||
KeyCode::AltLeft => Key::Alt,
|
||||
KeyCode::ControlLeft => Key::Control,
|
||||
KeyCode::ShiftRight => Key::Shift,
|
||||
KeyCode::AltRight => Key::Alt,
|
||||
KeyCode::ControlRight => Key::Control,
|
||||
pub fn code_to_key(key: PhysicalKey, scancode: u16) -> Key {
|
||||
let code = match key {
|
||||
PhysicalKey::Code(code) => code,
|
||||
PhysicalKey::Unidentified(code) => return Key::Unidentified(code.into()),
|
||||
};
|
||||
|
||||
KeyCode::NumLock => Key::NumLock,
|
||||
KeyCode::AudioVolumeUp => Key::AudioVolumeUp,
|
||||
KeyCode::AudioVolumeDown => Key::AudioVolumeDown,
|
||||
Key::Named(match code {
|
||||
KeyCode::Enter => NamedKey::Enter,
|
||||
KeyCode::Tab => NamedKey::Tab,
|
||||
KeyCode::Space => NamedKey::Space,
|
||||
KeyCode::Backspace => NamedKey::Backspace,
|
||||
KeyCode::Escape => NamedKey::Escape,
|
||||
KeyCode::SuperRight => NamedKey::Super,
|
||||
KeyCode::SuperLeft => NamedKey::Super,
|
||||
KeyCode::ShiftLeft => NamedKey::Shift,
|
||||
KeyCode::AltLeft => NamedKey::Alt,
|
||||
KeyCode::ControlLeft => NamedKey::Control,
|
||||
KeyCode::ShiftRight => NamedKey::Shift,
|
||||
KeyCode::AltRight => NamedKey::Alt,
|
||||
KeyCode::ControlRight => NamedKey::Control,
|
||||
|
||||
KeyCode::NumLock => NamedKey::NumLock,
|
||||
KeyCode::AudioVolumeUp => NamedKey::AudioVolumeUp,
|
||||
KeyCode::AudioVolumeDown => NamedKey::AudioVolumeDown,
|
||||
|
||||
// Other numpad keys all generate text on macOS (if I understand correctly)
|
||||
KeyCode::NumpadEnter => Key::Enter,
|
||||
KeyCode::NumpadEnter => NamedKey::Enter,
|
||||
|
||||
KeyCode::F1 => Key::F1,
|
||||
KeyCode::F2 => Key::F2,
|
||||
KeyCode::F3 => Key::F3,
|
||||
KeyCode::F4 => Key::F4,
|
||||
KeyCode::F5 => Key::F5,
|
||||
KeyCode::F6 => Key::F6,
|
||||
KeyCode::F7 => Key::F7,
|
||||
KeyCode::F8 => Key::F8,
|
||||
KeyCode::F9 => Key::F9,
|
||||
KeyCode::F10 => Key::F10,
|
||||
KeyCode::F11 => Key::F11,
|
||||
KeyCode::F12 => Key::F12,
|
||||
KeyCode::F13 => Key::F13,
|
||||
KeyCode::F14 => Key::F14,
|
||||
KeyCode::F15 => Key::F15,
|
||||
KeyCode::F16 => Key::F16,
|
||||
KeyCode::F17 => Key::F17,
|
||||
KeyCode::F18 => Key::F18,
|
||||
KeyCode::F19 => Key::F19,
|
||||
KeyCode::F20 => Key::F20,
|
||||
KeyCode::F1 => NamedKey::F1,
|
||||
KeyCode::F2 => NamedKey::F2,
|
||||
KeyCode::F3 => NamedKey::F3,
|
||||
KeyCode::F4 => NamedKey::F4,
|
||||
KeyCode::F5 => NamedKey::F5,
|
||||
KeyCode::F6 => NamedKey::F6,
|
||||
KeyCode::F7 => NamedKey::F7,
|
||||
KeyCode::F8 => NamedKey::F8,
|
||||
KeyCode::F9 => NamedKey::F9,
|
||||
KeyCode::F10 => NamedKey::F10,
|
||||
KeyCode::F11 => NamedKey::F11,
|
||||
KeyCode::F12 => NamedKey::F12,
|
||||
KeyCode::F13 => NamedKey::F13,
|
||||
KeyCode::F14 => NamedKey::F14,
|
||||
KeyCode::F15 => NamedKey::F15,
|
||||
KeyCode::F16 => NamedKey::F16,
|
||||
KeyCode::F17 => NamedKey::F17,
|
||||
KeyCode::F18 => NamedKey::F18,
|
||||
KeyCode::F19 => NamedKey::F19,
|
||||
KeyCode::F20 => NamedKey::F20,
|
||||
|
||||
KeyCode::Insert => Key::Insert,
|
||||
KeyCode::Home => Key::Home,
|
||||
KeyCode::PageUp => Key::PageUp,
|
||||
KeyCode::Delete => Key::Delete,
|
||||
KeyCode::End => Key::End,
|
||||
KeyCode::PageDown => Key::PageDown,
|
||||
KeyCode::ArrowLeft => Key::ArrowLeft,
|
||||
KeyCode::ArrowRight => Key::ArrowRight,
|
||||
KeyCode::ArrowDown => Key::ArrowDown,
|
||||
KeyCode::ArrowUp => Key::ArrowUp,
|
||||
_ => Key::Unidentified(NativeKey::MacOS(scancode)),
|
||||
}
|
||||
KeyCode::Insert => NamedKey::Insert,
|
||||
KeyCode::Home => NamedKey::Home,
|
||||
KeyCode::PageUp => NamedKey::PageUp,
|
||||
KeyCode::Delete => NamedKey::Delete,
|
||||
KeyCode::End => NamedKey::End,
|
||||
KeyCode::PageDown => NamedKey::PageDown,
|
||||
KeyCode::ArrowLeft => NamedKey::ArrowLeft,
|
||||
KeyCode::ArrowRight => NamedKey::ArrowRight,
|
||||
KeyCode::ArrowDown => NamedKey::ArrowDown,
|
||||
KeyCode::ArrowUp => NamedKey::ArrowUp,
|
||||
_ => return Key::Unidentified(NativeKey::MacOS(scancode)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn code_to_location(code: KeyCode) -> KeyLocation {
|
||||
pub fn code_to_location(key: PhysicalKey) -> KeyLocation {
|
||||
let code = match key {
|
||||
PhysicalKey::Code(code) => code,
|
||||
PhysicalKey::Unidentified(_) => return KeyLocation::Standard,
|
||||
};
|
||||
|
||||
match code {
|
||||
KeyCode::SuperRight => KeyLocation::Right,
|
||||
KeyCode::SuperLeft => KeyLocation::Left,
|
||||
@@ -283,56 +300,98 @@ pub fn code_to_location(code: KeyCode) -> KeyLocation {
|
||||
// While F1-F20 have scancodes we can match on, we have to check against UTF-16
|
||||
// constants for the rest.
|
||||
// https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?preferredLanguage=occ
|
||||
pub fn extra_function_key_to_code(scancode: u16, string: &str) -> KeyCode {
|
||||
pub fn extra_function_key_to_code(scancode: u16, string: &str) -> PhysicalKey {
|
||||
if let Some(ch) = string.encode_utf16().next() {
|
||||
match ch {
|
||||
0xf718 => KeyCode::F21,
|
||||
0xf719 => KeyCode::F22,
|
||||
0xf71a => KeyCode::F23,
|
||||
0xf71b => KeyCode::F24,
|
||||
_ => KeyCode::Unidentified(NativeKeyCode::MacOS(scancode)),
|
||||
0xf718 => PhysicalKey::Code(KeyCode::F21),
|
||||
0xf719 => PhysicalKey::Code(KeyCode::F22),
|
||||
0xf71a => PhysicalKey::Code(KeyCode::F23),
|
||||
0xf71b => PhysicalKey::Code(KeyCode::F24),
|
||||
_ => PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode)),
|
||||
}
|
||||
} else {
|
||||
KeyCode::Unidentified(NativeKeyCode::MacOS(scancode))
|
||||
PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode))
|
||||
}
|
||||
}
|
||||
|
||||
// The values are from the https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h#L258-L259
|
||||
const NX_DEVICELCTLKEYMASK: NSEventModifierFlags = 0x00000001;
|
||||
const NX_DEVICELSHIFTKEYMASK: NSEventModifierFlags = 0x00000002;
|
||||
const NX_DEVICERSHIFTKEYMASK: NSEventModifierFlags = 0x00000004;
|
||||
const NX_DEVICELCMDKEYMASK: NSEventModifierFlags = 0x00000008;
|
||||
const NX_DEVICERCMDKEYMASK: NSEventModifierFlags = 0x00000010;
|
||||
const NX_DEVICELALTKEYMASK: NSEventModifierFlags = 0x00000020;
|
||||
const NX_DEVICERALTKEYMASK: NSEventModifierFlags = 0x00000040;
|
||||
const NX_DEVICERCTLKEYMASK: NSEventModifierFlags = 0x00002000;
|
||||
|
||||
pub(super) fn flags_contains(flags: NSEventModifierFlags, value: NSEventModifierFlags) -> bool {
|
||||
flags & value == value
|
||||
}
|
||||
|
||||
pub(super) fn lalt_pressed(event: &NSEvent) -> bool {
|
||||
flags_contains(unsafe { event.modifierFlags() }, NX_DEVICELALTKEYMASK)
|
||||
}
|
||||
|
||||
pub(super) fn ralt_pressed(event: &NSEvent) -> bool {
|
||||
flags_contains(unsafe { event.modifierFlags() }, NX_DEVICERALTKEYMASK)
|
||||
}
|
||||
|
||||
pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
|
||||
let flags = event.modifierFlags();
|
||||
let flags = unsafe { event.modifierFlags() };
|
||||
let mut state = ModifiersState::empty();
|
||||
let mut pressed_mods = ModifiersKeys::empty();
|
||||
|
||||
state.set(
|
||||
ModifiersState::SHIFT,
|
||||
flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||
flags_contains(flags, NSEventModifierFlagShift),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::LSHIFT,
|
||||
flags_contains(flags, NX_DEVICELSHIFTKEYMASK),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::RSHIFT,
|
||||
flags_contains(flags, NX_DEVICERSHIFTKEYMASK),
|
||||
);
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LSHIFT, event.lshift_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RSHIFT, event.rshift_pressed());
|
||||
|
||||
state.set(
|
||||
ModifiersState::CONTROL,
|
||||
flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||
flags_contains(flags, NSEventModifierFlagControl),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::LCONTROL,
|
||||
flags_contains(flags, NX_DEVICELCTLKEYMASK),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::RCONTROL,
|
||||
flags_contains(flags, NX_DEVICERCTLKEYMASK),
|
||||
);
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LCONTROL, event.lctrl_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RCONTROL, event.rctrl_pressed());
|
||||
|
||||
state.set(
|
||||
ModifiersState::ALT,
|
||||
flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||
flags_contains(flags, NSEventModifierFlagOption),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::LALT,
|
||||
flags_contains(flags, NX_DEVICELALTKEYMASK),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::RALT,
|
||||
flags_contains(flags, NX_DEVICERALTKEYMASK),
|
||||
);
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LALT, event.lalt_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RALT, event.ralt_pressed());
|
||||
|
||||
state.set(
|
||||
ModifiersState::SUPER,
|
||||
flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||
flags_contains(flags, NSEventModifierFlagCommand),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::LSUPER,
|
||||
flags_contains(flags, NX_DEVICELCMDKEYMASK),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::RSUPER,
|
||||
flags_contains(flags, NX_DEVICERCMDKEYMASK),
|
||||
);
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LSUPER, event.lcmd_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RSUPER, event.rcmd_pressed());
|
||||
|
||||
Modifiers {
|
||||
state,
|
||||
@@ -340,9 +399,30 @@ pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyCodeExtScancode for KeyCode {
|
||||
pub(super) fn dummy_event() -> Option<Id<NSEvent>> {
|
||||
unsafe {
|
||||
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2(
|
||||
NSEventTypeApplicationDefined,
|
||||
NSPoint::new(0.0, 0.0),
|
||||
0, // Empty NSEventModifierFlags
|
||||
0.0,
|
||||
0,
|
||||
None,
|
||||
NSEventSubtypeWindowExposed,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalKeyExtScancode for PhysicalKey {
|
||||
fn to_scancode(self) -> Option<u32> {
|
||||
match self {
|
||||
let code = match self {
|
||||
PhysicalKey::Code(code) => code,
|
||||
PhysicalKey::Unidentified(_) => return None,
|
||||
};
|
||||
|
||||
match code {
|
||||
KeyCode::KeyA => Some(0x00),
|
||||
KeyCode::KeyS => Some(0x01),
|
||||
KeyCode::KeyD => Some(0x02),
|
||||
@@ -458,8 +538,8 @@ impl KeyCodeExtScancode for KeyCode {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_scancode(scancode: u32) -> KeyCode {
|
||||
match scancode {
|
||||
fn from_scancode(scancode: u32) -> PhysicalKey {
|
||||
PhysicalKey::Code(match scancode {
|
||||
0x00 => KeyCode::KeyA,
|
||||
0x01 => KeyCode::KeyS,
|
||||
0x02 => KeyCode::KeyD,
|
||||
@@ -596,7 +676,7 @@ impl KeyCodeExtScancode for KeyCode {
|
||||
// 0xA is the caret (^) an macOS's German QERTZ layout. This key is at the same location as
|
||||
// backquote (`) on Windows' US layout.
|
||||
0xa => KeyCode::Backquote,
|
||||
_ => KeyCode::Unidentified(NativeKeyCode::MacOS(scancode as u16)),
|
||||
}
|
||||
_ => return PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode as u16)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,18 @@ use core_foundation::runloop::{
|
||||
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
|
||||
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use objc2::runtime::NSObjectProtocol;
|
||||
use icrate::AppKit::{
|
||||
NSApplication, NSApplicationActivationPolicyAccessory, NSApplicationActivationPolicyProhibited,
|
||||
NSApplicationActivationPolicyRegular, NSWindow,
|
||||
};
|
||||
use icrate::Foundation::{MainThreadMarker, NSObjectProtocol};
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
use objc2::{
|
||||
rc::{autoreleasepool, Id},
|
||||
runtime::ProtocolObject,
|
||||
};
|
||||
|
||||
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent, NSWindow};
|
||||
use super::event::dummy_event;
|
||||
use crate::{
|
||||
error::EventLoopError,
|
||||
event::Event,
|
||||
@@ -123,19 +129,19 @@ impl<T: 'static> EventLoopWindowTarget<T> {
|
||||
|
||||
impl<T> EventLoopWindowTarget<T> {
|
||||
pub(crate) fn hide_application(&self) {
|
||||
NSApplication::shared(self.mtm).hide(None)
|
||||
NSApplication::sharedApplication(self.mtm).hide(None)
|
||||
}
|
||||
|
||||
pub(crate) fn hide_other_applications(&self) {
|
||||
NSApplication::shared(self.mtm).hideOtherApplications(None)
|
||||
NSApplication::sharedApplication(self.mtm).hideOtherApplications(None)
|
||||
}
|
||||
|
||||
pub(crate) fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
|
||||
NSWindow::setAllowsAutomaticWindowTabbing(enabled)
|
||||
NSWindow::setAllowsAutomaticWindowTabbing(enabled, self.mtm)
|
||||
}
|
||||
|
||||
pub(crate) fn allows_automatic_window_tabbing(&self) -> bool {
|
||||
NSWindow::allowsAutomaticWindowTabbing()
|
||||
NSWindow::allowsAutomaticWindowTabbing(self.mtm)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,20 +202,20 @@ impl<T> EventLoop<T> {
|
||||
panic!("`winit` requires control over the principal class. You must create the event loop before other parts of your application initialize NSApplication");
|
||||
}
|
||||
|
||||
use NSApplicationActivationPolicy::*;
|
||||
let activation_policy = match attributes.activation_policy {
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
|
||||
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory,
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited,
|
||||
};
|
||||
let delegate = ApplicationDelegate::new(
|
||||
mtm,
|
||||
activation_policy,
|
||||
attributes.default_menu,
|
||||
attributes.activate_ignoring_other_apps,
|
||||
);
|
||||
|
||||
autoreleasepool(|_| {
|
||||
app.setDelegate(&delegate);
|
||||
app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
});
|
||||
|
||||
let panic_info: Rc<PanicInfo> = Default::default();
|
||||
@@ -307,7 +313,7 @@ impl<T> EventLoop<T> {
|
||||
|
||||
// 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 `NSApp` and saving the
|
||||
// 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() {
|
||||
@@ -355,8 +361,6 @@ impl<T> EventLoop<T> {
|
||||
self._callback = Some(Rc::clone(&callback));
|
||||
|
||||
autoreleasepool(|_| {
|
||||
let app = NSApp();
|
||||
|
||||
// A bit of juggling with the callback references to make sure
|
||||
// that `self.callback` is the only owner of the callback.
|
||||
let weak_cb: Weak<_> = Rc::downgrade(&callback);
|
||||
@@ -377,25 +381,24 @@ impl<T> EventLoop<T> {
|
||||
// catch panics to make sure we can't unwind without clearing the set callback
|
||||
// (which would leave the global `AppState` in an undefined, unsafe state)
|
||||
let catch_result = catch_unwind(AssertUnwindSafe(|| {
|
||||
// As a special case, if the `NSApp` hasn't been launched yet then we at least run
|
||||
// As a special case, if the application hasn't been launched yet then we at least run
|
||||
// the loop until it has fully launched.
|
||||
if !AppState::is_launched() {
|
||||
debug_assert!(!AppState::is_running());
|
||||
|
||||
AppState::request_stop_on_launch();
|
||||
unsafe {
|
||||
app.run();
|
||||
self.app.run();
|
||||
}
|
||||
|
||||
// Note: we dispatch `NewEvents(Init)` + `Resumed` events after the `NSApp` has launched
|
||||
// Note: we dispatch `NewEvents(Init)` + `Resumed` events after the application has launched
|
||||
} else if !AppState::is_running() {
|
||||
// Even though the NSApp may have been launched, it's possible we aren't running
|
||||
// Even though the application may have been launched, it's possible we aren't running
|
||||
// if the `EventLoop` was run before and has since exited. This indicates that
|
||||
// we just starting to re-run the same `EventLoop` again.
|
||||
AppState::start_running(); // Set is_running = true + dispatch `NewEvents(Init)` + `Resumed`
|
||||
} else {
|
||||
// Only run the NSApp for as long as the given `Duration` allows so we
|
||||
// don't block the external loop.
|
||||
// Only run for as long as the given `Duration` allows so we don't block the external loop.
|
||||
match timeout {
|
||||
Some(Duration::ZERO) => {
|
||||
AppState::set_wait_timeout(None);
|
||||
@@ -415,13 +418,13 @@ impl<T> EventLoop<T> {
|
||||
}
|
||||
AppState::set_stop_app_on_redraw_requested(true);
|
||||
unsafe {
|
||||
app.run();
|
||||
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 `NSApp` and saving the
|
||||
// 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() {
|
||||
@@ -459,6 +462,7 @@ impl<T> EventLoop<T> {
|
||||
/// 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> {
|
||||
@@ -473,11 +477,11 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
|
||||
let panic_info = panic_info.upgrade().unwrap();
|
||||
panic_info.set_panic(e);
|
||||
}
|
||||
let app = NSApp();
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
app.stop(None);
|
||||
// Posting a dummy event to get `stop` to take effect immediately.
|
||||
// See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752
|
||||
app.postEvent_atStart(&NSEvent::dummy(), true);
|
||||
app.postEvent_atStart(&dummy_event().unwrap(), true);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use core_graphics::{
|
||||
base::CGError,
|
||||
display::{CGDirectDisplayID, CGDisplayConfigRef},
|
||||
};
|
||||
use objc2::{ffi::NSInteger, runtime::AnyObject};
|
||||
|
||||
pub type CGDisplayFadeInterval = f32;
|
||||
pub type CGDisplayReservationInterval = f32;
|
||||
@@ -113,6 +114,14 @@ extern "C" {
|
||||
pub fn CGDisplayModeCopyPixelEncoding(mode: CGDisplayModeRef) -> CFStringRef;
|
||||
pub fn CGDisplayModeRetain(mode: CGDisplayModeRef);
|
||||
pub fn CGDisplayModeRelease(mode: CGDisplayModeRef);
|
||||
|
||||
// Wildly used private APIs; Apple uses them for their Terminal.app.
|
||||
pub fn CGSMainConnectionID() -> *mut AnyObject;
|
||||
pub fn CGSSetWindowBackgroundBlurRadius(
|
||||
connection_id: *mut AnyObject,
|
||||
window_id: NSInteger,
|
||||
radius: i64,
|
||||
) -> i32;
|
||||
}
|
||||
|
||||
mod core_video {
|
||||
@@ -201,3 +210,45 @@ extern "C" {
|
||||
unicodeString: *mut UniChar,
|
||||
) -> OSStatus;
|
||||
}
|
||||
|
||||
// CGWindowLevel.h
|
||||
//
|
||||
// Note: There are two different things at play in this header:
|
||||
// `CGWindowLevel` and `CGWindowLevelKey`.
|
||||
//
|
||||
// It seems like there was a push towards using "key" values instead of the
|
||||
// raw window level values, and then you were supposed to use
|
||||
// `CGWindowLevelForKey` to get the actual level.
|
||||
//
|
||||
// But the values that `NSWindowLevel` has are compiled in, and as such has
|
||||
// to remain ABI compatible, so they're safe for us to hardcode as well.
|
||||
#[allow(dead_code, non_upper_case_globals)]
|
||||
mod window_level {
|
||||
const kCGNumReservedWindowLevels: i32 = 16;
|
||||
const kCGNumReservedBaseWindowLevels: i32 = 5;
|
||||
|
||||
pub const kCGBaseWindowLevel: i32 = i32::MIN;
|
||||
pub const kCGMinimumWindowLevel: i32 = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels;
|
||||
pub const kCGMaximumWindowLevel: i32 = i32::MAX - kCGNumReservedWindowLevels;
|
||||
|
||||
pub const kCGDesktopWindowLevel: i32 = kCGMinimumWindowLevel + 20;
|
||||
pub const kCGDesktopIconWindowLevel: i32 = kCGDesktopWindowLevel + 20;
|
||||
pub const kCGBackstopMenuLevel: i32 = -20;
|
||||
pub const kCGNormalWindowLevel: i32 = 0;
|
||||
pub const kCGFloatingWindowLevel: i32 = 3;
|
||||
pub const kCGTornOffMenuWindowLevel: i32 = 3;
|
||||
pub const kCGModalPanelWindowLevel: i32 = 8;
|
||||
pub const kCGUtilityWindowLevel: i32 = 19;
|
||||
pub const kCGDockWindowLevel: i32 = 20;
|
||||
pub const kCGMainMenuWindowLevel: i32 = 24;
|
||||
pub const kCGStatusWindowLevel: i32 = 25;
|
||||
pub const kCGPopUpMenuWindowLevel: i32 = 101;
|
||||
pub const kCGOverlayWindowLevel: i32 = 102;
|
||||
pub const kCGHelpWindowLevel: i32 = 200;
|
||||
pub const kCGDraggingWindowLevel: i32 = 500;
|
||||
pub const kCGScreenSaverWindowLevel: i32 = 1000;
|
||||
pub const kCGAssistiveTechHighWindowLevel: i32 = 1500;
|
||||
pub const kCGCursorWindowLevel: i32 = kCGMaximumWindowLevel - 1;
|
||||
}
|
||||
|
||||
pub use window_level::*;
|
||||
|
||||
@@ -1,36 +1,49 @@
|
||||
use icrate::ns_string;
|
||||
use icrate::Foundation::{NSProcessInfo, NSString};
|
||||
use icrate::AppKit::{
|
||||
NSApplication, NSEventModifierFlagCommand, NSEventModifierFlagOption, NSEventModifierFlags,
|
||||
NSMenu, NSMenuItem,
|
||||
};
|
||||
use icrate::Foundation::{ns_string, MainThreadMarker, NSProcessInfo, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::Sel;
|
||||
use objc2::sel;
|
||||
|
||||
use super::appkit::{NSApp, NSEventModifierFlags, NSMenu, NSMenuItem};
|
||||
|
||||
struct KeyEquivalent<'a> {
|
||||
key: &'a NSString,
|
||||
masks: Option<NSEventModifierFlags>,
|
||||
}
|
||||
|
||||
pub fn initialize() {
|
||||
let menubar = NSMenu::new();
|
||||
let app_menu_item = NSMenuItem::new();
|
||||
pub fn initialize(app: &NSApplication) {
|
||||
let mtm = MainThreadMarker::from(app);
|
||||
let menubar = NSMenu::new(mtm);
|
||||
let app_menu_item = NSMenuItem::new(mtm);
|
||||
menubar.addItem(&app_menu_item);
|
||||
|
||||
let app_menu = NSMenu::new();
|
||||
let app_menu = NSMenu::new(mtm);
|
||||
let process_name = NSProcessInfo::processInfo().processName();
|
||||
|
||||
// About menu item
|
||||
let about_item_title = ns_string!("About ").stringByAppendingString(&process_name);
|
||||
let about_item = menu_item(&about_item_title, sel!(orderFrontStandardAboutPanel:), None);
|
||||
let about_item = menu_item(
|
||||
mtm,
|
||||
&about_item_title,
|
||||
Some(sel!(orderFrontStandardAboutPanel:)),
|
||||
None,
|
||||
);
|
||||
|
||||
// Services menu item
|
||||
let services_menu = NSMenu::new(mtm);
|
||||
let services_item = menu_item(mtm, ns_string!("Services"), None, None);
|
||||
services_item.setSubmenu(Some(&services_menu));
|
||||
|
||||
// Seperator menu item
|
||||
let sep_first = NSMenuItem::separatorItem();
|
||||
let sep_first = NSMenuItem::separatorItem(mtm);
|
||||
|
||||
// Hide application menu item
|
||||
let hide_item_title = ns_string!("Hide ").stringByAppendingString(&process_name);
|
||||
let hide_item = menu_item(
|
||||
mtm,
|
||||
&hide_item_title,
|
||||
sel!(hide:),
|
||||
Some(sel!(hide:)),
|
||||
Some(KeyEquivalent {
|
||||
key: ns_string!("h"),
|
||||
masks: None,
|
||||
@@ -40,28 +53,33 @@ pub fn initialize() {
|
||||
// Hide other applications menu item
|
||||
let hide_others_item_title = ns_string!("Hide Others");
|
||||
let hide_others_item = menu_item(
|
||||
mtm,
|
||||
hide_others_item_title,
|
||||
sel!(hideOtherApplications:),
|
||||
Some(sel!(hideOtherApplications:)),
|
||||
Some(KeyEquivalent {
|
||||
key: ns_string!("h"),
|
||||
masks: Some(
|
||||
NSEventModifierFlags::NSAlternateKeyMask | NSEventModifierFlags::NSCommandKeyMask,
|
||||
),
|
||||
masks: Some(NSEventModifierFlagOption | NSEventModifierFlagCommand),
|
||||
}),
|
||||
);
|
||||
|
||||
// Show applications menu item
|
||||
let show_all_item_title = ns_string!("Show All");
|
||||
let show_all_item = menu_item(show_all_item_title, sel!(unhideAllApplications:), None);
|
||||
let show_all_item = menu_item(
|
||||
mtm,
|
||||
show_all_item_title,
|
||||
Some(sel!(unhideAllApplications:)),
|
||||
None,
|
||||
);
|
||||
|
||||
// Seperator menu item
|
||||
let sep = NSMenuItem::separatorItem();
|
||||
let sep = NSMenuItem::separatorItem(mtm);
|
||||
|
||||
// Quit application menu item
|
||||
let quit_item_title = ns_string!("Quit ").stringByAppendingString(&process_name);
|
||||
let quit_item = menu_item(
|
||||
mtm,
|
||||
&quit_item_title,
|
||||
sel!(terminate:),
|
||||
Some(sel!(terminate:)),
|
||||
Some(KeyEquivalent {
|
||||
key: ns_string!("q"),
|
||||
masks: None,
|
||||
@@ -70,27 +88,31 @@ pub fn initialize() {
|
||||
|
||||
app_menu.addItem(&about_item);
|
||||
app_menu.addItem(&sep_first);
|
||||
app_menu.addItem(&services_item);
|
||||
app_menu.addItem(&hide_item);
|
||||
app_menu.addItem(&hide_others_item);
|
||||
app_menu.addItem(&show_all_item);
|
||||
app_menu.addItem(&sep);
|
||||
app_menu.addItem(&quit_item);
|
||||
app_menu_item.setSubmenu(&app_menu);
|
||||
app_menu_item.setSubmenu(Some(&app_menu));
|
||||
|
||||
let app = NSApp();
|
||||
app.setMainMenu(&menubar);
|
||||
unsafe { app.setServicesMenu(Some(&services_menu)) };
|
||||
app.setMainMenu(Some(&menubar));
|
||||
}
|
||||
|
||||
fn menu_item(
|
||||
mtm: MainThreadMarker,
|
||||
title: &NSString,
|
||||
selector: Sel,
|
||||
selector: Option<Sel>,
|
||||
key_equivalent: Option<KeyEquivalent<'_>>,
|
||||
) -> Id<NSMenuItem> {
|
||||
let (key, masks) = match key_equivalent {
|
||||
Some(ke) => (ke.key, ke.masks),
|
||||
None => (ns_string!(""), None),
|
||||
};
|
||||
let item = NSMenuItem::newWithTitle(title, selector, key);
|
||||
let item = unsafe {
|
||||
NSMenuItem::initWithTitle_action_keyEquivalent(mtm.alloc(), title, selector, key)
|
||||
};
|
||||
if let Some(masks) = masks {
|
||||
item.setKeyEquivalentModifierMask(masks)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ mod util;
|
||||
mod app;
|
||||
mod app_delegate;
|
||||
mod app_state;
|
||||
mod appkit;
|
||||
mod cursor;
|
||||
mod event;
|
||||
mod event_loop;
|
||||
mod ffi;
|
||||
@@ -27,7 +27,9 @@ pub(crate) use self::{
|
||||
};
|
||||
use crate::event::DeviceId as RootDeviceId;
|
||||
|
||||
pub(crate) use self::window::Window;
|
||||
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
|
||||
pub(crate) use self::window::{OwnedWindowHandle, Window};
|
||||
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(crate) use crate::platform_impl::Fullscreen;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user