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.
This commit is contained in:
nevaforget 2026-03-27 14:13:32 +01:00
parent 2e359f358d
commit 467c022525
2 changed files with 25 additions and 28 deletions

View File

@ -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

View File

@ -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,