diff --git a/Cargo.lock b/Cargo.lock index fcb8703d..ef45c76b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,18 +77,18 @@ dependencies = [ [[package]] name = "bincode" -version = "2.0.0-beta.2" +version = "2.0.0-beta.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b09eea9771bcc3c1d3f21325c51066859dbb9addfad6325273630d9ea1707d" +checksum = "d20beb3a5bf7c149e1c9cfe54a53eab9c31e7f00ab74e734e05272c65be4b61f" dependencies = [ "bincode_derive", ] [[package]] name = "bincode_derive" -version = "2.0.0-beta.2" +version = "2.0.0-beta.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebde72585aa64e130b4c1d3612742253e7c486f25cb299b4373e505130519279" +checksum = "cb2390a472627bfe18ac250746d32a47d91c4720faacc3d21147298d67063e49" dependencies = [ "virtue", ] @@ -116,12 +116,45 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cairo-rs" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b869e97a87170f96762f9f178eae8c461147e722ba21dd8814105bf5716bf14a" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "cc" version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +[[package]] +name = "cfg-expr" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295b6eb918a60a25fec0b23a5e633e74fddbaf7bb04411e65a10c366aca4b5cd" +dependencies = [ + "smallvec", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -284,6 +317,68 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +[[package]] +name = "glib" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dcfbdb6cc6c02aee163339465d8a40d6f3f64c3a43f729a4195f0e153338b7" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58b262ff65ef771003873cea8c10e0fe854f1c508d48d62a4111a1ff163f7d1" +dependencies = [ + "anyhow", + "heck", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa1d4e1a63d8574541e5b92931e4e669ddc87ffa85d58e84e631dba13ad2e10c" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6859463843c20cf3837e3a9069b6ab2051aeeadf4c899d33344f4aea83189a" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -310,6 +405,7 @@ dependencies = [ "bitflags", "bstr", "byteorder", + "cairo-rs", "chrono", "default-config", "env_logger", @@ -321,6 +417,8 @@ dependencies = [ "num-derive", "num-traits", "once_cell", + "pango", + "pangocairo", "rand", "renderdoc", "repc", @@ -437,6 +535,58 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +[[package]] +name = "pango" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79211eff430c29cc38c69e0ab54bc78fa1568121ca9737707eee7f92a8417a94" +dependencies = [ + "bitflags", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7022c2fb88cd2d9d55e1a708a8c53a3ae8678234c4a54bf623400aeb7f31fac2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "pangocairo" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7876a45c1f1d1a75a2601dc6d9ef2cb5a8be0e3d76f909d82450759929035366" +dependencies = [ + "bitflags", + "cairo-rs", + "glib", + "libc", + "pango", + "pangocairo-sys", +] + +[[package]] +name = "pangocairo-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cf746594916c81d5f739af9335c5f55a1f4606d80b3e1d821f18cf95a29494" +dependencies = [ + "cairo-sys-rs", + "glib-sys", + "libc", + "pango-sys", + "system-deps", +] + [[package]] name = "pin-project-lite" version = "0.2.8" @@ -449,12 +599,52 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + [[package]] name = "ppv-lite86" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "proc-macro-crate" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dada8c9981fcf32929c3c0f0cd796a9284aca335565227ed88c83babb1d43dc" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.36" @@ -568,6 +758,12 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" + [[package]] name = "slab" version = "0.4.5" @@ -591,6 +787,19 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "system-deps" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + [[package]] name = "termcolor" version = "1.1.2" @@ -631,6 +840,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "uapi" version = "0.2.7" @@ -663,6 +881,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "version-compare" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" + [[package]] name = "version_check" version = "0.9.4" @@ -671,9 +895,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "virtue" -version = "0.0.4" +version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e85ed1066abcc0ea331cce3ce83cccf30ae9900529ca46f353b22ca79b56b8" +checksum = "757cfbfe0d17ee6f22fe97e536d463047d451b47cf9d11e2b7d1398b0ef274dd" [[package]] name = "wasi" diff --git a/Cargo.toml b/Cargo.toml index cfb23c6c..f184d697 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,10 @@ smallvec = { version = "1.8.0", features = ["const_generics", "const_new", "unio backtrace = "0.3.64" byteorder = "1.4.3" chrono = "0.4.19" -bincode = "2.0.0-beta.2" +bincode = "2.0.0-beta.3" +pangocairo = "0.15.1" +cairo-rs = { version = "0.15.1", features = ["png"] } +pango = { version = "0.15.2", features = ["v1_44"] } i4config = { path = "i4config" } default-config = { path = "default-config" } diff --git a/default-config/src/lib.rs b/default-config/src/lib.rs index f50006ec..00cbe3cd 100644 --- a/default-config/src/lib.rs +++ b/default-config/src/lib.rs @@ -1,8 +1,8 @@ use i4config::embedded::grab_keyboard; use i4config::keyboard::mods::{Modifiers, ALT, CTRL, SHIFT}; use i4config::keyboard::syms::{ - SYM_Super_L, SYM_b, SYM_comma, SYM_d, SYM_h, SYM_j, SYM_k, SYM_l, SYM_period, SYM_r, SYM_t, - SYM_v, SYM_y, + SYM_Super_L, SYM_b, SYM_comma, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_period, SYM_r, + SYM_t, SYM_v, SYM_y, }; use i4config::theme::{get_title_height, set_title_color, set_title_height, Color}; use i4config::Axis::{Horizontal, Vertical}; @@ -54,6 +54,10 @@ fn configure_seat(s: Seat) { s.set_split(s.split().other()); }); + s.bind(MOD | SYM_f, move || { + s.focus_parent(); + }); + s.bind(MOD | SHIFT | SYM_h, move || s.move_(Left)); s.bind(MOD | SHIFT | SYM_j, move || s.move_(Down)); s.bind(MOD | SHIFT | SYM_k, move || s.move_(Up)); diff --git a/i4config/Cargo.toml b/i4config/Cargo.toml index 3a51bc4b..32399fc9 100644 --- a/i4config/Cargo.toml +++ b/i4config/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] -bincode = "2.0.0-beta.2" +bincode = "2.0.0-beta.3" log = "0.4.14" diff --git a/i4config/src/_private/client.rs b/i4config/src/_private/client.rs index f93d7459..fbf49e03 100644 --- a/i4config/src/_private/client.rs +++ b/i4config/src/_private/client.rs @@ -250,6 +250,10 @@ impl Client { self.send(&ClientMessage::CreateSplit { seat, axis }); } + pub fn focus_parent(&self, seat: Seat) { + self.send(&ClientMessage::FocusParent { seat }); + } + pub fn create_seat(&self, name: &str) -> Seat { let response = self.with_response(|| self.send(&ClientMessage::CreateSeat { name })); match response { diff --git a/i4config/src/_private/ipc.rs b/i4config/src/_private/ipc.rs index 25c08e70..d1cbdb71 100644 --- a/i4config/src/_private/ipc.rs +++ b/i4config/src/_private/ipc.rs @@ -119,6 +119,9 @@ pub enum ClientMessage<'a> { seat: Seat, axis: Axis, }, + FocusParent { + seat: Seat, + }, } #[derive(Encode, Decode, Debug)] diff --git a/i4config/src/lib.rs b/i4config/src/lib.rs index 5e8d0df9..88c2f7eb 100644 --- a/i4config/src/lib.rs +++ b/i4config/src/lib.rs @@ -134,6 +134,10 @@ impl Seat { pub fn create_split(self, axis: Axis) { get!().create_split(self, axis); } + + pub fn focus_parent(self) { + get!().focus_parent(self); + } } pub fn get_seats() -> Vec { diff --git a/src/async_engine.rs b/src/async_engine.rs index e1576ca8..b2d3a808 100644 --- a/src/async_engine.rs +++ b/src/async_engine.rs @@ -23,6 +23,14 @@ pub enum AsyncError { EventLoopError(#[from] EventLoopError), } +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum Phase { + EventHandling, + Layout, + PostLayout, +} +const NUM_PHASES: usize = 3; + pub struct AsyncEngine { wheel: Rc, el: Rc, @@ -56,7 +64,15 @@ impl AsyncEngine { } pub fn spawn + 'static>(&self, f: F) -> SpawnedFuture { - self.queue.spawn(f) + self.queue.spawn(Phase::EventHandling, f) + } + + pub fn spawn2 + 'static>( + &self, + phase: Phase, + f: F, + ) -> SpawnedFuture { + self.queue.spawn(phase, f) } pub fn fd(self: &Rc, fd: &Rc) -> Result { @@ -171,6 +187,7 @@ mod timeout { mod task { use crate::async_engine::queue::DispatchQueue; + use crate::async_engine::Phase; use crate::utils::ptr_ext::{MutPtrExt, PtrExt}; use crate::NumCell; use std::cell::{Cell, UnsafeCell}; @@ -253,6 +270,7 @@ mod task { struct Task> { ref_count: NumCell, + phase: Phase, state: NumCell, data: UnsafeCell>, waker: Cell>, @@ -282,9 +300,14 @@ mod task { } impl DispatchQueue { - pub(super) fn spawn>(self: &Rc, f: F) -> SpawnedFuture { + pub(super) fn spawn>( + self: &Rc, + phase: Phase, + f: F, + ) -> SpawnedFuture { let f = Box::new(Task { ref_count: NumCell::new(1), + phase, state: NumCell::new(0), data: UnsafeCell::new(TaskData { future: ManuallyDrop::new(f), @@ -354,10 +377,13 @@ mod task { self.state.or_assign(RUNNING); self.inc_ref_count(); let data = self as *const _ as _; - self.queue.push(Runnable { - data, - run: Self::run_proxy, - }); + self.queue.push( + Runnable { + data, + run: Self::run_proxy, + }, + self.phase, + ); } else { self.state.or_assign(RUN_AGAIN); } @@ -404,13 +430,15 @@ mod task { mod queue { use crate::async_engine::task::Runnable; - use crate::async_engine::AsyncError; + use crate::async_engine::{AsyncError, Phase, NUM_PHASES}; use crate::event_loop::{EventLoop, EventLoopDispatcher, EventLoopId}; + use crate::utils::array; use crate::utils::numcell::NumCell; use std::cell::{Cell, RefCell}; use std::collections::VecDeque; use std::error::Error; use std::mem; + use std::ops::DerefMut; use std::rc::Rc; pub(super) struct Dispatcher { @@ -425,7 +453,8 @@ mod queue { id, el: el.clone(), dispatch_scheduled: Cell::new(false), - queue: RefCell::new(Default::default()), + num_queued: Default::default(), + queues: array::from_fn(|_| Default::default()), iteration: Default::default(), }); let slf = Rc::new(Dispatcher { @@ -439,15 +468,20 @@ mod queue { impl EventLoopDispatcher for Dispatcher { fn dispatch(self: Rc, _events: i32) -> Result<(), Box> { - loop { + let mut stash = self.stash.borrow_mut(); + while self.queue.num_queued.get() > 0 { self.queue.iteration.fetch_add(1); - let mut stash = self.stash.borrow_mut(); - mem::swap(&mut *stash, &mut *self.queue.queue.borrow_mut()); - if stash.is_empty() { - break; - } - for runnable in stash.drain(..) { - runnable.run(); + let mut phase = 0; + while phase < NUM_PHASES as usize { + mem::swap(&mut *stash, &mut *self.queue.queues[phase].borrow_mut()); + if stash.is_empty() { + phase += 1; + continue; + } + self.queue.num_queued.fetch_sub(stash.len()); + for runnable in stash.drain(..) { + runnable.run(); + } } } self.queue.dispatch_scheduled.set(false); @@ -458,7 +492,9 @@ mod queue { impl Drop for Dispatcher { fn drop(&mut self) { let _ = self.queue.el.remove(self.queue.id); - mem::take(&mut *self.queue.queue.borrow_mut()); + for queue in &self.queue.queues { + mem::take(queue.borrow_mut().deref_mut()); + } } } @@ -466,13 +502,15 @@ mod queue { dispatch_scheduled: Cell, id: EventLoopId, el: Rc, - queue: RefCell>, + num_queued: NumCell, + queues: [RefCell>; NUM_PHASES], iteration: NumCell, } impl DispatchQueue { - pub fn push(&self, runnable: Runnable) { - self.queue.borrow_mut().push_back(runnable); + pub fn push(&self, runnable: Runnable, phase: Phase) { + self.queues[phase as usize].borrow_mut().push_back(runnable); + self.num_queued.fetch_add(1); if !self.dispatch_scheduled.get() { let _ = self.el.schedule(self.id); self.dispatch_scheduled.set(true); diff --git a/src/client/tasks.rs b/src/client/tasks.rs index ff157afd..c0444976 100644 --- a/src/client/tasks.rs +++ b/src/client/tasks.rs @@ -2,7 +2,7 @@ use crate::client::{Client, ClientError}; use crate::object::ObjectId; use crate::utils::buffd::{BufFdIn, BufFdOut, MsgParser}; use crate::utils::vec_ext::VecExt; -use crate::ErrorFmt; +use crate::{ErrorFmt, Phase}; use futures::{select, FutureExt}; use std::collections::VecDeque; use std::mem; @@ -12,7 +12,7 @@ pub async fn client(data: Rc) { let mut recv = data.state.eng.spawn(receive(data.clone())).fuse(); let mut dispatch_fr = data.state.eng.spawn(dispatch_fr(data.clone())).fuse(); let mut shutdown = data.shutdown.triggered().fuse(); - let _send = data.state.eng.spawn(send(data.clone())); + let _send = data.state.eng.spawn2(Phase::PostLayout, send(data.clone())); select! { _ = recv => { }, _ = dispatch_fr => { }, diff --git a/src/config/handler.rs b/src/config/handler.rs index 9699a7eb..20710153 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1,7 +1,8 @@ use crate::backend::{KeyboardId, MouseId}; use crate::ifs::wl_seat::{SeatId, WlSeatGlobal}; use crate::state::DeviceHandlerData; -use crate::tree::ContainerSplit; +use crate::tree::walker::visit_containers; +use crate::tree::{ContainerSplit, Node}; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::debug_fn::debug_fn; use crate::utils::stack::Stack; @@ -335,6 +336,12 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_focus_parent(&self, seat: Seat) -> Result<(), FocusParentError> { + let seat = self.get_seat(seat)?; + seat.focus_parent(); + Ok(()) + } + fn handle_set_title_height(&self, height: i32) -> Result<(), SetTitleHeightError> { if height < 0 { return Err(SetTitleHeightError::Negative(height)); @@ -343,9 +350,10 @@ impl ConfigProxyHandler { return Err(SetTitleHeightError::Excessive(height)); } self.state.theme.title_height.set(height); - self.state.theme.version.fetch_add(1); - self.state.root.needs_layout.set(true); - self.state.pending_layout.push(self.state.root.clone()); + self.state + .root + .clone() + .visit(&mut visit_containers(|c| c.on_theme_changed())); Ok(()) } @@ -357,9 +365,10 @@ impl ConfigProxyHandler { return Err(SetBorderWidthError::Excessive(width)); } self.state.theme.border_width.set(width); - self.state.theme.version.fetch_add(1); - self.state.root.needs_layout.set(true); - self.state.pending_layout.push(self.state.root.clone()); + self.state + .root + .clone() + .visit(&mut visit_containers(|c| c.on_theme_changed())); Ok(()) } @@ -434,6 +443,7 @@ impl ConfigProxyHandler { ClientMessage::GetTitleHeight => self.handle_get_title_height(), ClientMessage::GetBorderWidth => self.handle_get_border_width(), ClientMessage::CreateSplit { seat, axis } => self.handle_create_split(seat, axis)?, + ClientMessage::FocusParent { seat } => self.handle_focus_parent(seat)?, } Ok(()) } @@ -467,6 +477,8 @@ enum CphError { GrabError(#[from] GrabError), #[error("Could not process a `create_split` request")] CreateSplitError(#[from] CreateSplitError), + #[error("Could not process a `focus_parent` request")] + FocusParentError(#[from] FocusParentError), #[error("Could not process a `set_title_height` request")] SetTitleHeightError(#[from] SetTitleHeightError), #[error("Could not process a `set_border_width` request")] @@ -591,3 +603,10 @@ enum CreateSplitError { CphError(#[from] Box), } efrom!(CreateSplitError, CphError); + +#[derive(Debug, Error)] +enum FocusParentError { + #[error(transparent)] + CphError(#[from] Box), +} +efrom!(FocusParentError, CphError); diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 30a3a7c7..4a6d9486 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -199,6 +199,10 @@ impl WlSeatGlobal { self.keyboard_node.get().create_split(axis) } + pub fn focus_parent(self: &Rc) { + self.keyboard_node.get().focus_parent(self); + } + pub fn get_rate(&self) -> (i32, i32) { self.repeat_rate.get() } diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 660f2a40..afe1dc31 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -212,12 +212,12 @@ impl WlSeatGlobal { } fn focus_xdg_surface(self: &Rc, xdg: &Rc) { - self.focus_surface(&xdg.focus_surface(self)); + self.focus_node(xdg.focus_surface(self)); } - fn focus_surface(self: &Rc, surface: &Rc) { + pub fn focus_node(self: &Rc, node: Rc) { let old = self.keyboard_node.get(); - if old.id() == surface.node_id { + if old.id() == node.id() { return; } old.unfocus(self); @@ -225,36 +225,11 @@ impl WlSeatGlobal { old.active_changed(false); } - if surface.seat_state().focus(self) { - surface.active_changed(true); - } - surface.clone().focus(self); - self.keyboard_node.set(surface.clone()); - - let pressed_keys: Vec<_> = self.pressed_keys.borrow().iter().copied().collect(); - let serial = self.serial.fetch_add(1); - self.surface_kb_event(0, &surface, |k| { - k.send_enter(serial, surface.id, &pressed_keys) - }); - let ModifierState { - mods_depressed, - mods_latched, - mods_locked, - group, - .. - } = self.kb_state.borrow().mods(); - let serial = self.serial.fetch_add(1); - self.surface_kb_event(0, &surface, |k| { - k.send_modifiers(serial, mods_depressed, mods_latched, mods_locked, group) - }); - - if old.client_id() != Some(surface.client.id) { - self.offer_selection::(&self.selection, &surface.client); - self.offer_selection::( - &self.primary_selection, - &surface.client, - ); + if node.seat_state().focus(self) { + node.active_changed(true); } + node.clone().focus(self); + self.keyboard_node.set(node.clone()); } fn offer_selection( @@ -405,7 +380,7 @@ impl WlSeatGlobal { self.surface_pointer_event(0, surface, |p| p.send_button(serial, 0, button, state)); self.surface_pointer_frame(surface); if pressed && surface.belongs_to_toplevel() { - self.focus_surface(surface); + self.focus_node(surface.clone()); } } } @@ -463,6 +438,36 @@ impl WlSeatGlobal { } } +// Focus callbacks +impl WlSeatGlobal { + pub fn focus_surface(&self, surface: &WlSurface) { + let pressed_keys: Vec<_> = self.pressed_keys.borrow().iter().copied().collect(); + let serial = self.serial.fetch_add(1); + self.surface_kb_event(0, &surface, |k| { + k.send_enter(serial, surface.id, &pressed_keys) + }); + let ModifierState { + mods_depressed, + mods_latched, + mods_locked, + group, + .. + } = self.kb_state.borrow().mods(); + let serial = self.serial.fetch_add(1); + self.surface_kb_event(0, &surface, |k| { + k.send_modifiers(serial, mods_depressed, mods_latched, mods_locked, group) + }); + + if self.keyboard_node.get().client_id() != Some(surface.client.id) { + self.offer_selection::(&self.selection, &surface.client); + self.offer_selection::( + &self.primary_selection, + &surface.client, + ); + } + } +} + // Key callbacks impl WlSeatGlobal { pub fn key_surface(&self, surface: &WlSurface, key: u32, state: u32) { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 28f9a690..0b65d46e 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -16,6 +16,7 @@ use crate::object::Object; use crate::pixman::Region; use crate::rect::Rect; use crate::render::Renderer; +use crate::tree::walker::NodeVisitor; use crate::tree::{ContainerSplit, Node, NodeId}; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::utils::clonecell::CloneCell; @@ -578,6 +579,12 @@ dedicated_add_obj!(WlSurface, WlSurfaceId, surfaces); tree_id!(SurfaceNodeId); impl Node for WlSurface { + fn focus_parent(&self, seat: &Rc) { + if let Some(xdg) = self.xdg.get() { + xdg.focus_parent(seat); + } + } + fn id(&self) -> NodeId { self.node_id.into() } @@ -658,8 +665,9 @@ impl Node for WlSurface { fn focus(self: Rc, seat: &Rc) { if let Some(xdg) = self.xdg.get() { - xdg.focus_surface.insert(seat.id(), self); + xdg.focus_surface.insert(seat.id(), self.clone()); } + seat.focus_surface(&self); } fn unfocus(&self, seat: &WlSeatGlobal) { @@ -705,6 +713,19 @@ impl Node for WlSurface { fn dnd_motion(&self, dnd: &Dnd, x: Fixed, y: Fixed) { dnd.seat.dnd_surface_motion(self, dnd, x, y); } + + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_surface(&self); + } + + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { + let children = self.children.borrow_mut(); + if let Some(c) = children.deref() { + for child in c.subsurfaces.values() { + visitor.visit_surface(&child.surface); + } + } + } } #[derive(Debug, Error)] diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index 75f07412..4725c2e8 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -75,6 +75,10 @@ struct PendingXdgSurfaceData { } pub trait XdgSurfaceExt: Debug { + fn focus_parent(&self, seat: &Rc) { + let _ = seat; + } + fn get_split(&self) -> Option { None } @@ -203,6 +207,12 @@ impl XdgSurface { Ok(()) } + pub fn focus_parent(&self, seat: &Rc) { + if let Some(ext) = self.ext.get() { + ext.focus_parent(seat); + } + } + pub fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc { self.focus_surface .get(&seat.id()) diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index 2aa5b9bc..2476b6a3 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -8,6 +8,7 @@ use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; use crate::render::Renderer; +use crate::tree::walker::NodeVisitor; use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode}; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -272,6 +273,14 @@ impl Node for XdgPopup { self.xdg.seat_state.destroy_node(self); } + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_popup(&self); + } + + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { + visitor.visit_surface(&self.xdg.surface); + } + fn absolute_position(&self) -> Rect { self.xdg.absolute_desired_extents.get() } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index f2902657..b1e0da07 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -8,6 +8,7 @@ use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; use crate::render::Renderer; +use crate::tree::walker::NodeVisitor; use crate::tree::{ContainerNode, ContainerSplit, FindTreeResult}; use crate::tree::{FloatNode, FoundNode, Node, NodeId, ToplevelNodeId, WorkspaceNode}; use crate::utils::buffd::MsgParser; @@ -81,6 +82,7 @@ pub struct XdgToplevel { min_height: Cell>, max_width: Cell>, max_height: Cell>, + title: RefCell, pub tracker: Tracker, } @@ -113,6 +115,7 @@ impl XdgToplevel { min_height: Cell::new(None), max_width: Cell::new(None), max_height: Cell::new(None), + title: RefCell::new("".to_string()), tracker: Default::default(), } } @@ -210,7 +213,13 @@ impl XdgToplevel { } fn set_title(&self, parser: MsgParser<'_, '_>) -> Result<(), SetTitleError> { - let _req: SetTitle = self.xdg.surface.client.parse(self, parser)?; + let req: SetTitle = self.xdg.surface.client.parse(self, parser)?; + let mut title = self.title.borrow_mut(); + title.clear(); + title.push_str(req.title); + if let Some(parent) = self.parent_node.get() { + parent.child_title_changed(self, &title); + } Ok(()) } @@ -302,6 +311,16 @@ impl XdgToplevel { Ok(()) } + fn notify_parent(&self) { + let parent = match self.parent_node.get() { + Some(p) => p, + _ => return, + }; + let extents = self.xdg.extents.get(); + parent.child_size_changed(self, extents.width(), extents.height()); + parent.child_title_changed(self, self.title.borrow_mut().deref()); + } + fn map_child(self: &Rc, parent: &XdgToplevel) { let workspace = match parent.xdg.workspace.get() { Some(w) => w, @@ -311,8 +330,8 @@ impl XdgToplevel { let output = workspace.output.get(); let output_rect = output.position.get(); log::info!("or = {:?}", output_rect); + let extents = self.xdg.extents.get(); let position = { - let extents = self.xdg.extents.get().to_origin(); let width = extents.width(); let height = extents.height(); let mut x1 = output_rect.x1(); @@ -345,6 +364,7 @@ impl XdgToplevel { floater .workspace_link .set(Some(workspace.stacked.add_last(floater.clone()))); + self.notify_parent(); } fn map_tiled(self: &Rc) { @@ -442,6 +462,14 @@ impl Node for XdgToplevel { self.xdg.seat_state.destroy_node(self) } + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_toplevel(&self); + } + + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { + visitor.visit_surface(&self.xdg.surface); + } + fn do_focus(self: Rc, seat: &Rc, _direction: Direction) { seat.focus_toplevel(&self); } @@ -488,6 +516,10 @@ impl Node for XdgToplevel { } impl XdgSurfaceExt for XdgToplevel { + fn focus_parent(&self, seat: &Rc) { + self.parent_node.get().map(|p| p.focus_self(seat)); + } + fn get_split(&self) -> Option { self.parent_node.get().and_then(|p| p.get_split()) } @@ -514,6 +546,7 @@ impl XdgSurfaceExt for XdgToplevel { )); self.parent_node.set(Some(cn.clone())); pn.replace_child(&*self, cn); + self.notify_parent(); } fn move_focus(self: Rc, seat: &Rc, direction: Direction) { @@ -574,9 +607,8 @@ impl XdgSurfaceExt for XdgToplevel { } fn extents_changed(&self) { - if let Some(parent) = self.parent_node.get() { - let extents = self.xdg.extents.get(); - parent.child_size_changed(self, extents.width(), extents.height()); + self.notify_parent(); + if self.parent_node.get().is_some() { self.xdg.surface.client.state.tree_changed(); } } diff --git a/src/main.rs b/src/main.rs index 892da8e7..0f6e21c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ )] use crate::acceptor::AcceptorError; -use crate::async_engine::AsyncError; +use crate::async_engine::{AsyncError, Phase}; use crate::backends::dummy::DummyBackend; use crate::backends::xorg::{XorgBackend, XorgBackendError}; use crate::client::Clients; @@ -26,7 +26,7 @@ use crate::ifs::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1Global; use crate::render::RenderError; use crate::sighand::SighandError; use crate::state::State; -use crate::tree::{DisplayNode, NodeIds}; +use crate::tree::{container_layout, DisplayNode, NodeIds, render_titles}; use crate::utils::clonecell::CloneCell; use crate::utils::errorfmt::ErrorFmt; use crate::utils::numcell::NumCell; @@ -71,6 +71,7 @@ mod servermem; mod sighand; mod state; mod tasks; +mod text; mod theme; mod time; mod tree; @@ -155,7 +156,8 @@ fn main_() -> Result<(), MainError> { mouse_ids: Default::default(), kb_handlers: Default::default(), theme: Default::default(), - pending_layout: Default::default(), + pending_container_layout: Default::default(), + pending_container_titles: Default::default(), }); forker.install(&state); let backend = XorgBackend::new(&state)?; @@ -164,7 +166,8 @@ fn main_() -> Result<(), MainError> { state.config.set(Some(Rc::new(config))); let _global_event_handler = engine.spawn(tasks::handle_backend_events(state.clone())); let _slow_client_handler = engine.spawn(tasks::handle_slow_clients(state.clone())); - let _do_layout = engine.spawn(tasks::do_layout(state.clone())); + let _do_layout = engine.spawn2(Phase::Layout, container_layout(state.clone())); + let _render_titles = engine.spawn2(Phase::PostLayout, render_titles(state.clone())); let socket_path = Acceptor::install(&state)?; forker.setenv(b"WAYLAND_DISPLAY", socket_path.as_bytes()); el.run()?; diff --git a/src/rect.rs b/src/rect.rs index ef7eca1d..0d0a7882 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -115,6 +115,7 @@ impl Rect { self.x1 == self.x2 || self.y1 == self.y2 } + #[allow(dead_code)] pub fn to_origin(&self) -> Self { Self { x1: 0, diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 0436127b..de088f77 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -1,4 +1,4 @@ -use crate::format::Format; +use crate::format::{Format, ARGB8888}; use crate::ifs::wl_buffer::WlBuffer; use crate::ifs::wl_surface::xdg_surface::XdgSurface; use crate::ifs::wl_surface::WlSurface; @@ -16,7 +16,7 @@ use crate::render::Texture; use crate::tree::{ ContainerFocus, ContainerNode, ContainerSplit, FloatNode, OutputNode, WorkspaceNode, }; -use crate::State; +use crate::{State}; use std::ops::Deref; use std::rc::Rc; use std::slice; @@ -34,7 +34,7 @@ fn focus_color(focus: ContainerFocus) -> (f32, f32, f32) { } pub struct Renderer<'a> { - pub(super) ctx: &'a RenderContext, + pub(super) ctx: &'a Rc, pub(super) fb: &'a GlFrameBuffer, pub(super) state: &'a State, } @@ -111,6 +111,7 @@ impl Renderer<'_> { let title_rect = Rect::new_sized(x, y, container.width.get(), title_height).unwrap(); let underline_rect = Rect::new_sized(x, y + title_height, container.width.get(), 1).unwrap(); + let mut titles = vec![]; if let Some(child) = container.mono_child.get() { let space_per_child = cwidth / num_children as i32; let mut rem = cwidth % num_children as i32; @@ -127,6 +128,13 @@ impl Renderer<'_> { rem -= 1; width += 1; } + if let Some(title) = child.title_texture.get() { + titles.push(( + pos, + 0, + title, + )); + } if focus != ContainerFocus::None { let rect = Rect::new_sized(pos, y, width, title_height).unwrap(); self.fill_boxes(slice::from_ref(&rect), r, g, b, 1.0); @@ -154,6 +162,13 @@ impl Renderer<'_> { underline_rects.push(underline_rect); for (i, child) in container.children.iter().enumerate() { let body = child.body.get(); + if let Some(title) = child.title_texture.get() { + titles.push(( + body.x1(), + body.y1() - title_height - 1, + title, + )); + } if child.active.get() { active_rects.push( Rect::new_sized( @@ -162,7 +177,7 @@ impl Renderer<'_> { body.width(), title_height, ) - .unwrap(), + .unwrap(), ); } if i + 1 < num_children { @@ -208,6 +223,9 @@ impl Renderer<'_> { self.fill_boxes(&underline_rects, c.r, c.g, c.b, c.a); let c = self.state.theme.border_color.get(); self.fill_boxes(&border_rects, c.r, c.g, c.b, c.a); + for (tx, ty, tex) in titles { + self.render_texture(&tex, x + tx, y + ty, ARGB8888); + } } for child in container.children.iter() { let body = child.body.get(); diff --git a/src/state.rs b/src/state.rs index 38f1c10c..4e288176 100644 --- a/src/state.rs +++ b/src/state.rs @@ -14,7 +14,7 @@ use crate::ifs::wl_seat::{SeatIds, WlSeatGlobal}; use crate::ifs::wl_surface::NoneSurfaceExt; use crate::render::RenderContext; use crate::theme::Theme; -use crate::tree::{DisplayNode, Node, NodeIds}; +use crate::tree::{ContainerNode, DisplayNode, NodeIds}; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::linkedlist::LinkedList; @@ -56,7 +56,8 @@ pub struct State { pub tree_changed_sent: Cell, pub config: CloneCell>>, pub theme: Theme, - pub pending_layout: AsyncQueue>, + pub pending_container_layout: AsyncQueue>, + pub pending_container_titles: AsyncQueue>, } pub struct MouseData { diff --git a/src/tasks/layout.rs b/src/tasks/layout.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs index d6730616..4073466a 100644 --- a/src/tasks/mod.rs +++ b/src/tasks/mod.rs @@ -17,12 +17,3 @@ pub async fn handle_slow_clients(state: Rc) { let mut sch = SlowClientHandler { state }; sch.handle_events().await; } - -pub async fn do_layout(state: Rc) { - loop { - let node = state.pending_layout.pop().await; - if node.needs_layout() { - node.do_layout(); - } - } -} diff --git a/src/text.rs b/src/text.rs new file mode 100644 index 00000000..f33b8632 --- /dev/null +++ b/src/text.rs @@ -0,0 +1,67 @@ +use crate::format::ARGB8888; +use crate::render::{RenderContext, Texture}; +use crate::theme::Color; +use crate::RenderError; +use cairo::{ImageSurface, Operator}; +use pango::{EllipsizeMode, Layout}; +use pangocairo::cairo::Format; +use pangocairo::pango::FontDescription; +use pangocairo::{cairo, pango}; +use std::mem; +use std::rc::Rc; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum TextError { + #[error("Could not create a cairo image")] + CreateImage(#[source] cairo::Error), + #[error("Could not create a cairo context")] + CairoContext(#[source] cairo::Error), + #[error("Could not create a pango context")] + PangoContext, + #[error("Could not import the rendered text")] + RenderError(#[source] RenderError), + #[error("Could not access the cairo image data")] + ImageData, +} + +pub fn render( + ctx: &Rc, + width: i32, + height: i32, + font: &str, + text: &str, + color: Color, +) -> Result, TextError> { + let image = match ImageSurface::create(Format::ARgb32, width, height) { + Ok(s) => s, + Err(e) => return Err(TextError::CreateImage(e)), + }; + let cctx = match cairo::Context::new(&image) { + Ok(c) => c, + Err(e) => return Err(TextError::CairoContext(e)), + }; + let pctx = match pangocairo::create_context(&cctx) { + Some(c) => c, + _ => return Err(TextError::PangoContext), + }; + let fd = FontDescription::from_string(font); + let layout = Layout::new(&pctx); + layout.set_width(width * pango::SCALE); + layout.set_ellipsize(EllipsizeMode::End); + layout.set_font_description(Some(&fd)); + layout.set_text(text); + cctx.set_operator(Operator::Source); + cctx.set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _); + pangocairo::show_layout(&cctx, &layout); + let mut texture = None; + let _ = image.with_data(|d| unsafe { + let d = mem::transmute(d); + texture = Some(ctx.shmem_texture(d, ARGB8888, width, height, image.stride())); + }); + match texture { + Some(Ok(t)) => Ok(t), + Some(Err(e)) => Err(TextError::RenderError(e)), + None => Err(TextError::ImageData), + } +} diff --git a/src/theme.rs b/src/theme.rs index 655e0d2a..9ba5c7e4 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -1,5 +1,4 @@ -use crate::NumCell; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; #[derive(Copy, Clone, Debug)] pub struct Color { @@ -9,6 +8,15 @@ pub struct Color { pub a: f32, } +impl Color { + pub const RED: Self = Self { + r: 1.0, + g: 0.0, + b: 0.0, + a: 1.0, + }; +} + fn to_f32(c: u8) -> f32 { c as f32 / 255 as f32 } @@ -43,7 +51,7 @@ pub struct Theme { pub border_color: Cell, pub title_height: Cell, pub border_width: Cell, - pub version: NumCell, + pub font: RefCell, } impl Default for Theme { @@ -56,7 +64,7 @@ impl Default for Theme { border_color: Cell::new(Color::from_rgba(0x36, 0x00, 0x07, 255)), title_height: Cell::new(17), border_width: Cell::new(4), - version: NumCell::new(1), + font: RefCell::new("monospace 8".to_string()), } } } diff --git a/src/tree/container.rs b/src/tree/container.rs index fb0ef12d..2e8f089f 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -1,20 +1,22 @@ -use crate::backend::KeyState; use crate::cursor::KnownCursor; use crate::fixed::Fixed; use crate::ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT}; use crate::rect::Rect; -use crate::render::Renderer; +use crate::render::{Renderer, Texture}; +use crate::theme::Color; +use crate::tree::walker::NodeVisitor; use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode}; use crate::utils::clonecell::CloneCell; use crate::utils::linkedlist::{LinkedList, LinkedNode, NodeRef}; -use crate::{NumCell, State}; +use crate::{text, ErrorFmt, NumCell, State}; use ahash::AHashMap; use i4config::{Axis, Direction}; use std::cell::{Cell, RefCell}; use std::fmt::{Debug, Formatter}; use std::mem; -use std::ops::DerefMut; +use std::ops::{Deref, DerefMut}; use std::rc::Rc; +use crate::backend::KeyState; #[allow(dead_code)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -55,6 +57,7 @@ pub struct ContainerNode { pub id: ContainerNodeId, pub parent: CloneCell>, pub split: Cell, + title: RefCell, pub mono_child: CloneCell>>, pub mono_body: Cell, pub mono_content: Cell, @@ -65,8 +68,8 @@ pub struct ContainerNode { pub content_width: Cell, pub content_height: Cell, pub sum_factors: Cell, - pub theme_version: Cell, - pub needs_layout: Cell, + layout_scheduled: Cell, + render_titles_scheduled: Cell, num_children: NumCell, pub children: LinkedList, child_nodes: RefCell>>, @@ -89,6 +92,8 @@ pub struct ContainerChild { pub content: Cell, factor: Cell, pub focus: Cell, + title: RefCell, + pub title_texture: CloneCell>>, } struct SeatState { @@ -136,12 +141,15 @@ impl ContainerNode { content: Cell::new(Default::default()), factor: Cell::new(1.0), focus: Cell::new(ContainerFocus::None), + title: Default::default(), + title_texture: Default::default(), }), ); Self { id: state.node_ids.next(), parent: CloneCell::new(parent), split: Cell::new(split), + title: Default::default(), mono_child: CloneCell::new(None), mono_body: Cell::new(Default::default()), mono_content: Cell::new(Default::default()), @@ -152,8 +160,8 @@ impl ContainerNode { content_width: Cell::new(0), content_height: Cell::new(0), sum_factors: Cell::new(1.0), - theme_version: Cell::new(0), - needs_layout: Cell::new(false), + layout_scheduled: Cell::new(false), + render_titles_scheduled: Cell::new(false), num_children: NumCell::new(1), children, child_nodes: RefCell::new(child_nodes), @@ -207,6 +215,8 @@ impl ContainerNode { content: Default::default(), factor: Cell::new(0.0), focus: Cell::new(ContainerFocus::None), + title: Default::default(), + title_texture: Default::default(), }), ); } @@ -224,7 +234,8 @@ impl ContainerNode { child.factor.set(factor); sum_factors += factor; } - self.apply_factors(sum_factors); + self.sum_factors.set(sum_factors); + self.schedule_layout(); self.cancel_seat_ops(); } @@ -235,16 +246,18 @@ impl ContainerNode { } } - fn apply_factors(self: &Rc, sum_factors: f64) { - self.sum_factors.set(sum_factors); - self.needs_layout.set(true); - self.state.pending_layout.push(self.clone()); + pub fn on_theme_changed(self: &Rc) { + self.update_content_size(); + self.schedule_layout(); } - fn do_apply_factors(&self) { - if self.theme_version.get() != self.state.theme.version.get() { - self.update_content_size(); + fn schedule_layout(self: &Rc) { + if !self.layout_scheduled.replace(true) { + self.state.pending_container_layout.push(self.clone()); } + } + + fn perform_layout(self: &Rc) { let sum_factors = self.sum_factors.get(); let border_width = self.state.theme.border_width.get(); let title_height = self.state.theme.title_height.get(); @@ -305,12 +318,13 @@ impl ContainerNode { } } self.sum_factors.set(1.0); - self.needs_layout.set(false); + self.layout_scheduled.set(false); for child in self.children.iter() { let body = child.body.get().move_(self.abs_x1.get(), self.abs_y1.get()); child.node.clone().change_extents(&body); child.position_content(); } + self.schedule_render_titles(); } fn update_content_size(&self) { @@ -335,7 +349,6 @@ impl ContainerNode { self.content_width.set(self.width.get()); } } - self.theme_version.set(self.state.theme.version.get()); } fn pointer_move(self: &Rc, seat: &Rc, mut x: i32, mut y: i32) { @@ -390,7 +403,8 @@ impl ContainerNode { + child_factor; prev.factor.set(prev_factor); op.child.factor.set(child_factor); - self.apply_factors(sum_factors); + self.sum_factors.set(sum_factors); + self.schedule_layout(); } } return; @@ -422,6 +436,59 @@ impl ContainerNode { } } } + + fn update_title(self: &Rc) { + let split = match self.split.get() { + ContainerSplit::Horizontal => "H", + ContainerSplit::Vertical => "V", + }; + let mut title = self.title.borrow_mut(); + title.clear(); + title.push_str(split); + title.push_str("["); + for (i, c) in self.children.iter().enumerate() { + if i > 0 { + title.push_str(" "); + } + title.push_str(c.title.borrow_mut().deref()); + } + title.push_str("]"); + self.parent.get().child_title_changed(&**self, &title); + self.schedule_render_titles(); + } + + fn schedule_render_titles(self: &Rc) { + if !self.render_titles_scheduled.replace(true) { + self.state.pending_container_titles.push(self.clone()); + } + } + + fn render_titles(&self) { + let theme = &self.state.theme; + let th = theme.title_height.get(); + let font = theme.font.borrow_mut(); + for c in self.children.iter() { + c.title_texture.set(None); + let title = c.title.borrow_mut(); + if title.is_empty() { + continue; + } + let ctx = match self.state.render_ctx.get() { + Some(c) => c, + _ => continue, + }; + let texture = + match text::render(&ctx, c.body.get().width(), th, &font, &title, Color::RED) { + Ok(t) => t, + Err(e) => { + log::error!("Could not render title {}: {}", title, ErrorFmt(e)); + continue; + } + }; + c.title_texture.set(Some(texture)); + } + self.render_titles_scheduled.set(false); + } } struct SeatOp { @@ -435,6 +502,24 @@ enum SeatOpKind { Resize { dist_left: i32, dist_right: i32 }, } +pub async fn container_layout(state: Rc) { + loop { + let container = state.pending_container_layout.pop().await; + if container.layout_scheduled.get() { + container.perform_layout(); + } + } +} + +pub async fn render_titles(state: Rc) { + loop { + let container = state.pending_container_titles.pop().await; + if container.render_titles_scheduled.get() { + container.render_titles(); + } + } +} + impl Node for ContainerNode { fn id(&self) -> NodeId { self.id.into() @@ -456,12 +541,32 @@ impl Node for ContainerNode { self.seat_state.destroy_node(self); } - fn needs_layout(&self) -> bool { - self.needs_layout.get() + fn child_title_changed(self: Rc, child: &dyn Node, title: &str) { + let child = match self.child_nodes.borrow_mut().get(&child.id()) { + Some(cn) => cn.to_ref(), + _ => return, + }; + { + let mut ct = child.title.borrow_mut(); + if ct.deref() == title { + return; + } + ct.clear(); + ct.push_str(title); + } + self.update_title(); } - fn do_layout(&self) { - self.do_apply_factors(); + fn focus_parent(&self, seat: &Rc) { + self.parent.get().focus_self(seat); + } + + fn active_changed(&self, active: bool) { + self.parent.get().child_active_changed(self, active); + } + + fn focus_self(self: Rc, seat: &Rc) { + seat.focus_node(self); } fn get_split(&self) -> Option { @@ -471,7 +576,7 @@ impl Node for ContainerNode { fn set_split(self: Rc, split: ContainerSplit) { self.split.set(split); self.update_content_size(); - self.apply_factors(self.sum_factors.get()); + self.schedule_layout(); } fn do_focus(self: Rc, seat: &Rc, direction: Direction) { @@ -647,6 +752,8 @@ impl Node for ContainerNode { content: Cell::new(node.content.get()), factor: Cell::new(node.factor.get()), focus: Cell::new(node.focus.get()), + title: Default::default(), + title_texture: Default::default(), }); self.child_nodes.borrow_mut().insert(new.id(), link); new.clone().set_workspace(&self.workspace.get()); @@ -681,7 +788,8 @@ impl Node for ContainerNode { sum += factor; } } - self.apply_factors(sum); + self.sum_factors.set(sum); + self.schedule_layout(); self.cancel_seat_ops(); } @@ -718,13 +826,9 @@ impl Node for ContainerNode { } fn child_active_changed(&self, child: &dyn Node, active: bool) { - log::info!("cac = {}", active); let node = match self.child_nodes.borrow_mut().get(&child.id()) { Some(l) => l.to_ref(), - None => { - log::info!("return"); - return - }, + None => return, }; node.active.set(active); } @@ -745,7 +849,7 @@ impl Node for ContainerNode { size_changed |= self.height.replace(rect.height()) != rect.height(); if size_changed { self.update_content_size(); - self.do_apply_factors(); + self.perform_layout(); self.cancel_seat_ops(); self.parent .get() @@ -764,4 +868,14 @@ impl Node for ContainerNode { } self.workspace.set(ws.clone()); } + + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_container(&self); + } + + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { + for child in self.children.iter() { + child.node.clone().visit(visitor); + } + } } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index e3a9b3d7..4faad60f 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -7,6 +7,7 @@ use crate::ifs::wl_seat::{Dnd, NodeSeatState, WlSeatGlobal}; use crate::ifs::wl_surface::WlSurface; use crate::rect::Rect; use crate::render::Renderer; +use crate::tree::walker::NodeVisitor; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::linkedlist::{LinkedList, LinkedNode}; @@ -21,6 +22,7 @@ use std::rc::Rc; pub use workspace::*; mod container; +pub mod walker; mod workspace; pub struct NodeIds { @@ -66,13 +68,12 @@ pub trait Node { fn id(&self) -> NodeId; fn seat_state(&self) -> &NodeSeatState; fn destroy_node(&self, detach: bool); + fn visit(self: Rc, visitor: &mut dyn NodeVisitor); + fn visit_children(&self, visitor: &mut dyn NodeVisitor); - fn needs_layout(&self) -> bool { - false - } - - fn do_layout(&self) { - // nothing + fn child_title_changed(self: Rc, child: &dyn Node, title: &str) { + let _ = child; + let _ = title; } fn get_parent_split(&self) -> Option { @@ -95,6 +96,10 @@ pub trait Node { let _ = split; } + fn focus_self(self: Rc, seat: &Rc) { + let _ = seat; + } + fn do_focus(self: Rc, seat: &Rc, direction: Direction) { let _ = seat; let _ = direction; @@ -155,6 +160,10 @@ pub trait Node { let _ = seat; } + fn focus_parent(&self, seat: &Rc) { + let _ = seat; + } + fn unfocus(&self, seat: &WlSeatGlobal) { let _ = seat; } @@ -279,7 +288,6 @@ pub struct DisplayNode { pub outputs: CopyHashMap>, pub stacked: LinkedList>, pub seat_state: NodeSeatState, - pub needs_layout: Cell, } impl DisplayNode { @@ -289,7 +297,6 @@ impl DisplayNode { outputs: Default::default(), stacked: Default::default(), seat_state: Default::default(), - needs_layout: Cell::new(false), } } } @@ -315,15 +322,17 @@ impl Node for DisplayNode { self.seat_state.destroy_node(self); } - fn needs_layout(&self) -> bool { - self.needs_layout.get() + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_display(&self); } - fn do_layout(&self) { - self.needs_layout.set(false); + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { let outputs = self.outputs.lock(); - for output in outputs.values() { - output.do_layout(); + for (_, output) in outputs.deref() { + visitor.visit_output(output); + } + for stacked in self.stacked.iter() { + stacked.deref().clone().visit(visitor); } } @@ -409,10 +418,14 @@ impl Node for OutputNode { self.seat_state.destroy_node(self); } - fn do_layout(&self) { - let workspaces = self.workspaces.borrow_mut(); - for ws in workspaces.deref() { - ws.do_layout(); + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_output(&self); + } + + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { + let ws = self.workspaces.borrow_mut(); + for ws in ws.deref() { + visitor.visit_workspace(ws); } } @@ -489,6 +502,16 @@ impl Node for FloatNode { self.seat_state.destroy_node(self); } + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_float(&self); + } + + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { + if let Some(c) = self.child.get() { + c.visit(visitor); + } + } + fn absolute_position(&self) -> Rect { self.position.get() } diff --git a/src/tree/walker.rs b/src/tree/walker.rs new file mode 100644 index 00000000..510bfd3a --- /dev/null +++ b/src/tree/walker.rs @@ -0,0 +1,96 @@ +use crate::ifs::wl_surface::xdg_surface::xdg_popup::XdgPopup; +use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; +use crate::ifs::wl_surface::WlSurface; +use crate::tree::{ContainerNode, FloatNode, Node, OutputNode, WorkspaceNode}; +use crate::DisplayNode; +use std::rc::Rc; + +pub trait NodeVisitorBase: Sized { + fn visit_surface(&mut self, node: &Rc) { + node.visit_children(self); + } + + fn visit_container(&mut self, node: &Rc) { + node.visit_children(self); + } + + fn visit_toplevel(&mut self, node: &Rc) { + node.visit_children(self); + } + + fn visit_popup(&mut self, node: &Rc) { + node.visit_children(self); + } + + fn visit_display(&mut self, node: &Rc) { + node.visit_children(self); + } + + fn visit_output(&mut self, node: &Rc) { + node.visit_children(self); + } + + fn visit_float(&mut self, node: &Rc) { + node.visit_children(self); + } + + fn visit_workspace(&mut self, node: &Rc) { + node.visit_children(self); + } +} + +pub trait NodeVisitor { + fn visit_surface(&mut self, node: &Rc); + fn visit_container(&mut self, node: &Rc); + fn visit_toplevel(&mut self, node: &Rc); + fn visit_popup(&mut self, node: &Rc); + fn visit_display(&mut self, node: &Rc); + fn visit_output(&mut self, node: &Rc); + fn visit_float(&mut self, node: &Rc); + fn visit_workspace(&mut self, node: &Rc); +} + +impl NodeVisitor for T { + fn visit_surface(&mut self, node: &Rc) { + ::visit_surface(self, node) + } + + fn visit_container(&mut self, node: &Rc) { + ::visit_container(self, node) + } + + fn visit_toplevel(&mut self, node: &Rc) { + ::visit_toplevel(self, node) + } + + fn visit_popup(&mut self, node: &Rc) { + ::visit_popup(self, node) + } + + fn visit_display(&mut self, node: &Rc) { + ::visit_display(self, node) + } + + fn visit_output(&mut self, node: &Rc) { + ::visit_output(self, node) + } + + fn visit_float(&mut self, node: &Rc) { + ::visit_float(self, node) + } + + fn visit_workspace(&mut self, node: &Rc) { + ::visit_workspace(self, node) + } +} + +pub fn visit_containers)>(f: F) -> impl NodeVisitor { + struct V(F); + impl)> NodeVisitorBase for V { + fn visit_container(&mut self, node: &Rc) { + (self.0)(node); + node.visit_children(self); + } + } + V(f) +} diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index 09ab44cb..d25c56bd 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -3,6 +3,7 @@ use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; use crate::rect::Rect; use crate::render::Renderer; use crate::tree::container::ContainerNode; +use crate::tree::walker::NodeVisitor; use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, OutputNode}; use crate::utils::clonecell::CloneCell; use crate::utils::linkedlist::LinkedList; @@ -47,12 +48,13 @@ impl Node for WorkspaceNode { self.seat_state.destroy_node(self); } - fn do_layout(&self) { + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_workspace(&self); + } + + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { if let Some(c) = self.container.get() { - c.do_layout(); - } - for s in self.stacked.iter() { - s.do_layout(); + visitor.visit_container(&c); } } diff --git a/src/utils/array.rs b/src/utils/array.rs new file mode 100644 index 00000000..7e5b7886 --- /dev/null +++ b/src/utils/array.rs @@ -0,0 +1,11 @@ +pub fn from_fn(mut cb: F) -> [T; N] +where + F: FnMut(usize) -> T, +{ + let mut idx = 0; + [(); N].map(|_| { + let res = cb(idx); + idx += 1; + res + }) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ff6e52b5..bcaf9c6b 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod array; pub mod asyncevent; pub mod bitflags; pub mod buffd; diff --git a/src/utils/queue.rs b/src/utils/queue.rs index c281c58e..9a3575b9 100644 --- a/src/utils/queue.rs +++ b/src/utils/queue.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::collections::VecDeque; use std::future::Future; use std::mem; @@ -7,7 +7,7 @@ use std::task::{Context, Poll, Waker}; pub struct AsyncQueue { data: RefCell>, - waiters: RefCell>, + waiter: Cell>, } impl Default for AsyncQueue { @@ -19,14 +19,14 @@ impl Default for AsyncQueue { impl AsyncQueue { pub fn new() -> Self { Self { - data: RefCell::new(Default::default()), - waiters: RefCell::new(vec![]), + data: Default::default(), + waiter: Default::default(), } } pub fn push(&self, t: T) { self.data.borrow_mut().push_back(t); - for waiter in self.waiters.borrow_mut().drain(..) { + if let Some(waiter) = self.waiter.take() { waiter.wake(); } } @@ -41,7 +41,7 @@ impl AsyncQueue { pub fn clear(&self) { mem::take(&mut *self.data.borrow_mut()); - mem::take(&mut *self.waiters.borrow_mut()); + self.waiter.take(); } } @@ -56,7 +56,7 @@ impl<'a, T> Future for AsyncQueuePop<'a, T> { if let Some(t) = self.queue.try_pop() { Poll::Ready(t) } else { - self.queue.waiters.borrow_mut().push(cx.waker().clone()); + self.queue.waiter.set(Some(cx.waker().clone())); Poll::Pending } } diff --git a/todo.md b/todo.md index 82ab124b..6f0070b8 100644 --- a/todo.md +++ b/todo.md @@ -3,10 +3,8 @@ - Container moving (mouse) - Container moving (kb) - Float toggle -- Toplevel splitting - Workspaces - Float moving -- Highlighting active - presentation time - viewporter - session lock @@ -19,3 +17,5 @@ - dnd - Shortcuts - Config +- Highlighting active +- Toplevel splitting