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]]
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
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
|
||||
|
||||
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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue