shell: add workspace helper module
This commit is contained in:
parent
74a4ed1058
commit
e0da1e779b
7 changed files with 256 additions and 20 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -422,9 +422,9 @@ checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.33"
|
version = "1.0.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
|
checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
@ -592,7 +592,7 @@ checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay"
|
name = "smithay"
|
||||||
version = "0.3.0"
|
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 = [
|
dependencies = [
|
||||||
"appendlist",
|
"appendlist",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,6 @@ slog-stdlog = "4.1"
|
||||||
[dependencies.smithay]
|
[dependencies.smithay]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
git = "https://github.com/Smithay/smithay.git"
|
git = "https://github.com/Smithay/smithay.git"
|
||||||
rev = "87ab37a1"
|
rev = "cb0bc33"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["backend_x11", "backend_egl", "desktop", "use_system_lib", "renderer_gl", "wayland_frontend"]
|
features = ["backend_x11", "backend_egl", "desktop", "use_system_lib", "renderer_gl", "wayland_frontend"]
|
||||||
|
|
@ -31,14 +31,15 @@ use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct X11State {
|
pub struct X11State {
|
||||||
handle: X11Handle,
|
|
||||||
allocator: Arc<Mutex<GbmDevice<DrmNode>>>,
|
allocator: Arc<Mutex<GbmDevice<DrmNode>>>,
|
||||||
_egl: EGLDisplay,
|
_egl: EGLDisplay,
|
||||||
renderer: Rc<RefCell<Gles2Renderer>>,
|
renderer: Rc<RefCell<Gles2Renderer>>,
|
||||||
surfaces: Vec<Surface>,
|
surfaces: Vec<Surface>,
|
||||||
|
handle: X11Handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11State {
|
impl X11State {
|
||||||
|
|
@ -51,7 +52,7 @@ impl X11State {
|
||||||
.title("COSMIC")
|
.title("COSMIC")
|
||||||
.build(&self.handle)
|
.build(&self.handle)
|
||||||
.with_context(|| "Failed to create window")?;
|
.with_context(|| "Failed to create window")?;
|
||||||
let fourcc = window.format().unwrap();
|
let fourcc = window.format();
|
||||||
let surface = self
|
let surface = self
|
||||||
.handle
|
.handle
|
||||||
.create_surface(
|
.create_surface(
|
||||||
|
|
@ -93,9 +94,11 @@ impl X11State {
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|s| s.output == output_ref)
|
.find(|s| s.output == output_ref)
|
||||||
{
|
{
|
||||||
if let Err(err) = surface
|
if let Err(err) = surface.render_from_space(
|
||||||
.render_from_space(&mut *x11_state.renderer.borrow_mut(), &mut state.spaces)
|
&mut *x11_state.renderer.borrow_mut(),
|
||||||
{
|
state.spaces.active_space_mut(&output_ref),
|
||||||
|
&state.start_time,
|
||||||
|
) {
|
||||||
slog_scope::error!("Error rendering: {}", err);
|
slog_scope::error!("Error rendering: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +132,7 @@ impl Surface {
|
||||||
&mut self,
|
&mut self,
|
||||||
renderer: &mut Gles2Renderer,
|
renderer: &mut Gles2Renderer,
|
||||||
space: &mut Space,
|
space: &mut Space,
|
||||||
|
start_time: &Instant,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (buffer, age) = self
|
let (buffer, age) = self
|
||||||
.surface
|
.surface
|
||||||
|
|
@ -145,6 +149,7 @@ impl Surface {
|
||||||
) {
|
) {
|
||||||
Ok(true) => {
|
Ok(true) => {
|
||||||
slog_scope::trace!("Finished rendering");
|
slog_scope::trace!("Finished rendering");
|
||||||
|
space.send_frames(false, start_time.elapsed().as_millis() as u32);
|
||||||
self.surface
|
self.surface
|
||||||
.submit()
|
.submit()
|
||||||
.with_context(|| "Failed to submit buffer for display")?;
|
.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()
|
.x11()
|
||||||
.add_window(&mut *state.display.borrow_mut(), event_loop.handle())
|
.add_window(&mut *state.display.borrow_mut(), event_loop.handle())
|
||||||
.with_context(|| "Failed to create wl_output")?;
|
.with_context(|| "Failed to create wl_output")?;
|
||||||
state
|
state.spaces.map_output(&output);
|
||||||
.spaces
|
|
||||||
.map_output(&output, 1.0, (0, 0).into() /* TODO */);
|
|
||||||
|
|
||||||
event_loop
|
event_loop
|
||||||
.handle()
|
.handle()
|
||||||
|
|
@ -255,7 +258,7 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
|
|
||||||
X11Event::Input(event) => { /*TODO*/ }
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use anyhow::{Context, Result};
|
||||||
use slog::Drain;
|
use slog::Drain;
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
|
pub mod shell;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
|
@ -38,9 +39,6 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// trigger routines
|
// trigger routines
|
||||||
state
|
|
||||||
.spaces
|
|
||||||
.send_frames(false, state.start_time.elapsed().as_millis() as u32);
|
|
||||||
state.spaces.refresh();
|
state.spaces.refresh();
|
||||||
|
|
||||||
// send out events
|
// send out events
|
||||||
|
|
|
||||||
3
src/shell/mod.rs
Normal file
3
src/shell/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
pub mod workspaces;
|
||||||
232
src/shell/workspaces.rs
Normal file
232
src/shell/workspaces.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::backend::x11::X11State;
|
use crate::{backend::x11::X11State, shell::workspaces::Workspaces};
|
||||||
use smithay::{desktop::Space, reexports::wayland_server::Display};
|
use smithay::reexports::wayland_server::Display;
|
||||||
use std::{cell::RefCell, rc::Rc, time::Instant};
|
use std::{cell::RefCell, rc::Rc, time::Instant};
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub display: Rc<RefCell<Display>>,
|
pub display: Rc<RefCell<Display>>,
|
||||||
pub spaces: Space, //TODO: Multiple workspaces
|
pub spaces: Workspaces,
|
||||||
|
|
||||||
pub start_time: Instant,
|
pub start_time: Instant,
|
||||||
pub should_stop: bool,
|
pub should_stop: bool,
|
||||||
|
|
@ -34,7 +34,7 @@ impl State {
|
||||||
pub fn new(display: Display) -> State {
|
pub fn new(display: Display) -> State {
|
||||||
State {
|
State {
|
||||||
display: Rc::new(RefCell::new(display)),
|
display: Rc::new(RefCell::new(display)),
|
||||||
spaces: Space::new(slog_scope::logger() /*TODO*/),
|
spaces: Workspaces::new(),
|
||||||
|
|
||||||
start_time: Instant::now(),
|
start_time: Instant::now(),
|
||||||
should_stop: false,
|
should_stop: false,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue