116 lines
3.5 KiB
Python
116 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Libervia tools for display servers
|
|
# Copyright (C) 2009-2026 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
|