Enhance Linux hotkey registration: add error notifications and support for parent window in portal shortcuts

This commit is contained in:
John Doe
2026-07-14 15:25:41 -07:00
parent 858f62c916
commit ed6d00c730
2 changed files with 27 additions and 7 deletions
+21 -4
View File
@@ -488,6 +488,14 @@ func (a *App) SaveSettings(settings map[string]interface{}) string {
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
a.hotkeyListener.SetRegistrationErrorCallback(func(err error) { a.hotkeyListener.SetRegistrationErrorCallback(func(err error) {
logger.Error("Linux hotkey registration failed: %v", err) logger.Error("Linux hotkey registration failed: %v", err)
// Show notification for hotkey registration failures
go func() {
time.Sleep(500 * time.Millisecond)
if a.overlay != nil {
a.overlay.Show("Shortcut registration failed. Use the command shown in Settings for a desktop shortcut.")
}
logger.Info("HOTKEY SETUP: Portal failed. Add a custom GNOME/KDE shortcut with: wisp-open --action=toggle")
}()
}) })
} }
a.hotkeyListener.Start() a.hotkeyListener.Start()
@@ -655,17 +663,26 @@ func (a *App) startupHeadless() {
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
a.hotkeyListener.SetRegistrationErrorCallback(func(err error) { a.hotkeyListener.SetRegistrationErrorCallback(func(err error) {
logger.Error("Linux hotkey registration failed: %v", err) logger.Error("Linux hotkey registration failed: %v", err)
// Always show an error notification when portal registration fails
// so the user knows to use the fallback method
go func() { go func() {
time.Sleep(2 * time.Second) // Show notification immediately via tray if available
if a.overlay != nil { if err != nil {
a.overlay.Show("Shortcut registration failed. Add a custom system shortcut with the command shown in Settings.") logger.Error("Portal hotkey error (will show notification): %v", err)
} }
// Also try to show overlay if window is visible
time.Sleep(500 * time.Millisecond)
if a.overlay != nil {
a.overlay.Show("Shortcut registration failed. Use the command shown in Settings for a desktop shortcut.")
}
// Log a clear instruction for the fallback method
logger.Info("HOTKEY SETUP: Portal failed. Add a custom GNOME/KDE shortcut with: wisp-open --action=toggle")
}() }()
}) })
} }
a.hotkeyListener.Start() a.hotkeyListener.Start()
} else { } else {
logger.Info("Linux portal hotkey disabled; use the command shown in Settings for a desktop shortcut") logger.Info("Linux portal hotkey disabled via WISFREE_USE_PORTAL_HOTKEY=0; use the command shown in Settings for a desktop shortcut")
} }
logger.Info("Components initialized successfully!") logger.Info("Components initialized successfully!")
+6 -3
View File
@@ -273,12 +273,14 @@ func (hk *Hotkey) registerPortal() error {
} }
type portalShortcut struct { type portalShortcut struct {
ID string ID string
Details map[string]dbus.Variant ParentWindow string // Required: empty string or window handle for the parent window
Details map[string]dbus.Variant
} }
shortcutsArg := []portalShortcut{ shortcutsArg := []portalShortcut{
{ {
ID: wisfreeGlobalShortcutID, ID: wisfreeGlobalShortcutID,
ParentWindow: "", // Empty string since we have no focused window handle
Details: map[string]dbus.Variant{ Details: map[string]dbus.Variant{
"description": dbus.MakeVariant("Hold to dictate; release to transcribe (WIS Free)"), "description": dbus.MakeVariant("Hold to dictate; release to transcribe (WIS Free)"),
"preferred_trigger": dbus.MakeVariant(trigger), "preferred_trigger": dbus.MakeVariant(trigger),
@@ -291,6 +293,7 @@ func (hk *Hotkey) registerPortal() error {
} }
var bindReqPath dbus.ObjectPath var bindReqPath dbus.ObjectPath
// NOTE: BindShortcuts expects shortcuts as a(ssa{sv}) - array of structs with (string, string, dict)
if err := portal.Call(ifaceGlobalShortcuts+".BindShortcuts", 0, sessPath, shortcutsArg, "", bindOpts).Store(&bindReqPath); err != nil { if err := portal.Call(ifaceGlobalShortcuts+".BindShortcuts", 0, sessPath, shortcutsArg, "", bindOpts).Store(&bindReqPath); err != nil {
_ = conn.Object(portalBusName, sessPath).Call(ifaceSession+".Close", 0).Store() _ = conn.Object(portalBusName, sessPath).Call(ifaceSession+".Close", 0).Store()
_ = conn.Close() _ = conn.Close()