-
-
Save SidharthArya/f4d80c246793eb61be0ae928c9184406 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3 | |
import sys | |
import json | |
import subprocess | |
direction=bool(sys.argv[1] == 't' or sys.argv[1] == 'T') | |
swaymsg = subprocess.run(['swaymsg', '-t', 'get_tree'], stdout=subprocess.PIPE) | |
data = json.loads(swaymsg.stdout) | |
current = data["nodes"][1]["current_workspace"] | |
workspace = int(data["nodes"][1]["current_workspace"])-1 | |
roi = data["nodes"][1]["nodes"][workspace] | |
temp = roi | |
windows = list() | |
def getNextWindow(): | |
if focus < len(windows) - 1: | |
return focus+1 | |
else: | |
return 0 | |
def getPrevWindow(): | |
if focus > 0: | |
return focus-1 | |
else: | |
return len(windows)-1 | |
def makelist(temp): | |
for nodes in "floating_nodes", "nodes": | |
for i in range(len(temp[nodes])): | |
if temp[nodes][i]["name"] is None: | |
makelist(temp[nodes][i]) | |
else: | |
windows.append(temp[nodes][i]) | |
def focused(temp_win): | |
for i in range(len(temp_win)): | |
if temp_win[i]["focused"] == True: | |
return i | |
makelist(temp) | |
# print(len(windows)) | |
focus = focused(windows) | |
if str(sys.argv[1]) == 't' or str(sys.argv[1]) == 'T': | |
print(windows[getNextWindow()]["id"]) | |
else: | |
print(windows[getPrevWindow()]["id"]) | |
bindsym $mod+tab exec swaymsg [con_id=$(swaymsg -t get_tree | ~/.config/sway/alttab t)] focus | |
bindsym $mod+shift+tab exec swaymsg [con_id=$(swaymsg -t get_tree | ~/.config/sway/alttab f)] focus |
I struggled by sometimes when it doesn't work but that was because I use zsh and it parses [ ] on a 'special' way... so I reworked the way it launches swaymsg in that debug process. I also found that script doesn't work when workspaces aren't continuous, I mean, not 1,2,3,4,5,6,7 but like only 2,5,7 workspaces active. In this case your script don't switch the focus. Thanks,
I will look into these issues soon enough. if you are facing zsh issues i would suggest using bash -c
in your exec command. Thank you for your suggestions, i will merge them once i figure out the issues you are facing and why. .
Hi guys! Your implementations don`t work with floating windows. I`m quite newbie in python so that my desicion isn`t perfect. But I would like to share it with you. I just modified makelist
function. Hope it helps you:
def makelist(temp):
for nodes in "floating_nodes", "nodes":
for i in range(len(temp[nodes])):
if temp[nodes][i]["name"] is None:
makelist(temp[nodes][i])
else:
windows.append(temp[nodes][i])
Thanks for the script!
Great improvement, gorsheninmv, thanks :)
@gorsheninmv , thank you for the improvement.
I have been successfully using @alejor's version for a while. Now, after an update to sway I believe, it is giving an error for line 11:
Traceback (most recent call last):
File "/home/[user]/.config/sway/alttab", line 11, in <module>
current = data["nodes"][1]["current_workspace"]
KeyError: 'nodes'
@curiositry, @alejor 's version work fine. I have just tested both. Feel free to post again if it doesn't work.
@curiositry, I just updated my sway to latest commit and still it works fine. So we would know more details to give you any advice, :)
Thanks for your replies @SidharthArya and @alejor. I have updated to @SidharthArya's latest version, and it is now working. (Curiously, it didn't work until I rebooted; reloading Sway didn't suffice.)
@curiositry Glad to hear it worked.
Adding multi-monitor support to the above contributions:
#!/usr/bin/env python3
import sys
import json
import subprocess
direction=bool(sys.argv[1] == 'next')
swaymsg = subprocess.run(['swaymsg', '-t', 'get_tree'], stdout=subprocess.PIPE)
data = json.loads(swaymsg.stdout)
def setup():
def dig(nodes):
if nodes["focused"]:
return True
for node_type in "nodes", "floating_nodes":
if node_type in nodes:
for node in nodes[node_type]:
if node["focused"] or dig(node):
return True
return False
for monitor in data["nodes"]:
for workspace in monitor["nodes"]:
if workspace["focused"] or dig(workspace):
return workspace
workspace = setup()
temp = workspace
windows = list()
def getNextWindow():
if focus < len(windows) - 1:
return focus+1
else:
return 0
def getPrevWindow():
if focus > 0:
return focus-1
else:
return len(windows)-1
def makelist(temp):
for nodes in "floating_nodes", "nodes":
for i in range(len(temp[nodes])):
if temp[nodes][i]["name"] is None:
makelist(temp[nodes][i])
else:
windows.append(temp[nodes][i])
def focused(temp_win):
for i in range(len(temp_win)):
if temp_win[i]["focused"] == True:
return i
return 9
makelist(temp)
focus = focused(windows)
if direction:
attr = "[con_id="+str(windows[getNextWindow()]["id"])+"]"
else:
attr = "[con_id="+str(windows[getPrevWindow()]["id"])+"]"
sway = subprocess.run(['swaymsg', attr, 'focus'])
sys.exit(sway.returncode)
And here is an extension for a multi-monitor aware win+tab to flip workspaces open on a monitor:
#!/usr/bin/env python3
# Original: https://gist.github.com/SidharthArya/f4d80c246793eb61be0ae928c9184406
import sys
import json
import subprocess
target_windows = bool(sys.argv[1] == 'window')
direction=bool(sys.argv[2] == 'next')
swaymsg = subprocess.run(['swaymsg', '-t', 'get_tree'], stdout=subprocess.PIPE)
data = json.loads(swaymsg.stdout)
def setup():
def dig(nodes):
if nodes["focused"]:
return True
for node_type in "nodes", "floating_nodes":
if node_type in nodes:
for node in nodes[node_type]:
if node["focused"] or dig(node):
return True
return False
for monitor in data["nodes"]:
for workspace in monitor["nodes"]:
if workspace["focused"] or dig(workspace):
return monitor, workspace
monitor, workspace = setup()
def getNext(target_list, focus):
if focus < len(target_list) - 1:
return focus+1
else:
return 0
def getPrev(target_list, focus):
if focus > 0:
return focus-1
else:
return len(target_list)-1
def makelist_windows(temp, target_list = []):
for nodes in "floating_nodes", "nodes":
for i in range(len(temp[nodes])):
if temp[nodes][i]["name"] is None:
makelist_windows(temp[nodes][i], target_list)
else:
target_list.append(temp[nodes][i])
return target_list
def makelist_workspaces(workspaces, target_list = []):
for workspace in monitor["nodes"]:
target_list.append(workspace)
return target_list
def focused_window(temp_win):
for i in range(len(temp_win)):
if temp_win[i]["focused"] == True:
return i
def focused_workspace(workspaces, current_workspace):
for i in range(len(workspaces)):
if workspaces[i]["name"] == current_workspace["name"]:
return i
if target_windows:
target_list = makelist_windows(workspace)
focus = focused_window(target_list)
if direction:
attr = "[con_id="+str(target_list[getNext(target_list, focus)]["id"])+"]"
else:
attr = "[con_id="+str(target_list[getPrev(target_list, focus)]["id"])+"]"
sway = subprocess.run(['swaymsg', attr, 'focus'])
sys.exit(sway.returncode)
else:
target_list = makelist_workspaces(monitor)
if len(target_list) > 1:
focus = focused_workspace(target_list, workspace)
if direction:
attr = target_list[getNext(target_list, focus)]["name"]
else:
attr = target_list[getPrev(target_list, focus)]["name"]
sway = subprocess.run(['swaymsg', 'workspace', attr])
sys.exit(sway.returncode)
Will this work in i3?
There is quite some room for simplification by using the i3ipc python module:
#!/usr/bin/env python3
import i3ipc
import sys
def main():
sway = i3ipc.Connection()
for workspace in [ws for output in sway.get_tree().nodes for ws in output.nodes]:
focus = workspace.find_focused()
if focus is None:
continue
descendants = [d for d in workspace.descendants() if d.name is not None]
focus = descendants.index(focus)
focus = (focus + 1 if sys.argv[1] == "next" else focus - 1) % len(descendants)
sway.command(f"[con_id={descendants[focus].id}] focus")
sys.exit()
if __name__ == '__main__':
main()
Does it work on hyprland as well?
I struggled by sometimes when it doesn't work but that was because I use zsh and it parses [ ] on a 'special' way... so I reworked the way it launches swaymsg in that debug process. I also found that script doesn't work when workspaces aren't continuous, I mean, not 1,2,3,4,5,6,7 but like only 2,5,7 workspaces active. In this case your script don't switch the focus. Thanks,