1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
use niri_ipc::socket::Socket;
use niri_ipc::{Action, Reply, Request, Response, Window, Workspace};
fn sock() -> Socket {
Socket::connect().expect("could not connect to Niri socket")
}
fn list_workspaces() -> Vec<Workspace> {
let (reply, _) = sock()
.send(Request::Workspaces)
.expect("failed to send workspace request");
match reply {
Reply::Err(err) => panic!("failed to list workspaces: {}", err),
Reply::Ok(Response::Workspaces(w)) => w,
Reply::Ok(other) => panic!("unexpected reply from Niri: {:#?}", other),
}
}
fn list_windows() -> Vec<Window> {
let (reply, _) = sock()
.send(Request::Windows)
.expect("failed to send window request");
match reply {
Reply::Err(err) => panic!("failed to list windows: {}", err),
Reply::Ok(Response::Windows(w)) => w,
Reply::Ok(other) => panic!("unexpected reply from Niri: {:#?}", other),
}
}
fn reap_window(window: u64, workspace: u64) {
let (reply, _) = sock()
.send(Request::Action(Action::MoveWindowToWorkspace {
window_id: Some(window),
reference: niri_ipc::WorkspaceReferenceArg::Id(workspace),
}))
.expect("failed to send window move request");
reply.expect("failed to move window to workspace");
}
fn get_active_workspace(workspaces: &[Workspace]) -> &Workspace {
workspaces
.iter()
.filter(|w| w.is_focused)
.next()
.expect("expected an active workspace")
}
fn move_workspace_up() {
let (result, _) = sock()
.send(Request::Action(Action::MoveWorkspaceUp {}))
.expect("failed to send workspace move command");
result.expect("failed to move workspace up");
}
fn main() {
let mut workspaces = list_workspaces();
let mut active_workspace = get_active_workspace(&workspaces);
// Ensure that the current workspace is the first one, to avoid issues with
// indices changing during the window moves.
while active_workspace.idx > 1 {
move_workspace_up();
workspaces = list_workspaces();
active_workspace = get_active_workspace(&workspaces);
}
let orphan_workspaces = workspaces
.iter()
.filter(|w| w.output == active_workspace.output)
.filter(|w| w.idx > 1)
.map(|w| w.id)
.collect::<Vec<_>>();
if orphan_workspaces.is_empty() {
return;
}
let reapable = list_windows()
.into_iter()
.filter(|w| match w.workspace_id {
Some(id) => orphan_workspaces.contains(&id),
None => true,
})
.collect::<Vec<_>>();
for window in reapable.iter().rev() {
reap_window(window.id, active_workspace.id);
}
}
|