view libervia/frontends/tools/display_servers.py @ 4326:5fd6a4dc2122

cli (output/std): use `rich` to output JSON.
author Goffi <goffi@goffi.org>
date Wed, 20 Nov 2024 11:38:44 +0100
parents 4af030d4d3d8
children
line wrap: on
line source

#!/usr/bin/env python3

# Libervia tools for display servers
# Copyright (C) 2009-2024 Jérôme Poisson (goffi@goffi.org)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


try:
    from Xlib import X, display
    from Xlib.xobject.drawable import Window
except ImportError:
    pass
import os

X11 = "X11"
WAYLAND = "Wayland"


def x11_list_windows() -> list[dict[str, object]]:
    """Lists all X11 windows with their metadata.

    @param disp: The X11 display connection.
    @return: A list of dictionaries with window metadata. Each dictionary contains the
        metadata returned by [x11_get_window_metadata].
    """
    disp = display.Display()
    window_list = []
    root = disp.screen().root
    window_ids = root.get_full_property(
        disp.intern_atom("_NET_CLIENT_LIST"), X.AnyPropertyType
    ).value

    for window_id in window_ids:
        window = disp.create_resource_object("window", window_id)
        window_metadata = x11_get_window_metadata(window)
        window_list.append(window_metadata)

    return window_list


def x11_get_window_metadata(window: Window) -> dict[str, object]:
    """Extracts metadata from a given X11 window.

    @param window: The X11 window object.
    @return: A dictionary with the window's metadata, including:

        ``id`` (int)
            window id
        ``type`` (str)
            "application" or "virtual desktop"
        ``title`` (str)
            combined class name and window name, truncated if long
        ``window_name`` (str|none)
            original window name
        ``class`` (tuple[str, str]|none)
            window class
        ``geometry`` (dict)
            dictionary with keys ``x``, ``y``, ``width``, ``height``
    """
    window_id = window.id
    window_name = window.get_wm_name()
    window_class = window.get_wm_class()
    geometry = window.get_geometry()

    # Q&D virtual desktop detection
    virtual_desktop_classes = ["plasmashell", "gnome-shell", "xfdesktop", "mate-panel"]
    window_type = (
        "virtual desktop"
        if window_class and any(vc in window_class[1] for vc in virtual_desktop_classes)
        else "application"
    )

    class_name = window_class[1] if window_class else "Unknown"
    title = f"{class_name}: {window_name or ''}"
    if len(title) > 50:
        title = f"{title[:47]}…"

    return {
        "id": window_id,
        "type": window_type,
        "title": title,
        "window_name": window_name,
        "class": window_class,
        "geometry": {
            "x": geometry.x,
            "y": geometry.y,
            "width": geometry.width,
            "height": geometry.height,
        },
    }


def detect() -> str | None:
    """Detects the type of window manager or display server in use.

    @return: A string indicating the type of window manager/display server ("X11",
        "Wayland", or None).
    """
    if "WAYLAND_DISPLAY" in os.environ:
        return WAYLAND
    elif "DISPLAY" in os.environ:
        return X11
    else:
        return None