#!/usr/bin/env python3 import sys import os import osd import argparse import i3ipc from woman.shared import * from woman.lib import * import woman.util as util from woman.constants import * def workspace(master, slave): if (master, slave) == GUI_WORKSPACE: return GUI_WORKSPACE_STR if slave > MAX_MASTERED_SLAVE: n = 900 + slave else: n = master * 10 + slave if n in WORKSPACES_RENAMES: return WORKSPACES_RENAMES[n] else: return str(n) def parse_workspace(w_str): if w_str == GUI_WORKSPACE_STR: return GUI_WORKSPACE if w_str in REV_RENAMES: w = REV_RENAMES[w_str] else: try: w = int(w_str) except Exception: return None, None if w >= 1000 or w < 10: return None, None if w // 100 == 9: return (None, w % 100) else: return (w // 10, w % 10) def goto_workspace(n_master, n_slave): master, slave = shared.workspace_on[shared.output] shared.qt_task("screenshot_and_goto", n_master, n_slave, master, slave, otherwise=lambda: shared.i3_cmd(f'workspace {workspace(n_master, n_slave)}')) def move_container(master, slave): shared.i3_cmd(f'move container to workspace {workspace(master, slave)}') shared.qt_task("ws_changed", master, slave) def swap_master(a, b): do_after = [] def proc_one_direction(a, b): for i in shared.master_on: if a == shared.master_on[i]: @do_after.append def _(): shared.master_on[i] = b for i in shared.slave_on_for: x = shared.slave_on_for[i][a] @do_after.append def _(): shared.slave_on_for[i][b] = x x = shared.slave_for[a] @do_after.append def _(): shared.slave_for[b] = x proc_one_direction(a, b) proc_one_direction(b, a) for slave in range(MIN_SLAVE, MAX_MASTERED_SLAVE+1): swap_workspace(a, slave, b, slave) for f in do_after: f() for slave in range(MIN_SLAVE, MAX_MASTERED_SLAVE+1): shared.qt_task("ws_metadata_changed", a, slave) shared.qt_task("ws_metadata_changed", b, slave) def swap_workspace(a_master, a_slave, b_master, b_slave): if (a_master, a_slave) == (b_master, b_slave): return def rename_workspace(a_master, a_slave, b_master, b_slave): a = (a_master, a_slave) b = (b_master, b_slave) shared.i3_cmd(f'rename workspace {workspace(*a)} to {workspace(*b)}') shared.qt_task("ws_rename", a_master, a_slave, b_master, b_slave) for i in shared.workspace_on: if shared.workspace_on[i] == a: shared.workspace_on[i] = b if a_master == b_master and a_master is not None: if shared.slave_for[a_master] == a_slave: shared.slave_for[a_master] = b_slave for i in shared.slave_on_for: if shared.slave_on_for[i][a_master] == a_slave: shared.slave_on_for[i][a_master] = b_slave else: for i in set(shared.slave_on).union(set(shared.master_on)): def set_ws(master, slave): if master is None: shared.slave_on[i] = slave else: shared.master_on[i] = master shared.slave_on_for[i][b_master] = slave if a == (None, shared.slave_on[i]): shared.slave_on[i] = None set_ws(*b) elif a == (shared.master_on[i], shared.slave_on_for[i][a_master]): set_ws(*b) event_workspace_is_on(b, shared.output_of_workspace[a_master][a_slave]) event_workspace_deleted(a) a = (a_master, a_slave) b = (b_master, b_slave) if not shared.workspace_exist(*a): if not shared.workspace_exist(*b): return return rename_workspace(*b, *a) if not shared.workspace_exist(*b): return rename_workspace(*a, *b) shared.i3_cmd(f'rename workspace {workspace(*a)} to tmp_swap') try: shared.i3_cmd(f'rename workspace {workspace(*b)} to {workspace(*a)}') except I3CmdException as e: shared.i3_cmd(f'rename workspace tmp_swap to {workspace(*a)}') raise e shared.i3_cmd(f'rename workspace tmp_swap to {workspace(*b)}') for i in shared.workspace_on: if shared.workspace_on[i] == a: shared.workspace_on[i] = b elif shared.workspace_on[i] == b: shared.workspace_on[i] = a if a_master == b_master and a_master is not None: if shared.slave_for[a_master] == a_slave: shared.slave_for[a_master] = b_slave elif shared.slave_for[a_master] == b_slave: shared.slave_for[a_master] = a_slave for i in shared.slave_on_for: if shared.slave_on_for[i][a_master] == a_slave: shared.slave_on_for[i][a_master] = b_slave elif shared.slave_on_for[i][a_master] == b_slave: shared.slave_on_for[i][a_master] = a_slave else: for i in set(shared.slave_on).union(set(shared.master_on)): def set_ws(master, slave): if master is None: shared.slave_on[i] = slave else: shared.master_on[i] = master shared.slave_on_for[i][b_master] = slave if a == (None, shared.slave_on[i]): shared.slave_on[i] = None set_ws(*b) elif b == (None, shared.slave_on[i]): shared.slave_on[i] = None set_ws(*a) elif a == (shared.master_on[i], shared.slave_on_for[i][a_master]): set_ws(*b) elif b == (shared.master_on[i], shared.slave_on_for[i][b_master]): set_ws(*a) tmp = shared.output_of_workspace[a_master][a_slave] shared.output_of_workspace[a_master][a_slave] = shared.output_of_workspace[b_master][b_slave] shared.output_of_workspace[b_master][b_slave] = tmp shared.qt_task("ws_swap", a_master, a_slave, b_master, b_slave) def load_workspaces(): i3_workspaces = shared.i3.value.get_workspaces() data = {parse_workspace(w.name): w.ipc_data['output'] for w in i3_workspaces} for w, output in data.items(): event_workspace_is_on(w, output) for w in WORKSPACES: if w not in data: event_workspace_deleted(w) shared.outputs = {o.name: o for o in shared.i3.value.get_outputs()} class WorkspaceOutOfRange(RuntimeError): pass def get_workspace(master_cmd=None, slave_cmd=None, workspace_cmd=None, old_master=None, old_slave=None, gui=False): def master_next(): nonlocal n_master nonlocal n_slave if gui: if n_slave is None: if n_master >= MAX_MASTER: raise WorkspaceOutOfRange() else: n_master += 1 else: n_master = MIN_MASTER n_slave = None else: if n_slave is None: if n_master >= MAX_MASTER: n_master = None n_slave = MAX_MASTERED_SLAVE + 1 else: n_master += 1 n_slave = None else: if n_slave >= MAX_SLAVE: raise WorkspaceOutOfRange() else: n_slave += 1 def master_prev(): nonlocal n_master nonlocal n_slave if gui: if n_slave is None: if n_master <= MIN_MASTER: n_slave = MAX_MASTERED_SLAVE + 2 n_master = None else: n_master -= 1 else: raise WorkspaceOutOfRange() else: if n_slave is None: if n_master <= MIN_MASTER: raise WorkspaceOutOfRange() else: n_master -= 1 n_slave = None else: if n_slave <= MAX_MASTERED_SLAVE + 1: nmaster = MAX_MASTER n_slave = None else: n_slave -= 1 def master_set(x): nonlocal n_master nonlocal n_slave n_master = x n_slave = None def master_alloc(): nonlocal n_master nonlocal n_slave for i in range(MIN_MASTER, MAX_MASTER+1): if i not in shared.output_of_workspace: n_master = i n_slave = None return raise WorkspaceOutOfRange() def slave_change(by): nonlocal n_master nonlocal n_slave W = GUI_WORKSPACES_ORDER if gui else WORKSPACES W_REV = GUI_WORKSPACES_ORDER_REV if gui else WORKSPACES_REV try: ind = W_REV[(n_master if n_slave <= MAX_MASTERED_SLAVE else None, n_slave)] + by if ind < 0: raise WorkspaceOutOfRange() x_master, n_slave = W[ind] if x_master is not None: n_master = x_master except IndexError: raise WorkspaceOutOfRange() def slave_change_limit(by): nonlocal n_slave x = n_slave + by if x < MIN_SLAVE or x > MAX_SLAVE or (x <= MAX_MASTERED_SLAVE) != (n_slave <= MAX_MASTERED_SLAVE): raise WorkspaceOutOfRange() n_slave = x def slave_set(x): nonlocal n_master nonlocal n_slave n_slave = x def slave_alloc(): nonlocal n_master nonlocal n_slave if n_master not in shared.output_of_workspace: n_slave = 1 return for i in MASTERED_SLAVE_LIST: if i not in shared.output_of_workspace[n_master]: n_slave = i return raise WorkspaceOutOfRange() def rep_until_find(f, is_master=False): nonlocal n_master nonlocal n_slave x_master = None def go(): nonlocal n_master nonlocal n_slave nonlocal x_master f() x_master = n_master if n_slave is None or n_slave <= MAX_MASTERED_SLAVE else None go() if is_master: if gui: while not x_master in shared.output_of_workspace: go() else: while not (x_master in shared.output_of_workspace and (x_master is not None or n_slave in shared.output_of_workspace[x_master])): go() else: while not (x_master in shared.output_of_workspace and n_slave in shared.output_of_workspace[x_master]): go() def proc_params(par, set_f, prev_f, next_f, alloc_f, prev_limit_f=None, next_limit_f=None, is_master=False): nonlocal n_master nonlocal n_slave if par is None: return if par == 'next-skip': rep_until_find(next_f, is_master) elif par == "next": next_f() elif par == 'prev-skip': rep_until_find(prev_f, is_master) elif par == "alloc": alloc_f() elif par == "prev": prev_f() elif par == 'next-limit-skip': rep_until_find(next_limit_f, is_master) elif par == "next-limit": next_limit_f() elif par == 'prev-limit-skip': rep_until_find(prev_limit_f, is_master) elif par == "prev-limit": prev_limit_f() else: set_f(int(par)) n_master = shared.master_on[shared.output] if old_master is None else old_master if old_slave is None: n_slave = shared.slave_on[shared.output] else: n_slave = None if old_slave <= MAX_MASTERED_SLAVE else old_slave proc_params(master_cmd, master_set, master_prev, master_next, master_alloc, is_master=True) if n_slave is None: if n_master is not None and n_master == old_master: if old_slave <= MAX_MASTERED_SLAVE: n_slave = old_slave if n_slave is None: n_slave = shared.slave_on_for[shared.output][n_master] if n_slave is None: n_slave = shared.slave_for[n_master] if n_slave is None: for i in MASTERED_SLAVE_LIST: if n_master in shared.output_of_workspace and i in shared.output_of_workspace[n_master]: if shared.output_of_workspace[n_master][i] == shared.output: n_slave = i break if n_slave is None: for i in MASTERED_SLAVE_LIST: if n_master in shared.output_of_workspace and i in shared.output_of_workspace[n_master]: n_slave = i break if n_slave is None: n_slave = MASTERED_SLAVE_LIST[0] proc_params(slave_cmd, slave_set, lambda: slave_change(-1), lambda: slave_change(1), slave_alloc, lambda: slave_change_limit(-1), lambda: slave_change_limit(1), ) if workspace_cmd is not None: n_master, n_slave = parse_workspace(workspace_cmd) if n_slave is None: raise RuntimeError("Workspace not parsed") return n_master, n_slave from woman.watch import event_workspace_is_on, event_workspace_deleted