Added config watching and improved workspace output assignment
This commit is contained in:
parent
b44110ca19
commit
ded2c8676e
42
poetry.lock
generated
42
poetry.lock
generated
@ -18,6 +18,17 @@ six = ">=1.12.0"
|
|||||||
astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"]
|
astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"]
|
||||||
test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"]
|
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]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@ -40,6 +51,20 @@ files = [
|
|||||||
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "exceptiongroup"
|
||||||
|
version = "1.2.2"
|
||||||
|
description = "Backport of PEP 654 (exception groups)"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
|
||||||
|
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
test = ["pytest (>=6)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "executing"
|
name = "executing"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@ -82,6 +107,7 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
decorator = "*"
|
decorator = "*"
|
||||||
|
exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
|
||||||
jedi = ">=0.16"
|
jedi = ">=0.16"
|
||||||
matplotlib-inline = "*"
|
matplotlib-inline = "*"
|
||||||
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
|
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
|
||||||
@ -89,6 +115,7 @@ prompt-toolkit = ">=3.0.41,<3.1.0"
|
|||||||
pygments = ">=2.4.0"
|
pygments = ">=2.4.0"
|
||||||
stack-data = "*"
|
stack-data = "*"
|
||||||
traitlets = ">=5.13.0"
|
traitlets = ">=5.13.0"
|
||||||
|
typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""}
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
|
all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
|
||||||
@ -290,6 +317,17 @@ files = [
|
|||||||
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
|
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"]
|
test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.12.2"
|
||||||
|
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
|
||||||
|
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wcwidth"
|
name = "wcwidth"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
@ -303,5 +341,5 @@ files = [
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.10"
|
||||||
content-hash = "8a614d458afd106177bcc01d92e0fa81508bbde310b172ecd879451341907021"
|
content-hash = "9124e77caf9be4c685a59b0aea187f0f0bb60f2639b74ed8aa6f47ea97aba32e"
|
||||||
|
|||||||
@ -14,6 +14,7 @@ sway_get_focused_workspace = 'sway_context_manager.client:get_workspace_definiti
|
|||||||
python = "^3.10"
|
python = "^3.10"
|
||||||
i3ipc = "^2.2.1"
|
i3ipc = "^2.2.1"
|
||||||
sdbus = "^0.12.0"
|
sdbus = "^0.12.0"
|
||||||
|
asyncinotify = "^4.2.0"
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
|||||||
@ -44,10 +44,12 @@ class ContextMngrInterface(
|
|||||||
async def focus_workspace(self, user_index: int) -> bool:
|
async def focus_workspace(self, user_index: int) -> bool:
|
||||||
"""Focus a workspace by its 1-indexed ID in its group."""
|
"""Focus a workspace by its 1-indexed ID in its group."""
|
||||||
print("focusing workspace", user_index)
|
print("focusing workspace", user_index)
|
||||||
workspace = self.workspace_tree.get_workspace(user_index)
|
workspace, group = self.workspace_tree.get_workspace(user_index)
|
||||||
if workspace is None:
|
if workspace is None or group is None:
|
||||||
return False
|
return False
|
||||||
await workspace.focus(self.connection)
|
await workspace.relocate(
|
||||||
|
self.connection, await group.get_output_name(self.connection)
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@dbus_method_async(input_signature="y", result_signature="b")
|
@dbus_method_async(input_signature="y", result_signature="b")
|
||||||
|
|||||||
@ -4,6 +4,8 @@ from .interface import ContextMngrInterface
|
|||||||
from .swayipc import SwayConnection
|
from .swayipc import SwayConnection
|
||||||
import os
|
import os
|
||||||
from sdbus import request_default_bus_name_async, sd_bus_open_user, set_default_bus
|
from sdbus import request_default_bus_name_async, sd_bus_open_user, set_default_bus
|
||||||
|
from .workspace_config_watcher import Watcher
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
WORKSPACES_FILE = os.environ.get(
|
WORKSPACES_FILE = os.environ.get(
|
||||||
"WORKSPACES_FILE", os.path.expanduser("~/.config/sway/workspaces.json")
|
"WORKSPACES_FILE", os.path.expanduser("~/.config/sway/workspaces.json")
|
||||||
@ -11,6 +13,7 @@ WORKSPACES_FILE = os.environ.get(
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
set_default_bus(sd_bus_open_user())
|
set_default_bus(sd_bus_open_user())
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
@ -18,11 +21,13 @@ def main():
|
|||||||
tree = WorkspaceTree(WORKSPACES_FILE)
|
tree = WorkspaceTree(WORKSPACES_FILE)
|
||||||
sway = SwayConnection(tree)
|
sway = SwayConnection(tree)
|
||||||
interface = ContextMngrInterface(tree, sway.connection)
|
interface = ContextMngrInterface(tree, sway.connection)
|
||||||
|
watcher = Watcher(Path(WORKSPACES_FILE), sway, tree, interface)
|
||||||
sway.on_change(interface.on_tree_changed)
|
sway.on_change(interface.on_tree_changed)
|
||||||
|
|
||||||
loop.run_until_complete(request_default_bus_name_async("dev.ezri.sway"))
|
loop.run_until_complete(request_default_bus_name_async("dev.ezri.sway"))
|
||||||
interface.export_to_dbus("/ContextManager")
|
interface.export_to_dbus("/ContextManager")
|
||||||
loop.create_task(sway.run())
|
loop.create_task(sway.run())
|
||||||
|
loop.create_task(watcher.run())
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ from i3ipc.aio import Connection
|
|||||||
from i3ipc import Event
|
from i3ipc import Event
|
||||||
from .workspace_tree import WorkspaceTree
|
from .workspace_tree import WorkspaceTree
|
||||||
from .utils import async_debounce
|
from .utils import async_debounce
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
class SwayConnection:
|
class SwayConnection:
|
||||||
@ -35,6 +36,11 @@ class SwayConnection:
|
|||||||
"""On mode change event, do something. Not sure what yet."""
|
"""On mode change event, do something. Not sure what yet."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
async def on_shutdown(self, event):
|
||||||
|
"""On shutdown event, close the connection and exit"""
|
||||||
|
await self.connection.close()
|
||||||
|
asyncio.get_event_loop().stop()
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
# Connect to sway
|
# Connect to sway
|
||||||
await self.connection.connect()
|
await self.connection.connect()
|
||||||
@ -43,6 +49,7 @@ class SwayConnection:
|
|||||||
self.connection.on(Event.WORKSPACE, self.on_workspace)
|
self.connection.on(Event.WORKSPACE, self.on_workspace)
|
||||||
self.connection.on(Event.OUTPUT, self.on_output)
|
self.connection.on(Event.OUTPUT, self.on_output)
|
||||||
self.connection.on(Event.MODE, self.on_mode)
|
self.connection.on(Event.MODE, self.on_mode)
|
||||||
|
self.connection.on(Event.SHUTDOWN, self.on_shutdown)
|
||||||
|
|
||||||
# Get initial output set
|
# Get initial output set
|
||||||
self.last_output_set = await self.connection.get_outputs()
|
self.last_output_set = await self.connection.get_outputs()
|
||||||
|
|||||||
40
sway_context_manager/workspace_config_watcher.py
Normal file
40
sway_context_manager/workspace_config_watcher.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from asyncinotify import Inotify, Mask
|
||||||
|
from .swayipc import SwayConnection
|
||||||
|
from .interface import ContextMngrInterface
|
||||||
|
from .workspace_tree import WorkspaceTree
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class Watcher:
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
filepath: Path,
|
||||||
|
sway: SwayConnection,
|
||||||
|
tree: WorkspaceTree,
|
||||||
|
interface: ContextMngrInterface,
|
||||||
|
):
|
||||||
|
self.sway = sway
|
||||||
|
self.tree = tree
|
||||||
|
self.interface = interface
|
||||||
|
self.filepath = filepath
|
||||||
|
|
||||||
|
if self.filepath.is_symlink():
|
||||||
|
# Resolve the symlink so we're tracking the actual file
|
||||||
|
self.filepath = self.filepath.resolve()
|
||||||
|
file_dir = self.filepath.parent
|
||||||
|
self.inotify = Inotify()
|
||||||
|
|
||||||
|
# Watch for changes in the file
|
||||||
|
self.watch = self.inotify.add_watch(
|
||||||
|
file_dir,
|
||||||
|
Mask.CREATE | Mask.DELETE | Mask.MOVED_TO | Mask.MOVED_FROM | Mask.MODIFY,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
async for event in self.inotify:
|
||||||
|
if event.path == self.filepath:
|
||||||
|
print(f"Config file changed, reloading", flush=True)
|
||||||
|
self.tree.load_file(str(self.filepath))
|
||||||
|
await self.tree.update_context(self.sway.connection, True)
|
||||||
|
await self.interface.on_tree_changed(self.tree)
|
||||||
@ -1,6 +1,6 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
import json
|
import json
|
||||||
from i3ipc.replies import OutputReply
|
from i3ipc.replies import OutputReply, WorkspaceReply
|
||||||
from i3ipc.aio import Connection
|
from i3ipc.aio import Connection
|
||||||
import asyncio
|
import asyncio
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -67,6 +67,15 @@ class Workspace:
|
|||||||
"""Move the focused container to the workspace."""
|
"""Move the focused container to the workspace."""
|
||||||
await i3.command(f"move container to workspace {self.index}")
|
await i3.command(f"move container to workspace {self.index}")
|
||||||
|
|
||||||
|
async def reassign(self, i3: Connection, output: str):
|
||||||
|
"""Reassign the workspace to a different output."""
|
||||||
|
await i3.command(f"workspace {self.index} output {output}")
|
||||||
|
|
||||||
|
async def relocate(self, i3: Connection, output: str):
|
||||||
|
"""Move the workspace to a different output."""
|
||||||
|
await i3.command(f"workspace {self.index}")
|
||||||
|
await i3.command(f"move workspace to output {output}")
|
||||||
|
|
||||||
|
|
||||||
class WorkspaceGroup:
|
class WorkspaceGroup:
|
||||||
"""A class representing a group of workspaces. Generally maps to a monitor."""
|
"""A class representing a group of workspaces. Generally maps to a monitor."""
|
||||||
@ -143,14 +152,38 @@ class WorkspaceGroup:
|
|||||||
if name in [output.name for output in outputs]:
|
if name in [output.name for output in outputs]:
|
||||||
selector = name
|
selector = name
|
||||||
break
|
break
|
||||||
# First, assign workspaces to the output
|
# Configure the output
|
||||||
for workspace in self.workspaces:
|
|
||||||
await i3.command(f"workspace {workspace.index} output {selector}")
|
|
||||||
# Then, configure the output
|
|
||||||
await i3.command(
|
await i3.command(
|
||||||
f"output {selector} position {self.position[0]} {self.position[1]} {mode} {transform} enable"
|
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:
|
async def get_output_name(self, i3: Connection) -> str:
|
||||||
"""Get the name of the output in Sway."""
|
"""Get the name of the output in Sway."""
|
||||||
outputs = await i3.get_outputs()
|
outputs = await i3.get_outputs()
|
||||||
@ -255,30 +288,29 @@ class WorkspaceContext:
|
|||||||
result += group_result
|
result += group_result
|
||||||
return 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):
|
async def activate(self, i3: Connection):
|
||||||
"""Activate the context in Sway."""
|
"""Activate the context in Sway."""
|
||||||
defined_displays = [
|
defined_displays = [
|
||||||
f"{group.make} {group.model} {group.serial}" for group in self.groups
|
f"{group.make} {group.model} {group.serial}" for group in self.groups
|
||||||
]
|
]
|
||||||
outputs = await i3.get_outputs()
|
outputs = await i3.get_outputs()
|
||||||
# First, close all EWW windows
|
|
||||||
proc = await asyncio.create_subprocess_exec("eww", "close-all")
|
|
||||||
await proc.wait()
|
|
||||||
|
|
||||||
# Second, if the focused workspace is not in the context, focus the primary output
|
# Configure all displays defined in the context
|
||||||
if not self.active_group:
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
# Then, disable all displays, so we can assign workspaces to the correct ones before enabling them
|
|
||||||
for output in outputs:
|
|
||||||
await i3.command(f"output {output.name} disable")
|
|
||||||
|
|
||||||
# Next, configure all displays defined in the context
|
|
||||||
for group in self.groups:
|
for group in self.groups:
|
||||||
await group.configure(i3, outputs)
|
await group.configure(i3, outputs)
|
||||||
|
|
||||||
# Finally, open all EWW windows defined in the context on the appropriate windows
|
# 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 group in self.groups:
|
||||||
for window in group.eww_windows:
|
for window in group.eww_windows:
|
||||||
proc = await asyncio.create_subprocess_exec(
|
proc = await asyncio.create_subprocess_exec(
|
||||||
@ -290,6 +322,11 @@ class WorkspaceContext:
|
|||||||
)
|
)
|
||||||
await proc.wait()
|
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
|
@property
|
||||||
def active_group(self) -> WorkspaceGroup:
|
def active_group(self) -> WorkspaceGroup:
|
||||||
"""Returns the active group in the context."""
|
"""Returns the active group in the context."""
|
||||||
@ -328,7 +365,9 @@ class WorkspaceTree:
|
|||||||
workspaces: list[Workspace] = None
|
workspaces: list[Workspace] = None
|
||||||
|
|
||||||
def __init__(self, filename: str):
|
def __init__(self, filename: str):
|
||||||
|
self.load_file(filename)
|
||||||
|
|
||||||
|
def load_file(self, filename: str):
|
||||||
with open(filename, "r") as file:
|
with open(filename, "r") as file:
|
||||||
initial: dict = json.load(file)
|
initial: dict = json.load(file)
|
||||||
|
|
||||||
@ -338,7 +377,11 @@ class WorkspaceTree:
|
|||||||
for context, context_dict in initial["contexts"].items():
|
for context, context_dict in initial["contexts"].items():
|
||||||
context_obj = WorkspaceContext(context, context_dict, self.workspaces)
|
context_obj = WorkspaceContext(context, context_dict, self.workspaces)
|
||||||
self.contexts.append(context_obj)
|
self.contexts.append(context_obj)
|
||||||
if initial.get("default_context", "personal") == context:
|
# 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
|
self.current_context = context_obj
|
||||||
|
|
||||||
def __dict__(self):
|
def __dict__(self):
|
||||||
@ -353,7 +396,7 @@ class WorkspaceTree:
|
|||||||
def __json__(self):
|
def __json__(self):
|
||||||
return {self.current_context.name: self.current_context.__json__()}
|
return {self.current_context.name: self.current_context.__json__()}
|
||||||
|
|
||||||
def get_workspace(self, user_index: int) -> Workspace:
|
def get_workspace(self, user_index: int) -> tuple[Workspace, WorkspaceGroup] | None:
|
||||||
"""Returns a workspace object based on the user index."""
|
"""Returns a workspace object based on the user index."""
|
||||||
if user_index < 1:
|
if user_index < 1:
|
||||||
raise IndexError("Workspace index must be greater than or equal to 1.")
|
raise IndexError("Workspace index must be greater than or equal to 1.")
|
||||||
@ -384,12 +427,15 @@ class WorkspaceTree:
|
|||||||
counter += 1
|
counter += 1
|
||||||
|
|
||||||
continue
|
continue
|
||||||
return group.workspaces[user_index - 1]
|
return group.workspaces[user_index - 1], group
|
||||||
# If we didn't find a workspace, return None
|
# If we didn't find a workspace, return None
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# If the user index is different from the active index, return the workspace in the active group
|
# 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]
|
return (
|
||||||
|
self.current_context.active_group.workspaces[user_index - 1],
|
||||||
|
self.current_context.active_group,
|
||||||
|
)
|
||||||
|
|
||||||
def update_workspaces(self, ws_sway_dict: dict):
|
def update_workspaces(self, ws_sway_dict: dict):
|
||||||
"""Updates the workspaces in the tree based on the return from sway IPC."""
|
"""Updates the workspaces in the tree based on the return from sway IPC."""
|
||||||
@ -412,7 +458,7 @@ class WorkspaceTree:
|
|||||||
if ws not in touched:
|
if ws not in touched:
|
||||||
ws.deactivate()
|
ws.deactivate()
|
||||||
|
|
||||||
async def update_context(self, i3: Connection):
|
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."""
|
"""Activates a new context in Sway based on the current display configuration."""
|
||||||
# First, get the current display configuration
|
# First, get the current display configuration
|
||||||
outputs = await i3.get_outputs()
|
outputs = await i3.get_outputs()
|
||||||
@ -427,11 +473,26 @@ class WorkspaceTree:
|
|||||||
scores.sort(key=lambda x: x[1], reverse=True)
|
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 the top context is the current context, or the rank is 0, do nothing
|
||||||
if scores[0][0] == self.current_context or scores[0][1] == 0:
|
if scores[0][1] == 0:
|
||||||
|
print(
|
||||||
|
"No context is compatible with the current display configuration, doing nothing.",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
return
|
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]
|
self.current_context = scores[0][0]
|
||||||
await self.current_context.activate(i3)
|
|
||||||
|
|
||||||
async def activate_context(self, i3: Connection, name: str):
|
async def activate_context(self, i3: Connection, name: str):
|
||||||
"""Activates a context by name. This will fail if the current display configuration is incompatible."""
|
"""Activates a context by name. This will fail if the current display configuration is incompatible."""
|
||||||
@ -448,5 +509,8 @@ class WorkspaceTree:
|
|||||||
"Context is incompatible with current display configuration."
|
"Context is incompatible with current display configuration."
|
||||||
)
|
)
|
||||||
print(f"Activating context {context.name} with score {score}.", flush=True)
|
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
|
self.current_context = context
|
||||||
|
|
||||||
await context.activate(i3)
|
await context.activate(i3)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user