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::{
|
2022-04-21 14:02:44 +02:00
|
|
|
desktop::{PopupGrab, PopupManager, PopupUngrabStrategy, Space, Window, layer_map_for_output},
|
2022-03-24 20:32:31 +01:00
|
|
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
|
|
|
|
utils::{Logical, Point, Rectangle, Size},
|
2022-03-30 22:00:44 +02:00
|
|
|
wayland::{
|
2022-04-05 16:35:58 +02:00
|
|
|
compositor::with_states,
|
2022-04-20 16:06:37 +02:00
|
|
|
output::{Mode as OutputMode, Output, Scale},
|
2022-04-05 16:35:58 +02:00
|
|
|
seat::Seat,
|
2022-04-21 14:02:44 +02:00
|
|
|
shell::{
|
|
|
|
|
xdg::XdgToplevelSurfaceRoleAttributes,
|
|
|
|
|
wlr_layer::{Layer, KeyboardInteractivity, LayerSurfaceCachedState},
|
|
|
|
|
},
|
2022-04-05 16:35:58 +02:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-29 14:41:09 +02:00
|
|
|
#[derive(Debug, serde::Deserialize, PartialEq, Eq, Clone, Copy)]
|
2022-03-24 20:32:31 +01:00
|
|
|
pub enum Mode {
|
|
|
|
|
OutputBound,
|
2022-03-29 14:41:09 +02:00
|
|
|
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
|
2022-04-05 16:35:58 +02:00
|
|
|
.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.unwrap()
|
2022-04-14 22:16:37 +02:00
|
|
|
.borrow();
|
|
|
|
|
workspace
|
|
|
|
|
.space
|
2022-04-20 16:06:37 +02:00
|
|
|
.map_output(output, 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 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-20 16:06:37 +02:00
|
|
|
workspace.space.map_output(output, (0, 0));
|
2022-03-30 13:47:06 +02:00
|
|
|
}
|
2022-04-05 16:35:58 +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());
|
2022-04-13 22:59:14 +02:00
|
|
|
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);
|
2022-04-20 16:06:37 +02:00
|
|
|
workspace.space.map_output(output, (0, 0));
|
2022-04-13 22:59:14 +02:00
|
|
|
}
|
|
|
|
|
Mode::Global { active } => {
|
|
|
|
|
let workspace = &mut self.spaces[active];
|
|
|
|
|
workspace
|
|
|
|
|
.space
|
2022-04-20 16:06:37 +02:00
|
|
|
.map_output(output, config.position);
|
2022-04-13 22:59:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-04-20 16:06:37 +02:00
|
|
|
output.change_current_state(None, None, Some(Scale::Fractional(config.scale)), None);
|
2022-04-13 22:59:14 +02:00
|
|
|
}
|
|
|
|
|
|
2022-04-14 22:16:37 +02:00
|
|
|
pub fn remove_output(&mut self, output: &Output) {
|
2022-04-13 22:59:14 +02:00
|
|
|
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> {
|
2022-04-05 16:35:58 +02:00
|
|
|
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
|
|
|
|
2022-03-29 13:25:31 +02:00
|
|
|
pub fn space_relative_output_geometry<C: smithay::utils::Coordinate>(
|
2022-03-24 20:32:31 +01:00
|
|
|
&self,
|
2022-03-29 13:25:31 +02:00
|
|
|
global_loc: impl Into<Point<C, Logical>>,
|
2022-03-24 20:32:31 +01:00
|
|
|
output: &Output,
|
2022-03-29 13:25:31 +02:00
|
|
|
) -> Point<C, Logical> {
|
2022-03-24 20:32:31 +01:00
|
|
|
match self.mode {
|
|
|
|
|
Mode::Global { .. } => global_loc.into(),
|
2022-03-29 13:25:31 +02:00
|
|
|
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)
|
|
|
|
|
|
2022-04-05 16:35:58 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2022-03-30 16:06:35 +02:00
|
|
|
pub fn activate(&mut self, seat: &Seat, output: &Output, idx: usize) {
|
2022-03-30 23:24:55 +02:00
|
|
|
if idx > MAX_WORKSPACES {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-24 20:32:31 +01:00
|
|
|
match self.mode {
|
|
|
|
|
Mode::OutputBound => {
|
2022-03-30 16:06:35 +02:00
|
|
|
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
|
|
|
}
|
2022-04-05 16:35:58 +02:00
|
|
|
self.spaces[idx]
|
|
|
|
|
.space
|
2022-04-20 16:06:37 +02:00
|
|
|
.map_output(output, (0, 0));
|
2022-03-30 16:06:35 +02:00
|
|
|
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);
|
2022-04-05 16:35:58 +02:00
|
|
|
let config = output
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.borrow();
|
|
|
|
|
self.spaces[*active]
|
|
|
|
|
.space
|
2022-04-20 16:06:37 +02:00
|
|
|
.map_output(output, config.position);
|
2021-12-28 16:23:12 +01:00
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 23:24:55 +02: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);
|
2022-03-30 23:24:55 +02:00
|
|
|
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);
|
2022-03-30 23:24:55 +02:00
|
|
|
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();
|
2022-04-05 16:35:58 +02:00
|
|
|
let config = output
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
2022-03-24 20:32:31 +01:00
|
|
|
.unwrap()
|
2022-04-05 16:35:58 +02:00
|
|
|
.borrow();
|
2022-03-24 20:32:31 +01:00
|
|
|
self.spaces[old_active].space.unmap_output(output);
|
2022-04-05 16:35:58 +02:00
|
|
|
self.spaces[active]
|
|
|
|
|
.space
|
2022-04-20 16:06:37 +02:00
|
|
|
.map_output(output, config.position);
|
2022-04-05 16:35:58 +02:00
|
|
|
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
|
|
|
|
2022-04-05 16:35:58 +02:00
|
|
|
let mut active = Some(active.clone());
|
2022-03-24 20:32:31 +01:00
|
|
|
self.mode = new;
|
2022-04-05 16:35:58 +02:00
|
|
|
for output in &self.outputs {
|
|
|
|
|
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)
|
|
|
|
|
};
|
2022-04-20 16:06:37 +02:00
|
|
|
workspace.space.map_output(output, (0, 0));
|
2022-04-05 16:35:58 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
2021-12-22 20:14:36 +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) {
|
2022-03-31 18:36:43 +02:00
|
|
|
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();
|
2021-12-22 20:14:36 +01:00
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2021-12-22 20:14:36 +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);
|
|
|
|
|
}
|
2022-04-21 14:02:44 +02:00
|
|
|
workspace.pending_windows.retain(|(w, _)| w != &window);
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2022-04-21 14:02:44 +02:00
|
|
|
if let Some((layer, output, seat)) = workspace
|
|
|
|
|
.pending_layers
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|(l, _, _)| l.get_surface() == Some(surface))
|
|
|
|
|
.cloned()
|
|
|
|
|
{
|
|
|
|
|
let focus = layer
|
|
|
|
|
.get_surface()
|
|
|
|
|
.map(|surface| {
|
|
|
|
|
with_states(surface, |states| {
|
|
|
|
|
let state = states.cached_state.current::<LayerSurfaceCachedState>();
|
|
|
|
|
matches!(state.layer, Layer::Top | Layer::Overlay)
|
|
|
|
|
&& dbg!(state.keyboard_interactivity) != KeyboardInteractivity::None
|
|
|
|
|
})
|
|
|
|
|
.unwrap()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
|
|
let mut map = layer_map_for_output(&output);
|
|
|
|
|
map.map_layer(&layer).unwrap();
|
|
|
|
|
|
|
|
|
|
if focus {
|
|
|
|
|
new_focus = Some(seat);
|
|
|
|
|
}
|
|
|
|
|
workspace.pending_layers.retain(|(l, _, _)| l != &layer);
|
|
|
|
|
}
|
|
|
|
|
workspace.space.commit(surface);
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
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);
|
2021-12-22 20:14:36 +01:00
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 23:08:35 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-22 20:14:36 +01:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
}
|
2021-12-22 20:14:36 +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<_>>();
|
|
|
|
|
|
2022-03-31 18:36:43 +02:00
|
|
|
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-29 14:41:09 +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(
|
2022-03-29 14:41:09 +02:00
|
|
|
&mut self,
|
2022-03-30 22:00:44 +02:00
|
|
|
surface: Option<&WlSurface>,
|
|
|
|
|
active_seat: &Seat,
|
|
|
|
|
serial: Option<Serial>,
|
2022-03-29 14:41:09 +02:00
|
|
|
) {
|
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);
|
2022-03-31 18:36:43 +02:00
|
|
|
if focus_stack.last().map(|w| &w != window).unwrap_or(true) {
|
2022-03-30 22:00:44 +02:00
|
|
|
fixup = true;
|
|
|
|
|
}
|
2022-03-31 18:36:43 +02:00
|
|
|
} 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())
|
2022-03-29 14:41:09 +02:00
|
|
|
}
|
2021-12-21 18:57:09 +01:00
|
|
|
}
|