103 lines
3.7 KiB
Python
103 lines
3.7 KiB
Python
import enum
|
|
import logging
|
|
import math
|
|
from typing import Optional
|
|
|
|
import i3ipc
|
|
|
|
import move_counter
|
|
|
|
|
|
def get_workspaces(i3: i3ipc.Connection) -> list[i3ipc.Con]:
|
|
return [i3.get_tree().find_by_id(reply.ipc_data["id"]).workspace()
|
|
for reply in i3.get_workspaces()]
|
|
|
|
|
|
def get_focused_workspace(i3: i3ipc.Connection) -> i3ipc.Con:
|
|
for reply in i3.get_workspaces():
|
|
if reply.focused:
|
|
return i3.get_tree().find_by_id(reply.ipc_data["id"]).workspace()
|
|
raise Exception("No workspaces were focused. This should never happen")
|
|
|
|
|
|
def get_focused_window(i3: i3ipc.Connection) -> i3ipc.Con:
|
|
# Should never return None because we start with the focused workspace.
|
|
return get_focused_workspace(i3).find_focused()
|
|
|
|
|
|
def get_window_of_event(i3: i3ipc.Connection, event: i3ipc.Event) -> Optional[i3ipc.Con]:
|
|
return i3.get_tree().find_by_id(event.container.id)
|
|
|
|
|
|
def get_workspace_of_window(window: Optional[i3ipc.Con]) -> Optional[i3ipc.Con]:
|
|
return window.workspace() if window is not None else None
|
|
|
|
|
|
def get_workspace_of_event(i3: i3ipc.Connection, event: i3ipc.Event) -> Optional[i3ipc.Con]:
|
|
return get_workspace_of_window(get_window_of_event(i3, event))
|
|
|
|
|
|
def refetch_container(i3: i3ipc.Connection, container: i3ipc.Con) -> i3ipc.Con:
|
|
return i3.get_tree().find_by_id(container.id)
|
|
|
|
|
|
def tree_str(container: i3ipc.Con, indent: str = "") -> str:
|
|
out = f"{indent} {container.id} {container.layout}\n"
|
|
for node in container.nodes:
|
|
out += tree_str(node, indent + " ")
|
|
return out
|
|
|
|
|
|
def move_container(con1: i3ipc.Con, con2: i3ipc.Con) -> None:
|
|
move_counter.increment()
|
|
con2.command("mark __swaymonad__mark");
|
|
con1.command("move window to mark __swaymonad__mark")
|
|
con2.command("unmark __swaymonad__mark");
|
|
|
|
|
|
def reverse_nodes(i3: i3ipc.Connection, container: i3ipc.Con, starting_idx: int = 0) -> None:
|
|
logging.debug(f"Reversing nodes in container {container.id} from index {starting_idx} onward.")
|
|
for i, node in enumerate(container.nodes[starting_idx:math.ceil(len(container.nodes)/2)]):
|
|
target_node_id = container.nodes[-i-1].id
|
|
logging.debug(f"Swapping node {i} (container id {node.id}) with "
|
|
f"node {len(container.nodes) - i - 1} (container id {target_node_id})")
|
|
if node.id != target_node_id:
|
|
node.command(f"swap container with con_id {target_node_id}")
|
|
|
|
|
|
def insert_node_at_index(i3: i3ipc.Connection,
|
|
container: i3ipc.Con,
|
|
node: i3ipc.Con,
|
|
index: int) -> None:
|
|
logging.debug(f"Inserting node {node.id} into container {container.id} at index {index}.")
|
|
move_container(node, container)
|
|
# We haven't refreshed our view of container, so it still only contains the
|
|
# old nodes and does not include the new node at the end.
|
|
for old_node in container.nodes[::-1]:
|
|
node.command(f"swap container with con_id {old_node.id}")
|
|
|
|
|
|
def add_node_to_front(i3: i3ipc.Connection, container: i3ipc.Con, node: i3ipc.Con) -> None:
|
|
insert_node_at_index(i3, container, node, 0)
|
|
|
|
|
|
def ensure_split(container: i3ipc.Con, split: str) -> list[i3ipc.replies.CommandReply]:
|
|
if container.layout != split:
|
|
return container.command(split)
|
|
else:
|
|
return []
|
|
|
|
|
|
def is_floating(container: i3ipc.Con) -> bool:
|
|
# TODO: replace first clause with container.is_floating() when the next
|
|
# version of i3ipc-python is released.
|
|
return container.floating in ['user_on', 'auto_on'] or container.type == "floating_con"
|
|
|
|
|
|
# https://docs.python.org/3/library/enum.html#using-automatic-values
|
|
class AutoName(enum.Enum):
|
|
value: str
|
|
# https://github.com/python/mypy/issues/7591
|
|
@staticmethod
|
|
def _generate_next_value_(name: str, start: int, count: int, last_values: list[str]) -> str:
|
|
return name
|