cosmic-comp/src/shell/mod.rs

906 lines
33 KiB
Rust
Raw Normal View History

2021-12-17 17:53:01 +01:00
// SPDX-License-Identifier: GPL-3.0-only
2022-05-02 17:43:58 +02:00
#[cfg(feature = "experimental")]
use crate::wayland::workspace as ext_work;
2022-03-30 13:47:06 +02:00
use crate::{
2022-04-14 22:16:37 +02:00
config::{Config, OutputConfig},
2022-05-03 13:06:37 +02:00
input::{active_output, set_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-05-16 18:10:12 +02:00
desktop::{layer_map_for_output, PopupGrab, PopupManager, PopupUngrabStrategy, Space, Window, WindowSurfaceType},
2022-05-02 17:43:58 +02:00
reexports::wayland_server::{protocol::wl_surface::WlSurface, Display},
2022-03-24 20:32:31 +01:00
utils::{Logical, Point, Rectangle, Size},
2022-03-30 22:00:44 +02:00
wayland::{
compositor::with_states,
2022-04-20 16:06:37 +02:00
output::{Mode as OutputMode, Output, Scale},
seat::Seat,
shell::{
2022-05-02 17:43:58 +02:00
wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState},
xdg::XdgToplevelSurfaceRoleAttributes,
},
Serial, SERIAL_COUNTER,
2022-03-30 22:00:44 +02:00
},
};
2022-05-02 17:43:58 +02:00
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],
2022-05-02 17:43:58 +02:00
#[cfg(feature = "experimental")]
manager: ext_work::WorkspaceManager,
#[cfg(feature = "experimental")]
global_group: Option<ext_work::WorkspaceGroup>,
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-05-02 17:43:58 +02:00
fn new(config: &Config, _display: &mut Display) -> 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)
},
2022-05-02 17:43:58 +02:00
#[cfg(feature = "experimental")]
manager: ext_work::init_ext_workspace(
_display,
|_, changes, mut ddata| {
let state = ddata.get::<crate::state::State>().unwrap();
if let Some((w, _)) = changes.into_iter().rev().find(|(_, p)| {
p.into_iter()
.rev()
.find(|o| {
**o == ext_work::PendingOperation::Activate
|| **o == ext_work::PendingOperation::Deactivate
})
.map(|o| *o == ext_work::PendingOperation::Activate)
.unwrap_or(false)
}) {
if let Some(idx) = state.common.shell.spaces.iter().position(|work| {
work.ext_workspace.as_ref().map(|e| e == w).unwrap_or(false)
}) {
state.common.event_loop_handle.insert_idle(move |state| {
let seat = &state.common.last_active_seat;
let output = active_output(&seat, &state.common);
state.common.shell.activate(seat, &output, idx);
});
}
}
},
|_| true,
)
.0,
#[cfg(feature = "experimental")]
global_group: None,
2022-03-24 20:32:31 +01:00
}
}
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();
2022-05-02 17:43:58 +02:00
workspace.space.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
}
}
}
fn assign_next_free_output<'a>(
spaces: &'a mut [Workspace],
output: &Output,
2022-05-02 17:43:58 +02:00
) -> (usize, &'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);
2022-05-02 17:43:58 +02:00
(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 => {
2022-05-02 17:43:58 +02:00
let _idx = {
let (idx, workspace) = Self::assign_next_free_output(&mut self.spaces, output);
workspace.space.map_output(output, (0, 0));
idx
};
#[cfg(feature = "experimental")]
{
let group = self
.manager
.new_group(|_, _, _| {}, std::iter::once(output.clone()));
if self.spaces.iter().all(|w| w.ext_workspace.is_none()) {
for (idx, workspace) in self.spaces.iter_mut().enumerate() {
let ext_workspace = group.create_workspace(format!("{}", idx + 1));
ext_workspace.set_coordinates(std::iter::once(idx as u32));
2022-05-02 17:43:58 +02:00
workspace.ext_workspace = Some(ext_workspace);
}
self.spaces[_idx]
.ext_workspace
.as_mut()
.unwrap()
.add_state(ext_work::State::Active);
} else {
let ext_workspace = group.create_workspace(format!("{}", _idx + 1));
ext_workspace.set_coordinates(std::iter::once(_idx as u32));
2022-05-02 17:43:58 +02:00
ext_workspace.add_state(ext_work::State::Active);
if let Some(old) = self.spaces[_idx].ext_workspace.replace(ext_workspace) {
old.remove();
}
}
output.user_data().insert_if_missing(|| {
RefCell::new(Option::<ext_work::WorkspaceGroup>::None)
});
*output
.user_data()
.get::<RefCell<Option<ext_work::WorkspaceGroup>>>()
.unwrap()
.borrow_mut() = Some(group);
self.manager.done();
}
}
Mode::Global { active } => {
2022-05-02 17:43:58 +02:00
#[cfg(feature = "experimental")]
{
if self.global_group.is_none() {
self.global_group = Some(
self.manager
.new_group(|_, _, _| {}, std::iter::once(output.clone())),
);
for (idx, workspace) in self.spaces.iter_mut().enumerate() {
let ext_workspace = self
.global_group
.as_mut()
.unwrap()
.create_workspace(format!("{}", idx + 1));
ext_workspace.set_coordinates(std::iter::once(idx as u32));
2022-05-02 17:43:58 +02:00
if idx == active {
ext_workspace.add_state(ext_work::State::Active);
}
workspace.ext_workspace = Some(ext_workspace);
}
} else {
self.manager.update_outputs(|_, outputs| {
outputs.insert(output.clone());
});
}
self.manager.done();
}
let workspace = &mut self.spaces[active];
2022-05-02 17:43:58 +02:00
workspace.space.map_output(output, config.position);
}
}
2022-04-20 16:06:37 +02:00
output.change_current_state(None, None, Some(Scale::Fractional(config.scale)), 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);
}
2022-05-02 17:43:58 +02:00
#[cfg(feature = "experimental")]
{
let group = output
.user_data()
.get::<RefCell<Option<ext_work::WorkspaceGroup>>>()
.unwrap()
.borrow_mut()
.take()
.unwrap();
let mut alternative_group = self.outputs.last_mut().map(|o| {
o.user_data()
.get::<RefCell<Option<ext_work::WorkspaceGroup>>>()
.unwrap()
});
for (idx, workspace) in self.spaces.iter_mut().enumerate() {
if workspace
.ext_workspace
.as_ref()
.map(|w| group.belongs(w))
.unwrap_or(false)
{
workspace.ext_workspace.take().unwrap().remove();
if let Some(alternative) = alternative_group.as_mut() {
let ext_workspace = alternative
.borrow_mut()
.as_mut()
.unwrap()
.create_workspace(format!("{}", idx + 1));
ext_workspace.set_coordinates(std::iter::once(idx as u32));
2022-05-02 17:43:58 +02:00
workspace.ext_workspace = Some(ext_workspace);
}
}
}
self.manager.remove_group(&group);
}
}
Mode::Global { active } => {
2022-05-02 17:43:58 +02:00
#[cfg(feature = "experimental")]
self.manager.update_outputs(|_, outputs| {
outputs.remove(output);
});
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,
);
2022-05-03 13:06:37 +02:00
set_active_output(seat, output);
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) {
2022-05-02 17:43:58 +02:00
#[cfg(feature = "experimental")]
if let Some(ext_workspace) = self.spaces[old_idx].ext_workspace.as_mut() {
ext_workspace.remove_state(ext_work::State::Active);
}
2022-03-24 20:32:31 +01:00
self.spaces[old_idx].space.unmap_output(output);
2021-12-28 16:23:12 +01:00
}
2022-05-02 17:43:58 +02:00
self.spaces[idx].space.map_output(output, (0, 0));
#[cfg(feature = "experimental")]
{
let mut group_borrow = output
.user_data()
.get::<RefCell<Option<ext_work::WorkspaceGroup>>>()
.unwrap()
.borrow_mut();
let group = group_borrow.as_mut().unwrap();
if self.spaces[idx]
.ext_workspace
.as_ref()
.map(|x| !group.belongs(x))
.unwrap_or(true)
{
let ext_workspace = group.create_workspace(format!("{}", idx + 1));
ext_workspace.set_coordinates(std::iter::once(idx as u32));
2022-05-02 17:43:58 +02:00
if let Some(old) = self.spaces[idx].ext_workspace.replace(ext_workspace)
{
old.remove();
}
}
self.spaces[idx]
.ext_workspace
.as_mut()
.unwrap()
.add_state(ext_work::State::Active);
self.manager.done();
}
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
2022-04-20 16:06:37 +02:00
.map_output(output, config.position);
2021-12-28 16:23:12 +01:00
}
2022-05-02 17:43:58 +02:00
#[cfg(feature = "experimental")]
{
for (idx, workspace) in self.spaces.iter_mut().enumerate() {
if idx == *active {
workspace
.ext_workspace
.as_mut()
.unwrap()
.add_state(ext_work::State::Active);
} else {
workspace
.ext_workspace
.as_mut()
.unwrap()
.remove_state(ext_work::State::Active);
}
}
self.manager.done();
}
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
2022-04-20 16:06:37 +02:00
.map_output(output, 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 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 {
2022-05-02 17:43:58 +02:00
Self::assign_next_free_output(&mut self.spaces, output).1
};
2022-04-20 16:06:37 +02:00
workspace.space.map_output(output, (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
}
}
pub fn outputs_for_surface(&self, surface: &WlSurface) -> impl Iterator<Item=Output> {
self.space_for_surface(surface)
.and_then(|w| if let Some(window) = w.space.window_for_surface(surface, WindowSurfaceType::ALL) {
Some(w.space.outputs_for_window(&window).into_iter())
} else { None })
.into_iter()
.flatten()
}
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))
2022-05-16 18:10:12 +02:00
|| workspace.space.window_for_surface(surface, WindowSurfaceType::ALL).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()
2022-05-16 18:10:12 +02:00
.find(|workspace| workspace.space.window_for_surface(surface, WindowSurfaceType::ALL).is_some())
2021-12-28 16:23:12 +01:00
}
2022-03-24 20:32:31 +01:00
pub fn refresh(&mut self) {
2022-04-26 18:55:04 +02:00
self.popups.cleanup();
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-04-26 18:55:04 +02:00
let mut map = layer_map_for_output(output);
map.cleanup();
map.arrange();
}
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);
}
2022-05-02 17:43:58 +02:00
workspace.pending_windows.retain(|(w, _)| w != &window);
2022-03-24 20:32:31 +01: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);
}
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) {
2022-05-16 18:10:12 +02:00
if let Some(window) = workspace.space.window_for_surface(surface, WindowSurfaceType::ALL) {
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)
}
2022-05-16 18:13:04 +02:00
fn update_active<'a>(&mut 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-05-16 18:13:04 +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],
};
for focused in focused_windows.iter() {
workspace.space.raise_window(focused, true);
}
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);
2022-05-16 18:10:12 +02:00
if let Some(window) = workspace.space.window_for_surface(&surface, WindowSurfaceType::ALL) {
2022-03-30 22:00:44 +02:00
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
}