roundabout,
created on Tuesday, 5 August 2025, 16:47:35 (1754412455),
received on Tuesday, 5 August 2025, 16:47:39 (1754412459)
Author identity: Vlad <vlad.muntoiu@gmail.com>
4afaf666ca9798e27880e39f0c392303f1e0ab4e
applets/wf-window-list/__init__.py
@@ -23,7 +23,8 @@ import sys
import locale from pathlib import Path from pywayland.client import Display from pywayland.protocol.wayland import WlRegistry, WlSeat, WlSurface, WlCompositorfrom pywayland.protocol.wayland import WlRegistry, WlSeat, WlSurface, WlCompositor, WlOutput from pywayland.protocol.wayland.wl_output import WlOutputProxyfrom pywayland.protocol.wlr_foreign_toplevel_management_unstable_v1 import ( ZwlrForeignToplevelManagerV1, ZwlrForeignToplevelHandleV1
@@ -50,6 +51,7 @@ ffi = FFI()
ffi.cdef(""" void * gdk_wayland_display_get_wl_display (void * display); void * gdk_wayland_surface_get_wl_surface (void * surface); void * gdk_wayland_monitor_get_wl_output (void * monitor);""") gtk = ffi.dlopen("libgtk-4.so.1")
@@ -245,6 +247,7 @@ class WFWindowList(panorama_panel.Applet):
self.initial_button = Gtk.ToggleButton() self.display = None self.my_output = Noneself.wl_surface_ptr = None self.registry = None self.compositor = None
@@ -319,6 +322,21 @@ class WFWindowList(panorama_panel.Applet):
# Intentionally commented: the display is already connected by GTK # self.display.connect() my_monitor = Gtk4LayerShell.get_monitor(self.get_root()) # Iterate through monitors and get their Wayland output (wl_output) # This is a hack to ensure output_enter/leave is called for toplevels for monitor in self.get_root().get_native().get_display().get_monitors(): wl_output = gtk.gdk_wayland_monitor_get_wl_output(ffi.cast("void *", ctypes.pythonapi.PyCapsule_GetPointer(monitor.__gpointer__, None))) if wl_output: print("Create proxy") output_proxy = WlOutputProxy(wl_output, self.display) output_proxy.interface.registry[output_proxy._ptr] = output_proxy if monitor == my_monitor: self.my_output = output_proxy # End hack self.registry = self.display.get_registry() self.registry.dispatcher["global"] = self.on_global self.display.roundtrip()
@@ -351,9 +369,28 @@ class WFWindowList(panorama_panel.Applet):
handle: ZwlrForeignToplevelHandleV1): handle.dispatcher["title"] = lambda h, title: self.on_title_changed(h, title) handle.dispatcher["app_id"] = lambda h, app_id: self.on_app_id_changed(h, app_id) handle.dispatcher["output_enter"] = self.on_output_entered handle.dispatcher["output_leave"] = self.on_output_lefthandle.dispatcher["state"] = lambda h, states: self.on_state_changed(h, states) handle.dispatcher["closed"] = lambda h: self.on_closed(h) def on_output_entered(self, handle, output): # TODO: make this configurable if output != self.my_output: return if handle in self.toplevel_buttons: button = self.toplevel_buttons[handle] self.append(button) self.set_all_rectangles() def on_output_left(self, handle, output): if output != self.my_output: return if handle in self.toplevel_buttons: button = self.toplevel_buttons[handle] self.remove(button) self.set_all_rectangles() def on_title_changed(self, handle, title): if handle not in self.toplevel_buttons: button = WindowButton(handle, title)
@@ -361,18 +398,19 @@ class WFWindowList(panorama_panel.Applet):
button.set_layout_manager(WindowButtonLayoutManager(self.window_button_options)) button.connect("clicked", self.on_button_click) self.toplevel_buttons[handle] = button self.append(button)else: button = self.toplevel_buttons[handle] button.window_title = title self.set_all_rectangles()def set_all_rectangles(self): for button in self.toplevel_buttons.values():surface = WlSurface()surface._ptr = self.wl_surface_ptrbutton.window_id.set_rectangle(surface, *get_widget_rect(button))child = self.get_first_child() while child is not None: if isinstance(child, WindowButton): surface = WlSurface() surface._ptr = self.wl_surface_ptr child.window_id.set_rectangle(surface, *get_widget_rect(child)) child = child.get_next_sibling()def on_button_click(self, button: WindowButton): # Set a rectangle for animation