cosmic-comp/src/shell/mod.rs

678 lines
22 KiB
Rust
Raw Normal View History

2021-12-17 17:53:01 +01:00
// SPDX-License-Identifier: GPL-3.0-only
2022-03-30 13:47:06 +02:00
use crate::{
2022-04-14 22:16:37 +02:00
config::{Config, OutputConfig},
2022-03-30 13:47:06 +02:00
input::active_output,
2022-04-14 22:16:37 +02:00
state::Common,
2022-03-30 13:47:06 +02:00
};
2022-03-24 20:32:31 +01:00
pub use smithay::{
desktop::{PopupGrab, PopupManager, PopupUngrabStrategy, Space, Window},
reexports::wayland_server::protocol::wl_surface::WlSurface,
utils::{Logical, Point, Rectangle, Size},
2022-03-30 22:00:44 +02:00
wayland::{
compositor::with_states,
output::{Mode as OutputMode, Output},
seat::Seat,
shell::xdg::XdgToplevelSurfaceRoleAttributes,
Serial, SERIAL_COUNTER,
2022-03-30 22:00:44 +02:00
},
};
use std::{
cell::{Cell, RefCell},
mem::MaybeUninit,
sync::Mutex,
2021-12-21 18:57:09 +01:00
};
2022-03-24 20:32:31 +01:00
pub const MAX_WORKSPACES: usize = 10; // TODO?
mod handler;
pub mod layout;
mod workspace;
2022-03-30 22:00:44 +02:00
pub use self::handler::{init_shell, PopupGrabData};
2022-03-24 20:32:31 +01:00
pub use self::layout::Layout;
pub use self::workspace::*;
pub struct ActiveWorkspace(Cell<Option<usize>>);
impl ActiveWorkspace {
fn new() -> Self {
ActiveWorkspace(Cell::new(None))
}
pub 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)
}
}
#[derive(Debug, serde::Deserialize, PartialEq, Eq, Clone, Copy)]
2022-03-24 20:32:31 +01:00
pub enum Mode {
OutputBound,
Global {
#[serde(default)]
active: usize,
},
2022-03-24 20:32:31 +01:00
}
impl Mode {
pub fn output_bound() -> Mode {
Mode::OutputBound
}
2021-12-21 18:57:09 +01:00
2022-03-24 20:32:31 +01:00
pub fn global() -> Mode {
Mode::Global { active: 0 }
}
}
2021-12-21 18:57:09 +01:00
2022-03-24 20:32:31 +01:00
pub struct Shell {
2021-12-21 18:57:09 +01:00
popups: PopupManager,
2022-03-24 20:32:31 +01:00
mode: Mode,
outputs: Vec<Output>,
pub spaces: [Workspace; MAX_WORKSPACES],
2021-12-21 18:57:09 +01:00
}
2022-03-24 20:32:31 +01:00
const UNINIT_SPACE: MaybeUninit<Workspace> = MaybeUninit::uninit();
impl Shell {
2022-03-30 22:00:44 +02:00
fn new(config: &Config) -> Self {
2022-03-24 20:32:31 +01:00
Shell {
popups: PopupManager::new(None),
2022-03-30 13:47:06 +02:00
mode: config.static_conf.workspace_mode,
2022-03-24 20:32:31 +01:00
outputs: Vec::new(),
spaces: unsafe {
let mut spaces = [UNINIT_SPACE; MAX_WORKSPACES];
2022-03-29 13:24:35 +02:00
for (idx, space) in spaces.iter_mut().enumerate() {
*space = MaybeUninit::new(Workspace::new(idx as u8));
2021-12-21 18:57:09 +01:00
}
2022-03-24 20:32:31 +01:00
std::mem::transmute(spaces)
},
}
}
2021-12-28 16:23:12 +01:00
2022-04-14 22:16:37 +02:00
pub fn refresh_outputs(&mut self) {
if let Mode::Global { active } = self.mode {
let workspace = &mut self.spaces[active];
for output in self.outputs.iter() {
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
2022-04-14 22:16:37 +02:00
.borrow();
workspace
.space
.map_output(output, config.scale, config.position);
2022-03-30 13:47:06 +02:00
}
2022-04-14 22:16:37 +02:00
} else {
for output in self.outputs.iter() {
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
let active = output
.user_data()
.get::<ActiveWorkspace>()
.unwrap()
.get()
.unwrap();
2022-03-24 20:32:31 +01:00
let workspace = &mut self.spaces[active];
2022-04-14 22:16:37 +02:00
workspace.space.map_output(output, config.scale, (0, 0));
2022-03-30 13:47:06 +02:00
}
}
}
fn assign_next_free_output<'a>(
spaces: &'a mut [Workspace],
output: &Output,
) -> &'a mut Workspace {
output
.user_data()
.insert_if_missing(|| ActiveWorkspace::new());
let (idx, workspace) = spaces
.iter_mut()
.enumerate()
.find(|(_, x)| x.space.outputs().next().is_none())
.expect("More then 10 outputs?");
output
.user_data()
.get::<ActiveWorkspace>()
.unwrap()
.set(idx);
workspace
}
2022-04-14 22:16:37 +02:00
pub fn add_output(&mut self, output: &Output) {
self.outputs.push(output.clone());
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
match self.mode {
Mode::OutputBound => {
let workspace = Self::assign_next_free_output(&mut self.spaces, output);
workspace.space.map_output(output, config.scale, (0, 0));
}
Mode::Global { active } => {
let workspace = &mut self.spaces[active];
workspace
.space
.map_output(output, config.scale, config.position);
}
}
2022-04-14 22:16:37 +02:00
output.change_current_state(None, None, Some(config.scale.ceil() as i32), None);
}
2022-04-14 22:16:37 +02:00
pub fn remove_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].space.unmap_output(output);
self.outputs.retain(|o| o != output);
}
}
Mode::Global { active } => {
self.spaces[active].space.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
}
}
}
2022-03-24 20:32:31 +01:00
pub fn output_size(&self, output: &Output) -> Size<i32, Logical> {
let workspace = self.active_space(output);
workspace
.space
.output_geometry(&output)
.unwrap_or(Rectangle::from_loc_and_size((0, 0), (0, 0)))
.size
}
pub fn global_space(&self) -> Rectangle<i32, Logical> {
self.outputs
.iter()
.fold(
Option::<Rectangle<i32, Logical>>::None,
|maybe_geo, output| match maybe_geo {
Some(rect) => Some(rect.merge(self.output_geometry(output))),
None => Some(self.output_geometry(output)),
},
)
.unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (0, 0)))
2022-03-24 20:32:31 +01:00
}
2021-12-28 16:23:12 +01:00
pub fn space_relative_output_geometry<C: smithay::utils::Coordinate>(
2022-03-24 20:32:31 +01:00
&self,
global_loc: impl Into<Point<C, Logical>>,
2022-03-24 20:32:31 +01:00
output: &Output,
) -> Point<C, Logical> {
2022-03-24 20:32:31 +01:00
match self.mode {
Mode::Global { .. } => global_loc.into(),
Mode::OutputBound => {
let p = global_loc.into().to_f64() - self.output_geometry(output).loc.to_f64();
(C::from_f64(p.x), C::from_f64(p.y)).into()
}
2022-03-24 20:32:31 +01:00
}
}
pub fn output_geometry(&self, output: &Output) -> Rectangle<i32, Logical> {
// due to our different modes, we cannot just ask the space for the global output coordinates,
// because for `Mode::OutputBound` the origin will always be (0, 0)
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
Rectangle::from_loc_and_size(config.position, self.output_size(output))
2022-03-24 20:32:31 +01:00
}
pub fn activate(&mut self, seat: &Seat, output: &Output, idx: usize) {
if idx > MAX_WORKSPACES {
return;
}
2022-03-24 20:32:31 +01:00
match self.mode {
Mode::OutputBound => {
for output in &self.outputs {
if output
.user_data()
.get::<ActiveWorkspace>()
.and_then(|i| i.get().map(|i| i == idx))
.unwrap_or(false)
{
let geometry = self.output_geometry(output);
if let Some(ptr) = seat.get_pointer() {
ptr.motion(
Point::<i32, Logical>::from((
geometry.loc.x + (geometry.size.w / 2),
geometry.loc.y + (geometry.size.h / 2),
))
.to_f64(),
None,
SERIAL_COUNTER.next_serial(),
0,
);
return;
}
}
}
2022-03-24 20:32:31 +01:00
if let Some(active) = output.user_data().get::<ActiveWorkspace>() {
if let Some(old_idx) = active.set(idx) {
self.spaces[old_idx].space.unmap_output(output);
2021-12-28 16:23:12 +01:00
}
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
self.spaces[idx]
.space
.map_output(output, config.scale, (0, 0));
self.spaces[idx].refresh();
2021-12-28 16:23:12 +01:00
}
2022-03-24 20:32:31 +01:00
}
Mode::Global { ref mut active } => {
let old = *active;
*active = idx;
for output in &self.outputs {
self.spaces[old].space.unmap_output(output);
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
self.spaces[*active]
.space
.map_output(output, config.scale, config.position);
2021-12-28 16:23:12 +01:00
}
2022-03-24 20:32:31 +01:00
}
};
}
pub fn move_current_window(&mut self, seat: &Seat, output: &Output, idx: usize) {
if idx > MAX_WORKSPACES {
return;
}
2022-03-30 22:11:29 +02:00
let workspace = self.active_space_mut(output);
if idx == workspace.idx as usize {
2022-03-30 22:11:29 +02:00
return;
}
let maybe_window = workspace.focus_stack(seat).last();
if let Some(window) = maybe_window {
workspace.unmap_window(&window);
self.spaces[idx].map_window(&window, seat);
2022-03-30 22:11:29 +02:00
}
}
2022-03-24 20:32:31 +01:00
#[cfg(feature = "debug")]
pub fn mode(&self) -> &Mode {
&self.mode
}
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);
for output in &self.outputs {
let old_active = output
.user_data()
.get::<ActiveWorkspace>()
2021-12-21 18:57:09 +01:00
.unwrap()
2022-03-24 20:32:31 +01:00
.clear()
.unwrap();
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
2022-03-24 20:32:31 +01:00
.unwrap()
.borrow();
2022-03-24 20:32:31 +01:00
self.spaces[old_active].space.unmap_output(output);
self.spaces[active]
.space
.map_output(output, config.scale, config.position);
self.spaces[active].refresh();
2021-12-21 18:57:09 +01:00
}
2022-03-24 20:32:31 +01:00
self.mode = Mode::Global { active };
}
(Mode::Global { active }, new @ Mode::OutputBound) => {
for output in &self.outputs {
self.spaces[*active].space.unmap_output(output);
2021-12-21 18:57:09 +01:00
}
2022-01-25 16:31:58 +01:00
let mut active = Some(active.clone());
2022-03-24 20:32:31 +01:00
self.mode = new;
for output in &self.outputs {
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
let workspace = if let Some(a) = active.take() {
output
.user_data()
.insert_if_missing(|| ActiveWorkspace::new());
output.user_data().get::<ActiveWorkspace>().unwrap().set(a);
&mut self.spaces[a]
} else {
Self::assign_next_free_output(&mut self.spaces, output)
};
workspace.space.map_output(output, config.scale, (0, 0));
workspace.refresh();
2022-01-25 16:31:58 +01:00
}
2021-12-21 18:57:09 +01:00
}
_ => {}
2022-03-24 20:32:31 +01:00
};
2021-12-21 18:57:09 +01:00
}
2022-03-24 20:32:31 +01:00
pub fn outputs(&self) -> impl Iterator<Item = &Output> {
self.outputs.iter()
2021-12-28 16:23:12 +01:00
}
2022-03-24 20:32:31 +01:00
pub fn active_space(&self, output: &Output) -> &Workspace {
match &self.mode {
Mode::OutputBound => {
let active = output
.user_data()
.get::<ActiveWorkspace>()
.unwrap()
.get()
.unwrap();
&self.spaces[active]
}
Mode::Global { active } => &self.spaces[*active],
2021-12-21 18:57:09 +01:00
}
2022-03-24 20:32:31 +01:00
}
2021-12-21 18:57:09 +01:00
2022-03-24 20:32:31 +01:00
pub fn active_space_mut(&mut self, output: &Output) -> &mut Workspace {
match &self.mode {
Mode::OutputBound => {
let active = output
.user_data()
.get::<ActiveWorkspace>()
.unwrap()
.get()
.unwrap();
&mut self.spaces[active]
2021-12-21 18:57:09 +01:00
}
2022-03-24 20:32:31 +01:00
Mode::Global { active } => &mut self.spaces[*active],
2021-12-21 18:57:09 +01:00
}
}
2022-03-24 20:32:31 +01:00
pub fn space_for_surface(&self, surface: &WlSurface) -> Option<&Workspace> {
self.spaces.iter().find(|workspace| {
workspace
.pending_windows
.iter()
.any(|(w, _)| w.toplevel().get_surface() == Some(surface))
|| workspace.space.window_for_surface(surface).is_some()
2021-12-28 16:23:12 +01:00
})
2022-03-24 20:32:31 +01:00
}
2021-12-28 16:23:12 +01:00
2022-03-24 20:32:31 +01:00
pub fn space_for_surface_mut(&mut self, surface: &WlSurface) -> Option<&mut Workspace> {
self.spaces
.iter_mut()
.find(|workspace| workspace.space.window_for_surface(surface).is_some())
2021-12-28 16:23:12 +01:00
}
2022-03-24 20:32:31 +01:00
pub fn refresh(&mut self) {
for output in self.outputs.iter() {
let workspace = 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],
};
2022-03-24 20:32:31 +01:00
workspace.refresh();
}
2022-03-24 20:32:31 +01:00
}
2022-03-30 22:00:44 +02:00
pub fn commit<'a>(&mut self, surface: &WlSurface, seats: impl Iterator<Item = &'a Seat>) {
2022-03-24 20:32:31 +01:00
let mut new_focus = None;
for (idx, workspace) in self.spaces.iter_mut().enumerate() {
if let Some((window, seat)) = workspace
.pending_windows
.iter()
.find(|(w, _)| w.toplevel().get_surface() == Some(surface))
.cloned()
{
workspace.map_window(&window, &seat);
if match self.mode {
Mode::OutputBound => self.outputs.iter().any(|o| {
o.user_data()
.get::<ActiveWorkspace>()
.unwrap()
.get()
.unwrap()
== idx
}),
Mode::Global { active } => active == idx,
} {
new_focus = Some(seat);
}
workspace.pending_windows.retain(|(w, _)| w != &window);
}
workspace.space.commit(surface)
}
if let Some(seat) = new_focus {
2022-03-30 22:00:44 +02:00
self.set_focus(Some(surface), &seat, seats, None)
2022-03-24 20:32:31 +01:00
}
self.popups.commit(&surface);
}
2022-03-30 22:00:44 +02:00
pub fn set_orientation(
&mut self,
seat: &Seat,
output: &Output,
orientation: layout::Orientation,
) {
self.active_space_mut(output)
.update_orientation(seat, orientation)
}
pub fn move_focus<'a>(
&mut self,
seat: &Seat,
output: &Output,
focus: layout::FocusDirection,
seats: impl Iterator<Item = &'a Seat>,
) {
if let Some(surface) = self
.active_space_mut(output)
.move_focus(seat, focus)
.and_then(|window| window.toplevel().get_surface().cloned())
{
self.set_focus(Some(&surface), seat, seats, None)
}
}
2022-03-30 22:00:44 +02:00
fn set_focus<'a>(
&mut self,
surface: Option<&WlSurface>,
active_seat: &Seat,
seats: impl Iterator<Item = &'a Seat>,
serial: Option<Serial>,
) {
2022-03-24 20:32:31 +01:00
// update FocusStack and notify layouts about new focus (if any window)
if let Some(surface) = surface {
if let Some(workspace) = self.space_for_surface_mut(surface) {
if let Some(window) = workspace.space.window_for_surface(surface) {
2022-03-30 22:00:44 +02:00
let mut focus_stack = workspace.focus_stack_mut(active_seat);
if Some(window) != focus_stack.last().as_ref() {
2022-03-24 20:32:31 +01:00
slog_scope::debug!("Focusing window: {:?}", window);
2022-03-30 22:00:44 +02:00
focus_stack.append(window);
2022-03-24 20:32:31 +01:00
// also remove popup grabs, if we are switching focus
2022-03-30 22:00:44 +02:00
if let Some(mut popup_grab) = active_seat
.user_data()
.get::<PopupGrabData>()
.and_then(|x| x.take())
{
2022-03-24 20:32:31 +01:00
if !popup_grab.has_ended() {
popup_grab.ungrab(PopupUngrabStrategy::All);
}
}
}
}
}
}
2022-03-24 20:32:31 +01:00
// update keyboard focus
if let Some(keyboard) = active_seat.get_keyboard() {
2022-03-30 22:00:44 +02:00
ActiveFocus::set(active_seat, surface.cloned());
keyboard.set_focus(
surface,
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
);
2022-03-24 20:32:31 +01:00
}
2022-03-30 22:00:44 +02:00
self.update_active(seats)
}
fn update_active<'a>(&self, seats: impl Iterator<Item = &'a Seat>) {
2022-03-24 20:32:31 +01:00
// update activate status
2022-03-30 22:00:44 +02:00
let focused_windows = seats
.flat_map(|seat| {
self.outputs
.iter()
.flat_map(|o| self.active_space(o).focus_stack(seat).last().clone())
})
2022-03-24 20:32:31 +01:00
.collect::<Vec<_>>();
for output in self.outputs() {
let workspace = self.active_space(output);
2022-03-24 20:32:31 +01:00
for window in workspace.space.windows() {
window.set_activated(focused_windows.contains(window));
window.configure();
}
}
}
2022-03-30 22:00:44 +02:00
}
2022-03-30 22:00:44 +02:00
pub struct ActiveFocus(RefCell<Option<WlSurface>>);
impl ActiveFocus {
fn set(seat: &Seat, surface: Option<WlSurface>) {
if !seat
.user_data()
.insert_if_missing(|| ActiveFocus(RefCell::new(surface.clone())))
{
*seat
.user_data()
.get::<ActiveFocus>()
.unwrap()
.0
.borrow_mut() = surface;
}
}
fn get(seat: &Seat) -> Option<WlSurface> {
seat.user_data()
.get::<ActiveFocus>()
.and_then(|a| a.0.borrow().clone())
}
}
impl Common {
pub fn set_focus(
&mut self,
2022-03-30 22:00:44 +02:00
surface: Option<&WlSurface>,
active_seat: &Seat,
serial: Option<Serial>,
) {
2022-03-30 22:00:44 +02:00
self.shell
.set_focus(surface, active_seat, self.seats.iter(), serial)
}
pub fn refresh_focus(&mut self) {
for seat in &self.seats {
let mut fixup = false;
let output = active_output(seat, &self);
let last_known_focus = ActiveFocus::get(seat);
if let Some(surface) = last_known_focus {
if surface.as_ref().is_alive() {
let is_toplevel = with_states(&surface, |states| {
states
.data_map
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
.is_some()
})
.unwrap_or(false);
if !is_toplevel {
continue;
}
let workspace = self.shell.active_space(&output);
if let Some(window) = workspace.space.window_for_surface(&surface) {
let focus_stack = workspace.focus_stack(&seat);
if focus_stack.last().map(|w| &w != window).unwrap_or(true) {
2022-03-30 22:00:44 +02:00
fixup = true;
}
} else {
fixup = true;
2022-03-30 22:00:44 +02:00
}
} else {
fixup = true;
}
}
if fixup {
// also remove popup grabs, if we are switching focus
if let Some(mut popup_grab) = seat
.user_data()
.get::<PopupGrabData>()
.and_then(|x| x.take())
{
if !popup_grab.has_ended() {
popup_grab.ungrab(PopupUngrabStrategy::All);
}
}
// update keyboard focus
let surface = self
.shell
.active_space(&output)
.focus_stack(seat)
.last()
.and_then(|w| w.toplevel().get_surface().cloned());
if let Some(keyboard) = seat.get_keyboard() {
keyboard.set_focus(surface.as_ref(), SERIAL_COUNTER.next_serial());
ActiveFocus::set(seat, surface);
}
}
}
self.shell.update_active(self.seats.iter())
}
2021-12-21 18:57:09 +01:00
}