From d3cb27129cc628afc4203479dd5f5fa387ca2b8b Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 2 May 2020 15:53:32 -0700 Subject: [PATCH] upgrade to latest smithay client toolkit This version greatly improves the client side decoration handling under Wayland and allows rendering the window title in the titlebar (shocker!). --- Cargo.lock | 180 ++++++++++++++++---- window/Cargo.toml | 14 +- window/src/egl.rs | 2 +- window/src/os/wayland/connection.rs | 195 ++++++++++------------ window/src/os/wayland/copy_and_paste.rs | 3 +- window/src/os/wayland/keyboard.rs | 64 ++++---- window/src/os/wayland/pointer.rs | 83 ++++------ window/src/os/wayland/window.rs | 210 ++++++++++++------------ window/src/os/x11/keyboard.rs | 4 +- window/src/spawn.rs | 10 +- 10 files changed, 433 insertions(+), 332 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index acc92c611..72f80bc15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,12 @@ dependencies = [ "const-random", ] +[[package]] +name = "ahash" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b909d1c126f78ace756fc337133356c499eebeefcce930fa5fb018823f2b2d" + [[package]] name = "aho-corasick" version = "0.7.10" @@ -317,6 +323,16 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" +[[package]] +name = "calloop" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9e85e9184ff1f5129d751a87500ac57069e28086da57da858b78f0f1530973" +dependencies = [ + "log", + "nix", +] + [[package]] name = "cassowary" version = "0.3.0" @@ -726,6 +742,15 @@ dependencies = [ "libloading", ] +[[package]] +name = "dlv-list" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b391911b9a786312a10cb9d2b3d0735adfd5a8113eb3648de26a75e91b0826c" +dependencies = [ + "rand 0.7.3", +] + [[package]] name = "downcast-rs" version = "1.1.1" @@ -1126,10 +1151,20 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" dependencies = [ - "ahash", + "ahash 0.2.18", "autocfg 0.1.7", ] +[[package]] +name = "hashbrown" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" +dependencies = [ + "ahash 0.3.3", + "autocfg 1.0.0", +] + [[package]] name = "hdrhistogram" version = "6.3.4" @@ -1688,9 +1723,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.14.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" dependencies = [ "bitflags 1.2.1", "cc", @@ -1705,6 +1740,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" + [[package]] name = "nom" version = "4.2.3" @@ -1957,6 +1998,16 @@ dependencies = [ "num-traits 0.2.11", ] +[[package]] +name = "ordered-multimap" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88f947c6799d5eff50e6cf8a2365c17ac4aa8f8f43aceeedc29b616d872a358" +dependencies = [ + "dlv-list", + "hashbrown 0.7.2", +] + [[package]] name = "output_vt100" version = "0.1.2" @@ -2508,6 +2559,16 @@ dependencies = [ "crossbeam-utils 0.7.2", ] +[[package]] +name = "rust-ini" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c609fa8151080b18c38d39e09d1e55d6301d5610428ff804d0d59c4bac15cf7" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-demangle" version = "0.1.16" @@ -2558,6 +2619,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + [[package]] name = "scoped_threadpool" version = "0.1.9" @@ -2743,17 +2810,20 @@ checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" [[package]] name = "smithay-client-toolkit" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421c8dc7acf5cb205b88160f8b4cc2c5cfabe210e43b2f80f009f4c1ef910f1d" +version = "0.9.1" +source = "git+https://github.com/wez/client-toolkit.git?branch=title_tunc#a178efdd19113228d02ce711602bb9ab78f66084" dependencies = [ "andrew", "bitflags 1.2.1", + "byteorder", + "calloop", "dlib", "lazy_static", + "log", "memmap", "nix", "wayland-client", + "wayland-cursor", "wayland-protocols", ] @@ -3374,14 +3444,15 @@ checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad" [[package]] name = "wayland-client" -version = "0.23.6" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda" +checksum = "b1d7a5dfc26df8b651cebffeda272b0e444a7485cc83009be2aaffee249d9ea6" dependencies = [ "bitflags 1.2.1", "downcast-rs", "libc", "nix", + "scoped-tls", "wayland-commons", "wayland-scanner", "wayland-sys", @@ -3389,19 +3460,43 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.23.6" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" +checksum = "3b0ad6f753185c9ad9c7d6daa588b5c42b2531c58a4ba2f2268f81f1aafb0580" dependencies = [ "nix", + "once_cell", + "smallvec 1.4.0", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823dded13083b2874710af9a7d258a90d44d8501e2c34bb43568b4cc427f47b" +dependencies = [ + "nix", + "wayland-client", + "xcur", + "xcursor", +] + +[[package]] +name = "wayland-egl" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ba50703dbdb6d4b5524b593519547b45b69bf1bb3692c191647325faa50371" +dependencies = [ + "wayland-client", "wayland-sys", ] [[package]] name = "wayland-protocols" -version = "0.23.6" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" +checksum = "d3458a4a34c37220130cce717aa4e4766a6ee8c19a8db1a565e19dc54d666c68" dependencies = [ "bitflags 1.2.1", "wayland-client", @@ -3411,20 +3506,20 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.23.6" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d" +checksum = "e794b09da468acaeee5f21feb6148ff61a79a6614f6540fe513b060bbb03f1e3" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", + "proc-macro2 1.0.12", + "quote 1.0.4", "xml-rs 0.8.3", ] [[package]] name = "wayland-sys" -version = "0.23.6" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4" +checksum = "26d91fd1620a38a2815091741e6e75e104d7c135f2239d464d319d63f872d906" dependencies = [ "dlib", ] @@ -3594,10 +3689,11 @@ dependencies = [ "smithay-client-toolkit", "thiserror", "wayland-client", + "wayland-egl", "winapi 0.3.8", "winreg", "x11", - "xcb", + "xcb 0.9.0", "xcb-util", "xkbcommon", ] @@ -3659,7 +3755,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea" dependencies = [ - "xcb", + "xcb 0.8.2", ] [[package]] @@ -3673,13 +3769,41 @@ dependencies = [ ] [[package]] -name = "xcb-util" -version = "0.2.1" +name = "xcb" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d35fc0bbe4349a46322f30a670f61f4da2c2896f64cef53fe54e6c5ec48d8ab" +checksum = "62056f63138b39116f82a540c983cc11f1c90cd70b3d492a70c25eaa50bd22a6" dependencies = [ "libc", - "xcb", + "log", +] + +[[package]] +name = "xcb-util" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43893e47f27bf7d81d489feef3a0e34a457e90bc314b7e74ad9bb3980e4c1c48" +dependencies = [ + "libc", + "xcb 0.9.0", +] + +[[package]] +name = "xcur" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1879006e82c8f58cf3d1a260434a39c210964e9665fa2f826dc53619201e8b" +dependencies = [ + "nom 1.2.4", +] + +[[package]] +name = "xcursor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad3ff3b2f6870f8bf3472cdb4aac8063d8bc2c2ef4d71746e307f5cca592d8a1" +dependencies = [ + "rust-ini", ] [[package]] @@ -3696,12 +3820,12 @@ checksum = "7395cdb9d0a6219fa0ea77d08c946adf9c1984c72fcd443ace30365f3daadef7" [[package]] name = "xkbcommon" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda0ea5f7ddabd51deeeda7799bee06274112f577da7dd3d954b8eda731b2fce" +version = "0.5.0" +source = "git+https://github.com/wez/xkbcommon-rs.git?rev=01a0a0cd5663405e6e4abb1ad3add9add1496f58#01a0a0cd5663405e6e4abb1ad3add9add1496f58" dependencies = [ "libc", - "xcb", + "memmap", + "xcb 0.9.0", ] [[package]] diff --git a/window/Cargo.toml b/window/Cargo.toml index 4a058f702..e3f4f7e09 100644 --- a/window/Cargo.toml +++ b/window/Cargo.toml @@ -33,7 +33,7 @@ glium = { version = "0.26.0-alpha3", optional=true, default-features = false} [features] async_await = [] opengl = ["cgl", "glium", "gl_generator", "libloading"] -wayland = ["smithay-client-toolkit", "memmap", "wayland-client"] +wayland = ["smithay-client-toolkit", "memmap", "wayland-client", "wayland-egl"] [target."cfg(windows)".dependencies] lazy_static = "1.4" @@ -52,16 +52,16 @@ clipboard-win = "2.2" [target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] filedescriptor = { version="0.7", path = "../filedescriptor" } x11 = {version ="2.18", features = ["xlib_xcb"]} -xcb = "0.8" -# See: https://github.com/meh/rust-xcb-util/issues/12 -xcb-util = { features = [ "icccm", "ewmh", "keysyms", "shm"], version = "=0.2.1" } -xkbcommon = { version = "0.4", features = ["x11"] } +xcb = {version="0.9", features=["shm", "xkb"]} +xcb-util = { features = [ "icccm", "ewmh", "keysyms", "shm"], version = "0.3" } +xkbcommon = { version = "0.5", features = ["x11", "wayland"], git="https://github.com/wez/xkbcommon-rs.git", rev="01a0a0cd5663405e6e4abb1ad3add9add1496f58"} mio = "0.6" mio-extras = "2.0" libc = "0.2" -smithay-client-toolkit = {version = "0.6", optional = true} +smithay-client-toolkit = {version = "0.9", optional = true, features=["calloop"], git="https://github.com/wez/client-toolkit.git", branch="title_tunc"} memmap = {version="0.7", optional=true} -wayland-client = {version="0.23", optional=true, features=["egl"]} +wayland-client = {version="0.26", optional=true} +wayland-egl = {version="0.26", optional=true} [target.'cfg(target_os="macos")'.dependencies] cocoa = "0.20" diff --git a/window/src/egl.rs b/window/src/egl.rs index 10e6a9199..11f3a706f 100644 --- a/window/src/egl.rs +++ b/window/src/egl.rs @@ -238,7 +238,7 @@ impl GlState { #[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] pub fn create_wayland( display: Option, - wegl_surface: &wayland_client::egl::WlEglSurface, + wegl_surface: &wayland_egl::WlEglSurface, ) -> anyhow::Result { Self::with_egl_lib(move |egl| { let egl_display = egl.get_display(display)?; diff --git a/window/src/os/wayland/connection.rs b/window/src/os/wayland/connection.rs index b25e7ed63..a70c2e5f9 100644 --- a/window/src/os/wayland/connection.rs +++ b/window/src/os/wayland/connection.rs @@ -6,18 +6,19 @@ use crate::connection::ConnectionOps; use crate::spawn::*; use crate::timerlist::{TimerEntry, TimerList}; use crate::Connection; -use anyhow::{bail, Context}; -use mio::unix::EventedFd; -use mio::{Evented, Events, Poll, PollOpt, Ready, Token}; +use anyhow::{anyhow, bail, Context}; use smithay_client_toolkit as toolkit; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::sync::atomic::AtomicUsize; use std::time::{Duration, Instant}; -use toolkit::reexports::client::protocol::wl_seat::{Event as SeatEvent, WlSeat}; -use toolkit::reexports::client::{Display, EventQueue}; -use toolkit::Environment; +use toolkit::environment::Environment; +use toolkit::reexports::calloop::{EventLoop, EventSource, Interest, Mode, Poll, Readiness, Token}; +use toolkit::reexports::client::Display; +use toolkit::WaylandSource; + +toolkit::default_environment!(MyEnvironment, desktop); pub struct WaylandConnection { should_terminate: RefCell, @@ -34,77 +35,55 @@ pub struct WaylandConnection { // bottom of this list. pub(crate) pointer: PointerDispatcher, pub(crate) keyboard: KeyboardDispatcher, - pub(crate) seat: WlSeat, - pub(crate) environment: RefCell, - event_q: RefCell, + pub(crate) environment: RefCell>, + event_q: RefCell>, pub(crate) display: RefCell, } -impl Evented for WaylandConnection { - fn register( - &self, - poll: &Poll, - token: Token, - interest: Ready, - opts: PollOpt, - ) -> std::io::Result<()> { - EventedFd(&self.event_q.borrow().get_connection_fd()).register(poll, token, interest, opts) - } - - fn reregister( - &self, - poll: &Poll, - token: Token, - interest: Ready, - opts: PollOpt, - ) -> std::io::Result<()> { - EventedFd(&self.event_q.borrow().get_connection_fd()) - .reregister(poll, token, interest, opts) - } - - fn deregister(&self, poll: &Poll) -> std::io::Result<()> { - EventedFd(&self.event_q.borrow().get_connection_fd()).deregister(poll) - } -} - impl WaylandConnection { pub fn create_new() -> anyhow::Result { - let (display, mut event_q) = Display::connect_to_env()?; - let environment = Environment::from_display(&*display, &mut event_q)?; + let (environment, display, event_q) = + toolkit::init_default_environment!(MyEnvironment, desktop)?; + let event_loop = toolkit::reexports::calloop::EventLoop::<()>::new()?; - let seat = environment - .manager - .instantiate_range(1, 6, move |seat| { - seat.implement_closure( - move |event, _seat| { - if let SeatEvent::Name { name } = event { - log::info!("seat name is {}", name); - } - }, - (), + let keyboard = KeyboardDispatcher::new(); + let mut pointer = None; + + for seat in environment.get_all_seats() { + if let Some((has_kbd, has_ptr)) = toolkit::seat::with_seat_data(&seat, |seat_data| { + ( + seat_data.has_keyboard && !seat_data.defunct, + seat_data.has_pointer && !seat_data.defunct, ) - }) - .context("Failed to create seat")?; - let keyboard = KeyboardDispatcher::register(&seat)?; + }) { + if has_kbd { + keyboard.register(event_loop.handle(), &seat)?; + } + if has_ptr { + pointer.replace(PointerDispatcher::register( + &seat, + environment.require_global(), + environment.require_global(), + environment.require_global(), + )?); + } + } + } - let pointer = PointerDispatcher::register( - &seat, - environment.compositor.clone(), - &environment.shm, - &environment.data_device_manager, - )?; + WaylandSource::new(event_q) + .quick_insert(event_loop.handle()) + .map_err(|e| anyhow!("failed to setup WaylandSource: {:?}", e))?; Ok(Self { display: RefCell::new(display), - event_q: RefCell::new(event_q), + event_q: RefCell::new(event_loop), environment: RefCell::new(environment), should_terminate: RefCell::new(false), timers: RefCell::new(TimerList::new()), next_window_id: AtomicUsize::new(1), windows: RefCell::new(HashMap::new()), - seat, keyboard, - pointer, + pointer: pointer.unwrap(), }) } @@ -124,22 +103,6 @@ impl WaylandConnection { fn do_paint(&self) {} - fn process_queued_events(&self) -> anyhow::Result<()> { - { - let mut event_q = self.event_q.borrow_mut(); - if let Some(guard) = event_q.prepare_read() { - if let Err(e) = guard.read_events() { - if e.kind() != ::std::io::ErrorKind::WouldBlock { - bail!("Error while reading events: {}", e); - } - } - } - event_q.dispatch_pending()?; - } - self.flush()?; - Ok(()) - } - pub(crate) fn window_by_id(&self, window_id: usize) -> Option>> { self.windows.borrow().get(&window_id).map(Rc::clone) } @@ -168,6 +131,38 @@ impl WaylandConnection { } } +struct SpawnQueueSource {} +impl EventSource for SpawnQueueSource { + type Event = (); + type Metadata = (); + type Ret = (); + + fn process_events( + &mut self, + _readiness: Readiness, + _token: Token, + mut callback: F, + ) -> std::io::Result<()> + where + F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, + { + callback((), &mut ()); + Ok(()) + } + + fn register(&mut self, poll: &mut Poll, token: Token) -> std::io::Result<()> { + poll.register(SPAWN_QUEUE.raw_fd(), Interest::Readable, Mode::Level, token) + } + + fn reregister(&mut self, poll: &mut Poll, token: Token) -> std::io::Result<()> { + poll.register(SPAWN_QUEUE.raw_fd(), Interest::Readable, Mode::Level, token) + } + + fn unregister(&mut self, poll: &mut Poll) -> std::io::Result<()> { + poll.unregister(SPAWN_QUEUE.raw_fd()) + } +} + impl ConnectionOps for WaylandConnection { fn terminate_message_loop(&self) { *self.should_terminate.borrow_mut() = true; @@ -176,20 +171,16 @@ impl ConnectionOps for WaylandConnection { fn run_message_loop(&self) -> anyhow::Result<()> { self.flush()?; - const TOK_WAYLAND: usize = 0xffff_fffc; - const TOK_SPAWN: usize = 0xffff_fffd; - let tok_wayland = Token(TOK_WAYLAND); - let tok_spawn = Token(TOK_SPAWN); - - let poll = Poll::new()?; - let mut events = Events::with_capacity(8); - poll.register(self, tok_wayland, Ready::readable(), PollOpt::level())?; - poll.register( - &*SPAWN_QUEUE, - tok_spawn, - Ready::readable(), - PollOpt::level(), - )?; + self.event_q + .borrow_mut() + .handle() + .insert_source(SpawnQueueSource {}, move |_, _, _| { + // In theory, we'd SPAWN_QUEUE.run() here but we + // prefer to defer that to the loop below where we + // can have better control over the event_q borrow, + // and so that we can inspect its return code. + }) + .map_err(|e| anyhow!("failed to insert SpawnQueueSource: {:?}", e))?; let paint_interval = Duration::from_millis(25); let mut last_interval = Instant::now(); @@ -207,13 +198,6 @@ impl ConnectionOps for WaylandConnection { paint_interval - diff }; - // Process any events that might have accumulated in the local - // buffer (eg: due to a flush) before we potentially go to sleep. - // The locally queued events won't mark the fd as ready, so we - // could potentially sleep when there is work to be done if we - // relied solely on that. - self.process_queued_events()?; - // Check the spawn queue before we try to sleep; there may // be work pending and we don't guarantee that there is a // 1:1 wakeup to queued function, so we need to be assertive @@ -230,15 +214,16 @@ impl ConnectionOps for WaylandConnection { .unwrap_or(period) }; - match poll.poll(&mut events, Some(period)) { - Ok(_) => { - // We process both event sources unconditionally - // in the loop above anyway; we're just using - // this to get woken up. - } + self.flush()?; - Err(err) => { - bail!("polling for events: {:?}", err); + { + let mut event_q = self.event_q.borrow_mut(); + if let Err(err) = event_q.dispatch(Some(period), &mut ()) { + if err.kind() != std::io::ErrorKind::WouldBlock + && err.kind() != std::io::ErrorKind::Interrupted + { + return Err(err).context("error during event_q.dispatch"); + } } } } diff --git a/window/src/os/wayland/copy_and_paste.rs b/window/src/os/wayland/copy_and_paste.rs index a79e22f12..aaa5072c0 100644 --- a/window/src/os/wayland/copy_and_paste.rs +++ b/window/src/os/wayland/copy_and_paste.rs @@ -5,6 +5,7 @@ use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex}; use toolkit::reexports::client::protocol::wl_data_offer::{Event as DataOfferEvent, WlDataOffer}; use toolkit::reexports::client::protocol::wl_data_source::WlDataSource; +use wayland_client::Attached; #[derive(Default)] pub struct CopyAndPaste { @@ -69,7 +70,7 @@ impl CopyAndPaste { self.data_offer.replace(offer); } - pub fn set_selection(&mut self, source: WlDataSource) { + pub fn set_selection(&mut self, source: &Attached) { use crate::connection::ConnectionOps; crate::Connection::get() .unwrap() diff --git a/window/src/os/wayland/keyboard.rs b/window/src/os/wayland/keyboard.rs index 9e3bc8ebe..415d4c567 100644 --- a/window/src/os/wayland/keyboard.rs +++ b/window/src/os/wayland/keyboard.rs @@ -4,12 +4,13 @@ use anyhow::anyhow; use smithay_client_toolkit as toolkit; use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use toolkit::keyboard::{ - map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind, KeyState, - ModifiersState, +use toolkit::reexports::calloop::LoopHandle; +use toolkit::seat::keyboard::{ + map_keyboard_repeat, Event as KbEvent, KeyState, ModifiersState, RepeatKind, }; use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::protocol::wl_surface::WlSurface; +use wayland_client::Attached; #[derive(Default)] struct Inner { @@ -37,16 +38,6 @@ impl Inner { } } - fn handle_repeat(&mut self, rawkey: u32, keysym: u32, utf8: Option) { - self.dispatch_to_window(KeyboardEvent::Key { - serial: 0, - rawkey, - keysym, - is_down: true, - utf8, - }); - } - fn dispatch_to_window(&mut self, evt: KeyboardEvent) { if let Some(window_id) = self.surface_to_window_id.get(&self.active_surface_id) { let mut evt = Some(evt); @@ -64,31 +55,29 @@ pub struct KeyboardDispatcher { } impl KeyboardDispatcher { - pub fn register(seat: &WlSeat) -> anyhow::Result { + pub fn new() -> Self { let inner = Arc::new(Mutex::new(Inner::default())); + Self { inner } + } - map_keyboard_auto_with_repeat( + pub fn register( + &self, + loop_handle: LoopHandle<()>, + seat: &Attached, + ) -> anyhow::Result<()> { + let inner = Arc::clone(&self.inner); + let (_kbd, _source) = map_keyboard_repeat( + loop_handle, &seat, - KeyRepeatKind::System, - { - let inner = Arc::clone(&inner); - move |evt: KbEvent, _| { - inner.lock().unwrap().handle_event(evt); - } - }, - { - let inner = Arc::clone(&inner); - move |evt: KeyRepeatEvent, _| { - inner - .lock() - .unwrap() - .handle_repeat(evt.rawkey, evt.keysym, evt.utf8); - } + None, + RepeatKind::System, + move |evt: KbEvent, _, _| { + inner.lock().unwrap().handle_event(evt); }, ) .map_err(|e| anyhow!("Failed to configure keyboard callback: {:?}", e))?; - Ok(Self { inner }) + Ok(()) } pub fn add_window(&self, window_id: usize, surface: &WlSurface) { @@ -138,10 +127,21 @@ impl KeyboardEvent { serial, utf8, }, + KbEvent::Repeat { + rawkey, + keysym, + utf8, + .. + } => KeyboardEvent::Key { + rawkey, + keysym, + is_down: true, + serial: 0, + utf8, + }, KbEvent::Modifiers { modifiers } => KeyboardEvent::Modifiers { modifiers: modifier_keys(modifiers), }, - _ => return None, }) } } diff --git a/window/src/os/wayland/pointer.rs b/window/src/os/wayland/pointer.rs index 1e216a560..5bc1f6d8d 100644 --- a/window/src/os/wayland/pointer.rs +++ b/window/src/os/wayland/pointer.rs @@ -1,11 +1,9 @@ use super::copy_and_paste::*; use crate::input::*; use crate::os::wayland::connection::WaylandConnection; -use anyhow::anyhow; use smithay_client_toolkit as toolkit; use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use toolkit::pointer::{AutoPointer, AutoThemer}; use toolkit::reexports::client::protocol::wl_data_device::{ Event as DataDeviceEvent, WlDataDevice, }; @@ -14,10 +12,12 @@ use toolkit::reexports::client::protocol::wl_pointer::{ self, Axis, AxisSource, Event as PointerEvent, }; use toolkit::reexports::client::protocol::wl_surface::WlSurface; +use toolkit::seat::pointer::{ThemeManager, ThemeSpec, ThemedPointer}; use wayland_client::protocol::wl_compositor::WlCompositor; use wayland_client::protocol::wl_data_device_manager::WlDataDeviceManager; use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::protocol::wl_shm::WlShm; +use wayland_client::{Attached, Main}; #[derive(Default)] struct Inner { @@ -66,16 +66,13 @@ impl Inner { fn handle_data_event(&mut self, event: DataDeviceEvent, inner: &Arc>) { match event { DataDeviceEvent::DataOffer { id } => { - id.implement_closure( - { - let inner = Arc::clone(inner); - move |event, offer| { - let mut inner = inner.lock().unwrap(); - inner.route_data_offer(event, offer); - } - }, - (), - ); + id.quick_assign({ + let inner = Arc::clone(inner); + move |offer, event, _dispatch_data| { + let mut inner = inner.lock().unwrap(); + inner.route_data_offer(event, offer.detach()); + } + }); } DataDeviceEvent::Enter { .. } | DataDeviceEvent::Leave { .. } @@ -96,10 +93,10 @@ impl Inner { pub struct PointerDispatcher { inner: Arc>, - pub(crate) data_device: WlDataDevice, - auto_pointer: AutoPointer, + pub(crate) data_device: Main, + auto_pointer: ThemedPointer, #[allow(dead_code)] - themer: AutoThemer, + themer: ThemeManager, } #[derive(Clone, Debug)] @@ -214,47 +211,29 @@ impl PendingMouse { impl PointerDispatcher { pub fn register( seat: &WlSeat, - compositor: WlCompositor, - shm: &WlShm, - dev_mgr: &WlDataDeviceManager, + compositor: Attached, + shm: Attached, + dev_mgr: Attached, ) -> anyhow::Result { let inner = Arc::new(Mutex::new(Inner::default())); - let pointer = seat - .get_pointer({ - let inner = Arc::clone(&inner); - move |ptr| { - ptr.implement_closure( - { - let inner = Arc::clone(&inner); - move |evt, _| { - inner.lock().unwrap().handle_event(evt); - } - }, - (), - ) - } - }) - .map_err(|()| anyhow!("Failed to configure pointer callback"))?; + let pointer = seat.get_pointer(); + pointer.quick_assign({ + let inner = Arc::clone(&inner); + move |_, evt, _| { + inner.lock().unwrap().handle_event(evt); + } + }); - let themer = AutoThemer::init(None, compositor, shm); - let auto_pointer = themer.theme_pointer(pointer); + let themer = ThemeManager::init(ThemeSpec::System, compositor, shm); + let auto_pointer = themer.theme_pointer(pointer.detach()); - let data_device = dev_mgr - .get_data_device(seat, { - let inner = Arc::clone(&inner); - move |device| { - device.implement_closure( - { - let inner = Arc::clone(&inner); - move |event, _device| { - inner.lock().unwrap().handle_data_event(event, &inner); - } - }, - (), - ) - } - }) - .map_err(|()| anyhow!("Failed to configure data_device"))?; + let data_device = dev_mgr.get_data_device(seat); + data_device.quick_assign({ + let inner = Arc::clone(&inner); + move |_device, event, _| { + inner.lock().unwrap().handle_data_event(event, &inner); + } + }); Ok(Self { inner, diff --git a/window/src/os/wayland/window.rs b/window/src/os/wayland/window.rs index b929427ab..849a4371b 100644 --- a/window/src/os/wayland/window.rs +++ b/window/src/os/wayland/window.rs @@ -22,61 +22,62 @@ use std::io::{Read, Write}; use std::os::unix::io::{AsRawFd, FromRawFd}; use std::rc::Rc; use std::sync::{Arc, Mutex}; +use toolkit::get_surface_scale_factor; use toolkit::reexports::client::protocol::wl_data_source::Event as DataSourceEvent; use toolkit::reexports::client::protocol::wl_surface::WlSurface; -use toolkit::utils::MemPool; -use toolkit::window::Event; +use toolkit::shm::MemPool; +use toolkit::window::{ButtonColorSpec, ColorSpec, ConceptConfig, ConceptFrame, Event}; +use wayland_client::protocol::wl_data_device_manager::WlDataDeviceManager; #[cfg(feature = "opengl")] -use wayland_client::egl::{is_available as egl_is_available, WlEglSurface}; - -struct MyTheme; -use toolkit::window::ButtonState; +use wayland_egl::{is_available as egl_is_available, WlEglSurface}; const DARK_GRAY: [u8; 4] = [0xff, 0x35, 0x35, 0x35]; const DARK_PURPLE: [u8; 4] = [0xff, 0x2b, 0x20, 0x42]; const PURPLE: [u8; 4] = [0xff, 0x3b, 0x30, 0x52]; const WHITE: [u8; 4] = [0xff, 0xff, 0xff, 0xff]; -const GRAY: [u8; 4] = [0x80, 0x80, 0x80, 0x80]; +const SILVER: [u8; 4] = [0xcc, 0xcc, 0xcc, 0xcc]; -impl toolkit::window::Theme for MyTheme { - fn get_primary_color(&self, active: bool) -> [u8; 4] { - if active { - DARK_PURPLE - } else { - DARK_GRAY - } - } +fn frame_config() -> ConceptConfig { + let icon = ButtonColorSpec { + hovered: ColorSpec::identical(WHITE.into()), + idle: ColorSpec { + active: PURPLE.into(), + inactive: SILVER.into(), + }, + disabled: ColorSpec::invisible(), + }; - fn get_secondary_color(&self, active: bool) -> [u8; 4] { - self.get_primary_color(active) - } + let close = Some(( + icon, + ButtonColorSpec { + hovered: ColorSpec::identical(PURPLE.into()), + idle: ColorSpec { + active: DARK_PURPLE.into(), + inactive: DARK_GRAY.into(), + }, + disabled: ColorSpec::invisible(), + }, + )); - fn get_close_button_color(&self, status: ButtonState) -> [u8; 4] { - match status { - ButtonState::Hovered => PURPLE, - ButtonState::Idle => DARK_PURPLE, - ButtonState::Disabled => DARK_GRAY, - } - } - fn get_maximize_button_color(&self, status: ButtonState) -> [u8; 4] { - self.get_close_button_color(status) - } - fn get_minimize_button_color(&self, status: ButtonState) -> [u8; 4] { - self.get_close_button_color(status) - } + ConceptConfig { + primary_color: ColorSpec { + active: DARK_PURPLE.into(), + inactive: DARK_GRAY.into(), + }, - fn get_close_button_icon_color(&self, status: ButtonState) -> [u8; 4] { - match status { - ButtonState::Hovered => WHITE, - ButtonState::Idle => GRAY, - ButtonState::Disabled => DARK_GRAY, - } - } - fn get_maximize_button_icon_color(&self, status: ButtonState) -> [u8; 4] { - self.get_close_button_icon_color(status) - } - fn get_minimize_button_icon_color(&self, status: ButtonState) -> [u8; 4] { - self.get_close_button_icon_color(status) + secondary_color: ColorSpec { + active: DARK_PURPLE.into(), + inactive: DARK_GRAY.into(), + }, + + close_button: close, + maximize_button: close, + minimize_button: close, + title_font: Some(("sans".into(), 17.0)), + title_color: ColorSpec { + active: WHITE.into(), + inactive: SILVER.into(), + }, } } @@ -85,7 +86,7 @@ pub struct WaylandWindowInner { callbacks: Box, surface: WlSurface, copy_and_paste: Arc>, - window: Option>, + window: Option>, pool: MemPool, dimensions: Dimensions, need_paint: bool, @@ -167,21 +168,24 @@ impl WaylandWindow { let window_id = conn.next_window_id(); let pending_event = Arc::new(Mutex::new(PendingEvent::default())); - let surface = conn.environment.borrow_mut().create_surface({ - let pending_event = Arc::clone(&pending_event); - move |dpi, surface| { - pending_event.lock().unwrap().dpi.replace(dpi); - log::debug!( - "surface id={} dpi scale changed to {}", - surface.as_ref().id(), - dpi - ); - WaylandConnection::with_window_inner(window_id, move |inner| { - inner.dispatch_pending_event(); - Ok(()) - }); - } - }); + let surface = conn + .environment + .borrow_mut() + .create_surface_with_scale_callback({ + let pending_event = Arc::clone(&pending_event); + move |dpi, surface, _dispatch_data| { + pending_event.lock().unwrap().dpi.replace(dpi); + log::debug!( + "surface id={} dpi scale changed to {}", + surface.as_ref().id(), + dpi + ); + WaylandConnection::with_window_inner(window_id, move |inner| { + inner.dispatch_pending_event(); + Ok(()) + }); + } + }); let dimensions = Dimensions { pixel_width: width, @@ -189,36 +193,37 @@ impl WaylandWindow { dpi: 96, }; - let mut window = toolkit::window::Window::::init_from_env( - &*conn.environment.borrow(), - surface.clone(), - ( - dimensions.pixel_width as u32, - dimensions.pixel_height as u32, - ), - { - let pending_event = Arc::clone(&pending_event); - move |evt| { - if pending_event.lock().unwrap().queue(evt) { - WaylandConnection::with_window_inner(window_id, move |inner| { - inner.dispatch_pending_event(); - Ok(()) - }); + let mut window = conn + .environment + .borrow() + .create_window::( + surface.clone(), + ( + dimensions.pixel_width as u32, + dimensions.pixel_height as u32, + ), + { + let pending_event = Arc::clone(&pending_event); + move |evt, mut _dispatch_data| { + if pending_event.lock().unwrap().queue(evt) { + WaylandConnection::with_window_inner(window_id, move |inner| { + inner.dispatch_pending_event(); + Ok(()) + }); + } } - } - }, - ) - .context("Failed to create window")?; + }, + ) + .context("Failed to create window")?; window.set_app_id(class_name.to_string()); - window.set_decorate(true); window.set_resizable(true); window.set_title(name.to_string()); - window.set_theme(MyTheme {}); + window.set_frame_config(frame_config()); - let pool = MemPool::new(&conn.environment.borrow().shm, || {})?; + let pool = MemPool::new(conn.environment.borrow().require_global(), |_| {})?; - window.new_seat(&conn.seat); + // window.new_seat(&conn.seat); conn.keyboard.add_window(window_id, &surface); let copy_and_paste = CopyAndPaste::create(); @@ -437,7 +442,7 @@ impl WaylandWindowInner { if let Some((w, h)) = pending.configure.take() { if self.window.is_some() { - let factor = toolkit::surface::get_dpi_factor(&self.surface); + let factor = get_surface_scale_factor(&self.surface); let pixel_width = self.surface_to_pixels(w.try_into().unwrap()); let pixel_height = self.surface_to_pixels(h.try_into().unwrap()); @@ -472,6 +477,7 @@ impl WaylandWindowInner { } self.refresh_frame(); + self.need_paint = true; self.do_paint().unwrap(); } } @@ -483,6 +489,7 @@ impl WaylandWindowInner { fn refresh_frame(&mut self) { if let Some(window) = self.window.as_mut() { window.refresh(); + self.surface.commit(); } } @@ -502,7 +509,6 @@ impl WaylandWindowInner { frame.finish()?; // self.damage(); self.refresh_frame(); - self.surface.commit(); self.need_paint = false; return Ok(()); } @@ -783,26 +789,22 @@ impl WindowOps for WaylandWindow { WaylandConnection::with_window_inner(self.0, move |inner| { let text = text.clone(); let conn = Connection::get().unwrap().wayland(); + let source = conn .environment .borrow() - .data_device_manager - .create_data_source(move |source| { - source.implement_closure( - move |event, _source| { - if let DataSourceEvent::Send { fd, .. } = event { - let fd = unsafe { FileDescriptor::from_raw_fd(fd) }; - if let Err(e) = write_pipe_with_timeout(fd, text.as_bytes()) { - log::error!("while sending paste to pipe: {}", e); - } - } - }, - (), - ) - }) - .map_err(|()| anyhow!("failed to create data source"))?; + .require_global::() + .create_data_source(); + source.quick_assign(move |_source, event, _dispatch_data| { + if let DataSourceEvent::Send { fd, .. } = event { + let fd = unsafe { FileDescriptor::from_raw_fd(fd) }; + if let Err(e) = write_pipe_with_timeout(fd, text.as_bytes()) { + log::error!("while sending paste to pipe: {}", e); + } + } + }); source.offer(TEXT_MIME_TYPE.to_string()); - inner.copy_and_paste.lock().unwrap().set_selection(source); + inner.copy_and_paste.lock().unwrap().set_selection(&source); Ok(()) }) @@ -887,7 +889,13 @@ impl WindowOpsMut for WaylandWindowInner { } let conn = Connection::get().unwrap().wayland(); - if !conn.environment.borrow().shell.needs_configure() { + if !conn + .environment + .borrow() + .get_shell() + .unwrap() + .needs_configure() + { self.do_paint().unwrap(); } else { self.refresh_frame(); diff --git a/window/src/os/x11/keyboard.rs b/window/src/os/x11/keyboard.rs index 19445bede..ea835ebe3 100644 --- a/window/src/os/x11/keyboard.rs +++ b/window/src/os/x11/keyboard.rs @@ -51,7 +51,7 @@ impl Keyboard { device_id, xkb::KEYMAP_COMPILE_NO_FLAGS, ); - let state = xkb::x11::state_new_from_device(&keymap, &connection, device_id); + let state = xkb::x11::state_new_from_device(&keymap, connection, device_id); let locale = query_lc_ctype()?; @@ -225,7 +225,7 @@ impl Keyboard { "problem with new keymap" ); - let new_state = xkb::x11::state_new_from_device(&new_keymap, &connection, self.device_id); + let new_state = xkb::x11::state_new_from_device(&new_keymap, connection, self.device_id); ensure!(!new_state.get_raw_ptr().is_null(), "problem with new state"); self.state.replace(new_state); diff --git a/window/src/spawn.rs b/window/src/spawn.rs index 47e7aa2ce..60e13e0e0 100644 --- a/window/src/spawn.rs +++ b/window/src/spawn.rs @@ -169,6 +169,10 @@ impl SpawnQueue { self.has_any_queued() } + + pub(crate) fn raw_fd(&self) -> std::os::unix::io::RawFd { + self.read.lock().unwrap().as_raw_fd() + } } #[cfg(all(unix, not(target_os = "macos")))] @@ -180,7 +184,7 @@ impl Evented for SpawnQueue { interest: Ready, opts: PollOpt, ) -> std::io::Result<()> { - EventedFd(&self.read.lock().unwrap().as_raw_fd()).register(poll, token, interest, opts) + EventedFd(&self.raw_fd()).register(poll, token, interest, opts) } fn reregister( @@ -190,11 +194,11 @@ impl Evented for SpawnQueue { interest: Ready, opts: PollOpt, ) -> std::io::Result<()> { - EventedFd(&self.read.lock().unwrap().as_raw_fd()).reregister(poll, token, interest, opts) + EventedFd(&self.raw_fd()).reregister(poll, token, interest, opts) } fn deregister(&self, poll: &Poll) -> std::io::Result<()> { - EventedFd(&self.read.lock().unwrap().as_raw_fd()).deregister(poll) + EventedFd(&self.raw_fd()).deregister(poll) } }