soreau,
created on Thursday, 4 September 2025, 04:27:38 (1756960058),
received on Saturday, 6 September 2025, 21:54:28 (1757195668)
Author identity: Scott Moreau <oreaus@gmail.com>
e46dce59d8e839e8af8cba31d8bdf30c8fbd5cd4
applets/wf-window-list/__init__.py
@@ -134,9 +134,10 @@ class WindowButtonOptions:
self.max_width = max_width class WindowButtonLayoutManager(Gtk.LayoutManager): def __init__(self, options: WindowButtonOptions, **kwargs):def __init__(self, options: WindowButtonOptions, button: Gtk.ToggleButton, **kwargs):super().__init__(**kwargs) self.options = options self.button = buttondef do_measure(self, widget, orientation, for_size): child = widget.get_first_child()
@@ -162,7 +163,11 @@ class WindowButtonLayoutManager(Gtk.LayoutManager):
alloc.width = alloc_width alloc.height = height child.allocate(alloc.width, alloc.height, baseline) if alloc.width > 0 and alloc.height > 0 and self.button.window_list.wl_surface: x, y = widget.translate_coordinates(self.button.get_root(), 0, 0) x = int(x) y = int(y) self.button.window_id.set_rectangle(self.button.window_list.wl_surface, x, y, alloc.width, alloc.height)class WindowButton(Gtk.ToggleButton): def __init__(self, window_id, window_title, window_list, **kwargs):
@@ -252,31 +257,26 @@ class WindowButton(Gtk.ToggleButton):
return True def on_button_enter(self, button, x, y): print("on_button_enter")if self.popover_open: return view_id = self.wf_ipc_id #self.window_list.set_live_preview_output_name("live-preview-" + str(view_id))message = self.window_list.get_msg_template("live_previews/request_stream") message["data"]["id"] = view_id self.wf_sock.send_json(message) def on_button_leave(self, button): print("on_button_leave")if self.popover_open: return message = self.window_list.get_msg_template("live_previews/release_output") self.wf_sock.send_json(message) self.window_list.set_live_preview_output(None) #self.window_list.set_live_preview_output_name(None)def show_menu(self, gesture, n_presses, x, y): print("show_menu")self.on_button_leave(None)self.popover_open = True self.popover_menu.popup() def hide_menu(self, popover): print("hide_menu")self.popover_open = False self.popover_menu.popdown()
@@ -436,7 +436,7 @@ class WFWindowList(panorama_panel.Applet):
self.my_output = None self.wl_surface = None self.compositor = None self.seat = Noneself.wl_seat = Noneself.output = None self.wl_shm = None #self.manager = None
@@ -580,37 +580,26 @@ class WFWindowList(panorama_panel.Applet):
self.remove(button) def get_wl_resources(self, widget): if self.wf_socket is not None: self.filter_to_wf_workspace()if self.wl_shm: return self.my_output = None #self.wl_surface = self.get_root().get_application().wl_surfaceself.compositor = self.get_root().get_application().compositorself.seat = self.get_root().get_application().seatself.wl_seat = self.get_root().get_application().wl_seatself.output = None self.wl_shm = self.get_root().get_application().wl_shm #self.manager = self.get_root().get_application().managerself.screencopy_manager = self.get_root().get_application().screencopy_manager self.live_preview_output_name = None if self.wf_socket is not None:self.filter_to_wf_workspace()def set_live_preview_output(self, output): self.output = output def get_live_preview_output(self): return self.output #def set_live_preview_output_name(self, name):# self.live_preview_output_name = name##def get_live_preview_output_name(self):# return self.live_preview_output_namedef wl_output_enter(self, output, name): print("wl_output_enter")self.set_live_preview_output(None) if name.startswith("live-preview"): self.set_live_preview_output(output)
@@ -633,19 +622,7 @@ class WFWindowList(panorama_panel.Applet):
if output != button.output: self.foreign_toplevel_closed(handle) def wl_output_leave(self, output, name):print("wl_output_leave")if self.get_live_preview_output() == output:self.set_live_preview_output(None)returnif name.startswith("live-preview"):returntoplevel_buttons = self.toplevel_buttons.copy()for handle in toplevel_buttons:self.foreign_toplevel_closed(handle)def foreign_toplevel_output_enter(self, handle, output): print("foreign_toplevel_output_enter")if self.show_only_this_output and (not hasattr(output, "name") or output.name != self.get_root().monitor_name): return
@@ -655,15 +632,13 @@ class WFWindowList(panorama_panel.Applet):
button.output = output self.set_title(button, handle.title) button.set_group(self.initial_button) button.set_layout_manager(WindowButtonLayoutManager(self.window_button_options))button.set_layout_manager(WindowButtonLayoutManager(self.window_button_options, button))button.connect("clicked", self.on_button_click) self.set_app_id(button, handle.app_id) self.toplevel_buttons[handle] = button self.append(button) self.set_all_rectangles()def foreign_toplevel_output_leave(self, handle, output): print("foreign_toplevel_output_leave")if handle in self.toplevel_buttons: button = self.toplevel_buttons[handle] if button.get_parent() == self:
@@ -696,12 +671,12 @@ class WFWindowList(panorama_panel.Applet):
# Set a rectangle for animation button.window_id.set_rectangle(self.wl_surface, *get_widget_rect(button)) if button.window_state.minimised: button.window_id.activate(self.seat)button.window_id.activate(self.wl_seat)else: if button.window_state.focused: button.window_id.set_minimized() else: button.window_id.activate(self.seat)button.window_id.activate(self.wl_seat)def foreign_toplevel_state(self, handle, states): if handle in self.toplevel_buttons:
@@ -713,8 +688,6 @@ class WFWindowList(panorama_panel.Applet):
else: self.initial_button.set_active(True) self.set_all_rectangles()def foreign_toplevel_refresh(self): for button in self.toplevel_buttons.values(): self.remove(button)
@@ -813,9 +786,6 @@ class WFWindowList(panorama_panel.Applet):
"show_only_this_output": self.show_only_this_output, } def output_changed(self):self.get_wl_resources()def make_draggable(self): for button in self.toplevel_buttons.values(): button.remove_controller(button.drag_source)
main.py
@@ -32,7 +32,7 @@ from pathlib import Path
import ruamel.yaml as yaml from pywayland.client import Display, EventQueue from pywayland.protocol.wayland.wl_output import WlOutputProxy from pywayland.protocol.wayland import WlRegistry, WlSeat, WlSurface, WlCompositor, WlOutput, WlShmfrom pywayland.protocol.wayland import WlRegistry, WlSeat, WlSurface, WlOutput, WlShmfrom pywayland.protocol.wlr_foreign_toplevel_management_unstable_v1 import ( ZwlrForeignToplevelManagerV1, ZwlrForeignToplevelHandleV1
@@ -580,13 +580,6 @@ class Panel(Gtk.Window):
applet.foreign_toplevel_output_enter(toplevel, output) applet = applet.get_next_sibling() def foreign_toplevel_output_leave(self, toplevel, output):for area in (self.left_area, self.centre_area, self.right_area):applet = area.get_first_child()while applet:applet.foreign_toplevel_output_leave(toplevel, output)applet = applet.get_next_sibling()def foreign_toplevel_app_id(self, toplevel, app_id): for area in (self.left_area, self.centre_area, self.right_area): applet = area.get_first_child()
@@ -677,17 +670,13 @@ class PanoramaPanel(Gtk.Application):
) self.display = Gdk.Display.get_default() self.monitors = self.display.get_monitors() for monitor in self.monitors:monitor.output_proxy = Noneself.applets_by_name: dict[str, panorama_panel.Applet] = {} self.panels: list[Panel] = [] self.wl_globals = [] self.wl_display = None self.compositor = Noneself.seat = Noneself.output = Noneself.wl_seat = Noneself.wl_shm = None self.manager = Noneself.foreign_toplevel_manager = Noneself.screencopy_manager = None self.manager_window = None self.edit_mode = False
@@ -707,7 +696,6 @@ class PanoramaPanel(Gtk.Application):
def on_new_toplevel(self, manager: ZwlrForeignToplevelManagerV1, handle: ZwlrForeignToplevelHandleV1): print("on_new_toplevel")for panel in self.panels: panel.foreign_toplevel_new(manager, handle) handle.dispatcher["title"] = self.on_title_changed
@@ -717,14 +705,14 @@ class PanoramaPanel(Gtk.Application):
handle.dispatcher["state"] = self.on_state_changed handle.dispatcher["closed"] = self.on_closed def on_output_leave(self, handle, output):for panel in self.panels:panel.foreign_toplevel_output_leave(handle, output)def on_output_enter(self, handle, output): for panel in self.panels: panel.foreign_toplevel_output_enter(handle, output) def on_output_leave(self, handle, output): for panel in self.panels: panel.foreign_toplevel_output_leave(handle, output) def on_title_changed(self, handle, title): for panel in self.panels: panel.foreign_toplevel_title(handle, title)
@@ -741,6 +729,10 @@ class PanoramaPanel(Gtk.Application):
for panel in self.panels: panel.foreign_toplevel_closed(handle) def foreign_toplevel_refresh(self): for panel in self.panels: panel.foreign_toplevel_refresh() def receive_output_name(self, output: WlOutput, name: str): output.name = name
@@ -759,7 +751,6 @@ class PanoramaPanel(Gtk.Application):
def on_global(self, registry, name, interface, version): if interface == "zwlr_foreign_toplevel_manager_v1": print("Interface registered")self.foreign_toplevel_manager_id = name self.foreign_toplevel_version = version elif interface == "wl_output":
@@ -779,32 +770,26 @@ class PanoramaPanel(Gtk.Application):
for panel in self.panels: panel.wl_output_enter(output, output.name) if not output.name.startswith("live-preview"): if self.manager:self.manager.destroy()for panel in self.panels:panel.foreign_toplevel_refresh()self.wl_globals.remove(self.manager)self.manager = self.registry.bind(self.foreign_toplevel_manager_id, ZwlrForeignToplevelManagerV1, self.foreign_toplevel_version)self.manager.id = self.foreign_toplevel_manager_idself.wl_globals.append(self.manager)self.manager.dispatcher["toplevel"] = self.on_new_toplevelself.manager.dispatcher["finished"] = lambda *a: print("Toplevel manager finished")if self.foreign_toplevel_manager: self.foreign_toplevel_manager.destroy() self.wl_globals.remove(self.foreign_toplevel_manager) self.foreign_toplevel_refresh() self.foreign_toplevel_manager = self.registry.bind(self.foreign_toplevel_manager_id, ZwlrForeignToplevelManagerV1, self.foreign_toplevel_version) self.foreign_toplevel_manager.id = self.foreign_toplevel_manager_id self.wl_globals.append(self.foreign_toplevel_manager) self.foreign_toplevel_manager.dispatcher["toplevel"] = self.on_new_toplevel self.foreign_toplevel_manager.dispatcher["finished"] = lambda *a: print("Toplevel manager finished")self.wl_display.roundtrip() self.panels_generated = True print(f"Monitor {name} ({interface}) connected", file=sys.stderr)print(f"Monitor {output.name} ({interface}) connected", file=sys.stderr)elif interface == "wl_seat": print("Seat found")self.seat = registry.bind(name, WlSeat, version)self.seat.id = nameself.wl_globals.append(self.seat)self.wl_seat = registry.bind(name, WlSeat, version) self.wl_seat.id = name self.wl_globals.append(self.wl_seat)elif interface == "wl_shm": self.wl_shm = registry.bind(name, WlShm, version) self.wl_shm.id = name self.wl_globals.append(self.wl_shm) elif interface == "wl_compositor":self.compositor = registry.bind(name, WlCompositor, version)self.compositor.id = nameself.wl_globals.append(self.compositor)elif interface == "zwlr_screencopy_manager_v1": self.screencopy_manager = registry.bind(name, ZwlrScreencopyManagerV1, version) self.screencopy_manager.id = name
@@ -813,12 +798,6 @@ class PanoramaPanel(Gtk.Application):
def on_global_remove(self, registry, name): if name in self.wl_output_ids: print(f"Monitor {name} disconnected", file=sys.stderr) #self.generate_panels()#for g in self.wl_globals:# if g.id == name:# for panel in self.panels:# panel.wl_output_leave(g, g.name)# breakself.wl_output_ids.discard(name) for g in self.wl_globals: if g.id == name:
@@ -841,7 +820,6 @@ class PanoramaPanel(Gtk.Application):
yaml_loader = yaml.YAML(typ="rt") yaml_file = yaml_loader.load(config_file) for panel_data in yaml_file["panels"]: print(f"comparing {panel_data["monitor"]} =? {output_name}")if panel_data["monitor"] != output_name: continue position = PANEL_POSITIONS[panel_data["position"]]
@@ -882,11 +860,6 @@ class PanoramaPanel(Gtk.Application):
panel.realize() panel.monitor_name = my_monitor.get_connector() def prepare(self):self.release()print("started", file=sys.stderr)def do_startup(self): Gtk.Application.do_startup(self)
@@ -908,9 +881,6 @@ class PanoramaPanel(Gtk.Application):
ffi.cast("void *", ctypes.pythonapi.PyCapsule_GetPointer(self.display.__gpointer__, None))) self.wl_display._ptr = wl_display_ptr # Intentionally commented: the display is already connected by GTK# self.display.connect() # Iterate through monitors and get their Wayland output (wl_output)self.panels_generated = False self.got_output_name = False self.foreign_toplevel_manager_id = None
@@ -921,6 +891,10 @@ class PanoramaPanel(Gtk.Application):
self.registry.dispatcher["global"] = self.on_global self.registry.dispatcher["global_remove"] = self.on_global_remove self.wl_display.roundtrip() if self.foreign_toplevel_version: print("Foreign Toplevel Interface: Found") else: print("Foreign Toplevel Interface: Not Found")self.hold()
shared/panorama_panel.py
@@ -78,9 +78,6 @@ class Applet(Gtk.Box):
def foreign_toplevel_output_enter(self, toplevel, output): pass def foreign_toplevel_output_leave(self, toplevel, output):passdef foreign_toplevel_app_id(self, toplevel, app_id): pass