master ohmu/wmutils / scripts / workspace.sh
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