Loading cursor persists after clicking notification on GNOME/Wayland
#61 opened on Mar 31, 2026
Repository metrics
- Stars
- (759 stars)
- PR merge metrics
- (PR metrics pending)
Description
Problem
On Ubuntu 24.04 (GNOME/Wayland), clicking a notification from claude-notifications causes the mouse cursor to change to a loading spinner for ~15 seconds, even though click-to-focus works correctly and the terminal window is focused immediately.
Root Cause
D-Bus monitoring (dbus-monitor --session "interface='org.freedesktop.Notifications'") reveals the issue:
signal member=ActivationToken
uint32 162
string "gnome-shell/Visual Studio Code/4019-22-gram_TIME139555122"
signal member=ActionInvoked
uint32 162
string "default"
signal member=NotificationClosed
uint32 162
uint32 2
When the user clicks a notification, GNOME Shell emits three signals in order:
ActivationToken— GNOME Shell creates a Waylandxdg-activation-v1token for the app specified in thedesktop-entryhint (e.g., "Visual Studio Code"). This is where the loading cursor starts.ActionInvoked— Our daemon receives this signal and successfully focuses the correct window.NotificationClosed— The notification is dismissed.
The problem: GNOME Shell creates an activation token expecting the target app (e.g., VS Code) to consume it via the xdg-activation-v1 Wayland protocol. However, the target app never receives or consumes this token — our daemon handles the focus instead. The unconsumed token causes GNOME Shell to show the loading cursor until its startup notification timeout (~15 seconds).
The desktop-entry hint was added in PR #42 to fix a bug where clicking a notification opened Nautilus instead of the terminal. The hint correctly tells GNOME Shell which app the notification belongs to, but it also triggers the activation token behavior.
Proposed Solution
Create a claude-notifications.desktop file with StartupNotify=false and use it as the desktop-entry hint instead of the terminal's desktop entry.
Desktop file (~/.local/share/applications/claude-notifications.desktop):
[Desktop Entry]
Name=Claude Notifications
Type=Application
Icon=utilities-terminal
Exec=true
NoDisplay=true
StartupNotify=false
Terminal=false
Code change in internal/daemon/server.go:
Hints: map[string]dbus.Variant{
"desktop-entry": dbus.MakeVariant("claude-notifications"),
"suppress-sound": dbus.MakeVariant(true),
},
Why this works:
desktop-entryis still present → GNOME Shell knows which app the notification belongs to → no Nautilus fallback (PR #42 fix preserved)StartupNotify=false→ GNOME Shell does NOT create an activation token → no loading cursor- Our daemon still receives
ActionInvokedsignal and handles window focus correctly NoDisplay=true→ the desktop entry doesn't appear in app launchers
Trade-off: Notifications will show as "Claude Notifications" in GNOME's notification panel instead of being grouped under the terminal app. This is acceptable since the daemon handles focus correctly via ActionInvoked.
Environment
- Ubuntu 24.04
- GNOME Shell on Wayland
- claude-notifications-go with click-to-focus enabled