shell: Move seats into shell
This commit is contained in:
parent
1216cd0b67
commit
647deb81f1
31 changed files with 824 additions and 883 deletions
|
|
@ -72,7 +72,7 @@ use super::{
|
|||
floating::{ResizeState, TiledCorners},
|
||||
tiling::NodeDesc,
|
||||
},
|
||||
Direction, ManagedLayer,
|
||||
Direction, ManagedLayer, SeatExt,
|
||||
};
|
||||
|
||||
space_elements! {
|
||||
|
|
|
|||
|
|
@ -416,7 +416,7 @@ impl Program for CosmicWindowInternal {
|
|||
if let Some(mapped) =
|
||||
state.common.shell.element_for_surface(&surface).cloned()
|
||||
{
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
state.common.shell.maximize_toggle(&mapped, &seat)
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use tracing::{debug, trace};
|
|||
|
||||
use self::target::{KeyboardFocusTarget, WindowGroup};
|
||||
|
||||
use super::layout::floating::FloatingLayout;
|
||||
use super::{layout::floating::FloatingLayout, SeatExt};
|
||||
|
||||
pub mod target;
|
||||
|
||||
|
|
@ -99,43 +99,10 @@ impl ActiveFocus {
|
|||
}
|
||||
|
||||
impl Shell {
|
||||
pub fn append_focus_stack(state: &mut State, mapped: &CosmicMapped, active_seat: &Seat<State>) {
|
||||
if mapped.is_minimized() {
|
||||
return;
|
||||
}
|
||||
|
||||
// update FocusStack and notify layouts about new focus (if any window)
|
||||
let workspace = state.common.shell.space_for_mut(&mapped);
|
||||
let workspace = if workspace.is_none() {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.active_space_mut(&active_seat.active_output())
|
||||
} else {
|
||||
workspace.unwrap()
|
||||
};
|
||||
|
||||
let mut focus_stack = workspace.focus_stack.get_mut(active_seat);
|
||||
if Some(mapped) != focus_stack.last() {
|
||||
trace!(?mapped, "Focusing window.");
|
||||
focus_stack.append(&mapped);
|
||||
// also remove popup grabs, if we are switching focus
|
||||
if let Some(mut popup_grab) = active_seat
|
||||
.user_data()
|
||||
.get::<PopupGrabData>()
|
||||
.and_then(|x| x.take())
|
||||
{
|
||||
if !popup_grab.has_ended() {
|
||||
popup_grab.ungrab(PopupUngrabStrategy::All);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_focus(
|
||||
state: &mut State,
|
||||
target: Option<&KeyboardFocusTarget>,
|
||||
active_seat: &Seat<State>,
|
||||
seat: &Seat<State>,
|
||||
serial: Option<Serial>,
|
||||
) {
|
||||
let element = match target {
|
||||
|
|
@ -150,23 +117,57 @@ impl Shell {
|
|||
if mapped.is_minimized() {
|
||||
return;
|
||||
}
|
||||
Self::append_focus_stack(state, &mapped, active_seat);
|
||||
state.common.shell.append_focus_stack(&mapped, seat);
|
||||
}
|
||||
|
||||
// update keyboard focus
|
||||
if let Some(keyboard) = active_seat.get_keyboard() {
|
||||
ActiveFocus::set(active_seat, target.cloned());
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
ActiveFocus::set(seat, target.cloned());
|
||||
keyboard.set_focus(
|
||||
state,
|
||||
target.cloned(),
|
||||
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
|
||||
);
|
||||
}
|
||||
|
||||
state.common.shell.update_active();
|
||||
}
|
||||
|
||||
fn update_active<'a, 'b>(&mut self, seats: impl Iterator<Item = &'a Seat<State>>) {
|
||||
pub fn append_focus_stack(&mut self, mapped: &CosmicMapped, seat: &Seat<State>) {
|
||||
if mapped.is_minimized() {
|
||||
return;
|
||||
}
|
||||
|
||||
// update FocusStack and notify layouts about new focus (if any window)
|
||||
let workspace = self.space_for_mut(&mapped);
|
||||
let workspace = if workspace.is_none() {
|
||||
self.active_space_mut(&seat.active_output())
|
||||
} else {
|
||||
workspace.unwrap()
|
||||
};
|
||||
|
||||
let mut focus_stack = workspace.focus_stack.get_mut(seat);
|
||||
if Some(mapped) != focus_stack.last() {
|
||||
trace!(?mapped, "Focusing window.");
|
||||
focus_stack.append(&mapped);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_active<'a, 'b>(&mut self) {
|
||||
// update activate status
|
||||
let focused_windows = seats
|
||||
let focused_windows = self
|
||||
.seats
|
||||
.iter()
|
||||
.flat_map(|seat| {
|
||||
if matches!(
|
||||
seat.get_keyboard().unwrap().current_focus(),
|
||||
|
|
@ -231,20 +232,16 @@ fn raise_with_children(floating_layer: &mut FloatingLayout, focused: &CosmicMapp
|
|||
}
|
||||
|
||||
impl Common {
|
||||
pub fn set_focus(
|
||||
state: &mut State,
|
||||
target: Option<&KeyboardFocusTarget>,
|
||||
active_seat: &Seat<State>,
|
||||
serial: Option<Serial>,
|
||||
) {
|
||||
Shell::set_focus(state, target, active_seat, serial);
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
state.common.shell.update_active(seats.iter());
|
||||
}
|
||||
|
||||
pub fn refresh_focus(state: &mut State) {
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
for seat in seats {
|
||||
for seat in state
|
||||
.common
|
||||
.shell
|
||||
.seats
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
{
|
||||
let output = seat.active_output();
|
||||
if !state.common.shell.outputs().any(|o| o == &output) {
|
||||
seat.set_active_output(&state.common.shell.outputs().next().unwrap());
|
||||
|
|
@ -254,7 +251,7 @@ impl Common {
|
|||
|
||||
if let Some(target) = last_known_focus {
|
||||
if target.alive() {
|
||||
if focus_target_is_valid(state, &seat, &output, target) {
|
||||
if focus_target_is_valid(&mut state.common.shell, &seat, &output, target) {
|
||||
continue; // Focus is valid
|
||||
} else {
|
||||
trace!("Wrong Window, focus fixup");
|
||||
|
|
@ -312,7 +309,7 @@ impl Common {
|
|||
}
|
||||
|
||||
// update keyboard focus
|
||||
let target = update_focus_target(state, &seat, &output);
|
||||
let target = update_focus_target(&state.common.shell, &seat, &output);
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
debug!("Restoring focus to {:?}", target.as_ref());
|
||||
keyboard.set_focus(state, target.clone(), SERIAL_COUNTER.next_serial());
|
||||
|
|
@ -321,25 +318,24 @@ impl Common {
|
|||
}
|
||||
}
|
||||
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
state.common.shell.update_active(seats.iter())
|
||||
state.common.shell.update_active()
|
||||
}
|
||||
}
|
||||
|
||||
fn focus_target_is_valid(
|
||||
state: &mut State,
|
||||
shell: &mut Shell,
|
||||
seat: &Seat<State>,
|
||||
output: &Output,
|
||||
target: KeyboardFocusTarget,
|
||||
) -> bool {
|
||||
// If a session lock is active, only lock surfaces can be focused
|
||||
if state.common.shell.session_lock.is_some() {
|
||||
if shell.session_lock.is_some() {
|
||||
return matches!(target, KeyboardFocusTarget::LockSurface(_));
|
||||
}
|
||||
|
||||
// If an exclusive layer shell surface exists (on any output), only exclusive
|
||||
// shell surfaces can have focus, on the highest layer with exclusive surfaces.
|
||||
if let Some(layer) = exclusive_layer_surface_layer(state) {
|
||||
if let Some(layer) = exclusive_layer_surface_layer(shell) {
|
||||
return if let KeyboardFocusTarget::LayerSurface(layer_surface) = target {
|
||||
let data = layer_surface.cached_state();
|
||||
(data.keyboard_interactivity, data.layer) == (KeyboardInteractivity::Exclusive, layer)
|
||||
|
|
@ -350,9 +346,7 @@ fn focus_target_is_valid(
|
|||
|
||||
match target {
|
||||
KeyboardFocusTarget::Element(mapped) => {
|
||||
let is_sticky = state
|
||||
.common
|
||||
.shell
|
||||
let is_sticky = shell
|
||||
.workspaces
|
||||
.sets
|
||||
.get(output)
|
||||
|
|
@ -361,13 +355,13 @@ fn focus_target_is_valid(
|
|||
.mapped()
|
||||
.any(|m| m == &mapped);
|
||||
|
||||
let workspace = state.common.shell.active_space(&output);
|
||||
let workspace = shell.active_space(&output);
|
||||
let focus_stack = workspace.focus_stack.get(&seat);
|
||||
let is_in_focus_stack = focus_stack.last().map(|m| m == &mapped).unwrap_or(false);
|
||||
let has_fullscreen = workspace.get_fullscreen().is_some();
|
||||
|
||||
if is_sticky && !is_in_focus_stack {
|
||||
Shell::append_focus_stack(state, &mapped, seat);
|
||||
shell.append_focus_stack(&mapped, seat);
|
||||
}
|
||||
|
||||
(is_sticky || is_in_focus_stack) && !has_fullscreen
|
||||
|
|
@ -375,16 +369,14 @@ fn focus_target_is_valid(
|
|||
KeyboardFocusTarget::LayerSurface(layer) => {
|
||||
layer_map_for_output(&output).layers().any(|l| l == &layer)
|
||||
}
|
||||
KeyboardFocusTarget::Group(WindowGroup { node, .. }) => state
|
||||
.common
|
||||
.shell
|
||||
KeyboardFocusTarget::Group(WindowGroup { node, .. }) => shell
|
||||
.workspaces
|
||||
.active(&output)
|
||||
.1
|
||||
.tiling_layer
|
||||
.has_node(&node),
|
||||
KeyboardFocusTarget::Fullscreen(window) => {
|
||||
let workspace = state.common.shell.active_space(&output);
|
||||
let workspace = shell.active_space(&output);
|
||||
let focus_stack = workspace.focus_stack.get(&seat);
|
||||
|
||||
focus_stack
|
||||
|
|
@ -399,17 +391,17 @@ fn focus_target_is_valid(
|
|||
}
|
||||
|
||||
fn update_focus_target(
|
||||
state: &State,
|
||||
shell: &Shell,
|
||||
seat: &Seat<State>,
|
||||
output: &Output,
|
||||
) -> Option<KeyboardFocusTarget> {
|
||||
if let Some(session_lock) = &state.common.shell.session_lock {
|
||||
if let Some(session_lock) = &shell.session_lock {
|
||||
session_lock
|
||||
.surfaces
|
||||
.get(output)
|
||||
.cloned()
|
||||
.map(KeyboardFocusTarget::from)
|
||||
} else if let Some(layer) = exclusive_layer_surface_layer(state) {
|
||||
} else if let Some(layer) = exclusive_layer_surface_layer(shell) {
|
||||
layer_map_for_output(output)
|
||||
.layers()
|
||||
.find(|layer_surface| {
|
||||
|
|
@ -419,12 +411,10 @@ fn update_focus_target(
|
|||
})
|
||||
.cloned()
|
||||
.map(KeyboardFocusTarget::from)
|
||||
} else if let Some(surface) = state.common.shell.active_space(&output).get_fullscreen() {
|
||||
} else if let Some(surface) = shell.active_space(&output).get_fullscreen() {
|
||||
Some(KeyboardFocusTarget::Fullscreen(surface.clone()))
|
||||
} else {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
shell
|
||||
.active_space(&output)
|
||||
.focus_stack
|
||||
.get(&seat)
|
||||
|
|
@ -436,9 +426,9 @@ fn update_focus_target(
|
|||
|
||||
// Get the top-most layer, if any, with at least one surface with exclusive keyboard interactivity.
|
||||
// Only considers surface in `Top` or `Overlay` layer.
|
||||
fn exclusive_layer_surface_layer(state: &State) -> Option<Layer> {
|
||||
fn exclusive_layer_surface_layer(shell: &Shell) -> Option<Layer> {
|
||||
let mut layer = None;
|
||||
for output in state.common.shell.outputs() {
|
||||
for output in shell.outputs() {
|
||||
for layer_surface in layer_map_for_output(output).layers() {
|
||||
let data = layer_surface.cached_state();
|
||||
if data.keyboard_interactivity == KeyboardInteractivity::Exclusive {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
shell::{
|
||||
element::{CosmicMapped, CosmicStack, CosmicWindow},
|
||||
layout::tiling::ResizeForkTarget,
|
||||
CosmicSurface,
|
||||
CosmicSurface, SeatExt,
|
||||
},
|
||||
utils::prelude::*,
|
||||
wayland::handlers::{screencopy::SessionHolder, xdg_shell::popup::get_popup_toplevel},
|
||||
|
|
|
|||
|
|
@ -8,21 +8,21 @@ use crate::{
|
|||
grabs::ReleaseMode,
|
||||
CosmicSurface, Shell,
|
||||
},
|
||||
state::{Common, State},
|
||||
state::State,
|
||||
utils::{prelude::SeatExt, screenshot::screenshot_window},
|
||||
};
|
||||
|
||||
use super::{Item, ResizeEdge};
|
||||
|
||||
fn toggle_stacking(state: &mut State, mapped: &CosmicMapped) {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
if let Some(new_focus) = state.common.shell.toggle_stacking(mapped) {
|
||||
Common::set_focus(state, Some(&new_focus), &seat, None);
|
||||
Shell::set_focus(state, Some(&new_focus), &seat, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn move_prev_workspace(state: &mut State, mapped: &CosmicMapped) {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
let (current_handle, output) = {
|
||||
let Some(ws) = state.common.shell.space_for(mapped) else {
|
||||
return;
|
||||
|
|
@ -46,20 +46,21 @@ fn move_prev_workspace(state: &mut State, mapped: &CosmicMapped) {
|
|||
.map(|s| s.handle)
|
||||
});
|
||||
if let Some(prev_handle) = maybe_handle {
|
||||
Shell::move_window(
|
||||
state,
|
||||
if let Some((target, _)) = state.common.shell.move_window(
|
||||
Some(&seat),
|
||||
mapped,
|
||||
¤t_handle,
|
||||
&prev_handle,
|
||||
true,
|
||||
None,
|
||||
);
|
||||
) {
|
||||
Shell::set_focus(state, Some(&target), &seat, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
let (current_handle, output) = {
|
||||
let Some(ws) = state.common.shell.space_for(mapped) else {
|
||||
return;
|
||||
|
|
@ -76,15 +77,16 @@ fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) {
|
|||
.next()
|
||||
.map(|space| space.handle);
|
||||
if let Some(next_handle) = maybe_handle {
|
||||
Shell::move_window(
|
||||
state,
|
||||
if let Some((target, _point)) = state.common.shell.move_window(
|
||||
Some(&seat),
|
||||
mapped,
|
||||
¤t_handle,
|
||||
&next_handle,
|
||||
true,
|
||||
None,
|
||||
);
|
||||
) {
|
||||
Shell::set_focus(state, Some(&target), &seat, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +114,7 @@ pub fn tab_items(
|
|||
)
|
||||
.into();
|
||||
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active();
|
||||
let output = seat.active_output();
|
||||
let workspace = state.common.shell.workspaces.active_mut(&output);
|
||||
if is_tiled {
|
||||
|
|
@ -125,7 +127,7 @@ pub fn tab_items(
|
|||
{
|
||||
workspace.unmaximize_request(&mapped);
|
||||
}
|
||||
let focus_stack = workspace.focus_stack.get(&seat);
|
||||
let focus_stack = workspace.focus_stack.get(seat);
|
||||
workspace
|
||||
.tiling_layer
|
||||
.map(mapped, Some(focus_stack.iter()), None);
|
||||
|
|
@ -204,7 +206,7 @@ pub fn window_items(
|
|||
Item::new(fl!("window-menu-maximize"), move |handle| {
|
||||
let mapped = maximize_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
state.common.shell.maximize_toggle(&mapped, &seat);
|
||||
});
|
||||
})
|
||||
|
|
@ -215,7 +217,7 @@ pub fn window_items(
|
|||
Item::new(fl!("window-menu-tiled"), move |handle| {
|
||||
let tile_clone = tile_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
if let Some(ws) = state.common.shell.space_for_mut(&tile_clone) {
|
||||
ws.toggle_floating_window(&seat, &tile_clone);
|
||||
}
|
||||
|
|
@ -236,7 +238,7 @@ pub fn window_items(
|
|||
let move_clone = move_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
if let Some(surface) = move_clone.wl_surface() {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
Shell::move_request(state, &surface, &seat, None, ReleaseMode::Click, false);
|
||||
}
|
||||
});
|
||||
|
|
@ -247,7 +249,7 @@ pub fn window_items(
|
|||
Item::new(fl!("window-menu-resize-edge-top"), move |handle| {
|
||||
let resize_clone = resize_top_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::TOP);
|
||||
});
|
||||
})
|
||||
|
|
@ -255,7 +257,7 @@ pub fn window_items(
|
|||
Item::new(fl!("window-menu-resize-edge-left"), move |handle| {
|
||||
let resize_clone = resize_left_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::LEFT);
|
||||
});
|
||||
})
|
||||
|
|
@ -263,7 +265,7 @@ pub fn window_items(
|
|||
Item::new(fl!("window-menu-resize-edge-right"), move |handle| {
|
||||
let resize_clone = resize_right_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::RIGHT);
|
||||
});
|
||||
})
|
||||
|
|
@ -271,7 +273,7 @@ pub fn window_items(
|
|||
Item::new(fl!("window-menu-resize-edge-bottom"), move |handle| {
|
||||
let resize_clone = resize_bottom_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
Shell::menu_resize_request(state, &resize_clone, &seat, ResizeEdge::BOTTOM);
|
||||
});
|
||||
})
|
||||
|
|
@ -299,12 +301,8 @@ pub fn window_items(
|
|||
Item::new(fl!("window-menu-sticky"), move |handle| {
|
||||
let mapped = sticky_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toggle_sticky(seats.iter(), &seat, &mapped);
|
||||
let seat = state.common.shell.seats.last_active().clone();
|
||||
state.common.shell.toggle_sticky(&seat, &mapped);
|
||||
});
|
||||
})
|
||||
.toggled(is_sticky),
|
||||
|
|
|
|||
|
|
@ -40,10 +40,11 @@ use smithay::{
|
|||
|
||||
use crate::{
|
||||
shell::focus::target::PointerFocusTarget,
|
||||
shell::SeatExt,
|
||||
state::State,
|
||||
utils::{
|
||||
iced::{IcedElement, Program},
|
||||
prelude::{Global, OutputExt, PointGlobalExt, PointLocalExt, SeatExt, SizeExt},
|
||||
prelude::{Global, OutputExt, PointGlobalExt, PointLocalExt, SizeExt},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -215,7 +216,7 @@ impl Program for ContextMenu {
|
|||
if let Some(Item::Submenu { items, .. }) = self.items.get_mut(idx) {
|
||||
let items = items.clone();
|
||||
let _ = loop_handle.insert_idle(move |state| {
|
||||
let seat = state.common.last_active_seat();
|
||||
let seat = state.common.shell.seats.last_active();
|
||||
let grab_state = seat
|
||||
.user_data()
|
||||
.get::<SeatMenuGrabState>()
|
||||
|
|
@ -300,7 +301,7 @@ impl Program for ContextMenu {
|
|||
Message::ItemLeft(idx, _) => {
|
||||
if let Some(Item::Submenu { .. }) = self.items.get_mut(idx) {
|
||||
let _ = loop_handle.insert_idle(|state| {
|
||||
let seat = state.common.last_active_seat();
|
||||
let seat = state.common.shell.seats.last_active();
|
||||
let grab_state = seat
|
||||
.user_data()
|
||||
.get::<SeatMenuGrabState>()
|
||||
|
|
|
|||
|
|
@ -846,7 +846,7 @@ impl Drop for MoveGrab {
|
|||
);
|
||||
}
|
||||
}
|
||||
Common::set_focus(
|
||||
Shell::set_focus(
|
||||
state,
|
||||
Some(&KeyboardFocusTarget::from(mapped)),
|
||||
&seat,
|
||||
|
|
|
|||
359
src/shell/mod.rs
359
src/shell/mod.rs
|
|
@ -1,4 +1,5 @@
|
|||
use calloop::LoopHandle;
|
||||
use cosmic::Theme;
|
||||
use indexmap::IndexMap;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
|
|
@ -72,8 +73,10 @@ pub mod element;
|
|||
pub mod focus;
|
||||
pub mod grabs;
|
||||
pub mod layout;
|
||||
mod seats;
|
||||
mod workspace;
|
||||
pub use self::element::{CosmicMapped, CosmicMappedRenderElement, CosmicSurface};
|
||||
pub use self::seats::*;
|
||||
pub use self::workspace::*;
|
||||
use self::{
|
||||
element::{
|
||||
|
|
@ -199,6 +202,7 @@ pub struct Shell {
|
|||
pub pending_activations: HashMap<ActivationKey, ActivationContext>,
|
||||
pub override_redirect_windows: Vec<X11Surface>,
|
||||
pub session_lock: Option<SessionLock>,
|
||||
pub seats: Seats,
|
||||
|
||||
// wayland_state
|
||||
pub layer_shell_state: WlrLayerShellState,
|
||||
|
|
@ -650,10 +654,10 @@ impl Workspaces {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn remove_output(
|
||||
pub fn remove_output<'a>(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
seats: impl Iterator<Item = Seat<State>>,
|
||||
seats: impl Iterator<Item = &'a Seat<State>>,
|
||||
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||
xdg_activation_state: &XdgActivationState,
|
||||
|
|
@ -1024,21 +1028,22 @@ impl Workspaces {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_autotile_behavior(
|
||||
fn update_autotile_behavior<'a>(
|
||||
&mut self,
|
||||
behavior: TileBehavior,
|
||||
guard: &mut WorkspaceUpdateGuard<'_, State>,
|
||||
seats: Vec<Seat<State>>,
|
||||
seats: impl Iterator<Item = &'a Seat<State>>,
|
||||
) {
|
||||
self.autotile_behavior = behavior;
|
||||
self.apply_tile_change(guard, seats);
|
||||
}
|
||||
|
||||
fn apply_tile_change(
|
||||
fn apply_tile_change<'a>(
|
||||
&mut self,
|
||||
guard: &mut WorkspaceUpdateGuard<'_, State>,
|
||||
seats: Vec<Seat<State>>,
|
||||
seats: impl Iterator<Item = &'a Seat<State>>,
|
||||
) {
|
||||
let seats = seats.cloned().collect::<Vec<_>>();
|
||||
for (_, set) in &mut self.sets {
|
||||
set.tiling_enabled = self.autotile;
|
||||
|
||||
|
|
@ -1056,11 +1061,11 @@ impl Workspaces {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_autotile(
|
||||
fn update_autotile<'a>(
|
||||
&mut self,
|
||||
autotile: bool,
|
||||
guard: &mut WorkspaceUpdateGuard<'_, State>,
|
||||
seats: Vec<Seat<State>>,
|
||||
seats: impl Iterator<Item = &'a Seat<State>>,
|
||||
) {
|
||||
self.autotile = autotile;
|
||||
self.apply_tile_change(guard, seats);
|
||||
|
|
@ -1105,6 +1110,7 @@ impl Shell {
|
|||
Shell {
|
||||
popups: PopupManager::default(),
|
||||
workspaces: Workspaces::new(config, theme.clone()),
|
||||
seats: Seats::new(),
|
||||
|
||||
pending_windows: Vec::new(),
|
||||
pending_layers: Vec::new(),
|
||||
|
|
@ -1139,10 +1145,10 @@ impl Shell {
|
|||
self.refresh(); // fixes indicies of any moved workspaces
|
||||
}
|
||||
|
||||
pub fn remove_output(&mut self, output: &Output, seats: impl Iterator<Item = Seat<State>>) {
|
||||
pub fn remove_output(&mut self, output: &Output) {
|
||||
self.workspaces.remove_output(
|
||||
output,
|
||||
seats,
|
||||
self.seats.iter(),
|
||||
&mut self.workspace_state.update(),
|
||||
&mut self.toplevel_info_state,
|
||||
&self.xdg_activation_state,
|
||||
|
|
@ -1601,11 +1607,10 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn overview_mode(&mut self) -> (OverviewMode, Option<SwapIndicator>) {
|
||||
pub fn overview_mode(&self) -> (OverviewMode, Option<SwapIndicator>) {
|
||||
if let OverviewMode::Ended(_, timestamp) = self.overview_mode {
|
||||
if Instant::now().duration_since(timestamp) > ANIMATION_DURATION {
|
||||
self.overview_mode = OverviewMode::None;
|
||||
self.swap_indicator = None;
|
||||
return (OverviewMode::None, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1641,11 +1646,10 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resize_mode(&mut self) -> (ResizeMode, Option<ResizeIndicator>) {
|
||||
pub fn resize_mode(&self) -> (ResizeMode, Option<ResizeIndicator>) {
|
||||
if let ResizeMode::Ended(timestamp, _) = self.resize_mode {
|
||||
if Instant::now().duration_since(timestamp) > ANIMATION_DURATION {
|
||||
self.resize_mode = ResizeMode::None;
|
||||
self.resize_indicator = None;
|
||||
return (ResizeMode::None, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1673,6 +1677,19 @@ impl Shell {
|
|||
|
||||
#[profiling::function]
|
||||
pub fn refresh(&mut self) {
|
||||
if let OverviewMode::Ended(_, timestamp) = self.overview_mode {
|
||||
if Instant::now().duration_since(timestamp) > ANIMATION_DURATION {
|
||||
self.overview_mode = OverviewMode::None;
|
||||
self.swap_indicator = None;
|
||||
}
|
||||
}
|
||||
if let ResizeMode::Ended(timestamp, _) = self.resize_mode {
|
||||
if Instant::now().duration_since(timestamp) > ANIMATION_DURATION {
|
||||
self.resize_mode = ResizeMode::None;
|
||||
self.resize_indicator = None;
|
||||
}
|
||||
}
|
||||
|
||||
self.popups.cleanup();
|
||||
|
||||
self.xdg_activation_state.retain_tokens(|_, data| {
|
||||
|
|
@ -1764,23 +1781,24 @@ impl Shell {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn map_window(state: &mut State, window: &CosmicSurface) {
|
||||
let pos = state
|
||||
.common
|
||||
.shell
|
||||
#[must_use]
|
||||
pub fn map_window(
|
||||
&mut self,
|
||||
window: &CosmicSurface,
|
||||
evlh: &LoopHandle<'static, State>,
|
||||
theme: &Theme,
|
||||
) -> Option<KeyboardFocusTarget> {
|
||||
let pos = self
|
||||
.pending_windows
|
||||
.iter()
|
||||
.position(|(w, _, _)| w == window)
|
||||
.unwrap();
|
||||
let (window, seat, output) = state.common.shell.pending_windows.remove(pos);
|
||||
let (window, seat, output) = self.pending_windows.remove(pos);
|
||||
|
||||
let parent_is_sticky = if let Some(toplevel) = window.0.toplevel() {
|
||||
if let Some(parent) = toplevel.parent() {
|
||||
if let Some(elem) = state.common.shell.element_for_surface(&parent) {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
if let Some(elem) = self.element_for_surface(&parent) {
|
||||
self.workspaces
|
||||
.sets
|
||||
.values()
|
||||
.any(|set| set.sticky_layer.mapped().any(|m| m == elem))
|
||||
|
|
@ -1794,11 +1812,7 @@ impl Shell {
|
|||
false
|
||||
};
|
||||
|
||||
let pending_activation = state
|
||||
.common
|
||||
.shell
|
||||
.pending_activations
|
||||
.remove(&(&window).into());
|
||||
let pending_activation = self.pending_activations.remove(&(&window).into());
|
||||
let workspace_handle = match pending_activation {
|
||||
Some(ActivationContext::Workspace(handle)) => Some(handle),
|
||||
_ => None,
|
||||
|
|
@ -1809,22 +1823,16 @@ impl Shell {
|
|||
|
||||
// this is beyond stupid, just to make the borrow checker happy
|
||||
let workspace = if let Some(handle) = workspace_handle.filter(|handle| {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
self.workspaces
|
||||
.spaces()
|
||||
.any(|space| &space.handle == handle)
|
||||
}) {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
self.workspaces
|
||||
.spaces_mut()
|
||||
.find(|space| space.handle == handle)
|
||||
.unwrap()
|
||||
} else {
|
||||
state.common.shell.workspaces.active_mut(&output)
|
||||
self.workspaces.active_mut(&output)
|
||||
};
|
||||
if output != workspace.output {
|
||||
output = workspace.output.clone();
|
||||
|
|
@ -1832,57 +1840,34 @@ impl Shell {
|
|||
|
||||
if let Some((mapped, layer, previous_workspace)) = workspace.remove_fullscreen() {
|
||||
let old_handle = workspace.handle.clone();
|
||||
let new_workspace_handle = state
|
||||
.common
|
||||
.shell
|
||||
let new_workspace_handle = self
|
||||
.workspaces
|
||||
.space_for_handle(&previous_workspace)
|
||||
.is_some()
|
||||
.then_some(previous_workspace)
|
||||
.unwrap_or(old_handle);
|
||||
|
||||
state.common.shell.remap_unfullscreened_window(
|
||||
mapped,
|
||||
&old_handle,
|
||||
&new_workspace_handle,
|
||||
layer,
|
||||
);
|
||||
self.remap_unfullscreened_window(mapped, &old_handle, &new_workspace_handle, layer);
|
||||
};
|
||||
|
||||
let active_handle = state.common.shell.workspaces.active(&output).1.handle;
|
||||
let active_handle = self.active_space(&output).handle;
|
||||
let workspace = if let Some(handle) = workspace_handle.filter(|handle| {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
self.workspaces
|
||||
.spaces()
|
||||
.any(|space| &space.handle == handle)
|
||||
}) {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
self.workspaces
|
||||
.spaces_mut()
|
||||
.find(|space| space.handle == handle)
|
||||
.unwrap()
|
||||
} else {
|
||||
state.common.shell.workspaces.active_mut(&output)
|
||||
self.workspaces.active_mut(&output)
|
||||
};
|
||||
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
.new_toplevel(&window, &state.common.shell.workspace_state);
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
self.toplevel_info_state.new_toplevel(&window, &self.workspace_state);
|
||||
self.toplevel_info_state
|
||||
.toplevel_enter_output(&window, &output);
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
self.toplevel_info_state
|
||||
.toplevel_enter_workspace(&window, &workspace.handle);
|
||||
|
||||
let workspace_output = workspace.output.clone();
|
||||
|
|
@ -1899,16 +1884,16 @@ impl Shell {
|
|||
{
|
||||
focused.stack_ref().unwrap().add_window(window, None);
|
||||
if was_activated {
|
||||
state.common.shell.set_urgent(&workspace_handle);
|
||||
self.set_urgent(&workspace_handle);
|
||||
}
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let mapped = CosmicMapped::from(CosmicWindow::new(
|
||||
window.clone(),
|
||||
state.common.event_loop_handle.clone(),
|
||||
state.common.theme.clone(),
|
||||
evlh.clone(),
|
||||
theme.clone(),
|
||||
));
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
|
|
@ -1935,9 +1920,7 @@ impl Shell {
|
|||
}
|
||||
|
||||
if !parent_is_sticky && should_be_fullscreen {
|
||||
let from = state
|
||||
.common
|
||||
.shell
|
||||
let from = self
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&output, &mapped.active_window());
|
||||
|
||||
|
|
@ -1945,37 +1928,34 @@ impl Shell {
|
|||
}
|
||||
|
||||
if parent_is_sticky {
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toggle_sticky(seats.iter(), &seat, &mapped);
|
||||
self.toggle_sticky(&seat, &mapped);
|
||||
}
|
||||
|
||||
if (workspace_output == seat.active_output() && active_handle == workspace_handle)
|
||||
let new_target = if (workspace_output == seat.active_output()
|
||||
&& active_handle == workspace_handle)
|
||||
|| parent_is_sticky
|
||||
{
|
||||
// TODO: enforce focus stealing prevention by also checking the same rules as for the else case.
|
||||
Shell::set_focus(
|
||||
state,
|
||||
Some(&KeyboardFocusTarget::from(mapped.clone())),
|
||||
&seat,
|
||||
None,
|
||||
);
|
||||
} else if workspace_empty || was_activated || should_be_fullscreen {
|
||||
Shell::append_focus_stack(state, &mapped, &seat);
|
||||
state.common.shell.set_urgent(&workspace_handle);
|
||||
Some(KeyboardFocusTarget::from(mapped.clone()))
|
||||
} else {
|
||||
if workspace_empty || was_activated || should_be_fullscreen {
|
||||
self.append_focus_stack(&mapped, &seat);
|
||||
self.set_urgent(&workspace_handle);
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
let active_space = self.active_space(&output);
|
||||
for mapped in active_space.mapped() {
|
||||
self.update_reactive_popups(mapped);
|
||||
}
|
||||
|
||||
let active_space = state.common.shell.active_space(&output);
|
||||
for mapped in active_space.mapped() {
|
||||
state.common.shell.update_reactive_popups(mapped);
|
||||
}
|
||||
new_target
|
||||
}
|
||||
|
||||
pub fn map_override_redirect(state: &mut State, window: X11Surface) {
|
||||
pub fn map_override_redirect(&mut self, window: X11Surface) {
|
||||
let geo = window.geometry();
|
||||
for (output, overlap) in state.common.shell.outputs().cloned().filter_map(|o| {
|
||||
for (output, overlap) in self.outputs().cloned().filter_map(|o| {
|
||||
o.geometry()
|
||||
.as_logical()
|
||||
.intersection(geo)
|
||||
|
|
@ -1984,18 +1964,17 @@ impl Shell {
|
|||
window.output_enter(&output, overlap);
|
||||
}
|
||||
|
||||
state.common.shell.override_redirect_windows.push(window);
|
||||
self.override_redirect_windows.push(window);
|
||||
}
|
||||
|
||||
pub fn map_layer(state: &mut State, layer_surface: &LayerSurface) {
|
||||
let pos = state
|
||||
.common
|
||||
.shell
|
||||
#[must_use]
|
||||
pub fn map_layer(&mut self, layer_surface: &LayerSurface) -> Option<KeyboardFocusTarget> {
|
||||
let pos = self
|
||||
.pending_layers
|
||||
.iter()
|
||||
.position(|(l, _, _)| l == layer_surface)
|
||||
.unwrap();
|
||||
let (layer_surface, output, seat) = state.common.shell.pending_layers.remove(pos);
|
||||
let (layer_surface, output, _seat) = self.pending_layers.remove(pos);
|
||||
|
||||
let wants_focus = {
|
||||
with_states(layer_surface.wl_surface(), |states| {
|
||||
|
|
@ -2009,13 +1988,11 @@ impl Shell {
|
|||
let mut map = layer_map_for_output(&output);
|
||||
map.map_layer(&layer_surface).unwrap();
|
||||
}
|
||||
for workspace in state.common.shell.workspaces.spaces_mut() {
|
||||
for workspace in self.workspaces.spaces_mut() {
|
||||
workspace.tiling_layer.recalculate();
|
||||
}
|
||||
|
||||
if wants_focus {
|
||||
Shell::set_focus(state, Some(&layer_surface.into()), &seat, None)
|
||||
}
|
||||
wants_focus.then(|| layer_surface.into())
|
||||
}
|
||||
|
||||
pub fn unmap_surface<S>(&mut self, surface: &S, seat: &Seat<State>)
|
||||
|
|
@ -2101,83 +2078,50 @@ impl Shell {
|
|||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn move_window(
|
||||
state: &mut State,
|
||||
&mut self,
|
||||
seat: Option<&Seat<State>>,
|
||||
mapped: &CosmicMapped,
|
||||
from: &WorkspaceHandle,
|
||||
to: &WorkspaceHandle,
|
||||
follow: bool,
|
||||
direction: Option<Direction>,
|
||||
) -> Option<Point<i32, Global>> {
|
||||
let from_output = state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
.space_for_handle(from)?
|
||||
.output
|
||||
.clone();
|
||||
let to_output = state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
.space_for_handle(to)?
|
||||
.output
|
||||
.clone();
|
||||
) -> Option<(KeyboardFocusTarget, Point<i32, Global>)> {
|
||||
let from_output = self.workspaces.space_for_handle(from)?.output.clone();
|
||||
let to_output = self.workspaces.space_for_handle(to)?.output.clone();
|
||||
|
||||
let from_workspace = state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
.space_for_handle_mut(from)
|
||||
.unwrap(); // checked above
|
||||
let from_workspace = self.workspaces.space_for_handle_mut(from).unwrap(); // checked above
|
||||
let window_state = from_workspace.unmap(mapped)?;
|
||||
let elements = from_workspace.mapped().cloned().collect::<Vec<_>>();
|
||||
|
||||
for (toplevel, _) in mapped.windows() {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
self.toplevel_info_state
|
||||
.toplevel_leave_workspace(&toplevel, from);
|
||||
if from_output != to_output {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
self.toplevel_info_state
|
||||
.toplevel_leave_output(&toplevel, &from_output);
|
||||
}
|
||||
}
|
||||
for mapped in elements.into_iter() {
|
||||
state.common.shell.update_reactive_popups(&mapped);
|
||||
self.update_reactive_popups(&mapped);
|
||||
}
|
||||
let new_pos = if follow {
|
||||
if let Some(seat) = seat {
|
||||
seat.set_active_output(&to_output);
|
||||
}
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
self.workspaces
|
||||
.idx_for_handle(&to_output, to)
|
||||
.and_then(|to_idx| {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.activate(&to_output, to_idx, WorkspaceDelta::new_shortcut())
|
||||
self.activate(&to_output, to_idx, WorkspaceDelta::new_shortcut())
|
||||
.unwrap()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let any_seat = seat.unwrap_or(state.common.last_active_seat()).clone();
|
||||
let mut to_workspace = state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
.space_for_handle_mut(to)
|
||||
.unwrap(); // checked above
|
||||
let any_seat = seat.unwrap_or(self.seats.last_active()).clone();
|
||||
let mut to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above
|
||||
let focus_stack = seat.map(|seat| to_workspace.focus_stack.get(&seat));
|
||||
if window_state.layer == ManagedLayer::Floating || !to_workspace.tiling_enabled {
|
||||
to_workspace.floating_layer.map(mapped.clone(), None);
|
||||
|
|
@ -2194,34 +2138,25 @@ impl Shell {
|
|||
if let Some((mapped, layer, previous_workspace)) = to_workspace.remove_fullscreen()
|
||||
{
|
||||
let old_handle = to.clone();
|
||||
let new_workspace_handle = state
|
||||
.common
|
||||
.shell
|
||||
let new_workspace_handle = self
|
||||
.workspaces
|
||||
.space_for_handle(&previous_workspace)
|
||||
.is_some()
|
||||
.then_some(previous_workspace)
|
||||
.unwrap_or(old_handle);
|
||||
|
||||
state.common.shell.remap_unfullscreened_window(
|
||||
self.remap_unfullscreened_window(
|
||||
mapped,
|
||||
&old_handle,
|
||||
&new_workspace_handle,
|
||||
layer,
|
||||
);
|
||||
to_workspace = state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
.space_for_handle_mut(to)
|
||||
.unwrap();
|
||||
to_workspace = self.workspaces.space_for_handle_mut(to).unwrap();
|
||||
// checked above
|
||||
}
|
||||
}
|
||||
|
||||
let from = state
|
||||
.common
|
||||
.shell
|
||||
let from = self
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&to_output, &mapped.active_window());
|
||||
|
||||
|
|
@ -2241,58 +2176,43 @@ impl Shell {
|
|||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
{
|
||||
state.common.shell.update_reactive_popups(&mapped);
|
||||
self.update_reactive_popups(&mapped);
|
||||
}
|
||||
for (toplevel, _) in mapped.windows() {
|
||||
if from_output != to_output {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
self.toplevel_info_state
|
||||
.toplevel_enter_output(&toplevel, &to_output);
|
||||
}
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
self.toplevel_info_state
|
||||
.toplevel_enter_workspace(&toplevel, to);
|
||||
}
|
||||
|
||||
if follow {
|
||||
if let Some(seat) = seat {
|
||||
Common::set_focus(state, Some(&focus_target), &seat, None);
|
||||
}
|
||||
}
|
||||
|
||||
new_pos
|
||||
new_pos.map(|pos| (focus_target, pos))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn move_current_window(
|
||||
state: &mut State,
|
||||
&mut self,
|
||||
seat: &Seat<State>,
|
||||
from_output: &Output,
|
||||
to: (&Output, Option<usize>),
|
||||
follow: bool,
|
||||
direction: Option<Direction>,
|
||||
) -> Result<Option<Point<i32, Global>>, InvalidWorkspaceIndex> {
|
||||
) -> Result<Option<(KeyboardFocusTarget, Point<i32, Global>)>, InvalidWorkspaceIndex> {
|
||||
let (to_output, to_idx) = to;
|
||||
let to_idx = to_idx.unwrap_or(state.common.shell.workspaces.active_num(to_output).1);
|
||||
let to_idx = to_idx.unwrap_or(self.workspaces.active_num(to_output).1);
|
||||
|
||||
if from_output == to_output
|
||||
&& to_idx == state.common.shell.workspaces.active_num(from_output).1
|
||||
{
|
||||
if from_output == to_output && to_idx == self.workspaces.active_num(from_output).1 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let to = state
|
||||
.common
|
||||
.shell
|
||||
let to = self
|
||||
.workspaces
|
||||
.get(to_idx, to_output)
|
||||
.map(|ws| ws.handle)
|
||||
.ok_or(InvalidWorkspaceIndex)?;
|
||||
|
||||
let from_workspace = state.common.shell.workspaces.active_mut(from_output);
|
||||
let from_workspace = self.workspaces.active_mut(from_output);
|
||||
let maybe_window = from_workspace.focus_stack.get(seat).last().cloned();
|
||||
|
||||
let Some(mapped) = maybe_window else {
|
||||
|
|
@ -2301,15 +2221,7 @@ impl Shell {
|
|||
|
||||
let from = from_workspace.handle;
|
||||
|
||||
Ok(Shell::move_window(
|
||||
state,
|
||||
Some(seat),
|
||||
&mapped,
|
||||
&from,
|
||||
&to,
|
||||
follow,
|
||||
direction,
|
||||
))
|
||||
Ok(self.move_window(Some(seat), &mapped, &from, &to, follow, direction))
|
||||
}
|
||||
|
||||
pub fn update_reactive_popups(&self, mapped: &CosmicMapped) {
|
||||
|
|
@ -2467,9 +2379,8 @@ impl Shell {
|
|||
return;
|
||||
}
|
||||
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
for workspace in state.common.shell.workspaces.spaces_mut() {
|
||||
for seat in seats.iter() {
|
||||
for seat in state.common.shell.seats.iter() {
|
||||
let mut stack = workspace.focus_stack.get_mut(seat);
|
||||
stack.remove(&old_mapped);
|
||||
}
|
||||
|
|
@ -2673,6 +2584,7 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_focus<'a>(&mut self, direction: FocusDirection, seat: &Seat<State>) -> FocusResult {
|
||||
let overview = self.overview_mode().0;
|
||||
let output = seat.active_output();
|
||||
|
|
@ -2804,6 +2716,7 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn move_current_element<'a>(
|
||||
&mut self,
|
||||
direction: Direction,
|
||||
|
|
@ -3270,6 +3183,7 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn toggle_stacking(&mut self, window: &CosmicMapped) -> Option<KeyboardFocusTarget> {
|
||||
if let Some(set) = self
|
||||
.workspaces
|
||||
|
|
@ -3291,6 +3205,7 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn toggle_stacking_focused(&mut self, seat: &Seat<State>) -> Option<KeyboardFocusTarget> {
|
||||
let set = self.workspaces.sets.get_mut(&seat.active_output()).unwrap();
|
||||
let workspace = &mut set.workspaces[set.active];
|
||||
|
|
@ -3315,16 +3230,10 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn toggle_sticky<'a>(
|
||||
&mut self,
|
||||
seats: impl Iterator<Item = &'a Seat<State>>,
|
||||
seat: &Seat<State>,
|
||||
mapped: &CosmicMapped,
|
||||
) {
|
||||
pub fn toggle_sticky<'a>(&mut self, seat: &Seat<State>, mapped: &CosmicMapped) {
|
||||
// clean from focus-stacks
|
||||
let seats = seats.collect::<Vec<_>>();
|
||||
for workspace in self.workspaces.spaces_mut() {
|
||||
for seat in seats.iter() {
|
||||
for seat in self.seats.iter() {
|
||||
let mut stack = workspace.focus_stack.get_mut(seat);
|
||||
stack.remove(mapped);
|
||||
}
|
||||
|
|
@ -3393,18 +3302,16 @@ impl Shell {
|
|||
_ => workspace.floating_layer.map(mapped.clone(), geometry.loc),
|
||||
}
|
||||
}
|
||||
|
||||
self.append_focus_stack(&mapped, seat);
|
||||
}
|
||||
|
||||
pub fn toggle_sticky_current<'a>(
|
||||
&mut self,
|
||||
seats: impl Iterator<Item = &'a Seat<State>>,
|
||||
seat: &Seat<State>,
|
||||
) {
|
||||
pub fn toggle_sticky_current<'a>(&mut self, seat: &Seat<State>) {
|
||||
let set = self.workspaces.sets.get_mut(&seat.active_output()).unwrap();
|
||||
let workspace = &mut set.workspaces[set.active];
|
||||
let maybe_window = workspace.focus_stack.get(seat).iter().next().cloned();
|
||||
if let Some(mapped) = maybe_window {
|
||||
self.toggle_sticky(seats, seat, &mapped);
|
||||
self.toggle_sticky(seat, &mapped);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3419,6 +3326,18 @@ impl Shell {
|
|||
let mut workspace_guard = self.workspace_state.update();
|
||||
workspace_guard.add_workspace_state(workspace, WState::Urgent);
|
||||
}
|
||||
|
||||
pub fn update_autotile(&mut self, autotile: bool) {
|
||||
let mut guard = self.workspace_state.update();
|
||||
self.workspaces
|
||||
.update_autotile(autotile, &mut guard, self.seats.iter())
|
||||
}
|
||||
|
||||
pub fn update_autotile_behavior(&mut self, behavior: TileBehavior) {
|
||||
let mut guard = self.workspace_state.update();
|
||||
self.workspaces
|
||||
.update_autotile_behavior(behavior, &mut guard, self.seats.iter())
|
||||
}
|
||||
}
|
||||
|
||||
fn workspace_set_idx<'a>(
|
||||
|
|
|
|||
306
src/shell/seats.rs
Normal file
306
src/shell/seats.rs
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{any::Any, cell::RefCell, collections::HashMap, sync::Mutex, time::Duration};
|
||||
|
||||
use crate::{
|
||||
backend::render::cursor::{CursorShape, CursorState},
|
||||
config::{xkb_config_to_wl, Config},
|
||||
input::{ModifiersShortcutQueue, SupressedKeys},
|
||||
state::State,
|
||||
};
|
||||
use smithay::{
|
||||
backend::input::{Device, DeviceCapability},
|
||||
desktop::utils::bbox_from_surface_tree,
|
||||
input::{
|
||||
keyboard::{LedState, XkbConfig},
|
||||
pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus},
|
||||
Seat, SeatState,
|
||||
},
|
||||
output::Output,
|
||||
reexports::{input::Device as InputDevice, wayland_server::DisplayHandle},
|
||||
utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Time, Transform},
|
||||
wayland::compositor::with_states,
|
||||
};
|
||||
use tracing::warn;
|
||||
|
||||
use super::grabs::{SeatMenuGrabState, SeatMoveGrabState};
|
||||
|
||||
crate::utils::id_gen!(next_seat_id, SEAT_ID, SEAT_IDS);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Seats {
|
||||
seats: Vec<Seat<State>>,
|
||||
last_active: Option<Seat<State>>,
|
||||
}
|
||||
|
||||
impl Seats {
|
||||
pub fn new() -> Seats {
|
||||
Seats {
|
||||
seats: Vec::new(),
|
||||
last_active: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_seat(&mut self, seat: Seat<State>) {
|
||||
if self.seats.is_empty() {
|
||||
self.last_active = Some(seat.clone());
|
||||
}
|
||||
self.seats.push(seat);
|
||||
}
|
||||
|
||||
pub fn remove_seat(&mut self, seat: &Seat<State>) {
|
||||
self.seats.retain(|s| s != seat);
|
||||
if self.seats.is_empty() {
|
||||
self.last_active = None;
|
||||
} else if self.last_active.as_ref().is_some_and(|s| s == seat) {
|
||||
self.last_active = Some(self.seats[0].clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Seat<State>> {
|
||||
self.seats.iter()
|
||||
}
|
||||
|
||||
pub fn last_active(&self) -> &Seat<State> {
|
||||
self.last_active.as_ref().expect("No seat?")
|
||||
}
|
||||
|
||||
pub fn update_last_active(&mut self, seat: &Seat<State>) {
|
||||
self.last_active = Some(seat.clone());
|
||||
}
|
||||
|
||||
pub fn for_device<D: Device>(&self, device: &D) -> Option<&Seat<State>> {
|
||||
self.iter().find(|seat| {
|
||||
let userdata = seat.user_data();
|
||||
let devices = userdata.get::<Devices>().unwrap();
|
||||
devices.has_device(device)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Devices {
|
||||
pub fn add_device<D: Device + 'static>(&self, device: &D) -> Vec<DeviceCapability> {
|
||||
let id = device.id();
|
||||
let mut map = self.capabilities.borrow_mut();
|
||||
let caps = [
|
||||
DeviceCapability::Keyboard,
|
||||
DeviceCapability::Pointer,
|
||||
DeviceCapability::TabletTool,
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|c| device.has_capability(*c))
|
||||
.collect::<Vec<_>>();
|
||||
let new_caps = caps
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|c| map.values().flatten().all(|has| *c != *has))
|
||||
.collect::<Vec<_>>();
|
||||
map.insert(id, caps);
|
||||
|
||||
if device.has_capability(DeviceCapability::Keyboard) {
|
||||
if let Some(device) = <dyn Any>::downcast_ref::<InputDevice>(device) {
|
||||
self.keyboards.borrow_mut().push(device.clone());
|
||||
}
|
||||
}
|
||||
|
||||
new_caps
|
||||
}
|
||||
|
||||
pub fn has_device<D: Device>(&self, device: &D) -> bool {
|
||||
self.capabilities.borrow().contains_key(&device.id())
|
||||
}
|
||||
|
||||
pub fn remove_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
|
||||
let id = device.id();
|
||||
|
||||
let mut keyboards = self.keyboards.borrow_mut();
|
||||
if let Some(idx) = keyboards.iter().position(|x| x.id() == id) {
|
||||
keyboards.remove(idx);
|
||||
}
|
||||
|
||||
let mut map = self.capabilities.borrow_mut();
|
||||
map.remove(&id)
|
||||
.unwrap_or(Vec::new())
|
||||
.into_iter()
|
||||
.filter(|c| map.values().flatten().all(|has| *c != *has))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn update_led_state(&self, led_state: LedState) {
|
||||
for keyboard in self.keyboards.borrow_mut().iter_mut() {
|
||||
keyboard.led_update(led_state.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Devices {
|
||||
capabilities: RefCell<HashMap<String, Vec<DeviceCapability>>>,
|
||||
// Used for updating keyboard leds on kms backend
|
||||
keyboards: RefCell<Vec<InputDevice>>,
|
||||
}
|
||||
|
||||
impl Default for SeatId {
|
||||
fn default() -> SeatId {
|
||||
SeatId(next_seat_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SeatId {
|
||||
fn drop(&mut self) {
|
||||
SEAT_IDS.lock().unwrap().remove(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct SeatId(pub usize);
|
||||
struct ActiveOutput(pub RefCell<Output>);
|
||||
|
||||
pub fn create_seat(
|
||||
dh: &DisplayHandle,
|
||||
seat_state: &mut SeatState<State>,
|
||||
output: &Output,
|
||||
config: &Config,
|
||||
name: String,
|
||||
) -> Seat<State> {
|
||||
let mut seat = seat_state.new_wl_seat(dh, name);
|
||||
let userdata = seat.user_data();
|
||||
userdata.insert_if_missing(SeatId::default);
|
||||
userdata.insert_if_missing(Devices::default);
|
||||
userdata.insert_if_missing(SupressedKeys::default);
|
||||
userdata.insert_if_missing(ModifiersShortcutQueue::default);
|
||||
userdata.insert_if_missing(SeatMoveGrabState::default);
|
||||
userdata.insert_if_missing(SeatMenuGrabState::default);
|
||||
userdata.insert_if_missing(CursorState::default);
|
||||
userdata.insert_if_missing(|| ActiveOutput(RefCell::new(output.clone())));
|
||||
userdata.insert_if_missing(|| RefCell::new(CursorImageStatus::default_named()));
|
||||
|
||||
// A lot of clients bind keyboard and pointer unconditionally once on launch..
|
||||
// Initial clients might race the compositor on adding periheral and
|
||||
// end up in a state, where they are not able to receive input.
|
||||
// Additionally a lot of clients don't handle keyboards/pointer objects being
|
||||
// removed very well either and we don't want to crash applications, because the
|
||||
// user is replugging their keyboard or mouse.
|
||||
//
|
||||
// So instead of doing the right thing (and initialize these capabilities as matching
|
||||
// devices appear), we have to surrender to reality and just always expose a keyboard and pointer.
|
||||
let conf = config.xkb_config();
|
||||
if let Err(err) = seat.add_keyboard(xkb_config_to_wl(&conf), 600, 25) {
|
||||
warn!(
|
||||
?err,
|
||||
"Failed to load provided xkb config. Trying default...",
|
||||
);
|
||||
seat.add_keyboard(XkbConfig::default(), 600, 25)
|
||||
.expect("Failed to load xkb configuration files");
|
||||
}
|
||||
seat.add_pointer();
|
||||
seat.add_touch();
|
||||
|
||||
seat
|
||||
}
|
||||
|
||||
pub trait SeatExt {
|
||||
fn id(&self) -> usize;
|
||||
|
||||
fn active_output(&self) -> Output;
|
||||
fn set_active_output(&self, output: &Output);
|
||||
fn devices(&self) -> &Devices;
|
||||
|
||||
fn cursor_geometry(
|
||||
&self,
|
||||
loc: impl Into<Point<f64, Buffer>>,
|
||||
time: Time<Monotonic>,
|
||||
) -> Option<(Rectangle<i32, Buffer>, Point<i32, Buffer>)>;
|
||||
}
|
||||
|
||||
impl SeatExt for Seat<State> {
|
||||
fn id(&self) -> usize {
|
||||
self.user_data().get::<SeatId>().unwrap().0
|
||||
}
|
||||
|
||||
fn active_output(&self) -> Output {
|
||||
self.user_data()
|
||||
.get::<ActiveOutput>()
|
||||
.map(|x| x.0.borrow().clone())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn set_active_output(&self, output: &Output) {
|
||||
*self
|
||||
.user_data()
|
||||
.get::<ActiveOutput>()
|
||||
.unwrap()
|
||||
.0
|
||||
.borrow_mut() = output.clone();
|
||||
}
|
||||
|
||||
fn devices(&self) -> &Devices {
|
||||
self.user_data().get::<Devices>().unwrap()
|
||||
}
|
||||
|
||||
fn cursor_geometry(
|
||||
&self,
|
||||
loc: impl Into<Point<f64, Buffer>>,
|
||||
time: Time<Monotonic>,
|
||||
) -> Option<(Rectangle<i32, Buffer>, Point<i32, Buffer>)> {
|
||||
let location = loc.into().to_i32_round();
|
||||
|
||||
let cursor_status = self
|
||||
.user_data()
|
||||
.get::<RefCell<CursorImageStatus>>()
|
||||
.map(|cell| {
|
||||
let mut cursor_status = cell.borrow_mut();
|
||||
if let CursorImageStatus::Surface(ref surface) = *cursor_status {
|
||||
if !surface.alive() {
|
||||
*cursor_status = CursorImageStatus::default_named();
|
||||
}
|
||||
}
|
||||
cursor_status.clone()
|
||||
})
|
||||
.unwrap_or(CursorImageStatus::default_named());
|
||||
|
||||
match cursor_status {
|
||||
CursorImageStatus::Surface(surface) => {
|
||||
let hotspot = with_states(&surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<CursorImageAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.hotspot
|
||||
});
|
||||
let geo = bbox_from_surface_tree(&surface, (location.x, location.y));
|
||||
let buffer_geo = Rectangle::from_loc_and_size(
|
||||
(geo.loc.x, geo.loc.y),
|
||||
geo.size.to_buffer(1, Transform::Normal),
|
||||
);
|
||||
Some((buffer_geo, (hotspot.x, hotspot.y).into()))
|
||||
}
|
||||
CursorImageStatus::Named(CursorIcon::Default) => {
|
||||
let seat_userdata = self.user_data();
|
||||
seat_userdata.insert_if_missing(CursorState::default);
|
||||
let state = seat_userdata.get::<CursorState>().unwrap();
|
||||
let frame = state
|
||||
.cursors
|
||||
.get(&CursorShape::Default)
|
||||
.unwrap()
|
||||
.get_image(1, Into::<Duration>::into(time).as_millis() as u32);
|
||||
|
||||
Some((
|
||||
Rectangle::from_loc_and_size(
|
||||
location,
|
||||
(frame.width as i32, frame.height as i32),
|
||||
),
|
||||
(frame.xhot as i32, frame.yhot as i32).into(),
|
||||
))
|
||||
}
|
||||
CursorImageStatus::Named(_) => {
|
||||
// TODO: Handle for `cursor_shape_v1` protocol
|
||||
None
|
||||
}
|
||||
CursorImageStatus::Hidden => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue