Skip to content

Commit

Permalink
workspace-management: Implement move_to_workspace requested
Browse files Browse the repository at this point in the history
Needed for `cosmic-workspaces`.

Requires pop-os/cosmic-protocols#21.
  • Loading branch information
ids1024 committed Dec 6, 2023
1 parent f9dc67a commit 0e7389f
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 75 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ wayland-backend = "0.3.2"
wayland-scanner = "0.31.0"
cosmic-comp-config = { path = "cosmic-comp-config" }
cosmic-config = { git = "https://github.com/pop-os/libcosmic/", features = ["calloop", "macro"] }
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"] }
# cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"] }
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "move-to-workspace", default-features = false, features = ["server"] }
libcosmic = { git = "https://github.com/pop-os/libcosmic/", default-features = false }
iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic/" }
tiny-skia = "0.10"
Expand Down
115 changes: 75 additions & 40 deletions src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1690,42 +1690,26 @@ impl Shell {
}
}

pub fn move_current_window(
state: &mut State,
seat: &Seat<State>,
pub fn move_window_to_workspace<'a>(
state: &'a mut State,
mapped: &CosmicMapped,
seat: Option<&Seat<State>>,
from_idx: usize,
from_output: &Output,
to: (&Output, Option<usize>),
follow: bool,
to_idx: usize,
to_output: &Output,
direction: Option<Direction>,
) -> Result<Option<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);
if state
) -> Result<Option<KeyboardFocusTarget>, InvalidWorkspaceIndex> {
let Some(from_workspace) = state
.common
.shell
.workspaces
.get(to_idx, to_output)
.is_none()
{
.get_mut(from_idx, from_output) else {
return Err(InvalidWorkspaceIndex);
}

if from_output == to_output
&& to_idx == state.common.shell.workspaces.active_num(from_output).1
{
return Ok(None);
}

let from_workspace = state.common.shell.workspaces.active_mut(from_output);
let maybe_window = from_workspace.focus_stack.get(seat).last().cloned();

let Some(mapped) = maybe_window else {
return Ok(None);
};
let Some(window_state) = from_workspace.unmap(&mapped) else {
let Some(window_state) = from_workspace.unmap(mapped) else {
return Ok(None);
};

for (toplevel, _) in mapped.windows() {
state
.common
Expand All @@ -1745,26 +1729,23 @@ impl Shell {
for mapped in elements.into_iter() {
state.common.shell.update_reactive_popups(&mapped);
}
let new_pos = if follow {
seat.set_active_output(&to_output);
state.common.shell.activate(to_output, to_idx)?
} else {
None
};

let mut to_workspace = state
let Some(mut to_workspace) = state
.common
.shell
.workspaces
.get_mut(to_idx, to_output)
.unwrap(); // checked above
let focus_stack = to_workspace.focus_stack.get(&seat);
.get_mut(to_idx, to_output) else {
return Err(InvalidWorkspaceIndex);
};
if window_state.layer == ManagedLayer::Floating {
to_workspace.floating_layer.map(mapped.clone(), None);
} else {
to_workspace
.tiling_layer
.map(mapped.clone(), Some(focus_stack.iter()), direction);
let focus_stack = seat.map(|seat| to_workspace.focus_stack.get(&seat));
to_workspace.tiling_layer.map(
mapped.clone(),
focus_stack.as_ref().map(|x| x.iter()),
direction,
);
}
let focus_target = if let Some(f) = window_state.was_fullscreen {
if to_workspace.fullscreen.is_some() {
Expand Down Expand Up @@ -1828,6 +1809,60 @@ impl Shell {
state.common.shell.update_reactive_popups(&mapped);
}

Ok(Some(focus_target))
}

pub fn move_current_window(
state: &mut State,
seat: &Seat<State>,
from_output: &Output,
to: (&Output, Option<usize>),
follow: bool,
direction: Option<Direction>,
) -> Result<Option<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);
if state
.common
.shell
.workspaces
.get(to_idx, to_output)
.is_none()
{
return Err(InvalidWorkspaceIndex);
}

let from_idx = state.common.shell.workspaces.active_num(from_output).1;

if from_output == to_output && to_idx == from_idx {
return Ok(None);
}

let from_workspace = state.common.shell.workspaces.active_mut(from_output);
let Some(mapped) = from_workspace.focus_stack.get(seat).last().cloned() else {
return Ok(None);
};

let new_pos = if follow {
seat.set_active_output(&to_output);
state.common.shell.activate(to_output, to_idx)?
} else {
None
};

let Some(focus_target) = Self::move_window_to_workspace(
state,
&mapped,
Some(seat),
from_idx,
from_output,
to_idx,
to_output,
direction,
)? else {
return Ok(None);
};

if follow {
Common::set_focus(state, Some(&focus_target), &seat, None);
}
Expand Down
60 changes: 58 additions & 2 deletions src/wayland/handlers/toplevel_management.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-only

use smithay::{input::Seat, reexports::wayland_server::DisplayHandle};
use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1;
use smithay::{input::Seat, output::Output, reexports::wayland_server::DisplayHandle};

use crate::{
shell::CosmicSurface,
shell::{CosmicSurface, Shell},
utils::prelude::*,
wayland::protocols::{
toplevel_info::ToplevelInfoHandler,
Expand Down Expand Up @@ -59,6 +60,61 @@ impl ToplevelManagementHandler for State {
fn close(&mut self, _dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
window.close();
}

fn move_to_workspace(
&mut self,
_dh: &DisplayHandle,
window: &<Self as ToplevelInfoHandler>::Window,
workspace: ZcosmicWorkspaceHandleV1,
output: Output,
) {
let Some(workspace_handle) = self.common.shell.workspace_state.get_workspace_handle(&workspace) else {
return;
};
let Some(to_idx) = self
.common
.shell
.workspaces
.spaces_for_output(&output)
.position(|w| w.handle == workspace_handle) else {
return;
};

for from_output in self
.common
.shell
.outputs()
.cloned()
.collect::<Vec<_>>()
.iter()
{
let maybe = self
.common
.shell
.workspaces
.spaces_for_output(from_output)
.enumerate()
.find(|(_, w)| w.windows().any(|w| &w == window));
if let Some((from_idx, from_workspace)) = maybe {
let mapped = from_workspace
.mapped()
.find(|m| m.windows().any(|(w, _)| &w == window))
.unwrap()
.clone();
let _ = Shell::move_window_to_workspace(
self,
&mapped,
None,
from_idx,
from_output,
to_idx,
&output,
None,
);
return;
}
}
}
}

impl ManagementWindow for CosmicSurface {
Expand Down
27 changes: 24 additions & 3 deletions src/wayland/protocols/toplevel_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ use smithay::{
};

pub use cosmic_protocols::toplevel_management::v1::server::zcosmic_toplevel_manager_v1::ZcosmicToplelevelManagementCapabilitiesV1 as ManagementCapabilities;
use cosmic_protocols::toplevel_management::v1::server::zcosmic_toplevel_manager_v1::{
self, ZcosmicToplevelManagerV1,
use cosmic_protocols::{
toplevel_management::v1::server::zcosmic_toplevel_manager_v1::{
self, ZcosmicToplevelManagerV1,
},
workspace::v1::server::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
};

use super::toplevel_info::{window_from_handle, ToplevelInfoHandler, ToplevelState, Window};
Expand Down Expand Up @@ -58,6 +61,14 @@ where
fn unmaximize(&mut self, dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {}
fn minimize(&mut self, dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {}
fn unminimize(&mut self, dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {}
fn move_to_workspace(
&mut self,
dh: &DisplayHandle,
window: &<Self as ToplevelInfoHandler>::Window,
workspace: ZcosmicWorkspaceHandleV1,
output: Output,
) {
}
}

pub struct ToplevelManagerGlobalData {
Expand All @@ -79,7 +90,7 @@ impl ToplevelManagementState {
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
{
let global = dh.create_global::<D, ZcosmicToplevelManagerV1, _>(
1,
2,
ToplevelManagerGlobalData {
filter: Box::new(client_filter),
},
Expand Down Expand Up @@ -221,6 +232,16 @@ where
}
}
}
zcosmic_toplevel_manager_v1::Request::MoveToWorkspace {
toplevel,
workspace,
output,
} => {
let window = window_from_handle(toplevel).unwrap();
if let Some(output) = Output::from_resource(&output) {
state.move_to_workspace(dh, &window, workspace, output);
}
}
_ => unreachable!(),
}
}
Expand Down
44 changes: 16 additions & 28 deletions src/wayland/protocols/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,58 +307,36 @@ where
) {
match request {
zcosmic_workspace_handle_v1::Request::Activate => {
if let Some(id) = state
.workspace_state()
.groups
.iter()
.find_map(|g| g.workspaces.iter().find(|w| w.instances.contains(obj)))
.map(|w| w.id)
{
if let Some(workspace_handle) = state.workspace_state().get_workspace_handle(obj) {
let mut state = client
.get_data::<<D as WorkspaceHandler>::Client>()
.unwrap()
.workspace_state()
.lock()
.unwrap();
state
.requests
.push(Request::Activate(WorkspaceHandle { id }));
state.requests.push(Request::Activate(workspace_handle));
}
}
zcosmic_workspace_handle_v1::Request::Deactivate => {
if let Some(id) = state
.workspace_state()
.groups
.iter()
.find_map(|g| g.workspaces.iter().find(|w| w.instances.contains(obj)))
.map(|w| w.id)
{
if let Some(workspace_handle) = state.workspace_state().get_workspace_handle(obj) {
let mut state = client
.get_data::<<D as WorkspaceHandler>::Client>()
.unwrap()
.workspace_state()
.lock()
.unwrap();
state
.requests
.push(Request::Deactivate(WorkspaceHandle { id }));
state.requests.push(Request::Deactivate(workspace_handle));
}
}
zcosmic_workspace_handle_v1::Request::Remove => {
if let Some(id) = state
.workspace_state()
.groups
.iter()
.find_map(|g| g.workspaces.iter().find(|w| w.instances.contains(obj)))
.map(|w| w.id)
{
if let Some(workspace_handle) = state.workspace_state().get_workspace_handle(obj) {
let mut state = client
.get_data::<<D as WorkspaceHandler>::Client>()
.unwrap()
.workspace_state()
.lock()
.unwrap();
state.requests.push(Request::Remove(WorkspaceHandle { id }));
state.requests.push(Request::Remove(workspace_handle));
}
}
zcosmic_workspace_handle_v1::Request::Destroy => {
Expand Down Expand Up @@ -556,6 +534,16 @@ where
}
}

pub fn get_workspace_handle(
&self,
handle: &ZcosmicWorkspaceHandleV1,
) -> Option<WorkspaceHandle> {
self.groups
.iter()
.find_map(|g| g.workspaces.iter().find(|w| w.instances.contains(handle)))
.map(|w| WorkspaceHandle { id: w.id })
}

pub fn global_id(&self) -> GlobalId {
self.global.clone()
}
Expand Down

0 comments on commit 0e7389f

Please sign in to comment.