shell: add workspace helper module

This commit is contained in:
Victoria Brekenfeld 2021-12-17 17:53:01 +01:00
parent 74a4ed1058
commit e0da1e779b
7 changed files with 256 additions and 20 deletions

6
Cargo.lock generated
View file

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

View file

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

View file

@ -31,14 +31,15 @@ use std::{
cell::RefCell,
rc::Rc,
sync::{Arc, Mutex},
time::Instant,
};
pub struct X11State {
handle: X11Handle,
allocator: Arc<Mutex<GbmDevice<DrmNode>>>,
_egl: EGLDisplay,
renderer: Rc<RefCell<Gles2Renderer>>,
surfaces: Vec<Surface>,
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>, 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>, 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(())
}

View file

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

3
src/shell/mod.rs Normal file
View file

@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-only
pub mod workspaces;

232
src/shell/workspaces.rs Normal file
View file

@ -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<Option<usize>>);
impl ActiveWorkspace {
fn new() -> Self {
ActiveWorkspace(Cell::new(None))
}
fn get(&self) -> Option<usize> {
self.0.get()
}
fn set(&self, active: usize) -> Option<usize> {
self.0.replace(Some(active))
}
fn clear(&self) -> Option<usize> {
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<Output>,
spaces: [Space; MAX_WORKSPACES],
}
const UNINIT_SPACE: MaybeUninit<Space> = 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::<ActiveWorkspace>()
.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::<ActiveWorkspace>()
.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::<ActiveWorkspace>() {
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::<ActiveWorkspace>()
.unwrap()
.get()
.unwrap()
})
.unwrap_or(0);
let mut x = 0;
for output in &self.outputs {
let old_active = output
.user_data()
.get::<ActiveWorkspace>()
.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::<Vec<_>>();
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<Item = &Output> {
self.outputs.iter()
}
pub fn active_space(&self, output: &Output) -> &Space {
match &self.mode {
Mode::OutputBound => {
let active = output
.user_data()
.get::<ActiveWorkspace>()
.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::<ActiveWorkspace>()
.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()
}
}
}

View file

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