From 467c0225259184d830a383513f64b1c8dac2c915 Mon Sep 17 00:00:00 2001 From: nevaforget Date: Fri, 27 Mar 2026 14:13:32 +0100 Subject: [PATCH] fix: panel on all monitors with keyboard exclusive Instead of guessing the primary monitor (unreliable on Niri), show the panel with action buttons on every monitor. All get keyboard exclusive so the focused monitor captures input. Also set exclusive_zone -1 to overlay Waybar, and delay initial focus grab via GLib.idle_add for layer shell readiness. --- src/moonset/main.py | 37 +++++++++---------------------------- src/moonset/panel.py | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/moonset/main.py b/src/moonset/main.py index 8eeb764..8c3931a 100644 --- a/src/moonset/main.py +++ b/src/moonset/main.py @@ -58,7 +58,7 @@ class MoonsetApp(Gtk.Application): def __init__(self) -> None: super().__init__(application_id="dev.moonarch.moonset") - self._secondary_windows: list[WallpaperWindow] = [] + self._windows: list[PanelWindow] = [] def do_activate(self) -> None: """Create and present power menu windows on all monitors.""" @@ -73,37 +73,17 @@ class MoonsetApp(Gtk.Application): config = load_config() bg_path = resolve_background_path(config) + # Panel window on every monitor — keyboard exclusive on all so + # the focused monitor always captures input monitors = display.get_monitors() - primary_monitor = None - - # Find primary monitor — fall back to first available for i in range(monitors.get_n_items()): monitor = monitors.get_item(i) - if hasattr(monitor, "is_primary") and monitor.is_primary(): - primary_monitor = monitor - break - if primary_monitor is None and monitors.get_n_items() > 0: - primary_monitor = monitors.get_item(0) - - # Main power menu window on primary monitor - panel = PanelWindow(bg_path=bg_path, application=self) - if HAS_LAYER_SHELL: - self._setup_layer_shell(panel, keyboard=True) - if primary_monitor is not None: - Gtk4LayerShell.set_monitor(panel, primary_monitor) - panel.present() - - # Wallpaper-only windows on secondary monitors - for i in range(monitors.get_n_items()): - monitor = monitors.get_item(i) - if monitor == primary_monitor: - continue - wallpaper_win = WallpaperWindow(bg_path=bg_path, application=self) + panel = PanelWindow(bg_path=bg_path, application=self) if HAS_LAYER_SHELL: - self._setup_layer_shell(wallpaper_win, keyboard=False) - Gtk4LayerShell.set_monitor(wallpaper_win, monitor) - wallpaper_win.present() - self._secondary_windows.append(wallpaper_win) + self._setup_layer_shell(panel, keyboard=True) + Gtk4LayerShell.set_monitor(panel, monitor) + panel.present() + self._windows.append(panel) def _load_css(self, display: Gdk.Display) -> None: """Load the CSS stylesheet for the power menu.""" @@ -122,6 +102,7 @@ class MoonsetApp(Gtk.Application): """Configure gtk4-layer-shell for fullscreen OVERLAY display.""" Gtk4LayerShell.init_for_window(window) Gtk4LayerShell.set_layer(window, Gtk4LayerShell.Layer.OVERLAY) + Gtk4LayerShell.set_exclusive_zone(window, -1) if keyboard: Gtk4LayerShell.set_keyboard_mode( window, Gtk4LayerShell.KeyboardMode.EXCLUSIVE diff --git a/src/moonset/panel.py b/src/moonset/panel.py index 7ef9505..552e237 100644 --- a/src/moonset/panel.py +++ b/src/moonset/panel.py @@ -119,6 +119,9 @@ class PanelWindow(Gtk.ApplicationWindow): self._build_ui(bg_path) self._setup_keyboard() + # Focus the first action button once the window is mapped + self.connect("map", self._on_map) + def _build_ui(self, bg_path: Path) -> None: """Build the panel layout with wallpaper background and action buttons.""" # Main overlay for background + centered content @@ -196,6 +199,19 @@ class PanelWindow(Gtk.ApplicationWindow): controller.connect("key-pressed", self._on_key_pressed) self.add_controller(controller) + def _on_map(self, widget: Gtk.Widget) -> None: + """Grab focus on the first action button once the window is visible.""" + # Delay focus grab — layer shell keyboard interactivity may not + # be ready at map time + GLib.idle_add(self._grab_initial_focus) + + def _grab_initial_focus(self) -> bool: + """Set focus on the first action button.""" + first_button = self._button_box.get_first_child() + if first_button is not None: + first_button.grab_focus() + return GLib.SOURCE_REMOVE + def _on_key_pressed( self, controller: Gtk.EventControllerKey,