davatorium / rofi

Rofi: A window switcher, application launcher and dmenu replacement
https://davatorium.github.io/rofi/
Other
13.04k stars 609 forks source link

[BUG] rofi does not spawn .desktop files with dbus activation #1924

Open russelltg opened 9 months ago

russelltg commented 9 months ago

It's possible this is a feature request and not bug (felt borderline), so feel free to update :)

Rofi version (rofi -v)

1.7.5

Configuration

https://gist.github.com/russelltg/302aa2af722d3c43192f53ee553da731

Theme

https://gist.github.com/russelltg/eb33d77bbb4645483bf2911fe2f507cb

Timing report

No response

Launch command

rofi -show drun

Step to reproduce

  1. Find a .desktop file with a different commandline for dbus activation and non-dbus activation. I use the easyeffects flatpak.
  2. Spawn it with rofi -show drun
  3. Check the commandline used. I use ps aux | grep easyeffects

Expected behavior

It uses d-bus activation. In the case of easyeffects, this means that the commndline is easyeffects --gapplication-service

Actual behavior

dbus activation is not use. In the case of easyeffects, the commandline is just easyeffects

Additional information

The author of easyeffects told me (https://github.com/wwmm/easyeffects/issues/1215#issuecomment-1825759453) that rofi should use dbus activation. It seems gnome, kde, and gtk-launch all launch properly with dbus activation.

Using wayland display server protocol

I've checked if the issue exists in the latest stable release

DaveDavenport commented 9 months ago

Can you attach easyeffects desktop file?

russelltg commented 9 months ago

https://gist.github.com/russelltg/e432828354187ee7abc76620bff0b69e

g_app_info_launch seems to be the GIO call that gtk-launch uses

DaveDavenport commented 9 months ago

A quick fix would be to change the Exec line to Exec=gapplication launch appidforeasyeffects

I see this is how most applications setup their .desktop file.

russelltg commented 9 months ago

Could you send me the examples so I can pass them along to easyeffects folks? (or is it more of a hack and I just should do it locally)

DaveDavenport commented 9 months ago
diff --git a/source/modes/drun.c b/source/modes/drun.c
index b05ce48e..ea28807b 100644
--- a/source/modes/drun.c
+++ b/source/modes/drun.c
@@ -44,6 +44,9 @@
 #include <sys/types.h>
 #include <unistd.h>

+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+
 #include "helper.h"
 #include "history.h"
 #include "mode-private.h"
@@ -321,6 +324,7 @@ static void launch_link_entry(DRunModeEntry *e) {
   g_snprintf(command, command_len, "%s %s", config.drun_url_launcher, url);
   g_free(url);

+
   g_debug("Link launch command: |%s|", command);
   if (helper_execute_command(NULL, command, FALSE, NULL)) {
     char *path = g_build_filename(cache_dir, DRUN_CACHE_FILE, NULL);
@@ -398,15 +402,28 @@ static void exec_cmd_entry(DRunModeEntry *e, const char *path) {
         g_key_file_get_string(e->key_file, e->action, "StartupWMClass", NULL);
   }

-  // Returns false if not found, if key not found, we don't want run in
-  // terminal.
-  gboolean terminal =
-      g_key_file_get_boolean(e->key_file, e->action, "Terminal", NULL);
-  if (helper_execute_command(exec_path, fp, terminal, sn ? &context : NULL)) {
-    char *drun_cach_path = g_build_filename(cache_dir, DRUN_CACHE_FILE, NULL);
-    // Store it based on the unique identifiers (desktop_id).
-    history_set(drun_cach_path, e->desktop_id);
-    g_free(drun_cach_path);
+  if ( g_key_file_get_boolean ( e->key_file, e->action, "DBusActivatable", NULL) ) {
+         GAppInfo *info = G_APP_INFO(g_desktop_app_info_new_from_keyfile(e->key_file));
+         printf("Desktop dbus launch\r\n");
+         if ( g_app_info_launch(info, NULL, NULL, NULL ) ) {
+                 char *drun_cach_path = g_build_filename(cache_dir, DRUN_CACHE_FILE, NULL);
+                 // Store it based on the unique identifiers (desktop_id).
+                 history_set(drun_cach_path, e->desktop_id);
+                 g_free(drun_cach_path);
+         }
+         g_object_unref(info);
+  } else {
+
+         // Returns false if not found, if key not found, we don't want run in
+         // terminal.
+         gboolean terminal =
+                 g_key_file_get_boolean(e->key_file, e->action, "Terminal", NULL);
+         if (helper_execute_command(exec_path, fp, terminal, sn ? &context : NULL)) {
+                 char *drun_cach_path = g_build_filename(cache_dir, DRUN_CACHE_FILE, NULL);
+                 // Store it based on the unique identifiers (desktop_id).
+                 history_set(drun_cach_path, e->desktop_id);
+                 g_free(drun_cach_path);
+         }
   }
   g_free(wmclass);
   g_free(exec_path);

For me it does not launch it with --gapplication-service, maybe I need the right GAppLaunchContext.

russelltg commented 9 months ago

Maybe https://github.com/linuxmint/gtk/blob/master/gtk/gtk-launch.c#L142C1-L142C105?

DaveDavenport commented 9 months ago

Saw that, but I do not really want to pull in Gdk as an dependency.

DaveDavenport commented 9 months ago

Saw that, but I do not really want to pull in Gdk as an dependency.

DaveDavenport commented 9 months ago

Maybe the easiest is to just call gtk-launch with the path to the desktop file and action/arguments.

DaveDavenport commented 9 months ago

Might have a solution by going to low-level dbus.. Will post a patch later.

DaveDavenport commented 9 months ago

Not something usable yet, but good to do some tests..

diff --git a/source/modes/drun.c b/source/modes/drun.c
index b05ce48e..57936556 100644
--- a/source/modes/drun.c
+++ b/source/modes/drun.c
@@ -44,6 +44,8 @@
 #include <sys/types.h>
 #include <unistd.h>

+#include <gio/gio.h>
+
 #include "helper.h"
 #include "history.h"
 #include "mode-private.h"
@@ -329,6 +331,37 @@ static void launch_link_entry(DRunModeEntry *e) {
     g_free(path);
   }
 }
+static gchar *app_path_for_id(const gchar *app_id) {
+  gchar *path;
+  gint i;
+
+  path = g_strconcat("/", app_id, NULL);
+  for (i = 0; path[i]; i++) {
+    if (path[i] == '.')
+      path[i] = '/';
+    if (path[i] == '-')
+      path[i] = '_';
+  }
+
+  return path;
+}
+static GVariant *app_get_platform_data(void) {
+  GVariantBuilder builder;
+  const gchar *startup_id;
+
+  g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+
+  if ((startup_id = g_getenv("DESKTOP_STARTUP_ID")))
+    g_variant_builder_add(&builder, "{sv}", "desktop-startup-id",
+                          g_variant_new_string(startup_id));
+
+  if ((startup_id = g_getenv("XDG_ACTIVATION_TOKEN")))
+    g_variant_builder_add(&builder, "{sv}", "activation-token",
+                          g_variant_new_string(startup_id));
+
+  return g_variant_builder_end(&builder);
+}
+
 static void exec_cmd_entry(DRunModeEntry *e, const char *path) {
   GError *error = NULL;
   GRegex *reg = g_regex_new("%[a-zA-Z%]", 0, 0, &error);
@@ -398,15 +431,66 @@ static void exec_cmd_entry(DRunModeEntry *e, const char *path) {
         g_key_file_get_string(e->key_file, e->action, "StartupWMClass", NULL);
   }

-  // Returns false if not found, if key not found, we don't want run in
-  // terminal.
-  gboolean terminal =
-      g_key_file_get_boolean(e->key_file, e->action, "Terminal", NULL);
-  if (helper_execute_command(exec_path, fp, terminal, sn ? &context : NULL)) {
-    char *drun_cach_path = g_build_filename(cache_dir, DRUN_CACHE_FILE, NULL);
-    // Store it based on the unique identifiers (desktop_id).
-    history_set(drun_cach_path, e->desktop_id);
-    g_free(drun_cach_path);
+  if (g_key_file_get_boolean(e->key_file, e->action, "DBusActivatable", NULL)) {
+    printf("Try dbus launch %s\r\n", e->app_id);
+    GDBusConnection *session;
+    GError *error = NULL;
+    gchar *object_path;
+    GVariant *result;
+
+    session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
+    if (!session) {
+      fprintf(stderr, ("unable to connect to D-Bus: %s\n"), error->message);
+      g_error_free(error);
+      return;
+    }
+
+    object_path = app_path_for_id(e->app_id);
+
+    GVariantBuilder files;
+
+    g_variant_builder_init(&files, G_VARIANT_TYPE_STRING_ARRAY);
+
+    GVariant *params = NULL;
+    const char *method = "Activate";
+    if (path != NULL) {
+      method = "Open";
+      params = g_variant_new("(as@a{sv})", &files, app_get_platform_data());
+    } else {
+      params = g_variant_new("(@a{sv})", app_get_platform_data());
+    }
+    if (path) {
+      GFile *file = g_file_new_for_commandline_arg(path);
+      g_variant_builder_add_value(
+          &files, g_variant_new_take_string(g_file_get_uri(file)));
+      g_object_unref(file);
+    }
+    result = g_dbus_connection_call_sync(
+        session, e->app_id, object_path, "org.freedesktop.Application", method,
+        params, G_VARIANT_TYPE_UNIT, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+
+    g_free(object_path);
+
+    if (result) {
+      g_variant_unref(result);
+    } else {
+      fprintf(stderr, ("error sending %s message to application: %s\n"), "Open",
+              error->message);
+      g_error_free(error);
+    }
+    g_object_unref(session);
+  } else {
+
+    // Returns false if not found, if key not found, we don't want run in
+    // terminal.
+    gboolean terminal =
+        g_key_file_get_boolean(e->key_file, e->action, "Terminal", NULL);
+    if (helper_execute_command(exec_path, fp, terminal, sn ? &context : NULL)) {
+      char *drun_cach_path = g_build_filename(cache_dir, DRUN_CACHE_FILE, NULL);
+      // Store it based on the unique identifiers (desktop_id).
+      history_set(drun_cach_path, e->desktop_id);
+      g_free(drun_cach_path);
+    }
   }
   g_free(wmclass);
   g_free(exec_path);

Does not support actions yet and does not fall back to classic style when failed.