Source code for sevaht_gui.monitors

"""Monitor geometry, multi-monitor aware.

tkinter has no native Wayland backend, so on Wayland it runs under XWayland and
the X11 RandR *extension* is available. This queries RandR through python-xlib
(already a Linux dependency) rather than shelling out to the ``xrandr`` binary,
which may not be installed; it therefore works under both X11 and XWayland.

Falls back to the full Tk screen size, which is correct for single-monitor
setups and for Windows (where Tk reports the primary monitor).
"""

from __future__ import annotations

import logging
import sys
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import tkinter as tk

logger = logging.getLogger(__name__)


[docs] def primary_monitor_bounds(root: tk.Misc) -> tuple[int, int, int, int]: """Return the primary monitor's ``(x, y, width, height)`` in pixels. ``root`` is any Tk widget, used both to talk to the display and for the fallback screen size. """ bounds = _x11_primary_bounds() if bounds is not None: return bounds return (0, 0, root.winfo_screenwidth(), root.winfo_screenheight())
def _x11_primary_bounds() -> tuple[int, int, int, int] | None: if sys.platform == "win32": return None try: from Xlib import display as xdisplay except ImportError: return None connection = None try: connection = xdisplay.Display() root = connection.screen().root resources = root.xrandr_get_screen_resources() timestamp = resources.config_timestamp primary = root.xrandr_get_output_primary().output # Prefer the primary output; otherwise the first active one. outputs = ( [primary, *resources.outputs] if primary else resources.outputs ) for output in outputs: if not output: continue info = connection.xrandr_get_output_info(output, timestamp) if not info.crtc: continue crtc = connection.xrandr_get_crtc_info(info.crtc, timestamp) if crtc.width and crtc.height: return (crtc.x, crtc.y, crtc.width, crtc.height) except Exception: # noqa: BLE001 logger.debug("Could not query RandR for monitor bounds", exc_info=True) return None finally: if connection is not None: connection.close() return None