magicciv/tools/detect-primary-monitor.py

119 lines
3.7 KiB
Python
Executable file

#!/usr/bin/env python3
"""Print the primary monitor's geometry as `WxH+X+Y`.
Queries GNOME Mutter via DBus (authoritative on Wayland sessions),
falls back to xrandr if Mutter isn't available. Used by
`tools/ai-arena.sh` to position arena windows on the primary monitor
specifically — not whatever monitor the mouse happens to focus.
Exits 0 with geometry on stdout on success, exits 1 with an empty
stdout on failure (caller should apply its own fallback).
"""
from __future__ import annotations
import re
import shutil
import subprocess
import sys
def detect_via_mutter() -> str | None:
"""Ask GNOME Mutter's DisplayConfig DBus interface for the primary
monitor. Parses the text form that `gdbus call` returns — the structured
tuple reply is nontrivial to deserialise without pydbus, so we walk the
repr with regex. The logical-monitor tuple shape is
(x, y, scale, uint32 transform, primary_bool, [(connector, ...)], ...)
and the physical-monitor section carries the current-mode WxH."""
if not shutil.which("gdbus"):
return None
try:
raw = subprocess.run(
[
"gdbus",
"call",
"--session",
"--dest",
"org.gnome.Mutter.DisplayConfig",
"--object-path",
"/org/gnome/Mutter/DisplayConfig",
"--method",
"org.gnome.Mutter.DisplayConfig.GetCurrentState",
],
check=True,
capture_output=True,
text=True,
timeout=5,
).stdout
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
return None
if not raw:
return None
logical_pat = re.compile(
r"\((\d+),\s*(\d+),\s*([\d.]+),\s*uint32\s*\d+,\s*(true|false),"
r"\s*\[\('([^']+)',"
)
primary = None
for m in logical_pat.finditer(raw):
x, y, scale_s, prim, connector = m.groups()
if prim == "true":
primary = (int(x), int(y), float(scale_s), connector)
break
if not primary:
return None
px, py, pscale, pconn = primary
# Find the primary connector's entry in the monitors array and look up
# its is-current mode to get the pixel dimensions.
conn_idx = raw.find(f"('{pconn}', ")
if conn_idx < 0:
return None
mode_pat = re.compile(
r"'(\d+)x(\d+)@[\d.]+',\s*(\d+),\s*(\d+)[^}]*'is-current':\s*<true>"
)
mm = mode_pat.search(raw, conn_idx)
if not mm:
return None
w, h = int(mm.group(3)), int(mm.group(4))
if pscale != 1.0:
# Logical monitor size under HiDPI scaling — divide physical by scale.
w = int(w / pscale)
h = int(h / pscale)
return f"{w}x{h}+{px}+{py}"
def detect_via_xrandr() -> str | None:
"""Fallback for X11 / XWayland systems with xrandr installed."""
if not shutil.which("xrandr"):
return None
try:
out = subprocess.run(
["xrandr", "--query"],
check=True,
capture_output=True,
text=True,
timeout=5,
).stdout
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
return None
# Line looks like:
# DP-1 connected primary 3440x1440+0+0 (normal ...)
geom_pat = re.compile(r"(\d+x\d+\+\d+\+\d+)")
for line in out.splitlines():
if " connected primary " in line:
m = geom_pat.search(line)
if m:
return m.group(1)
return None
def main() -> int:
geom = detect_via_mutter() or detect_via_xrandr()
if not geom:
return 1
print(geom)
return 0
if __name__ == "__main__":
sys.exit(main())