Skip to content

Instantly share code, notes, and snippets.

@erenon
Created January 20, 2023 16:21
Show Gist options
  • Save erenon/024679fecf849454f54d245537f3a783 to your computer and use it in GitHub Desktop.
Save erenon/024679fecf849454f54d245537f3a783 to your computer and use it in GitHub Desktop.
Sway sessions à la tmux
set $mode_session session [s]elect [r]ename [f]latten
bindsym $mod+s mode "$mode_session"
mode "$mode_session" {
bindsym s exec --no-startup-id ~/bin/way_select_session.py, mode "default"
bindsym r exec --no-startup-id ~/bin/way_rename_workspace.py, mode "default"
bindsym f exec --no-startup-id ~/bin/way_flatten_sessions.py, mode "default"
bindsym Escape mode "default"
bindsym Return mode "default"
}
#!/usr/bin/python3
# Assign each workspace the lowest number still available.
import json
import subprocess
# do not move these workspaces
protected_workspaces = set([9])
renames = []
index = 1
workspaces = subprocess.check_output(["/usr/bin/swaymsg", "-r", "-t", "get_workspaces"])
workspaces = json.loads(workspaces)
for ws in workspaces:
if ws["num"] in protected_workspaces:
continue
session = ws["name"].partition(":")[2]
if session:
renames.append((ws["name"], f"{index}:{session}"))
else:
renames.append((ws["name"], f"{index}"))
index = index + 1
while index in protected_workspaces:
index = index + 1
for rn in renames:
subprocess.call(["/usr/bin/swaymsg", "rename", "workspace", rn[0], "to", rn[1]])
#!/usr/bin/python3
# Prompts for a name and renames the current workspace to it, ESC cancels.
# It preserves the number of the workspace - therefore the ordering.
import json
import subprocess
def ui_input(prompt):
s = subprocess.check_output(["/usr/bin/dmenu", "-b", "-p", prompt], input="", encoding="ascii")
return s.strip()
def focused_workspace_num():
workspaces = subprocess.check_output(["/usr/bin/swaymsg", "-r", "-t", "get_workspaces"])
workspaces = json.loads(workspaces)
for ws in workspaces:
if ws["focused"]:
return ws["num"]
return ""
name = ui_input("rename workspace")
num = focused_workspace_num()
subprocess.check_output(["/usr/bin/swaymsg", "rename", "workspace", "to", f"{num}:{name}"])
#!/usr/bin/python3
# Prompts for a workspace name, ESC cancels.
# If a name is given, it looks for the selected workspace.
# If a workspace is found, it moves that workspace, and every
# workspace to the right -- until a different named-workspace is found --
# to the beginning, and moves every other workspace to the end,
# starting at workspace number 10.
# If no workspace matches the given name, it will be created,
# and every other workspace will be moved to the end.
# It does not touch workspaces given in `protected_workspaces`.
import json
import subprocess
# do not move these workspaces
protected_workspaces = set([1, 9])
def ui_input(prompt, selection):
s = subprocess.check_output(["/usr/bin/dmenu", "-b", "-p", prompt], input=selection, encoding="ascii")
return s.strip()
def strip_before(s, d):
a,_,b = s.partition(d)
return b or a
def selected_range(workspaces, s):
first = 0
for ws in workspaces:
if ws["name"] == s:
break
first += 1
last = first + 1
for ws in workspaces[last:]:
if ':' in ws["name"]:
break
last += 1
return first, last
def renumber(i, ws):
tag = ws["name"].partition(":")[2]
return f"{i}:{tag}" if tag else str(i)
# select a session, find the range of workspaces that belongs to it
workspaces = subprocess.check_output(["/usr/bin/swaymsg", "-r", "-t", "get_workspaces"])
workspaces = json.loads(workspaces)
sessions = []
for ws in workspaces:
session = ws["name"].partition(":")[2]
if session:
sessions.append(ws["name"])
selected_session = ui_input("select session", "\n".join(sessions))
first, last = selected_range(workspaces, selected_session)
# move selected worspaces to si..., deselected workspaces to di...
si = 1
di = max(last-first, 10)
renames = []
for (i, ws) in enumerate(workspaces):
if ws["num"] in protected_workspaces:
continue
if first <= i and i < last:
while si in protected_workspaces:
si += 1
renames.append((ws["name"], renumber(si, ws)))
si += 1
else:
while di in protected_workspaces:
di += 1
if len(renames) == 0 and not ":" in ws["name"]:
# first deselected workspace is unnamed, name it "main" to avoid
# merging it with the selected session
renames.append((ws["name"], str(di) + ":main"))
else:
renames.append((ws["name"], renumber(di, ws)))
di += 1
# move them to tmp names to avoid clashes
for (src, dst) in renames:
subprocess.call(["/usr/bin/swaymsg", "rename", "workspace", src, "to", f"_{dst}"])
# move them back
for (_, dst) in renames:
subprocess.call(["/usr/bin/swaymsg", "rename", "workspace", f"_{dst}", "to", dst])
# finally, jump to the leading workspace if the selected session
# (this also creates the session if it is a new one)
tag = strip_before(selected_session, ":")
i = 1
while i in protected_workspaces:
i += 1
subprocess.call(["/usr/bin/swaymsg", "--", "workspace", "--no-auto-back-and-forth", f"{i}:{tag}"])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment