Improved output name selection and context scoring

This commit is contained in:
Ezri Brimhall 2025-02-05 14:35:46 -07:00
parent c478a4f7d4
commit c439ad41aa
Signed by: ezri
GPG Key ID: 058A78E5680C6F24
2 changed files with 29 additions and 18 deletions

View File

@ -1,10 +1,8 @@
from sdbus import (
DbusInterfaceCommonAsync,
dbus_method_async,
dbus_property_async,
dbus_signal_async,
)
import os
from .workspace_tree import WorkspaceTree
from i3ipc.aio import Connection
import json
@ -25,6 +23,15 @@ class ContextMngrInterface(
self.workspace_tree = workspace_tree
self.connection = connection
@dbus_method_async(input_signature="", result_signature="as")
async def get_available_contexts(self) -> list[str]:
"""
Request a list of contexts compatible with the current monitor configuration, sorted by score.
"""
scores = await self.workspace_tree.score_contexts(self.connection)
contexts = [score[0].name for score in scores if score[1] > 0]
return contexts
@dbus_method_async(input_signature="s", result_signature="s")
async def request_context(self, context: str) -> str:
"""Request a context switch. This will fail if the current monitor configuration is not compatible with the requested context."""

View File

@ -3,8 +3,8 @@ import json
from i3ipc.replies import OutputReply, WorkspaceReply
from i3ipc.aio import Connection
import asyncio
import subprocess
from .utils import OutputMatch
import itertools
DEFAULT_TERMINAL = "alacritty"
@ -90,6 +90,7 @@ class WorkspaceGroup:
name: str = None
workspaces: list[Workspace] = None
reverse: bool = False
output_name: str = None
def __init__(self, output_data: dict[str, str]):
self.name = output_data["group"]
@ -156,10 +157,12 @@ class WorkspaceGroup:
mode = f"mode {self.mode}"
if self.make and self.model and self.serial:
selector = f'"{self.make} {self.model} {self.serial}"'
await self.get_output_name(i3, force=True)
elif len(self.output_names) > 0:
for name in self.output_names:
if name in [output.name for output in outputs]:
selector = name
self.output_name = selector
break
# Configure the output
await i3.command(
@ -193,8 +196,10 @@ class WorkspaceGroup:
)
await workspace.relocate(i3, ouput_name)
async def get_output_name(self, i3: Connection) -> str:
async def get_output_name(self, i3: Connection, force: bool = False) -> str:
"""Get the name of the output in Sway."""
if not force and self.output_name is not None:
return self.output_name
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:
@ -208,6 +213,7 @@ class WorkspaceGroup:
f"Found output {output.name} by make, model, and serial for group {self.name}",
flush=True,
)
self.output_name = output.name
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:
@ -217,6 +223,7 @@ class WorkspaceGroup:
f"Found output {output.name} by name for group {self.name}",
flush=True,
)
self.output_name = output.name
return output.name
return None
@ -239,9 +246,6 @@ class WorkspaceGroup:
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
@ -293,6 +297,7 @@ class WorkspaceContext:
[group.get_match_level(output).value for output in outputs]
)
if group_result == 0:
print(f"Context {self.name}: failed to match group {group.name}")
return 0
result += group_result
return result
@ -308,9 +313,6 @@ class WorkspaceContext:
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
@ -335,11 +337,13 @@ class WorkspaceContext:
"eww",
"open",
window,
"--id",
f"{group.name}-{window}",
"--screen",
await group.get_output_name(i3),
"--arg",
f"group={group.name}",
*extra_args,
*list(itertools.chain(*extra_args)),
)
await proc.wait()
@ -479,19 +483,19 @@ class WorkspaceTree:
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
async def score_contexts(self, i3: Connection):
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)
return scores
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."""
# Get the scores
scores = await self.score_contexts(i3)
# If the top context is the current context, or the rank is 0, do nothing
if scores[0][1] == 0: