Run KVM switch in background and target DDM by monitor model

This commit is contained in:
Lago
2026-03-27 14:49:28 +01:00
parent 8591e22a7b
commit 33e762c182
5 changed files with 164 additions and 21 deletions
+62 -9
View File
@@ -26,6 +26,8 @@ class DDMCommandResult:
class DDMBackend(Protocol):
def is_available(self) -> bool: ...
def supports_monitor_targeting(self) -> bool: ...
def resolve_alienware_slot(self, force: bool = False) -> int | None: ...
def invalidate_slot(self) -> None: ...
@@ -51,6 +53,9 @@ class RealDDMBackend:
def is_available(self) -> bool:
return self.executable_path is not None and self.executable_path.exists()
def supports_monitor_targeting(self) -> bool:
return True
def invalidate_slot(self) -> None:
with self._lock:
self._cached_slot = None
@@ -86,17 +91,43 @@ class RealDDMBackend:
return DDMCommandResult(False, f"Port {port_name} is not supported.")
ddm_input = str(port_spec["ddm_input"])
monitor_selector = f"/MNT:{ALIENWARE_MODEL_TOKEN}"
output = self._run_logged_command(f"/{slot}:WriteActiveInput", ddm_input)
line = _extract_result_line(output, f"{slot}:WriteActiveInput")
if line and "INVALID COMMAND" in line.upper():
return DDMCommandResult(False, "DDM rejected the WriteActiveInput command.", output)
return DDMCommandResult(
True,
f"Alienware DDM slot {slot} was instructed to switch to {ddm_input}.",
output,
targeted_output = self._run_logged_command(
monitor_selector,
"/WriteActiveInput",
ddm_input,
)
targeted_line = _extract_result_line_fragment(targeted_output, "WRITEACTIVEINPUT")
if not _output_has_failure(targeted_line, targeted_output):
return DDMCommandResult(
True,
f"Alienware {ALIENWARE_MODEL_TOKEN} was instructed to switch to {ddm_input}.",
targeted_output,
)
if slot > 0:
legacy_output = self._run_logged_command(f"/{slot}:WriteActiveInput", ddm_input)
legacy_line = _extract_result_line(legacy_output, f"{slot}:WriteActiveInput")
if not _output_has_failure(legacy_line, legacy_output):
return DDMCommandResult(
True,
f"Alienware DDM slot {slot} was instructed to switch to {ddm_input}.",
legacy_output,
)
merged_output = "\n".join(
block
for block in [
"[targeted]",
targeted_output.strip(),
"[legacy]",
legacy_output.strip(),
]
if block
)
return DDMCommandResult(False, "DDM rejected the WriteActiveInput command.", merged_output)
return DDMCommandResult(False, "DDM rejected the WriteActiveInput command.", targeted_output)
def _run_logged_command(self, *command_args: str) -> str:
assert self.executable_path is not None
@@ -148,3 +179,25 @@ def _extract_result_line(text: str, prefix: str) -> str:
if normalized.startswith(prefix):
return normalized
return ""
def _extract_result_line_fragment(text: str, fragment: str) -> str:
needle = fragment.upper()
for line in text.splitlines():
normalized = line.strip()
if needle in normalized.upper():
return normalized
return ""
def _output_has_failure(line: str, output: str) -> bool:
normalized_line = line.upper()
normalized_output = output.upper()
checks = (
"INVALID COMMAND",
"UNSUPPORTED",
"NOT FOUND",
"FAILED",
"FAIL",
)
return any(token in normalized_line or token in normalized_output for token in checks)
+11 -5
View File
@@ -117,10 +117,15 @@ class KvmSwitcherService:
errors.extend(config.validate())
ddm_slot = self.ddm_backend.resolve_alienware_slot(force=False)
ddm_ready = self.ddm_backend.is_available() and ddm_slot is not None
supports_monitor_targeting = bool(
getattr(self.ddm_backend, "supports_monitor_targeting", lambda: False)()
)
ddm_ready = self.ddm_backend.is_available() and (
ddm_slot is not None or supports_monitor_targeting
)
if not self.ddm_backend.is_available():
errors.append("DDM.exe was not found.")
elif ddm_slot is None:
elif ddm_slot is None and not supports_monitor_targeting:
errors.append("Alienware DDM slot could not be resolved.")
resolved_target, desired_port = self._resolve_target(config, scan)
@@ -132,7 +137,7 @@ class KvmSwitcherService:
not errors
and desired_port is not None
and scan.alienware_input_code is not None
and ddm_slot is not None
and ddm_ready
and scan.samsung_present
and not self._samsung_session_successful
and self._samsung_session_attempt_count < 3
@@ -229,7 +234,7 @@ class KvmSwitcherService:
def _attempt_switch_sequence(
self,
ddm_slot: int,
ddm_slot: int | None,
desired_port: str,
scan: HardwareScan,
last_switch_at: str | None,
@@ -241,7 +246,8 @@ class KvmSwitcherService:
self._samsung_session_attempted = True
self._samsung_session_attempt_count += 1
result = self.ddm_backend.switch_to_port(ddm_slot, desired_port)
switch_slot = ddm_slot if ddm_slot is not None else 0
result = self.ddm_backend.switch_to_port(switch_slot, desired_port)
if not result.success:
errors.append(result.message)
self.ddm_backend.invalidate_slot()