1
0
Fork 0
forked from wry/wry

autocommit 2022-02-19 19:41:18 CET

This commit is contained in:
Julian Orth 2022-02-19 19:41:18 +01:00
parent bb0468feea
commit ae66acef73
32 changed files with 880 additions and 164 deletions

236
Cargo.lock generated
View file

@ -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"

View file

@ -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" }

View file

@ -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));

View file

@ -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"

View file

@ -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 {

View file

@ -119,6 +119,9 @@ pub enum ClientMessage<'a> {
seat: Seat,
axis: Axis,
},
FocusParent {
seat: Seat,
},
}
#[derive(Encode, Decode, Debug)]

View file

@ -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<Seat> {

View file

@ -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<Wheel>,
el: Rc<EventLoop>,
@ -56,7 +64,15 @@ impl AsyncEngine {
}
pub fn spawn<T, F: Future<Output = T> + 'static>(&self, f: F) -> SpawnedFuture<T> {
self.queue.spawn(f)
self.queue.spawn(Phase::EventHandling, f)
}
pub fn spawn2<T, F: Future<Output = T> + 'static>(
&self,
phase: Phase,
f: F,
) -> SpawnedFuture<T> {
self.queue.spawn(phase, f)
}
pub fn fd(self: &Rc<Self>, fd: &Rc<OwnedFd>) -> Result<AsyncFd, AsyncError> {
@ -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<T, F: Future<Output = T>> {
ref_count: NumCell<u64>,
phase: Phase,
state: NumCell<u32>,
data: UnsafeCell<TaskData<T, F>>,
waker: Cell<Option<Waker>>,
@ -282,9 +300,14 @@ mod task {
}
impl DispatchQueue {
pub(super) fn spawn<T, F: Future<Output = T>>(self: &Rc<Self>, f: F) -> SpawnedFuture<T> {
pub(super) fn spawn<T, F: Future<Output = T>>(
self: &Rc<Self>,
phase: Phase,
f: F,
) -> SpawnedFuture<T> {
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<Self>, _events: i32) -> Result<(), Box<dyn Error>> {
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<bool>,
id: EventLoopId,
el: Rc<EventLoop>,
queue: RefCell<VecDeque<Runnable>>,
num_queued: NumCell<usize>,
queues: [RefCell<VecDeque<Runnable>>; NUM_PHASES],
iteration: NumCell<u64>,
}
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);

View file

@ -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<Client>) {
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 => { },

View file

@ -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<CphError>),
}
efrom!(CreateSplitError, CphError);
#[derive(Debug, Error)]
enum FocusParentError {
#[error(transparent)]
CphError(#[from] Box<CphError>),
}
efrom!(FocusParentError, CphError);

View file

@ -199,6 +199,10 @@ impl WlSeatGlobal {
self.keyboard_node.get().create_split(axis)
}
pub fn focus_parent(self: &Rc<Self>) {
self.keyboard_node.get().focus_parent(self);
}
pub fn get_rate(&self) -> (i32, i32) {
self.repeat_rate.get()
}

View file

@ -212,12 +212,12 @@ impl WlSeatGlobal {
}
fn focus_xdg_surface(self: &Rc<Self>, xdg: &Rc<XdgSurface>) {
self.focus_surface(&xdg.focus_surface(self));
self.focus_node(xdg.focus_surface(self));
}
fn focus_surface(self: &Rc<Self>, surface: &Rc<WlSurface>) {
pub fn focus_node(self: &Rc<Self>, node: Rc<dyn Node>) {
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::<WlDataDevice>(&self.selection, &surface.client);
self.offer_selection::<ZwpPrimarySelectionDeviceV1>(
&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<T: ipc::Vtable>(
@ -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::<WlDataDevice>(&self.selection, &surface.client);
self.offer_selection::<ZwpPrimarySelectionDeviceV1>(
&self.primary_selection,
&surface.client,
);
}
}
}
// Key callbacks
impl WlSeatGlobal {
pub fn key_surface(&self, surface: &WlSurface, key: u32, state: u32) {

View file

@ -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<WlSeatGlobal>) {
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<Self>, seat: &Rc<WlSeatGlobal>) {
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<Self>, 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)]

View file

@ -75,6 +75,10 @@ struct PendingXdgSurfaceData {
}
pub trait XdgSurfaceExt: Debug {
fn focus_parent(&self, seat: &Rc<WlSeatGlobal>) {
let _ = seat;
}
fn get_split(&self) -> Option<ContainerSplit> {
None
}
@ -203,6 +207,12 @@ impl XdgSurface {
Ok(())
}
pub fn focus_parent(&self, seat: &Rc<WlSeatGlobal>) {
if let Some(ext) = self.ext.get() {
ext.focus_parent(seat);
}
}
pub fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc<WlSurface> {
self.focus_surface
.get(&seat.id())

View file

@ -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<Self>, 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()
}

View file

@ -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<Option<i32>>,
max_width: Cell<Option<i32>>,
max_height: Cell<Option<i32>>,
title: RefCell<String>,
pub tracker: Tracker<Self>,
}
@ -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<Self>, 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<Self>) {
@ -442,6 +462,14 @@ impl Node for XdgToplevel {
self.xdg.seat_state.destroy_node(self)
}
fn visit(self: Rc<Self>, 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<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
seat.focus_toplevel(&self);
}
@ -488,6 +516,10 @@ impl Node for XdgToplevel {
}
impl XdgSurfaceExt for XdgToplevel {
fn focus_parent(&self, seat: &Rc<WlSeatGlobal>) {
self.parent_node.get().map(|p| p.focus_self(seat));
}
fn get_split(&self) -> Option<ContainerSplit> {
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<Self>, seat: &Rc<WlSeatGlobal>, 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();
}
}

View file

@ -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()?;

View file

@ -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,

View file

@ -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<RenderContext>,
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();

View file

@ -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<bool>,
pub config: CloneCell<Option<Rc<ConfigProxy>>>,
pub theme: Theme,
pub pending_layout: AsyncQueue<Rc<dyn Node>>,
pub pending_container_layout: AsyncQueue<Rc<ContainerNode>>,
pub pending_container_titles: AsyncQueue<Rc<ContainerNode>>,
}
pub struct MouseData {

View file

View file

@ -17,12 +17,3 @@ pub async fn handle_slow_clients(state: Rc<State>) {
let mut sch = SlowClientHandler { state };
sch.handle_events().await;
}
pub async fn do_layout(state: Rc<State>) {
loop {
let node = state.pending_layout.pop().await;
if node.needs_layout() {
node.do_layout();
}
}
}

67
src/text.rs Normal file
View file

@ -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<RenderContext>,
width: i32,
height: i32,
font: &str,
text: &str,
color: Color,
) -> Result<Rc<Texture>, 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),
}
}

View file

@ -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<Color>,
pub title_height: Cell<i32>,
pub border_width: Cell<i32>,
pub version: NumCell<u32>,
pub font: RefCell<String>,
}
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()),
}
}
}

View file

@ -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<Rc<dyn Node>>,
pub split: Cell<ContainerSplit>,
title: RefCell<String>,
pub mono_child: CloneCell<Option<NodeRef<ContainerChild>>>,
pub mono_body: Cell<Rect>,
pub mono_content: Cell<Rect>,
@ -65,8 +68,8 @@ pub struct ContainerNode {
pub content_width: Cell<i32>,
pub content_height: Cell<i32>,
pub sum_factors: Cell<f64>,
pub theme_version: Cell<u32>,
pub needs_layout: Cell<bool>,
layout_scheduled: Cell<bool>,
render_titles_scheduled: Cell<bool>,
num_children: NumCell<usize>,
pub children: LinkedList<ContainerChild>,
child_nodes: RefCell<AHashMap<NodeId, LinkedNode<ContainerChild>>>,
@ -89,6 +92,8 @@ pub struct ContainerChild {
pub content: Cell<Rect>,
factor: Cell<f64>,
pub focus: Cell<ContainerFocus>,
title: RefCell<String>,
pub title_texture: CloneCell<Option<Rc<Texture>>>,
}
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<Self>, 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>) {
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<Self>) {
if !self.layout_scheduled.replace(true) {
self.state.pending_container_layout.push(self.clone());
}
}
fn perform_layout(self: &Rc<Self>) {
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<Self>, seat: &Rc<WlSeatGlobal>, 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<Self>) {
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<Self>) {
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<State>) {
loop {
let container = state.pending_container_layout.pop().await;
if container.layout_scheduled.get() {
container.perform_layout();
}
}
}
pub async fn render_titles(state: Rc<State>) {
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<Self>, 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<WlSeatGlobal>) {
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<Self>, seat: &Rc<WlSeatGlobal>) {
seat.focus_node(self);
}
fn get_split(&self) -> Option<ContainerSplit> {
@ -471,7 +576,7 @@ impl Node for ContainerNode {
fn set_split(self: Rc<Self>, 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<Self>, seat: &Rc<WlSeatGlobal>, 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<Self>, 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);
}
}
}

View file

@ -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<Self>, 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<Self>, child: &dyn Node, title: &str) {
let _ = child;
let _ = title;
}
fn get_parent_split(&self) -> Option<ContainerSplit> {
@ -95,6 +96,10 @@ pub trait Node {
let _ = split;
}
fn focus_self(self: Rc<Self>, seat: &Rc<WlSeatGlobal>) {
let _ = seat;
}
fn do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, direction: Direction) {
let _ = seat;
let _ = direction;
@ -155,6 +160,10 @@ pub trait Node {
let _ = seat;
}
fn focus_parent(&self, seat: &Rc<WlSeatGlobal>) {
let _ = seat;
}
fn unfocus(&self, seat: &WlSeatGlobal) {
let _ = seat;
}
@ -279,7 +288,6 @@ pub struct DisplayNode {
pub outputs: CopyHashMap<OutputId, Rc<OutputNode>>,
pub stacked: LinkedList<Rc<dyn Node>>,
pub seat_state: NodeSeatState,
pub needs_layout: Cell<bool>,
}
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<Self>, 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<Self>, 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<Self>, 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()
}

96
src/tree/walker.rs Normal file
View file

@ -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<WlSurface>) {
node.visit_children(self);
}
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
node.visit_children(self);
}
fn visit_toplevel(&mut self, node: &Rc<XdgToplevel>) {
node.visit_children(self);
}
fn visit_popup(&mut self, node: &Rc<XdgPopup>) {
node.visit_children(self);
}
fn visit_display(&mut self, node: &Rc<DisplayNode>) {
node.visit_children(self);
}
fn visit_output(&mut self, node: &Rc<OutputNode>) {
node.visit_children(self);
}
fn visit_float(&mut self, node: &Rc<FloatNode>) {
node.visit_children(self);
}
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
node.visit_children(self);
}
}
pub trait NodeVisitor {
fn visit_surface(&mut self, node: &Rc<WlSurface>);
fn visit_container(&mut self, node: &Rc<ContainerNode>);
fn visit_toplevel(&mut self, node: &Rc<XdgToplevel>);
fn visit_popup(&mut self, node: &Rc<XdgPopup>);
fn visit_display(&mut self, node: &Rc<DisplayNode>);
fn visit_output(&mut self, node: &Rc<OutputNode>);
fn visit_float(&mut self, node: &Rc<FloatNode>);
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>);
}
impl<T: NodeVisitorBase> NodeVisitor for T {
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
<T as NodeVisitorBase>::visit_surface(self, node)
}
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
<T as NodeVisitorBase>::visit_container(self, node)
}
fn visit_toplevel(&mut self, node: &Rc<XdgToplevel>) {
<T as NodeVisitorBase>::visit_toplevel(self, node)
}
fn visit_popup(&mut self, node: &Rc<XdgPopup>) {
<T as NodeVisitorBase>::visit_popup(self, node)
}
fn visit_display(&mut self, node: &Rc<DisplayNode>) {
<T as NodeVisitorBase>::visit_display(self, node)
}
fn visit_output(&mut self, node: &Rc<OutputNode>) {
<T as NodeVisitorBase>::visit_output(self, node)
}
fn visit_float(&mut self, node: &Rc<FloatNode>) {
<T as NodeVisitorBase>::visit_float(self, node)
}
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
<T as NodeVisitorBase>::visit_workspace(self, node)
}
}
pub fn visit_containers<F: FnMut(&Rc<ContainerNode>)>(f: F) -> impl NodeVisitor {
struct V<F>(F);
impl<F: FnMut(&Rc<ContainerNode>)> NodeVisitorBase for V<F> {
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
(self.0)(node);
node.visit_children(self);
}
}
V(f)
}

View file

@ -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<Self>, 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);
}
}

11
src/utils/array.rs Normal file
View file

@ -0,0 +1,11 @@
pub fn from_fn<F, T, const N: usize>(mut cb: F) -> [T; N]
where
F: FnMut(usize) -> T,
{
let mut idx = 0;
[(); N].map(|_| {
let res = cb(idx);
idx += 1;
res
})
}

View file

@ -1,3 +1,4 @@
pub mod array;
pub mod asyncevent;
pub mod bitflags;
pub mod buffd;

View file

@ -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<T> {
data: RefCell<VecDeque<T>>,
waiters: RefCell<Vec<Waker>>,
waiter: Cell<Option<Waker>>,
}
impl<T> Default for AsyncQueue<T> {
@ -19,14 +19,14 @@ impl<T> Default for AsyncQueue<T> {
impl<T> AsyncQueue<T> {
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<T> AsyncQueue<T> {
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
}
}

View file

@ -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