mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 23:23:14 -04:00
Compare commits
27 Commits
madsmtm/ev
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba6da05252 | ||
|
|
27e17e3f29 | ||
|
|
56fde0791b | ||
|
|
4ef144b519 | ||
|
|
7fe8c206ae | ||
|
|
fb78ecbb03 | ||
|
|
850d5f59a7 | ||
|
|
81b2729765 | ||
|
|
c4afadbfab | ||
|
|
b5252f1366 | ||
|
|
f93a223da9 | ||
|
|
d75a0dada0 | ||
|
|
9bf46af6f7 | ||
|
|
464c37a94e | ||
|
|
557d285170 | ||
|
|
ba856e127a | ||
|
|
0ffd303db6 | ||
|
|
5a74bf0aab | ||
|
|
117ec364f9 | ||
|
|
9f789e56ee | ||
|
|
91558169d2 | ||
|
|
4998cb990f | ||
|
|
98692641c4 | ||
|
|
a630b5333c | ||
|
|
ca7735f10b | ||
|
|
7adb805011 | ||
|
|
a8c7d809b9 |
27
.github/workflows/ci.yml
vendored
27
.github/workflows/ci.yml
vendored
@@ -124,7 +124,7 @@ jobs:
|
|||||||
# the cache has been downloaded.
|
# the cache has been downloaded.
|
||||||
#
|
#
|
||||||
# This could be avoided if we added Cargo.lock to the repository.
|
# This could be avoided if we added Cargo.lock to the repository.
|
||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v6
|
||||||
with:
|
with:
|
||||||
# https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
|
# https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
|
||||||
path: |
|
path: |
|
||||||
@@ -136,7 +136,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate lockfile
|
- name: Generate lockfile
|
||||||
# Also updates the crates.io index
|
# Also updates the crates.io index
|
||||||
run: cargo generate-lockfile && cargo update -p smol_str --precise 0.3.2
|
run: cargo generate-lockfile && cargo update -p smol_str --precise 0.3.2 && cargo update -p unicode-segmentation --precise 1.12.0 && cargo update -p wayland-protocols --precise 0.32.12
|
||||||
|
|
||||||
- name: Install GCC Multilib
|
- name: Install GCC Multilib
|
||||||
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
||||||
@@ -145,7 +145,7 @@ jobs:
|
|||||||
- name: Cache cargo-apk
|
- name: Cache cargo-apk
|
||||||
if: contains(matrix.platform.target, 'android')
|
if: contains(matrix.platform.target, 'android')
|
||||||
id: cargo-apk-cache
|
id: cargo-apk-cache
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ~/.cargo/bin/cargo-apk
|
path: ~/.cargo/bin/cargo-apk
|
||||||
# Change this key if we update the required cargo-apk version
|
# Change this key if we update the required cargo-apk version
|
||||||
@@ -281,7 +281,7 @@ jobs:
|
|||||||
|
|
||||||
# See restore step above
|
# See restore step above
|
||||||
- name: Save cache of cargo folder
|
- name: Save cache of cargo folder
|
||||||
uses: actions/cache/save@v5
|
uses: actions/cache/save@v6
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cargo/registry/index/
|
~/.cargo/registry/index/
|
||||||
@@ -290,31 +290,14 @@ jobs:
|
|||||||
key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}
|
key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}
|
||||||
|
|
||||||
cargo-deny:
|
cargo-deny:
|
||||||
name: Run cargo-deny on ${{ matrix.platform.name }}
|
name: Run cargo-deny
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# TODO: remove this matrix when https://github.com/EmbarkStudios/cargo-deny/issues/324 is resolved
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform:
|
|
||||||
- { name: 'Android', target: aarch64-linux-android }
|
|
||||||
- { name: 'iOS', target: aarch64-apple-ios }
|
|
||||||
- { name: 'Linux', target: x86_64-unknown-linux-gnu }
|
|
||||||
- { name: 'macOS', target: aarch64-apple-darwin }
|
|
||||||
- { name: 'Redox OS', target: x86_64-unknown-redox }
|
|
||||||
- { name: 'Web', target: wasm32-unknown-unknown }
|
|
||||||
- { name: 'Windows GNU', target: x86_64-pc-windows-gnu }
|
|
||||||
- { name: 'Windows MSVC', target: x86_64-pc-windows-msvc }
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: taiki-e/checkout-action@v1
|
- uses: taiki-e/checkout-action@v1
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||||
with:
|
with:
|
||||||
command: check
|
|
||||||
log-level: error
|
log-level: error
|
||||||
manifest-path: winit/Cargo.toml
|
|
||||||
arguments: --all-features --target ${{ matrix.platform.target }}
|
|
||||||
|
|
||||||
eslint:
|
eslint:
|
||||||
name: ESLint
|
name: ESLint
|
||||||
|
|||||||
8
.github/workflows/docs.yml
vendored
8
.github/workflows/docs.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=serde,mint,android-native-activity
|
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=serde,mint,android-native-activity
|
||||||
|
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v5
|
uses: actions/configure-pages@v6
|
||||||
|
|
||||||
- name: Fix permissions
|
- name: Fix permissions
|
||||||
run: |
|
run: |
|
||||||
@@ -41,10 +41,10 @@ jobs:
|
|||||||
done
|
done
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v4
|
uses: actions/upload-pages-artifact@v5
|
||||||
with:
|
with:
|
||||||
path: target/doc
|
path: target/doc
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v5
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ objc2-core-foundation = { version = "0.3.2", default-features = false }
|
|||||||
objc2-core-graphics = { version = "0.3.2", default-features = false }
|
objc2-core-graphics = { version = "0.3.2", default-features = false }
|
||||||
objc2-core-video = { version = "0.3.2", default-features = false }
|
objc2-core-video = { version = "0.3.2", default-features = false }
|
||||||
objc2-foundation = { version = "0.3.2", default-features = false }
|
objc2-foundation = { version = "0.3.2", default-features = false }
|
||||||
objc2-quartz-core = { version = "0.3.2", default-features = false }
|
|
||||||
objc2-ui-kit = { version = "0.3.2", default-features = false }
|
objc2-ui-kit = { version = "0.3.2", default-features = false }
|
||||||
|
|
||||||
# Windows dependencies.
|
# Windows dependencies.
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
# Using allow-invalid because this is platform-specific code
|
# Using allow-invalid because this is platform-specific code
|
||||||
|
disallowed-macros = [
|
||||||
|
{ path = "std::print", reason = "works badly on web", replacement = "tracing::info" },
|
||||||
|
{ path = "std::println", reason = "works badly on web", replacement = "tracing::info" },
|
||||||
|
{ path = "std::eprint", reason = "works badly on web", replacement = "tracing::error" },
|
||||||
|
{ path = "std::eprintln", reason = "works badly on web", replacement = "tracing::error" },
|
||||||
|
{ path = "std::dbg", reason = "leftover debugging aid, remove it or use tracing" },
|
||||||
|
]
|
||||||
disallowed-methods = [
|
disallowed-methods = [
|
||||||
{ allow-invalid = true, path = "objc2_app_kit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
|
{ allow-invalid = true, path = "objc2_app_kit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
|
||||||
{ allow-invalid = true, path = "objc2_app_kit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
|
{ allow-invalid = true, path = "objc2_app_kit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },
|
||||||
|
|||||||
82
deny.toml
82
deny.toml
@@ -1,27 +1,25 @@
|
|||||||
# https://embarkstudios.github.io/cargo-deny
|
# https://embarkstudios.github.io/cargo-deny
|
||||||
# cargo install cargo-deny
|
# cargo install cargo-deny
|
||||||
# cargo update && cargo deny --target aarch64-apple-ios check
|
# cargo update && cargo deny check
|
||||||
# Note: running just `cargo deny check` without a `--target` will result in
|
|
||||||
# false positives due to https://github.com/EmbarkStudios/cargo-deny/issues/324
|
|
||||||
[graph]
|
[graph]
|
||||||
all-features = true
|
all-features = true
|
||||||
exclude-dev = true
|
exclude-dev = true
|
||||||
targets = [
|
targets = [
|
||||||
{ triple = "aarch64-apple-darwin" },
|
"aarch64-apple-darwin",
|
||||||
{ triple = "aarch64-apple-ios" },
|
"aarch64-apple-ios",
|
||||||
{ triple = "aarch64-linux-android" },
|
"aarch64-linux-android",
|
||||||
{ triple = "i686-pc-windows-gnu" },
|
"i686-pc-windows-gnu",
|
||||||
{ triple = "i686-pc-windows-msvc" },
|
"i686-pc-windows-msvc",
|
||||||
{ triple = "i686-unknown-linux-gnu" },
|
"i686-unknown-linux-gnu",
|
||||||
{ triple = "wasm32-unknown-unknown", features = [
|
{ triple = "wasm32-unknown-unknown", features = [
|
||||||
"atomics",
|
"atomics",
|
||||||
] },
|
] },
|
||||||
{ triple = "x86_64-apple-darwin" },
|
"x86_64-apple-darwin",
|
||||||
{ triple = "x86_64-apple-ios" },
|
"x86_64-apple-ios",
|
||||||
{ triple = "x86_64-pc-windows-gnu" },
|
"x86_64-pc-windows-gnu",
|
||||||
{ triple = "x86_64-pc-windows-msvc" },
|
"x86_64-pc-windows-msvc",
|
||||||
{ triple = "x86_64-unknown-linux-gnu" },
|
"x86_64-unknown-linux-gnu",
|
||||||
{ triple = "x86_64-unknown-redox" },
|
"x86_64-unknown-redox",
|
||||||
]
|
]
|
||||||
|
|
||||||
[licenses]
|
[licenses]
|
||||||
@@ -33,6 +31,7 @@ allow = [
|
|||||||
"MIT", # https://tldrlegal.com/license/mit-license
|
"MIT", # https://tldrlegal.com/license/mit-license
|
||||||
"Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html
|
"Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html
|
||||||
"Zlib", # https://spdx.org/licenses/Zlib.html
|
"Zlib", # https://spdx.org/licenses/Zlib.html
|
||||||
|
"MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/
|
||||||
]
|
]
|
||||||
confidence-threshold = 1.0
|
confidence-threshold = 1.0
|
||||||
private = { ignore = true }
|
private = { ignore = true }
|
||||||
@@ -40,46 +39,23 @@ private = { ignore = true }
|
|||||||
[bans]
|
[bans]
|
||||||
multiple-versions = "deny"
|
multiple-versions = "deny"
|
||||||
skip = [
|
skip = [
|
||||||
{ crate = "bitflags@1", reason = "the ecosystem is in the process of migrating" },
|
{ crate = "jni-sys@0.3", reason = "uses the semver trick to depend on v0.4, but `ndk` hasn't been updated to v0.4 yet" },
|
||||||
{ crate = "rustix@0.38", reason = "the ecosystem is in the process of migrating" },
|
{ crate = "thiserror@1.0", reason = "dep of `ndk` crate, yet to be updated" },
|
||||||
{ crate = "linux-raw-sys@0.4", reason = "the ecosystem is in the process of migrating" },
|
{ crate = "thiserror-impl@1.0", reason = "dep of `thiserror`" },
|
||||||
|
{ crate = "objc2@0.5", reason = "used by crossfont" },
|
||||||
|
{ crate = "objc2-foundation@0.2", reason = "used by crossfont" },
|
||||||
|
]
|
||||||
|
skip-tree = [
|
||||||
|
{ crate = "windows-sys", reason = "foundational but bumps fairly often, nothing we can do about it not having a shared version" },
|
||||||
]
|
]
|
||||||
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
|
|
||||||
|
|
||||||
[bans.build]
|
[bans.build]
|
||||||
|
bypass = [
|
||||||
|
{ crate = "android-activity", allow-globs = ["android-games-sdk/import-games-sdk.sh"] },
|
||||||
|
{ crate = "freetype-sys", allow-globs = ["freetype2/*"] },
|
||||||
|
# `crossfont` still depends (partially transitively) on `winapi`.
|
||||||
|
{ crate = "winapi-i686-pc-windows-gnu", allow-globs = ["lib/lib*.a"] },
|
||||||
|
{ crate = "winapi-x86_64-pc-windows-gnu", allow-globs = ["lib/lib*.a"] },
|
||||||
|
]
|
||||||
include-archives = true
|
include-archives = true
|
||||||
interpreted = "deny"
|
interpreted = "deny"
|
||||||
|
|
||||||
[[bans.build.bypass]]
|
|
||||||
allow = [
|
|
||||||
{ path = "generate-bindings.sh", checksum = "268ec23248218d779e33853cdc60e2985e70214ff004716cd734270de1f6b561" },
|
|
||||||
]
|
|
||||||
crate = "android-activity"
|
|
||||||
|
|
||||||
[[bans.build.bypass]]
|
|
||||||
allow-globs = ["ci/*", "githooks/*"]
|
|
||||||
crate = "zerocopy"
|
|
||||||
|
|
||||||
[[bans.build.bypass]]
|
|
||||||
allow-globs = ["cherry-pick-stable.sh"]
|
|
||||||
crate = "libc"
|
|
||||||
|
|
||||||
[[bans.build.bypass]]
|
|
||||||
allow-globs = ["freetype2/*"]
|
|
||||||
crate = "freetype-sys"
|
|
||||||
|
|
||||||
[[bans.build.bypass]]
|
|
||||||
allow-globs = ["lib/*.a"]
|
|
||||||
crate = "windows_i686_gnu"
|
|
||||||
|
|
||||||
[[bans.build.bypass]]
|
|
||||||
allow-globs = ["lib/*.lib"]
|
|
||||||
crate = "windows_i686_msvc"
|
|
||||||
|
|
||||||
[[bans.build.bypass]]
|
|
||||||
allow-globs = ["lib/*.a"]
|
|
||||||
crate = "windows_x86_64_gnu"
|
|
||||||
|
|
||||||
[[bans.build.bypass]]
|
|
||||||
allow-globs = ["lib/*.lib"]
|
|
||||||
crate = "windows_x86_64_msvc"
|
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ pub fn to_physical_key(keycode: Keycode) -> PhysicalKey {
|
|||||||
Keycode::MediaStop => KeyCode::MediaStop,
|
Keycode::MediaStop => KeyCode::MediaStop,
|
||||||
Keycode::MediaNext => KeyCode::MediaTrackNext,
|
Keycode::MediaNext => KeyCode::MediaTrackNext,
|
||||||
Keycode::MediaPrevious => KeyCode::MediaTrackPrevious,
|
Keycode::MediaPrevious => KeyCode::MediaTrackPrevious,
|
||||||
|
Keycode::MediaEject => KeyCode::Eject,
|
||||||
|
|
||||||
Keycode::Plus => KeyCode::Equal,
|
Keycode::Plus => KeyCode::Equal,
|
||||||
Keycode::Minus => KeyCode::Minus,
|
Keycode::Minus => KeyCode::Minus,
|
||||||
@@ -131,7 +132,11 @@ pub fn to_physical_key(keycode: Keycode) -> PhysicalKey {
|
|||||||
// These are exactly the same
|
// These are exactly the same
|
||||||
Keycode::ScrollLock => KeyCode::ScrollLock,
|
Keycode::ScrollLock => KeyCode::ScrollLock,
|
||||||
|
|
||||||
|
Keycode::Eisu => KeyCode::Lang2,
|
||||||
|
Keycode::Muhenkan => KeyCode::NonConvert,
|
||||||
|
Keycode::Henkan => KeyCode::Convert,
|
||||||
Keycode::Yen => KeyCode::IntlYen,
|
Keycode::Yen => KeyCode::IntlYen,
|
||||||
|
Keycode::Ro => KeyCode::IntlRo,
|
||||||
Keycode::Kana => KeyCode::Lang1,
|
Keycode::Kana => KeyCode::Lang1,
|
||||||
Keycode::KatakanaHiragana => KeyCode::KanaMode,
|
Keycode::KatakanaHiragana => KeyCode::KanaMode,
|
||||||
|
|
||||||
@@ -154,6 +159,14 @@ pub fn to_physical_key(keycode: Keycode) -> PhysicalKey {
|
|||||||
Keycode::Sleep => KeyCode::Sleep, // what about SoftSleep?
|
Keycode::Sleep => KeyCode::Sleep, // what about SoftSleep?
|
||||||
Keycode::Wakeup => KeyCode::WakeUp,
|
Keycode::Wakeup => KeyCode::WakeUp,
|
||||||
|
|
||||||
|
Keycode::CapsLock => KeyCode::CapsLock,
|
||||||
|
Keycode::Help => KeyCode::Help,
|
||||||
|
|
||||||
|
Keycode::Back => KeyCode::BrowserBack,
|
||||||
|
Keycode::Forward => KeyCode::BrowserForward,
|
||||||
|
Keycode::Refresh => KeyCode::BrowserRefresh,
|
||||||
|
Keycode::Search => KeyCode::BrowserSearch,
|
||||||
|
|
||||||
keycode => return PhysicalKey::Unidentified(NativeKeyCode::Android(keycode.into())),
|
keycode => return PhysicalKey::Unidentified(NativeKeyCode::Android(keycode.into())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ objc2-foundation = { workspace = true, features = [
|
|||||||
"NSThread",
|
"NSThread",
|
||||||
"NSValue",
|
"NSValue",
|
||||||
] }
|
] }
|
||||||
objc2-quartz-core = { workspace = true, features = ["std", "CABase"] }
|
winit-common = { workspace = true, features = ["core-foundation", "event-handler", "foundation"] }
|
||||||
winit-common = { workspace = true, features = ["core-foundation", "event-handler"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
winit.workspace = true
|
winit.workspace = true
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
#![allow(clippy::unnecessary_cast)]
|
#![allow(clippy::unnecessary_cast)]
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::mem;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::{mem, ptr};
|
||||||
|
|
||||||
use dispatch2::MainThreadBound;
|
use dispatch2::MainThreadBound;
|
||||||
use objc2::runtime::{Imp, Sel};
|
use objc2::runtime::{Imp, Sel};
|
||||||
use objc2::sel;
|
use objc2::sel;
|
||||||
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType};
|
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType};
|
||||||
use objc2_foundation::MainThreadMarker;
|
use objc2_foundation::MainThreadMarker;
|
||||||
|
use tracing::trace_span;
|
||||||
use winit_core::event::{DeviceEvent, ElementState};
|
use winit_core::event::{DeviceEvent, ElementState};
|
||||||
|
|
||||||
use super::app_state::AppState;
|
use super::app_state::AppState;
|
||||||
@@ -21,6 +22,10 @@ static ORIGINAL: MainThreadBound<Cell<Option<SendEvent>>> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern "C-unwind" fn send_event(app: &NSApplication, sel: Sel, event: &NSEvent) {
|
extern "C-unwind" fn send_event(app: &NSApplication, sel: Sel, event: &NSEvent) {
|
||||||
|
// This can be a bit noisy, since `event` is fairly large. Note that you can use
|
||||||
|
// `RUST_LOG='trace,winit_appkit::app=warn'` if you're debugging and want TRACE-level logs but
|
||||||
|
// not this.
|
||||||
|
let _entered = trace_span!("sendEvent:", ?event).entered();
|
||||||
let mtm = MainThreadMarker::from(app);
|
let mtm = MainThreadMarker::from(app);
|
||||||
|
|
||||||
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
|
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
|
||||||
@@ -75,9 +80,7 @@ pub(crate) fn override_send_event(global_app: &NSApplication) {
|
|||||||
let overridden = unsafe { mem::transmute::<SendEvent, Imp>(send_event) };
|
let overridden = unsafe { mem::transmute::<SendEvent, Imp>(send_event) };
|
||||||
|
|
||||||
// If we've already overridden the method, don't do anything.
|
// If we've already overridden the method, don't do anything.
|
||||||
// FIXME(madsmtm): Use `std::ptr::fn_addr_eq` (Rust 1.85) once available in MSRV.
|
if ptr::fn_addr_eq(overridden, method.implementation()) {
|
||||||
#[allow(unknown_lints, unpredictable_function_pointer_comparisons)]
|
|
||||||
if overridden == method.implementation() {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +101,6 @@ pub(crate) fn override_send_event(global_app: &NSApplication) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||||
let time = app_state.event_time(event);
|
|
||||||
let event_type = event.r#type();
|
let event_type = event.r#type();
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
match event_type {
|
match event_type {
|
||||||
@@ -111,7 +113,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
|||||||
|
|
||||||
if delta_x != 0.0 || delta_y != 0.0 {
|
if delta_x != 0.0 || delta_y != 0.0 {
|
||||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||||
app.device_event(event_loop, None, time, DeviceEvent::PointerMotion {
|
app.device_event(event_loop, None, DeviceEvent::PointerMotion {
|
||||||
delta: (delta_x, delta_y),
|
delta: (delta_x, delta_y),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -120,7 +122,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
|||||||
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
|
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
|
||||||
let button = event.buttonNumber() as u32;
|
let button = event.buttonNumber() as u32;
|
||||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||||
app.device_event(event_loop, None, time, DeviceEvent::Button {
|
app.device_event(event_loop, None, DeviceEvent::Button {
|
||||||
button,
|
button,
|
||||||
state: ElementState::Pressed,
|
state: ElementState::Pressed,
|
||||||
});
|
});
|
||||||
@@ -129,7 +131,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
|||||||
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
||||||
let button = event.buttonNumber() as u32;
|
let button = event.buttonNumber() as u32;
|
||||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||||
app.device_event(event_loop, None, time, DeviceEvent::Button {
|
app.device_event(event_loop, None, DeviceEvent::Button {
|
||||||
button,
|
button,
|
||||||
state: ElementState::Released,
|
state: ElementState::Released,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ use std::cell::{Cell, OnceCell, RefCell};
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Instant;
|
||||||
|
|
||||||
use dispatch2::MainThreadBound;
|
use dispatch2::MainThreadBound;
|
||||||
use objc2::MainThreadMarker;
|
use objc2::MainThreadMarker;
|
||||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSEvent, NSRunningApplication};
|
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningApplication};
|
||||||
use objc2_foundation::{NSNotification, NSTimeInterval};
|
use objc2_foundation::NSNotification;
|
||||||
use objc2_quartz_core::CACurrentMediaTime;
|
|
||||||
use tracing::warn;
|
|
||||||
use winit_common::core_foundation::{EventLoopProxy, MainRunLoop};
|
use winit_common::core_foundation::{EventLoopProxy, MainRunLoop};
|
||||||
use winit_common::event_handler::EventHandler;
|
use winit_common::event_handler::EventHandler;
|
||||||
use winit_core::application::ApplicationHandler;
|
use winit_core::application::ApplicationHandler;
|
||||||
@@ -45,8 +43,6 @@ pub(super) struct AppState {
|
|||||||
start_time: Cell<Option<Instant>>,
|
start_time: Cell<Option<Instant>>,
|
||||||
wait_timeout: Cell<Option<Instant>>,
|
wait_timeout: Cell<Option<Instant>>,
|
||||||
pending_redraw: RefCell<Vec<WindowId>>,
|
pending_redraw: RefCell<Vec<WindowId>>,
|
||||||
startup_instant: Instant,
|
|
||||||
startup_timestamp: NSTimeInterval,
|
|
||||||
// NOTE: This is strongly referenced by our `NSWindowDelegate` and our `NSView` subclass, and
|
// NOTE: This is strongly referenced by our `NSWindowDelegate` and our `NSView` subclass, and
|
||||||
// as such should be careful to not add fields that, in turn, strongly reference those.
|
// as such should be careful to not add fields that, in turn, strongly reference those.
|
||||||
}
|
}
|
||||||
@@ -67,17 +63,6 @@ impl AppState {
|
|||||||
Self::get(mtm).with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
|
Self::get(mtm).with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Prime dylib caches etc.
|
|
||||||
let _ = CACurrentMediaTime();
|
|
||||||
|
|
||||||
// Find the current Rust timestamp.
|
|
||||||
let startup_instant = Instant::now();
|
|
||||||
// Find the timestamp
|
|
||||||
//
|
|
||||||
// `NSProcessInfo::processInfo().systemUptime()` needs the required reason manifest,
|
|
||||||
// `CACurrentMediaTime` (currently) doesn't, so we use that instead.
|
|
||||||
let startup_timestamp = CACurrentMediaTime();
|
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
mtm,
|
mtm,
|
||||||
activation_policy,
|
activation_policy,
|
||||||
@@ -98,8 +83,6 @@ impl AppState {
|
|||||||
start_time: Cell::new(None),
|
start_time: Cell::new(None),
|
||||||
wait_timeout: Cell::new(None),
|
wait_timeout: Cell::new(None),
|
||||||
pending_redraw: RefCell::new(vec![]),
|
pending_redraw: RefCell::new(vec![]),
|
||||||
startup_instant,
|
|
||||||
startup_timestamp,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
GLOBAL.get(mtm).set(this.clone()).ok().and(Some(this))
|
GLOBAL.get(mtm).set(this.clone()).ok().and(Some(this))
|
||||||
@@ -113,20 +96,9 @@ impl AppState {
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn event_time(&self, event: &NSEvent) -> Instant {
|
|
||||||
if event.timestamp() == 0.0 {
|
|
||||||
warn!(?event, "got zero timestamp");
|
|
||||||
return Instant::now();
|
|
||||||
}
|
|
||||||
let duration_since_startup = event.timestamp() - self.startup_timestamp;
|
|
||||||
let duration_since_startup = Duration::from_secs_f64(duration_since_startup as f64);
|
|
||||||
self.startup_instant + duration_since_startup
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: This notification will, globally, only be emitted once,
|
// NOTE: This notification will, globally, only be emitted once,
|
||||||
// no matter how many `EventLoop`s the user creates.
|
// no matter how many `EventLoop`s the user creates.
|
||||||
pub fn did_finish_launching(self: &Rc<Self>, _notification: &NSNotification) {
|
pub fn did_finish_launching(self: &Rc<Self>, _notification: &NSNotification) {
|
||||||
trace_scope!("NSApplicationDidFinishLaunchingNotification");
|
|
||||||
self.is_launched.set(true);
|
self.is_launched.set(true);
|
||||||
|
|
||||||
let app = NSApplication::sharedApplication(self.mtm);
|
let app = NSApplication::sharedApplication(self.mtm);
|
||||||
@@ -181,7 +153,6 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) {
|
pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) {
|
||||||
trace_scope!("NSApplicationWillTerminateNotification");
|
|
||||||
let app = NSApplication::sharedApplication(self.mtm);
|
let app = NSApplication::sharedApplication(self.mtm);
|
||||||
notify_windows_of_exit(&app);
|
notify_windows_of_exit(&app);
|
||||||
self.event_handler.terminate();
|
self.event_handler.terminate();
|
||||||
@@ -274,12 +245,7 @@ impl AppState {
|
|||||||
// -> Don't go back into the event handler when our callstack originates from there
|
// -> Don't go back into the event handler when our callstack originates from there
|
||||||
if !self.event_handler.in_use() {
|
if !self.event_handler.in_use() {
|
||||||
self.with_handler(|app, event_loop| {
|
self.with_handler(|app, event_loop| {
|
||||||
app.window_event(
|
app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
|
||||||
event_loop,
|
|
||||||
window_id,
|
|
||||||
Instant::now(),
|
|
||||||
WindowEvent::RedrawRequested,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
|
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
|
||||||
@@ -379,12 +345,7 @@ impl AppState {
|
|||||||
let redraw = mem::take(&mut *self.pending_redraw.borrow_mut());
|
let redraw = mem::take(&mut *self.pending_redraw.borrow_mut());
|
||||||
for window_id in redraw {
|
for window_id in redraw {
|
||||||
self.with_handler(|app, event_loop| {
|
self.with_handler(|app, event_loop| {
|
||||||
app.window_event(
|
app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
|
||||||
event_loop,
|
|
||||||
window_id,
|
|
||||||
Instant::now(),
|
|
||||||
WindowEvent::RedrawRequested,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.with_handler(|app, event_loop| {
|
self.with_handler(|app, event_loop| {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ use std::sync::OnceLock;
|
|||||||
use objc2::rc::Retained;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::Sel;
|
use objc2::runtime::Sel;
|
||||||
use objc2::{AllocAnyThread, ClassType, available, msg_send, sel};
|
use objc2::{AllocAnyThread, ClassType, available, msg_send, sel};
|
||||||
use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
|
use objc2_app_kit::{
|
||||||
|
NSBitmapImageRep, NSCursor, NSCursorFrameResizeDirections, NSCursorFrameResizePosition,
|
||||||
|
NSDeviceRGBColorSpace, NSImage,
|
||||||
|
};
|
||||||
use objc2_foundation::{
|
use objc2_foundation::{
|
||||||
NSData, NSDictionary, NSNumber, NSObject, NSPoint, NSSize, NSString, ns_string,
|
NSData, NSDictionary, NSNumber, NSObject, NSPoint, NSSize, NSString, ns_string,
|
||||||
};
|
};
|
||||||
@@ -204,23 +207,155 @@ pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Retained<NSCursor> {
|
|||||||
CursorIcon::NotAllowed | CursorIcon::NoDrop => NSCursor::operationNotAllowedCursor(),
|
CursorIcon::NotAllowed | CursorIcon::NoDrop => NSCursor::operationNotAllowedCursor(),
|
||||||
CursorIcon::ContextMenu => NSCursor::contextualMenuCursor(),
|
CursorIcon::ContextMenu => NSCursor::contextualMenuCursor(),
|
||||||
CursorIcon::Crosshair => NSCursor::crosshairCursor(),
|
CursorIcon::Crosshair => NSCursor::crosshairCursor(),
|
||||||
CursorIcon::EResize => NSCursor::resizeRightCursor(),
|
CursorIcon::EResize => {
|
||||||
CursorIcon::NResize => NSCursor::resizeUpCursor(),
|
if available!(macos = 15.0) {
|
||||||
CursorIcon::WResize => NSCursor::resizeLeftCursor(),
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
CursorIcon::SResize => NSCursor::resizeDownCursor(),
|
NSCursorFrameResizePosition::Right,
|
||||||
CursorIcon::EwResize | CursorIcon::ColResize => NSCursor::resizeLeftRightCursor(),
|
NSCursorFrameResizeDirections::Outward,
|
||||||
CursorIcon::NsResize | CursorIcon::RowResize => NSCursor::resizeUpDownCursor(),
|
)
|
||||||
|
} else {
|
||||||
|
NSCursor::resizeRightCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::NResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::Top,
|
||||||
|
NSCursorFrameResizeDirections::Outward,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
NSCursor::resizeUpCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::WResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::Left,
|
||||||
|
NSCursorFrameResizeDirections::Outward,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
NSCursor::resizeLeftCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::SResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::Bottom,
|
||||||
|
NSCursorFrameResizeDirections::Outward,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
NSCursor::resizeDownCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::EwResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::Right,
|
||||||
|
NSCursorFrameResizeDirections::All,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
NSCursor::resizeLeftRightCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::NsResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::Top,
|
||||||
|
NSCursorFrameResizeDirections::All,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
NSCursor::resizeUpDownCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::NeResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::TopRight,
|
||||||
|
NSCursorFrameResizeDirections::Outward,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_windowResizeNorthEastCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::NwResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::TopLeft,
|
||||||
|
NSCursorFrameResizeDirections::Outward,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_windowResizeNorthWestCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::SeResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::BottomRight,
|
||||||
|
NSCursorFrameResizeDirections::Outward,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_windowResizeSouthEastCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::SwResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::BottomLeft,
|
||||||
|
NSCursorFrameResizeDirections::Outward,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_windowResizeSouthWestCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::NeswResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::TopRight,
|
||||||
|
NSCursorFrameResizeDirections::All,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_windowResizeNorthEastSouthWestCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::NwseResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::frameResizeCursorFromPosition_inDirections(
|
||||||
|
NSCursorFrameResizePosition::TopLeft,
|
||||||
|
NSCursorFrameResizeDirections::All,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_windowResizeNorthWestSouthEastCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::ColResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::columnResizeCursor()
|
||||||
|
} else {
|
||||||
|
NSCursor::resizeLeftRightCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::RowResize => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::rowResizeCursor()
|
||||||
|
} else {
|
||||||
|
NSCursor::resizeUpDownCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::ZoomIn => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::zoomInCursor()
|
||||||
|
} else {
|
||||||
|
_zoomInCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CursorIcon::ZoomOut => {
|
||||||
|
if available!(macos = 15.0) {
|
||||||
|
NSCursor::zoomOutCursor()
|
||||||
|
} else {
|
||||||
|
_zoomOutCursor()
|
||||||
|
}
|
||||||
|
},
|
||||||
CursorIcon::Help => _helpCursor(),
|
CursorIcon::Help => _helpCursor(),
|
||||||
CursorIcon::ZoomIn if available!(macos = 15.0) => NSCursor::zoomInCursor(),
|
|
||||||
CursorIcon::ZoomIn => _zoomInCursor(),
|
|
||||||
CursorIcon::ZoomOut if available!(macos = 15.0) => NSCursor::zoomOutCursor(),
|
|
||||||
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
|
// This is the wrong semantics for `Wait`, but it's the same as
|
||||||
// what's used in Safari and Chrome.
|
// what's used in Safari and Chrome.
|
||||||
CursorIcon::Wait | CursorIcon::Progress => busyButClickableCursor(),
|
CursorIcon::Wait | CursorIcon::Progress => busyButClickableCursor(),
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ use objc2_app_kit::{
|
|||||||
use objc2_core_foundation::{CFIndex, CFRunLoopActivity, kCFRunLoopCommonModes};
|
use objc2_core_foundation::{CFIndex, CFRunLoopActivity, kCFRunLoopCommonModes};
|
||||||
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
|
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
|
||||||
use rwh_06::HasDisplayHandle;
|
use rwh_06::HasDisplayHandle;
|
||||||
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver};
|
use tracing::debug_span;
|
||||||
|
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver, tracing_observers};
|
||||||
|
use winit_common::foundation::create_observer;
|
||||||
use winit_core::application::ApplicationHandler;
|
use winit_core::application::ApplicationHandler;
|
||||||
use winit_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
|
use winit_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
|
||||||
use winit_core::error::{EventLoopError, RequestError};
|
use winit_core::error::{EventLoopError, RequestError};
|
||||||
@@ -29,7 +31,6 @@ use super::app_state::AppState;
|
|||||||
use super::cursor::CustomCursor;
|
use super::cursor::CustomCursor;
|
||||||
use super::event::dummy_event;
|
use super::event::dummy_event;
|
||||||
use super::monitor;
|
use super::monitor;
|
||||||
use super::notification_center::create_observer;
|
|
||||||
use crate::ActivationPolicy;
|
use crate::ActivationPolicy;
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
|
|
||||||
@@ -152,6 +153,7 @@ pub struct EventLoop {
|
|||||||
_did_finish_launching_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
|
_did_finish_launching_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
|
||||||
_will_terminate_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
|
_will_terminate_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
|
||||||
|
|
||||||
|
_tracing_observers: Option<(MainRunLoopObserver, MainRunLoopObserver)>,
|
||||||
_before_waiting_observer: MainRunLoopObserver,
|
_before_waiting_observer: MainRunLoopObserver,
|
||||||
_after_waiting_observer: MainRunLoopObserver,
|
_after_waiting_observer: MainRunLoopObserver,
|
||||||
}
|
}
|
||||||
@@ -203,6 +205,7 @@ impl EventLoop {
|
|||||||
// `applicationDidFinishLaunching:`
|
// `applicationDidFinishLaunching:`
|
||||||
unsafe { NSApplicationDidFinishLaunchingNotification },
|
unsafe { NSApplicationDidFinishLaunchingNotification },
|
||||||
move |notification| {
|
move |notification| {
|
||||||
|
let _entered = debug_span!("NSApplicationDidFinishLaunchingNotification").entered();
|
||||||
if let Some(app_state) = weak_app_state.upgrade() {
|
if let Some(app_state) = weak_app_state.upgrade() {
|
||||||
app_state.did_finish_launching(notification);
|
app_state.did_finish_launching(notification);
|
||||||
}
|
}
|
||||||
@@ -215,6 +218,7 @@ impl EventLoop {
|
|||||||
// `applicationWillTerminate:`
|
// `applicationWillTerminate:`
|
||||||
unsafe { NSApplicationWillTerminateNotification },
|
unsafe { NSApplicationWillTerminateNotification },
|
||||||
move |notification| {
|
move |notification| {
|
||||||
|
let _entered = debug_span!("NSApplicationWillTerminateNotification").entered();
|
||||||
if let Some(app_state) = weak_app_state.upgrade() {
|
if let Some(app_state) = weak_app_state.upgrade() {
|
||||||
app_state.will_terminate(notification);
|
app_state.will_terminate(notification);
|
||||||
}
|
}
|
||||||
@@ -224,14 +228,20 @@ impl EventLoop {
|
|||||||
let main_loop = MainRunLoop::get(mtm);
|
let main_loop = MainRunLoop::get(mtm);
|
||||||
let mode = unsafe { kCFRunLoopCommonModes }.unwrap();
|
let mode = unsafe { kCFRunLoopCommonModes }.unwrap();
|
||||||
|
|
||||||
|
// Tracing observers have the lowest and highest orderings.
|
||||||
|
let _tracing_observers = tracing_observers(mtm).inspect(|(start, end)| {
|
||||||
|
main_loop.add_observer(start, mode);
|
||||||
|
main_loop.add_observer(end, mode);
|
||||||
|
});
|
||||||
|
|
||||||
let app_state_clone = Rc::clone(&app_state);
|
let app_state_clone = Rc::clone(&app_state);
|
||||||
let _before_waiting_observer = MainRunLoopObserver::new(
|
let _before_waiting_observer = MainRunLoopObserver::new(
|
||||||
mtm,
|
mtm,
|
||||||
CFRunLoopActivity::BeforeWaiting,
|
CFRunLoopActivity::BeforeWaiting,
|
||||||
true,
|
true,
|
||||||
// Queued with the lowest priority to ensure it is processed after other observers.
|
// Queued with the second-lowest priority (tracing observers use the lowest) to ensure
|
||||||
// Without that, we'd get a `LoopExiting` after `AboutToWait`.
|
// it is processed after other observers.
|
||||||
CFIndex::MAX,
|
CFIndex::MAX - 1,
|
||||||
move |_| app_state_clone.cleared(),
|
move |_| app_state_clone.cleared(),
|
||||||
);
|
);
|
||||||
main_loop.add_observer(&_before_waiting_observer, mode);
|
main_loop.add_observer(&_before_waiting_observer, mode);
|
||||||
@@ -241,8 +251,9 @@ impl EventLoop {
|
|||||||
mtm,
|
mtm,
|
||||||
CFRunLoopActivity::AfterWaiting,
|
CFRunLoopActivity::AfterWaiting,
|
||||||
true,
|
true,
|
||||||
// Queued with the highest priority to ensure it is processed before other observers.
|
// Queued with the second-highest priority (tracing observers use the highest) to
|
||||||
CFIndex::MIN,
|
// ensure it is processed before other observers.
|
||||||
|
CFIndex::MIN + 1,
|
||||||
move |_| app_state_clone.wakeup(),
|
move |_| app_state_clone.wakeup(),
|
||||||
);
|
);
|
||||||
main_loop.add_observer(&_after_waiting_observer, mode);
|
main_loop.add_observer(&_after_waiting_observer, mode);
|
||||||
@@ -253,6 +264,7 @@ impl EventLoop {
|
|||||||
window_target: ActiveEventLoop { app_state, mtm },
|
window_target: ActiveEventLoop { app_state, mtm },
|
||||||
_did_finish_launching_observer,
|
_did_finish_launching_observer,
|
||||||
_will_terminate_observer,
|
_will_terminate_observer,
|
||||||
|
_tracing_observers,
|
||||||
_before_waiting_observer,
|
_before_waiting_observer,
|
||||||
_after_waiting_observer,
|
_after_waiting_observer,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ mod event_loop;
|
|||||||
mod ffi;
|
mod ffi;
|
||||||
mod menu;
|
mod menu;
|
||||||
mod monitor;
|
mod monitor;
|
||||||
mod notification_center;
|
|
||||||
mod observer;
|
mod observer;
|
||||||
mod view;
|
mod view;
|
||||||
mod window;
|
mod window;
|
||||||
|
|||||||
@@ -1,37 +1,10 @@
|
|||||||
use objc2_core_graphics::CGError;
|
use objc2_core_graphics::CGError;
|
||||||
use tracing::trace;
|
|
||||||
use winit_core::error::OsError;
|
use winit_core::error::OsError;
|
||||||
|
|
||||||
macro_rules! os_error {
|
macro_rules! os_error {
|
||||||
($error:expr) => {{ winit_core::error::OsError::new(line!(), file!(), $error) }};
|
($error:expr) => {{ winit_core::error::OsError::new(line!(), file!(), $error) }};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! trace_scope {
|
|
||||||
($s:literal) => {
|
|
||||||
let _crate = $crate::util::TraceGuard::new(module_path!(), $s);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct TraceGuard {
|
|
||||||
module_path: &'static str,
|
|
||||||
called_from_fn: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TraceGuard {
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn new(module_path: &'static str, called_from_fn: &'static str) -> Self {
|
|
||||||
trace!(target = module_path, "Triggered `{}`", called_from_fn);
|
|
||||||
Self { module_path, called_from_fn }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TraceGuard {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
trace!(target = self.module_path, "Completed `{}`", self.called_from_fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn cgerr(err: CGError) -> Result<(), OsError> {
|
pub(crate) fn cgerr(err: CGError) -> Result<(), OsError> {
|
||||||
if err == CGError::Success { Ok(()) } else { Err(os_error!(format!("CGError {err:?}"))) }
|
if err == CGError::Success { Ok(()) } else { Err(os_error!(format!("CGError {err:?}"))) }
|
||||||
|
|||||||
@@ -2,22 +2,21 @@
|
|||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use dpi::{LogicalPosition, LogicalSize};
|
use dpi::{LogicalPosition, PhysicalSize};
|
||||||
use objc2::rc::Retained;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::{AnyObject, Sel};
|
use objc2::runtime::{AnyObject, Sel};
|
||||||
use objc2::{AnyThread, DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send};
|
use objc2::{AnyThread, DefinedClass, MainThreadMarker, define_class, msg_send};
|
||||||
use objc2_app_kit::{
|
use objc2_app_kit::{
|
||||||
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingArea,
|
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingArea,
|
||||||
NSTrackingAreaOptions, NSView, NSWindow,
|
NSTrackingAreaOptions, NSView, NSViewLayerContentsRedrawPolicy, NSWindow,
|
||||||
};
|
};
|
||||||
use objc2_core_foundation::CGRect;
|
use objc2_core_foundation::CGRect;
|
||||||
use objc2_foundation::{
|
use objc2_foundation::{
|
||||||
NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString,
|
NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString,
|
||||||
NSNotFound, NSObject, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
NSNotFound, NSObject, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::{debug_span, trace_span};
|
||||||
use winit_core::event::{
|
use winit_core::event::{
|
||||||
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
|
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
|
||||||
PointerKind, PointerSource, TouchPhase, WindowEvent,
|
PointerKind, PointerSource, TouchPhase, WindowEvent,
|
||||||
@@ -155,22 +154,32 @@ define_class!(
|
|||||||
// Not a normal method on `NSView`, it's triggered by `NSViewFrameDidChangeNotification`.
|
// Not a normal method on `NSView`, it's triggered by `NSViewFrameDidChangeNotification`.
|
||||||
#[unsafe(method(viewFrameDidChangeNotification:))]
|
#[unsafe(method(viewFrameDidChangeNotification:))]
|
||||||
fn frame_did_change(&self, _notification: Option<&AnyObject>) {
|
fn frame_did_change(&self, _notification: Option<&AnyObject>) {
|
||||||
trace_scope!("NSViewFrameDidChangeNotification");
|
let _entered = debug_span!("NSViewFrameDidChangeNotification").entered();
|
||||||
|
|
||||||
// Emit resize event here rather than from windowDidResize because:
|
// Emit resize event here rather than from windowDidResize because:
|
||||||
// 1. When a new window is created as a tab, the frame size may change without a window
|
// 1. When a new window is created as a tab, the frame size may change without a window
|
||||||
// resize occurring.
|
// resize occurring.
|
||||||
// 2. Even when a window resize does occur on a new tabbed window, it contains the wrong
|
// 2. Even when a window resize does occur on a new tabbed window, it contains the wrong
|
||||||
// size (includes tab height).
|
// size (includes tab height).
|
||||||
let rect = self.frame();
|
self.surface_resized();
|
||||||
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
// During live resize, AppKit may not let the normal event loop reach its next redraw
|
||||||
let size = logical_size.to_physical::<u32>(self.scale_factor());
|
// point before stretching the current layer contents. Redraw immediately after the
|
||||||
self.queue_event(WindowEvent::SurfaceResized(size));
|
// app has observed the new surface size.
|
||||||
|
self.redraw_during_live_resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(method(viewDidChangeBackingProperties))]
|
||||||
|
fn view_did_change_backing_properties(&self) {
|
||||||
|
let _entered = debug_span!("viewDidChangeBackingProperties").entered();
|
||||||
|
// Moving between displays or changing scale can alter the drawable backing size
|
||||||
|
// without a matching frame-size change.
|
||||||
|
self.surface_resized();
|
||||||
|
self.redraw_during_live_resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(drawRect:))]
|
#[unsafe(method(drawRect:))]
|
||||||
fn draw_rect(&self, _rect: NSRect) {
|
fn draw_rect(&self, _rect: NSRect) {
|
||||||
trace_scope!("drawRect:");
|
let _entered = debug_span!("drawRect:").entered();
|
||||||
|
|
||||||
self.ivars().app_state.handle_redraw(window_id(&self.window()));
|
self.ivars().app_state.handle_redraw(window_id(&self.window()));
|
||||||
|
|
||||||
@@ -179,7 +188,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(acceptsFirstResponder))]
|
#[unsafe(method(acceptsFirstResponder))]
|
||||||
fn accepts_first_responder(&self) -> bool {
|
fn accepts_first_responder(&self) -> bool {
|
||||||
trace_scope!("acceptsFirstResponder");
|
let _entered = trace_span!("acceptsFirstResponder").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,13 +202,13 @@ define_class!(
|
|||||||
// extension for using `NSTouchBar`
|
// extension for using `NSTouchBar`
|
||||||
#[unsafe(method_id(touchBar))]
|
#[unsafe(method_id(touchBar))]
|
||||||
fn touch_bar(&self) -> Option<Retained<NSObject>> {
|
fn touch_bar(&self) -> Option<Retained<NSObject>> {
|
||||||
trace_scope!("touchBar");
|
let _entered = debug_span!("touchBar").entered();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(resetCursorRects))]
|
#[unsafe(method(resetCursorRects))]
|
||||||
fn reset_cursor_rects(&self) {
|
fn reset_cursor_rects(&self) {
|
||||||
trace_scope!("resetCursorRects");
|
let _entered = debug_span!("resetCursorRects").entered();
|
||||||
let bounds = self.bounds();
|
let bounds = self.bounds();
|
||||||
let cursor_state = self.ivars().cursor_state.borrow();
|
let cursor_state = self.ivars().cursor_state.borrow();
|
||||||
// We correctly invoke `addCursorRect` only from inside `resetCursorRects`
|
// We correctly invoke `addCursorRect` only from inside `resetCursorRects`
|
||||||
@@ -214,13 +223,13 @@ define_class!(
|
|||||||
unsafe impl NSTextInputClient for WinitView {
|
unsafe impl NSTextInputClient for WinitView {
|
||||||
#[unsafe(method(hasMarkedText))]
|
#[unsafe(method(hasMarkedText))]
|
||||||
fn has_marked_text(&self) -> bool {
|
fn has_marked_text(&self) -> bool {
|
||||||
trace_scope!("hasMarkedText");
|
let _entered = debug_span!("hasMarkedText").entered();
|
||||||
self.ivars().marked_text.borrow().length() > 0
|
self.ivars().marked_text.borrow().length() > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(markedRange))]
|
#[unsafe(method(markedRange))]
|
||||||
fn marked_range(&self) -> NSRange {
|
fn marked_range(&self) -> NSRange {
|
||||||
trace_scope!("markedRange");
|
let _entered = debug_span!("markedRange").entered();
|
||||||
let length = self.ivars().marked_text.borrow().length();
|
let length = self.ivars().marked_text.borrow().length();
|
||||||
if length > 0 {
|
if length > 0 {
|
||||||
NSRange::new(0, length)
|
NSRange::new(0, length)
|
||||||
@@ -232,7 +241,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(selectedRange))]
|
#[unsafe(method(selectedRange))]
|
||||||
fn selected_range(&self) -> NSRange {
|
fn selected_range(&self) -> NSRange {
|
||||||
trace_scope!("selectedRange");
|
let _entered = debug_span!("selectedRange").entered();
|
||||||
// Documented to return `{NSNotFound, 0}` if there is no selection.
|
// Documented to return `{NSNotFound, 0}` if there is no selection.
|
||||||
NSRange::new(NSNotFound as NSUInteger, 0)
|
NSRange::new(NSNotFound as NSUInteger, 0)
|
||||||
}
|
}
|
||||||
@@ -245,7 +254,7 @@ define_class!(
|
|||||||
_replacement_range: NSRange,
|
_replacement_range: NSRange,
|
||||||
) {
|
) {
|
||||||
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
|
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
|
||||||
trace_scope!("setMarkedText:selectedRange:replacementRange:");
|
let _entered = debug_span!("setMarkedText:selectedRange:replacementRange:").entered();
|
||||||
|
|
||||||
let (marked_text, string) = if let Some(string) =
|
let (marked_text, string) = if let Some(string) =
|
||||||
string.downcast_ref::<NSAttributedString>()
|
string.downcast_ref::<NSAttributedString>()
|
||||||
@@ -274,32 +283,31 @@ define_class!(
|
|||||||
self.ivars().ime_state.set(ImeState::Ground);
|
self.ivars().ime_state.set(ImeState::Ground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let string = string.to_string();
|
||||||
let cursor_range = if string.is_empty() {
|
let cursor_range = if string.is_empty() {
|
||||||
// An empty string basically means that there's no preedit, so indicate that by
|
// An empty string basically means that there's no preedit, so indicate that by
|
||||||
// sending a `None` cursor range.
|
// sending a `None` cursor range.
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
// Clamp to string length to avoid NSRangeException from out-of-bounds
|
// Convert the selected range from UTF-16 code unit indices to UTF-8 byte
|
||||||
// indices sent by macOS IME (e.g. native Pinyin, see
|
// offsets. `utf16_to_utf8_offset` is defensive: it snaps an offset that would
|
||||||
// https://github.com/alacritty/alacritty/issues/8791).
|
// split a surrogate pair down to the character boundary and clamps an
|
||||||
let len = string.length();
|
// out-of-bounds offset to the string length, so no `NSRangeException` is
|
||||||
let location = selected_range.location.min(len);
|
// possible and the resulting range can never be inverted (`lower <= upper`).
|
||||||
let end = selected_range.end().min(len);
|
// IMEs are known to send both mid-surrogate and out-of-bounds offsets (e.g.
|
||||||
// Convert the selected range from UTF-16 indices to UTF-8 indices.
|
// native Pinyin, see https://github.com/alacritty/alacritty/issues/8791).
|
||||||
let sub_string_a = string.substringToIndex(location);
|
let lowerbound_utf8 = utf16_to_utf8_offset(&string, selected_range.location);
|
||||||
let sub_string_b = string.substringToIndex(end);
|
let upperbound_utf8 = utf16_to_utf8_offset(&string, selected_range.end());
|
||||||
let lowerbound_utf8 = sub_string_a.len();
|
|
||||||
let upperbound_utf8 = sub_string_b.len();
|
|
||||||
Some((lowerbound_utf8, upperbound_utf8))
|
Some((lowerbound_utf8, upperbound_utf8))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send WindowEvent for updating marked text
|
// Send WindowEvent for updating marked text
|
||||||
self.queue_event(WindowEvent::Ime(Ime::Preedit(string.to_string(), cursor_range)));
|
self.queue_event(WindowEvent::Ime(Ime::Preedit(string, cursor_range)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(unmarkText))]
|
#[unsafe(method(unmarkText))]
|
||||||
fn unmark_text(&self) {
|
fn unmark_text(&self) {
|
||||||
trace_scope!("unmarkText");
|
let _entered = debug_span!("unmarkText").entered();
|
||||||
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
|
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
|
||||||
|
|
||||||
let input_context = self.inputContext().expect("input context");
|
let input_context = self.inputContext().expect("input context");
|
||||||
@@ -316,7 +324,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method_id(validAttributesForMarkedText))]
|
#[unsafe(method_id(validAttributesForMarkedText))]
|
||||||
fn valid_attributes_for_marked_text(&self) -> Retained<NSArray<NSAttributedStringKey>> {
|
fn valid_attributes_for_marked_text(&self) -> Retained<NSArray<NSAttributedStringKey>> {
|
||||||
trace_scope!("validAttributesForMarkedText");
|
let _entered = trace_span!("validAttributesForMarkedText").entered();
|
||||||
NSArray::new()
|
NSArray::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,13 +334,14 @@ define_class!(
|
|||||||
_range: NSRange,
|
_range: NSRange,
|
||||||
_actual_range: *mut NSRange,
|
_actual_range: *mut NSRange,
|
||||||
) -> Option<Retained<NSAttributedString>> {
|
) -> Option<Retained<NSAttributedString>> {
|
||||||
trace_scope!("attributedSubstringForProposedRange:actualRange:");
|
let _entered =
|
||||||
|
trace_span!("attributedSubstringForProposedRange:actualRange:").entered();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(characterIndexForPoint:))]
|
#[unsafe(method(characterIndexForPoint:))]
|
||||||
fn character_index_for_point(&self, _point: NSPoint) -> NSUInteger {
|
fn character_index_for_point(&self, _point: NSPoint) -> NSUInteger {
|
||||||
trace_scope!("characterIndexForPoint:");
|
let _entered = debug_span!("characterIndexForPoint:").entered();
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +351,7 @@ define_class!(
|
|||||||
_range: NSRange,
|
_range: NSRange,
|
||||||
_actual_range: *mut NSRange,
|
_actual_range: *mut NSRange,
|
||||||
) -> NSRect {
|
) -> NSRect {
|
||||||
trace_scope!("firstRectForCharacterRange:actualRange:");
|
let _entered = debug_span!("firstRectForCharacterRange:actualRange:").entered();
|
||||||
|
|
||||||
// Guard when the view is no longer in a window during teardown.
|
// Guard when the view is no longer in a window during teardown.
|
||||||
let Some(window) = (**self).window() else {
|
let Some(window) = (**self).window() else {
|
||||||
@@ -358,7 +367,7 @@ define_class!(
|
|||||||
#[unsafe(method(insertText:replacementRange:))]
|
#[unsafe(method(insertText:replacementRange:))]
|
||||||
fn insert_text(&self, string: &NSObject, _replacement_range: NSRange) {
|
fn insert_text(&self, string: &NSObject, _replacement_range: NSRange) {
|
||||||
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
|
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
|
||||||
trace_scope!("insertText:replacementRange:");
|
let _entered = debug_span!("insertText:replacementRange:").entered();
|
||||||
|
|
||||||
let string = if let Some(string) = string.downcast_ref::<NSAttributedString>() {
|
let string = if let Some(string) = string.downcast_ref::<NSAttributedString>() {
|
||||||
string.string().to_string()
|
string.string().to_string()
|
||||||
@@ -383,7 +392,7 @@ define_class!(
|
|||||||
// "human readable" character happens, i.e. newlines, tabs, and Ctrl+C.
|
// "human readable" character happens, i.e. newlines, tabs, and Ctrl+C.
|
||||||
#[unsafe(method(doCommandBySelector:))]
|
#[unsafe(method(doCommandBySelector:))]
|
||||||
fn do_command_by_selector(&self, command: Sel) {
|
fn do_command_by_selector(&self, command: Sel) {
|
||||||
trace_scope!("doCommandBySelector:");
|
let _entered = debug_span!("doCommandBySelector:").entered();
|
||||||
|
|
||||||
// We shouldn't forward any character from just committed text, since we'll end up
|
// We shouldn't forward any character from just committed text, since we'll end up
|
||||||
// sending it twice with some IMEs like Korean one. We'll also always send
|
// sending it twice with some IMEs like Korean one. We'll also always send
|
||||||
@@ -422,7 +431,7 @@ define_class!(
|
|||||||
impl WinitView {
|
impl WinitView {
|
||||||
#[unsafe(method(keyDown:))]
|
#[unsafe(method(keyDown:))]
|
||||||
fn key_down(&self, event: &NSEvent) {
|
fn key_down(&self, event: &NSEvent) {
|
||||||
trace_scope!("keyDown:");
|
let _entered = debug_span!("keyDown:").entered();
|
||||||
{
|
{
|
||||||
let mut prev_input_source = self.ivars().input_source.borrow_mut();
|
let mut prev_input_source = self.ivars().input_source.borrow_mut();
|
||||||
let current_input_source = self.current_input_source();
|
let current_input_source = self.current_input_source();
|
||||||
@@ -481,7 +490,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(keyUp:))]
|
#[unsafe(method(keyUp:))]
|
||||||
fn key_up(&self, event: &NSEvent) {
|
fn key_up(&self, event: &NSEvent) {
|
||||||
trace_scope!("keyUp:");
|
let _entered = debug_span!("keyUp:").entered();
|
||||||
|
|
||||||
let event = replace_event(event, self.option_as_alt());
|
let event = replace_event(event, self.option_as_alt());
|
||||||
self.update_modifiers(&event, false);
|
self.update_modifiers(&event, false);
|
||||||
@@ -498,14 +507,14 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(flagsChanged:))]
|
#[unsafe(method(flagsChanged:))]
|
||||||
fn flags_changed(&self, event: &NSEvent) {
|
fn flags_changed(&self, event: &NSEvent) {
|
||||||
trace_scope!("flagsChanged:");
|
let _entered = debug_span!("flagsChanged:").entered();
|
||||||
|
|
||||||
self.update_modifiers(event, true);
|
self.update_modifiers(event, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(insertTab:))]
|
#[unsafe(method(insertTab:))]
|
||||||
fn insert_tab(&self, _sender: Option<&AnyObject>) {
|
fn insert_tab(&self, _sender: Option<&AnyObject>) {
|
||||||
trace_scope!("insertTab:");
|
let _entered = debug_span!("insertTab:").entered();
|
||||||
let window = self.window();
|
let window = self.window();
|
||||||
if let Some(first_responder) = window.firstResponder() {
|
if let Some(first_responder) = window.firstResponder() {
|
||||||
if *first_responder == ***self {
|
if *first_responder == ***self {
|
||||||
@@ -516,7 +525,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(insertBackTab:))]
|
#[unsafe(method(insertBackTab:))]
|
||||||
fn insert_back_tab(&self, _sender: Option<&AnyObject>) {
|
fn insert_back_tab(&self, _sender: Option<&AnyObject>) {
|
||||||
trace_scope!("insertBackTab:");
|
let _entered = debug_span!("insertBackTab:").entered();
|
||||||
let window = self.window();
|
let window = self.window();
|
||||||
if let Some(first_responder) = window.firstResponder() {
|
if let Some(first_responder) = window.firstResponder() {
|
||||||
if *first_responder == ***self {
|
if *first_responder == ***self {
|
||||||
@@ -530,7 +539,7 @@ define_class!(
|
|||||||
#[unsafe(method(cancelOperation:))]
|
#[unsafe(method(cancelOperation:))]
|
||||||
fn cancel_operation(&self, _sender: Option<&AnyObject>) {
|
fn cancel_operation(&self, _sender: Option<&AnyObject>) {
|
||||||
let mtm = MainThreadMarker::from(self);
|
let mtm = MainThreadMarker::from(self);
|
||||||
trace_scope!("cancelOperation:");
|
let _entered = debug_span!("cancelOperation:").entered();
|
||||||
|
|
||||||
let event = NSApplication::sharedApplication(mtm)
|
let event = NSApplication::sharedApplication(mtm)
|
||||||
.currentEvent()
|
.currentEvent()
|
||||||
@@ -559,71 +568,73 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(mouseDown:))]
|
#[unsafe(method(mouseDown:))]
|
||||||
fn mouse_down(&self, event: &NSEvent) {
|
fn mouse_down(&self, event: &NSEvent) {
|
||||||
trace_scope!("mouseDown:");
|
let _entered = debug_span!("mouseDown:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
self.mouse_click(event, ElementState::Pressed);
|
self.mouse_click(event, ElementState::Pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(mouseUp:))]
|
#[unsafe(method(mouseUp:))]
|
||||||
fn mouse_up(&self, event: &NSEvent) {
|
fn mouse_up(&self, event: &NSEvent) {
|
||||||
trace_scope!("mouseUp:");
|
let _entered = debug_span!("mouseUp:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
self.mouse_click(event, ElementState::Released);
|
self.mouse_click(event, ElementState::Released);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(rightMouseDown:))]
|
#[unsafe(method(rightMouseDown:))]
|
||||||
fn right_mouse_down(&self, event: &NSEvent) {
|
fn right_mouse_down(&self, event: &NSEvent) {
|
||||||
trace_scope!("rightMouseDown:");
|
let _entered = debug_span!("rightMouseDown:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
self.mouse_click(event, ElementState::Pressed);
|
self.mouse_click(event, ElementState::Pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(rightMouseUp:))]
|
#[unsafe(method(rightMouseUp:))]
|
||||||
fn right_mouse_up(&self, event: &NSEvent) {
|
fn right_mouse_up(&self, event: &NSEvent) {
|
||||||
trace_scope!("rightMouseUp:");
|
let _entered = debug_span!("rightMouseUp:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
self.mouse_click(event, ElementState::Released);
|
self.mouse_click(event, ElementState::Released);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(otherMouseDown:))]
|
#[unsafe(method(otherMouseDown:))]
|
||||||
fn other_mouse_down(&self, event: &NSEvent) {
|
fn other_mouse_down(&self, event: &NSEvent) {
|
||||||
trace_scope!("otherMouseDown:");
|
let _entered = debug_span!("otherMouseDown:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
self.mouse_click(event, ElementState::Pressed);
|
self.mouse_click(event, ElementState::Pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(otherMouseUp:))]
|
#[unsafe(method(otherMouseUp:))]
|
||||||
fn other_mouse_up(&self, event: &NSEvent) {
|
fn other_mouse_up(&self, event: &NSEvent) {
|
||||||
trace_scope!("otherMouseUp:");
|
let _entered = debug_span!("otherMouseUp:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
self.mouse_click(event, ElementState::Released);
|
self.mouse_click(event, ElementState::Released);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No tracing on these because that would be overly verbose
|
|
||||||
|
|
||||||
#[unsafe(method(mouseMoved:))]
|
#[unsafe(method(mouseMoved:))]
|
||||||
fn mouse_moved(&self, event: &NSEvent) {
|
fn mouse_moved(&self, event: &NSEvent) {
|
||||||
|
let _entered = debug_span!("mouseMoved:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(mouseDragged:))]
|
#[unsafe(method(mouseDragged:))]
|
||||||
fn mouse_dragged(&self, event: &NSEvent) {
|
fn mouse_dragged(&self, event: &NSEvent) {
|
||||||
|
let _entered = debug_span!("mouseDragged:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(rightMouseDragged:))]
|
#[unsafe(method(rightMouseDragged:))]
|
||||||
fn right_mouse_dragged(&self, event: &NSEvent) {
|
fn right_mouse_dragged(&self, event: &NSEvent) {
|
||||||
|
let _entered = debug_span!("rightMouseDragged:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(otherMouseDragged:))]
|
#[unsafe(method(otherMouseDragged:))]
|
||||||
fn other_mouse_dragged(&self, event: &NSEvent) {
|
fn other_mouse_dragged(&self, event: &NSEvent) {
|
||||||
|
let _entered = debug_span!("otherMouseDragged:").entered();
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(mouseEntered:))]
|
#[unsafe(method(mouseEntered:))]
|
||||||
fn mouse_entered(&self, event: &NSEvent) {
|
fn mouse_entered(&self, event: &NSEvent) {
|
||||||
trace_scope!("mouseEntered:");
|
let _entered = debug_span!("mouseEntered:").entered();
|
||||||
|
|
||||||
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
|
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
|
||||||
|
|
||||||
@@ -637,7 +648,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(mouseExited:))]
|
#[unsafe(method(mouseExited:))]
|
||||||
fn mouse_exited(&self, event: &NSEvent) {
|
fn mouse_exited(&self, event: &NSEvent) {
|
||||||
trace_scope!("mouseExited:");
|
let _entered = debug_span!("mouseExited:").entered();
|
||||||
|
|
||||||
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
|
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
|
||||||
|
|
||||||
@@ -651,7 +662,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(scrollWheel:))]
|
#[unsafe(method(scrollWheel:))]
|
||||||
fn scroll_wheel(&self, event: &NSEvent) {
|
fn scroll_wheel(&self, event: &NSEvent) {
|
||||||
trace_scope!("scrollWheel:");
|
let _entered = debug_span!("scrollWheel:").entered();
|
||||||
|
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
|
|
||||||
@@ -682,16 +693,15 @@ define_class!(
|
|||||||
|
|
||||||
self.update_modifiers(event, false);
|
self.update_modifiers(event, false);
|
||||||
|
|
||||||
let time = self.ivars().app_state.event_time(event);
|
|
||||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||||
app.device_event(event_loop, None, time, DeviceEvent::MouseWheel { delta })
|
app.device_event(event_loop, None, DeviceEvent::MouseWheel { delta })
|
||||||
});
|
});
|
||||||
self.queue_event(WindowEvent::MouseWheel { device_id: None, delta, phase });
|
self.queue_event(WindowEvent::MouseWheel { device_id: None, delta, phase });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(magnifyWithEvent:))]
|
#[unsafe(method(magnifyWithEvent:))]
|
||||||
fn magnify_with_event(&self, event: &NSEvent) {
|
fn magnify_with_event(&self, event: &NSEvent) {
|
||||||
trace_scope!("magnifyWithEvent:");
|
let _entered = debug_span!("magnifyWithEvent:").entered();
|
||||||
|
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
|
|
||||||
@@ -713,7 +723,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(smartMagnifyWithEvent:))]
|
#[unsafe(method(smartMagnifyWithEvent:))]
|
||||||
fn smart_magnify_with_event(&self, event: &NSEvent) {
|
fn smart_magnify_with_event(&self, event: &NSEvent) {
|
||||||
trace_scope!("smartMagnifyWithEvent:");
|
let _entered = debug_span!("smartMagnifyWithEvent:").entered();
|
||||||
|
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
|
|
||||||
@@ -722,7 +732,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(rotateWithEvent:))]
|
#[unsafe(method(rotateWithEvent:))]
|
||||||
fn rotate_with_event(&self, event: &NSEvent) {
|
fn rotate_with_event(&self, event: &NSEvent) {
|
||||||
trace_scope!("rotateWithEvent:");
|
let _entered = debug_span!("rotateWithEvent:").entered();
|
||||||
|
|
||||||
self.mouse_motion(event);
|
self.mouse_motion(event);
|
||||||
|
|
||||||
@@ -744,7 +754,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(pressureChangeWithEvent:))]
|
#[unsafe(method(pressureChangeWithEvent:))]
|
||||||
fn pressure_change_with_event(&self, event: &NSEvent) {
|
fn pressure_change_with_event(&self, event: &NSEvent) {
|
||||||
trace_scope!("pressureChangeWithEvent:");
|
let _entered = debug_span!("pressureChangeWithEvent:").entered();
|
||||||
|
|
||||||
self.queue_event(WindowEvent::TouchpadPressure {
|
self.queue_event(WindowEvent::TouchpadPressure {
|
||||||
device_id: None,
|
device_id: None,
|
||||||
@@ -758,13 +768,13 @@ define_class!(
|
|||||||
// https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
|
// https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
|
||||||
#[unsafe(method(_wantsKeyDownForEvent:))]
|
#[unsafe(method(_wantsKeyDownForEvent:))]
|
||||||
fn wants_key_down_for_event(&self, _event: &NSEvent) -> bool {
|
fn wants_key_down_for_event(&self, _event: &NSEvent) -> bool {
|
||||||
trace_scope!("_wantsKeyDownForEvent:");
|
let _entered = debug_span!("_wantsKeyDownForEvent:").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(acceptsFirstMouse:))]
|
#[unsafe(method(acceptsFirstMouse:))]
|
||||||
fn accepts_first_mouse(&self, _event: &NSEvent) -> bool {
|
fn accepts_first_mouse(&self, _event: &NSEvent) -> bool {
|
||||||
trace_scope!("acceptsFirstMouse:");
|
let _entered = debug_span!("acceptsFirstMouse:").entered();
|
||||||
self.ivars().accepts_first_mouse
|
self.ivars().accepts_first_mouse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -795,6 +805,10 @@ impl WinitView {
|
|||||||
let this: Retained<Self> = unsafe { msg_send![super(this), init] };
|
let this: Retained<Self> = unsafe { msg_send![super(this), init] };
|
||||||
*this.ivars().input_source.borrow_mut() = this.current_input_source();
|
*this.ivars().input_source.borrow_mut() = this.current_input_source();
|
||||||
|
|
||||||
|
// Ask AppKit to redisplay the layer while the view is being resized so layer-backed
|
||||||
|
// surfaces keep painting.
|
||||||
|
this.setLayerContentsRedrawPolicy(NSViewLayerContentsRedrawPolicy::DuringViewResize);
|
||||||
|
|
||||||
// `MouseEnteredAndExited` enables receiving events through `mouseEntered:` and
|
// `MouseEnteredAndExited` enables receiving events through `mouseEntered:` and
|
||||||
// `mouseExited:`.
|
// `mouseExited:`.
|
||||||
//
|
//
|
||||||
@@ -847,19 +861,43 @@ impl WinitView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn queue_event(&self, event: WindowEvent) {
|
fn queue_event(&self, event: WindowEvent) {
|
||||||
let app = NSApplication::sharedApplication(self.mtm());
|
|
||||||
let window_id = window_id(&self.window());
|
let window_id = window_id(&self.window());
|
||||||
let time = if let Some(nsevent) = app.currentEvent() {
|
|
||||||
self.ivars().app_state.event_time(&nsevent)
|
|
||||||
} else {
|
|
||||||
warn!("queued event with wrong timestamp, no active NSEvent found");
|
|
||||||
Instant::now()
|
|
||||||
};
|
|
||||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||||
app.window_event(event_loop, window_id, time, event);
|
app.window_event(event_loop, window_id, event);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn surface_resized(&self) {
|
||||||
|
let Some(window) = (**self).window() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let size = self.surface_size();
|
||||||
|
let window_id = window_id(&window);
|
||||||
|
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||||
|
app.window_event(event_loop, window_id, WindowEvent::SurfaceResized(size));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the drawable size from the view's backing-coordinate bounds.
|
||||||
|
pub(super) fn surface_size(&self) -> PhysicalSize<u32> {
|
||||||
|
// The view bounds are authoritative for full-size content views and during live resize.
|
||||||
|
// Deriving this from the window frame can exclude custom titlebar content or be stale.
|
||||||
|
let backing_bounds = self.convertRectToBacking(self.bounds());
|
||||||
|
PhysicalSize::new(
|
||||||
|
backing_bounds.size.width.round().max(0.0) as u32,
|
||||||
|
backing_bounds.size.height.round().max(0.0) as u32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redraw_during_live_resize(&self) {
|
||||||
|
let Some(window) = (**self).window() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if window.inLiveResize() {
|
||||||
|
self.ivars().app_state.handle_redraw(window_id(&window));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn scale_factor(&self) -> f64 {
|
fn scale_factor(&self) -> f64 {
|
||||||
self.window().backingScaleFactor() as f64
|
self.window().backingScaleFactor() as f64
|
||||||
}
|
}
|
||||||
@@ -1176,3 +1214,92 @@ fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Retained<NSEven
|
|||||||
event.copy()
|
event.copy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a UTF-16 code unit offset into the corresponding UTF-8 byte offset within `s`.
|
||||||
|
///
|
||||||
|
/// IMEs are not required to send well-formed offsets, so this is defensive: an offset that
|
||||||
|
/// would split a surrogate pair is snapped down to the start of that character, and an
|
||||||
|
/// out-of-bounds offset is clamped to the end of the string (e.g. native Pinyin sends
|
||||||
|
/// out-of-bounds indices, see <https://github.com/alacritty/alacritty/issues/8791>).
|
||||||
|
///
|
||||||
|
/// The mapping is monotone non-decreasing, so applying it to the location and end of an
|
||||||
|
/// `NSRange` (where `location <= end`) can never produce an inverted byte range.
|
||||||
|
fn utf16_to_utf8_offset(s: &str, utf16_offset: usize) -> usize {
|
||||||
|
let mut utf16_pos = 0;
|
||||||
|
for (utf8_pos, ch) in s.char_indices() {
|
||||||
|
if utf16_pos >= utf16_offset {
|
||||||
|
return utf8_pos;
|
||||||
|
}
|
||||||
|
utf16_pos += ch.len_utf16();
|
||||||
|
// The target offset lands strictly inside this character's UTF-16 representation,
|
||||||
|
// i.e. it splits a surrogate pair: snap down to the character boundary.
|
||||||
|
if utf16_pos > utf16_offset {
|
||||||
|
return utf8_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Apply the UTF-16 -> UTF-8 conversion to both ends of a `selectedRange {loc, len}`,
|
||||||
|
/// mirroring what `set_marked_text` does for the emitted `Ime::Preedit` cursor range.
|
||||||
|
fn convert(s: &str, loc: usize, len: usize) -> (usize, usize) {
|
||||||
|
(utf16_to_utf8_offset(s, loc), utf16_to_utf8_offset(s, loc + len))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mid_surrogate_offset_snaps_down() {
|
||||||
|
// "😀a": 😀 is one char = 2 UTF-16 units = 4 UTF-8 bytes; offset 1 is mid-pair.
|
||||||
|
assert_eq!(utf16_to_utf8_offset("\u{1F600}a", 1), 0);
|
||||||
|
// Offset 2 is the boundary just after the pair.
|
||||||
|
assert_eq!(utf16_to_utf8_offset("\u{1F600}a", 2), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_longer_inverted() {
|
||||||
|
// "a😀b" with selectedRange {1,1}: previously emitted (1, 0) -- lower > upper, a
|
||||||
|
// slice-panic vector. The boundary-snapping conversion keeps lower <= upper.
|
||||||
|
assert_eq!(convert("a\u{1F600}b", 1, 1), (1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn prefix_preserved_on_mid_pair_collapse() {
|
||||||
|
// "a😀b" with selectedRange {2,0}: previously collapsed to (0, 0), discarding the
|
||||||
|
// valid "a" prefix; now snaps to the char boundary after "a".
|
||||||
|
assert_eq!(convert("a\u{1F600}b", 2, 0), (1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn out_of_bounds_clamps_to_len() {
|
||||||
|
// Subsumes the #4494 `.min(len)` clamp: an out-of-bounds offset maps to the string
|
||||||
|
// length instead of triggering an NSRangeException.
|
||||||
|
assert_eq!(convert("\u{1F600}a", 99, 0), (5, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn well_formed_inputs_are_identity() {
|
||||||
|
// The common case (well-formed boundary indices) must be byte-for-byte unchanged.
|
||||||
|
assert_eq!(convert("a\u{1F600}b", 3, 0), (5, 5));
|
||||||
|
assert_eq!(convert("a\u{1F600}b", 4, 0), (6, 6));
|
||||||
|
// BMP multi-byte (Japanese): each char is 1 UTF-16 unit and 3 UTF-8 bytes.
|
||||||
|
assert_eq!(convert("\u{3053}\u{3093}", 1, 1), (3, 6));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn monotone_non_decreasing() {
|
||||||
|
// Sweep every UTF-16 offset (including out-of-bounds) over a string mixing BMP and
|
||||||
|
// non-BMP characters and assert the conversion never goes backwards, which is what
|
||||||
|
// guarantees `lower <= upper` for any `NSRange`.
|
||||||
|
let s = "a\u{1F600}b\u{3053}\u{1F4A9}c";
|
||||||
|
let mut prev = 0;
|
||||||
|
for off in 0..=20 {
|
||||||
|
let cur = utf16_to_utf8_offset(s, off);
|
||||||
|
assert!(cur >= prev, "non-monotone at offset {off}: {cur} < {prev}");
|
||||||
|
assert!(cur <= s.len(), "offset {off} mapped past end: {cur} > {}", s.len());
|
||||||
|
prev = cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use objc2::rc::{Retained, autoreleasepool};
|
|||||||
use objc2::{MainThreadMarker, Message, define_class};
|
use objc2::{MainThreadMarker, Message, define_class};
|
||||||
use objc2_app_kit::{NSPanel, NSResponder, NSWindow};
|
use objc2_app_kit::{NSPanel, NSResponder, NSWindow};
|
||||||
use objc2_foundation::NSObject;
|
use objc2_foundation::NSObject;
|
||||||
|
use tracing::trace_span;
|
||||||
use winit_core::cursor::Cursor;
|
use winit_core::cursor::Cursor;
|
||||||
use winit_core::error::RequestError;
|
use winit_core::error::RequestError;
|
||||||
use winit_core::icon::Icon;
|
use winit_core::icon::Icon;
|
||||||
@@ -350,13 +351,13 @@ define_class!(
|
|||||||
impl WinitWindow {
|
impl WinitWindow {
|
||||||
#[unsafe(method(canBecomeMainWindow))]
|
#[unsafe(method(canBecomeMainWindow))]
|
||||||
fn can_become_main_window(&self) -> bool {
|
fn can_become_main_window(&self) -> bool {
|
||||||
trace_scope!("canBecomeMainWindow");
|
let _entered = trace_span!("canBecomeMainWindow").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(canBecomeKeyWindow))]
|
#[unsafe(method(canBecomeKeyWindow))]
|
||||||
fn can_become_key_window(&self) -> bool {
|
fn can_become_key_window(&self) -> bool {
|
||||||
trace_scope!("canBecomeKeyWindow");
|
let _entered = trace_span!("canBecomeKeyWindow").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,7 +375,7 @@ define_class!(
|
|||||||
// it doesn't if window doesn't have NSWindowStyleMask::Titled
|
// it doesn't if window doesn't have NSWindowStyleMask::Titled
|
||||||
#[unsafe(method(canBecomeKeyWindow))]
|
#[unsafe(method(canBecomeKeyWindow))]
|
||||||
fn can_become_key_window(&self) -> bool {
|
fn can_become_key_window(&self) -> bool {
|
||||||
trace_scope!("canBecomeKeyWindow");
|
let _entered = trace_span!("canBecomeKeyWindow").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use std::ffi::c_void;
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use dpi::{
|
use dpi::{
|
||||||
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
|
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
|
||||||
@@ -42,7 +41,7 @@ use objc2_foundation::{
|
|||||||
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
|
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
|
||||||
NSRect, NSSize, NSString, ns_string,
|
NSRect, NSSize, NSString, ns_string,
|
||||||
};
|
};
|
||||||
use tracing::{trace, warn};
|
use tracing::{debug_span, trace, warn};
|
||||||
use winit_common::core_foundation::MainRunLoop;
|
use winit_common::core_foundation::MainRunLoop;
|
||||||
use winit_core::cursor::Cursor;
|
use winit_core::cursor::Cursor;
|
||||||
use winit_core::error::{NotSupportedError, RequestError};
|
use winit_core::error::{NotSupportedError, RequestError};
|
||||||
@@ -122,14 +121,14 @@ define_class!(
|
|||||||
unsafe impl NSWindowDelegate for WindowDelegate {
|
unsafe impl NSWindowDelegate for WindowDelegate {
|
||||||
#[unsafe(method(windowShouldClose:))]
|
#[unsafe(method(windowShouldClose:))]
|
||||||
fn window_should_close(&self, _: Option<&AnyObject>) -> bool {
|
fn window_should_close(&self, _: Option<&AnyObject>) -> bool {
|
||||||
trace_scope!("windowShouldClose:");
|
let _entered = debug_span!("windowShouldClose:").entered();
|
||||||
self.queue_event(WindowEvent::CloseRequested);
|
self.queue_event(WindowEvent::CloseRequested);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(windowWillClose:))]
|
#[unsafe(method(windowWillClose:))]
|
||||||
fn window_will_close(&self, _: Option<&AnyObject>) {
|
fn window_will_close(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowWillClose:");
|
let _entered = debug_span!("windowWillClose:").entered();
|
||||||
// `setDelegate:` retains the previous value and then autoreleases it
|
// `setDelegate:` retains the previous value and then autoreleases it
|
||||||
autoreleasepool(|_| {
|
autoreleasepool(|_| {
|
||||||
// Since El Capitan, we need to be careful that delegate methods can't
|
// Since El Capitan, we need to be careful that delegate methods can't
|
||||||
@@ -141,14 +140,14 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(windowDidResize:))]
|
#[unsafe(method(windowDidResize:))]
|
||||||
fn window_did_resize(&self, _: Option<&AnyObject>) {
|
fn window_did_resize(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidResize:");
|
let _entered = debug_span!("windowDidResize:").entered();
|
||||||
// NOTE: WindowEvent::SurfaceResized is reported using NSViewFrameDidChangeNotification.
|
// NOTE: WindowEvent::SurfaceResized is reported using NSViewFrameDidChangeNotification.
|
||||||
self.emit_move_event();
|
self.emit_move_event();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(windowWillStartLiveResize:))]
|
#[unsafe(method(windowWillStartLiveResize:))]
|
||||||
fn window_will_start_live_resize(&self, _: Option<&AnyObject>) {
|
fn window_will_start_live_resize(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowWillStartLiveResize:");
|
let _entered = debug_span!("windowWillStartLiveResize:").entered();
|
||||||
|
|
||||||
let increments = self.ivars().surface_resize_increments.get();
|
let increments = self.ivars().surface_resize_increments.get();
|
||||||
self.set_resize_increments_inner(increments);
|
self.set_resize_increments_inner(increments);
|
||||||
@@ -156,20 +155,20 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(windowDidEndLiveResize:))]
|
#[unsafe(method(windowDidEndLiveResize:))]
|
||||||
fn window_did_end_live_resize(&self, _: Option<&AnyObject>) {
|
fn window_did_end_live_resize(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidEndLiveResize:");
|
let _entered = debug_span!("windowDidEndLiveResize:").entered();
|
||||||
self.set_resize_increments_inner(NSSize::new(1., 1.));
|
self.set_resize_increments_inner(NSSize::new(1., 1.));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This won't be triggered if the move was part of a resize.
|
// This won't be triggered if the move was part of a resize.
|
||||||
#[unsafe(method(windowDidMove:))]
|
#[unsafe(method(windowDidMove:))]
|
||||||
fn window_did_move(&self, _: Option<&AnyObject>) {
|
fn window_did_move(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidMove:");
|
let _entered = debug_span!("windowDidMove:").entered();
|
||||||
self.emit_move_event();
|
self.emit_move_event();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(windowDidChangeBackingProperties:))]
|
#[unsafe(method(windowDidChangeBackingProperties:))]
|
||||||
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
|
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidChangeBackingProperties:");
|
let _entered = debug_span!("windowDidChangeBackingProperties:").entered();
|
||||||
let scale_factor = self.scale_factor();
|
let scale_factor = self.scale_factor();
|
||||||
if scale_factor == self.ivars().previous_scale_factor.get() {
|
if scale_factor == self.ivars().previous_scale_factor.get() {
|
||||||
return;
|
return;
|
||||||
@@ -185,7 +184,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(windowDidBecomeKey:))]
|
#[unsafe(method(windowDidBecomeKey:))]
|
||||||
fn window_did_become_key(&self, _: Option<&AnyObject>) {
|
fn window_did_become_key(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidBecomeKey:");
|
let _entered = debug_span!("windowDidBecomeKey:").entered();
|
||||||
// TODO: center the cursor if the window had mouse grab when it
|
// TODO: center the cursor if the window had mouse grab when it
|
||||||
// lost focus
|
// lost focus
|
||||||
self.queue_event(WindowEvent::Focused(true));
|
self.queue_event(WindowEvent::Focused(true));
|
||||||
@@ -193,7 +192,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(windowDidResignKey:))]
|
#[unsafe(method(windowDidResignKey:))]
|
||||||
fn window_did_resign_key(&self, _: Option<&AnyObject>) {
|
fn window_did_resign_key(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidResignKey:");
|
let _entered = debug_span!("windowDidResignKey:").entered();
|
||||||
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
|
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
|
||||||
// NSWindowDelegate will receive a didResignKey event despite no event
|
// NSWindowDelegate will receive a didResignKey event despite no event
|
||||||
// being received when the modifiers are released. This is because
|
// being received when the modifiers are released. This is because
|
||||||
@@ -209,7 +208,7 @@ define_class!(
|
|||||||
/// Invoked when before enter fullscreen
|
/// Invoked when before enter fullscreen
|
||||||
#[unsafe(method(windowWillEnterFullScreen:))]
|
#[unsafe(method(windowWillEnterFullScreen:))]
|
||||||
fn window_will_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
fn window_will_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowWillEnterFullScreen:");
|
let _entered = debug_span!("windowWillEnterFullScreen:").entered();
|
||||||
|
|
||||||
self.ivars().maximized.set(self.is_zoomed());
|
self.ivars().maximized.set(self.is_zoomed());
|
||||||
let mut fullscreen = self.ivars().fullscreen.borrow_mut();
|
let mut fullscreen = self.ivars().fullscreen.borrow_mut();
|
||||||
@@ -237,7 +236,7 @@ define_class!(
|
|||||||
/// Invoked when before exit fullscreen
|
/// Invoked when before exit fullscreen
|
||||||
#[unsafe(method(windowWillExitFullScreen:))]
|
#[unsafe(method(windowWillExitFullScreen:))]
|
||||||
fn window_will_exit_fullscreen(&self, _: Option<&AnyObject>) {
|
fn window_will_exit_fullscreen(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowWillExitFullScreen:");
|
let _entered = debug_span!("windowWillExitFullScreen:").entered();
|
||||||
|
|
||||||
self.ivars().in_fullscreen_transition.set(true);
|
self.ivars().in_fullscreen_transition.set(true);
|
||||||
}
|
}
|
||||||
@@ -248,7 +247,7 @@ define_class!(
|
|||||||
_: Option<&AnyObject>,
|
_: Option<&AnyObject>,
|
||||||
proposed_options: NSApplicationPresentationOptions,
|
proposed_options: NSApplicationPresentationOptions,
|
||||||
) -> NSApplicationPresentationOptions {
|
) -> NSApplicationPresentationOptions {
|
||||||
trace_scope!("window:willUseFullScreenPresentationOptions:");
|
let _entered = debug_span!("window:willUseFullScreenPresentationOptions:").entered();
|
||||||
// Generally, games will want to disable the menu bar and the dock. Ideally,
|
// Generally, games will want to disable the menu bar and the dock. Ideally,
|
||||||
// this would be configurable by the user. Unfortunately because of our
|
// this would be configurable by the user. Unfortunately because of our
|
||||||
// `CGShieldingWindowLevel() + 1` hack (see `set_fullscreen`), our window is
|
// `CGShieldingWindowLevel() + 1` hack (see `set_fullscreen`), our window is
|
||||||
@@ -271,7 +270,7 @@ define_class!(
|
|||||||
/// Invoked when entered fullscreen
|
/// Invoked when entered fullscreen
|
||||||
#[unsafe(method(windowDidEnterFullScreen:))]
|
#[unsafe(method(windowDidEnterFullScreen:))]
|
||||||
fn window_did_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
fn window_did_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidEnterFullScreen:");
|
let _entered = debug_span!("windowDidEnterFullScreen:").entered();
|
||||||
self.ivars().initial_fullscreen.set(false);
|
self.ivars().initial_fullscreen.set(false);
|
||||||
self.ivars().in_fullscreen_transition.set(false);
|
self.ivars().in_fullscreen_transition.set(false);
|
||||||
if let Some(target_fullscreen) = self.ivars().target_fullscreen.take() {
|
if let Some(target_fullscreen) = self.ivars().target_fullscreen.take() {
|
||||||
@@ -282,7 +281,7 @@ define_class!(
|
|||||||
/// Invoked when exited fullscreen
|
/// Invoked when exited fullscreen
|
||||||
#[unsafe(method(windowDidExitFullScreen:))]
|
#[unsafe(method(windowDidExitFullScreen:))]
|
||||||
fn window_did_exit_fullscreen(&self, _: Option<&AnyObject>) {
|
fn window_did_exit_fullscreen(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidExitFullScreen:");
|
let _entered = debug_span!("windowDidExitFullScreen:").entered();
|
||||||
|
|
||||||
self.restore_state_from_fullscreen();
|
self.restore_state_from_fullscreen();
|
||||||
self.ivars().in_fullscreen_transition.set(false);
|
self.ivars().in_fullscreen_transition.set(false);
|
||||||
@@ -309,7 +308,7 @@ define_class!(
|
|||||||
/// work you may have done to prepare to enter full-screen mode.
|
/// work you may have done to prepare to enter full-screen mode.
|
||||||
#[unsafe(method(windowDidFailToEnterFullScreen:))]
|
#[unsafe(method(windowDidFailToEnterFullScreen:))]
|
||||||
fn window_did_fail_to_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
fn window_did_fail_to_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidFailToEnterFullScreen:");
|
let _entered = debug_span!("windowDidFailToEnterFullScreen:").entered();
|
||||||
self.ivars().in_fullscreen_transition.set(false);
|
self.ivars().in_fullscreen_transition.set(false);
|
||||||
self.ivars().target_fullscreen.replace(None);
|
self.ivars().target_fullscreen.replace(None);
|
||||||
if self.ivars().initial_fullscreen.get() {
|
if self.ivars().initial_fullscreen.get() {
|
||||||
@@ -328,7 +327,7 @@ define_class!(
|
|||||||
// Invoked when the occlusion state of the window changes
|
// Invoked when the occlusion state of the window changes
|
||||||
#[unsafe(method(windowDidChangeOcclusionState:))]
|
#[unsafe(method(windowDidChangeOcclusionState:))]
|
||||||
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
|
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidChangeOcclusionState:");
|
let _entered = debug_span!("windowDidChangeOcclusionState:").entered();
|
||||||
let visible = self.window().occlusionState().contains(NSWindowOcclusionState::Visible);
|
let visible = self.window().occlusionState().contains(NSWindowOcclusionState::Visible);
|
||||||
self.queue_event(WindowEvent::Occluded(!visible));
|
self.queue_event(WindowEvent::Occluded(!visible));
|
||||||
|
|
||||||
@@ -349,7 +348,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(windowDidChangeScreen:))]
|
#[unsafe(method(windowDidChangeScreen:))]
|
||||||
fn window_did_change_screen(&self, _: Option<&AnyObject>) {
|
fn window_did_change_screen(&self, _: Option<&AnyObject>) {
|
||||||
trace_scope!("windowDidChangeScreen:");
|
let _entered = debug_span!("windowDidChangeScreen:").entered();
|
||||||
let is_simple_fullscreen = self.ivars().is_simple_fullscreen.get();
|
let is_simple_fullscreen = self.ivars().is_simple_fullscreen.get();
|
||||||
if is_simple_fullscreen {
|
if is_simple_fullscreen {
|
||||||
if let Some(screen) = self.window().screen() {
|
if let Some(screen) = self.window().screen() {
|
||||||
@@ -363,7 +362,7 @@ define_class!(
|
|||||||
/// Invoked when the dragged image enters destination bounds or frame
|
/// Invoked when the dragged image enters destination bounds or frame
|
||||||
#[unsafe(method(draggingEntered:))]
|
#[unsafe(method(draggingEntered:))]
|
||||||
fn dragging_entered(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
|
fn dragging_entered(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
|
||||||
trace_scope!("draggingEntered:");
|
let _entered = debug_span!("draggingEntered:").entered();
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@@ -394,7 +393,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(wantsPeriodicDraggingUpdates))]
|
#[unsafe(method(wantsPeriodicDraggingUpdates))]
|
||||||
fn wants_periodic_dragging_updates(&self) -> bool {
|
fn wants_periodic_dragging_updates(&self) -> bool {
|
||||||
trace_scope!("wantsPeriodicDraggingUpdates:");
|
let _entered = debug_span!("wantsPeriodicDraggingUpdates:").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +401,7 @@ define_class!(
|
|||||||
/// modification of the dragging operation or mouse-pointer position.
|
/// modification of the dragging operation or mouse-pointer position.
|
||||||
#[unsafe(method(draggingUpdated:))]
|
#[unsafe(method(draggingUpdated:))]
|
||||||
fn dragging_updated(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
|
fn dragging_updated(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
|
||||||
trace_scope!("draggingUpdated:");
|
let _entered = debug_span!("draggingUpdated:").entered();
|
||||||
|
|
||||||
let dl = sender.draggingLocation();
|
let dl = sender.draggingLocation();
|
||||||
let dl = self.view().convertPoint_fromView(dl, None);
|
let dl = self.view().convertPoint_fromView(dl, None);
|
||||||
@@ -417,14 +416,14 @@ define_class!(
|
|||||||
/// Invoked when the image is released
|
/// Invoked when the image is released
|
||||||
#[unsafe(method(prepareForDragOperation:))]
|
#[unsafe(method(prepareForDragOperation:))]
|
||||||
fn prepare_for_drag_operation(&self, _sender: &NSObject) -> bool {
|
fn prepare_for_drag_operation(&self, _sender: &NSObject) -> bool {
|
||||||
trace_scope!("prepareForDragOperation:");
|
let _entered = debug_span!("prepareForDragOperation:").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoked after the released image has been removed from the screen
|
/// Invoked after the released image has been removed from the screen
|
||||||
#[unsafe(method(performDragOperation:))]
|
#[unsafe(method(performDragOperation:))]
|
||||||
fn perform_drag_operation(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
|
fn perform_drag_operation(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
|
||||||
trace_scope!("performDragOperation:");
|
let _entered = debug_span!("performDragOperation:").entered();
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@@ -456,13 +455,13 @@ define_class!(
|
|||||||
/// Invoked when the dragging operation is complete
|
/// Invoked when the dragging operation is complete
|
||||||
#[unsafe(method(concludeDragOperation:))]
|
#[unsafe(method(concludeDragOperation:))]
|
||||||
fn conclude_drag_operation(&self, _sender: Option<&NSObject>) {
|
fn conclude_drag_operation(&self, _sender: Option<&NSObject>) {
|
||||||
trace_scope!("concludeDragOperation:");
|
let _entered = debug_span!("concludeDragOperation:").entered();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoked when the dragging operation is cancelled
|
/// Invoked when the dragging operation is cancelled
|
||||||
#[unsafe(method(draggingExited:))]
|
#[unsafe(method(draggingExited:))]
|
||||||
fn dragging_exited(&self, sender: Option<&ProtocolObject<dyn NSDraggingInfo>>) {
|
fn dragging_exited(&self, sender: Option<&ProtocolObject<dyn NSDraggingInfo>>) {
|
||||||
trace_scope!("draggingExited:");
|
let _entered = debug_span!("draggingExited:").entered();
|
||||||
|
|
||||||
let position = sender.map(|sender| {
|
let position = sender.map(|sender| {
|
||||||
let dl = sender.draggingLocation();
|
let dl = sender.draggingLocation();
|
||||||
@@ -484,7 +483,7 @@ define_class!(
|
|||||||
change: Option<&NSDictionary<NSKeyValueChangeKey, AnyObject>>,
|
change: Option<&NSDictionary<NSKeyValueChangeKey, AnyObject>>,
|
||||||
_context: *mut c_void,
|
_context: *mut c_void,
|
||||||
) {
|
) {
|
||||||
trace_scope!("observeValueForKeyPath:ofObject:change:context:");
|
let _entered = debug_span!("observeValueForKeyPath:ofObject:change:context:").entered();
|
||||||
// NOTE: We don't _really_ need to check the key path, as there should only be one, but
|
// NOTE: We don't _really_ need to check the key path, as there should only be one, but
|
||||||
// in the future we might want to observe other key paths.
|
// in the future we might want to observe other key paths.
|
||||||
if key_path == Some(ns_string!("effectiveAppearance")) {
|
if key_path == Some(ns_string!("effectiveAppearance")) {
|
||||||
@@ -904,26 +903,16 @@ impl WindowDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
||||||
let app = NSApplication::sharedApplication(self.mtm());
|
|
||||||
let window_id = window_id(self.window());
|
let window_id = window_id(self.window());
|
||||||
let time = if let Some(nsevent) = app.currentEvent() {
|
|
||||||
self.ivars().app_state.event_time(&nsevent)
|
|
||||||
} else {
|
|
||||||
warn!("queued event with wrong timestamp, no active NSEvent found");
|
|
||||||
Instant::now()
|
|
||||||
};
|
|
||||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||||
app.window_event(event_loop, window_id, time, event);
|
app.window_event(event_loop, window_id, event);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_scale_factor_changed(&self, scale_factor: CGFloat) {
|
fn handle_scale_factor_changed(&self, scale_factor: CGFloat) {
|
||||||
let window = self.window();
|
let window = self.window();
|
||||||
|
|
||||||
let content_size = window.contentRectForFrameRect(window.frame()).size;
|
let suggested_size = self.view().surface_size();
|
||||||
let content_size = LogicalSize::new(content_size.width, content_size.height);
|
|
||||||
|
|
||||||
let suggested_size = content_size.to_physical(scale_factor);
|
|
||||||
let new_surface_size = Arc::new(Mutex::new(suggested_size));
|
let new_surface_size = Arc::new(Mutex::new(suggested_size));
|
||||||
self.queue_event(WindowEvent::ScaleFactorChanged {
|
self.queue_event(WindowEvent::ScaleFactorChanged {
|
||||||
scale_factor,
|
scale_factor,
|
||||||
@@ -1048,9 +1037,7 @@ impl WindowDelegate {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
||||||
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
|
self.view().surface_size()
|
||||||
let logical = LogicalSize::new(content_rect.size.width, content_rect.size.height);
|
|
||||||
logical.to_physical(self.scale_factor())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ xkb = ["dep:xkbcommon-dl", "dep:smol_str"]
|
|||||||
# CoreFoundation
|
# CoreFoundation
|
||||||
core-foundation = ["dep:block2", "dep:objc2", "dep:objc2-core-foundation"]
|
core-foundation = ["dep:block2", "dep:objc2", "dep:objc2-core-foundation"]
|
||||||
|
|
||||||
|
# Foundation
|
||||||
|
foundation = ["dep:block2", "dep:objc2", "dep:objc2-foundation"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
smol_str = { workspace = true, optional = true }
|
smol_str = { workspace = true, optional = true }
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
@@ -30,7 +33,7 @@ memmap2 = { workspace = true, optional = true }
|
|||||||
x11-dl = { workspace = true, optional = true }
|
x11-dl = { workspace = true, optional = true }
|
||||||
xkbcommon-dl = { workspace = true, optional = true }
|
xkbcommon-dl = { workspace = true, optional = true }
|
||||||
|
|
||||||
# CoreFoundation
|
# Foundation / CoreFoundation
|
||||||
block2 = { workspace = true, optional = true }
|
block2 = { workspace = true, optional = true }
|
||||||
objc2 = { workspace = true, optional = true }
|
objc2 = { workspace = true, optional = true }
|
||||||
objc2-core-foundation = { workspace = true, optional = true, features = [
|
objc2-core-foundation = { workspace = true, optional = true, features = [
|
||||||
@@ -40,6 +43,13 @@ objc2-core-foundation = { workspace = true, optional = true, features = [
|
|||||||
"CFRunLoop",
|
"CFRunLoop",
|
||||||
"CFString",
|
"CFString",
|
||||||
] }
|
] }
|
||||||
|
objc2-foundation = { workspace = true, optional = true, features = [
|
||||||
|
"std",
|
||||||
|
"block2",
|
||||||
|
"NSNotification",
|
||||||
|
"NSString",
|
||||||
|
"NSOperation",
|
||||||
|
] }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use std::cell::Cell;
|
|||||||
|
|
||||||
use objc2::MainThreadMarker;
|
use objc2::MainThreadMarker;
|
||||||
use objc2_core_foundation::{CFRetained, CFRunLoop, CFRunLoopMode, kCFRunLoopDefaultMode};
|
use objc2_core_foundation::{CFRetained, CFRunLoop, CFRunLoopMode, kCFRunLoopDefaultMode};
|
||||||
use tracing::error;
|
use tracing::{Span, error};
|
||||||
|
|
||||||
use super::MainRunLoopObserver;
|
use super::MainRunLoopObserver;
|
||||||
|
|
||||||
@@ -49,7 +49,8 @@ impl MainRunLoop {
|
|||||||
/// This queuing could be implemented in the following several ways with subtle differences in
|
/// This queuing could be implemented in the following several ways with subtle differences in
|
||||||
/// timing. This list is sorted in rough order in which they are run:
|
/// timing. This list is sorted in rough order in which they are run:
|
||||||
///
|
///
|
||||||
/// 1. Using `CFRunLoopPerformBlock` or `-[NSRunLoop performBlock:]`.
|
/// 1. Using `CFRunLoopPerformBlock` or `-[NSRunLoop performBlock:]` to queue a closure to run
|
||||||
|
/// the next time runloop sources are processed.
|
||||||
///
|
///
|
||||||
/// 2. Using `-[NSObject performSelectorOnMainThread:withObject:waitUntilDone:]` or wrapping the
|
/// 2. Using `-[NSObject performSelectorOnMainThread:withObject:waitUntilDone:]` or wrapping the
|
||||||
/// event in `NSEvent` and posting that to `-[NSApplication postEvent:atStart:]` (both
|
/// event in `NSEvent` and posting that to `-[NSApplication postEvent:atStart:]` (both
|
||||||
@@ -73,9 +74,17 @@ impl MainRunLoop {
|
|||||||
/// put the event at the very front of the queue, to be handled as soon as possible after
|
/// put the event at the very front of the queue, to be handled as soon as possible after
|
||||||
/// handling whatever event it's currently handling.
|
/// handling whatever event it's currently handling.
|
||||||
pub fn queue_closure(&self, closure: impl FnOnce() + 'static) {
|
pub fn queue_closure(&self, closure: impl FnOnce() + 'static) {
|
||||||
|
// We use this to run a closure asynchronously at a later point, so it also makes sense to
|
||||||
|
// re-enter the current span when running the queued closure.
|
||||||
|
let span = Span::current();
|
||||||
|
|
||||||
// Convert `FnOnce()` to `Block<dyn Fn()>`.
|
// Convert `FnOnce()` to `Block<dyn Fn()>`.
|
||||||
let closure = Cell::new(Some(closure));
|
let closure = Cell::new(Some(closure));
|
||||||
let block = block2::RcBlock::new(move || {
|
let block = block2::RcBlock::new(move || {
|
||||||
|
// Running this block happens inside a `CFRunLoopSource`, but the spans that we emit for
|
||||||
|
// that are (intentionally) overwritten by entering the span from before.
|
||||||
|
let _enter = span.enter();
|
||||||
|
|
||||||
debug_assert!(MainThreadMarker::new().is_some());
|
debug_assert!(MainThreadMarker::new().is_some());
|
||||||
if let Some(closure) = closure.take() {
|
if let Some(closure) = closure.take() {
|
||||||
closure()
|
closure()
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
mod event_loop_proxy;
|
mod event_loop_proxy;
|
||||||
mod main_run_loop;
|
mod main_run_loop;
|
||||||
mod main_run_loop_observer;
|
mod main_run_loop_observer;
|
||||||
|
mod tracing_observers;
|
||||||
|
|
||||||
pub use self::event_loop_proxy::*;
|
pub use self::event_loop_proxy::*;
|
||||||
pub use self::main_run_loop::*;
|
pub use self::main_run_loop::*;
|
||||||
pub use self::main_run_loop_observer::*;
|
pub use self::main_run_loop_observer::*;
|
||||||
|
pub use self::tracing_observers::*;
|
||||||
|
|||||||
146
winit-common/src/core_foundation/tracing_observers.rs
Normal file
146
winit-common/src/core_foundation/tracing_observers.rs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use objc2::MainThreadMarker;
|
||||||
|
use objc2_core_foundation::{CFIndex, CFRunLoop, CFRunLoopActivity, kCFRunLoopDefaultMode};
|
||||||
|
use tracing::span::EnteredSpan;
|
||||||
|
use tracing::{Level, error, field, span_enabled, trace_span};
|
||||||
|
|
||||||
|
use crate::core_foundation::MainRunLoopObserver;
|
||||||
|
|
||||||
|
/// Create two run loop observers that add TRACE-level [spans][tracing::span].
|
||||||
|
///
|
||||||
|
/// This is useful when debugging run loops, it makes it easier to see in which run loop activity an
|
||||||
|
/// event is triggered inside (if any).
|
||||||
|
///
|
||||||
|
/// When debugging these interactions, it can also be useful to configure your tracing subscriber
|
||||||
|
/// with `.with_span_events(tracing_subscriber::fmt::format::FmtSpan::ACTIVE)` to emit events upon
|
||||||
|
/// entering and exiting these stages.
|
||||||
|
pub fn tracing_observers(
|
||||||
|
mtm: MainThreadMarker,
|
||||||
|
) -> Option<(MainRunLoopObserver, MainRunLoopObserver)> {
|
||||||
|
// Observers are a bit costly, so don't create them if the tracing-level for this module is
|
||||||
|
// configured to disable them.
|
||||||
|
if !span_enabled!(Level::TRACE) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The state that we think the runloop is currently in.
|
||||||
|
///
|
||||||
|
/// The order of activities we observe if waiting twice looks something like:
|
||||||
|
/// - CFRunLoopActivity::Entry
|
||||||
|
/// - CFRunLoopActivity::BeforeTimers
|
||||||
|
/// - CFRunLoopActivity::BeforeSources
|
||||||
|
/// - CFRunLoopActivity::BeforeWaiting
|
||||||
|
/// - CFRunLoopActivity::AfterWaiting
|
||||||
|
/// - CFRunLoopActivity::BeforeTimers
|
||||||
|
/// - CFRunLoopActivity::BeforeSources
|
||||||
|
/// - CFRunLoopActivity::BeforeWaiting
|
||||||
|
/// - CFRunLoopActivity::AfterWaiting
|
||||||
|
/// - CFRunLoopActivity::Exit
|
||||||
|
///
|
||||||
|
/// And if not waiting, it looks something like:
|
||||||
|
/// - CFRunLoopActivity::Entry
|
||||||
|
/// - CFRunLoopActivity::BeforeTimers
|
||||||
|
/// - CFRunLoopActivity::BeforeSources
|
||||||
|
/// - CFRunLoopActivity::Exit
|
||||||
|
#[derive(Default)]
|
||||||
|
#[allow(unused)] // EnteredSpans are kept around
|
||||||
|
enum RunLoopState {
|
||||||
|
/// Currently processing `Entry`/`Exit` observers.
|
||||||
|
#[default]
|
||||||
|
Entered,
|
||||||
|
/// Currently processing timers or `BeforeTimers` observers.
|
||||||
|
Timers(EnteredSpan),
|
||||||
|
/// Currently processing sources or `BeforeSources` observers.
|
||||||
|
Sources(EnteredSpan),
|
||||||
|
/// Currently waiting or processing `BeforeWaiting`/`AfterWaiting` observers.
|
||||||
|
Waiting(EnteredSpan),
|
||||||
|
}
|
||||||
|
|
||||||
|
// A list of currently entered (outer) spans and their state.
|
||||||
|
//
|
||||||
|
// This is a list because runloops can be run recursively.
|
||||||
|
let spans: Rc<RefCell<Vec<(EnteredSpan, RunLoopState)>>> = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
let spans_clone = Rc::clone(&spans);
|
||||||
|
|
||||||
|
// An observer at the start of run loop activities.
|
||||||
|
let activities = CFRunLoopActivity::Entry
|
||||||
|
| CFRunLoopActivity::BeforeTimers
|
||||||
|
| CFRunLoopActivity::BeforeSources
|
||||||
|
| CFRunLoopActivity::BeforeWaiting;
|
||||||
|
let start = MainRunLoopObserver::new(mtm, activities, true, CFIndex::MIN, move |activity| {
|
||||||
|
match activity {
|
||||||
|
// Add an outer span for each runloop iteration.
|
||||||
|
CFRunLoopActivity::Entry => {
|
||||||
|
let span = trace_span!("inside runloop", mode = field::Empty);
|
||||||
|
|
||||||
|
// Get the mode dynamically, the observer may added to multiple different modes.
|
||||||
|
let mode = CFRunLoop::current().unwrap().current_mode().unwrap();
|
||||||
|
// Mode isn't interesting if it's the default mode.
|
||||||
|
if &*mode != unsafe { kCFRunLoopDefaultMode }.unwrap() {
|
||||||
|
span.record("mode", field::display(mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
let entered = span.entered();
|
||||||
|
spans.borrow_mut().push((entered, RunLoopState::Entered));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Add inner spans that help inspecting the state the runloop is in.
|
||||||
|
CFRunLoopActivity::BeforeTimers => {
|
||||||
|
if let Some((_, state)) = spans.borrow_mut().last_mut() {
|
||||||
|
*state = RunLoopState::Entered; // Drop any previous spans.
|
||||||
|
*state = RunLoopState::Timers(trace_span!("processing timers").entered());
|
||||||
|
} else {
|
||||||
|
error!("unbalanced observer invocations");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CFRunLoopActivity::BeforeSources => {
|
||||||
|
if let Some((_, state)) = spans.borrow_mut().last_mut() {
|
||||||
|
*state = RunLoopState::Entered; // Drop any previous spans.
|
||||||
|
*state = RunLoopState::Sources(trace_span!("processing sources").entered());
|
||||||
|
} else {
|
||||||
|
error!("unbalanced observer invocations");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CFRunLoopActivity::BeforeWaiting => {
|
||||||
|
if let Some((_, state)) = spans.borrow_mut().last_mut() {
|
||||||
|
*state = RunLoopState::Entered; // Drop any previous spans.
|
||||||
|
*state = RunLoopState::Waiting(trace_span!("waiting").entered());
|
||||||
|
} else {
|
||||||
|
error!("unbalanced observer invocations");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
activity => unreachable!("unexpected activity: {activity:?}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// An observer at the end of run loop activities.
|
||||||
|
let activities = CFRunLoopActivity::AfterWaiting | CFRunLoopActivity::Exit;
|
||||||
|
let end = MainRunLoopObserver::new(mtm, activities, true, CFIndex::MAX, move |activity| {
|
||||||
|
match activity {
|
||||||
|
CFRunLoopActivity::AfterWaiting => {
|
||||||
|
if let Some((_, state)) = spans_clone.borrow_mut().last_mut() {
|
||||||
|
// Transition from the waiting state to the initial state.
|
||||||
|
*state = RunLoopState::Entered;
|
||||||
|
} else {
|
||||||
|
error!("unbalanced observer invocations");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
CFRunLoopActivity::Exit => {
|
||||||
|
if let Some((span, state)) = spans_clone.borrow_mut().pop() {
|
||||||
|
drop(state); // Explicitly exit and drop inner span.
|
||||||
|
drop(span); // Explicitly exit and drop outer span.
|
||||||
|
} else {
|
||||||
|
error!("unbalanced observer invocations");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
activity => unreachable!("unexpected activity: {activity:?}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Some((start, end))
|
||||||
|
}
|
||||||
@@ -79,6 +79,10 @@ impl EventHandler {
|
|||||||
// Allowed, happens if the handler was cleared manually
|
// Allowed, happens if the handler was cleared manually
|
||||||
// elsewhere (such as in `applicationWillTerminate:`).
|
// elsewhere (such as in `applicationWillTerminate:`).
|
||||||
},
|
},
|
||||||
|
// We use `eprintln!` here over `tracing::error!`, since we're going to abort
|
||||||
|
// immediately after this, and it'd be annoying for the user if they didn't get
|
||||||
|
// any feedback on that if they don't have a tracing subscriber.
|
||||||
|
#[allow(clippy::disallowed_macros)]
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// Note: This is not expected to ever happen, this
|
// Note: This is not expected to ever happen, this
|
||||||
// module generally controls the `RefCell`, and
|
// module generally controls the `RefCell`, and
|
||||||
|
|||||||
3
winit-common/src/foundation/mod.rs
Normal file
3
winit-common/src/foundation/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
mod notification_center;
|
||||||
|
|
||||||
|
pub use self::notification_center::*;
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// NOTE: This is symlinked to be contained in both the AppKit and UIKit implementations.
|
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use block2::RcBlock;
|
use block2::RcBlock;
|
||||||
@@ -12,7 +11,7 @@ use objc2_foundation::{
|
|||||||
///
|
///
|
||||||
/// This is used in Winit as an alternative to declaring an application delegate, as we want to
|
/// This is used in Winit as an alternative to declaring an application delegate, as we want to
|
||||||
/// give the user full control over those.
|
/// give the user full control over those.
|
||||||
pub(crate) fn create_observer(
|
pub fn create_observer(
|
||||||
center: &NSNotificationCenter,
|
center: &NSNotificationCenter,
|
||||||
name: &NSNotificationName,
|
name: &NSNotificationName,
|
||||||
handler: impl Fn(&NSNotification) + 'static,
|
handler: impl Fn(&NSNotification) + 'static,
|
||||||
@@ -4,5 +4,7 @@
|
|||||||
pub mod core_foundation;
|
pub mod core_foundation;
|
||||||
#[cfg(feature = "event-handler")]
|
#[cfg(feature = "event-handler")]
|
||||||
pub mod event_handler;
|
pub mod event_handler;
|
||||||
|
#[cfg(feature = "foundation")]
|
||||||
|
pub mod foundation;
|
||||||
#[cfg(feature = "xkb")]
|
#[cfg(feature = "xkb")]
|
||||||
pub mod xkb;
|
pub mod xkb;
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
//! End user application handling.
|
//! End user application handling.
|
||||||
#[cfg(not(target_family = "wasm"))]
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
use web_time::Instant;
|
|
||||||
|
|
||||||
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
|
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
|
||||||
use crate::event_loop::ActiveEventLoop;
|
use crate::event_loop::ActiveEventLoop;
|
||||||
@@ -197,14 +192,10 @@ pub trait ApplicationHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Emitted when the OS sends an event to a winit window.
|
/// Emitted when the OS sends an event to a winit window.
|
||||||
///
|
|
||||||
/// Contains the ID of the window, the event, and the time the event was received. Note that
|
|
||||||
/// since events are queued, the time will differ from [`Instant::now()`].
|
|
||||||
fn window_event(
|
fn window_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &dyn ActiveEventLoop,
|
event_loop: &dyn ActiveEventLoop,
|
||||||
window_id: WindowId,
|
window_id: WindowId,
|
||||||
timestamp: Instant,
|
|
||||||
event: WindowEvent,
|
event: WindowEvent,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -215,10 +206,9 @@ pub trait ApplicationHandler {
|
|||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &dyn ActiveEventLoop,
|
event_loop: &dyn ActiveEventLoop,
|
||||||
device_id: Option<DeviceId>,
|
device_id: Option<DeviceId>,
|
||||||
timestamp: Instant,
|
|
||||||
event: DeviceEvent,
|
event: DeviceEvent,
|
||||||
) {
|
) {
|
||||||
let _ = (event_loop, device_id, timestamp, event);
|
let _ = (event_loop, device_id, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emitted when the event loop is about to block and wait for new events.
|
/// Emitted when the event loop is about to block and wait for new events.
|
||||||
@@ -382,10 +372,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
|||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &dyn ActiveEventLoop,
|
event_loop: &dyn ActiveEventLoop,
|
||||||
window_id: WindowId,
|
window_id: WindowId,
|
||||||
timestamp: Instant,
|
|
||||||
event: WindowEvent,
|
event: WindowEvent,
|
||||||
) {
|
) {
|
||||||
(**self).window_event(event_loop, window_id, timestamp, event);
|
(**self).window_event(event_loop, window_id, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -393,10 +382,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
|||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &dyn ActiveEventLoop,
|
event_loop: &dyn ActiveEventLoop,
|
||||||
device_id: Option<DeviceId>,
|
device_id: Option<DeviceId>,
|
||||||
timestamp: Instant,
|
|
||||||
event: DeviceEvent,
|
event: DeviceEvent,
|
||||||
) {
|
) {
|
||||||
(**self).device_event(event_loop, device_id, timestamp, event);
|
(**self).device_event(event_loop, device_id, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -452,10 +440,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &dyn ActiveEventLoop,
|
event_loop: &dyn ActiveEventLoop,
|
||||||
window_id: WindowId,
|
window_id: WindowId,
|
||||||
timestamp: Instant,
|
|
||||||
event: WindowEvent,
|
event: WindowEvent,
|
||||||
) {
|
) {
|
||||||
(**self).window_event(event_loop, window_id, timestamp, event);
|
(**self).window_event(event_loop, window_id, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -463,10 +450,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &dyn ActiveEventLoop,
|
event_loop: &dyn ActiveEventLoop,
|
||||||
device_id: Option<DeviceId>,
|
device_id: Option<DeviceId>,
|
||||||
timestamp: Instant,
|
|
||||||
event: DeviceEvent,
|
event: DeviceEvent,
|
||||||
) {
|
) {
|
||||||
(**self).device_event(event_loop, device_id, timestamp, event);
|
(**self).device_event(event_loop, device_id, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -269,6 +269,20 @@ pub enum WindowEvent {
|
|||||||
button: ButtonSource,
|
button: ButtonSource,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Multi-finger hold gesture on the touchpad or touchscreen without movement.
|
||||||
|
///
|
||||||
|
/// The `phase` field indicates the lifecycle of the hold gesture:
|
||||||
|
/// - `Started`: One or more fingers are in contact with the touchpad/touchscreen.
|
||||||
|
/// - `Ended`: All fingers have been lifted from the touchpad/touchscreen.
|
||||||
|
/// - `Cancelled`: The hold gesture was interrupted, for example when another finger touches the
|
||||||
|
/// touchpad (causing a new `Started` event with more fingers), or when movement begins and
|
||||||
|
/// transitions to other gestures like pinch, pan, or rotation.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - Only available on **Wayland**.
|
||||||
|
HoldGesture { device_id: Option<DeviceId>, phase: TouchPhase },
|
||||||
|
|
||||||
/// Two-finger pinch gesture, often used for magnification.
|
/// Two-finger pinch gesture, often used for magnification.
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
@@ -1006,9 +1020,17 @@ pub enum Ime {
|
|||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum TouchPhase {
|
pub enum TouchPhase {
|
||||||
|
/// Initial touch contact or gesture start, for example when one or more fingers touch the
|
||||||
|
/// screen or touchpad.
|
||||||
Started,
|
Started,
|
||||||
|
/// The touch contact point changed, for example without lifting the finger.
|
||||||
Moved,
|
Moved,
|
||||||
|
/// All touch contact points have been lifted from the touchscreen or touchpad.
|
||||||
|
///
|
||||||
|
/// This event is important as it should clear any state or event in flight that was
|
||||||
|
/// generated by the preceding `Started` and `Moved` events.
|
||||||
Ended,
|
Ended,
|
||||||
|
/// The event was cancelled and should cancel any event in flight and clear state.
|
||||||
Cancelled,
|
Cancelled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -880,7 +880,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug {
|
|||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **Android / iOS / X11 / Web / Windows:** Unsupported.
|
/// - **Android / iOS / X11 / Web / Windows:** Unsupported.
|
||||||
/// - **Wayland:** Only works with org_kde_kwin_blur_manager protocol.
|
/// - **Wayland:** Only works with `org_kde_kwin_blur_manager` or
|
||||||
|
/// `ext_background_effect_manager_v1` protocol.
|
||||||
fn set_blur(&self, blur: bool);
|
fn set_blur(&self, blur: bool);
|
||||||
|
|
||||||
/// Modifies the window's visibility.
|
/// Modifies the window's visibility.
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ objc2-ui-kit = { workspace = true, features = [
|
|||||||
"UIViewController",
|
"UIViewController",
|
||||||
"UIWindow",
|
"UIWindow",
|
||||||
] }
|
] }
|
||||||
winit-common = { workspace = true, features = ["core-foundation", "event-handler"] }
|
winit-common = { workspace = true, features = ["core-foundation", "event-handler", "foundation"] }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ use objc2_ui_kit::{
|
|||||||
UIApplicationWillResignActiveNotification, UIApplicationWillTerminateNotification, UIScreen,
|
UIApplicationWillResignActiveNotification, UIApplicationWillTerminateNotification, UIScreen,
|
||||||
};
|
};
|
||||||
use rwh_06::HasDisplayHandle;
|
use rwh_06::HasDisplayHandle;
|
||||||
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver};
|
use tracing::debug_span;
|
||||||
|
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver, tracing_observers};
|
||||||
|
use winit_common::foundation::create_observer;
|
||||||
use winit_core::application::ApplicationHandler;
|
use winit_core::application::ApplicationHandler;
|
||||||
use winit_core::cursor::{CustomCursor, CustomCursorSource};
|
use winit_core::cursor::{CustomCursor, CustomCursorSource};
|
||||||
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
|
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
|
||||||
@@ -24,7 +26,6 @@ use winit_core::monitor::MonitorHandle as CoreMonitorHandle;
|
|||||||
use winit_core::window::{Theme, Window as CoreWindow};
|
use winit_core::window::{Theme, Window as CoreWindow};
|
||||||
|
|
||||||
use super::app_state::{AppState, send_occluded_event_for_all_windows};
|
use super::app_state::{AppState, send_occluded_event_for_all_windows};
|
||||||
use super::notification_center::create_observer;
|
|
||||||
use crate::monitor::MonitorHandle;
|
use crate::monitor::MonitorHandle;
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
use crate::{app_state, monitor};
|
use crate::{app_state, monitor};
|
||||||
@@ -134,6 +135,7 @@ pub struct EventLoop {
|
|||||||
_will_terminate_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
|
_will_terminate_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
|
||||||
_did_receive_memory_warning_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
|
_did_receive_memory_warning_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
|
||||||
|
|
||||||
|
_tracing_observers: Option<(MainRunLoopObserver, MainRunLoopObserver)>,
|
||||||
_wakeup_observer: MainRunLoopObserver,
|
_wakeup_observer: MainRunLoopObserver,
|
||||||
_main_events_cleared_observer: MainRunLoopObserver,
|
_main_events_cleared_observer: MainRunLoopObserver,
|
||||||
_events_cleared_observer: MainRunLoopObserver,
|
_events_cleared_observer: MainRunLoopObserver,
|
||||||
@@ -159,6 +161,7 @@ impl EventLoop {
|
|||||||
// `application:didFinishLaunchingWithOptions:`
|
// `application:didFinishLaunchingWithOptions:`
|
||||||
unsafe { UIApplicationDidFinishLaunchingNotification },
|
unsafe { UIApplicationDidFinishLaunchingNotification },
|
||||||
move |_| {
|
move |_| {
|
||||||
|
let _entered = debug_span!("UIApplicationDidFinishLaunchingNotification").entered();
|
||||||
app_state::did_finish_launching(mtm);
|
app_state::did_finish_launching(mtm);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -166,19 +169,27 @@ impl EventLoop {
|
|||||||
¢er,
|
¢er,
|
||||||
// `applicationDidBecomeActive:`
|
// `applicationDidBecomeActive:`
|
||||||
unsafe { UIApplicationDidBecomeActiveNotification },
|
unsafe { UIApplicationDidBecomeActiveNotification },
|
||||||
move |_| app_state::handle_resumed(mtm),
|
move |_| {
|
||||||
|
let _entered = debug_span!("UIApplicationDidBecomeActiveNotification").entered();
|
||||||
|
app_state::handle_resumed(mtm)
|
||||||
|
},
|
||||||
);
|
);
|
||||||
let _will_resign_active_observer = create_observer(
|
let _will_resign_active_observer = create_observer(
|
||||||
¢er,
|
¢er,
|
||||||
// `applicationWillResignActive:`
|
// `applicationWillResignActive:`
|
||||||
unsafe { UIApplicationWillResignActiveNotification },
|
unsafe { UIApplicationWillResignActiveNotification },
|
||||||
move |_| app_state::handle_suspended(mtm),
|
move |_| {
|
||||||
|
let _entered = debug_span!("UIApplicationWillResignActiveNotification").entered();
|
||||||
|
app_state::handle_suspended(mtm)
|
||||||
|
},
|
||||||
);
|
);
|
||||||
let _will_enter_foreground_observer = create_observer(
|
let _will_enter_foreground_observer = create_observer(
|
||||||
¢er,
|
¢er,
|
||||||
// `applicationWillEnterForeground:`
|
// `applicationWillEnterForeground:`
|
||||||
unsafe { UIApplicationWillEnterForegroundNotification },
|
unsafe { UIApplicationWillEnterForegroundNotification },
|
||||||
move |notification| {
|
move |notification| {
|
||||||
|
let _entered =
|
||||||
|
debug_span!("UIApplicationWillEnterForegroundNotification").entered();
|
||||||
let app = notification.object().expect(
|
let app = notification.object().expect(
|
||||||
"UIApplicationWillEnterForegroundNotification to have application object",
|
"UIApplicationWillEnterForegroundNotification to have application object",
|
||||||
);
|
);
|
||||||
@@ -193,6 +204,7 @@ impl EventLoop {
|
|||||||
// `applicationDidEnterBackground:`
|
// `applicationDidEnterBackground:`
|
||||||
unsafe { UIApplicationDidEnterBackgroundNotification },
|
unsafe { UIApplicationDidEnterBackgroundNotification },
|
||||||
move |notification| {
|
move |notification| {
|
||||||
|
let _entered = debug_span!("UIApplicationDidEnterBackgroundNotification").entered();
|
||||||
let app = notification.object().expect(
|
let app = notification.object().expect(
|
||||||
"UIApplicationDidEnterBackgroundNotification to have application object",
|
"UIApplicationDidEnterBackgroundNotification to have application object",
|
||||||
);
|
);
|
||||||
@@ -207,6 +219,7 @@ impl EventLoop {
|
|||||||
// `applicationWillTerminate:`
|
// `applicationWillTerminate:`
|
||||||
unsafe { UIApplicationWillTerminateNotification },
|
unsafe { UIApplicationWillTerminateNotification },
|
||||||
move |notification| {
|
move |notification| {
|
||||||
|
let _entered = debug_span!("UIApplicationWillTerminateNotification").entered();
|
||||||
let app = notification
|
let app = notification
|
||||||
.object()
|
.object()
|
||||||
.expect("UIApplicationWillTerminateNotification to have application object");
|
.expect("UIApplicationWillTerminateNotification to have application object");
|
||||||
@@ -220,18 +233,29 @@ impl EventLoop {
|
|||||||
¢er,
|
¢er,
|
||||||
// `applicationDidReceiveMemoryWarning:`
|
// `applicationDidReceiveMemoryWarning:`
|
||||||
unsafe { UIApplicationDidReceiveMemoryWarningNotification },
|
unsafe { UIApplicationDidReceiveMemoryWarningNotification },
|
||||||
move |_| app_state::handle_memory_warning(mtm),
|
move |_| {
|
||||||
|
let _entered =
|
||||||
|
debug_span!("UIApplicationDidReceiveMemoryWarningNotification").entered();
|
||||||
|
app_state::handle_memory_warning(mtm)
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let main_loop = MainRunLoop::get(mtm);
|
let main_loop = MainRunLoop::get(mtm);
|
||||||
let mode = unsafe { kCFRunLoopDefaultMode }.unwrap();
|
let mode = unsafe { kCFRunLoopDefaultMode }.unwrap();
|
||||||
|
|
||||||
|
// Tracing observers have the lowest and highest orderings.
|
||||||
|
let _tracing_observers = tracing_observers(mtm).inspect(|(start, end)| {
|
||||||
|
main_loop.add_observer(start, mode);
|
||||||
|
main_loop.add_observer(end, mode);
|
||||||
|
});
|
||||||
|
|
||||||
let _wakeup_observer = MainRunLoopObserver::new(
|
let _wakeup_observer = MainRunLoopObserver::new(
|
||||||
mtm,
|
mtm,
|
||||||
CFRunLoopActivity::AfterWaiting,
|
CFRunLoopActivity::AfterWaiting,
|
||||||
true,
|
true,
|
||||||
// Queued with the highest priority to ensure it is processed before other observers.
|
// Queued with the second-highest priority (tracing observers use the highest) to
|
||||||
CFIndex::MIN,
|
// ensure it is processed before other observers.
|
||||||
|
CFIndex::MIN + 1,
|
||||||
move |_| app_state::handle_wakeup_transition(mtm),
|
move |_| app_state::handle_wakeup_transition(mtm),
|
||||||
);
|
);
|
||||||
main_loop.add_observer(&_wakeup_observer, mode);
|
main_loop.add_observer(&_wakeup_observer, mode);
|
||||||
@@ -241,17 +265,17 @@ impl EventLoop {
|
|||||||
CFRunLoopActivity::BeforeWaiting,
|
CFRunLoopActivity::BeforeWaiting,
|
||||||
true,
|
true,
|
||||||
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
||||||
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the
|
// `CA::Transaction::ensure_implicit` with a priority of `2000000`. We set the
|
||||||
// main_end priority to be 0, in order to send `AboutToWait` before `RedrawRequested`.
|
// main_end priority to be 0, in order to send `AboutToWait` before `RedrawRequested`.
|
||||||
// This value was chosen conservatively to guard against apple using different
|
// This value was chosen conservatively to guard against apple using different
|
||||||
// priorities for their redraw observers in different OS's or on different devices. If
|
// priorities for their redraw observers in different OS's or on different devices. If
|
||||||
// it so happens that it's too conservative, the main symptom would be non-redraw
|
// it so happens that it's too conservative, the main symptom would be non-redraw
|
||||||
// events coming in after `AboutToWait`.
|
// events coming in after `AboutToWait`.
|
||||||
//
|
//
|
||||||
// The value of `0x1e8480` was determined by inspecting stack traces and the associated
|
// The value of `2000000` was determined by inspecting stack traces and the associated
|
||||||
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
|
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
|
||||||
//
|
//
|
||||||
// Also tested to be `0x1e8480` on iPhone 8, iOS 13 beta 4.
|
// Also tested to be `2000000` on iPhone 8, iOS 13 beta 4.
|
||||||
0,
|
0,
|
||||||
move |_| app_state::handle_main_events_cleared(mtm),
|
move |_| app_state::handle_main_events_cleared(mtm),
|
||||||
);
|
);
|
||||||
@@ -261,8 +285,9 @@ impl EventLoop {
|
|||||||
mtm,
|
mtm,
|
||||||
CFRunLoopActivity::BeforeWaiting,
|
CFRunLoopActivity::BeforeWaiting,
|
||||||
true,
|
true,
|
||||||
// Queued with the lowest priority to ensure it is processed after other observers.
|
// Queued with the second-lowest priority (tracing observers use the lowest) to ensure
|
||||||
CFIndex::MAX,
|
// it is processed after other observers.
|
||||||
|
CFIndex::MAX - 1,
|
||||||
move |_| app_state::handle_events_cleared(mtm),
|
move |_| app_state::handle_events_cleared(mtm),
|
||||||
);
|
);
|
||||||
main_loop.add_observer(&_events_cleared_observer, mode);
|
main_loop.add_observer(&_events_cleared_observer, mode);
|
||||||
@@ -277,6 +302,7 @@ impl EventLoop {
|
|||||||
_did_enter_background_observer,
|
_did_enter_background_observer,
|
||||||
_will_terminate_observer,
|
_will_terminate_observer,
|
||||||
_did_receive_memory_warning_observer,
|
_did_receive_memory_warning_observer,
|
||||||
|
_tracing_observers,
|
||||||
_wakeup_observer,
|
_wakeup_observer,
|
||||||
_main_events_cleared_observer,
|
_main_events_cleared_observer,
|
||||||
_events_cleared_observer,
|
_events_cleared_observer,
|
||||||
|
|||||||
@@ -103,7 +103,6 @@
|
|||||||
mod app_state;
|
mod app_state;
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod monitor;
|
mod monitor;
|
||||||
mod notification_center;
|
|
||||||
mod view;
|
mod view;
|
||||||
mod view_controller;
|
mod view_controller;
|
||||||
mod window;
|
mod window;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
../../winit-appkit/src/notification_center.rs
|
|
||||||
@@ -13,7 +13,7 @@ use objc2_ui_kit::{
|
|||||||
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITextInputTraits, UITouch,
|
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITextInputTraits, UITouch,
|
||||||
UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
UITouchPhase, UITouchType, UITraitEnvironment, UIView,
|
||||||
};
|
};
|
||||||
use tracing::debug;
|
use tracing::{debug, debug_span, trace_span};
|
||||||
use winit_core::event::{
|
use winit_core::event::{
|
||||||
ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource,
|
ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource,
|
||||||
TabletToolAngle, TabletToolButton, TabletToolData, TabletToolKind, TouchPhase, WindowEvent,
|
TabletToolAngle, TabletToolButton, TabletToolData, TabletToolKind, TouchPhase, WindowEvent,
|
||||||
@@ -48,6 +48,7 @@ define_class!(
|
|||||||
impl WinitView {
|
impl WinitView {
|
||||||
#[unsafe(method(drawRect:))]
|
#[unsafe(method(drawRect:))]
|
||||||
fn draw_rect(&self, rect: CGRect) {
|
fn draw_rect(&self, rect: CGRect) {
|
||||||
|
let _entered = debug_span!("drawRect:").entered();
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
|
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
|
||||||
@@ -59,6 +60,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(layoutSubviews))]
|
#[unsafe(method(layoutSubviews))]
|
||||||
fn layout_subviews(&self) {
|
fn layout_subviews(&self) {
|
||||||
|
let _entered = debug_span!("layoutSubviews").entered();
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
|
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
|
||||||
|
|
||||||
@@ -79,6 +81,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(setContentScaleFactor:))]
|
#[unsafe(method(setContentScaleFactor:))]
|
||||||
fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) {
|
fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) {
|
||||||
|
let _entered = debug_span!("setContentScaleFactor:").entered();
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
let _: () =
|
let _: () =
|
||||||
unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] };
|
unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] };
|
||||||
@@ -124,6 +127,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(safeAreaInsetsDidChange))]
|
#[unsafe(method(safeAreaInsetsDidChange))]
|
||||||
fn safe_area_changed(&self) {
|
fn safe_area_changed(&self) {
|
||||||
|
let _entered = debug_span!("safeAreaInsetsDidChange").entered();
|
||||||
debug!("safeAreaInsetsDidChange was called, requesting redraw");
|
debug!("safeAreaInsetsDidChange was called, requesting redraw");
|
||||||
// When the safe area changes we want to make sure to emit a redraw event
|
// When the safe area changes we want to make sure to emit a redraw event
|
||||||
self.setNeedsDisplay();
|
self.setNeedsDisplay();
|
||||||
@@ -131,26 +135,31 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(touchesBegan:withEvent:))]
|
#[unsafe(method(touchesBegan:withEvent:))]
|
||||||
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||||
|
let _entered = debug_span!("touchesBegan:withEvent:").entered();
|
||||||
self.handle_touches(touches)
|
self.handle_touches(touches)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(touchesMoved:withEvent:))]
|
#[unsafe(method(touchesMoved:withEvent:))]
|
||||||
fn touches_moved(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
fn touches_moved(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||||
|
let _entered = debug_span!("touchesMoved:withEvent:").entered();
|
||||||
self.handle_touches(touches)
|
self.handle_touches(touches)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(touchesEnded:withEvent:))]
|
#[unsafe(method(touchesEnded:withEvent:))]
|
||||||
fn touches_ended(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
fn touches_ended(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||||
|
let _entered = debug_span!("touchesEnded:withEvent:").entered();
|
||||||
self.handle_touches(touches)
|
self.handle_touches(touches)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(touchesCancelled:withEvent:))]
|
#[unsafe(method(touchesCancelled:withEvent:))]
|
||||||
fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
|
||||||
|
let _entered = debug_span!("touchesCancelled:withEvent:").entered();
|
||||||
self.handle_touches(touches)
|
self.handle_touches(touches)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(pinchGesture:))]
|
#[unsafe(method(pinchGesture:))]
|
||||||
fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) {
|
fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) {
|
||||||
|
let _entered = debug_span!("pinchGesture:").entered();
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
|
|
||||||
let (phase, delta) = match recognizer.state() {
|
let (phase, delta) = match recognizer.state() {
|
||||||
@@ -185,6 +194,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(doubleTapGesture:))]
|
#[unsafe(method(doubleTapGesture:))]
|
||||||
fn double_tap_gesture(&self, recognizer: &UITapGestureRecognizer) {
|
fn double_tap_gesture(&self, recognizer: &UITapGestureRecognizer) {
|
||||||
|
let _entered = debug_span!("doubleTapGesture:").entered();
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
|
|
||||||
if recognizer.state() == UIGestureRecognizerState::Ended {
|
if recognizer.state() == UIGestureRecognizerState::Ended {
|
||||||
@@ -200,6 +210,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(rotationGesture:))]
|
#[unsafe(method(rotationGesture:))]
|
||||||
fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) {
|
fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) {
|
||||||
|
let _entered = debug_span!("rotationGesture:").entered();
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
|
|
||||||
let (phase, delta) = match recognizer.state() {
|
let (phase, delta) = match recognizer.state() {
|
||||||
@@ -244,6 +255,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(panGesture:))]
|
#[unsafe(method(panGesture:))]
|
||||||
fn pan_gesture(&self, recognizer: &UIPanGestureRecognizer) {
|
fn pan_gesture(&self, recognizer: &UIPanGestureRecognizer) {
|
||||||
|
let _entered = debug_span!("panGesture:").entered();
|
||||||
let window = self.window().unwrap();
|
let window = self.window().unwrap();
|
||||||
|
|
||||||
let translation = recognizer.translationInView(Some(self));
|
let translation = recognizer.translationInView(Some(self));
|
||||||
@@ -296,6 +308,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(canBecomeFirstResponder))]
|
#[unsafe(method(canBecomeFirstResponder))]
|
||||||
fn can_become_first_responder(&self) -> bool {
|
fn can_become_first_responder(&self) -> bool {
|
||||||
|
let _entered = trace_span!("canBecomeFirstResponder").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -309,6 +322,10 @@ define_class!(
|
|||||||
_gesture_recognizer: &UIGestureRecognizer,
|
_gesture_recognizer: &UIGestureRecognizer,
|
||||||
_other_gesture_recognizer: &UIGestureRecognizer,
|
_other_gesture_recognizer: &UIGestureRecognizer,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
let _entered = trace_span!(
|
||||||
|
"gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:"
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,16 +335,19 @@ define_class!(
|
|||||||
unsafe impl UIKeyInput for WinitView {
|
unsafe impl UIKeyInput for WinitView {
|
||||||
#[unsafe(method(hasText))]
|
#[unsafe(method(hasText))]
|
||||||
fn has_text(&self) -> bool {
|
fn has_text(&self) -> bool {
|
||||||
|
let _entered = debug_span!("hasText").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(insertText:))]
|
#[unsafe(method(insertText:))]
|
||||||
fn insert_text(&self, text: &NSString) {
|
fn insert_text(&self, text: &NSString) {
|
||||||
|
let _entered = debug_span!("insertText:").entered();
|
||||||
self.handle_insert_text(text)
|
self.handle_insert_text(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(deleteBackward))]
|
#[unsafe(method(deleteBackward))]
|
||||||
fn delete_backward(&self) {
|
fn delete_backward(&self) {
|
||||||
|
let _entered = debug_span!("deleteBackward").entered();
|
||||||
self.handle_delete_backward()
|
self.handle_delete_backward()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use objc2_ui_kit::{
|
|||||||
UIDevice, UIInterfaceOrientationMask, UIRectEdge, UIResponder, UIStatusBarStyle,
|
UIDevice, UIInterfaceOrientationMask, UIRectEdge, UIResponder, UIStatusBarStyle,
|
||||||
UIUserInterfaceIdiom, UIView, UIViewController,
|
UIUserInterfaceIdiom, UIView, UIViewController,
|
||||||
};
|
};
|
||||||
|
use tracing::trace_span;
|
||||||
|
|
||||||
use crate::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos};
|
use crate::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos};
|
||||||
|
|
||||||
@@ -28,31 +29,37 @@ define_class!(
|
|||||||
impl WinitViewController {
|
impl WinitViewController {
|
||||||
#[unsafe(method(shouldAutorotate))]
|
#[unsafe(method(shouldAutorotate))]
|
||||||
fn should_autorotate(&self) -> bool {
|
fn should_autorotate(&self) -> bool {
|
||||||
|
let _entered = trace_span!("shouldAutorotate").entered();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(prefersStatusBarHidden))]
|
#[unsafe(method(prefersStatusBarHidden))]
|
||||||
fn prefers_status_bar_hidden(&self) -> bool {
|
fn prefers_status_bar_hidden(&self) -> bool {
|
||||||
|
let _entered = trace_span!("prefersStatusBarHidden").entered();
|
||||||
self.ivars().prefers_status_bar_hidden.get()
|
self.ivars().prefers_status_bar_hidden.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(preferredStatusBarStyle))]
|
#[unsafe(method(preferredStatusBarStyle))]
|
||||||
fn preferred_status_bar_style(&self) -> UIStatusBarStyle {
|
fn preferred_status_bar_style(&self) -> UIStatusBarStyle {
|
||||||
|
let _entered = trace_span!("preferredStatusBarStyle").entered();
|
||||||
self.ivars().preferred_status_bar_style.get()
|
self.ivars().preferred_status_bar_style.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(prefersHomeIndicatorAutoHidden))]
|
#[unsafe(method(prefersHomeIndicatorAutoHidden))]
|
||||||
fn prefers_home_indicator_auto_hidden(&self) -> bool {
|
fn prefers_home_indicator_auto_hidden(&self) -> bool {
|
||||||
|
let _entered = trace_span!("prefersHomeIndicatorAutoHidden").entered();
|
||||||
self.ivars().prefers_home_indicator_auto_hidden.get()
|
self.ivars().prefers_home_indicator_auto_hidden.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(supportedInterfaceOrientations))]
|
#[unsafe(method(supportedInterfaceOrientations))]
|
||||||
fn supported_orientations(&self) -> UIInterfaceOrientationMask {
|
fn supported_orientations(&self) -> UIInterfaceOrientationMask {
|
||||||
|
let _entered = trace_span!("supportedInterfaceOrientations").entered();
|
||||||
self.ivars().supported_orientations.get()
|
self.ivars().supported_orientations.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(preferredScreenEdgesDeferringSystemGestures))]
|
#[unsafe(method(preferredScreenEdgesDeferringSystemGestures))]
|
||||||
fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
|
fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
|
||||||
|
let _entered = trace_span!("preferredScreenEdgesDeferringSystemGestures").entered();
|
||||||
self.ivars().preferred_screen_edges_deferring_system_gestures.get()
|
self.ivars().preferred_screen_edges_deferring_system_gestures.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use objc2_ui_kit::{
|
|||||||
UIApplication, UICoordinateSpace, UIEdgeInsets, UIResponder, UIScreen,
|
UIApplication, UICoordinateSpace, UIEdgeInsets, UIResponder, UIScreen,
|
||||||
UIScreenOverscanCompensation, UIViewController, UIWindow,
|
UIScreenOverscanCompensation, UIViewController, UIWindow,
|
||||||
};
|
};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, debug_span, warn};
|
||||||
use winit_core::cursor::Cursor;
|
use winit_core::cursor::Cursor;
|
||||||
use winit_core::error::{NotSupportedError, RequestError};
|
use winit_core::error::{NotSupportedError, RequestError};
|
||||||
use winit_core::event::WindowEvent;
|
use winit_core::event::WindowEvent;
|
||||||
@@ -46,6 +46,7 @@ define_class!(
|
|||||||
impl WinitUIWindow {
|
impl WinitUIWindow {
|
||||||
#[unsafe(method(becomeKeyWindow))]
|
#[unsafe(method(becomeKeyWindow))]
|
||||||
fn become_key_window(&self) {
|
fn become_key_window(&self) {
|
||||||
|
let _entered = debug_span!("becomeKeyWindow").entered();
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
|
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
|
||||||
window_id: self.id(),
|
window_id: self.id(),
|
||||||
@@ -56,6 +57,7 @@ define_class!(
|
|||||||
|
|
||||||
#[unsafe(method(resignKeyWindow))]
|
#[unsafe(method(resignKeyWindow))]
|
||||||
fn resign_key_window(&self) {
|
fn resign_key_window(&self) {
|
||||||
|
let _entered = debug_span!("resignKeyWindow").entered();
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
|
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
|
||||||
window_id: self.id(),
|
window_id: self.id(),
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ sctk = { package = "smithay-client-toolkit", version = "0.20.0", default-feature
|
|||||||
sctk-adwaita = { version = "0.11.0", default-features = false, optional = true }
|
sctk-adwaita = { version = "0.11.0", default-features = false, optional = true }
|
||||||
wayland-backend = { version = "0.3.10", default-features = false, features = ["client_system"] }
|
wayland-backend = { version = "0.3.10", default-features = false, features = ["client_system"] }
|
||||||
wayland-client = "0.31.10"
|
wayland-client = "0.31.10"
|
||||||
wayland-protocols = { version = "0.32.8", features = ["staging"] }
|
wayland-protocols = { version = "0.32.11", features = ["staging", "unstable"] }
|
||||||
wayland-protocols-plasma = { version = "0.3.8", features = ["client"] }
|
wayland-protocols-plasma = { version = "0.3.8", features = ["client"] }
|
||||||
winit-common = { workspace = true, features = ["xkb", "wayland"] }
|
winit-common = { workspace = true, features = ["xkb", "wayland"] }
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
|
|||||||
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
||||||
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
use wayland_protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_hold_v1::ZwpPointerGestureHoldV1;
|
||||||
use wayland_protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1;
|
use wayland_protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1;
|
||||||
use wayland_protocols::wp::tablet::zv2::client::zwp_tablet_seat_v2::ZwpTabletSeatV2;
|
use wayland_protocols::wp::tablet::zv2::client::zwp_tablet_seat_v2::ZwpTabletSeatV2;
|
||||||
use winit_core::event::WindowEvent;
|
use winit_core::event::WindowEvent;
|
||||||
@@ -60,6 +61,9 @@ pub struct WinitSeatState {
|
|||||||
/// The pinch pointer gesture bound on the seat.
|
/// The pinch pointer gesture bound on the seat.
|
||||||
pointer_gesture_pinch: Option<ZwpPointerGesturePinchV1>,
|
pointer_gesture_pinch: Option<ZwpPointerGesturePinchV1>,
|
||||||
|
|
||||||
|
/// The hold pointer gesture bound on the seat.
|
||||||
|
pointer_gesture_hold: Option<ZwpPointerGestureHoldV1>,
|
||||||
|
|
||||||
/// The keyboard bound on the seat.
|
/// The keyboard bound on the seat.
|
||||||
keyboard_state: Option<KeyboardState>,
|
keyboard_state: Option<KeyboardState>,
|
||||||
|
|
||||||
@@ -141,6 +145,14 @@ impl SeatHandler for WinitState {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
seat_state.pointer_gesture_hold = self.pointer_gestures.as_ref().map(|manager| {
|
||||||
|
manager.get_hold_gesture(
|
||||||
|
themed_pointer.pointer(),
|
||||||
|
queue_handle,
|
||||||
|
PointerGestureData::default(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let themed_pointer = Arc::new(themed_pointer);
|
let themed_pointer = Arc::new(themed_pointer);
|
||||||
|
|
||||||
// Register cursor surface.
|
// Register cursor surface.
|
||||||
@@ -209,6 +221,10 @@ impl SeatHandler for WinitState {
|
|||||||
pointer_gesture_pinch.destroy();
|
pointer_gesture_pinch.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(pointer_gesture_hold) = seat_state.pointer_gesture_hold.take() {
|
||||||
|
pointer_gesture_hold.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(pointer) = seat_state.pointer.take() {
|
if let Some(pointer) = seat_state.pointer.take() {
|
||||||
let pointer_data = pointer.pointer().winit_data();
|
let pointer_data = pointer.pointer().winit_data();
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ use sctk::globals::GlobalData;
|
|||||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||||
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, delegate_dispatch};
|
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, delegate_dispatch};
|
||||||
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::{
|
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::{
|
||||||
Event, ZwpPointerGesturePinchV1,
|
Event as PinchEvent, ZwpPointerGesturePinchV1,
|
||||||
};
|
};
|
||||||
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gestures_v1::ZwpPointerGesturesV1;
|
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gestures_v1::ZwpPointerGesturesV1;
|
||||||
|
use wayland_protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_hold_v1::{
|
||||||
|
Event as HoldEvent, ZwpPointerGestureHoldV1,
|
||||||
|
};
|
||||||
use winit_core::event::{TouchPhase, WindowEvent};
|
use winit_core::event::{TouchPhase, WindowEvent};
|
||||||
use winit_core::window::WindowId;
|
use winit_core::window::WindowId;
|
||||||
|
|
||||||
@@ -27,7 +30,7 @@ impl PointerGesturesState {
|
|||||||
globals: &GlobalList,
|
globals: &GlobalList,
|
||||||
queue_handle: &QueueHandle<WinitState>,
|
queue_handle: &QueueHandle<WinitState>,
|
||||||
) -> Result<Self, BindError> {
|
) -> Result<Self, BindError> {
|
||||||
let pointer_gestures = globals.bind(queue_handle, 1..=1, GlobalData)?;
|
let pointer_gestures = globals.bind(queue_handle, 3..=3, GlobalData)?;
|
||||||
Ok(Self { pointer_gestures })
|
Ok(Self { pointer_gestures })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,6 +73,49 @@ impl Dispatch<ZwpPointerGesturesV1, GlobalData, WinitState> for PointerGesturesS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ZwpPointerGestureHoldV1, PointerGestureData, WinitState> for PointerGesturesState {
|
||||||
|
fn event(
|
||||||
|
state: &mut WinitState,
|
||||||
|
_proxy: &ZwpPointerGestureHoldV1,
|
||||||
|
event: <ZwpPointerGestureHoldV1 as wayland_client::Proxy>::Event,
|
||||||
|
data: &PointerGestureData,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qhandle: &QueueHandle<WinitState>,
|
||||||
|
) {
|
||||||
|
let mut pointer_gesture_data = data.inner.lock().unwrap();
|
||||||
|
let (window_id, phase) = match event {
|
||||||
|
HoldEvent::Begin { surface, fingers, .. } => {
|
||||||
|
if fingers < 2 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let window_id = crate::make_wid(&surface);
|
||||||
|
pointer_gesture_data.window_id = Some(window_id);
|
||||||
|
|
||||||
|
(window_id, TouchPhase::Started)
|
||||||
|
},
|
||||||
|
HoldEvent::End { cancelled, .. } => {
|
||||||
|
let window_id = match pointer_gesture_data.window_id {
|
||||||
|
Some(window_id) => window_id,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reset the state.
|
||||||
|
*pointer_gesture_data = Default::default();
|
||||||
|
|
||||||
|
let phase = if cancelled == 0 { TouchPhase::Ended } else { TouchPhase::Cancelled };
|
||||||
|
|
||||||
|
(window_id, phase)
|
||||||
|
},
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
state
|
||||||
|
.events_sink
|
||||||
|
.push_window_event(WindowEvent::HoldGesture { device_id: None, phase }, window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for PointerGesturesState {
|
impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for PointerGesturesState {
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut WinitState,
|
state: &mut WinitState,
|
||||||
@@ -81,7 +127,7 @@ impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for Poin
|
|||||||
) {
|
) {
|
||||||
let mut pointer_gesture_data = data.inner.lock().unwrap();
|
let mut pointer_gesture_data = data.inner.lock().unwrap();
|
||||||
let (window_id, phase, pan_delta, pinch_delta, rotation_delta) = match event {
|
let (window_id, phase, pan_delta, pinch_delta, rotation_delta) = match event {
|
||||||
Event::Begin { surface, fingers, .. } => {
|
PinchEvent::Begin { surface, fingers, .. } => {
|
||||||
// We only support two fingers for now.
|
// We only support two fingers for now.
|
||||||
if fingers != 2 {
|
if fingers != 2 {
|
||||||
return;
|
return;
|
||||||
@@ -100,7 +146,7 @@ impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for Poin
|
|||||||
|
|
||||||
(window_id, TouchPhase::Started, PhysicalPosition::new(0., 0.), 0., 0.)
|
(window_id, TouchPhase::Started, PhysicalPosition::new(0., 0.), 0., 0.)
|
||||||
},
|
},
|
||||||
Event::Update { dx, dy, scale: pinch, rotation, .. } => {
|
PinchEvent::Update { dx, dy, scale: pinch, rotation, .. } => {
|
||||||
let window_id = match pointer_gesture_data.window_id {
|
let window_id = match pointer_gesture_data.window_id {
|
||||||
Some(window_id) => window_id,
|
Some(window_id) => window_id,
|
||||||
_ => return,
|
_ => return,
|
||||||
@@ -121,7 +167,7 @@ impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for Poin
|
|||||||
let rotation_delta = -rotation as f32;
|
let rotation_delta = -rotation as f32;
|
||||||
(window_id, TouchPhase::Moved, pan_delta, pinch_delta, rotation_delta)
|
(window_id, TouchPhase::Moved, pan_delta, pinch_delta, rotation_delta)
|
||||||
},
|
},
|
||||||
Event::End { cancelled, .. } => {
|
PinchEvent::End { cancelled, .. } => {
|
||||||
let window_id = match pointer_gesture_data.window_id {
|
let window_id = match pointer_gesture_data.window_id {
|
||||||
Some(window_id) => window_id,
|
Some(window_id) => window_id,
|
||||||
_ => return,
|
_ => return,
|
||||||
@@ -155,3 +201,4 @@ impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for Poin
|
|||||||
|
|
||||||
delegate_dispatch!(WinitState: [ZwpPointerGesturesV1: GlobalData] => PointerGesturesState);
|
delegate_dispatch!(WinitState: [ZwpPointerGesturesV1: GlobalData] => PointerGesturesState);
|
||||||
delegate_dispatch!(WinitState: [ZwpPointerGesturePinchV1: PointerGestureData] => PointerGesturesState);
|
delegate_dispatch!(WinitState: [ZwpPointerGesturePinchV1: PointerGestureData] => PointerGesturesState);
|
||||||
|
delegate_dispatch!(WinitState: [ZwpPointerGestureHoldV1: PointerGestureData] => PointerGesturesState);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use crate::seat::{
|
|||||||
PointerConstraintsState, PointerGesturesState, RelativePointerState, TextInputState,
|
PointerConstraintsState, PointerGesturesState, RelativePointerState, TextInputState,
|
||||||
WinitPointerData, WinitPointerDataExt, WinitSeatState,
|
WinitPointerData, WinitPointerDataExt, WinitSeatState,
|
||||||
};
|
};
|
||||||
use crate::types::kwin_blur::KWinBlurManager;
|
use crate::types::bgr_effects::BgrEffectManager;
|
||||||
use crate::types::wp_fractional_scaling::FractionalScalingManager;
|
use crate::types::wp_fractional_scaling::FractionalScalingManager;
|
||||||
use crate::types::wp_tablet_input_v2::TabletManager;
|
use crate::types::wp_tablet_input_v2::TabletManager;
|
||||||
use crate::types::wp_viewporter::ViewporterState;
|
use crate::types::wp_viewporter::ViewporterState;
|
||||||
@@ -116,8 +116,8 @@ pub struct WinitState {
|
|||||||
/// Fractional scaling manager.
|
/// Fractional scaling manager.
|
||||||
pub fractional_scaling_manager: Option<FractionalScalingManager>,
|
pub fractional_scaling_manager: Option<FractionalScalingManager>,
|
||||||
|
|
||||||
/// KWin blur manager.
|
/// Blur manager.
|
||||||
pub kwin_blur_manager: Option<KWinBlurManager>,
|
pub blur_manager: Option<BgrEffectManager>,
|
||||||
|
|
||||||
/// Loop handle to re-register event sources, such as keyboard repeat.
|
/// Loop handle to re-register event sources, such as keyboard repeat.
|
||||||
pub loop_handle: LoopHandle<'static, Self>,
|
pub loop_handle: LoopHandle<'static, Self>,
|
||||||
@@ -192,7 +192,7 @@ impl WinitState {
|
|||||||
window_events_sink: Default::default(),
|
window_events_sink: Default::default(),
|
||||||
viewporter_state,
|
viewporter_state,
|
||||||
fractional_scaling_manager,
|
fractional_scaling_manager,
|
||||||
kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(),
|
blur_manager: BgrEffectManager::new(globals, queue_handle).ok(),
|
||||||
|
|
||||||
seats,
|
seats,
|
||||||
text_input_state: TextInputState::new(globals, queue_handle).ok(),
|
text_input_state: TextInputState::new(globals, queue_handle).ok(),
|
||||||
|
|||||||
85
winit-wayland/src/types/bgr_effects.rs
Normal file
85
winit-wayland/src/types/bgr_effects.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use sctk::compositor::Region;
|
||||||
|
use sctk::reexports::client::QueueHandle;
|
||||||
|
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||||
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
|
use sctk::reexports::protocols::ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1;
|
||||||
|
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
||||||
|
|
||||||
|
use crate::state::WinitState;
|
||||||
|
use crate::types::ext_background_effect::ExtBackgroundEffectManager;
|
||||||
|
use crate::types::kwin_blur::KWinBlurManager;
|
||||||
|
|
||||||
|
/// Wrapper around various background effects for [`WlSurface`].
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum BgrEffectManager {
|
||||||
|
Ext(ExtBackgroundEffectManager),
|
||||||
|
KWin(KWinBlurManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BgrEffectManager {
|
||||||
|
pub fn new(
|
||||||
|
globals: &GlobalList,
|
||||||
|
queue_handle: &QueueHandle<WinitState>,
|
||||||
|
) -> Result<Self, BindError> {
|
||||||
|
ExtBackgroundEffectManager::new(globals, queue_handle)
|
||||||
|
.map(Self::Ext)
|
||||||
|
.or_else(|_| KWinBlurManager::new(globals, queue_handle).map(Self::KWin))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new blur effect for the surface.
|
||||||
|
pub fn new_blur_effect(
|
||||||
|
&mut self,
|
||||||
|
surface: &WlSurface,
|
||||||
|
queue_handle: &QueueHandle<WinitState>,
|
||||||
|
) -> SurfaceBlurEffect {
|
||||||
|
match self {
|
||||||
|
BgrEffectManager::Ext(mgr) => SurfaceBlurEffect::Ext(mgr.blur(surface, queue_handle)),
|
||||||
|
BgrEffectManager::KWin(mgr) => SurfaceBlurEffect::Kwin(
|
||||||
|
mgr.blur(surface, queue_handle),
|
||||||
|
mgr.clone(),
|
||||||
|
surface.clone(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SurfaceBlurEffect {
|
||||||
|
Ext(ExtBackgroundEffectSurfaceV1),
|
||||||
|
Kwin(OrgKdeKwinBlur, KWinBlurManager, WlSurface),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SurfaceBlurEffect {
|
||||||
|
/// Returns `true` if the main surface commit is required.
|
||||||
|
///
|
||||||
|
/// `None` clears the blur.
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_blur(&self, region: Option<&Region>) -> bool {
|
||||||
|
let region = region.map(|region| region.wl_region());
|
||||||
|
match self {
|
||||||
|
SurfaceBlurEffect::Ext(surface) => {
|
||||||
|
surface.set_blur_region(region);
|
||||||
|
true
|
||||||
|
},
|
||||||
|
SurfaceBlurEffect::Kwin(blur, ..) => {
|
||||||
|
blur.set_region(region);
|
||||||
|
blur.commit();
|
||||||
|
true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SurfaceBlurEffect {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match self {
|
||||||
|
SurfaceBlurEffect::Ext(surface) => surface.destroy(),
|
||||||
|
SurfaceBlurEffect::Kwin(blur, mgr, wl_surface) => {
|
||||||
|
blur.set_region(None);
|
||||||
|
blur.commit();
|
||||||
|
blur.release();
|
||||||
|
mgr.unset(wl_surface);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
winit-wayland/src/types/ext_background_effect.rs
Normal file
59
winit-wayland/src/types/ext_background_effect.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use sctk::globals::GlobalData;
|
||||||
|
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||||
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
|
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, delegate_dispatch};
|
||||||
|
use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_manager_v1::ExtBackgroundEffectManagerV1;
|
||||||
|
use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1;
|
||||||
|
|
||||||
|
use crate::state::WinitState;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ExtBackgroundEffectManager {
|
||||||
|
manager: ExtBackgroundEffectManagerV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtBackgroundEffectManager {
|
||||||
|
pub fn new(
|
||||||
|
globals: &GlobalList,
|
||||||
|
queue_handle: &QueueHandle<WinitState>,
|
||||||
|
) -> Result<Self, BindError> {
|
||||||
|
let manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
|
||||||
|
Ok(Self { manager })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blur(
|
||||||
|
&mut self,
|
||||||
|
surface: &WlSurface,
|
||||||
|
queue_handle: &QueueHandle<WinitState>,
|
||||||
|
) -> ExtBackgroundEffectSurfaceV1 {
|
||||||
|
self.manager.get_background_effect(surface, queue_handle, ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ExtBackgroundEffectManagerV1, GlobalData, WinitState> for ExtBackgroundEffectManager {
|
||||||
|
fn event(
|
||||||
|
_: &mut WinitState,
|
||||||
|
_: &ExtBackgroundEffectManagerV1,
|
||||||
|
_: <ExtBackgroundEffectManagerV1 as Proxy>::Event,
|
||||||
|
_: &GlobalData,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<WinitState>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ExtBackgroundEffectSurfaceV1, (), WinitState> for ExtBackgroundEffectManager {
|
||||||
|
fn event(
|
||||||
|
_: &mut WinitState,
|
||||||
|
_: &ExtBackgroundEffectSurfaceV1,
|
||||||
|
_: <ExtBackgroundEffectSurfaceV1 as Proxy>::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<WinitState>,
|
||||||
|
) {
|
||||||
|
// There is no event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate_dispatch!(WinitState: [ExtBackgroundEffectManagerV1: GlobalData] => ExtBackgroundEffectManager);
|
||||||
|
delegate_dispatch!(WinitState: [ExtBackgroundEffectSurfaceV1: ()] => ExtBackgroundEffectManager);
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
//! Wayland protocol implementation boilerplate.
|
//! Wayland protocol implementation boilerplate.
|
||||||
|
|
||||||
|
pub mod bgr_effects;
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
|
pub mod ext_background_effect;
|
||||||
pub mod kwin_blur;
|
pub mod kwin_blur;
|
||||||
pub mod wp_fractional_scaling;
|
pub mod wp_fractional_scaling;
|
||||||
pub mod wp_tablet_input_v2;
|
pub mod wp_tablet_input_v2;
|
||||||
|
|||||||
@@ -127,7 +127,8 @@ impl Window {
|
|||||||
// Set transparency hint.
|
// Set transparency hint.
|
||||||
window_state.set_transparent(attributes.transparent);
|
window_state.set_transparent(attributes.transparent);
|
||||||
|
|
||||||
window_state.set_blur(attributes.blur);
|
// Set blur.
|
||||||
|
let _ = window_state.set_blur(attributes.blur);
|
||||||
|
|
||||||
// Set the decorations hint.
|
// Set the decorations hint.
|
||||||
window_state.set_decorate(attributes.decorations);
|
window_state.set_decorate(attributes.decorations);
|
||||||
@@ -498,7 +499,9 @@ impl CoreWindow for Window {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_blur(&self, blur: bool) {
|
fn set_blur(&self, blur: bool) {
|
||||||
self.window_state.lock().unwrap().set_blur(blur);
|
if self.window_state.lock().unwrap().set_blur(blur) {
|
||||||
|
self.request_redraw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ use sctk::shm::slot::SlotPool;
|
|||||||
use sctk::subcompositor::SubcompositorState;
|
use sctk::subcompositor::SubcompositorState;
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
use wayland_protocols::xdg::toplevel_icon::v1::client::xdg_toplevel_icon_manager_v1::XdgToplevelIconManagerV1;
|
use wayland_protocols::xdg::toplevel_icon::v1::client::xdg_toplevel_icon_manager_v1::XdgToplevelIconManagerV1;
|
||||||
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
|
||||||
use winit_core::cursor::{CursorIcon, CustomCursor as CoreCustomCursor};
|
use winit_core::cursor::{CursorIcon, CustomCursor as CoreCustomCursor};
|
||||||
use winit_core::error::{NotSupportedError, RequestError};
|
use winit_core::error::{NotSupportedError, RequestError};
|
||||||
use winit_core::window::{
|
use winit_core::window::{
|
||||||
@@ -43,8 +42,8 @@ use crate::seat::{
|
|||||||
ZwpTextInputV3Ext,
|
ZwpTextInputV3Ext,
|
||||||
};
|
};
|
||||||
use crate::state::{WindowCompositorUpdate, WinitState};
|
use crate::state::{WindowCompositorUpdate, WinitState};
|
||||||
|
use crate::types::bgr_effects::{BgrEffectManager, SurfaceBlurEffect};
|
||||||
use crate::types::cursor::{CustomCursor, SelectedCursor, WaylandCustomCursor};
|
use crate::types::cursor::{CustomCursor, SelectedCursor, WaylandCustomCursor};
|
||||||
use crate::types::kwin_blur::KWinBlurManager;
|
|
||||||
use crate::types::xdg_toplevel_icon_manager::ToplevelIcon;
|
use crate::types::xdg_toplevel_icon_manager::ToplevelIcon;
|
||||||
|
|
||||||
#[cfg(feature = "sctk-adwaita")]
|
#[cfg(feature = "sctk-adwaita")]
|
||||||
@@ -156,8 +155,8 @@ pub struct WindowState {
|
|||||||
|
|
||||||
viewport: Option<WpViewport>,
|
viewport: Option<WpViewport>,
|
||||||
fractional_scale: Option<WpFractionalScaleV1>,
|
fractional_scale: Option<WpFractionalScaleV1>,
|
||||||
blur: Option<OrgKdeKwinBlur>,
|
blur: Option<SurfaceBlurEffect>,
|
||||||
blur_manager: Option<KWinBlurManager>,
|
blur_manager: Option<BgrEffectManager>,
|
||||||
|
|
||||||
/// Whether the client side decorations have pending move operations.
|
/// Whether the client side decorations have pending move operations.
|
||||||
///
|
///
|
||||||
@@ -206,7 +205,7 @@ impl WindowState {
|
|||||||
toplevel_icon: None,
|
toplevel_icon: None,
|
||||||
xdg_toplevel_icon_manager,
|
xdg_toplevel_icon_manager,
|
||||||
blur: None,
|
blur: None,
|
||||||
blur_manager: winit_state.kwin_blur_manager.clone(),
|
blur_manager: winit_state.blur_manager.clone(),
|
||||||
compositor,
|
compositor,
|
||||||
handle,
|
handle,
|
||||||
csd_fails: false,
|
csd_fails: false,
|
||||||
@@ -742,6 +741,13 @@ impl WindowState {
|
|||||||
// Set surface size without the borders.
|
// Set surface size without the borders.
|
||||||
viewport.set_destination(self.size.width as _, self.size.height as _);
|
viewport.set_destination(self.size.width as _, self.size.height as _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update blur region with new size.
|
||||||
|
if self.blur.is_some() {
|
||||||
|
// NOTE: either user resized or configure, in both cases
|
||||||
|
// the redraw scheduling is done on the caller side.
|
||||||
|
let _ = self.set_blur(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the scale factor of the window.
|
/// Get the scale factor of the window.
|
||||||
@@ -1113,20 +1119,37 @@ impl WindowState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make window background blurred
|
/// Make window background blurred.
|
||||||
#[inline]
|
///
|
||||||
pub fn set_blur(&mut self, blurred: bool) {
|
/// Returns `true` if redraw is required.
|
||||||
if blurred && self.blur.is_none() {
|
#[must_use]
|
||||||
if let Some(blur_manager) = self.blur_manager.as_ref() {
|
pub fn set_blur(&mut self, blurred: bool) -> bool {
|
||||||
let blur = blur_manager.blur(self.window.wl_surface(), &self.queue_handle);
|
if !blurred {
|
||||||
blur.commit();
|
self.blur = None;
|
||||||
self.blur = Some(blur);
|
return true;
|
||||||
} else {
|
}
|
||||||
info!("Blur manager unavailable, unable to change blur")
|
|
||||||
}
|
let mgr = match self.blur_manager.as_mut() {
|
||||||
} else if !blurred && self.blur.is_some() {
|
Some(mgr) => mgr,
|
||||||
self.blur_manager.as_ref().unwrap().unset(self.window.wl_surface());
|
None => {
|
||||||
self.blur.take().unwrap().release();
|
info!("Blur manager unavailable, unable to change blur");
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let blur = match self.blur.as_ref() {
|
||||||
|
Some(blur) => blur,
|
||||||
|
None => {
|
||||||
|
self.blur = Some(mgr.new_blur_effect(self.window.wl_surface(), &self.queue_handle));
|
||||||
|
self.blur.as_ref().unwrap()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(region) = Region::new(&*self.compositor) {
|
||||||
|
region.add(0, 0, i32::MAX, i32::MAX);
|
||||||
|
blur.set_blur(Some(®ion))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1224,10 +1247,6 @@ impl WindowState {
|
|||||||
|
|
||||||
impl Drop for WindowState {
|
impl Drop for WindowState {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(blur) = self.blur.take() {
|
|
||||||
blur.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(fs) = self.fractional_scale.take() {
|
if let Some(fs) = self.fractional_scale.take() {
|
||||||
fs.destroy();
|
fs.destroy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -526,7 +526,7 @@ impl MonitorHandler {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Notifying `Future`s is not dependant on the lifetime of the runner,
|
// Notifying `Future`s is not dependent on the lifetime of the runner,
|
||||||
// because they can outlive it.
|
// because they can outlive it.
|
||||||
if let Some(runner) = runner.upgrade() {
|
if let Some(runner) = runner.upgrade() {
|
||||||
if let Some(details) = details {
|
if let Some(details) = details {
|
||||||
@@ -761,7 +761,7 @@ impl MonitorPermissionFuture {
|
|||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
match future.await {
|
match future.await {
|
||||||
Ok(details) => {
|
Ok(details) => {
|
||||||
// Notifying `Future`s is not dependant on the lifetime of the runner, because
|
// Notifying `Future`s is not dependent on the lifetime of the runner, because
|
||||||
// they can outlive it.
|
// they can outlive it.
|
||||||
notifier.notify(Ok(()));
|
notifier.notify(Ok(()));
|
||||||
|
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
|||||||
SystemParametersInfoW, TranslateMessage, WHEEL_DELTA, WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE,
|
SystemParametersInfoW, TranslateMessage, WHEEL_DELTA, WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE,
|
||||||
WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO,
|
WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO,
|
||||||
WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION,
|
WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION,
|
||||||
WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
|
WM_INPUT, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP,
|
||||||
WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE,
|
WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL,
|
||||||
WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN,
|
WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT,
|
||||||
WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS,
|
WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR,
|
||||||
WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH,
|
WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP,
|
||||||
WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WMSZ_BOTTOM,
|
WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WMSZ_BOTTOM,
|
||||||
WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
|
WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
|
||||||
WMSZ_TOPRIGHT, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
|
WMSZ_TOPRIGHT, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
|
||||||
WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
||||||
@@ -1508,6 +1508,21 @@ unsafe fn public_window_callback_inner(
|
|||||||
result = ProcResult::Value(unsafe { DefWindowProcW(window, msg, wparam, lparam) });
|
result = ProcResult::Value(unsafe { DefWindowProcW(window, msg, wparam, lparam) });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
WM_INPUTLANGCHANGE => {
|
||||||
|
// Refresh the cached keyboard layout for the newly activated input
|
||||||
|
// language. This message is sent (by Windows or by layout switchers
|
||||||
|
// such as Punto Switcher) after the layout changes. Refreshing the
|
||||||
|
// cache here prevents a freeze that otherwise occurs when switching
|
||||||
|
// layout via such tools. We still defer to `DefWindowProc` so the
|
||||||
|
// message keeps propagating to first-level child windows, as the
|
||||||
|
// Win32 documentation requires.
|
||||||
|
{
|
||||||
|
let mut layouts = LAYOUT_CACHE.lock().unwrap();
|
||||||
|
layouts.get_current_layout();
|
||||||
|
}
|
||||||
|
result = ProcResult::DefWindowProc(wparam);
|
||||||
|
},
|
||||||
|
|
||||||
// this is necessary for us to maintain minimize/restore state
|
// this is necessary for us to maintain minimize/restore state
|
||||||
WM_SYSCOMMAND => {
|
WM_SYSCOMMAND => {
|
||||||
if wparam == SC_RESTORE as usize {
|
if wparam == SC_RESTORE as usize {
|
||||||
|
|||||||
@@ -387,11 +387,7 @@ impl LayoutCache {
|
|||||||
let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_id);
|
let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_id);
|
||||||
let key = match unicode {
|
let key = match unicode {
|
||||||
ToUnicodeResult::Str(str) => Key::Character(SmolStr::new(str)),
|
ToUnicodeResult::Str(str) => Key::Character(SmolStr::new(str)),
|
||||||
ToUnicodeResult::Dead(dead_char) => {
|
ToUnicodeResult::Dead(dead_char) => Key::Dead(dead_char),
|
||||||
// println!("{:?} - {:?} produced dead {:?}", key_code, mod_state,
|
|
||||||
// dead_char);
|
|
||||||
Key::Dead(dead_char)
|
|
||||||
},
|
|
||||||
ToUnicodeResult::None => {
|
ToUnicodeResult::None => {
|
||||||
let has_alt = mod_state.contains(WindowsModifiers::ALT);
|
let has_alt = mod_state.contains(WindowsModifiers::ALT);
|
||||||
let has_ctrl = mod_state.contains(WindowsModifiers::CONTROL);
|
let has_ctrl = mod_state.contains(WindowsModifiers::CONTROL);
|
||||||
|
|||||||
@@ -1757,7 +1757,7 @@ impl EventProcessor {
|
|||||||
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
||||||
.map(|prev_monitor| prev_monitor.scale_factor);
|
.map(|prev_monitor| prev_monitor.scale_factor);
|
||||||
if Some(new_monitor.scale_factor) != maybe_prev_scale_factor {
|
if Some(new_monitor.scale_factor) != maybe_prev_scale_factor {
|
||||||
for window in self.target.windows.borrow().iter().filter_map(|(_, w)| w.upgrade()) {
|
for window in self.target.windows.borrow().values().filter_map(|w| w.upgrade()) {
|
||||||
window.refresh_dpi_for_monitor(
|
window.refresh_dpi_for_monitor(
|
||||||
&new_monitor,
|
&new_monitor,
|
||||||
maybe_prev_scale_factor,
|
maybe_prev_scale_factor,
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ impl XConnection {
|
|||||||
// All util functions that abstract an async function will return a `Flusher`.
|
// All util functions that abstract an async function will return a `Flusher`.
|
||||||
pub fn flush_requests(&self) -> Result<(), XError> {
|
pub fn flush_requests(&self) -> Result<(), XError> {
|
||||||
unsafe { (self.xlib.XFlush)(self.display) };
|
unsafe { (self.xlib.XFlush)(self.display) };
|
||||||
// println!("XFlush");
|
|
||||||
// This isn't necessarily a useful time to check for errors (since our request hasn't
|
// This isn't necessarily a useful time to check for errors (since our request hasn't
|
||||||
// necessarily been processed yet)
|
// necessarily been processed yet)
|
||||||
self.check_errors()
|
self.check_errors()
|
||||||
@@ -74,7 +73,6 @@ impl XConnection {
|
|||||||
|
|
||||||
pub fn sync_with_server(&self) -> Result<(), XError> {
|
pub fn sync_with_server(&self) -> Result<(), XError> {
|
||||||
unsafe { (self.xlib.XSync)(self.display, ffi::False) };
|
unsafe { (self.xlib.XSync)(self.display, ffi::False) };
|
||||||
// println!("XSync");
|
|
||||||
self.check_errors()
|
self.check_errors()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,9 +76,13 @@ winit-core.workspace = true
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
image = { workspace = true, features = ["png"] }
|
image = { workspace = true, features = ["png"] }
|
||||||
softbuffer.workspace = true
|
|
||||||
tracing = { workspace = true, features = ["log"] }
|
tracing = { workspace = true, features = ["log"] }
|
||||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||||
|
# Launching a window without drawing to it has unpredictable results varying from platform to
|
||||||
|
# platform. We use the `softbuffer` crate in our examples because of its ease of use to avoid
|
||||||
|
# confusion around this. `glutin` or `wgpu` could also be used to fill the window buffer, but they
|
||||||
|
# are more complicated to set up.
|
||||||
|
softbuffer.workspace = true
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
winit-android.workspace = true
|
winit-android.workspace = true
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use cfg_aliases::cfg_aliases;
|
use cfg_aliases::cfg_aliases;
|
||||||
|
|
||||||
|
// Only relevant for examples and Winit, our usage of println! is fine here.
|
||||||
|
#[allow(clippy::disallowed_macros)]
|
||||||
fn main() {
|
fn main() {
|
||||||
// The script doesn't depend on our code.
|
// Dummy invocation to enable change-tracking in build scripts.
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
|
||||||
// Setup cfg aliases.
|
// Setup cfg aliases.
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ use std::time::Instant;
|
|||||||
use std::{fmt, mem};
|
use std::{fmt, mem};
|
||||||
|
|
||||||
use cursor_icon::CursorIcon;
|
use cursor_icon::CursorIcon;
|
||||||
use rwh_06::{DisplayHandle, HasDisplayHandle};
|
|
||||||
use softbuffer::{Context, Surface};
|
use softbuffer::{Context, Surface};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
#[cfg(web_platform)]
|
#[cfg(web_platform)]
|
||||||
@@ -25,7 +24,7 @@ use winit::cursor::{Cursor, CustomCursor, CustomCursorSource};
|
|||||||
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
|
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
|
||||||
use winit::error::RequestError;
|
use winit::error::RequestError;
|
||||||
use winit::event::{DeviceEvent, DeviceId, MouseButton, MouseScrollDelta, WindowEvent};
|
use winit::event::{DeviceEvent, DeviceId, MouseButton, MouseScrollDelta, WindowEvent};
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||||
use winit::icon::{Icon, RgbaIcon};
|
use winit::icon::{Icon, RgbaIcon};
|
||||||
use winit::keyboard::{Key, ModifiersState};
|
use winit::keyboard::{Key, ModifiersState};
|
||||||
use winit::monitor::Fullscreen;
|
use winit::monitor::Fullscreen;
|
||||||
@@ -45,9 +44,6 @@ use winit_core::application::macos::ApplicationHandlerExtMacOS;
|
|||||||
#[path = "util/tracing.rs"]
|
#[path = "util/tracing.rs"]
|
||||||
mod tracing_init;
|
mod tracing_init;
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
|
||||||
mod fill;
|
|
||||||
|
|
||||||
/// The amount of points to around the window for drag resize direction calculations.
|
/// The amount of points to around the window for drag resize direction calculations.
|
||||||
const BORDER_SIZE: f64 = 20.;
|
const BORDER_SIZE: f64 = 20.;
|
||||||
|
|
||||||
@@ -94,20 +90,12 @@ struct Application {
|
|||||||
/// Drawing context.
|
/// Drawing context.
|
||||||
///
|
///
|
||||||
/// With OpenGL it could be EGLDisplay.
|
/// With OpenGL it could be EGLDisplay.
|
||||||
context: Option<Context<DisplayHandle<'static>>>,
|
context: Context<OwnedDisplayHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
fn new(event_loop: &EventLoop, receiver: Receiver<Action>, sender: Sender<Action>) -> Self {
|
fn new(event_loop: &EventLoop, receiver: Receiver<Action>, sender: Sender<Action>) -> Self {
|
||||||
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
|
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||||
let context = Some(
|
|
||||||
Context::new(unsafe {
|
|
||||||
std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(
|
|
||||||
event_loop.display_handle().unwrap(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// You'll have to choose an icon size at your own discretion. On X11, the desired size
|
// You'll have to choose an icon size at your own discretion. On X11, the desired size
|
||||||
// varies by WM, and on Windows, you still have to account for screen scaling. Here
|
// varies by WM, and on Windows, you still have to account for screen scaling. Here
|
||||||
@@ -541,16 +529,7 @@ impl ApplicationHandler for Application {
|
|||||||
WindowEvent::DoubleTapGesture { .. } => {
|
WindowEvent::DoubleTapGesture { .. } => {
|
||||||
info!("Smart zoom");
|
info!("Smart zoom");
|
||||||
},
|
},
|
||||||
WindowEvent::TouchpadPressure { .. }
|
_ => (),
|
||||||
| WindowEvent::DragLeft { .. }
|
|
||||||
| WindowEvent::KeyboardInput { .. }
|
|
||||||
| WindowEvent::PointerEntered { .. }
|
|
||||||
| WindowEvent::DragEntered { .. }
|
|
||||||
| WindowEvent::DragMoved { .. }
|
|
||||||
| WindowEvent::DragDropped { .. }
|
|
||||||
| WindowEvent::Destroyed
|
|
||||||
| WindowEvent::Ime(_)
|
|
||||||
| WindowEvent::Moved(_) => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,9 +584,7 @@ impl ApplicationHandlerExtMacOS for Application {
|
|||||||
/// State of the window.
|
/// State of the window.
|
||||||
struct WindowState {
|
struct WindowState {
|
||||||
/// Render surface.
|
/// Render surface.
|
||||||
///
|
surface: Surface<OwnedDisplayHandle, Arc<dyn Window>>,
|
||||||
/// NOTE: This surface must be dropped before the `Window`.
|
|
||||||
surface: Surface<DisplayHandle<'static>, Arc<dyn Window>>,
|
|
||||||
/// The actual winit Window.
|
/// The actual winit Window.
|
||||||
window: Arc<dyn Window>,
|
window: Arc<dyn Window>,
|
||||||
/// The window theme we're drawing with.
|
/// The window theme we're drawing with.
|
||||||
@@ -648,9 +625,7 @@ impl WindowState {
|
|||||||
fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> {
|
fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> {
|
||||||
let window: Arc<dyn Window> = Arc::from(window);
|
let window: Arc<dyn Window> = Arc::from(window);
|
||||||
|
|
||||||
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
|
let surface = Surface::new(&app.context, Arc::clone(&window))?;
|
||||||
// it doesn't outlive it.
|
|
||||||
let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
|
|
||||||
|
|
||||||
let theme = window.theme().unwrap_or(Theme::Dark);
|
let theme = window.theme().unwrap_or(Theme::Dark);
|
||||||
info!("Theme: {theme:?}");
|
info!("Theme: {theme:?}");
|
||||||
@@ -937,35 +912,40 @@ impl WindowState {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.animated_fill_color {
|
|
||||||
fill::fill_window_with_animated_color(&*self.window, self.start_time);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buffer = self.surface.buffer_mut()?;
|
let mut buffer = self.surface.buffer_mut()?;
|
||||||
|
|
||||||
// Draw a different color inside the safe area
|
if self.animated_fill_color {
|
||||||
let surface_size = self.window.surface_size();
|
// Fill the entire buffer with a single color.
|
||||||
let insets = self.window.safe_area();
|
let time = self.start_time.elapsed().as_secs_f32() * 1.5;
|
||||||
for y in 0..surface_size.height {
|
let blue = (time.sin() * 255.0) as u32;
|
||||||
for x in 0..surface_size.width {
|
let green = ((time.cos() * 255.0) as u32) << 8;
|
||||||
let index = y as usize * surface_size.width as usize + x as usize;
|
let red = ((1.0 - time.sin() * 255.0) as u32) << 16;
|
||||||
if insets.left <= x
|
let color = red | green | blue;
|
||||||
&& x <= (surface_size.width - insets.right)
|
buffer.fill(color);
|
||||||
&& insets.top <= y
|
} else {
|
||||||
&& y <= (surface_size.height - insets.bottom)
|
// Draw a different color inside the safe area
|
||||||
{
|
let surface_size = self.window.surface_size();
|
||||||
// In safe area
|
let insets = self.window.safe_area();
|
||||||
buffer[index] = match self.theme {
|
for y in 0..surface_size.height {
|
||||||
Theme::Light => 0xffe8e8e8, // Light gray
|
for x in 0..surface_size.width {
|
||||||
Theme::Dark => 0xff525252, // Medium gray
|
let index = y as usize * surface_size.width as usize + x as usize;
|
||||||
};
|
if insets.left <= x
|
||||||
} else {
|
&& x <= (surface_size.width - insets.right)
|
||||||
// Outside safe area
|
&& insets.top <= y
|
||||||
buffer[index] = match self.theme {
|
&& y <= (surface_size.height - insets.bottom)
|
||||||
Theme::Light => 0xffffffff, // White
|
{
|
||||||
Theme::Dark => 0xff181818, // Dark gray
|
// In safe area
|
||||||
};
|
buffer[index] = match self.theme {
|
||||||
|
Theme::Light => 0xffe8e8e8, // Light gray
|
||||||
|
Theme::Dark => 0xff525252, // Medium gray
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Outside safe area
|
||||||
|
buffer[index] = match self.theme {
|
||||||
|
Theme::Light => 0xffffffff, // White
|
||||||
|
Theme::Dark => 0xff181818, // Dark gray
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
fn main() -> Result<(), impl std::error::Error> {
|
fn main() -> Result<(), impl std::error::Error> {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use softbuffer::{Context, Surface};
|
||||||
|
use tracing::info;
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::dpi::{LogicalPosition, LogicalSize, Position};
|
use winit::dpi::{LogicalPosition, LogicalSize, Position};
|
||||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||||
use winit::raw_window_handle::HasRawWindowHandle;
|
use winit::raw_window_handle::HasRawWindowHandle;
|
||||||
use winit::window::{Window, WindowAttributes, WindowId};
|
use winit::window::{Window, WindowAttributes, WindowId};
|
||||||
|
|
||||||
@@ -15,18 +17,20 @@ fn main() -> Result<(), impl std::error::Error> {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct WindowData {
|
struct WindowData {
|
||||||
window: Box<dyn Window>,
|
surface: Surface<OwnedDisplayHandle, Box<dyn Window>>,
|
||||||
color: u32,
|
color: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowData {
|
impl WindowData {
|
||||||
fn new(window: Box<dyn Window>, color: u32) -> Self {
|
fn new(context: &Context<OwnedDisplayHandle>, window: Box<dyn Window>, color: u32) -> Self {
|
||||||
Self { window, color }
|
let surface = Surface::new(context, window).unwrap();
|
||||||
|
Self { surface, color }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Debug)]
|
||||||
struct Application {
|
struct Application {
|
||||||
|
context: Context<OwnedDisplayHandle>,
|
||||||
parent_window_id: Option<WindowId>,
|
parent_window_id: Option<WindowId>,
|
||||||
windows: HashMap<WindowId, WindowData>,
|
windows: HashMap<WindowId, WindowData>,
|
||||||
}
|
}
|
||||||
@@ -38,10 +42,10 @@ fn main() -> Result<(), impl std::error::Error> {
|
|||||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||||
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
|
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||||
let window = event_loop.create_window(attributes).unwrap();
|
let window = event_loop.create_window(attributes).unwrap();
|
||||||
println!("Parent window id: {:?})", window.id());
|
info!("Parent window id: {:?})", window.id());
|
||||||
self.parent_window_id = Some(window.id());
|
self.parent_window_id = Some(window.id());
|
||||||
|
|
||||||
self.windows.insert(window.id(), WindowData::new(window, 0xffbbbbbb));
|
self.windows.insert(window.id(), WindowData::new(&self.context, window, 0xffbbbbbb));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(
|
fn window_event(
|
||||||
@@ -56,12 +60,12 @@ fn main() -> Result<(), impl std::error::Error> {
|
|||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
},
|
},
|
||||||
WindowEvent::PointerEntered { device_id: _, .. } => {
|
WindowEvent::PointerEntered { device_id: _, .. } => {
|
||||||
// On x11, println when the cursor entered in a window even if the child window
|
// On x11, log when the cursor entered in a window even if the child window
|
||||||
// is created by some key inputs.
|
// is created by some key inputs.
|
||||||
// the child windows are always placed at (0, 0) with size (200, 200) in the
|
// the child windows are always placed at (0, 0) with size (200, 200) in the
|
||||||
// parent window, so we also can see this log when we move
|
// parent window, so we also can see this log when we move
|
||||||
// the cursor around (200, 200) in parent window.
|
// the cursor around (200, 200) in parent window.
|
||||||
println!("cursor entered in the window {window_id:?}");
|
info!("cursor entered in the window {window_id:?}");
|
||||||
},
|
},
|
||||||
WindowEvent::KeyboardInput {
|
WindowEvent::KeyboardInput {
|
||||||
event: KeyEvent { state: ElementState::Pressed, .. },
|
event: KeyEvent { state: ElementState::Pressed, .. },
|
||||||
@@ -72,18 +76,24 @@ fn main() -> Result<(), impl std::error::Error> {
|
|||||||
0xff000000 + 3_u32.pow((child_index + 2).rem_euclid(16) as u32);
|
0xff000000 + 3_u32.pow((child_index + 2).rem_euclid(16) as u32);
|
||||||
|
|
||||||
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap();
|
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap();
|
||||||
let child_window =
|
let child_window = spawn_child_window(
|
||||||
spawn_child_window(parent_window.window.as_ref(), event_loop, child_index);
|
parent_window.surface.window().as_ref(),
|
||||||
|
event_loop,
|
||||||
|
child_index,
|
||||||
|
);
|
||||||
let child_id = child_window.id();
|
let child_id = child_window.id();
|
||||||
println!("Child window created with id: {child_id:?}");
|
info!("Child window created with id: {child_id:?}");
|
||||||
self.windows.insert(child_id, WindowData::new(child_window, child_color));
|
self.windows.insert(
|
||||||
|
child_id,
|
||||||
|
WindowData::new(&self.context, child_window, child_color),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
if let Some(window) = self.windows.get(&window_id) {
|
if let Some(window) = self.windows.get_mut(&window_id) {
|
||||||
if window_id == self.parent_window_id.unwrap() {
|
if window_id == self.parent_window_id.unwrap() {
|
||||||
fill::fill_window(window.window.as_ref());
|
fill::fill(&mut window.surface);
|
||||||
} else {
|
} else {
|
||||||
fill::fill_window_with_color(window.window.as_ref(), window.color);
|
fill::fill_with_color(&mut window.surface, window.color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -117,7 +127,8 @@ fn main() -> Result<(), impl std::error::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let event_loop = EventLoop::new().unwrap();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
event_loop.run_app(Application::default())
|
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||||
|
event_loop.run_app(Application { context, parent_window_id: None, windows: HashMap::new() })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))]
|
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))]
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ use std::thread;
|
|||||||
#[cfg(not(web_platform))]
|
#[cfg(not(web_platform))]
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
use ::tracing::{info, warn};
|
use softbuffer::{Context, Surface};
|
||||||
|
use tracing::{info, warn};
|
||||||
#[cfg(web_platform)]
|
#[cfg(web_platform)]
|
||||||
use web_time as time;
|
use web_time as time;
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
|
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
|
||||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop, OwnedDisplayHandle};
|
||||||
use winit::keyboard::{Key, NamedKey};
|
use winit::keyboard::{Key, NamedKey};
|
||||||
use winit::window::{Window, WindowAttributes, WindowId};
|
use winit::window::{Window, WindowAttributes, WindowId};
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ struct ControlFlowDemo {
|
|||||||
request_redraw: bool,
|
request_redraw: bool,
|
||||||
wait_cancelled: bool,
|
wait_cancelled: bool,
|
||||||
close_requested: bool,
|
close_requested: bool,
|
||||||
window: Option<Box<dyn Window>>,
|
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationHandler for ControlFlowDemo {
|
impl ApplicationHandler for ControlFlowDemo {
|
||||||
@@ -69,7 +70,10 @@ impl ApplicationHandler for ControlFlowDemo {
|
|||||||
let window_attributes = WindowAttributes::default().with_title(
|
let window_attributes = WindowAttributes::default().with_title(
|
||||||
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
|
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
|
||||||
);
|
);
|
||||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
let window = event_loop.create_window(window_attributes).unwrap();
|
||||||
|
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||||
|
let surface = Surface::new(&context, window).unwrap();
|
||||||
|
self.surface = Some(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(
|
fn window_event(
|
||||||
@@ -112,9 +116,9 @@ impl ApplicationHandler for ControlFlowDemo {
|
|||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
let window = self.window.as_ref().unwrap();
|
let surface = self.surface.as_mut().unwrap();
|
||||||
window.pre_present_notify();
|
surface.window().pre_present_notify();
|
||||||
fill::fill_window(window.as_ref());
|
fill::fill(surface);
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@@ -122,7 +126,7 @@ impl ApplicationHandler for ControlFlowDemo {
|
|||||||
|
|
||||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||||
if self.request_redraw && !self.wait_cancelled && !self.close_requested {
|
if self.request_redraw && !self.wait_cancelled && !self.close_requested {
|
||||||
self.window.as_ref().unwrap().request_redraw();
|
self.surface.as_ref().unwrap().window().request_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.mode {
|
match self.mode {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
use softbuffer::{Context, Surface};
|
||||||
|
use tracing::info;
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||||
use winit::window::{Window, WindowAttributes, WindowId};
|
use winit::window::{Window, WindowAttributes, WindowId};
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
@@ -15,27 +17,24 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
let event_loop = EventLoop::new()?;
|
let event_loop = EventLoop::new()?;
|
||||||
|
|
||||||
let app = Application::new();
|
let app = Application::default();
|
||||||
Ok(event_loop.run_app(app)?)
|
Ok(event_loop.run_app(app)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Application state and event handling.
|
/// Application state and event handling.
|
||||||
#[derive(Debug)]
|
#[derive(Default, Debug)]
|
||||||
struct Application {
|
struct Application {
|
||||||
window: Option<Box<dyn Window>>,
|
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||||
}
|
|
||||||
|
|
||||||
impl Application {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self { window: None }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationHandler for Application {
|
impl ApplicationHandler for Application {
|
||||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||||
let window_attributes =
|
let window_attributes =
|
||||||
WindowAttributes::default().with_title("Drag and drop files on me!");
|
WindowAttributes::default().with_title("Drag and drop files on me!");
|
||||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
let window = event_loop.create_window(window_attributes).unwrap();
|
||||||
|
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||||
|
let surface = Surface::new(&context, window).unwrap();
|
||||||
|
self.surface = Some(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(
|
fn window_event(
|
||||||
@@ -49,12 +48,12 @@ impl ApplicationHandler for Application {
|
|||||||
| WindowEvent::DragEntered { .. }
|
| WindowEvent::DragEntered { .. }
|
||||||
| WindowEvent::DragMoved { .. }
|
| WindowEvent::DragMoved { .. }
|
||||||
| WindowEvent::DragDropped { .. } => {
|
| WindowEvent::DragDropped { .. } => {
|
||||||
println!("{event:?}");
|
info!("{event:?}");
|
||||||
},
|
},
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
let window = self.window.as_ref().unwrap();
|
let surface = self.surface.as_mut().unwrap();
|
||||||
window.pre_present_notify();
|
surface.window().pre_present_notify();
|
||||||
fill::fill_window(window.as_ref());
|
fill::fill(surface);
|
||||||
},
|
},
|
||||||
WindowEvent::CloseRequested => {
|
WindowEvent::CloseRequested => {
|
||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ use std::cmp;
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use dpi::{LogicalPosition, PhysicalSize};
|
use dpi::{LogicalPosition, PhysicalSize};
|
||||||
|
use softbuffer::{Context, Surface};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::{Ime, WindowEvent};
|
use winit::event::{Ime, WindowEvent};
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||||
use winit::keyboard::{Key, ModifiersState, NamedKey};
|
use winit::keyboard::{Key, ModifiersState, NamedKey};
|
||||||
#[cfg(web_platform)]
|
#[cfg(web_platform)]
|
||||||
use winit::platform::web::WindowAttributesWeb;
|
use winit::platform::web::WindowAttributesWeb;
|
||||||
@@ -30,7 +31,7 @@ const IME_CURSOR_SIZE: PhysicalSize<u32> = PhysicalSize::new(20, 20);
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct App {
|
struct App {
|
||||||
window: Option<Box<dyn Window>>,
|
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||||
input_state: TextInputState,
|
input_state: TextInputState,
|
||||||
modifiers: ModifiersState,
|
modifiers: ModifiersState,
|
||||||
}
|
}
|
||||||
@@ -73,14 +74,12 @@ impl ApplicationHandler for App {
|
|||||||
#[cfg(web_platform)]
|
#[cfg(web_platform)]
|
||||||
let window_attributes = WindowAttributes::default()
|
let window_attributes = WindowAttributes::default()
|
||||||
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
|
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
|
||||||
self.window = match event_loop.create_window(window_attributes) {
|
let window = event_loop.create_window(window_attributes).expect("failed creating window");
|
||||||
Ok(window) => Some(window),
|
|
||||||
Err(err) => {
|
let context =
|
||||||
eprintln!("error creating window: {err}");
|
Context::new(event_loop.owned_display_handle()).expect("failed creating context");
|
||||||
event_loop.exit();
|
let surface = Surface::new(&context, window).expect("failed creating surface");
|
||||||
return;
|
self.surface = Some(surface);
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allow IME out of the box.
|
// Allow IME out of the box.
|
||||||
let enable_request = ImeEnableRequest::new(
|
let enable_request = ImeEnableRequest::new(
|
||||||
@@ -101,11 +100,13 @@ impl ApplicationHandler for App {
|
|||||||
match event {
|
match event {
|
||||||
WindowEvent::CloseRequested => {
|
WindowEvent::CloseRequested => {
|
||||||
info!("Close was requested; stopping");
|
info!("Close was requested; stopping");
|
||||||
self.window = None;
|
self.surface = None;
|
||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
},
|
},
|
||||||
WindowEvent::SurfaceResized(_) => {
|
WindowEvent::SurfaceResized(surface_size) => {
|
||||||
self.window.as_ref().expect("resize event without a window").request_redraw();
|
let surface = self.surface.as_mut().expect("resize event without a surface");
|
||||||
|
fill::resize(surface, surface_size);
|
||||||
|
surface.window().request_redraw();
|
||||||
},
|
},
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
// Redraw the application.
|
// Redraw the application.
|
||||||
@@ -114,13 +115,13 @@ impl ApplicationHandler for App {
|
|||||||
// this event rather than in AboutToWait, since rendering in here allows
|
// this event rather than in AboutToWait, since rendering in here allows
|
||||||
// the program to gracefully handle redraws requested by the OS.
|
// the program to gracefully handle redraws requested by the OS.
|
||||||
|
|
||||||
let window = self.window.as_ref().expect("redraw request without a window");
|
let surface = self.surface.as_mut().expect("redraw event without a surface");
|
||||||
|
|
||||||
// Notify that you're about to draw.
|
// Notify that you're about to draw.
|
||||||
window.pre_present_notify();
|
surface.window().pre_present_notify();
|
||||||
|
|
||||||
// Draw.
|
// Draw.
|
||||||
fill::fill_window(window.as_ref());
|
fill::fill(surface);
|
||||||
|
|
||||||
// For contiguous redraw loop you can request a redraw from here.
|
// For contiguous redraw loop you can request a redraw from here.
|
||||||
// window.request_redraw();
|
// window.request_redraw();
|
||||||
@@ -214,14 +215,14 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ime_event(&mut self, event: Ime) {
|
fn handle_ime_event(&mut self, event: Ime) {
|
||||||
let window = self.window.as_ref().expect("IME request without a window");
|
let surface = self.surface.as_ref().expect("IME request without a window");
|
||||||
match event {
|
match event {
|
||||||
Ime::Enabled => info!("IME enabled for Window={:?}", window.id()),
|
Ime::Enabled => info!("IME enabled for Window={:?}", surface.window().id()),
|
||||||
Ime::Preedit(text, caret_pos) => info!("Preedit: {text}, with caret at {caret_pos:?}"),
|
Ime::Preedit(text, caret_pos) => info!("Preedit: {text}, with caret at {caret_pos:?}"),
|
||||||
Ime::Commit(text) => {
|
Ime::Commit(text) => {
|
||||||
self.input_state.append_text(&text);
|
self.input_state.append_text(&text);
|
||||||
let request_data = self.get_ime_update();
|
let request_data = self.get_ime_update();
|
||||||
window.request_ime_update(ImeRequest::Update(request_data)).unwrap();
|
surface.window().request_ime_update(ImeRequest::Update(request_data)).unwrap();
|
||||||
self.print_input_state();
|
self.print_input_state();
|
||||||
},
|
},
|
||||||
Ime::DeleteSurrounding { before_bytes, after_bytes } => {
|
Ime::DeleteSurrounding { before_bytes, after_bytes } => {
|
||||||
@@ -246,7 +247,7 @@ impl App {
|
|||||||
error!("Buggy IME tried to delete with indices not on char boundary.");
|
error!("Buggy IME tried to delete with indices not on char boundary.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Ime::Disabled => info!("IME disabled for Window={:?}", window.id()),
|
Ime::Disabled => info!("IME disabled for Window={:?}", surface.window().id()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +313,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn window(&self) -> &dyn Window {
|
fn window(&self) -> &dyn Window {
|
||||||
self.window.as_ref().unwrap().as_ref()
|
self.surface.as_ref().unwrap().window().as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,7 +347,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
let event_loop = EventLoop::new()?;
|
let event_loop = EventLoop::new()?;
|
||||||
|
|
||||||
println!(
|
info!(
|
||||||
r#"This showcases the use of an input method engine (IME) by emulating a text edit field.
|
r#"This showcases the use of an input method engine (IME) by emulating a text edit field.
|
||||||
Use CTRL+i to toggle IME support.
|
Use CTRL+i to toggle IME support.
|
||||||
Use CTRL+p to cycle content purpose values.
|
Use CTRL+p to cycle content purpose values.
|
||||||
@@ -355,7 +356,7 @@ Use CTRL+h to cycle content hint permutations.
|
|||||||
);
|
);
|
||||||
|
|
||||||
let app = App {
|
let app = App {
|
||||||
window: None,
|
surface: None,
|
||||||
input_state: TextInputState {
|
input_state: TextInputState {
|
||||||
ime_enabled: true,
|
ime_enabled: true,
|
||||||
contents: String::new(),
|
contents: String::new(),
|
||||||
|
|||||||
@@ -7,24 +7,31 @@ fn main() -> std::process::ExitCode {
|
|||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use softbuffer::{Context, Surface};
|
||||||
|
use tracing::info;
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
use winit::event_loop::pump_events::{EventLoopExtPumpEvents, PumpStatus};
|
use winit::event_loop::pump_events::{EventLoopExtPumpEvents, PumpStatus};
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||||
use winit::window::{Window, WindowAttributes, WindowId};
|
use winit::window::{Window, WindowAttributes, WindowId};
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
#[path = "util/tracing.rs"]
|
||||||
|
mod tracing;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
struct PumpDemo {
|
struct PumpDemo {
|
||||||
window: Option<Box<dyn Window>>,
|
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationHandler for PumpDemo {
|
impl ApplicationHandler for PumpDemo {
|
||||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||||
let window_attributes = WindowAttributes::default().with_title("A fantastic window!");
|
let window_attributes = WindowAttributes::default().with_title("A fantastic window!");
|
||||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
let window = event_loop.create_window(window_attributes).unwrap();
|
||||||
|
|
||||||
|
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||||
|
self.surface = Some(Surface::new(&context, window).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(
|
fn window_event(
|
||||||
@@ -33,27 +40,27 @@ fn main() -> std::process::ExitCode {
|
|||||||
_window_id: WindowId,
|
_window_id: WindowId,
|
||||||
event: WindowEvent,
|
event: WindowEvent,
|
||||||
) {
|
) {
|
||||||
println!("{event:?}");
|
info!("{event:?}");
|
||||||
|
|
||||||
let window = match self.window.as_ref() {
|
let surface = match self.surface.as_mut() {
|
||||||
Some(window) => window,
|
Some(surface) => surface,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CloseRequested => event_loop.exit(),
|
WindowEvent::CloseRequested => event_loop.exit(),
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
fill::fill_window(window.as_ref());
|
fill::fill(surface);
|
||||||
window.request_redraw();
|
surface.window().request_redraw();
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut event_loop = EventLoop::new().unwrap();
|
tracing::init();
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
let mut event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let mut app = PumpDemo::default();
|
let mut app = PumpDemo::default();
|
||||||
|
|
||||||
@@ -69,12 +76,12 @@ fn main() -> std::process::ExitCode {
|
|||||||
//
|
//
|
||||||
// Since `pump_events` doesn't block it will be important to
|
// Since `pump_events` doesn't block it will be important to
|
||||||
// throttle the loop in the app somehow.
|
// throttle the loop in the app somehow.
|
||||||
println!("Update()");
|
info!("Update()");
|
||||||
sleep(Duration::from_millis(16));
|
sleep(Duration::from_millis(16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(ios_platform, web_platform, orbital_platform))]
|
#[cfg(any(ios_platform, web_platform, orbital_platform))]
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("This platform doesn't support pump_events.");
|
panic!("This platform doesn't support pump_events.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,36 +5,43 @@
|
|||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use softbuffer::{Context, Surface};
|
||||||
|
use tracing::info;
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand;
|
use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand;
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||||
use winit::window::{Window, WindowAttributes, WindowId};
|
use winit::window::{Window, WindowAttributes, WindowId};
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
#[path = "util/tracing.rs"]
|
||||||
|
mod tracing;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Debug)]
|
||||||
struct App {
|
struct App {
|
||||||
|
context: Context<OwnedDisplayHandle>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
|
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||||
window_id: Option<WindowId>,
|
window_id: Option<WindowId>,
|
||||||
window: Option<Box<dyn Window>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationHandler for App {
|
impl ApplicationHandler for App {
|
||||||
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||||
if let Some(window) = self.window.as_ref() {
|
if let Some(surface) = self.surface.as_ref() {
|
||||||
window.request_redraw();
|
surface.window().request_redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||||
let window_attributes = WindowAttributes::default()
|
let window_attributes = WindowAttributes::default()
|
||||||
.with_title("Fantastic window number one!")
|
.with_title(format!("Fantastic window number {}!", self.idx))
|
||||||
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
|
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
|
||||||
let window = event_loop.create_window(window_attributes).unwrap();
|
let window = event_loop.create_window(window_attributes).unwrap();
|
||||||
self.window_id = Some(window.id());
|
self.window_id = Some(window.id());
|
||||||
self.window = Some(window);
|
|
||||||
|
let surface = Surface::new(&self.context, window).unwrap();
|
||||||
|
self.surface = Some(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(
|
fn window_event(
|
||||||
@@ -44,52 +51,44 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
event: WindowEvent,
|
event: WindowEvent,
|
||||||
) {
|
) {
|
||||||
if event == WindowEvent::Destroyed && self.window_id == Some(window_id) {
|
if event == WindowEvent::Destroyed && self.window_id == Some(window_id) {
|
||||||
println!(
|
info!("Window {} Destroyed", self.idx);
|
||||||
"--------------------------------------------------------- Window {} Destroyed",
|
|
||||||
self.idx
|
|
||||||
);
|
|
||||||
self.window_id = None;
|
self.window_id = None;
|
||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let window = match self.window.as_mut() {
|
let Some(surface) = self.surface.as_mut() else {
|
||||||
Some(window) => window,
|
return;
|
||||||
None => return,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CloseRequested => {
|
WindowEvent::CloseRequested => {
|
||||||
println!(
|
info!("Window {} CloseRequested", self.idx);
|
||||||
"--------------------------------------------------------- Window {} \
|
self.surface = None;
|
||||||
CloseRequested",
|
|
||||||
self.idx
|
|
||||||
);
|
|
||||||
fill::cleanup_window(window.as_ref());
|
|
||||||
self.window = None;
|
|
||||||
},
|
},
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
fill::fill_window(window.as_ref());
|
fill::fill(surface);
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
tracing::init();
|
||||||
|
|
||||||
let mut event_loop = EventLoop::new().unwrap();
|
let mut event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
let mut app = App { idx: 1, ..Default::default() };
|
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||||
|
let mut app = App { context, idx: 1, surface: None, window_id: None };
|
||||||
event_loop.run_app_on_demand(&mut app)?;
|
event_loop.run_app_on_demand(&mut app)?;
|
||||||
|
|
||||||
println!("--------------------------------------------------------- Finished first loop");
|
info!("Finished first loop");
|
||||||
println!("--------------------------------------------------------- Waiting 5 seconds");
|
info!("Waiting 5 seconds");
|
||||||
std::thread::sleep(Duration::from_secs(5));
|
std::thread::sleep(Duration::from_secs(5));
|
||||||
|
|
||||||
app.idx += 1;
|
app.idx += 1;
|
||||||
event_loop.run_app_on_demand(&mut app)?;
|
event_loop.run_app_on_demand(&mut app)?;
|
||||||
println!("--------------------------------------------------------- Finished second loop");
|
info!("Finished second loop");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,5 +100,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
orbital_platform
|
orbital_platform
|
||||||
)))]
|
)))]
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("This example is not supported on this platform");
|
panic!("This example is not supported on this platform")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,115 +1,43 @@
|
|||||||
//! Fill the window buffer with a solid color.
|
|
||||||
//!
|
|
||||||
//! Launching a window without drawing to it has unpredictable results varying from platform to
|
|
||||||
//! platform. In order to have well-defined examples, this module provides an easy way to
|
|
||||||
//! fill the window buffer with a solid color.
|
|
||||||
//!
|
|
||||||
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
|
|
||||||
//! also be used to fill the window buffer, but they are more complicated to use.
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::mem;
|
|
||||||
use std::mem::ManuallyDrop;
|
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
#[cfg(not(web_platform))]
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use softbuffer::{Context, Surface};
|
use rwh_06::{HasDisplayHandle, HasWindowHandle};
|
||||||
#[cfg(web_platform)]
|
use softbuffer::Surface;
|
||||||
use web_time::Instant;
|
use winit::window::Window;
|
||||||
use winit::window::{Window, WindowId};
|
|
||||||
|
|
||||||
thread_local! {
|
/// Resize the surface.
|
||||||
// NOTE: You should never do things like that, create context and drop it before
|
pub fn resize(
|
||||||
// you drop the event loop. We do this for brevity to not blow up examples. We use
|
surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle>,
|
||||||
// ManuallyDrop to prevent destructors from running.
|
surface_size: dpi::PhysicalSize<u32>,
|
||||||
|
) {
|
||||||
|
// Handle zero-sized buffers.
|
||||||
//
|
//
|
||||||
// A static, thread-local map of graphics contexts to open windows.
|
// FIXME(madsmtm): This should be done by softbuffer internally in the future:
|
||||||
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) };
|
// https://github.com/rust-windowing/softbuffer/issues/238
|
||||||
|
let (Some(width), Some(height)) =
|
||||||
|
(NonZeroU32::new(surface_size.width), NonZeroU32::new(surface_size.height))
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
surface.resize(width, height).expect("Failed to resize the softbuffer surface");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The graphics context used to draw to a window.
|
/// Fill the window buffer with a solid color.
|
||||||
struct GraphicsContext {
|
pub fn fill_with_color(
|
||||||
/// The global softbuffer context.
|
surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle + AsRef<dyn Window>>,
|
||||||
context: RefCell<Context<&'static dyn Window>>,
|
color: u32,
|
||||||
|
) {
|
||||||
|
let surface_size = surface.window().as_ref().surface_size();
|
||||||
|
resize(surface, surface_size);
|
||||||
|
|
||||||
/// The hash map of window IDs to surfaces.
|
let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer");
|
||||||
surfaces: HashMap<WindowId, Surface<&'static dyn Window, &'static dyn Window>>,
|
buffer.fill(color);
|
||||||
}
|
buffer.present().expect("Failed to present the softbuffer buffer");
|
||||||
|
|
||||||
impl GraphicsContext {
|
|
||||||
fn new(w: &dyn Window) -> Self {
|
|
||||||
Self {
|
|
||||||
context: RefCell::new(
|
|
||||||
Context::new(unsafe { mem::transmute::<&'_ dyn Window, &'static dyn Window>(w) })
|
|
||||||
.expect("Failed to create a softbuffer context"),
|
|
||||||
),
|
|
||||||
surfaces: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_surface(
|
|
||||||
&mut self,
|
|
||||||
window: &dyn Window,
|
|
||||||
) -> &mut Surface<&'static dyn Window, &'static dyn Window> {
|
|
||||||
self.surfaces.entry(window.id()).or_insert_with(|| {
|
|
||||||
Surface::new(&self.context.borrow(), unsafe {
|
|
||||||
mem::transmute::<&'_ dyn Window, &'static dyn Window>(window)
|
|
||||||
})
|
|
||||||
.expect("Failed to create a softbuffer surface")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn destroy_surface(&mut self, window: &dyn Window) {
|
|
||||||
self.surfaces.remove(&window.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fill_window_with_color(window: &dyn Window, color: u32) {
|
|
||||||
GC.with(|gc| {
|
|
||||||
let size = window.surface_size();
|
|
||||||
let (Some(width), Some(height)) =
|
|
||||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Either get the last context used or create a new one.
|
|
||||||
let mut gc = gc.borrow_mut();
|
|
||||||
let surface = gc.get_or_insert_with(|| GraphicsContext::new(window)).create_surface(window);
|
|
||||||
|
|
||||||
// Fill a buffer with a solid color
|
|
||||||
|
|
||||||
surface.resize(width, height).expect("Failed to resize the softbuffer surface");
|
|
||||||
|
|
||||||
let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer");
|
|
||||||
buffer.fill(color);
|
|
||||||
buffer.present().expect("Failed to present the softbuffer buffer");
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn fill_window(window: &dyn Window) {
|
pub fn fill(
|
||||||
fill_window_with_color(window, 0xff181818);
|
surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle + AsRef<dyn Window>>,
|
||||||
}
|
) {
|
||||||
|
fill_with_color(surface, 0xff181818);
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn fill_window_with_animated_color(window: &dyn Window, start: Instant) {
|
|
||||||
let time = start.elapsed().as_secs_f32() * 1.5;
|
|
||||||
let blue = (time.sin() * 255.0) as u32;
|
|
||||||
let green = ((time.cos() * 255.0) as u32) << 8;
|
|
||||||
let red = ((1.0 - time.sin() * 255.0) as u32) << 16;
|
|
||||||
let color = red | green | blue;
|
|
||||||
fill_window_with_color(window, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn cleanup_window(window: &dyn Window) {
|
|
||||||
GC.with(|gc| {
|
|
||||||
let mut gc = gc.borrow_mut();
|
|
||||||
if let Some(context) = gc.as_mut() {
|
|
||||||
context.destroy_surface(window);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,3 +23,6 @@ pub fn init() {
|
|||||||
)
|
)
|
||||||
.init();
|
.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use ::tracing::*;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
//! Simple winit window example.
|
//! Simple winit window example.
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
|
use softbuffer::{Context, Surface};
|
||||||
|
use tracing::info;
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||||
#[cfg(web_platform)]
|
#[cfg(web_platform)]
|
||||||
use winit::platform::web::WindowAttributesWeb;
|
use winit::platform::web::WindowAttributesWeb;
|
||||||
use winit::window::{Window, WindowAttributes, WindowId};
|
use winit::window::{Window, WindowAttributes, WindowId};
|
||||||
@@ -17,7 +18,7 @@ mod tracing;
|
|||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
struct App {
|
struct App {
|
||||||
window: Option<Box<dyn Window>>,
|
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationHandler for App {
|
impl ApplicationHandler for App {
|
||||||
@@ -27,31 +28,25 @@ impl ApplicationHandler for App {
|
|||||||
#[cfg(web_platform)]
|
#[cfg(web_platform)]
|
||||||
let window_attributes = WindowAttributes::default()
|
let window_attributes = WindowAttributes::default()
|
||||||
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
|
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
|
||||||
self.window = match event_loop.create_window(window_attributes) {
|
let window = event_loop.create_window(window_attributes).expect("failed creating window");
|
||||||
Ok(window) => Some(window),
|
|
||||||
Err(err) => {
|
let context =
|
||||||
eprintln!("error creating window: {err}");
|
Context::new(event_loop.owned_display_handle()).expect("failed creating context");
|
||||||
event_loop.exit();
|
let surface = Surface::new(&context, window).expect("failed creating surface");
|
||||||
return;
|
self.surface = Some(surface);
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(
|
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) {
|
||||||
&mut self,
|
info!("{event:?}");
|
||||||
event_loop: &dyn ActiveEventLoop,
|
|
||||||
_: WindowId,
|
|
||||||
timestamp: Instant,
|
|
||||||
event: WindowEvent,
|
|
||||||
) {
|
|
||||||
::tracing::info!("{:?}: {event:?}", timestamp.elapsed());
|
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CloseRequested => {
|
WindowEvent::CloseRequested => {
|
||||||
println!("Close was requested; stopping");
|
info!("Close was requested; stopping");
|
||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
},
|
},
|
||||||
WindowEvent::SurfaceResized(_) => {
|
WindowEvent::SurfaceResized(surface_size) => {
|
||||||
self.window.as_ref().expect("resize event without a window").request_redraw();
|
let surface = self.surface.as_mut().expect("resize event without a surface");
|
||||||
|
fill::resize(surface, surface_size);
|
||||||
|
surface.window().request_redraw();
|
||||||
},
|
},
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
// Redraw the application.
|
// Redraw the application.
|
||||||
@@ -60,30 +55,22 @@ impl ApplicationHandler for App {
|
|||||||
// this event rather than in AboutToWait, since rendering in here allows
|
// this event rather than in AboutToWait, since rendering in here allows
|
||||||
// the program to gracefully handle redraws requested by the OS.
|
// the program to gracefully handle redraws requested by the OS.
|
||||||
|
|
||||||
let window = self.window.as_ref().expect("redraw request without a window");
|
let surface = self.surface.as_mut().expect("redraw event without a surface");
|
||||||
|
|
||||||
// Notify that you're about to draw.
|
// Notify that you're about to draw.
|
||||||
window.pre_present_notify();
|
surface.window().pre_present_notify();
|
||||||
|
|
||||||
// Draw.
|
// Draw.
|
||||||
fill::fill_window(window.as_ref());
|
let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer");
|
||||||
|
buffer.fill(0xff181818);
|
||||||
|
buffer.present().expect("Failed to present the softbuffer buffer");
|
||||||
|
|
||||||
// For contiguous redraw loop you can request a redraw from here.
|
// For contiguous redraw loop you can request a redraw from here.
|
||||||
window.request_redraw();
|
// window.request_redraw();
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_event(
|
|
||||||
&mut self,
|
|
||||||
_event_loop: &dyn ActiveEventLoop,
|
|
||||||
_device_id: Option<winit::event::DeviceId>,
|
|
||||||
timestamp: Instant,
|
|
||||||
event: winit::event::DeviceEvent,
|
|
||||||
) {
|
|
||||||
::tracing::info!("{:?}: {event:?}", timestamp.elapsed());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|||||||
@@ -3,19 +3,22 @@ use std::error::Error;
|
|||||||
|
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
use softbuffer::{Context, Surface};
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||||
use winit::platform::x11::WindowAttributesX11;
|
use winit::platform::x11::WindowAttributesX11;
|
||||||
use winit::window::{Window, WindowAttributes, WindowId};
|
use winit::window::{Window, WindowAttributes, WindowId};
|
||||||
|
|
||||||
#[path = "util/fill.rs"]
|
#[path = "util/fill.rs"]
|
||||||
mod fill;
|
mod fill;
|
||||||
|
#[path = "util/tracing.rs"]
|
||||||
|
mod tracing;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct XEmbedDemo {
|
pub struct XEmbedDemo {
|
||||||
parent_window_id: u32,
|
parent_window_id: u32,
|
||||||
window: Option<Box<dyn Window>>,
|
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationHandler for XEmbedDemo {
|
impl ApplicationHandler for XEmbedDemo {
|
||||||
@@ -27,7 +30,10 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
WindowAttributesX11::default().with_embed_parent_window(self.parent_window_id);
|
WindowAttributesX11::default().with_embed_parent_window(self.parent_window_id);
|
||||||
window_attributes = window_attributes.with_platform_attributes(Box::new(x11_attrs));
|
window_attributes = window_attributes.with_platform_attributes(Box::new(x11_attrs));
|
||||||
|
|
||||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
let window = event_loop.create_window(window_attributes).unwrap();
|
||||||
|
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||||
|
let surface = Surface::new(&context, window).unwrap();
|
||||||
|
self.surface = Some(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(
|
fn window_event(
|
||||||
@@ -36,19 +42,19 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
_window_id: WindowId,
|
_window_id: WindowId,
|
||||||
event: WindowEvent,
|
event: WindowEvent,
|
||||||
) {
|
) {
|
||||||
let window = self.window.as_ref().unwrap();
|
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CloseRequested => event_loop.exit(),
|
WindowEvent::CloseRequested => event_loop.exit(),
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
window.pre_present_notify();
|
let surface = self.surface.as_mut().unwrap();
|
||||||
fill::fill_window(window.as_ref());
|
surface.window().pre_present_notify();
|
||||||
|
fill::fill(surface);
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||||
self.window.as_ref().unwrap().request_redraw();
|
self.surface.as_ref().unwrap().window().request_redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,14 +64,13 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
|
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
|
||||||
.parse::<u32>()?;
|
.parse::<u32>()?;
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
tracing::init();
|
||||||
let event_loop = EventLoop::new()?;
|
let event_loop = EventLoop::new()?;
|
||||||
|
|
||||||
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?)
|
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, surface: None })?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(x11_platform))]
|
#[cfg(not(x11_platform))]
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
println!("This example is only supported on X11 platforms.");
|
panic!("This example is only supported on X11 platforms.")
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ changelog entry.
|
|||||||
- On iOS, add Apple Pencil support with force, altitude, and azimuth data.
|
- On iOS, add Apple Pencil support with force, altitude, and azimuth data.
|
||||||
- On Redox, add support for missing keyboard scancodes.
|
- On Redox, add support for missing keyboard scancodes.
|
||||||
- Implement `Send` and `Sync` for `OwnedDisplayHandle`.
|
- Implement `Send` and `Sync` for `OwnedDisplayHandle`.
|
||||||
|
- Use new macOS 15 cursors for resize icons.
|
||||||
|
- On Android, added scancode conversions for more obscure key codes.
|
||||||
|
- On Wayland, added `HoldGesture` event for multi-finger hold gestures
|
||||||
|
- On Wayland, added ext-background-effect-v1 support.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@@ -54,7 +58,12 @@ changelog entry.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- On Windows, fix a freeze that occurs when the keyboard layout is switched by
|
||||||
|
tools such as Punto Switcher. The `WM_INPUTLANGCHANGE` message is now handled
|
||||||
|
to refresh the cached keyboard layout, while still deferring to
|
||||||
|
`DefWindowProc` for normal propagation.
|
||||||
- On Redox, handle `EINTR` when reading from `event_socket` instead of panicking.
|
- On Redox, handle `EINTR` when reading from `event_socket` instead of panicking.
|
||||||
- On Wayland, switch from using the `ahash` hashing algorithm to `foldhash`.
|
- On Wayland, switch from using the `ahash` hashing algorithm to `foldhash`.
|
||||||
- On macOS, fix borderless game presentation options not sticking after switching spaces.
|
- On macOS, fix borderless game presentation options not sticking after switching spaces.
|
||||||
- On macOS, fix IME being locked on (regardless of requests to disable) after being enabled once.
|
- On macOS, fix IME being locked on (regardless of requests to disable) after being enabled once.
|
||||||
|
- On macOS, fix a panic and incorrect cursor position in Ime::Preedit when the preedit string contains special characters (ie. emojis) caused by incorrect UTF-16 to UTF-8 offset conversion.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use crate::platform_impl;
|
|||||||
///
|
///
|
||||||
/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs.
|
/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs.
|
||||||
///
|
///
|
||||||
/// Note that this cannot be shared across threads (due to platform-dependant logic
|
/// Note that this cannot be shared across threads (due to platform-dependent logic
|
||||||
/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access,
|
/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access,
|
||||||
/// the [`Window`] created from this _can_ be sent to an other thread, and the
|
/// the [`Window`] created from this _can_ be sent to an other thread, and the
|
||||||
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
|
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
|
||||||
@@ -88,7 +88,7 @@ impl EventLoopBuilder {
|
|||||||
)]
|
)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
|
pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
|
||||||
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
|
let _entered = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
|
||||||
|
|
||||||
// Certain platforms accept a mutable reference in their API.
|
// Certain platforms accept a mutable reference in their API.
|
||||||
#[allow(clippy::unnecessary_mut_passed)]
|
#[allow(clippy::unnecessary_mut_passed)]
|
||||||
@@ -262,7 +262,7 @@ impl EventLoop {
|
|||||||
///
|
///
|
||||||
/// [`DeviceEvent`]: crate::event::DeviceEvent
|
/// [`DeviceEvent`]: crate::event::DeviceEvent
|
||||||
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
||||||
let _span = tracing::debug_span!(
|
let _entered = tracing::debug_span!(
|
||||||
"winit::EventLoop::listen_device_events",
|
"winit::EventLoop::listen_device_events",
|
||||||
allowed = ?allowed
|
allowed = ?allowed
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user