diff libervia/frontends/tools/display_servers.py @ 4203:4af030d4d3d8

frontends (tools): module to handle display servers: rel 433
author Goffi <goffi@goffi.org>
date Tue, 16 Jan 2024 10:41:58 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libervia/frontends/tools/display_servers.py	Tue Jan 16 10:41:58 2024 +0100
@@ -0,0 +1,116 @@
+#!/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