mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
1 Commits
master
...
madsmtm/no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04e7516cf9 |
21
.github/workflows/ci.yml
vendored
21
.github/workflows/ci.yml
vendored
@@ -136,7 +136,7 @@ jobs:
|
||||
|
||||
- name: Generate lockfile
|
||||
# Also updates the crates.io index
|
||||
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
|
||||
run: cargo generate-lockfile && cargo update -p smol_str --precise 0.3.2 && cargo update -p unicode-segmentation --precise 1.12.0
|
||||
|
||||
- name: Install GCC Multilib
|
||||
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
||||
@@ -290,14 +290,31 @@ jobs:
|
||||
key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
cargo-deny:
|
||||
name: Run cargo-deny
|
||||
name: Run cargo-deny on ${{ matrix.platform.name }}
|
||||
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:
|
||||
- uses: taiki-e/checkout-action@v1
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
with:
|
||||
command: check
|
||||
log-level: error
|
||||
manifest-path: winit/Cargo.toml
|
||||
arguments: --all-features --target ${{ matrix.platform.target }}
|
||||
|
||||
eslint:
|
||||
name: ESLint
|
||||
|
||||
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --features=serde,mint,android-native-activity
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v6
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Fix permissions
|
||||
run: |
|
||||
|
||||
77
deny.toml
77
deny.toml
@@ -1,25 +1,27 @@
|
||||
# https://embarkstudios.github.io/cargo-deny
|
||||
# cargo install cargo-deny
|
||||
# cargo update && cargo deny check
|
||||
# cargo update && cargo deny --target aarch64-apple-ios 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]
|
||||
all-features = true
|
||||
exclude-dev = true
|
||||
targets = [
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-apple-ios",
|
||||
"aarch64-linux-android",
|
||||
"i686-pc-windows-gnu",
|
||||
"i686-pc-windows-msvc",
|
||||
"i686-unknown-linux-gnu",
|
||||
{ triple = "aarch64-apple-darwin" },
|
||||
{ triple = "aarch64-apple-ios" },
|
||||
{ triple = "aarch64-linux-android" },
|
||||
{ triple = "i686-pc-windows-gnu" },
|
||||
{ triple = "i686-pc-windows-msvc" },
|
||||
{ triple = "i686-unknown-linux-gnu" },
|
||||
{ triple = "wasm32-unknown-unknown", features = [
|
||||
"atomics",
|
||||
] },
|
||||
"x86_64-apple-darwin",
|
||||
"x86_64-apple-ios",
|
||||
"x86_64-pc-windows-gnu",
|
||||
"x86_64-pc-windows-msvc",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-unknown-redox",
|
||||
{ triple = "x86_64-apple-darwin" },
|
||||
{ triple = "x86_64-apple-ios" },
|
||||
{ triple = "x86_64-pc-windows-gnu" },
|
||||
{ triple = "x86_64-pc-windows-msvc" },
|
||||
{ triple = "x86_64-unknown-linux-gnu" },
|
||||
{ triple = "x86_64-unknown-redox" },
|
||||
]
|
||||
|
||||
[licenses]
|
||||
@@ -31,7 +33,6 @@ allow = [
|
||||
"MIT", # https://tldrlegal.com/license/mit-license
|
||||
"Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html
|
||||
"Zlib", # https://spdx.org/licenses/Zlib.html
|
||||
"MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/
|
||||
]
|
||||
confidence-threshold = 1.0
|
||||
private = { ignore = true }
|
||||
@@ -39,23 +40,47 @@ private = { ignore = true }
|
||||
[bans]
|
||||
multiple-versions = "deny"
|
||||
skip = [
|
||||
{ crate = "bitflags@1", reason = "the ecosystem is in the process of migrating" },
|
||||
{ crate = "rustix@0.38", reason = "the ecosystem is in the process of migrating" },
|
||||
{ crate = "linux-raw-sys@0.4", reason = "the ecosystem is in the process of migrating" },
|
||||
{ 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 = "thiserror@1.0", reason = "dep of `ndk` crate, yet to be updated" },
|
||||
{ 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]
|
||||
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
|
||||
interpreted = "deny"
|
||||
|
||||
[[bans.build.bypass]]
|
||||
allow-globs = ["android-games-sdk/import-games-sdk.sh"]
|
||||
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"
|
||||
|
||||
@@ -3,13 +3,13 @@ use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::rc::Rc;
|
||||
|
||||
use dpi::{LogicalPosition, PhysicalSize};
|
||||
use dpi::{LogicalPosition, LogicalSize};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::{AnyObject, Sel};
|
||||
use objc2::{AnyThread, DefinedClass, MainThreadMarker, define_class, msg_send};
|
||||
use objc2_app_kit::{
|
||||
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingArea,
|
||||
NSTrackingAreaOptions, NSView, NSViewLayerContentsRedrawPolicy, NSWindow,
|
||||
NSTrackingAreaOptions, NSView, NSWindow,
|
||||
};
|
||||
use objc2_core_foundation::CGRect;
|
||||
use objc2_foundation::{
|
||||
@@ -161,20 +161,10 @@ define_class!(
|
||||
// resize occurring.
|
||||
// 2. Even when a window resize does occur on a new tabbed window, it contains the wrong
|
||||
// size (includes tab height).
|
||||
self.surface_resized();
|
||||
// During live resize, AppKit may not let the normal event loop reach its next redraw
|
||||
// point before stretching the current layer contents. Redraw immediately after the
|
||||
// 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();
|
||||
let rect = self.frame();
|
||||
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
||||
let size = logical_size.to_physical::<u32>(self.scale_factor());
|
||||
self.queue_event(WindowEvent::SurfaceResized(size));
|
||||
}
|
||||
|
||||
#[unsafe(method(drawRect:))]
|
||||
@@ -283,26 +273,27 @@ define_class!(
|
||||
self.ivars().ime_state.set(ImeState::Ground);
|
||||
}
|
||||
|
||||
let string = string.to_string();
|
||||
let cursor_range = if string.is_empty() {
|
||||
// An empty string basically means that there's no preedit, so indicate that by
|
||||
// sending a `None` cursor range.
|
||||
None
|
||||
} else {
|
||||
// Convert the selected range from UTF-16 code unit indices to UTF-8 byte
|
||||
// offsets. `utf16_to_utf8_offset` is defensive: it snaps an offset that would
|
||||
// split a surrogate pair down to the character boundary and clamps an
|
||||
// out-of-bounds offset to the string length, so no `NSRangeException` is
|
||||
// possible and the resulting range can never be inverted (`lower <= upper`).
|
||||
// IMEs are known to send both mid-surrogate and out-of-bounds offsets (e.g.
|
||||
// native Pinyin, see https://github.com/alacritty/alacritty/issues/8791).
|
||||
let lowerbound_utf8 = utf16_to_utf8_offset(&string, selected_range.location);
|
||||
let upperbound_utf8 = utf16_to_utf8_offset(&string, selected_range.end());
|
||||
// Clamp to string length to avoid NSRangeException from out-of-bounds
|
||||
// indices sent by macOS IME (e.g. native Pinyin, see
|
||||
// https://github.com/alacritty/alacritty/issues/8791).
|
||||
let len = string.length();
|
||||
let location = selected_range.location.min(len);
|
||||
let end = selected_range.end().min(len);
|
||||
// Convert the selected range from UTF-16 indices to UTF-8 indices.
|
||||
let sub_string_a = string.substringToIndex(location);
|
||||
let sub_string_b = string.substringToIndex(end);
|
||||
let lowerbound_utf8 = sub_string_a.len();
|
||||
let upperbound_utf8 = sub_string_b.len();
|
||||
Some((lowerbound_utf8, upperbound_utf8))
|
||||
};
|
||||
|
||||
// Send WindowEvent for updating marked text
|
||||
self.queue_event(WindowEvent::Ime(Ime::Preedit(string, cursor_range)));
|
||||
self.queue_event(WindowEvent::Ime(Ime::Preedit(string.to_string(), cursor_range)));
|
||||
}
|
||||
|
||||
#[unsafe(method(unmarkText))]
|
||||
@@ -805,10 +796,6 @@ impl WinitView {
|
||||
let this: Retained<Self> = unsafe { msg_send![super(this), init] };
|
||||
*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
|
||||
// `mouseExited:`.
|
||||
//
|
||||
@@ -867,37 +854,6 @@ impl WinitView {
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
self.window().backingScaleFactor() as f64
|
||||
}
|
||||
@@ -1214,92 +1170,3 @@ fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Retained<NSEven
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,7 +912,10 @@ impl WindowDelegate {
|
||||
fn handle_scale_factor_changed(&self, scale_factor: CGFloat) {
|
||||
let window = self.window();
|
||||
|
||||
let suggested_size = self.view().surface_size();
|
||||
let content_size = window.contentRectForFrameRect(window.frame()).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));
|
||||
self.queue_event(WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
@@ -1037,7 +1040,9 @@ impl WindowDelegate {
|
||||
|
||||
#[inline]
|
||||
pub fn surface_size(&self) -> PhysicalSize<u32> {
|
||||
self.view().surface_size()
|
||||
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
|
||||
let logical = LogicalSize::new(content_rect.size.width, content_rect.size.height);
|
||||
logical.to_physical(self.scale_factor())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -14,7 +14,7 @@ event-handler = []
|
||||
|
||||
# XKB
|
||||
wayland = ["dep:memmap2"]
|
||||
x11 = ["xkbcommon-dl?/x11", "dep:x11-dl"]
|
||||
x11 = ["xkbcommon-dl?/x11"]
|
||||
xkb = ["dep:xkbcommon-dl", "dep:smol_str"]
|
||||
|
||||
# CoreFoundation
|
||||
@@ -30,7 +30,6 @@ winit-core.workspace = true
|
||||
|
||||
# XKB
|
||||
memmap2 = { workspace = true, optional = true }
|
||||
x11-dl = { workspace = true, optional = true }
|
||||
xkbcommon-dl = { workspace = true, optional = true }
|
||||
|
||||
# Foundation / CoreFoundation
|
||||
|
||||
@@ -7,9 +7,9 @@ use std::ptr::{self, NonNull};
|
||||
use winit_core::keyboard::{
|
||||
Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey,
|
||||
};
|
||||
#[cfg(feature = "x11")]
|
||||
use x11_dl::xlib_xcb::xcb_connection_t;
|
||||
use xkb::XKB_MOD_INVALID;
|
||||
#[cfg(feature = "x11")]
|
||||
use xkbcommon_dl::x11::xcb_connection_t;
|
||||
use xkbcommon_dl::{
|
||||
self as xkb, xkb_keycode_t, xkb_keymap, xkb_keymap_compile_flags, xkb_keysym_t,
|
||||
xkb_layout_index_t, xkb_mod_index_t,
|
||||
|
||||
@@ -9,12 +9,12 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use smol_str::SmolStr;
|
||||
use winit_core::event::{ElementState, KeyEvent};
|
||||
use winit_core::keyboard::{Key, KeyLocation};
|
||||
#[cfg(feature = "x11")]
|
||||
use xkbcommon_dl::x11::{xcb_connection_t, xkbcommon_x11_handle};
|
||||
use xkbcommon_dl::{
|
||||
self as xkb, XkbCommon, XkbCommonCompose, xkb_compose_status, xkb_context, xkb_context_flags,
|
||||
xkbcommon_compose_handle, xkbcommon_handle,
|
||||
};
|
||||
#[cfg(feature = "x11")]
|
||||
use {x11_dl::xlib_xcb::xcb_connection_t, xkbcommon_dl::x11::xkbcommon_x11_handle};
|
||||
|
||||
mod compose;
|
||||
mod keymap;
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::ptr::NonNull;
|
||||
|
||||
use smol_str::SmolStr;
|
||||
#[cfg(feature = "x11")]
|
||||
use x11_dl::xlib_xcb::xcb_connection_t;
|
||||
use xkbcommon_dl::x11::xcb_connection_t;
|
||||
use xkbcommon_dl::{
|
||||
self as xkb, xkb_keycode_t, xkb_keysym_t, xkb_layout_index_t, xkb_state, xkb_state_component,
|
||||
};
|
||||
|
||||
@@ -269,20 +269,6 @@ pub enum WindowEvent {
|
||||
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.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
@@ -1020,17 +1006,9 @@ pub enum Ime {
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum TouchPhase {
|
||||
/// Initial touch contact or gesture start, for example when one or more fingers touch the
|
||||
/// screen or touchpad.
|
||||
Started,
|
||||
/// The touch contact point changed, for example without lifting the finger.
|
||||
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,
|
||||
/// The event was cancelled and should cancel any event in flight and clear state.
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
|
||||
@@ -880,8 +880,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug {
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android / iOS / X11 / Web / Windows:** Unsupported.
|
||||
/// - **Wayland:** Only works with `org_kde_kwin_blur_manager` or
|
||||
/// `ext_background_effect_manager_v1` protocol.
|
||||
/// - **Wayland:** Only works with org_kde_kwin_blur_manager protocol.
|
||||
fn set_blur(&self, blur: bool);
|
||||
|
||||
/// Modifies the window's visibility.
|
||||
|
||||
@@ -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 }
|
||||
wayland-backend = { version = "0.3.10", default-features = false, features = ["client_system"] }
|
||||
wayland-client = "0.31.10"
|
||||
wayland-protocols = { version = "0.32.11", features = ["staging", "unstable"] }
|
||||
wayland-protocols = { version = "0.32.8", features = ["staging"] }
|
||||
wayland-protocols-plasma = { version = "0.3.8", features = ["client"] }
|
||||
winit-common = { workspace = true, features = ["xkb", "wayland"] }
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
|
||||
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
||||
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
||||
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::tablet::zv2::client::zwp_tablet_seat_v2::ZwpTabletSeatV2;
|
||||
use winit_core::event::WindowEvent;
|
||||
@@ -61,9 +60,6 @@ pub struct WinitSeatState {
|
||||
/// The pinch pointer gesture bound on the seat.
|
||||
pointer_gesture_pinch: Option<ZwpPointerGesturePinchV1>,
|
||||
|
||||
/// The hold pointer gesture bound on the seat.
|
||||
pointer_gesture_hold: Option<ZwpPointerGestureHoldV1>,
|
||||
|
||||
/// The keyboard bound on the seat.
|
||||
keyboard_state: Option<KeyboardState>,
|
||||
|
||||
@@ -145,14 +141,6 @@ 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);
|
||||
|
||||
// Register cursor surface.
|
||||
@@ -221,10 +209,6 @@ impl SeatHandler for WinitState {
|
||||
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() {
|
||||
let pointer_data = pointer.pointer().winit_data();
|
||||
|
||||
|
||||
@@ -7,12 +7,9 @@ use sctk::globals::GlobalData;
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, delegate_dispatch};
|
||||
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::{
|
||||
Event as PinchEvent, ZwpPointerGesturePinchV1,
|
||||
Event, ZwpPointerGesturePinchV1,
|
||||
};
|
||||
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::window::WindowId;
|
||||
|
||||
@@ -30,7 +27,7 @@ impl PointerGesturesState {
|
||||
globals: &GlobalList,
|
||||
queue_handle: &QueueHandle<WinitState>,
|
||||
) -> Result<Self, BindError> {
|
||||
let pointer_gestures = globals.bind(queue_handle, 3..=3, GlobalData)?;
|
||||
let pointer_gestures = globals.bind(queue_handle, 1..=1, GlobalData)?;
|
||||
Ok(Self { pointer_gestures })
|
||||
}
|
||||
}
|
||||
@@ -73,49 +70,6 @@ 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 {
|
||||
fn event(
|
||||
state: &mut WinitState,
|
||||
@@ -127,7 +81,7 @@ impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for Poin
|
||||
) {
|
||||
let mut pointer_gesture_data = data.inner.lock().unwrap();
|
||||
let (window_id, phase, pan_delta, pinch_delta, rotation_delta) = match event {
|
||||
PinchEvent::Begin { surface, fingers, .. } => {
|
||||
Event::Begin { surface, fingers, .. } => {
|
||||
// We only support two fingers for now.
|
||||
if fingers != 2 {
|
||||
return;
|
||||
@@ -146,7 +100,7 @@ impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for Poin
|
||||
|
||||
(window_id, TouchPhase::Started, PhysicalPosition::new(0., 0.), 0., 0.)
|
||||
},
|
||||
PinchEvent::Update { dx, dy, scale: pinch, rotation, .. } => {
|
||||
Event::Update { dx, dy, scale: pinch, rotation, .. } => {
|
||||
let window_id = match pointer_gesture_data.window_id {
|
||||
Some(window_id) => window_id,
|
||||
_ => return,
|
||||
@@ -167,7 +121,7 @@ impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for Poin
|
||||
let rotation_delta = -rotation as f32;
|
||||
(window_id, TouchPhase::Moved, pan_delta, pinch_delta, rotation_delta)
|
||||
},
|
||||
PinchEvent::End { cancelled, .. } => {
|
||||
Event::End { cancelled, .. } => {
|
||||
let window_id = match pointer_gesture_data.window_id {
|
||||
Some(window_id) => window_id,
|
||||
_ => return,
|
||||
@@ -201,4 +155,3 @@ impl Dispatch<ZwpPointerGesturePinchV1, PointerGestureData, WinitState> for Poin
|
||||
|
||||
delegate_dispatch!(WinitState: [ZwpPointerGesturesV1: GlobalData] => PointerGesturesState);
|
||||
delegate_dispatch!(WinitState: [ZwpPointerGesturePinchV1: PointerGestureData] => PointerGesturesState);
|
||||
delegate_dispatch!(WinitState: [ZwpPointerGestureHoldV1: PointerGestureData] => PointerGesturesState);
|
||||
|
||||
@@ -29,7 +29,7 @@ use crate::seat::{
|
||||
PointerConstraintsState, PointerGesturesState, RelativePointerState, TextInputState,
|
||||
WinitPointerData, WinitPointerDataExt, WinitSeatState,
|
||||
};
|
||||
use crate::types::bgr_effects::BgrEffectManager;
|
||||
use crate::types::kwin_blur::KWinBlurManager;
|
||||
use crate::types::wp_fractional_scaling::FractionalScalingManager;
|
||||
use crate::types::wp_tablet_input_v2::TabletManager;
|
||||
use crate::types::wp_viewporter::ViewporterState;
|
||||
@@ -116,8 +116,8 @@ pub struct WinitState {
|
||||
/// Fractional scaling manager.
|
||||
pub fractional_scaling_manager: Option<FractionalScalingManager>,
|
||||
|
||||
/// Blur manager.
|
||||
pub blur_manager: Option<BgrEffectManager>,
|
||||
/// KWin blur manager.
|
||||
pub kwin_blur_manager: Option<KWinBlurManager>,
|
||||
|
||||
/// Loop handle to re-register event sources, such as keyboard repeat.
|
||||
pub loop_handle: LoopHandle<'static, Self>,
|
||||
@@ -192,7 +192,7 @@ impl WinitState {
|
||||
window_events_sink: Default::default(),
|
||||
viewporter_state,
|
||||
fractional_scaling_manager,
|
||||
blur_manager: BgrEffectManager::new(globals, queue_handle).ok(),
|
||||
kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(),
|
||||
|
||||
seats,
|
||||
text_input_state: TextInputState::new(globals, queue_handle).ok(),
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
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);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
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,8 +1,6 @@
|
||||
//! Wayland protocol implementation boilerplate.
|
||||
|
||||
pub mod bgr_effects;
|
||||
pub mod cursor;
|
||||
pub mod ext_background_effect;
|
||||
pub mod kwin_blur;
|
||||
pub mod wp_fractional_scaling;
|
||||
pub mod wp_tablet_input_v2;
|
||||
|
||||
@@ -127,8 +127,7 @@ impl Window {
|
||||
// Set transparency hint.
|
||||
window_state.set_transparent(attributes.transparent);
|
||||
|
||||
// Set blur.
|
||||
let _ = window_state.set_blur(attributes.blur);
|
||||
window_state.set_blur(attributes.blur);
|
||||
|
||||
// Set the decorations hint.
|
||||
window_state.set_decorate(attributes.decorations);
|
||||
@@ -499,9 +498,7 @@ impl CoreWindow for Window {
|
||||
|
||||
#[inline]
|
||||
fn set_blur(&self, blur: bool) {
|
||||
if self.window_state.lock().unwrap().set_blur(blur) {
|
||||
self.request_redraw();
|
||||
}
|
||||
self.window_state.lock().unwrap().set_blur(blur);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -29,6 +29,7 @@ use sctk::shm::slot::SlotPool;
|
||||
use sctk::subcompositor::SubcompositorState;
|
||||
use tracing::{info, warn};
|
||||
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::error::{NotSupportedError, RequestError};
|
||||
use winit_core::window::{
|
||||
@@ -42,8 +43,8 @@ use crate::seat::{
|
||||
ZwpTextInputV3Ext,
|
||||
};
|
||||
use crate::state::{WindowCompositorUpdate, WinitState};
|
||||
use crate::types::bgr_effects::{BgrEffectManager, SurfaceBlurEffect};
|
||||
use crate::types::cursor::{CustomCursor, SelectedCursor, WaylandCustomCursor};
|
||||
use crate::types::kwin_blur::KWinBlurManager;
|
||||
use crate::types::xdg_toplevel_icon_manager::ToplevelIcon;
|
||||
|
||||
#[cfg(feature = "sctk-adwaita")]
|
||||
@@ -155,8 +156,8 @@ pub struct WindowState {
|
||||
|
||||
viewport: Option<WpViewport>,
|
||||
fractional_scale: Option<WpFractionalScaleV1>,
|
||||
blur: Option<SurfaceBlurEffect>,
|
||||
blur_manager: Option<BgrEffectManager>,
|
||||
blur: Option<OrgKdeKwinBlur>,
|
||||
blur_manager: Option<KWinBlurManager>,
|
||||
|
||||
/// Whether the client side decorations have pending move operations.
|
||||
///
|
||||
@@ -205,7 +206,7 @@ impl WindowState {
|
||||
toplevel_icon: None,
|
||||
xdg_toplevel_icon_manager,
|
||||
blur: None,
|
||||
blur_manager: winit_state.blur_manager.clone(),
|
||||
blur_manager: winit_state.kwin_blur_manager.clone(),
|
||||
compositor,
|
||||
handle,
|
||||
csd_fails: false,
|
||||
@@ -741,13 +742,6 @@ impl WindowState {
|
||||
// Set surface size without the borders.
|
||||
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.
|
||||
@@ -1119,37 +1113,20 @@ impl WindowState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Make window background blurred.
|
||||
///
|
||||
/// Returns `true` if redraw is required.
|
||||
#[must_use]
|
||||
pub fn set_blur(&mut self, blurred: bool) -> bool {
|
||||
if !blurred {
|
||||
self.blur = None;
|
||||
return true;
|
||||
}
|
||||
|
||||
let mgr = match self.blur_manager.as_mut() {
|
||||
Some(mgr) => mgr,
|
||||
None => {
|
||||
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
|
||||
/// Make window background blurred
|
||||
#[inline]
|
||||
pub fn set_blur(&mut self, blurred: bool) {
|
||||
if blurred && self.blur.is_none() {
|
||||
if let Some(blur_manager) = self.blur_manager.as_ref() {
|
||||
let blur = blur_manager.blur(self.window.wl_surface(), &self.queue_handle);
|
||||
blur.commit();
|
||||
self.blur = Some(blur);
|
||||
} else {
|
||||
info!("Blur manager unavailable, unable to change blur")
|
||||
}
|
||||
} else if !blurred && self.blur.is_some() {
|
||||
self.blur_manager.as_ref().unwrap().unset(self.window.wl_surface());
|
||||
self.blur.take().unwrap().release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1247,6 +1224,10 @@ impl WindowState {
|
||||
|
||||
impl Drop for WindowState {
|
||||
fn drop(&mut self) {
|
||||
if let Some(blur) = self.blur.take() {
|
||||
blur.release();
|
||||
}
|
||||
|
||||
if let Some(fs) = self.fractional_scale.take() {
|
||||
fs.destroy();
|
||||
}
|
||||
|
||||
@@ -51,12 +51,12 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
SystemParametersInfoW, TranslateMessage, WHEEL_DELTA, WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE,
|
||||
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_INPUT, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP,
|
||||
WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL,
|
||||
WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT,
|
||||
WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR,
|
||||
WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP,
|
||||
WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WMSZ_BOTTOM,
|
||||
WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
|
||||
WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE,
|
||||
WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN,
|
||||
WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS,
|
||||
WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, 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_TOPRIGHT, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
|
||||
WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
||||
@@ -1508,21 +1508,6 @@ unsafe fn public_window_callback_inner(
|
||||
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
|
||||
WM_SYSCOMMAND => {
|
||||
if wparam == SC_RESTORE as usize {
|
||||
|
||||
@@ -1757,7 +1757,7 @@ impl EventProcessor {
|
||||
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
||||
.map(|prev_monitor| prev_monitor.scale_factor);
|
||||
if Some(new_monitor.scale_factor) != maybe_prev_scale_factor {
|
||||
for window in self.target.windows.borrow().values().filter_map(|w| w.upgrade()) {
|
||||
for window in self.target.windows.borrow().iter().filter_map(|(_, w)| w.upgrade()) {
|
||||
window.refresh_dpi_for_monitor(
|
||||
&new_monitor,
|
||||
maybe_prev_scale_factor,
|
||||
|
||||
@@ -76,13 +76,9 @@ winit-core.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
image = { workspace = true, features = ["png"] }
|
||||
softbuffer.workspace = true
|
||||
tracing = { workspace = true, features = ["log"] }
|
||||
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]
|
||||
winit-android.workspace = true
|
||||
|
||||
@@ -15,6 +15,7 @@ use std::time::Instant;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use cursor_icon::CursorIcon;
|
||||
use rwh_06::{DisplayHandle, HasDisplayHandle};
|
||||
use softbuffer::{Context, Surface};
|
||||
use tracing::{error, info};
|
||||
#[cfg(web_platform)]
|
||||
@@ -24,7 +25,7 @@ use winit::cursor::{Cursor, CustomCursor, CustomCursorSource};
|
||||
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
|
||||
use winit::error::RequestError;
|
||||
use winit::event::{DeviceEvent, DeviceId, MouseButton, MouseScrollDelta, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::icon::{Icon, RgbaIcon};
|
||||
use winit::keyboard::{Key, ModifiersState};
|
||||
use winit::monitor::Fullscreen;
|
||||
@@ -44,6 +45,9 @@ use winit_core::application::macos::ApplicationHandlerExtMacOS;
|
||||
#[path = "util/tracing.rs"]
|
||||
mod tracing_init;
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
/// The amount of points to around the window for drag resize direction calculations.
|
||||
const BORDER_SIZE: f64 = 20.;
|
||||
|
||||
@@ -90,12 +94,20 @@ struct Application {
|
||||
/// Drawing context.
|
||||
///
|
||||
/// With OpenGL it could be EGLDisplay.
|
||||
context: Context<OwnedDisplayHandle>,
|
||||
context: Option<Context<DisplayHandle<'static>>>,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
fn new(event_loop: &EventLoop, receiver: Receiver<Action>, sender: Sender<Action>) -> Self {
|
||||
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
|
||||
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
|
||||
// varies by WM, and on Windows, you still have to account for screen scaling. Here
|
||||
@@ -529,7 +541,16 @@ impl ApplicationHandler for Application {
|
||||
WindowEvent::DoubleTapGesture { .. } => {
|
||||
info!("Smart zoom");
|
||||
},
|
||||
_ => (),
|
||||
WindowEvent::TouchpadPressure { .. }
|
||||
| WindowEvent::DragLeft { .. }
|
||||
| WindowEvent::KeyboardInput { .. }
|
||||
| WindowEvent::PointerEntered { .. }
|
||||
| WindowEvent::DragEntered { .. }
|
||||
| WindowEvent::DragMoved { .. }
|
||||
| WindowEvent::DragDropped { .. }
|
||||
| WindowEvent::Destroyed
|
||||
| WindowEvent::Ime(_)
|
||||
| WindowEvent::Moved(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,7 +605,9 @@ impl ApplicationHandlerExtMacOS for Application {
|
||||
/// State of the window.
|
||||
struct WindowState {
|
||||
/// 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.
|
||||
window: Arc<dyn Window>,
|
||||
/// The window theme we're drawing with.
|
||||
@@ -625,7 +648,9 @@ impl WindowState {
|
||||
fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> {
|
||||
let window: Arc<dyn Window> = Arc::from(window);
|
||||
|
||||
let surface = Surface::new(&app.context, Arc::clone(&window))?;
|
||||
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
|
||||
// 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);
|
||||
info!("Theme: {theme:?}");
|
||||
@@ -912,40 +937,35 @@ impl WindowState {
|
||||
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()?;
|
||||
|
||||
if self.animated_fill_color {
|
||||
// Fill the entire buffer with a single color.
|
||||
let time = self.start_time.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;
|
||||
buffer.fill(color);
|
||||
} else {
|
||||
// Draw a different color inside the safe area
|
||||
let surface_size = self.window.surface_size();
|
||||
let insets = self.window.safe_area();
|
||||
for y in 0..surface_size.height {
|
||||
for x in 0..surface_size.width {
|
||||
let index = y as usize * surface_size.width as usize + x as usize;
|
||||
if insets.left <= x
|
||||
&& x <= (surface_size.width - insets.right)
|
||||
&& insets.top <= y
|
||||
&& y <= (surface_size.height - insets.bottom)
|
||||
{
|
||||
// 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
|
||||
};
|
||||
}
|
||||
// Draw a different color inside the safe area
|
||||
let surface_size = self.window.surface_size();
|
||||
let insets = self.window.safe_area();
|
||||
for y in 0..surface_size.height {
|
||||
for x in 0..surface_size.width {
|
||||
let index = y as usize * surface_size.width as usize + x as usize;
|
||||
if insets.left <= x
|
||||
&& x <= (surface_size.width - insets.right)
|
||||
&& insets.top <= y
|
||||
&& y <= (surface_size.height - insets.bottom)
|
||||
{
|
||||
// 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,12 +3,11 @@
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use softbuffer::{Context, Surface};
|
||||
use tracing::info;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize, Position};
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::raw_window_handle::HasRawWindowHandle;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
@@ -17,20 +16,18 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WindowData {
|
||||
surface: Surface<OwnedDisplayHandle, Box<dyn Window>>,
|
||||
window: Box<dyn Window>,
|
||||
color: u32,
|
||||
}
|
||||
|
||||
impl WindowData {
|
||||
fn new(context: &Context<OwnedDisplayHandle>, window: Box<dyn Window>, color: u32) -> Self {
|
||||
let surface = Surface::new(context, window).unwrap();
|
||||
Self { surface, color }
|
||||
fn new(window: Box<dyn Window>, color: u32) -> Self {
|
||||
Self { window, color }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Default, Debug)]
|
||||
struct Application {
|
||||
context: Context<OwnedDisplayHandle>,
|
||||
parent_window_id: Option<WindowId>,
|
||||
windows: HashMap<WindowId, WindowData>,
|
||||
}
|
||||
@@ -45,7 +42,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
info!("Parent window id: {:?})", window.id());
|
||||
self.parent_window_id = Some(window.id());
|
||||
|
||||
self.windows.insert(window.id(), WindowData::new(&self.context, window, 0xffbbbbbb));
|
||||
self.windows.insert(window.id(), WindowData::new(window, 0xffbbbbbb));
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
@@ -76,24 +73,18 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
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 child_window = spawn_child_window(
|
||||
parent_window.surface.window().as_ref(),
|
||||
event_loop,
|
||||
child_index,
|
||||
);
|
||||
let child_window =
|
||||
spawn_child_window(parent_window.window.as_ref(), event_loop, child_index);
|
||||
let child_id = child_window.id();
|
||||
info!("Child window created with id: {child_id:?}");
|
||||
self.windows.insert(
|
||||
child_id,
|
||||
WindowData::new(&self.context, child_window, child_color),
|
||||
);
|
||||
self.windows.insert(child_id, WindowData::new(child_window, child_color));
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = self.windows.get_mut(&window_id) {
|
||||
if let Some(window) = self.windows.get(&window_id) {
|
||||
if window_id == self.parent_window_id.unwrap() {
|
||||
fill::fill(&mut window.surface);
|
||||
fill::fill_window(window.window.as_ref());
|
||||
} else {
|
||||
fill::fill_with_color(&mut window.surface, window.color);
|
||||
fill::fill_window_with_color(window.window.as_ref(), window.color);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -127,8 +118,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
}
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||
event_loop.run_app(Application { context, parent_window_id: None, windows: HashMap::new() })
|
||||
event_loop.run_app(Application::default())
|
||||
}
|
||||
|
||||
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))]
|
||||
|
||||
@@ -4,13 +4,12 @@ use std::thread;
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time;
|
||||
|
||||
use softbuffer::{Context, Surface};
|
||||
use tracing::{info, warn};
|
||||
#[cfg(web_platform)]
|
||||
use web_time as time;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop, OwnedDisplayHandle};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
@@ -53,7 +52,7 @@ struct ControlFlowDemo {
|
||||
request_redraw: bool,
|
||||
wait_cancelled: bool,
|
||||
close_requested: bool,
|
||||
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for ControlFlowDemo {
|
||||
@@ -70,10 +69,7 @@ impl ApplicationHandler for ControlFlowDemo {
|
||||
let window_attributes = WindowAttributes::default().with_title(
|
||||
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
|
||||
);
|
||||
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);
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
@@ -116,9 +112,9 @@ impl ApplicationHandler for ControlFlowDemo {
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
let surface = self.surface.as_mut().unwrap();
|
||||
surface.window().pre_present_notify();
|
||||
fill::fill(surface);
|
||||
let window = self.window.as_ref().unwrap();
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
@@ -126,7 +122,7 @@ impl ApplicationHandler for ControlFlowDemo {
|
||||
|
||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
if self.request_redraw && !self.wait_cancelled && !self.close_requested {
|
||||
self.surface.as_ref().unwrap().window().request_redraw();
|
||||
self.window.as_ref().unwrap().request_redraw();
|
||||
}
|
||||
|
||||
match self.mode {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use std::error::Error;
|
||||
|
||||
use softbuffer::{Context, Surface};
|
||||
use tracing::info;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -17,24 +16,27 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
let app = Application::default();
|
||||
let app = Application::new();
|
||||
Ok(event_loop.run_app(app)?)
|
||||
}
|
||||
|
||||
/// Application state and event handling.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Debug)]
|
||||
struct Application {
|
||||
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
fn new() -> Self {
|
||||
Self { window: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for Application {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes =
|
||||
WindowAttributes::default().with_title("Drag and drop files on me!");
|
||||
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);
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
@@ -51,9 +53,9 @@ impl ApplicationHandler for Application {
|
||||
info!("{event:?}");
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
let surface = self.surface.as_mut().unwrap();
|
||||
surface.window().pre_present_notify();
|
||||
fill::fill(surface);
|
||||
let window = self.window.as_ref().unwrap();
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
WindowEvent::CloseRequested => {
|
||||
event_loop.exit();
|
||||
|
||||
@@ -9,11 +9,10 @@ use std::cmp;
|
||||
use std::error::Error;
|
||||
|
||||
use dpi::{LogicalPosition, PhysicalSize};
|
||||
use softbuffer::{Context, Surface};
|
||||
use tracing::{error, info};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{Ime, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, ModifiersState, NamedKey};
|
||||
#[cfg(web_platform)]
|
||||
use winit::platform::web::WindowAttributesWeb;
|
||||
@@ -31,7 +30,7 @@ const IME_CURSOR_SIZE: PhysicalSize<u32> = PhysicalSize::new(20, 20);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct App {
|
||||
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
input_state: TextInputState,
|
||||
modifiers: ModifiersState,
|
||||
}
|
||||
@@ -74,12 +73,14 @@ impl ApplicationHandler for App {
|
||||
#[cfg(web_platform)]
|
||||
let window_attributes = WindowAttributes::default()
|
||||
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
|
||||
let window = event_loop.create_window(window_attributes).expect("failed creating window");
|
||||
|
||||
let context =
|
||||
Context::new(event_loop.owned_display_handle()).expect("failed creating context");
|
||||
let surface = Surface::new(&context, window).expect("failed creating surface");
|
||||
self.surface = Some(surface);
|
||||
self.window = match event_loop.create_window(window_attributes) {
|
||||
Ok(window) => Some(window),
|
||||
Err(err) => {
|
||||
error!("error creating window: {err}");
|
||||
event_loop.exit();
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Allow IME out of the box.
|
||||
let enable_request = ImeEnableRequest::new(
|
||||
@@ -100,13 +101,11 @@ impl ApplicationHandler for App {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Close was requested; stopping");
|
||||
self.surface = None;
|
||||
self.window = None;
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::SurfaceResized(surface_size) => {
|
||||
let surface = self.surface.as_mut().expect("resize event without a surface");
|
||||
fill::resize(surface, surface_size);
|
||||
surface.window().request_redraw();
|
||||
WindowEvent::SurfaceResized(_) => {
|
||||
self.window.as_ref().expect("resize event without a window").request_redraw();
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Redraw the application.
|
||||
@@ -115,13 +114,13 @@ impl ApplicationHandler for App {
|
||||
// this event rather than in AboutToWait, since rendering in here allows
|
||||
// the program to gracefully handle redraws requested by the OS.
|
||||
|
||||
let surface = self.surface.as_mut().expect("redraw event without a surface");
|
||||
let window = self.window.as_ref().expect("redraw request without a window");
|
||||
|
||||
// Notify that you're about to draw.
|
||||
surface.window().pre_present_notify();
|
||||
window.pre_present_notify();
|
||||
|
||||
// Draw.
|
||||
fill::fill(surface);
|
||||
fill::fill_window(window.as_ref());
|
||||
|
||||
// For contiguous redraw loop you can request a redraw from here.
|
||||
// window.request_redraw();
|
||||
@@ -215,14 +214,14 @@ impl App {
|
||||
}
|
||||
|
||||
fn handle_ime_event(&mut self, event: Ime) {
|
||||
let surface = self.surface.as_ref().expect("IME request without a window");
|
||||
let window = self.window.as_ref().expect("IME request without a window");
|
||||
match event {
|
||||
Ime::Enabled => info!("IME enabled for Window={:?}", surface.window().id()),
|
||||
Ime::Enabled => info!("IME enabled for Window={:?}", window.id()),
|
||||
Ime::Preedit(text, caret_pos) => info!("Preedit: {text}, with caret at {caret_pos:?}"),
|
||||
Ime::Commit(text) => {
|
||||
self.input_state.append_text(&text);
|
||||
let request_data = self.get_ime_update();
|
||||
surface.window().request_ime_update(ImeRequest::Update(request_data)).unwrap();
|
||||
window.request_ime_update(ImeRequest::Update(request_data)).unwrap();
|
||||
self.print_input_state();
|
||||
},
|
||||
Ime::DeleteSurrounding { before_bytes, after_bytes } => {
|
||||
@@ -247,7 +246,7 @@ impl App {
|
||||
error!("Buggy IME tried to delete with indices not on char boundary.");
|
||||
}
|
||||
},
|
||||
Ime::Disabled => info!("IME disabled for Window={:?}", surface.window().id()),
|
||||
Ime::Disabled => info!("IME disabled for Window={:?}", window.id()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +312,7 @@ impl App {
|
||||
}
|
||||
|
||||
fn window(&self) -> &dyn Window {
|
||||
self.surface.as_ref().unwrap().window().as_ref()
|
||||
self.window.as_ref().unwrap().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +355,7 @@ Use CTRL+h to cycle content hint permutations.
|
||||
);
|
||||
|
||||
let app = App {
|
||||
surface: None,
|
||||
window: None,
|
||||
input_state: TextInputState {
|
||||
ime_enabled: true,
|
||||
contents: String::new(),
|
||||
|
||||
@@ -7,12 +7,11 @@ fn main() -> std::process::ExitCode {
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
use softbuffer::{Context, Surface};
|
||||
use tracing::info;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::pump_events::{EventLoopExtPumpEvents, PumpStatus};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -22,16 +21,13 @@ fn main() -> std::process::ExitCode {
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct PumpDemo {
|
||||
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for PumpDemo {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default().with_title("A fantastic window!");
|
||||
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());
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
@@ -42,16 +38,16 @@ fn main() -> std::process::ExitCode {
|
||||
) {
|
||||
info!("{event:?}");
|
||||
|
||||
let surface = match self.surface.as_mut() {
|
||||
Some(surface) => surface,
|
||||
let window = match self.window.as_ref() {
|
||||
Some(window) => window,
|
||||
None => return,
|
||||
};
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill(surface);
|
||||
surface.window().request_redraw();
|
||||
fill::fill_window(window.as_ref());
|
||||
window.request_redraw();
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use std::time::Duration;
|
||||
|
||||
use softbuffer::{Context, Surface};
|
||||
use tracing::info;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
@@ -18,30 +17,27 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[path = "util/tracing.rs"]
|
||||
mod tracing;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Default, Debug)]
|
||||
struct App {
|
||||
context: Context<OwnedDisplayHandle>,
|
||||
idx: usize,
|
||||
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||
window_id: Option<WindowId>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||
if let Some(surface) = self.surface.as_ref() {
|
||||
surface.window().request_redraw();
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default()
|
||||
.with_title(format!("Fantastic window number {}!", self.idx))
|
||||
.with_title("Fantastic window number one!")
|
||||
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
|
||||
let window = event_loop.create_window(window_attributes).unwrap();
|
||||
self.window_id = Some(window.id());
|
||||
|
||||
let surface = Surface::new(&self.context, window).unwrap();
|
||||
self.surface = Some(surface);
|
||||
self.window = Some(window);
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
@@ -57,17 +53,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(surface) = self.surface.as_mut() else {
|
||||
return;
|
||||
let window = match self.window.as_mut() {
|
||||
Some(window) => window,
|
||||
None => return,
|
||||
};
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Window {} CloseRequested", self.idx);
|
||||
self.surface = None;
|
||||
fill::cleanup_window(window.as_ref());
|
||||
self.window = None;
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill(surface);
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
@@ -78,8 +76,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let mut event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let context = Context::new(event_loop.owned_display_handle()).unwrap();
|
||||
let mut app = App { context, idx: 1, surface: None, window_id: None };
|
||||
let mut app = App { idx: 1, ..Default::default() };
|
||||
event_loop.run_app_on_demand(&mut app)?;
|
||||
|
||||
info!("Finished first loop");
|
||||
|
||||
@@ -1,43 +1,115 @@
|
||||
//! 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;
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time::Instant;
|
||||
|
||||
use rwh_06::{HasDisplayHandle, HasWindowHandle};
|
||||
use softbuffer::Surface;
|
||||
use winit::window::Window;
|
||||
use softbuffer::{Context, Surface};
|
||||
#[cfg(web_platform)]
|
||||
use web_time::Instant;
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
/// Resize the surface.
|
||||
pub fn resize(
|
||||
surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle>,
|
||||
surface_size: dpi::PhysicalSize<u32>,
|
||||
) {
|
||||
// Handle zero-sized buffers.
|
||||
thread_local! {
|
||||
// NOTE: You should never do things like that, create context and drop it before
|
||||
// you drop the event loop. We do this for brevity to not blow up examples. We use
|
||||
// ManuallyDrop to prevent destructors from running.
|
||||
//
|
||||
// FIXME(madsmtm): This should be done by softbuffer internally in the future:
|
||||
// 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");
|
||||
// A static, thread-local map of graphics contexts to open windows.
|
||||
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) };
|
||||
}
|
||||
|
||||
/// Fill the window buffer with a solid color.
|
||||
pub fn fill_with_color(
|
||||
surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle + AsRef<dyn Window>>,
|
||||
color: u32,
|
||||
) {
|
||||
let surface_size = surface.window().as_ref().surface_size();
|
||||
resize(surface, surface_size);
|
||||
/// The graphics context used to draw to a window.
|
||||
struct GraphicsContext {
|
||||
/// The global softbuffer context.
|
||||
context: RefCell<Context<&'static dyn Window>>,
|
||||
|
||||
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");
|
||||
/// The hash map of window IDs to surfaces.
|
||||
surfaces: HashMap<WindowId, Surface<&'static dyn Window, &'static dyn Window>>,
|
||||
}
|
||||
|
||||
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)]
|
||||
pub fn fill(
|
||||
surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle + AsRef<dyn Window>>,
|
||||
) {
|
||||
fill_with_color(surface, 0xff181818);
|
||||
pub fn fill_window(window: &dyn Window) {
|
||||
fill_window_with_color(window, 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
use softbuffer::{Context, Surface};
|
||||
use tracing::info;
|
||||
use tracing::{error, info};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
#[cfg(web_platform)]
|
||||
use winit::platform::web::WindowAttributesWeb;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
@@ -18,7 +17,7 @@ mod tracing;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct App {
|
||||
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
@@ -28,12 +27,14 @@ impl ApplicationHandler for App {
|
||||
#[cfg(web_platform)]
|
||||
let window_attributes = WindowAttributes::default()
|
||||
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
|
||||
let window = event_loop.create_window(window_attributes).expect("failed creating window");
|
||||
|
||||
let context =
|
||||
Context::new(event_loop.owned_display_handle()).expect("failed creating context");
|
||||
let surface = Surface::new(&context, window).expect("failed creating surface");
|
||||
self.surface = Some(surface);
|
||||
self.window = match event_loop.create_window(window_attributes) {
|
||||
Ok(window) => Some(window),
|
||||
Err(err) => {
|
||||
error!("error creating window: {err}");
|
||||
event_loop.exit();
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) {
|
||||
@@ -43,10 +44,8 @@ impl ApplicationHandler for App {
|
||||
info!("Close was requested; stopping");
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::SurfaceResized(surface_size) => {
|
||||
let surface = self.surface.as_mut().expect("resize event without a surface");
|
||||
fill::resize(surface, surface_size);
|
||||
surface.window().request_redraw();
|
||||
WindowEvent::SurfaceResized(_) => {
|
||||
self.window.as_ref().expect("resize event without a window").request_redraw();
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Redraw the application.
|
||||
@@ -55,15 +54,13 @@ impl ApplicationHandler for App {
|
||||
// this event rather than in AboutToWait, since rendering in here allows
|
||||
// the program to gracefully handle redraws requested by the OS.
|
||||
|
||||
let surface = self.surface.as_mut().expect("redraw event without a surface");
|
||||
let window = self.window.as_ref().expect("redraw request without a window");
|
||||
|
||||
// Notify that you're about to draw.
|
||||
surface.window().pre_present_notify();
|
||||
window.pre_present_notify();
|
||||
|
||||
// Draw.
|
||||
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");
|
||||
fill::fill_window(window.as_ref());
|
||||
|
||||
// For contiguous redraw loop you can request a redraw from here.
|
||||
// window.request_redraw();
|
||||
|
||||
@@ -3,10 +3,9 @@ use std::error::Error;
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
use softbuffer::{Context, Surface};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::platform::x11::WindowAttributesX11;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
@@ -18,7 +17,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
#[derive(Debug)]
|
||||
pub struct XEmbedDemo {
|
||||
parent_window_id: u32,
|
||||
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for XEmbedDemo {
|
||||
@@ -30,10 +29,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
WindowAttributesX11::default().with_embed_parent_window(self.parent_window_id);
|
||||
window_attributes = window_attributes.with_platform_attributes(Box::new(x11_attrs));
|
||||
|
||||
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);
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
@@ -42,19 +38,19 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
let window = self.window.as_ref().unwrap();
|
||||
match event {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
let surface = self.surface.as_mut().unwrap();
|
||||
surface.window().pre_present_notify();
|
||||
fill::fill(surface);
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||
self.surface.as_ref().unwrap().window().request_redraw();
|
||||
self.window.as_ref().unwrap().request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +63,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
tracing::init();
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, surface: None })?)
|
||||
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?)
|
||||
}
|
||||
|
||||
#[cfg(not(x11_platform))]
|
||||
|
||||
@@ -48,8 +48,6 @@ changelog entry.
|
||||
- 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
|
||||
|
||||
@@ -58,12 +56,7 @@ changelog entry.
|
||||
|
||||
### 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 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 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.
|
||||
|
||||
Reference in New Issue
Block a user