diff --git a/default-config/src/lib.rs b/default-config/src/lib.rs index dcb52e4d..ff7a6a34 100644 --- a/default-config/src/lib.rs +++ b/default-config/src/lib.rs @@ -1,98 +1,65 @@ use jay_config::embedded::grab_input_device; -use jay_config::input::{ - InputDevice, CAP_KEYBOARD, CAP_POINTER, -}; +use jay_config::input::{InputDevice, CAP_KEYBOARD, CAP_POINTER}; use jay_config::keyboard::mods::{Modifiers, ALT, CTRL, SHIFT}; use jay_config::keyboard::syms::{ - SYM_Super_L, SYM_b, SYM_comma, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_m, SYM_p, - SYM_period, SYM_q, SYM_r, SYM_t, SYM_v, SYM_y, SYM_F1, SYM_F10, SYM_F11, SYM_F12, SYM_F2, - SYM_F3, SYM_F4, SYM_F5, SYM_F6, SYM_F7, SYM_F8, SYM_F9, + SYM_Super_L, SYM_b, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_m, SYM_p, SYM_q, SYM_t, + SYM_v, SYM_y, SYM_F1, SYM_F10, SYM_F11, SYM_F12, SYM_F13, SYM_F14, SYM_F15, SYM_F16, SYM_F17, + SYM_F18, SYM_F19, SYM_F2, SYM_F20, SYM_F21, SYM_F22, SYM_F23, SYM_F24, SYM_F25, SYM_F3, SYM_F4, + SYM_F5, SYM_F6, SYM_F7, SYM_F8, SYM_F9, }; -use jay_config::theme::{get_title_height, set_title_color, set_title_height, Color}; use jay_config::Axis::{Horizontal, Vertical}; use jay_config::Direction::{Down, Left, Right, Up}; use jay_config::{ - config, create_seat, input_devices, on_new_input_device, quit, switch_to_vt, Command, Seat, + config, create_seat, get_workspace, input_devices, on_new_input_device, quit, switch_to_vt, + Command, Seat, }; -use rand::Rng; const MOD: Modifiers = ALT; fn configure_seat(s: Seat) { - log::info!("Configuring seat {:?}", s); - - let change_rate = move |delta| { - let (rate, delay) = s.repeat_rate(); - let new_rate = rate - delta; - let new_delay = delay + 10 * delta; - log::info!("Changing repeat rate to {}/{}", new_rate, new_delay); - s.set_repeat_rate(new_rate, new_delay); - }; - - s.bind(CTRL | SHIFT | SYM_l, move || change_rate(-1)); - s.bind(CTRL | SHIFT | SYM_r, move || change_rate(1)); - - s.bind(MOD | SYM_comma, move || { - let mut rng = rand::thread_rng(); - set_title_color(Color { - r: rng.gen(), - g: rng.gen(), - b: rng.gen(), - a: rng.gen(), - }) - }); - - s.bind(MOD | SYM_period, move || { - set_title_height(get_title_height() + 1) - }); - s.bind(MOD | SYM_h, move || s.focus(Left)); s.bind(MOD | SYM_j, move || s.focus(Down)); s.bind(MOD | SYM_k, move || s.focus(Up)); s.bind(MOD | SYM_l, move || s.focus(Right)); - s.bind(MOD | SYM_d, move || s.create_split(Horizontal)); - s.bind(MOD | SYM_v, move || s.create_split(Vertical)); - - s.bind(MOD | SYM_t, move || { - s.set_split(s.split().other()); - }); - - s.bind(MOD | SYM_m, move || { - s.set_mono(!s.mono()); - }); - - s.bind(MOD | SYM_f, move || { - s.focus_parent(); - }); - - s.bind(MOD | SHIFT | SYM_f, move || { - s.toggle_floating(); - }); - 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)); s.bind(MOD | SHIFT | SYM_l, move || s.move_(Right)); + s.bind(MOD | SYM_d, move || s.create_split(Horizontal)); + s.bind(MOD | SYM_v, move || s.create_split(Vertical)); + + s.bind(MOD | SYM_t, move || s.set_split(s.split().other())); + + s.bind(MOD | SYM_m, move || s.set_mono(!s.mono())); + + s.bind(MOD | SYM_f, move || s.focus_parent()); + + s.bind(MOD | SHIFT | SYM_f, move || s.toggle_floating()); + s.bind(SYM_Super_L, || Command::new("alacritty").spawn()); s.bind(MOD | SYM_p, || Command::new("xeyes").spawn()); s.bind(MOD | SYM_q, quit); - s.bind(CTRL | ALT | SYM_F1, || switch_to_vt(1)); - s.bind(CTRL | ALT | SYM_F2, || switch_to_vt(2)); - s.bind(CTRL | ALT | SYM_F3, || switch_to_vt(3)); - s.bind(CTRL | ALT | SYM_F4, || switch_to_vt(4)); - s.bind(CTRL | ALT | SYM_F5, || switch_to_vt(5)); - s.bind(CTRL | ALT | SYM_F6, || switch_to_vt(6)); - s.bind(CTRL | ALT | SYM_F7, || switch_to_vt(7)); - s.bind(CTRL | ALT | SYM_F8, || switch_to_vt(8)); - s.bind(CTRL | ALT | SYM_F9, || switch_to_vt(9)); - s.bind(CTRL | ALT | SYM_F10, || switch_to_vt(10)); - s.bind(CTRL | ALT | SYM_F11, || switch_to_vt(11)); - s.bind(CTRL | ALT | SYM_F12, || switch_to_vt(12)); + let fnkeys = [ + SYM_F1, SYM_F2, SYM_F3, SYM_F4, SYM_F5, SYM_F6, SYM_F7, SYM_F8, SYM_F9, SYM_F10, SYM_F11, + SYM_F12, + ]; + for (i, sym) in fnkeys.into_iter().enumerate() { + s.bind(CTRL | ALT | sym, move || switch_to_vt(i as u32 + 1)); + } + + let fnkeys2 = [ + SYM_F13, SYM_F14, SYM_F15, SYM_F16, SYM_F17, SYM_F18, SYM_F19, SYM_F20, SYM_F21, SYM_F22, + SYM_F23, SYM_F24, SYM_F25, + ]; + for (i, sym) in fnkeys2.into_iter().enumerate() { + let ws = get_workspace(&format!("{}", i + 1)); + s.bind(MOD | sym, move || s.show_workspace(ws)); + } fn do_grab(s: Seat, grab: bool) { for device in s.input_devices() { diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index 55530b98..7660f029 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -5,7 +5,7 @@ use crate::_private::{bincode_ops, logging, Config, ConfigEntry, ConfigEntryGen, use crate::input::{AccelProfile, Capability, InputDevice}; use crate::keyboard::keymap::Keymap; use crate::theme::Color; -use crate::{Axis, Command, Direction, LogLevel, ModifiedKeySym, Seat}; +use crate::{Axis, Command, Direction, LogLevel, ModifiedKeySym, Seat, Workspace}; use std::cell::{Cell, RefCell}; use std::collections::hash_map::Entry; use std::collections::HashMap; @@ -199,6 +199,21 @@ impl Client { } } + pub fn get_workspace(&self, name: &str) -> Workspace { + let res = self.with_response(|| self.send(&ClientMessage::GetWorkspace { name })); + match res { + Response::GetWorkspace { workspace } => workspace, + _ => { + log::error!("Server did not send a response to a get_workspace request"); + Workspace(0) + } + } + } + + pub fn show_workspace(&self, seat: Seat, workspace: Workspace) { + self.send(&ClientMessage::ShowWorkspace { seat, workspace }); + } + pub fn split(&self, seat: Seat) -> Axis { let res = self.with_response(|| self.send(&ClientMessage::GetSplit { seat })); match res { @@ -337,6 +352,17 @@ impl Client { self.send(&ClientMessage::SetTransformMatrix { device, matrix }) } + pub fn device_name(&self, device: InputDevice) -> String { + let res = self.with_response(|| self.send(&ClientMessage::GetDeviceName { device })); + match res { + Response::GetDeviceName { name } => name, + _ => { + log::error!("Server did not send a response to a get_device_name request"); + String::new() + } + } + } + pub fn has_capability(&self, device: InputDevice, cap: Capability) -> bool { let res = self.with_response(|| self.send(&ClientMessage::HasCapability { device, cap })); match res { diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index 5787f02a..a7cfa5ff 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -3,7 +3,7 @@ use crate::keyboard::keymap::Keymap; use crate::keyboard::mods::Modifiers; use crate::keyboard::syms::KeySym; use crate::theme::Color; -use crate::{Axis, Direction, LogLevel, Seat}; +use crate::{Axis, Direction, LogLevel, Seat, Workspace}; use bincode::{BorrowDecode, Decode, Encode}; #[derive(Encode, BorrowDecode, Debug)] @@ -157,6 +157,16 @@ pub enum ClientMessage<'a> { device: InputDevice, matrix: [[f64; 2]; 2], }, + GetDeviceName { + device: InputDevice, + }, + GetWorkspace { + name: &'a str, + }, + ShowWorkspace { + seat: Seat, + workspace: Workspace, + }, } #[derive(Encode, Decode, Debug)] @@ -172,6 +182,8 @@ pub enum Response { GetTitleHeight { height: i32 }, GetBorderWidth { width: i32 }, HasCapability { has: bool }, + GetDeviceName { name: String }, + GetWorkspace { workspace: Workspace }, } #[derive(Encode, Decode, Debug)] diff --git a/jay-config/src/input.rs b/jay-config/src/input.rs index 466e7ad5..d1ea63fe 100644 --- a/jay-config/src/input.rs +++ b/jay-config/src/input.rs @@ -28,6 +28,10 @@ impl InputDevice { pub fn set_transform_matrix(self, matrix: [[f64; 2]; 2]) { get!().set_transform_matrix(self, matrix); } + + pub fn name(self) -> String { + get!(String::new()).device_name(self) + } } #[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)] diff --git a/jay-config/src/lib.rs b/jay-config/src/lib.rs index 20a6085a..aceb1d7f 100644 --- a/jay-config/src/lib.rs +++ b/jay-config/src/lib.rs @@ -134,6 +134,10 @@ impl Seat { pub fn toggle_floating(self) { get!().toggle_floating(self); } + + pub fn show_workspace(self, workspace: Workspace) { + get!().show_workspace(self, workspace) + } } pub fn get_seats() -> Vec { @@ -201,3 +205,10 @@ impl Command { get!().spawn(self); } } + +#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub struct Workspace(pub u64); + +pub fn get_workspace(name: &str) -> Workspace { + get!(Workspace(0)).get_workspace(name) +} diff --git a/src/backend.rs b/src/backend.rs index 47b7a791..b0cd5f41 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -28,6 +28,7 @@ pub trait InputDevice { fn set_accel_profile(&self, profile: InputDeviceAccelProfile); fn set_accel_speed(&self, speed: f64); fn set_transform_matrix(&self, matrix: [[f64; 2]; 2]); + fn name(&self) -> Rc; } #[derive(Debug, Copy, Clone)] diff --git a/src/backends.rs b/src/backends.rs index 04023a10..6bf2f9c6 100644 --- a/src/backends.rs +++ b/src/backends.rs @@ -1,2 +1,3 @@ +pub mod dummy; pub mod metal; pub mod x; diff --git a/src/backends/dummy.rs b/src/backends/dummy.rs new file mode 100644 index 00000000..a5f1a6dd --- /dev/null +++ b/src/backends/dummy.rs @@ -0,0 +1,36 @@ +use crate::backend::{Backend, Output, OutputId}; +use std::rc::Rc; + +pub struct DummyBackend {} + +impl Backend for DummyBackend { + fn switch_to(&self, vtnr: u32) { + let _ = vtnr; + } +} + +pub struct DummyOutput { + pub id: OutputId, +} + +impl Output for DummyOutput { + fn id(&self) -> OutputId { + self.id + } + + fn removed(&self) -> bool { + false + } + + fn width(&self) -> i32 { + 100 + } + + fn height(&self) -> i32 { + 100 + } + + fn on_change(&self, _cb: Rc) { + // nothing + } +} diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 37ac97fa..926d27da 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -196,6 +196,7 @@ struct MetalInputDevice { cb: CloneCell>>, hscroll: Cell, vscroll: Cell, + name: CloneCell>, // config left_handed: Cell>, @@ -223,16 +224,13 @@ impl LibInputAdapter for DeviceHolder { Ok(s) => s, Err(e) => return Err(LibInputError::Stat(e.into())), }; - match self.devices.get(&stat.st_rdev) { - Some(MetalDevice::Input(d)) => match d.fd.get() { - Some(fd) => match uapi::fcntl_dupfd_cloexec(fd.raw(), 0) { - Ok(fd) => Ok(fd), - Err(e) => Err(LibInputError::DupFd(e.into())), - }, - _ => Err(LibInputError::DeviceUnavailable), - }, - _ => Err(LibInputError::DeviceUnavailable), + if let Some(MetalDevice::Input(d)) = self.devices.get(&stat.st_rdev) { + if let Some(fd) = d.fd.get() { + return uapi::fcntl_dupfd_cloexec(fd.raw(), 0) + .map_err(|e| LibInputError::DupFd(e.into())); + } } + Err(LibInputError::DeviceUnavailable) } } @@ -319,6 +317,10 @@ impl InputDevice for MetalInputDevice { fn set_transform_matrix(&self, matrix: [[f64; 2]; 2]) { self.transform_matrix.set(Some(matrix)); } + + fn name(&self) -> Rc { + self.name.get() + } } impl MetalInputDevice { diff --git a/src/backends/metal/monitor.rs b/src/backends/metal/monitor.rs index a09b0e48..963d1661 100644 --- a/src/backends/metal/monitor.rs +++ b/src/backends/metal/monitor.rs @@ -281,6 +281,7 @@ impl MetalBackend { cb: Default::default(), hscroll: Cell::new(0.0), vscroll: Cell::new(0.0), + name: Default::default(), left_handed: Default::default(), accel_profile: Default::default(), accel_speed: Default::default(), @@ -323,6 +324,7 @@ impl MetalBackend { Err(_) => return, }; inputdev.device().set_slot(slot); + dev.name.set(Rc::new(inputdev.device().name())); dev.inputdev.set(Some(inputdev)); dev.apply_config(); slf.state diff --git a/src/backends/x.rs b/src/backends/x.rs index fed79472..72787beb 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -532,6 +532,8 @@ impl XBackendData { kb_events: RefCell::new(Default::default()), mouse_events: RefCell::new(Default::default()), button_map: Default::default(), + kb_name: Rc::new(format!("kb{}", info.deviceid)), + mouse_name: Rc::new(format!("mouse{}", info.deviceid)), }); seat.update_button_map().await; self.seats.set(info.deviceid, seat.clone()); @@ -888,6 +890,8 @@ struct XSeat { kb_events: RefCell>, mouse_events: RefCell>, button_map: CopyHashMap, + kb_name: Rc, + mouse_name: Rc, } struct XSeatKeyboard(Rc); @@ -982,6 +986,10 @@ impl InputDevice for XSeatKeyboard { fn set_transform_matrix(&self, matrix: [[f64; 2]; 2]) { let _ = matrix; } + + fn name(&self) -> Rc { + self.0.kb_name.clone() + } } impl InputDevice for XSeatMouse { @@ -1027,4 +1035,8 @@ impl InputDevice for XSeatMouse { fn set_transform_matrix(&self, matrix: [[f64; 2]; 2]) { let _ = matrix; } + + fn name(&self) -> Rc { + self.0.mouse_name.clone() + } } diff --git a/src/compositor.rs b/src/compositor.rs index 4f5e9ff6..4fca3114 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -13,9 +13,7 @@ use crate::logger::Logger; use crate::render::RenderError; use crate::sighand::SighandError; use crate::state::State; -use crate::tree::{ - container_layout, container_render_data, float_layout, float_titles, DisplayNode, NodeIds, -}; +use crate::tree::{container_layout, container_render_data, float_layout, float_titles, DisplayNode, NodeIds, OutputNode, WorkspaceNode}; use crate::utils::errorfmt::ErrorFmt; use crate::utils::fdcloser::FdCloser; use crate::utils::numcell::NumCell; @@ -25,11 +23,14 @@ use crate::wheel::{Wheel, WheelError}; use crate::xkbcommon::XkbContext; use crate::{clientmem, forker, leaks, render, sighand, tasks, xwayland}; use forker::ForkerProxy; -use std::cell::Cell; +use std::cell::{Cell}; use std::ops::Deref; use std::rc::Rc; use std::sync::Arc; use thiserror::Error; +use crate::backends::dummy::DummyOutput; +use crate::ifs::wl_output::WlOutputGlobal; +use crate::utils::clonecell::CloneCell; pub fn start_compositor(global: GlobalArgs, args: RunArgs) { let logger = Logger::install_compositor(global.log_level.into()); @@ -90,6 +91,8 @@ fn main_(logger: Arc, _args: &RunArgs) -> Result<(), MainError> { globals: Globals::new(), output_ids: Default::default(), root: Rc::new(DisplayNode::new(node_ids.next())), + workspaces: Default::default(), + dummy_output: Default::default(), node_ids, backend_events: AsyncQueue::new(), output_handlers: Default::default(), @@ -111,6 +114,33 @@ fn main_(logger: Arc, _args: &RunArgs) -> Result<(), MainError> { fdcloser: FdCloser::new(), logger, }); + { + let dummy_output = Rc::new(OutputNode { + id: state.node_ids.next(), + position: Default::default(), + global: Rc::new(WlOutputGlobal::new(state.globals.name(), Rc::new(DummyOutput { + id: state.output_ids.next(), + }))), + workspaces: Default::default(), + workspace: Default::default(), + seat_state: Default::default(), + layers: Default::default(), + render_data: Default::default(), + state: state.clone(), + is_dummy: true, + }); + let dummy_workspace = Rc::new(WorkspaceNode { + id: state.node_ids.next(), + output: CloneCell::new(dummy_output.clone()), + container: Default::default(), + stacked: Default::default(), + seat_state: Default::default(), + name: "dummy".to_string() + }); + dummy_output.workspace.set(Some(dummy_workspace.clone())); + dummy_output.workspaces.borrow_mut().push(dummy_workspace); + state.dummy_output.set(Some(dummy_output)); + } forker.install(&state); let config = ConfigProxy::default(&state); state.config.set(Some(Rc::new(config))); diff --git a/src/config.rs b/src/config.rs index e78fb057..305c245c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -87,6 +87,9 @@ impl ConfigProxy { next_id: NumCell::new(1), keymaps: Default::default(), bufs: Default::default(), + workspace_ids: NumCell::new(1), + workspaces_by_name: Default::default(), + workspaces_by_id: Default::default(), }); let init_msg = bincode::encode_to_vec(&InitMessage::V1(V1InitMessage {}), bincode_ops()).unwrap(); diff --git a/src/config/handler.rs b/src/config/handler.rs index ccc8217e..31a88c79 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -3,7 +3,7 @@ use crate::backend::{InputDeviceAccelProfile, InputDeviceCapability, InputDevice use crate::ifs::wl_seat::{SeatId, WlSeatGlobal}; use crate::state::{DeviceHandlerData, State}; use crate::tree::walker::NodeVisitorBase; -use crate::tree::{ContainerNode, ContainerSplit, FloatNode, Node}; +use crate::tree::{ContainerNode, ContainerSplit, FloatNode, Node, WorkspaceNode}; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::debug_fn::debug_fn; use crate::utils::errorfmt::ErrorFmt; @@ -20,12 +20,13 @@ use jay_config::input::{ use jay_config::keyboard::keymap::Keymap; use jay_config::keyboard::mods::Modifiers; use jay_config::keyboard::syms::KeySym; -use jay_config::{Axis, Direction, LogLevel, Seat}; +use jay_config::{Axis, Direction, LogLevel, Seat, Workspace}; use libloading::Library; use log::Level; use std::cell::Cell; use std::rc::Rc; use thiserror::Error; +use crate::utils::clonecell::CloneCell; pub(super) struct ConfigProxyHandler { pub client_data: Cell<*const u8>, @@ -38,6 +39,9 @@ pub(super) struct ConfigProxyHandler { pub next_id: NumCell, pub keymaps: CopyHashMap>, pub bufs: Stack>, + pub workspace_ids: NumCell, + pub workspaces_by_name: CopyHashMap, u64>, + pub workspaces_by_id: CopyHashMap>, } impl ConfigProxyHandler { @@ -156,6 +160,13 @@ impl ConfigProxyHandler { Ok(()) } + fn get_workspace(&self, ws: Workspace) -> Result, CphError> { + match self.workspaces_by_id.get(&ws.0) { + Some(ws) => Ok(ws), + _ => Err(CphError::WorkspaceDoesNotExist(ws)) + } + } + fn get_device_handler_data( &self, device: InputDevice, @@ -253,6 +264,64 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_get_workspace(&self, name: &str) { + let name = Rc::new(name.to_owned()); + let ws = match self.workspaces_by_name.get(&name) { + Some(w) => w, + _ => { + let ws = self.workspace_ids.fetch_add(1); + self.workspaces_by_name.set(name.clone(), ws); + self.workspaces_by_id.set(ws, name); + ws + } + }; + self.respond(Response::GetWorkspace { + workspace: Workspace(ws), + }); + } + + fn handle_show_workspace(&self, seat: Seat, ws: Workspace) -> Result<(), ShowWorkspaceError> { + let seat = self.get_seat(seat)?; + let name = self.get_workspace(ws)?; + let output = match self.state.workspaces.get(name.as_str()) { + Some(ws) => { + let output = ws.output.get(); + output.workspace.set(Some(ws.clone())); + output + } + None => { + let output = seat.get_output(); + if output.is_dummy { + return Ok(()); + } + let ws = Rc::new(WorkspaceNode { + id: self.state.node_ids.next(), + output: CloneCell::new(output.clone()), + container: Default::default(), + stacked: Default::default(), + seat_state: Default::default(), + name: name.to_string(), + }); + output.workspaces.borrow_mut().push(ws.clone()); + output.workspace.set(Some(ws.clone())); + self.state.workspaces.set(name.to_string(), ws); + output + } + }; + output.update_render_data(); + self.state.tree_changed(); + Ok(()) + } + + fn handle_get_device_name(&self, device: InputDevice) -> Result<(), GetDeviceNameError> { + let dev = self.get_device_handler_data(device)?; + let name = dev.device.name(); + self.respond(Response::GetDeviceName { + name: name.to_string(), + }); + Ok(()) + } + fn handle_has_capability( &self, device: InputDevice, @@ -582,6 +651,9 @@ impl ConfigProxyHandler { ClientMessage::SetTransformMatrix { device, matrix } => { self.handle_set_transform_matrix(device, matrix)? } + ClientMessage::GetDeviceName { device } => self.handle_get_device_name(device)?, + ClientMessage::GetWorkspace { name } => self.handle_get_workspace(name), + ClientMessage::ShowWorkspace { seat, workspace } => self.handle_show_workspace(seat, workspace)?, } Ok(()) } @@ -639,12 +711,18 @@ enum CphError { SetAccelSpeedError(#[from] SetAccelSpeedError), #[error("Could not process a `set_transform_matrix` request")] SetTransformMatrixError(#[from] SetTransformMatrixError), + #[error("Could not process a `get_device_name` request")] + GetDeviceNameError(#[from] GetDeviceNameError), + #[error("Could not process a `show_workspace` request")] + ShowWorkspaceError(#[from] ShowWorkspaceError), #[error("Device {0:?} does not exist")] DeviceDoesNotExist(InputDevice), #[error("Device {0:?} does not exist")] KeymapDoesNotExist(Keymap), #[error("Seat {0:?} does not exist")] SeatDoesNotExist(Seat), + #[error("Workspace {0:?} does not exist")] + WorkspaceDoesNotExist(Workspace), #[error("Keyboard {0:?} does not exist")] KeyboardDoesNotExist(InputDevice), #[error("Could not parse the message")] @@ -694,6 +772,20 @@ enum SetTransformMatrixError { } efrom!(SetTransformMatrixError, CphError); +#[derive(Debug, Error)] +enum GetDeviceNameError { + #[error(transparent)] + CphError(#[from] Box), +} +efrom!(GetDeviceNameError, CphError); + +#[derive(Debug, Error)] +enum ShowWorkspaceError { + #[error(transparent)] + CphError(#[from] Box), +} +efrom!(ShowWorkspaceError, CphError); + #[derive(Debug, Error)] enum HasCapabilityError { #[error(transparent)] diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index d5e44b29..d65fa4da 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -59,7 +59,7 @@ pub struct WlOutputGlobal { } impl WlOutputGlobal { - pub fn new(name: GlobalName, output: &Rc) -> Self { + pub fn new(name: GlobalName, output: Rc) -> Self { Self { name, output: output.clone(), diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 30fde601..16472ea4 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -16,7 +16,6 @@ use crate::ifs::ipc::wl_data_source::WlDataSource; use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1; use crate::ifs::ipc::IpcError; -use crate::ifs::wl_output::WlOutputGlobal; use crate::ifs::wl_seat::kb_owner::KbOwnerHolder; use crate::ifs::wl_seat::pointer_owner::PointerOwnerHolder; use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardError, REPEAT_INFO_SINCE}; @@ -27,7 +26,7 @@ use crate::leaks::Tracker; use crate::object::{Object, ObjectId}; use crate::state::State; use crate::tree::toplevel::ToplevelNode; -use crate::tree::{ContainerSplit, FloatNode, FoundNode, Node}; +use crate::tree::{ContainerSplit, FloatNode, FoundNode, Node, OutputNode}; use crate::utils::asyncevent::AsyncEvent; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -49,7 +48,7 @@ use jay_config::Direction; use std::cell::{Cell, RefCell}; use std::collections::hash_map::Entry; use std::mem; -use std::ops::{Deref, DerefMut}; +use std::ops::{DerefMut}; use std::rc::Rc; use thiserror::Error; use uapi::{c, Errno, OwnedFd}; @@ -123,6 +122,7 @@ pub struct WlSeatGlobal { shortcuts: CopyHashMap<(u32, u32), Modifiers>, queue_link: Cell>>>, tree_changed_handler: Cell>>, + output: CloneCell>, } impl WlSeatGlobal { @@ -158,6 +158,7 @@ impl WlSeatGlobal { shortcuts: Default::default(), queue_link: Cell::new(None), tree_changed_handler: Cell::new(None), + output: CloneCell::new(state.dummy_output.get().unwrap()), }); let seat = slf.clone(); let future = state.eng.spawn(async move { @@ -171,15 +172,8 @@ impl WlSeatGlobal { slf } - pub fn get_output(&self) -> Option> { - let ps = self.pointer_stack.borrow_mut(); - for node in ps.deref() { - if node.is_output() { - let on = node.clone().into_output().unwrap(); - return Some(on.global.clone()); - } - } - None + pub fn get_output(&self) -> Rc { + self.output.get() } pub fn mark_last_active(self: &Rc) { diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index f5ec3753..2f8c5132 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -11,7 +11,7 @@ use crate::ifs::wl_surface::xdg_surface::xdg_popup::XdgPopup; use crate::ifs::wl_surface::WlSurface; use crate::object::ObjectId; use crate::tree::toplevel::ToplevelNode; -use crate::tree::{FloatNode, Node}; +use crate::tree::{FloatNode, Node, OutputNode}; use crate::utils::clonecell::CloneCell; use crate::utils::smallmap::SmallMap; use crate::wire::WlDataOfferId; @@ -413,6 +413,10 @@ impl WlSeatGlobal { // self.focus_xdg_surface(&n.xdg); } + pub fn enter_output(self: &Rc, output: &Rc) { + self.output.set(output.clone()); + } + pub fn enter_surface(&self, n: &WlSurface, x: Fixed, y: Fixed) { let serial = self.serial.fetch_add(1); self.surface_pointer_event(0, n, |p| p.send_enter(serial, n.id, x, y)); @@ -427,6 +431,10 @@ impl WlSeatGlobal { self.surface_pointer_event(0, n, |p| p.send_leave(serial, n.id)); self.surface_pointer_frame(n); } + + pub fn leave_output(&self) { + self.output.set(self.state.dummy_output.get().unwrap()); + } } // Unfocus callbacks diff --git a/src/ifs/zwlr_layer_shell_v1.rs b/src/ifs/zwlr_layer_shell_v1.rs index 73410c56..48943f38 100644 --- a/src/ifs/zwlr_layer_shell_v1.rs +++ b/src/ifs/zwlr_layer_shell_v1.rs @@ -70,8 +70,9 @@ impl ZwlrLayerShellV1 { self.client.lookup(req.output)?.global.node.get().unwrap() } else { for seat in self.client.state.seat_queue.rev_iter() { - if let Some(output) = seat.get_output() { - break 'get_output output.node.get().unwrap(); + let output = seat.get_output(); + if !output.is_dummy { + break 'get_output output; } } let outputs = self.client.state.outputs.lock(); diff --git a/src/keymap.xkb b/src/keymap.xkb index 8f0bfc47..57f852a4 100644 --- a/src/keymap.xkb +++ b/src/keymap.xkb @@ -106,6 +106,18 @@ xkb_keymap { <125> = 133; # LEFTMETA <126> = 134; # RIGHTMETA <139> = 147; # MENU + <183> = 191; # F13 + <184> = 192; # F14 + <185> = 193; # F15 + <186> = 194; # F16 + <187> = 195; # F17 + <188> = 196; # F18 + <189> = 197; # F19 + <190> = 198; # F20 + <191> = 199; # F21 + <192> = 200; # F22 + <193> = 201; # F23 + <194> = 202; # F24 <210> = 218; # PRINT # We must include at least one indicator here. Otherwise Xwayland segfaults. @@ -188,6 +200,18 @@ xkb_keymap { key <68> { [ F10 ] }; key <87> { [ F11 ] }; key <88> { [ F12 ] }; + key <183> { [ F13 ] }; + key <184> { [ F14 ] }; + key <185> { [ F15 ] }; + key <186> { [ F16 ] }; + key <187> { [ F17 ] }; + key <188> { [ F18 ] }; + key <189> { [ F19 ] }; + key <190> { [ F20 ] }; + key <191> { [ F21 ] }; + key <192> { [ F22 ] }; + key <193> { [ F23 ] }; + key <194> { [ F24 ] }; key <210> { [ Print ] }; key <70> { [ Scroll_Lock ] }; key <119> { [ Pause ] }; diff --git a/src/libinput/device.rs b/src/libinput/device.rs index 8fc01021..e8d69fb0 100644 --- a/src/libinput/device.rs +++ b/src/libinput/device.rs @@ -2,10 +2,12 @@ use crate::libinput::consts::{AccelProfile, DeviceCapability}; use crate::libinput::sys::{ libinput_device, libinput_device_config_accel_set_profile, libinput_device_config_accel_set_speed, libinput_device_config_left_handed_set, - libinput_device_get_user_data, libinput_device_has_capability, libinput_device_set_user_data, - libinput_device_unref, libinput_path_remove_device, + libinput_device_get_name, libinput_device_get_user_data, libinput_device_has_capability, + libinput_device_set_user_data, libinput_device_unref, libinput_path_remove_device, }; use crate::libinput::LibInput; +use bstr::ByteSlice; +use std::ffi::CStr; use std::marker::PhantomData; use std::rc::Rc; @@ -65,6 +67,13 @@ impl<'a> LibInputDevice<'a> { libinput_device_config_accel_set_speed(self.dev, speed); } } + + pub fn name(&self) -> String { + unsafe { + let name = libinput_device_get_name(self.dev); + CStr::from_ptr(name).to_bytes().as_bstr().to_string() + } + } } impl RegisteredDevice { diff --git a/src/libinput/sys.rs b/src/libinput/sys.rs index c9507064..edb7a1e8 100644 --- a/src/libinput/sys.rs +++ b/src/libinput/sys.rs @@ -54,6 +54,7 @@ extern "C" { device: *mut libinput_device, speed: f64, ) -> libinput_config_status; + pub fn libinput_device_get_name(device: *mut libinput_device) -> *const c::c_char; pub fn libinput_event_destroy(event: *mut libinput_event); pub fn libinput_event_get_type(event: *mut libinput_event) -> libinput_event_type; diff --git a/src/pango.rs b/src/pango.rs index b4053375..5b80b0e0 100644 --- a/src/pango.rs +++ b/src/pango.rs @@ -1,7 +1,9 @@ #![allow(non_camel_case_types)] use crate::pango::consts::{CairoFormat, CairoOperator, PangoEllipsizeMode}; +use crate::rect::Rect; use std::cell::Cell; +use std::ptr; use std::rc::Rc; use thiserror::Error; use uapi::{c, IntoUstr}; @@ -73,6 +75,13 @@ extern "C" { width: *mut c::c_int, height: *mut c::c_int, ); + fn pango_layout_get_extents( + layout: *mut PangoLayout_, + ink_rect: *mut PangoRectangle, + logical_rect: *mut PangoRectangle, + ); + + fn pango_extents_to_pixels(inclusive: *mut PangoRectangle, nearest: *mut PangoRectangle); } #[derive(Debug, Error)] @@ -89,6 +98,14 @@ pub enum PangoError { GetData, } +#[repr(C)] +struct PangoRectangle { + x: c::c_int, + y: c::c_int, + width: c::c_int, + height: c::c_int, +} + pub struct CairoImageSurface { s: *mut cairo_surface_t, } @@ -285,6 +302,20 @@ impl PangoLayout { } } + pub fn inc_pixel_rect(&self) -> Rect { + unsafe { + let mut rect = PangoRectangle { + x: 0, + y: 0, + width: 0, + height: 0, + }; + pango_layout_get_extents(self.l, &mut rect, ptr::null_mut()); + pango_extents_to_pixels(&mut rect, ptr::null_mut()); + Rect::new_sized(rect.x, rect.y, rect.width, rect.height).unwrap() + } + } + pub fn show_layout(&self) { unsafe { pango_cairo_show_layout(self.c.c.c, self.l); diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 106e2374..1655205c 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -39,14 +39,26 @@ impl Renderer<'_> { } render_layer!(output.layers[0]); render_layer!(output.layers[1]); + let theme = &self.state.theme; + let th = theme.title_height.get(); + { + let rd = output.render_data.borrow_mut(); + let c = theme.active_title_color.get(); + self.fill_boxes(std::slice::from_ref(&rd.active_workspace), &c); + let c = theme.title_color.get(); + self.fill_boxes(&rd.inactive_workspaces, &c); + for title in &rd.titles { + self.render_texture(&title.tex, x + title.x, y + title.y, ARGB8888); + } + } if let Some(ws) = output.workspace.get() { - self.render_workspace(&ws, x, y); + self.render_workspace(&ws, x, y + th); } render_layer!(output.layers[2]); render_layer!(output.layers[3]); - for stacked in output.display.xstacked.iter() { + for stacked in self.state.root.xstacked.iter() { let pos = stacked.absolute_position(); - stacked.render(self, pos.x1(), pos.y1()); + stacked.render(self, x + pos.x1(), y + pos.y1()); } } diff --git a/src/render/renderer/texture.rs b/src/render/renderer/texture.rs index 83a01f13..1eca86e0 100644 --- a/src/render/renderer/texture.rs +++ b/src/render/renderer/texture.rs @@ -6,3 +6,9 @@ pub struct Texture { pub(super) ctx: Rc, pub(super) gl: GlTexture, } + +impl Texture { + pub fn width(&self) -> i32 { + self.gl.width + } +} diff --git a/src/state.rs b/src/state.rs index d31f464e..2333eaa5 100644 --- a/src/state.rs +++ b/src/state.rs @@ -16,8 +16,9 @@ use crate::logger::Logger; use crate::rect::Rect; use crate::render::RenderContext; use crate::theme::Theme; +use crate::tree::walker::NodeVisitorBase; use crate::tree::{ - ContainerNode, ContainerSplit, DisplayNode, FloatNode, Node, NodeIds, WorkspaceNode, + ContainerNode, ContainerSplit, DisplayNode, FloatNode, Node, NodeIds, OutputNode, WorkspaceNode, }; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; @@ -51,6 +52,8 @@ pub struct State { pub input_device_ids: InputDeviceIds, pub node_ids: NodeIds, pub root: Rc, + pub workspaces: CopyHashMap>, + pub dummy_output: CloneCell>>, pub backend_events: AsyncQueue, pub output_handlers: RefCell>>, pub input_device_handlers: RefCell>, @@ -92,6 +95,25 @@ impl State { }; self.cursors.set(cursors); self.render_ctx.set(Some(ctx.clone())); + + struct Walker; + impl NodeVisitorBase for Walker { + fn visit_container(&mut self, node: &Rc) { + node.schedule_compute_render_data(); + node.visit_children(self); + } + + fn visit_output(&mut self, node: &Rc) { + node.update_render_data(); + node.visit_children(self); + } + + fn visit_float(&mut self, node: &Rc) { + node.schedule_render_titles(); + node.visit_children(self); + } + } + self.root.clone().visit(&mut Walker); } pub fn add_global(&self, global: &Rc) { diff --git a/src/tasks/output.rs b/src/tasks/output.rs index 45545151..c199f5fb 100644 --- a/src/tasks/output.rs +++ b/src/tasks/output.rs @@ -2,7 +2,7 @@ use crate::backend::Output; use crate::ifs::wl_output::WlOutputGlobal; use crate::rect::Rect; use crate::state::State; -use crate::tree::{Node, OutputNode, WorkspaceNode}; +use crate::tree::{Node, OutputNode, OutputRenderData, WorkspaceNode}; use crate::utils::asyncevent::AsyncEvent; use crate::utils::clonecell::CloneCell; use std::cell::{Cell, RefCell}; @@ -21,9 +21,8 @@ impl OutputHandler { self.output.on_change(Rc::new(move || ae.trigger())); } let name = self.state.globals.name(); - let global = Rc::new(WlOutputGlobal::new(name, &self.output)); + let global = Rc::new(WlOutputGlobal::new(name, self.output.clone())); let on = Rc::new(OutputNode { - display: self.state.root.clone(), id: self.state.node_ids.next(), workspaces: RefCell::new(vec![]), position: Cell::new(Default::default()), @@ -31,17 +30,36 @@ impl OutputHandler { seat_state: Default::default(), global: global.clone(), layers: Default::default(), + render_data: RefCell::new(OutputRenderData { + active_workspace: Rect::new_empty(0, 0), + inactive_workspaces: Default::default(), + titles: Default::default(), + }), + state: self.state.clone(), + is_dummy: false, }); global.node.set(Some(on.clone())); + let name = 'name: { + for i in 1.. { + let name = i.to_string(); + if !self.state.workspaces.contains(&name) { + break 'name name; + } + } + unreachable!(); + }; let workspace = Rc::new(WorkspaceNode { id: self.state.node_ids.next(), output: CloneCell::new(on.clone()), container: Default::default(), stacked: Default::default(), seat_state: Default::default(), + name: name.clone(), }); + self.state.workspaces.set(name, workspace.clone()); on.workspaces.borrow_mut().push(workspace.clone()); on.workspace.set(Some(workspace)); + on.update_render_data(); self.state.root.outputs.set(self.output.id(), on.clone()); self.state.add_global(&global); self.state.outputs.set(self.output.id(), global.clone()); diff --git a/src/text.rs b/src/text.rs index 3ba21475..cf50b138 100644 --- a/src/text.rs +++ b/src/text.rs @@ -2,9 +2,14 @@ use crate::format::ARGB8888; use crate::pango::consts::{ CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE, PANGO_ELLIPSIZE_END, PANGO_SCALE, }; -use crate::pango::{CairoImageSurface, PangoError, PangoFontDescription}; +use crate::pango::{ + CairoContext, CairoImageSurface, PangoCairoContext, PangoError, PangoFontDescription, + PangoLayout, +}; +use crate::rect::Rect; use crate::render::{RenderContext, RenderError, Texture}; use crate::theme::Color; +use std::ops::Neg; use std::rc::Rc; use thiserror::Error; @@ -24,14 +29,15 @@ pub enum TextError { ImageData(#[source] PangoError), } -pub fn render( - ctx: &Rc, - width: i32, - height: i32, - font: &str, - text: &str, - color: Color, -) -> Result, TextError> { +struct Data { + image: Rc, + cctx: Rc, + _pctx: Rc, + _fd: PangoFontDescription, + layout: PangoLayout, +} + +fn create_data(font: &str, width: i32, height: i32) -> Result { let image = match CairoImageSurface::new_image_surface(CAIRO_FORMAT_ARGB32, width, height) { Ok(s) => s, Err(e) => return Err(TextError::CreateImage(e)), @@ -49,22 +55,86 @@ pub fn render( Ok(l) => l, Err(e) => return Err(TextError::CreateLayout(e)), }; - layout.set_width((width - 2).max(0) * PANGO_SCALE); - layout.set_ellipsize(PANGO_ELLIPSIZE_END); layout.set_font_description(&fd); - layout.set_text(text); - let font_height = layout.pixel_size().1; - cctx.set_operator(CAIRO_OPERATOR_SOURCE); - cctx.set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _); - cctx.move_to(1.0, ((height - font_height) / 2) as f64); - layout.show_layout(); - image.flush(); - let data = match image.data() { + Ok(Data { + image, + cctx, + _pctx: pctx, + _fd: fd, + layout, + }) +} + +pub fn measure(font: &str, text: &str) -> Result { + let data = create_data(font, 1, 1)?; + data.layout.set_text(text); + Ok(data.layout.inc_pixel_rect()) +} + +pub fn render( + ctx: &Rc, + width: i32, + height: i32, + font: &str, + text: &str, + color: Color, +) -> Result, TextError> { + render2(ctx, 1, width, height, 1, font, text, color, true) +} + +pub fn render2( + ctx: &Rc, + x: i32, + width: i32, + height: i32, + padding: i32, + font: &str, + text: &str, + color: Color, + ellipsize: bool, +) -> Result, TextError> { + let data = create_data(font, width, height)?; + if ellipsize { + data.layout + .set_width((width - 2 * padding).max(0) * PANGO_SCALE); + data.layout.set_ellipsize(PANGO_ELLIPSIZE_END); + } + data.layout.set_text(text); + let font_height = data.layout.pixel_size().1; + data.cctx.set_operator(CAIRO_OPERATOR_SOURCE); + data.cctx + .set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _); + data.cctx + .move_to(x as f64, ((height - font_height) / 2) as f64); + data.layout.show_layout(); + data.image.flush(); + let bytes = match data.image.data() { Ok(d) => d, Err(e) => return Err(TextError::ImageData(e)), }; - match ctx.shmem_texture(data, ARGB8888, width, height, image.stride()) { + match ctx.shmem_texture(bytes, ARGB8888, width, height, data.image.stride()) { Ok(t) => Ok(t), Err(e) => Err(TextError::RenderError(e)), } } + +pub fn render_fitting( + ctx: &Rc, + height: i32, + font: &str, + text: &str, + color: Color, +) -> Result, TextError> { + let rect = measure(font, text)?; + render2( + ctx, + rect.x1().neg(), + rect.width(), + height, + 0, + font, + text, + color, + false, + ) +} diff --git a/src/tree/container.rs b/src/tree/container.rs index 6a5ec662..76424f6b 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -542,7 +542,7 @@ impl ContainerNode { self.parent.get().child_title_changed(&**self, &title); } - fn schedule_compute_render_data(self: &Rc) { + pub fn schedule_compute_render_data(self: &Rc) { if !self.compute_render_data_scheduled.replace(true) { self.state.pending_container_render_data.push(self.clone()); } diff --git a/src/tree/float.rs b/src/tree/float.rs index d7307660..3dff23ab 100644 --- a/src/tree/float.rs +++ b/src/tree/float.rs @@ -151,7 +151,7 @@ impl FloatNode { self.schedule_render_titles(); } - fn schedule_render_titles(self: &Rc) { + pub fn schedule_render_titles(self: &Rc) { if !self.render_titles_scheduled.replace(true) { self.state.pending_float_titles.push(self.clone()); } diff --git a/src/tree/output.rs b/src/tree/output.rs index 7d4d193d..7ca23e58 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -3,19 +3,23 @@ use crate::ifs::wl_output::WlOutputGlobal; use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use crate::rect::Rect; -use crate::render::Renderer; +use crate::render::{Renderer, Texture}; +use crate::state::State; +use crate::text; +use crate::theme::Color; use crate::tree::walker::NodeVisitor; -use crate::tree::{DisplayNode, FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode}; +use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode}; use crate::utils::clonecell::CloneCell; +use crate::utils::errorfmt::ErrorFmt; use crate::utils::linkedlist::LinkedList; use std::cell::{Cell, RefCell}; use std::fmt::{Debug, Formatter}; -use std::ops::Deref; +use std::ops::{Deref, Sub}; use std::rc::Rc; +use crate::fixed::Fixed; tree_id!(OutputNodeId); pub struct OutputNode { - pub display: Rc, pub id: OutputNodeId, pub position: Cell, pub global: Rc, @@ -23,6 +27,70 @@ pub struct OutputNode { pub workspace: CloneCell>>, pub seat_state: NodeSeatState, pub layers: [LinkedList>; 4], + pub render_data: RefCell, + pub state: Rc, + pub is_dummy: bool, +} + +impl OutputNode { + pub fn update_render_data(&self) { + let mut rd = self.render_data.borrow_mut(); + rd.titles.clear(); + rd.inactive_workspaces.clear(); + let workspaces = self.workspaces.borrow_mut(); + let mut pos = 0; + let font = self.state.theme.font.borrow_mut(); + let th = self.state.theme.title_height.get(); + let active_id = self.workspace.get().map(|w| w.id); + for ws in workspaces.deref() { + let mut title_width = th; + 'create_texture: { + if let Some(ctx) = self.state.render_ctx.get() { + if th == 0 || ws.name.is_empty() { + break 'create_texture; + } + let title = match text::render_fitting(&ctx, th, &font, &ws.name, Color::GREY) { + Ok(t) => t, + Err(e) => { + log::error!("Could not render title {}: {}", ws.name, ErrorFmt(e)); + break 'create_texture; + } + }; + let mut x = pos + 1; + if title.width() + 2 > title_width { + title_width = title.width() + 2; + } else { + x = pos + (title_width - title.width()) / 2; + } + rd.titles.push(OutputTitle { + x, + y: 0, + tex: title, + }); + } + } + let rect = Rect::new_sized(pos, 0, title_width, th).unwrap(); + if Some(ws.id) == active_id { + rd.active_workspace = rect; + } else { + rd.inactive_workspaces.push(rect); + } + pos += title_width; + } + } +} + +pub struct OutputTitle { + pub x: i32, + pub y: i32, + pub tex: Rc, +} + +#[derive(Default)] +pub struct OutputRenderData { + pub active_workspace: Rect, + pub inactive_workspaces: Vec, + pub titles: Vec, } impl Debug for OutputNode { @@ -32,6 +100,14 @@ impl Debug for OutputNode { } impl Node for OutputNode { + fn pointer_enter(self: Rc, seat: &Rc, _x: Fixed, _y: Fixed) { + seat.enter_output(&self) + } + + fn leave(&self, seat: &WlSeatGlobal) { + seat.leave_output(); + } + fn id(&self) -> NodeId { self.id.into() } @@ -42,7 +118,7 @@ impl Node for OutputNode { fn destroy_node(&self, detach: bool) { if detach { - self.display.clone().remove_child(self); + self.state.root.clone().remove_child(self); } let mut workspaces = self.workspaces.borrow_mut(); for workspace in workspaces.drain(..) { @@ -104,9 +180,17 @@ impl Node for OutputNode { } fn change_extents(self: Rc, rect: &Rect) { + let th = self.state.theme.title_height.get(); self.position.set(*rect); if let Some(c) = self.workspace.get() { - c.change_extents(rect); + let wrect = Rect::new_sized( + rect.x1(), + rect.y1() + th, + rect.width(), + rect.height().sub(th).max(0), + ) + .unwrap(); + c.change_extents(&wrect); } for layer in &self.layers { for surface in layer.iter() { diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index 639f430d..a1d77e82 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -18,6 +18,7 @@ pub struct WorkspaceNode { pub container: CloneCell>>, pub stacked: LinkedList>, pub seat_state: NodeSeatState, + pub name: String, } impl WorkspaceNode { diff --git a/src/utils/copyhashmap.rs b/src/utils/copyhashmap.rs index aea91442..d55ee385 100644 --- a/src/utils/copyhashmap.rs +++ b/src/utils/copyhashmap.rs @@ -1,4 +1,5 @@ use ahash::AHashMap; +use std::borrow::Borrow; use std::cell::{RefCell, RefMut}; use std::fmt::{Debug, Formatter}; use std::hash::Hash; @@ -31,9 +32,11 @@ impl CopyHashMap { self.map.borrow_mut().insert(k, v); } - pub fn get(&self, k: &K) -> Option + pub fn get(&self, k: &Q) -> Option where V: Clone, + Q: Hash + Eq, + K: Borrow, { self.map.borrow_mut().get(k).cloned() } @@ -42,7 +45,11 @@ impl CopyHashMap { self.map.borrow_mut().remove(k) } - pub fn contains(&self, k: &K) -> bool { + pub fn contains(&self, k: &Q) -> bool + where + Q: Hash + Eq, + K: Borrow, + { self.map.borrow_mut().contains_key(k) }