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 94 95 96 97 98 99100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
|
#!/usr/bin/env python
from __future__ import print_function
import i3ipc
import getopt
import subprocess
import sys
def usage():
print("\n-h help")
print("-o / --output VM to focus")
print("-s / --screen VM screen to focus on")
print("-f / --flip flip the VM (or emacs) to the screen at the left")
print("-g / --glip flip the VM (or emacs) to the screen at the right")
print("-a / --assign move the VM (or emacs) to the specified screen")
print("-r / --run start the VM specified in -o if it's not running")
print("-e / --emacs focus Emacs")
print("-v / --verbose print debugging information\n")
print("One, and only one, of -o / -e / -f / -g / -a are mandatory")
print("Note -f and -g will shift a window only on l/c/r.")
print("-r / --run depends on $HOME / bin / vb being available")
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "vhfga:o:s:er", [
"verbose", "help", "flip", "glip", "assign=", "output=",
"screen=", 'emacs', 'run'])
except getopt.GetoptError as err:
print(str(err))
usage()
sys.exit(2)
output = None
emacs = False
run = False
verbose = False
flip = False
glip = False
assign = False
screen = ""
exclusive = 0
for o, a in opts:
if o in ("-s", "--screen"):
screen = a
elif o in ("-h", "--help"):
usage()
sys.exit()
elif o in ("-o", "--output"):
output = a
exclusive += 1
elif o in ("-v", "--verbose"):
verbose = True
elif o in ("-e", "--emacs"):
emacs = True
exclusive += 1
elif o in ("-r", "--run"):
run = True
elif o in ("-f", "--flip"):
flip = True
exclusive += 1
elif o in ("-g", "--glip"):
glip = True
exclusive += 1
elif o in ("-a", "--assign"):
assign = a
exclusive += 1
else:
assert False, "unhandled option"
if exclusive != 1:
print("One, and only one, of -o / -e / -f / -g / -a is required")
usage()
sys.exit(-1)
conn = i3ipc.Connection()
workspaces = conn.get_workspaces()
current_workspace = filter(lambda w: w.focused, workspaces)[0]
cname = current_workspace.name
current_window = conn.get_tree().find_focused()
# If we are flipping we don't know what we are focusing, obviously
# all this relies on the workspace naming convention we have
if flip or glip or assign:
if cname.startswith('emacs'):
if assign:
dest_workspace = assign[0]
elif cname.endswith('e'):
dest_workspace = 'r' if flip else 'l'
elif cname.endswith('c'):
dest_workspace = 'l' if flip else 'c'
elif cname.endswith('l'):
dest_workspace = 'r' if flip else 'l'
else:
dest_workspace = 'c' if flip else 'r'
dest_workspace = "emacs" + dest_workspace
elif (len(cname) == 2 and
cname[0] in ('1', '2', '3', '4', '5') and
cname[1] in ('e', 'l', 'r', 'c')):
# Are we on a normal e/l/c/r workspace, if so move to the same
# workspace number on the other monitor, note no flip ending on
# 'e' as that monitor is not always on.
if assign:
dest_workspace = assign[0]
elif cname.endswith('e'):
dest_workspace = 'r' if flip else 'l'
elif cname.endswith('c'):
dest_workspace = 'l' if flip else 'r'
elif cname.endswith('l'):
dest_workspace = 'r' if flip else 'c'
else:
dest_workspace = 'c' if flip else 'l'
dest_workspace = cname[0] + dest_workspace
else:
# let's see if we are on a VM workspace, same here no flipping
# to the extra screen, only from it.
w = cname.split('_')
if (len(w) != 3 or
w[1] not in ('extra', 'left', 'right', 'center')
or w[2] != ''):
if verbose:
print("Not emacs, not a vm workspace, not flipping %s" % w)
sys.exit(-1)
# Note VM workspaces end with _ to skip from the pager
if assign:
dest_workspace = '_' + assign
elif cname.endswith('_extra_'):
dest_workspace = '_right' if flip else '_left'
elif cname.endswith('_center_'):
dest_workspace = '_left' if flip else '_right'
elif cname.endswith('_left_'):
dest_workspace = '_right' if flip else '_center'
else:
dest_workspace = '_center' if flip else '_left'
dest_workspace = w[0] + dest_workspace + "_"
conn.command('[id="%d"] move window to workspace %s' %
(current_window.window, dest_workspace))
conn.command('[id="%d"] focus' % current_window.window)
sys.exit(0)
if emacs:
candidate = conn.get_tree().find_classed('Emacs')
else:
# Note that if the VM is running a snapshot its name will be
# vmname (snapshotname) [Running]
# so use a regex, this also covers the case when a vm has a
# name that is a subset of another vm's name
if screen == "":
candidate = conn.get_tree().find_named(
'^%s [^[]*\[Running\] - Oracle VM VirtualBox.*$' % output)
else:
screen = " : %s" % screen
candidate = conn.get_tree().find_named(
'^%s [^[]*\[Running\] - Oracle VM VirtualBox%s$' %
(output, screen))
if len(candidate) == 0:
if run:
subprocess.call(['$HOME/bin/vb %s' % output], shell=True)
else:
if verbose:
print("VM %s not found" % output)
sys.exit(-1)
else:
# If we have more than one back & forth does not make much sense
# as it'd always be the same vm workspace on the other screen
if len(candidate) == 2 or len(candidate) == 3:
if verbose:
print("Focus the windows no matter what workspace they are on")
for x in candidate:
conn.command('[id="%d"] focus' % x.window)
elif len(candidate) == 1:
if (candidate[0].workspace().name == current_workspace.name):
if verbose:
print("Already on the correct workspace, ")
print("go to the previous workspace")
print("assuming workspace_back_and_forth is configured")
conn.command('workspace %s' % current_workspace.name)
else:
if verbose:
print("Not on the correct workspace, let's switch")
conn.command('[id="%d"] focus' % candidate[0].window)
else:
print("Too many windows match")
if verbose:
for x in candidate:
print("Name: %s on workspace %s" % (
x.name, x.workspace().name))
sys.exit(-1)
if __name__ == "__main__":
main()
|