Improved output name selection and context scoring
This commit is contained in:
parent
c478a4f7d4
commit
c439ad41aa
@ -1,10 +1,8 @@
|
|||||||
from sdbus import (
|
from sdbus import (
|
||||||
DbusInterfaceCommonAsync,
|
DbusInterfaceCommonAsync,
|
||||||
dbus_method_async,
|
dbus_method_async,
|
||||||
dbus_property_async,
|
|
||||||
dbus_signal_async,
|
dbus_signal_async,
|
||||||
)
|
)
|
||||||
import os
|
|
||||||
from .workspace_tree import WorkspaceTree
|
from .workspace_tree import WorkspaceTree
|
||||||
from i3ipc.aio import Connection
|
from i3ipc.aio import Connection
|
||||||
import json
|
import json
|
||||||
@ -25,6 +23,15 @@ class ContextMngrInterface(
|
|||||||
self.workspace_tree = workspace_tree
|
self.workspace_tree = workspace_tree
|
||||||
self.connection = connection
|
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")
|
@dbus_method_async(input_signature="s", result_signature="s")
|
||||||
async def request_context(self, context: str) -> str:
|
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."""
|
"""Request a context switch. This will fail if the current monitor configuration is not compatible with the requested context."""
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import json
|
|||||||
from i3ipc.replies import OutputReply, WorkspaceReply
|
from i3ipc.replies import OutputReply, WorkspaceReply
|
||||||
from i3ipc.aio import Connection
|
from i3ipc.aio import Connection
|
||||||
import asyncio
|
import asyncio
|
||||||
import subprocess
|
|
||||||
from .utils import OutputMatch
|
from .utils import OutputMatch
|
||||||
|
import itertools
|
||||||
|
|
||||||
DEFAULT_TERMINAL = "alacritty"
|
DEFAULT_TERMINAL = "alacritty"
|
||||||
|
|
||||||
@ -90,6 +90,7 @@ class WorkspaceGroup:
|
|||||||
name: str = None
|
name: str = None
|
||||||
workspaces: list[Workspace] = None
|
workspaces: list[Workspace] = None
|
||||||
reverse: bool = False
|
reverse: bool = False
|
||||||
|
output_name: str = None
|
||||||
|
|
||||||
def __init__(self, output_data: dict[str, str]):
|
def __init__(self, output_data: dict[str, str]):
|
||||||
self.name = output_data["group"]
|
self.name = output_data["group"]
|
||||||
@ -156,10 +157,12 @@ class WorkspaceGroup:
|
|||||||
mode = f"mode {self.mode}"
|
mode = f"mode {self.mode}"
|
||||||
if self.make and self.model and self.serial:
|
if self.make and self.model and self.serial:
|
||||||
selector = f'"{self.make} {self.model} {self.serial}"'
|
selector = f'"{self.make} {self.model} {self.serial}"'
|
||||||
|
await self.get_output_name(i3, force=True)
|
||||||
elif len(self.output_names) > 0:
|
elif len(self.output_names) > 0:
|
||||||
for name in self.output_names:
|
for name in self.output_names:
|
||||||
if name in [output.name for output in outputs]:
|
if name in [output.name for output in outputs]:
|
||||||
selector = name
|
selector = name
|
||||||
|
self.output_name = selector
|
||||||
break
|
break
|
||||||
# Configure the output
|
# Configure the output
|
||||||
await i3.command(
|
await i3.command(
|
||||||
@ -193,8 +196,10 @@ class WorkspaceGroup:
|
|||||||
)
|
)
|
||||||
await workspace.relocate(i3, ouput_name)
|
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."""
|
"""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()
|
outputs = await i3.get_outputs()
|
||||||
# If we have make, model, and serial, search by those first
|
# If we have make, model, and serial, search by those first
|
||||||
if self.make and self.model and self.serial:
|
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}",
|
f"Found output {output.name} by make, model, and serial for group {self.name}",
|
||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
|
self.output_name = output.name
|
||||||
return output.name
|
return output.name
|
||||||
# If we don't find an exact match for the output, search by name if we have any
|
# If we don't find an exact match for the output, search by name if we have any
|
||||||
if len(self.output_names) > 0:
|
if len(self.output_names) > 0:
|
||||||
@ -217,6 +223,7 @@ class WorkspaceGroup:
|
|||||||
f"Found output {output.name} by name for group {self.name}",
|
f"Found output {output.name} by name for group {self.name}",
|
||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
|
self.output_name = output.name
|
||||||
return output.name
|
return output.name
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -239,9 +246,6 @@ class WorkspaceGroup:
|
|||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
return OutputMatch.NAME_MATCH
|
return OutputMatch.NAME_MATCH
|
||||||
print(
|
|
||||||
f"Match level: NO_MATCH for {output.name} on group {self.name}", flush=True
|
|
||||||
)
|
|
||||||
return OutputMatch.NO_MATCH
|
return OutputMatch.NO_MATCH
|
||||||
|
|
||||||
|
|
||||||
@ -293,6 +297,7 @@ class WorkspaceContext:
|
|||||||
[group.get_match_level(output).value for output in outputs]
|
[group.get_match_level(output).value for output in outputs]
|
||||||
)
|
)
|
||||||
if group_result == 0:
|
if group_result == 0:
|
||||||
|
print(f"Context {self.name}: failed to match group {group.name}")
|
||||||
return 0
|
return 0
|
||||||
result += group_result
|
result += group_result
|
||||||
return result
|
return result
|
||||||
@ -308,9 +313,6 @@ class WorkspaceContext:
|
|||||||
|
|
||||||
async def activate(self, i3: Connection):
|
async def activate(self, i3: Connection):
|
||||||
"""Activate the context in Sway."""
|
"""Activate the context in Sway."""
|
||||||
defined_displays = [
|
|
||||||
f"{group.make} {group.model} {group.serial}" for group in self.groups
|
|
||||||
]
|
|
||||||
outputs = await i3.get_outputs()
|
outputs = await i3.get_outputs()
|
||||||
|
|
||||||
# Configure all displays defined in the context
|
# Configure all displays defined in the context
|
||||||
@ -335,11 +337,13 @@ class WorkspaceContext:
|
|||||||
"eww",
|
"eww",
|
||||||
"open",
|
"open",
|
||||||
window,
|
window,
|
||||||
|
"--id",
|
||||||
|
f"{group.name}-{window}",
|
||||||
"--screen",
|
"--screen",
|
||||||
await group.get_output_name(i3),
|
await group.get_output_name(i3),
|
||||||
"--arg",
|
"--arg",
|
||||||
f"group={group.name}",
|
f"group={group.name}",
|
||||||
*extra_args,
|
*list(itertools.chain(*extra_args)),
|
||||||
)
|
)
|
||||||
await proc.wait()
|
await proc.wait()
|
||||||
|
|
||||||
@ -479,19 +483,19 @@ class WorkspaceTree:
|
|||||||
if ws not in touched:
|
if ws not in touched:
|
||||||
ws.deactivate()
|
ws.deactivate()
|
||||||
|
|
||||||
async def update_context(self, i3: Connection, match_context_on_name: bool = False):
|
async def score_contexts(self, i3: Connection):
|
||||||
"""Activates a new context in Sway based on the current display configuration."""
|
|
||||||
# First, get the current display configuration
|
|
||||||
outputs = await i3.get_outputs()
|
outputs = await i3.get_outputs()
|
||||||
print(outputs, flush=True)
|
|
||||||
|
|
||||||
# Next, calculate match scores for each context
|
|
||||||
scores = [
|
scores = [
|
||||||
(context, context.compatability_rank(outputs)) for context in self.contexts
|
(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)
|
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 the top context is the current context, or the rank is 0, do nothing
|
||||||
if scores[0][1] == 0:
|
if scores[0][1] == 0:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user