From e0da1e779b9538e316fadd4f1fd6466d55394e80 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 17 Dec 2021 17:53:01 +0100 Subject: [PATCH] shell: add workspace helper module --- Cargo.lock | 6 +- Cargo.toml | 2 +- src/backend/x11.rs | 21 ++-- src/main.rs | 4 +- src/shell/mod.rs | 3 + src/shell/workspaces.rs | 232 ++++++++++++++++++++++++++++++++++++++++ src/state.rs | 8 +- 7 files changed, 256 insertions(+), 20 deletions(-) create mode 100644 src/shell/mod.rs create mode 100644 src/shell/workspaces.rs diff --git a/Cargo.lock b/Cargo.lock index 31a64f41..6a4eb73a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,9 +422,9 @@ checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] name = "proc-macro2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a" +checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" dependencies = [ "unicode-xid", ] @@ -592,7 +592,7 @@ checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/Smithay/smithay.git?rev=87ab37a1#87ab37a1ac16328db15d0efa242dfdac6e1edfd7" +source = "git+https://github.com/Smithay/smithay.git?rev=cb0bc33#cb0bc3379faf183087c90355edd80d1bb5e33841" dependencies = [ "appendlist", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index 6c060b06..2ae351ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,6 @@ slog-stdlog = "4.1" [dependencies.smithay] version = "0.3" git = "https://github.com/Smithay/smithay.git" -rev = "87ab37a1" +rev = "cb0bc33" default-features = false features = ["backend_x11", "backend_egl", "desktop", "use_system_lib", "renderer_gl", "wayland_frontend"] \ No newline at end of file diff --git a/src/backend/x11.rs b/src/backend/x11.rs index f62ae707..24d00f37 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -31,14 +31,15 @@ use std::{ cell::RefCell, rc::Rc, sync::{Arc, Mutex}, + time::Instant, }; pub struct X11State { - handle: X11Handle, allocator: Arc>>, _egl: EGLDisplay, renderer: Rc>, surfaces: Vec, + handle: X11Handle, } impl X11State { @@ -51,7 +52,7 @@ impl X11State { .title("COSMIC") .build(&self.handle) .with_context(|| "Failed to create window")?; - let fourcc = window.format().unwrap(); + let fourcc = window.format(); let surface = self .handle .create_surface( @@ -93,9 +94,11 @@ impl X11State { .iter_mut() .find(|s| s.output == output_ref) { - if let Err(err) = surface - .render_from_space(&mut *x11_state.renderer.borrow_mut(), &mut state.spaces) - { + if let Err(err) = surface.render_from_space( + &mut *x11_state.renderer.borrow_mut(), + state.spaces.active_space_mut(&output_ref), + &state.start_time, + ) { slog_scope::error!("Error rendering: {}", err); } } @@ -129,6 +132,7 @@ impl Surface { &mut self, renderer: &mut Gles2Renderer, space: &mut Space, + start_time: &Instant, ) -> Result<()> { let (buffer, age) = self .surface @@ -145,6 +149,7 @@ impl Surface { ) { Ok(true) => { slog_scope::trace!("Finished rendering"); + space.send_frames(false, start_time.elapsed().as_millis() as u32); self.surface .submit() .with_context(|| "Failed to submit buffer for display")?; @@ -198,9 +203,7 @@ pub fn init_backend(event_loop: &mut EventLoop, state: &mut State) -> Res .x11() .add_window(&mut *state.display.borrow_mut(), event_loop.handle()) .with_context(|| "Failed to create wl_output")?; - state - .spaces - .map_output(&output, 1.0, (0, 0).into() /* TODO */); + state.spaces.map_output(&output); event_loop .handle() @@ -255,7 +258,7 @@ pub fn init_backend(event_loop: &mut EventLoop, state: &mut State) -> Res X11Event::Input(event) => { /*TODO*/ } }) - .expect("Failed to insert X11 Backend into event loop"); + .map_err(|_| anyhow::anyhow!("Failed to insert X11 Backend into event loop"))?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index 0c48e56b..9b0943e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use anyhow::{Context, Result}; use slog::Drain; pub mod backend; +pub mod shell; pub mod state; pub mod utils; @@ -38,9 +39,6 @@ fn main() -> Result<()> { } // trigger routines - state - .spaces - .send_frames(false, state.start_time.elapsed().as_millis() as u32); state.spaces.refresh(); // send out events diff --git a/src/shell/mod.rs b/src/shell/mod.rs new file mode 100644 index 00000000..c735ede3 --- /dev/null +++ b/src/shell/mod.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-3.0-only + +pub mod workspaces; diff --git a/src/shell/workspaces.rs b/src/shell/workspaces.rs new file mode 100644 index 00000000..4167f340 --- /dev/null +++ b/src/shell/workspaces.rs @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-3.0-only + +pub use smithay::{desktop::Space, wayland::output::Output}; +use std::{cell::Cell, mem::MaybeUninit}; + +const MAX_WORKSPACES: usize = 10; // TODO? + +pub struct ActiveWorkspace(Cell>); +impl ActiveWorkspace { + fn new() -> Self { + ActiveWorkspace(Cell::new(None)) + } + fn get(&self) -> Option { + self.0.get() + } + fn set(&self, active: usize) -> Option { + self.0.replace(Some(active)) + } + fn clear(&self) -> Option { + self.0.replace(None) + } +} + +pub enum Mode { + OutputBound, + Global { active: usize }, +} + +impl Mode { + pub fn output_bound() -> Mode { + Mode::OutputBound + } + + pub fn global() -> Mode { + Mode::Global { active: 0 } + } +} + +pub struct Workspaces { + mode: Mode, + outputs: Vec, + spaces: [Space; MAX_WORKSPACES], +} + +const UNINIT_SPACE: MaybeUninit = MaybeUninit::uninit(); + +impl Workspaces { + pub fn new() -> Self { + Workspaces { + mode: Mode::global(), + outputs: Vec::new(), + spaces: unsafe { + let mut spaces = [UNINIT_SPACE; MAX_WORKSPACES]; + spaces.fill_with(|| MaybeUninit::new(Space::new(None))); + std::mem::transmute(spaces) + }, + } + } + + pub fn map_output(&mut self, output: &Output) { + match self.mode { + Mode::OutputBound => { + output + .user_data() + .insert_if_missing(|| ActiveWorkspace::new()); + + let (idx, space) = self + .spaces + .iter_mut() + .enumerate() + .find(|(_, x)| x.outputs().next().is_none()) + .expect("More then 10 outputs?"); + output + .user_data() + .get::() + .unwrap() + .set(idx); + space.map_output(output, 1.0, (0, 0)); + self.outputs.push(output.clone()); + } + Mode::Global { active } => { + // just put new outputs on the right of the previous ones. + // in the future we will only need that as a fallback and need to read saved configurations here + let space = &mut self.spaces[active]; + let x = space + .outputs() + .map(|output| space.output_geometry(&output).unwrap()) + .fold(0, |acc, geo| std::cmp::max(acc, geo.loc.x + geo.size.w)); + space.map_output(output, 1.0, (x, 0)); + self.outputs.push(output.clone()); + } + } + } + + pub fn unmap_output(&mut self, output: &Output) { + match self.mode { + Mode::OutputBound => { + if let Some(idx) = output + .user_data() + .get::() + .and_then(|a| a.get()) + { + self.spaces[idx].unmap_output(output); + self.outputs.retain(|o| o != output); + } + } + Mode::Global { active } => { + self.spaces[active].unmap_output(output); + self.outputs.retain(|o| o != output); + // TODO move windows and outputs farther on the right / or load save config for remaining monitors + } + } + } + + pub fn activate(&mut self, output: &Output, idx: usize) { + match self.mode { + Mode::OutputBound => { + // TODO check for other outputs already occupying that space + if let Some(active) = output.user_data().get::() { + if let Some(old_idx) = active.set(idx) { + self.spaces[old_idx].unmap_output(output); + } + self.spaces[idx].map_output(output, 1.0, (0, 0)); + } + // TODO translate windows from previous space size into new size + } + Mode::Global { ref mut active } => { + let old = *active; + *active = idx; + for output in &self.outputs { + let loc = self.spaces[old].output_geometry(output).unwrap().loc; + self.spaces[old].unmap_output(output); + self.spaces[*active].map_output(output, 1.0, loc); + } + } + }; + } + + pub fn set_mode(&mut self, mode: Mode) { + match (&mut self.mode, mode) { + (Mode::OutputBound, Mode::Global { .. }) => { + let active = self + .outputs + .iter() + .next() + .map(|o| { + o.user_data() + .get::() + .unwrap() + .get() + .unwrap() + }) + .unwrap_or(0); + let mut x = 0; + + for output in &self.outputs { + let old_active = output + .user_data() + .get::() + .unwrap() + .clear() + .unwrap(); + let width = self.spaces[old_active] + .output_geometry(output) + .unwrap() + .size + .w; + self.spaces[old_active].unmap_output(output); + self.spaces[active].map_output(output, 1.0, (x, 0)); + x += width; + } + + self.mode = Mode::Global { active }; + // TODO move windows into new bounds + } + (Mode::Global { active }, new @ Mode::OutputBound) => { + for output in &self.outputs { + self.spaces[*active].unmap_output(output); + } + + self.mode = new; + let outputs = self.outputs.drain(..).collect::>(); + for output in &outputs { + self.map_output(output); + } + // TODO move windows into new bounds + // TODO active should probably be mapped somewhere + } + _ => {} + }; + } + + pub fn outputs(&self) -> impl Iterator { + self.outputs.iter() + } + + pub fn active_space(&self, output: &Output) -> &Space { + match &self.mode { + Mode::OutputBound => { + let active = output + .user_data() + .get::() + .unwrap() + .get() + .unwrap(); + &self.spaces[active] + } + Mode::Global { active } => &self.spaces[*active], + } + } + + pub fn active_space_mut(&mut self, output: &Output) -> &mut Space { + match &self.mode { + Mode::OutputBound => { + let active = output + .user_data() + .get::() + .unwrap() + .get() + .unwrap(); + &mut self.spaces[active] + } + Mode::Global { active } => &mut self.spaces[*active], + } + } + + pub fn refresh(&mut self) { + for space in &mut self.spaces { + space.refresh() + } + } +} diff --git a/src/state.rs b/src/state.rs index 27e0d327..71e51340 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::backend::x11::X11State; -use smithay::{desktop::Space, reexports::wayland_server::Display}; +use crate::{backend::x11::X11State, shell::workspaces::Workspaces}; +use smithay::reexports::wayland_server::Display; use std::{cell::RefCell, rc::Rc, time::Instant}; pub struct State { pub display: Rc>, - pub spaces: Space, //TODO: Multiple workspaces + pub spaces: Workspaces, pub start_time: Instant, pub should_stop: bool, @@ -34,7 +34,7 @@ impl State { pub fn new(display: Display) -> State { State { display: Rc::new(RefCell::new(display)), - spaces: Space::new(slog_scope::logger() /*TODO*/), + spaces: Workspaces::new(), start_time: Instant::now(), should_stop: false,