@@ -30,6 +30,9 @@ const (
3030 notificationServiceObj = "/org/freedesktop/Notifications"
3131 notificationServiceInterface = "org.freedesktop.Notifications"
3232 signalActionInvoked = "org.freedesktop.Notifications.ActionInvoked"
33+ desktopPortalPath = "/org/freedesktop/portal/desktop"
34+ desktopPortalName = "org.freedesktop.portal.Desktop"
35+ methodOpenUri = "org.freedesktop.portal.OpenURI.OpenURI"
3336)
3437
3538// We default to xdg-open first because, if available, it appears to be better at picking
@@ -57,6 +60,33 @@ func NewDesktopNotifier(slogger *slog.Logger, iconFilepath string, localizationP
5760 }
5861}
5962
63+ // OpenViaDbus opens the given URI via call to XDG Desktop Portal's OpenURI method.
64+ // We treat it as successful if the portal accepts; we do not wait for the response.
65+ // See: https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.OpenURI.html
66+ func OpenViaDbus (conn * dbus.Conn , uri string ) error {
67+ if conn == nil {
68+ return errors .New ("no connection available" )
69+ }
70+
71+ desktopPortal := conn .Object (
72+ desktopPortalName ,
73+ desktopPortalPath ,
74+ )
75+
76+ var requestHandle dbus.ObjectPath
77+ if err := desktopPortal .Call (
78+ methodOpenUri ,
79+ 0 ,
80+ "" , // parent_window
81+ uri , // uri
82+ map [string ]dbus.Variant {}, // options
83+ ).Store (& requestHandle ); err != nil {
84+ return fmt .Errorf ("calling OpenURI: %w" , err )
85+ }
86+
87+ return nil
88+ }
89+
6090func (d * dbusNotifier ) Execute () error {
6191 if d .conn != nil {
6292 if err := d .conn .AddMatchSignal (
@@ -100,6 +130,17 @@ func (d *dbusNotifier) Execute() error {
100130 // Attempt to open a browser to the given URL
101131 actionUri := signal .Body [1 ].(string )
102132
133+ // Try via dbus before falling back to xdg-open and www-browser --
134+ // we see improved behavior when using dbus.
135+ err := OpenViaDbus (d .conn , actionUri )
136+ if err == nil {
137+ continue
138+ }
139+ d .slogger .Log (context .TODO (), slog .LevelWarn ,
140+ "couldn't open URI via dbus, falling back to exec" ,
141+ "err" , err ,
142+ )
143+
103144 for _ , browserLauncher := range browserLaunchers {
104145 cmd , err := browserLauncher .Cmd (context .TODO (), actionUri )
105146 if err != nil {
@@ -137,10 +178,21 @@ func (d *dbusNotifier) Interrupt(err error) {
137178
138179 if d .conn != nil {
139180 d .conn .RemoveSignal (d .signal )
140- d .conn .RemoveMatchSignal (
181+ if err := d .conn .RemoveMatchSignal (
141182 dbus .WithMatchObjectPath (notificationServiceObj ),
142183 dbus .WithMatchInterface (notificationServiceInterface ),
143- )
184+ ); err != nil {
185+ d .slogger .Log (context .TODO (), slog .LevelWarn ,
186+ "could not remove match signal during shutdown" ,
187+ "err" , err ,
188+ )
189+ }
190+ if err := d .conn .Close (); err != nil {
191+ d .slogger .Log (context .TODO (), slog .LevelWarn ,
192+ "could not close session bus connection during shutdown" ,
193+ "err" , err ,
194+ )
195+ }
144196 }
145197}
146198
0 commit comments