initial workspace tree code ported from sway-context-manager
This commit is contained in:
parent
46903dd09c
commit
c2504c9cf9
5
VoidShell/entrypoints/app.py
Normal file
5
VoidShell/entrypoints/app.py
Normal file
@ -0,0 +1,5 @@
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import GLib, Gtk
|
||||
|
||||
|
||||
118
VoidShell/services/sway/sway.py
Normal file
118
VoidShell/services/sway/sway.py
Normal file
@ -0,0 +1,118 @@
|
||||
from gi.repository import Gtk, Gdk, GLib, GObject
|
||||
from i3ipc.aio import Connection
|
||||
from i3ipc.events import WorkspaceEvent, OutputEvent, ModeEvent
|
||||
from i3ipc import Event
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
|
||||
class GWorkspaceEvent(GObject.Object):
|
||||
|
||||
def __init__(self, event: WorkspaceEvent):
|
||||
super().__init__()
|
||||
self.event = event
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.event, name)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.event)
|
||||
|
||||
class GOutputEvent(GObject.Object):
|
||||
|
||||
def __init__(self, event: OutputEvent):
|
||||
super().__init__()
|
||||
self.event = event
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.event, name)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.event)
|
||||
|
||||
class GModeEvent(GObject.Object):
|
||||
|
||||
def __init__(self, event: ModeEvent):
|
||||
super().__init__()
|
||||
self.event = event
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.event, name)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.event)
|
||||
|
||||
def async_debounce(wait):
|
||||
def decorator(func):
|
||||
task: asyncio.Task | None = None
|
||||
|
||||
@wraps(func)
|
||||
async def debounced(*args, **kwargs):
|
||||
nonlocal task
|
||||
if task and not task.done():
|
||||
return
|
||||
|
||||
async def call_func():
|
||||
await asyncio.sleep(wait)
|
||||
await func(*args, **kwargs)
|
||||
|
||||
task = asyncio.create_task(call_func())
|
||||
return task
|
||||
|
||||
return debounced
|
||||
|
||||
return decorator
|
||||
|
||||
class SwayIPC(GObject.Object, Connection):
|
||||
|
||||
__instance: Optional["SwayIPC"] = None
|
||||
|
||||
def __init__(self):
|
||||
if SwayIPC.__instance is not None:
|
||||
raise Exception("SwayIPC is a singleton")
|
||||
super().__init__()
|
||||
super(Connection, self).__init__(auto_reconnect=True)
|
||||
SwayIPC.__instance = self
|
||||
# Run async initialization
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.create_task(self._init())
|
||||
|
||||
@staticmethod
|
||||
def get_instance():
|
||||
if SwayIPC.__instance is None:
|
||||
SwayIPC()
|
||||
return SwayIPC.__instance
|
||||
|
||||
async def _init(self):
|
||||
await self.connect()
|
||||
self.on(Event.WORKSPACE, self._on_workspace)
|
||||
self.on(Event.OUTPUT, self._on_output)
|
||||
self.on(Event.MODE, self._on_mode)
|
||||
self.on(Event.SHUTDOWN, self._on_shutdown)
|
||||
|
||||
async def _on_workspace(self, event):
|
||||
self.emit('workspace', GWorkspaceEvent(event))
|
||||
|
||||
@async_debounce(0.1)
|
||||
async def _on_output(self, event):
|
||||
self.emit('output', GOutputEvent(event))
|
||||
|
||||
async def _on_mode(self, event):
|
||||
self.emit('mode', GModeEvent(event))
|
||||
|
||||
async def _on_shutdown(self, event):
|
||||
# exit the application
|
||||
Gtk.main_quit()
|
||||
|
||||
@GObject.Signal(arg_types=(GWorkspaceEvent,))
|
||||
def workspace(self, event: GWorkspaceEvent):
|
||||
pass
|
||||
|
||||
@GObject.Signal(arg_types=(GOutputEvent,))
|
||||
def output(self, event: GOutputEvent):
|
||||
pass
|
||||
|
||||
@GObject.Signal(arg_types=(GModeEvent,))
|
||||
def mode(self, event: GModeEvent):
|
||||
pass
|
||||
|
||||
|
||||
539
VoidShell/services/sway/tree.py
Normal file
539
VoidShell/services/sway/tree.py
Normal file
@ -0,0 +1,539 @@
|
||||
from typing import Any
|
||||
import json
|
||||
from i3ipc.replies import OutputReply, WorkspaceReply
|
||||
from i3ipc.aio import Connection
|
||||
import asyncio
|
||||
import subprocess
|
||||
from .utils import OutputMatch
|
||||
from .sway import SwayIPC
|
||||
from gi.repository import GObject
|
||||
|
||||
|
||||
class Workspace(GObject.Object):
|
||||
"""A class representing a Sway workspace in the format we use.
|
||||
|
||||
Attributes:
|
||||
index: The index of the workspace, as a string. This is the Sway workspace name, and is never displayed to the user.
|
||||
name: The name of the workspace, as a string. This is the name that is displayed to the user, as defined in `workspaces.json`.
|
||||
"""
|
||||
|
||||
_index: int = None
|
||||
_name: str = None
|
||||
|
||||
_active: bool = False
|
||||
_visible: bool = False
|
||||
_focused: bool = False
|
||||
_alerted: bool = False
|
||||
|
||||
@GObject.Property
|
||||
def index(self) -> int:
|
||||
return self._index
|
||||
|
||||
@GObject.Property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
@GObject.Property
|
||||
def active(self) -> bool:
|
||||
return self._active
|
||||
|
||||
@GObject.Property
|
||||
def visible(self) -> bool:
|
||||
return self._visible
|
||||
|
||||
@GObject.Property
|
||||
def focused(self) -> bool:
|
||||
return self._focused
|
||||
|
||||
@GObject.Property
|
||||
def alerted(self) -> bool:
|
||||
return self._alerted
|
||||
|
||||
def __init__(self, dictionary: dict[str, str | int | dict[str, str]]):
|
||||
super().__init__()
|
||||
self.index = dictionary.get("index")
|
||||
self.name = dictionary.get("name")
|
||||
|
||||
self.definition = dictionary
|
||||
|
||||
def update_state(self, state_update: Any):
|
||||
self._active = True
|
||||
self._visible = state_update.visible
|
||||
self._focused = state_update.focused
|
||||
self._alerted = state_update.urgent
|
||||
|
||||
self.notify("active")
|
||||
self.notify("visible")
|
||||
self.notify("focused")
|
||||
self.notify("alerted")
|
||||
|
||||
def deactivate(self):
|
||||
self.active = False
|
||||
self.visible = False
|
||||
self.focused = False
|
||||
self.alerted = False
|
||||
|
||||
self.notify("active")
|
||||
self.notify("visible")
|
||||
self.notify("focused")
|
||||
self.notify("alerted")
|
||||
|
||||
def __repr__(self):
|
||||
return f"Workspace({self.index}, '{self.name}')"
|
||||
|
||||
async def focus(self):
|
||||
"""Focus the workspace in Sway."""
|
||||
sway = SwayIPC.get_instance()
|
||||
await sway.command(f"workspace {self.index}")
|
||||
|
||||
async def move_container(self):
|
||||
"""Move the focused container to the workspace."""
|
||||
sway = SwayIPC.get_instance()
|
||||
await sway.command(f"move container to workspace {self.index}")
|
||||
|
||||
async def reassign(self, output: str):
|
||||
"""Reassign the workspace to a different output."""
|
||||
sway = SwayIPC.get_instance()
|
||||
await sway.command(f"workspace {self.index} output {output}")
|
||||
|
||||
async def relocate(self, output: str):
|
||||
"""Move the workspace to a different output."""
|
||||
sway = SwayIPC.get_instance()
|
||||
await sway.command(f"workspace {self.index}")
|
||||
await sway.command(f"move workspace to output {output}")
|
||||
|
||||
|
||||
class WorkspaceGroup(GObject.Object):
|
||||
"""A class representing a group of workspaces. Generally maps to a monitor."""
|
||||
|
||||
name: str = None
|
||||
workspaces: list[Workspace] = None
|
||||
reverse: bool = False
|
||||
|
||||
def __init__(self, output_data: dict[str, str]):
|
||||
super().__init__()
|
||||
self.name = output_data["group"]
|
||||
self.output_names = output_data.get("names", [])
|
||||
self.make = output_data.get("make", None)
|
||||
self.model = output_data.get("model", None)
|
||||
self.serial = output_data.get("serial", None)
|
||||
self.position = output_data.get("position", [0, 0])
|
||||
self.mode = output_data.get("mode", None)
|
||||
self.transform = output_data.get("transform", None)
|
||||
self.eww_windows = output_data.get("eww_windows", [])
|
||||
self.workspaces = []
|
||||
|
||||
def add_workspace(self, workspace: Workspace):
|
||||
"""Add a workspace to the group."""
|
||||
self.workspaces.append(workspace)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.workspaces)
|
||||
|
||||
def __repr__(self):
|
||||
return f"WorkspaceGroup({self.name}, {repr(self.workspaces)})"
|
||||
|
||||
@property
|
||||
def active_workspace(self) -> Workspace:
|
||||
"""Returns the active workspace in the group."""
|
||||
return next(
|
||||
(workspace for workspace in self.workspaces if workspace.visible),
|
||||
None,
|
||||
)
|
||||
|
||||
@property
|
||||
def active(self) -> bool:
|
||||
"""Returns whether the group is active."""
|
||||
return any(workspace.focused for workspace in self.workspaces)
|
||||
|
||||
async def focus(self):
|
||||
"""Focus the group in Sway."""
|
||||
sway = SwayIPC.get_instance()
|
||||
if self.make and self.model and self.serial:
|
||||
await sway.command(f"focus output {self.make} {self.model} {self.serial}")
|
||||
elif len(self.output_names) > 0:
|
||||
for name in self.output_names:
|
||||
await sway.command(f"focus output {name}")
|
||||
else:
|
||||
raise ValueError(
|
||||
"No output name or make/model/serial provided, cannot focus group"
|
||||
)
|
||||
|
||||
async def configure(self, outputs: list[OutputReply]):
|
||||
"""Configure the group output in Sway."""
|
||||
transform = ""
|
||||
mode = ""
|
||||
selector = ""
|
||||
if self.transform:
|
||||
transform = f"transform {self.transform}"
|
||||
if self.mode:
|
||||
mode = f"mode {self.mode}"
|
||||
if self.make and self.model and self.serial:
|
||||
selector = f'"{self.make} {self.model} {self.serial}"'
|
||||
elif len(self.output_names) > 0:
|
||||
for name in self.output_names:
|
||||
if name in [output.name for output in outputs]:
|
||||
selector = name
|
||||
break
|
||||
# Configure the output
|
||||
await i3.command(
|
||||
f"output {selector} position {self.position[0]} {self.position[1]} {mode} {transform} enable"
|
||||
)
|
||||
|
||||
async def validate(self, i3: Connection, workspaces: list[WorkspaceReply]):
|
||||
"""Validate that each workspace in the group is assigned to and present on the correct output."""
|
||||
ouput_name = self.get_output_name(i3)
|
||||
|
||||
for workspace in self.workspaces:
|
||||
# Get output name for workspace
|
||||
workspace_output = next(
|
||||
(
|
||||
workspace_output
|
||||
for workspace_output in workspaces
|
||||
if workspace_output.name == workspace.index
|
||||
),
|
||||
None,
|
||||
)
|
||||
if workspace_output is None:
|
||||
print(
|
||||
f"Workspace {workspace.index} not found in workspaces, skipping validation",
|
||||
flush=True,
|
||||
)
|
||||
continue
|
||||
if workspace_output.output != ouput_name:
|
||||
print(
|
||||
f"Workspace {workspace.index} is assigned to {workspace_output.output}, not {ouput_name}, reassigning",
|
||||
flush=True,
|
||||
)
|
||||
await workspace.relocate(i3, ouput_name)
|
||||
|
||||
async def get_output_name(self, i3: Connection) -> str:
|
||||
"""Get the name of the output in Sway."""
|
||||
outputs = await i3.get_outputs()
|
||||
# If we have make, model, and serial, search by those first
|
||||
if self.make and self.model and self.serial:
|
||||
for output in outputs:
|
||||
if (
|
||||
output.make == self.make
|
||||
and output.model == self.model
|
||||
and output.serial == self.serial
|
||||
):
|
||||
print(
|
||||
f"Found output {output.name} by make, model, and serial for group {self.name}",
|
||||
flush=True,
|
||||
)
|
||||
return output.name
|
||||
# If we don't find an exact match for the output, search by name if we have any
|
||||
if len(self.output_names) > 0:
|
||||
for output in outputs:
|
||||
if output.name in self.output_names:
|
||||
print(
|
||||
f"Found output {output.name} by name for group {self.name}",
|
||||
flush=True,
|
||||
)
|
||||
return output.name
|
||||
return None
|
||||
|
||||
def get_match_level(self, output: OutputReply) -> OutputMatch:
|
||||
"""Get the match level score for the output."""
|
||||
if self.make and self.model and self.serial:
|
||||
if (
|
||||
output.make == self.make
|
||||
and output.model == self.model
|
||||
and output.serial == self.serial
|
||||
):
|
||||
print(
|
||||
f"Match level: ID_MATCH for {output.name} on group {self.name}",
|
||||
flush=True,
|
||||
)
|
||||
return OutputMatch.ID_MATCH
|
||||
if output.name in self.output_names:
|
||||
print(
|
||||
f"Match level: NAME_MATCH for {output.name} on group {self.name}",
|
||||
flush=True,
|
||||
)
|
||||
return OutputMatch.NAME_MATCH
|
||||
print(
|
||||
f"Match level: NO_MATCH for {output.name} on group {self.name}", flush=True
|
||||
)
|
||||
return OutputMatch.NO_MATCH
|
||||
|
||||
|
||||
class WorkspaceContext:
|
||||
"""A class representing a context, containing all workspaces and groups within the context."""
|
||||
|
||||
name: str = None
|
||||
groups: list[WorkspaceGroup] = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
data: dict[str, list[dict[str, list[int]]] | list[dict[str, str]]],
|
||||
workspaces: list[Workspace],
|
||||
):
|
||||
self.groups = [WorkspaceGroup(output) for output in data["outputs"]]
|
||||
self.name = name
|
||||
for group in self.groups:
|
||||
group_object = data["groups"][group.name]
|
||||
if group_object.get("reverse", False):
|
||||
group.reverse = True
|
||||
for workspace in group_object["workspaces"]:
|
||||
workspace_obj = next(
|
||||
(w for w in workspaces if w.index == workspace),
|
||||
None,
|
||||
)
|
||||
if workspace_obj:
|
||||
group.add_workspace(workspace_obj)
|
||||
else:
|
||||
raise Exception(
|
||||
f"Error: undefined workspace {workspace} referenced in context {name} group {group.name}"
|
||||
)
|
||||
primary_group_name = data.get("primary", self.groups[0].name)
|
||||
self.primary_group = next(
|
||||
(group for group in self.groups if group.name == primary_group_name),
|
||||
None,
|
||||
)
|
||||
|
||||
def add_group(self, group: WorkspaceGroup):
|
||||
"""Add a group to the context."""
|
||||
self.groups.append(group)
|
||||
|
||||
def compatability_rank(self, outputs: list[OutputReply]) -> int:
|
||||
"""Get the compatability rank of the context with the given outputs."""
|
||||
|
||||
result = 0
|
||||
for group in self.groups:
|
||||
group_result = max(
|
||||
[group.get_match_level(output).value for output in outputs]
|
||||
)
|
||||
if group_result == 0:
|
||||
return 0
|
||||
result += group_result
|
||||
return result
|
||||
|
||||
async def deactivate(self, i3: Connection):
|
||||
"""Deactivate the context in Sway."""
|
||||
# First, close all EWW windows
|
||||
proc = await asyncio.create_subprocess_exec("eww", "close-all")
|
||||
await proc.wait()
|
||||
|
||||
# Then, disable all displays
|
||||
await i3.command("output * disable")
|
||||
|
||||
async def activate(self, i3: Connection):
|
||||
"""Activate the context in Sway."""
|
||||
defined_displays = [
|
||||
f"{group.make} {group.model} {group.serial}" for group in self.groups
|
||||
]
|
||||
outputs = await i3.get_outputs()
|
||||
|
||||
# Configure all displays defined in the context
|
||||
for group in self.groups:
|
||||
await group.configure(i3, outputs)
|
||||
|
||||
# Then, open all EWW windows defined in the context on the appropriate windows
|
||||
proc = await asyncio.create_subprocess_exec("eww", "reload")
|
||||
await proc.wait()
|
||||
for group in self.groups:
|
||||
for window in group.eww_windows:
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
"eww",
|
||||
"open",
|
||||
window,
|
||||
"--screen",
|
||||
await group.get_output_name(i3),
|
||||
)
|
||||
await proc.wait()
|
||||
|
||||
# Finally, validate workspaces and move them if necessary
|
||||
for group in self.groups:
|
||||
workspaces = await i3.get_workspaces()
|
||||
await group.validate(i3, workspaces)
|
||||
|
||||
@property
|
||||
def active_group(self) -> WorkspaceGroup:
|
||||
"""Returns the active group in the context."""
|
||||
return next(
|
||||
(group for group in self.groups if group.active),
|
||||
None,
|
||||
)
|
||||
|
||||
@property
|
||||
def active_workspace(self) -> Workspace:
|
||||
"""Returns the active workspace in the context."""
|
||||
try:
|
||||
return self.active_group.active_workspace
|
||||
except AttributeError:
|
||||
# A context may not have any active workspaces, in which case we return None
|
||||
return None
|
||||
|
||||
@property
|
||||
def visible_workspaces(self) -> dict[str, Workspace]:
|
||||
"""Returns a dictionary of all visible workspaces in the context."""
|
||||
return {group.name: group.active_workspace for group in self.groups}
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.groups)
|
||||
|
||||
def __repr__(self):
|
||||
return f"WorkspaceContext({self.name}, {repr(self.groups)})"
|
||||
|
||||
def __json__(self):
|
||||
return {group.name: group.__json__() for group in self.groups}
|
||||
|
||||
|
||||
class WorkspaceTree:
|
||||
|
||||
current_context: WorkspaceContext = None
|
||||
workspaces: list[Workspace] = None
|
||||
|
||||
def __init__(self, filename: str):
|
||||
self.load_file(filename)
|
||||
|
||||
def load_file(self, filename: str):
|
||||
with open(filename, "r") as file:
|
||||
initial: dict = json.load(file)
|
||||
|
||||
self.contexts: list[WorkspaceContext] = []
|
||||
self.workspaces = [Workspace(workspace) for workspace in initial["workspaces"]]
|
||||
|
||||
for context, context_dict in initial["contexts"].items():
|
||||
context_obj = WorkspaceContext(context, context_dict, self.workspaces)
|
||||
self.contexts.append(context_obj)
|
||||
# Don't override the current context if it's already set
|
||||
if (
|
||||
initial.get("default_context", "default") == context
|
||||
and self.current_context is None
|
||||
):
|
||||
self.current_context = context_obj
|
||||
|
||||
def __dict__(self):
|
||||
return {
|
||||
"current_context": self.current_context,
|
||||
"contexts": self.contexts,
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f"WorkspaceTree({self.current_context}, {repr(self.contexts)})"
|
||||
|
||||
def __json__(self):
|
||||
return {self.current_context.name: self.current_context.__json__()}
|
||||
|
||||
def get_workspace(self, user_index: int) -> tuple[Workspace, WorkspaceGroup] | None:
|
||||
"""Returns a workspace object based on the user index."""
|
||||
if user_index < 1:
|
||||
raise IndexError("Workspace index must be greater than or equal to 1.")
|
||||
# First, find the active workspace
|
||||
active_workspace = self.current_context.active_workspace
|
||||
# Next, get its index within its group, and the active group index
|
||||
active_index = (
|
||||
self.current_context.active_group.workspaces.index(active_workspace) + 1
|
||||
)
|
||||
group_index = self.current_context.groups.index(
|
||||
self.current_context.active_group
|
||||
)
|
||||
|
||||
# If the user index is the same as the active index, or is outside the range for this group, start searching other groups
|
||||
if user_index == active_index or user_index > len(
|
||||
self.current_context.active_group.workspaces
|
||||
):
|
||||
counter = 0
|
||||
while counter < len(self.current_context.groups):
|
||||
group = self.current_context.groups[
|
||||
(group_index + counter) % len(self.current_context.groups)
|
||||
]
|
||||
if (
|
||||
user_index > len(group.workspaces)
|
||||
or group == self.current_context.active_group
|
||||
):
|
||||
# Group doesn't have enough workspaces, or is the active group
|
||||
counter += 1
|
||||
|
||||
continue
|
||||
return group.workspaces[user_index - 1], group
|
||||
# If we didn't find a workspace, return None
|
||||
return None
|
||||
else:
|
||||
# If the user index is different from the active index, return the workspace in the active group
|
||||
return (
|
||||
self.current_context.active_group.workspaces[user_index - 1],
|
||||
self.current_context.active_group,
|
||||
)
|
||||
|
||||
def update_workspaces(self, ws_sway_dict: dict):
|
||||
"""Updates the workspaces in the tree based on the return from sway IPC."""
|
||||
touched = set()
|
||||
|
||||
for index, ws_data in ws_sway_dict.items():
|
||||
# Udpate data for all active workspaces
|
||||
ws_state: Workspace = next(
|
||||
(ws for ws in self.workspaces if str(ws.index) == index),
|
||||
None,
|
||||
)
|
||||
if ws_state:
|
||||
touched.add(ws_state)
|
||||
ws_state.update_state(ws_data)
|
||||
else:
|
||||
print(f"Warning: workspace {index} not found in tree.")
|
||||
|
||||
# Deactivate any workspaces that weren't touched
|
||||
for ws in self.workspaces:
|
||||
if ws not in touched:
|
||||
ws.deactivate()
|
||||
|
||||
async def update_context(self, i3: Connection, match_context_on_name: bool = False):
|
||||
"""Activates a new context in Sway based on the current display configuration."""
|
||||
# First, get the current display configuration
|
||||
outputs = await i3.get_outputs()
|
||||
print(outputs, flush=True)
|
||||
|
||||
# Next, calculate match scores for each context
|
||||
scores = [
|
||||
(context, context.compatability_rank(outputs)) for context in self.contexts
|
||||
]
|
||||
print([f"{context.name}: {score}" for context, score in scores], flush=True)
|
||||
# Sort the scores by rank
|
||||
scores.sort(key=lambda x: x[1], reverse=True)
|
||||
|
||||
# If the top context is the current context, or the rank is 0, do nothing
|
||||
if scores[0][1] == 0:
|
||||
print(
|
||||
"No context is compatible with the current display configuration, doing nothing.",
|
||||
flush=True,
|
||||
)
|
||||
return
|
||||
if self.current_context is None:
|
||||
await scores[0][0].activate(i3)
|
||||
self.current_context = scores[0][0]
|
||||
return
|
||||
elif scores[0][0] == self.current_context:
|
||||
print("Context is already active.", flush=True)
|
||||
elif match_context_on_name and scores[0][0].name == self.current_context.name:
|
||||
print("Context is already active.", flush=True)
|
||||
await scores[0][0].activate(i3)
|
||||
self.current_context = scores[0][0]
|
||||
else:
|
||||
await self.current_context.deactivate(i3)
|
||||
await scores[0][0].activate(i3)
|
||||
self.current_context = scores[0][0]
|
||||
|
||||
async def activate_context(self, i3: Connection, name: str):
|
||||
"""Activates a context by name. This will fail if the current display configuration is incompatible."""
|
||||
context = next(
|
||||
(context for context in self.contexts if context.name == name),
|
||||
None,
|
||||
)
|
||||
|
||||
outputs = await i3.get_outputs()
|
||||
|
||||
score = context.compatability_rank(outputs)
|
||||
if score == 0:
|
||||
raise ValueError(
|
||||
"Context is incompatible with current display configuration."
|
||||
)
|
||||
print(f"Activating context {context.name} with score {score}.", flush=True)
|
||||
if self.current_context is not None and self.current_context != context:
|
||||
await self.current_context.deactivate(i3)
|
||||
self.current_context = context
|
||||
|
||||
await context.activate(i3)
|
||||
541
poetry.lock
generated
Normal file
541
poetry.lock
generated
Normal file
@ -0,0 +1,541 @@
|
||||
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "asttokens"
|
||||
version = "2.4.1"
|
||||
description = "Annotate AST trees with source code positions"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"},
|
||||
{file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
six = ">=1.12.0"
|
||||
|
||||
[package.extras]
|
||||
astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"]
|
||||
test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "asyncinotify"
|
||||
version = "4.2.0"
|
||||
description = "A simple optionally-async python inotify library, focused on simplicity of use and operation, and leveraging modern Python features"
|
||||
optional = false
|
||||
python-versions = "<4,>=3.6"
|
||||
files = [
|
||||
{file = "asyncinotify-4.2.0-py3-none-any.whl", hash = "sha256:23cbcb0704cc65a2009d5ddc5a70dc5be6560708d8a684bba82e03e384c6295f"},
|
||||
{file = "asyncinotify-4.2.0.tar.gz", hash = "sha256:dac1d75e16a4919c6eab84a90ff51218db622c5524a84a5c501a0b62ea7ec7ea"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.10.0"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"},
|
||||
{file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"},
|
||||
{file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"},
|
||||
{file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"},
|
||||
{file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"},
|
||||
{file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"},
|
||||
{file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"},
|
||||
{file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"},
|
||||
{file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"},
|
||||
{file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"},
|
||||
{file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"},
|
||||
{file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"},
|
||||
{file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"},
|
||||
{file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"},
|
||||
{file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"},
|
||||
{file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"},
|
||||
{file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"},
|
||||
{file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"},
|
||||
{file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"},
|
||||
{file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"},
|
||||
{file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"},
|
||||
{file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
packaging = ">=22.0"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.10)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.7"
|
||||
description = "Composable command line interface toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
|
||||
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "5.1.1"
|
||||
description = "Decorators for Humans"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
|
||||
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "executing"
|
||||
version = "2.1.0"
|
||||
description = "Get the currently executing AST node of a frame, and other information"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"},
|
||||
{file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
|
||||
|
||||
[[package]]
|
||||
name = "i3ipc"
|
||||
version = "2.2.1"
|
||||
description = "An improved Python library to control i3wm and sway"
|
||||
optional = false
|
||||
python-versions = ">=3.4.0"
|
||||
files = [
|
||||
{file = "i3ipc-2.2.1-py3-none-any.whl", hash = "sha256:c0b898223d50d42c90c818deb5033d1304c582755547dee7d15df3e3781bc690"},
|
||||
{file = "i3ipc-2.2.1.tar.gz", hash = "sha256:e880d7d7147959ead5cb34764f08b97b41385b36eb8256e8af1ce163dbcccce8"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
python-xlib = "*"
|
||||
|
||||
[[package]]
|
||||
name = "ipython"
|
||||
version = "8.29.0"
|
||||
description = "IPython: Productive Interactive Computing"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "ipython-8.29.0-py3-none-any.whl", hash = "sha256:0188a1bd83267192123ccea7f4a8ed0a78910535dbaa3f37671dca76ebd429c8"},
|
||||
{file = "ipython-8.29.0.tar.gz", hash = "sha256:40b60e15b22591450eef73e40a027cf77bd652e757523eebc5bd7c7c498290eb"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
decorator = "*"
|
||||
jedi = ">=0.16"
|
||||
matplotlib-inline = "*"
|
||||
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
|
||||
prompt-toolkit = ">=3.0.41,<3.1.0"
|
||||
pygments = ">=2.4.0"
|
||||
stack-data = "*"
|
||||
traitlets = ">=5.13.0"
|
||||
|
||||
[package.extras]
|
||||
all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
|
||||
black = ["black"]
|
||||
doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"]
|
||||
kernel = ["ipykernel"]
|
||||
matplotlib = ["matplotlib"]
|
||||
nbconvert = ["nbconvert"]
|
||||
nbformat = ["nbformat"]
|
||||
notebook = ["ipywidgets", "notebook"]
|
||||
parallel = ["ipyparallel"]
|
||||
qtconsole = ["qtconsole"]
|
||||
test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"]
|
||||
test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
|
||||
|
||||
[[package]]
|
||||
name = "jedi"
|
||||
version = "0.19.2"
|
||||
description = "An autocompletion tool for Python that can be used for text editors."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
|
||||
{file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
parso = ">=0.8.4,<0.9.0"
|
||||
|
||||
[package.extras]
|
||||
docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
|
||||
qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
|
||||
testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "matplotlib-inline"
|
||||
version = "0.1.7"
|
||||
description = "Inline Matplotlib backend for Jupyter"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
|
||||
{file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
traitlets = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "netifaces"
|
||||
version = "0.11.0"
|
||||
description = "Portable network interface information."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "netifaces-0.11.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eb4813b77d5df99903af4757ce980a98c4d702bbcb81f32a0b305a1537bdf0b1"},
|
||||
{file = "netifaces-0.11.0-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5f9ca13babe4d845e400921973f6165a4c2f9f3379c7abfc7478160e25d196a4"},
|
||||
{file = "netifaces-0.11.0-cp27-cp27m-win32.whl", hash = "sha256:7dbb71ea26d304e78ccccf6faccef71bb27ea35e259fb883cfd7fd7b4f17ecb1"},
|
||||
{file = "netifaces-0.11.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0f6133ac02521270d9f7c490f0c8c60638ff4aec8338efeff10a1b51506abe85"},
|
||||
{file = "netifaces-0.11.0-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:08e3f102a59f9eaef70948340aeb6c89bd09734e0dca0f3b82720305729f63ea"},
|
||||
{file = "netifaces-0.11.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c03fb2d4ef4e393f2e6ffc6376410a22a3544f164b336b3a355226653e5efd89"},
|
||||
{file = "netifaces-0.11.0-cp34-cp34m-win32.whl", hash = "sha256:73ff21559675150d31deea8f1f8d7e9a9a7e4688732a94d71327082f517fc6b4"},
|
||||
{file = "netifaces-0.11.0-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815eafdf8b8f2e61370afc6add6194bd5a7252ae44c667e96c4c1ecf418811e4"},
|
||||
{file = "netifaces-0.11.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:50721858c935a76b83dd0dd1ab472cad0a3ef540a1408057624604002fcfb45b"},
|
||||
{file = "netifaces-0.11.0-cp35-cp35m-win32.whl", hash = "sha256:c9a3a47cd3aaeb71e93e681d9816c56406ed755b9442e981b07e3618fb71d2ac"},
|
||||
{file = "netifaces-0.11.0-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:aab1dbfdc55086c789f0eb37affccf47b895b98d490738b81f3b2360100426be"},
|
||||
{file = "netifaces-0.11.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c37a1ca83825bc6f54dddf5277e9c65dec2f1b4d0ba44b8fd42bc30c91aa6ea1"},
|
||||
{file = "netifaces-0.11.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:28f4bf3a1361ab3ed93c5ef360c8b7d4a4ae060176a3529e72e5e4ffc4afd8b0"},
|
||||
{file = "netifaces-0.11.0-cp36-cp36m-win32.whl", hash = "sha256:2650beee182fed66617e18474b943e72e52f10a24dc8cac1db36c41ee9c041b7"},
|
||||
{file = "netifaces-0.11.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cb925e1ca024d6f9b4f9b01d83215fd00fe69d095d0255ff3f64bffda74025c8"},
|
||||
{file = "netifaces-0.11.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:84e4d2e6973eccc52778735befc01638498781ce0e39aa2044ccfd2385c03246"},
|
||||
{file = "netifaces-0.11.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18917fbbdcb2d4f897153c5ddbb56b31fa6dd7c3fa9608b7e3c3a663df8206b5"},
|
||||
{file = "netifaces-0.11.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:48324183af7f1bc44f5f197f3dad54a809ad1ef0c78baee2c88f16a5de02c4c9"},
|
||||
{file = "netifaces-0.11.0-cp37-cp37m-win32.whl", hash = "sha256:8f7da24eab0d4184715d96208b38d373fd15c37b0dafb74756c638bd619ba150"},
|
||||
{file = "netifaces-0.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2479bb4bb50968089a7c045f24d120f37026d7e802ec134c4490eae994c729b5"},
|
||||
{file = "netifaces-0.11.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3ecb3f37c31d5d51d2a4d935cfa81c9bc956687c6f5237021b36d6fdc2815b2c"},
|
||||
{file = "netifaces-0.11.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:96c0fe9696398253f93482c84814f0e7290eee0bfec11563bd07d80d701280c3"},
|
||||
{file = "netifaces-0.11.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c92ff9ac7c2282009fe0dcb67ee3cd17978cffbe0c8f4b471c00fe4325c9b4d4"},
|
||||
{file = "netifaces-0.11.0-cp38-cp38-win32.whl", hash = "sha256:d07b01c51b0b6ceb0f09fc48ec58debd99d2c8430b09e56651addeaf5de48048"},
|
||||
{file = "netifaces-0.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:469fc61034f3daf095e02f9f1bbac07927b826c76b745207287bc594884cfd05"},
|
||||
{file = "netifaces-0.11.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5be83986100ed1fdfa78f11ccff9e4757297735ac17391b95e17e74335c2047d"},
|
||||
{file = "netifaces-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54ff6624eb95b8a07e79aa8817288659af174e954cca24cdb0daeeddfc03c4ff"},
|
||||
{file = "netifaces-0.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:841aa21110a20dc1621e3dd9f922c64ca64dd1eb213c47267a2c324d823f6c8f"},
|
||||
{file = "netifaces-0.11.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e76c7f351e0444721e85f975ae92718e21c1f361bda946d60a214061de1f00a1"},
|
||||
{file = "netifaces-0.11.0.tar.gz", hash = "sha256:043a79146eb2907edf439899f262b3dfe41717d34124298ed281139a8b93ca32"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.2"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
|
||||
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parso"
|
||||
version = "0.8.4"
|
||||
description = "A Python Parser"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"},
|
||||
{file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
|
||||
testing = ["docopt", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pexpect"
|
||||
version = "4.9.0"
|
||||
description = "Pexpect allows easy control of interactive console applications."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
|
||||
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
ptyprocess = ">=0.5"
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.3.6"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
|
||||
{file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
|
||||
type = ["mypy (>=1.11.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "prompt-toolkit"
|
||||
version = "3.0.48"
|
||||
description = "Library for building powerful interactive command lines in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"},
|
||||
{file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
wcwidth = "*"
|
||||
|
||||
[[package]]
|
||||
name = "psutil"
|
||||
version = "6.1.0"
|
||||
description = "Cross-platform lib for process and system monitoring in Python."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
||||
files = [
|
||||
{file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"},
|
||||
{file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"},
|
||||
{file = "psutil-6.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047"},
|
||||
{file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76"},
|
||||
{file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc"},
|
||||
{file = "psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e"},
|
||||
{file = "psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85"},
|
||||
{file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"},
|
||||
{file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"},
|
||||
{file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"},
|
||||
{file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"},
|
||||
{file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"},
|
||||
{file = "psutil-6.1.0-cp36-cp36m-win32.whl", hash = "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca"},
|
||||
{file = "psutil-6.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747"},
|
||||
{file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"},
|
||||
{file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"},
|
||||
{file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "wheel"]
|
||||
test = ["pytest", "pytest-xdist", "setuptools"]
|
||||
|
||||
[[package]]
|
||||
name = "ptyprocess"
|
||||
version = "0.7.0"
|
||||
description = "Run a subprocess in a pseudo terminal"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
|
||||
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pure-eval"
|
||||
version = "0.2.3"
|
||||
description = "Safely evaluate AST nodes without side effects"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
|
||||
{file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
tests = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "pycairo"
|
||||
version = "1.27.0"
|
||||
description = "Python interface for cairo"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pycairo-1.27.0-cp310-cp310-win32.whl", hash = "sha256:e20f431244634cf244ab6b4c3a2e540e65746eed1324573cf291981c3e65fc05"},
|
||||
{file = "pycairo-1.27.0-cp310-cp310-win_amd64.whl", hash = "sha256:03bf570e3919901572987bc69237b648fe0de242439980be3e606b396e3318c9"},
|
||||
{file = "pycairo-1.27.0-cp311-cp311-win32.whl", hash = "sha256:9a9b79f92a434dae65c34c830bb9abdbd92654195e73d52663cbe45af1ad14b2"},
|
||||
{file = "pycairo-1.27.0-cp311-cp311-win_amd64.whl", hash = "sha256:d40a6d80b15dacb3672dc454df4bc4ab3988c6b3f36353b24a255dc59a1c8aea"},
|
||||
{file = "pycairo-1.27.0-cp312-cp312-win32.whl", hash = "sha256:e2239b9bb6c05edae5f3be97128e85147a155465e644f4d98ea0ceac7afc04ee"},
|
||||
{file = "pycairo-1.27.0-cp312-cp312-win_amd64.whl", hash = "sha256:27cb4d3a80e3b9990af552818515a8e466e0317063a6e61585533f1a86f1b7d5"},
|
||||
{file = "pycairo-1.27.0-cp313-cp313-win32.whl", hash = "sha256:01505c138a313df2469f812405963532fc2511fb9bca9bdc8e0ab94c55d1ced8"},
|
||||
{file = "pycairo-1.27.0-cp313-cp313-win_amd64.whl", hash = "sha256:b0349d744c068b6644ae23da6ada111c8a8a7e323b56cbce3707cba5bdb474cc"},
|
||||
{file = "pycairo-1.27.0-cp39-cp39-win32.whl", hash = "sha256:f9ca8430751f1fdcd3f072377560c9e15608b9a42d61375469db853566993c9b"},
|
||||
{file = "pycairo-1.27.0-cp39-cp39-win_amd64.whl", hash = "sha256:1b1321652a6e27c4de3069709b1cae22aed2707fd8c5e889c04a95669228af2a"},
|
||||
{file = "pycairo-1.27.0.tar.gz", hash = "sha256:5cb21e7a00a2afcafea7f14390235be33497a2cce53a98a19389492a60628430"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.18.0"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
|
||||
{file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
windows-terminal = ["colorama (>=0.4.6)"]
|
||||
|
||||
[[package]]
|
||||
name = "pygobject"
|
||||
version = "3.50.0"
|
||||
description = "Python bindings for GObject Introspection"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.9"
|
||||
files = [
|
||||
{file = "pygobject-3.50.0.tar.gz", hash = "sha256:4500ad3dbf331773d8dedf7212544c999a76fc96b63a91b3dcac1e5925a1d103"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pycairo = ">=1.16"
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.0.1"
|
||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
|
||||
{file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "python-xlib"
|
||||
version = "0.33"
|
||||
description = "Python X Library"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32"},
|
||||
{file = "python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
six = ">=1.10.0"
|
||||
|
||||
[[package]]
|
||||
name = "sdbus"
|
||||
version = "0.13.0"
|
||||
description = "Modern Python D-Bus library. Based on sd-bus from libsystemd."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "sdbus-0.13.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcc88e70e76723234fd0987edccbcc3fe5f4e99245eb30a6b362ef7f882fd8a3"},
|
||||
{file = "sdbus-0.13.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:199a4c321fc5b4cded57a110f82625f6541909c40e733f574d12afc5ace149a7"},
|
||||
{file = "sdbus-0.13.0.tar.gz", hash = "sha256:801bd46608ee82614d42960c8ba8ae9300edb1bf5bbeb534bc8fd21f13d2c20e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
files = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stack-data"
|
||||
version = "0.6.3"
|
||||
description = "Extract data from python stack frames and tracebacks for informative displays"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
|
||||
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
asttokens = ">=2.1.0"
|
||||
executing = ">=1.2.0"
|
||||
pure-eval = "*"
|
||||
|
||||
[package.extras]
|
||||
tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
|
||||
|
||||
[[package]]
|
||||
name = "traitlets"
|
||||
version = "5.14.3"
|
||||
description = "Traitlets Python configuration system"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
|
||||
{file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
|
||||
test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.13"
|
||||
description = "Measures the displayed width of unicode strings in a terminal"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
|
||||
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
|
||||
]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "cd01259969fc6940f43ef71ecdd04f6072c71e918c264b5b29a197198be92748"
|
||||
@ -8,6 +8,13 @@ readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12"
|
||||
i3ipc = "^2.2.1"
|
||||
sdbus = "^0.13.0"
|
||||
asyncinotify = "^4.2.0"
|
||||
pygobject = "^3.50.0"
|
||||
psutil = "^6.1.0"
|
||||
netifaces = "^0.11.0"
|
||||
python-dotenv = "^1.0.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ipython = "^8.29.0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user