2022-07-18 21:26:02 +02:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
|
|
2024-02-23 17:25:40 +01:00
|
|
|
use smithay::{
|
|
|
|
|
desktop::{layer_map_for_output, WindowSurfaceType},
|
2024-08-20 08:50:13 -07:00
|
|
|
input::{pointer::MotionEvent, Seat},
|
2024-02-23 17:25:40 +01:00
|
|
|
output::Output,
|
|
|
|
|
reexports::wayland_server::DisplayHandle,
|
2024-08-20 08:50:13 -07:00
|
|
|
utils::{Point, Rectangle, Size, SERIAL_COUNTER},
|
2024-02-23 17:25:40 +01:00
|
|
|
};
|
2022-07-18 21:26:02 +02:00
|
|
|
|
|
|
|
|
use crate::{
|
2025-01-27 18:22:34 -08:00
|
|
|
shell::{element::CosmicWindow, CosmicSurface, Shell, WorkspaceDelta},
|
2022-07-18 21:26:02 +02:00
|
|
|
utils::prelude::*,
|
2023-01-16 15:12:25 +01:00
|
|
|
wayland::protocols::{
|
|
|
|
|
toplevel_info::ToplevelInfoHandler,
|
|
|
|
|
toplevel_management::{
|
2024-04-10 15:49:08 +02:00
|
|
|
delegate_toplevel_management, toplevel_rectangle_for, ManagementWindow,
|
|
|
|
|
ToplevelManagementHandler, ToplevelManagementState,
|
2023-01-16 15:12:25 +01:00
|
|
|
},
|
2025-02-20 10:29:23 -08:00
|
|
|
workspace::WorkspaceHandle,
|
2022-07-18 21:26:02 +02:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
impl ToplevelManagementHandler for State {
|
|
|
|
|
fn toplevel_management_state(&mut self) -> &mut ToplevelManagementState {
|
2024-04-10 15:49:08 +02:00
|
|
|
&mut self.common.toplevel_management_state
|
2022-07-18 21:26:02 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
fn activate(
|
|
|
|
|
&mut self,
|
2024-02-23 17:25:40 +01:00
|
|
|
dh: &DisplayHandle,
|
2023-01-16 15:12:25 +01:00
|
|
|
window: &<Self as ToplevelInfoHandler>::Window,
|
|
|
|
|
seat: Option<Seat<Self>>,
|
|
|
|
|
) {
|
2024-02-23 17:25:40 +01:00
|
|
|
self.unminimize(dh, window);
|
2024-04-10 15:49:08 +02:00
|
|
|
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
for output in shell.outputs().cloned().collect::<Vec<_>>().iter() {
|
|
|
|
|
let maybe = shell
|
2022-09-28 12:01:29 +02:00
|
|
|
.workspaces
|
|
|
|
|
.spaces_for_output(output)
|
|
|
|
|
.enumerate()
|
2024-02-23 17:25:40 +01:00
|
|
|
.find(|(_, w)| {
|
|
|
|
|
w.mapped()
|
|
|
|
|
.flat_map(|m| m.windows().map(|(s, _)| s))
|
|
|
|
|
.any(|w| &w == window)
|
|
|
|
|
});
|
2022-09-28 12:01:29 +02:00
|
|
|
if let Some((idx, workspace)) = maybe {
|
2024-04-10 15:49:08 +02:00
|
|
|
let seat = seat.unwrap_or(shell.seats.last_active().clone());
|
2022-09-28 12:01:29 +02:00
|
|
|
let mapped = workspace
|
|
|
|
|
.mapped()
|
|
|
|
|
.find(|m| m.windows().any(|(w, _)| &w == window))
|
|
|
|
|
.unwrap()
|
|
|
|
|
.clone();
|
|
|
|
|
|
2025-03-26 18:25:22 +01:00
|
|
|
let handle = workspace.handle;
|
2024-08-20 08:50:13 -07:00
|
|
|
let res = shell.activate(
|
2024-03-07 13:14:53 -06:00
|
|
|
&output,
|
2024-09-03 12:35:39 +01:00
|
|
|
idx,
|
2024-03-07 13:14:53 -06:00
|
|
|
WorkspaceDelta::new_shortcut(),
|
2024-04-10 15:49:08 +02:00
|
|
|
&mut self.common.workspace_state.update(),
|
2024-08-20 08:50:13 -07:00
|
|
|
);
|
2025-03-26 18:25:22 +01:00
|
|
|
|
|
|
|
|
let workspace = shell.workspaces.space_for_handle_mut(&handle).unwrap();
|
|
|
|
|
if seat.get_keyboard().unwrap().current_focus() != Some(mapped.clone().into())
|
|
|
|
|
&& workspace.is_tiled(&mapped)
|
|
|
|
|
{
|
|
|
|
|
for mapped in workspace
|
|
|
|
|
.mapped()
|
|
|
|
|
.filter(|m| m.maximized_state.lock().unwrap().is_some())
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.into_iter()
|
|
|
|
|
{
|
|
|
|
|
workspace.unmaximize_request(&mapped);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-10 15:49:08 +02:00
|
|
|
std::mem::drop(shell);
|
2024-08-20 08:50:13 -07:00
|
|
|
|
|
|
|
|
if seat.active_output() != *output {
|
|
|
|
|
match res {
|
|
|
|
|
Ok(Some(new_pos)) => {
|
|
|
|
|
seat.set_active_output(&output);
|
|
|
|
|
if let Some(ptr) = seat.get_pointer() {
|
|
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
|
|
|
|
ptr.motion(
|
|
|
|
|
self,
|
|
|
|
|
None,
|
|
|
|
|
&MotionEvent {
|
|
|
|
|
location: new_pos.to_f64().as_logical(),
|
|
|
|
|
serial,
|
2024-08-21 13:49:49 -07:00
|
|
|
time: self.common.clock.now().as_millis(),
|
2024-08-20 08:50:13 -07:00
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
ptr.frame(self);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(None) => {
|
|
|
|
|
seat.set_active_output(&output);
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mapped.focus_window(window);
|
2024-09-09 20:02:56 +02:00
|
|
|
Shell::set_focus(self, Some(&mapped.clone().into()), &seat, None, false);
|
2022-09-28 12:01:29 +02:00
|
|
|
return;
|
2022-07-18 21:26:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 15:12:25 +01:00
|
|
|
fn close(&mut self, _dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
|
|
|
|
|
window.close();
|
|
|
|
|
}
|
2023-12-06 15:13:48 -08:00
|
|
|
|
|
|
|
|
fn move_to_workspace(
|
|
|
|
|
&mut self,
|
|
|
|
|
_dh: &DisplayHandle,
|
|
|
|
|
window: &<Self as ToplevelInfoHandler>::Window,
|
2025-02-20 10:29:23 -08:00
|
|
|
to_handle: WorkspaceHandle,
|
2023-12-06 15:13:48 -08:00
|
|
|
_output: Output,
|
|
|
|
|
) {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2025-01-27 18:22:34 -08:00
|
|
|
if let Some(mut mapped) = shell.element_for_surface(window).cloned() {
|
|
|
|
|
if let Some(from_workspace) = shell.space_for_mut(&mapped) {
|
|
|
|
|
// If window is part of a stack, remove it and map it outside the stack
|
|
|
|
|
if let Some(stack) = mapped.stack_ref() {
|
|
|
|
|
stack.remove_window(&window);
|
|
|
|
|
mapped = CosmicWindow::new(
|
|
|
|
|
window.clone(),
|
|
|
|
|
self.common.event_loop_handle.clone(),
|
|
|
|
|
self.common.theme.clone(),
|
|
|
|
|
)
|
|
|
|
|
.into();
|
|
|
|
|
if from_workspace.tiling_enabled {
|
|
|
|
|
from_workspace.tiling_layer.map(
|
|
|
|
|
mapped.clone(),
|
|
|
|
|
None::<std::iter::Empty<_>>,
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
from_workspace.floating_layer.map(mapped.clone(), None);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-27 18:12:50 -08:00
|
|
|
let from_handle = from_workspace.handle;
|
|
|
|
|
let seat = shell.seats.last_active().clone();
|
|
|
|
|
let res = shell.move_window(
|
|
|
|
|
Some(&seat),
|
|
|
|
|
&mapped,
|
|
|
|
|
&from_handle,
|
|
|
|
|
&to_handle,
|
|
|
|
|
false,
|
|
|
|
|
None,
|
|
|
|
|
&mut self.common.workspace_state.update(),
|
|
|
|
|
);
|
|
|
|
|
if let Some((target, _)) = res {
|
|
|
|
|
std::mem::drop(shell);
|
|
|
|
|
Shell::set_focus(self, Some(&target), &seat, None, true);
|
|
|
|
|
}
|
2024-04-05 13:53:35 +02:00
|
|
|
}
|
2023-12-06 15:13:48 -08:00
|
|
|
}
|
|
|
|
|
}
|
2024-02-08 20:29:16 +01:00
|
|
|
|
|
|
|
|
fn fullscreen(
|
|
|
|
|
&mut self,
|
|
|
|
|
_dh: &DisplayHandle,
|
|
|
|
|
window: &<Self as ToplevelInfoHandler>::Window,
|
|
|
|
|
output: Option<Output>,
|
|
|
|
|
) {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
let seat = shell.seats.last_active().clone();
|
|
|
|
|
if let Some(mapped) = shell.element_for_surface(window).cloned() {
|
2025-01-06 19:23:06 +01:00
|
|
|
if let Some((output, workspace)) =
|
|
|
|
|
output.and_then(|output| shell.workspaces.active_mut(&output).map(|w| (output, w)))
|
|
|
|
|
{
|
2024-04-10 15:49:08 +02:00
|
|
|
let from = minimize_rectangle(&output, window);
|
2024-02-23 17:25:40 +01:00
|
|
|
workspace.fullscreen_request(window, None, from, &seat);
|
2024-04-10 15:49:08 +02:00
|
|
|
} else if let Some((output, handle)) = shell
|
2024-02-23 17:25:40 +01:00
|
|
|
.space_for(&mapped)
|
|
|
|
|
.map(|workspace| (workspace.output.clone(), workspace.handle.clone()))
|
|
|
|
|
{
|
2024-04-10 15:49:08 +02:00
|
|
|
let from = minimize_rectangle(&output, window);
|
|
|
|
|
shell
|
2024-02-23 17:25:40 +01:00
|
|
|
.workspaces
|
|
|
|
|
.space_for_handle_mut(&handle)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.fullscreen_request(window, None, from, &seat);
|
2024-02-08 20:29:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn unfullscreen(
|
|
|
|
|
&mut self,
|
|
|
|
|
_dh: &DisplayHandle,
|
|
|
|
|
window: &<Self as ToplevelInfoHandler>::Window,
|
|
|
|
|
) {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(mapped) = shell.element_for_surface(window).cloned() {
|
|
|
|
|
if let Some(workspace) = shell.space_for_mut(&mapped) {
|
2024-02-23 17:25:40 +01:00
|
|
|
if let Some((layer, previous_workspace)) = workspace.unfullscreen_request(window) {
|
|
|
|
|
let old_handle = workspace.handle.clone();
|
2024-04-10 15:49:08 +02:00
|
|
|
let new_workspace_handle = shell
|
2024-02-23 17:25:40 +01:00
|
|
|
.workspaces
|
|
|
|
|
.space_for_handle(&previous_workspace)
|
|
|
|
|
.is_some()
|
|
|
|
|
.then_some(previous_workspace)
|
|
|
|
|
.unwrap_or(old_handle); // if the workspace doesn't exist anymore, we can still remap on the right layer
|
|
|
|
|
|
2024-04-10 15:49:08 +02:00
|
|
|
shell.remap_unfullscreened_window(
|
2024-02-23 17:25:40 +01:00
|
|
|
mapped,
|
|
|
|
|
&old_handle,
|
|
|
|
|
&new_workspace_handle,
|
|
|
|
|
layer,
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-02-08 20:29:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn maximize(&mut self, _dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(mapped) = shell.element_for_surface(window).cloned() {
|
|
|
|
|
let seat = shell.seats.last_active().clone();
|
2025-02-04 17:49:19 +01:00
|
|
|
shell.maximize_request(&mapped, &seat, true);
|
2024-02-08 20:29:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn unmaximize(&mut self, _dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(mapped) = shell.element_for_surface(window).cloned() {
|
|
|
|
|
shell.unmaximize_request(&mapped);
|
2024-02-08 20:29:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn minimize(&mut self, _dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(mapped) = shell.element_for_surface(window).cloned() {
|
2024-02-27 14:19:59 +01:00
|
|
|
if !mapped.is_stack() || &mapped.active_window() == window {
|
2024-04-10 15:49:08 +02:00
|
|
|
shell.minimize_request(&mapped);
|
2024-02-27 14:19:59 +01:00
|
|
|
}
|
2024-02-08 20:29:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn unminimize(&mut self, _dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2025-01-27 18:37:27 -08:00
|
|
|
if let Some(mapped) = shell.element_for_surface(window).cloned() {
|
2024-04-10 15:49:08 +02:00
|
|
|
let seat = shell.seats.last_active().clone();
|
|
|
|
|
shell.unminimize_request(&mapped, &seat);
|
2024-02-27 14:19:59 +01:00
|
|
|
if mapped.is_stack() {
|
2025-01-27 18:37:27 -08:00
|
|
|
mapped.stack_ref().unwrap().set_active(window);
|
2024-02-27 14:19:59 +01:00
|
|
|
}
|
2024-02-08 20:29:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-09-18 16:02:41 +02:00
|
|
|
|
|
|
|
|
fn set_sticky(&mut self, _dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
|
|
|
|
|
if window.is_sticky() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-09-18 16:02:41 +02:00
|
|
|
if let Some(mapped) = shell.element_for_surface(window).cloned() {
|
|
|
|
|
let seat = shell.seats.last_active().clone();
|
|
|
|
|
shell.toggle_sticky(&seat, &mapped);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn unset_sticky(
|
|
|
|
|
&mut self,
|
|
|
|
|
_dh: &DisplayHandle,
|
|
|
|
|
window: &<Self as ToplevelInfoHandler>::Window,
|
|
|
|
|
) {
|
|
|
|
|
if !window.is_sticky() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-09-18 16:02:41 +02:00
|
|
|
if let Some(mapped) = shell.element_for_surface(window).cloned() {
|
|
|
|
|
let seat = shell.seats.last_active().clone();
|
|
|
|
|
shell.toggle_sticky(&seat, &mapped);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-16 15:12:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ManagementWindow for CosmicSurface {
|
|
|
|
|
fn close(&self) {
|
|
|
|
|
CosmicSurface::close(self)
|
2022-07-18 21:26:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-10 15:49:08 +02:00
|
|
|
pub fn minimize_rectangle(output: &Output, window: &CosmicSurface) -> Rectangle<i32, Local> {
|
|
|
|
|
toplevel_rectangle_for(window)
|
|
|
|
|
.find_map(|(surface, relative)| {
|
|
|
|
|
let map = layer_map_for_output(output);
|
|
|
|
|
let layer = map.layer_for_surface(&surface, WindowSurfaceType::ALL);
|
|
|
|
|
layer.and_then(|s| map.layer_geometry(s)).map(|local| {
|
2024-12-26 18:18:35 -08:00
|
|
|
Rectangle::new(
|
2024-04-10 15:49:08 +02:00
|
|
|
Point::from((local.loc.x + relative.loc.x, local.loc.y + relative.loc.y)),
|
|
|
|
|
relative.size,
|
2024-02-23 17:25:40 +01:00
|
|
|
)
|
|
|
|
|
})
|
2024-04-10 15:49:08 +02:00
|
|
|
})
|
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
|
let output_size = output.geometry().size;
|
2024-12-26 18:18:35 -08:00
|
|
|
Rectangle::new(
|
2024-04-10 15:49:08 +02:00
|
|
|
Point::from((
|
|
|
|
|
(output_size.w / 2) - 100,
|
|
|
|
|
output_size.h - (output_size.h / 3) - 50,
|
|
|
|
|
)),
|
|
|
|
|
Size::from((200, 100)),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.as_local()
|
2024-02-23 17:25:40 +01:00
|
|
|
}
|
|
|
|
|
|
2022-07-18 21:26:02 +02:00
|
|
|
delegate_toplevel_management!(State);
|