ohmu
·
2026-07-25
1#!/bin/sh
2# Constructs and groups windows into workspaces to switch between using your favorite keybinder.
3
4NUM_WS=9
5LOCKFILE="/tmp/ws_switch.lock"
6
7help() {
8 cat << EOF
9usage: $(basename $0) [-hinp] [-g ws_num] [-m ws_num]
10 -h: Displays this message
11 -i: Initialize workspaces. Should be called once in a startup script.
12 -n: Move up one workspace
13 -p: Move down one workspace
14 -g, <ws_num>: go to the workspace specified by <ws_num>
15 -m, <ws_num>: move the currently focused window to the worskpace specified by <ws_num>
16EOF
17 exit 1
18}
19
20# Save focused window for current workspace
21save_ws_focus() {
22 ws=$1
23 focused=$(pfw 2>/dev/null)
24 if [ -n "$focused" ]; then
25 echo "$focused" > "/tmp/workspaces/ws${ws}_focus"
26 fi
27}
28
29# Restore focused window for workspace
30restore_ws_focus() {
31 ws=$1
32 focus_file="/tmp/workspaces/ws${ws}_focus"
33
34 if [ -f "$focus_file" ]; then
35 wid=$(cat "$focus_file")
36 if wattr "$wid" 2>/dev/null; then
37 echo "$wid"
38 return 0
39 fi
40 fi
41
42 # Get first window from workspace file
43 ws_file="/tmp/workspaces/ws$ws"
44 if [ -f "$ws_file" ]; then
45 head -n1 "$ws_file"
46 else
47 echo ""
48 fi
49}
50
51# Initializes us in workspace 0.
52ws_init() {
53 mkdir -p /tmp/workspaces/
54 i=0
55 while [ $i -le $NUM_WS ]; do
56 :> /tmp/workspaces/ws"$i"
57 i=$(expr $i + 1)
58 done
59 echo 0 > /tmp/workspaces/curr
60}
61
62# Saves all mapped windows to the current workspace.
63save_ws() {
64 curr=$(cat /tmp/workspaces/curr)
65 lsw > /tmp/workspaces/ws"$curr"
66}
67
68move_to_ws() {
69 ws_num=$1
70 if [ $ws_num -gt $NUM_WS ] || [ $ws_num -lt 0 ]; then
71 echo "Workspace not found"
72 return
73 fi
74 curr_ws=$(cat /tmp/workspaces/curr);
75 if [ $ws_num == $curr_ws ]; then
76 # same workspace. ignore flicker
77 return
78 fi
79
80 touch "$LOCKFILE"
81
82 # Save current focus before switching
83 save_ws_focus "$curr_ws"
84
85 save_ws
86 curr_windows=$(lsw)
87 if [ "$curr_windows" ]; then
88 mapw -u $(lsw)
89 fi
90 new_windows="$(cat /tmp/workspaces/ws$ws_num)"
91 if [ "$new_windows" ]; then
92 mapw -m $new_windows
93 fi
94 echo $ws_num > /tmp/workspaces/curr
95
96 # Restore focus for new workspace
97 focused=$(restore_ws_focus "$ws_num")
98 if [ -n "$focused" ] && wattr "$focused" 2>/dev/null; then
99 wtf "$focused"
100 fi
101
102 (
103 sleep 0.3
104 rm -f "$LOCKFILE"
105 ) &
106}
107
108next_ws() {
109 curr=$(cat /tmp/workspaces/curr)
110 curr=$(expr $curr + 1)
111 if [ $curr -gt $NUM_WS ]; then
112 curr=0
113 fi
114 move_to_ws $curr
115}
116
117prev_ws() {
118 curr=$(cat /tmp/workspaces/curr)
119 curr=$(expr $curr - 1)
120 if [ $curr -lt 0 ]; then
121 curr=$NUM_WS
122 fi
123 move_to_ws $curr
124}
125
126move_focused_window() {
127 ws_num=$1
128 if [ $ws_num -gt $NUM_WS ] || [ $ws_num -lt 0 ]; then
129 echo "Workspace not found"
130 return
131 fi
132 wid=$(pfw)
133 curr_ws=$(cat /tmp/workspaces/curr);
134 if [ $ws_num != $curr_ws ]; then
135 touch "$LOCKFILE"
136
137 pfw >> /tmp/workspaces/ws"$1"
138 mapw -u $wid
139
140 (
141 sleep 0.3
142 rm -f "$LOCKFILE"
143 ) &
144 fi
145}
146
147while getopts ":m:g:npi" opt; do
148 case $opt in
149 n)
150 next_ws;;
151 p)
152 prev_ws;;
153 g)
154 move_to_ws $OPTARG;;
155 m)
156 move_focused_window $OPTARG;;
157 i)
158 ws_init;;
159 \?)
160 help;;
161 esac
162done