about summary refs log tree commit diff
path: root/users/tazjin/niri-reap/src/main.rs
blob: 315a5015d4137449979d9b5f7a7523bf96e2d096 (plain) (blame)
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);
    }
}