From eea198d882bb15aa8326af00af945626da627375 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 14 Jul 2026 16:26:15 -0700 Subject: [PATCH] Refactor portalSignalLoop: improve D-Bus signal handling by removing sender filter and enhancing logging for signal reception --- internal/xhotkey/hotkey_linux_portal.go | 30 ++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/internal/xhotkey/hotkey_linux_portal.go b/internal/xhotkey/hotkey_linux_portal.go index 0152018..56aac2a 100644 --- a/internal/xhotkey/hotkey_linux_portal.go +++ b/internal/xhotkey/hotkey_linux_portal.go @@ -455,9 +455,16 @@ func (hk *Hotkey) portalSignalLoop() { conn.Signal(ch) defer conn.RemoveSignal(ch) + // Note: we intentionally do NOT filter on `sender` here. A D-Bus signal's + // SENDER header is always the portal's *unique* connection name (:1.x), not + // the well-known org.freedesktop.portal.Desktop. Filtering the match-rule + // `sender` key on a well-known name is unreliable across dbus-daemon/libdbus + // versions and silently drops every signal. The interface filter plus the + // shortcut-ID body check below is sufficient and matches the reference + // implementations (e.g. Ghostty subscribes with sender=null). rule := fmt.Sprintf( - "type='signal',sender='%s',interface='%s'", - portalBusName, ifaceGlobalShortcuts, + "type='signal',interface='%s'", + ifaceGlobalShortcuts, ) if err := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule).Store(); err != nil { logger.Error("wis-free-v3 hotkey: AddMatch GlobalShortcuts: %v", err) @@ -465,7 +472,7 @@ func (hk *Hotkey) portalSignalLoop() { } defer func() { _ = conn.BusObject().Call("org.freedesktop.DBus.RemoveMatch", 0, rule).Store() }() - logger.Info("Listening for global shortcut signals (sender=%s, interface=%s)", portalBusName, ifaceGlobalShortcuts) + logger.Info("Listening for global shortcut signals (interface=%s)", ifaceGlobalShortcuts) for { select { @@ -479,11 +486,18 @@ func (hk *Hotkey) portalSignalLoop() { continue } - // The XDG GlobalShortcuts Activated/Deactivated signals carry a - // single argument of type a(su): an array of (id, state) structs. - // We match on our unique shortcut ID rather than on a specific - // body index, which is what the old code got wrong (it expected - // sig.Body[1] to be a string and silently dropped every signal). + // Diagnostic: confirm the daemon is actually delivering + // GlobalShortcuts signals to us (regardless of whether the ID + // matches our shortcut). + logger.Debug("wis-free-v3 hotkey: received signal %s (args=%d)", sig.Name, len(sig.Body)) + + // The XDG GlobalShortcuts Activated/Deactivated signals carry + // (session_handle o, shortcut_id s, timestamp t, options a{sv}). + // Older drafts used a single a(su) argument. We match on our + // unique shortcut ID across *all* body arguments rather than a + // specific index, which is what the old code got wrong (it + // expected sig.Body[1] to be a string and silently dropped every + // signal whenever the signature didn't match). if !portalSignalMatchesID(sig.Body, wisfreeGlobalShortcutID) { continue }