! Please note that this is a snapshot of our old Bugzilla server, which is read only since May 29, 2020. Please go to gitlab.xfce.org for our new server !
Implement D-BUS support for xfrun4
Status:
RESOLVED: FIXED
Severity:
enhancement
Product:
Xfce-utils

Comments

Description Jannis Pohlmann editbugs 2006-03-21 21:41:38 CET
Based on Benny's ideas, Jani Monoses submitted a patch to the xfce4-dev mailinglist. This implements D-BUS org.xfce.RunDialog lookup and should also include fallbacks if D-BUS is not present. 

I'm reporting this feature request for Brian to save some time (and to remember working on the patch later).
Comment 1 Jannis Pohlmann editbugs 2006-03-21 21:42:32 CET
Created attachment 481 
Header with D-BUS service description
Comment 2 Jannis Pohlmann editbugs 2006-03-21 21:43:35 CET
Created attachment 482 
Incomplete patch for xfrun
Comment 3 Jannis Pohlmann editbugs 2006-03-21 21:44:11 CET
Created attachment 483 
D-BUS service desktop file
Comment 4 Jannis Pohlmann editbugs 2006-03-27 13:47:56 CEST
Created attachment 487 
Patch for adding org.xfce.RunDialog capabilities to xfrun4

Redone patch for adding the org.xfce.RunDialog D-BUS service feature to xfrun4, originally contributed by Jani Monoses. Also fixes a little memleak.
Comment 5 Jannis Pohlmann editbugs 2006-03-27 14:07:45 CEST
Comment on attachment 487 
Patch for adding org.xfce.RunDialog capabilities to xfrun4

Index: xfce-utils/xfrun/org.xfce.RunDialog.service.in
===================================================================
--- xfce-utils/xfrun/org.xfce.RunDialog.service.in	(revision 0)
+++ xfce-utils/xfrun/org.xfce.RunDialog.service.in	(revision 0)
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.xfce.RunDialog
+Exec=@bindir@/xfrun4 --daemon
Index: xfce-utils/xfrun/xfrun.c
===================================================================
--- xfce-utils/xfrun/xfrun.c	(revision 20510)
+++ xfce-utils/xfrun/xfrun.c	(working copy)
@@ -2,6 +2,8 @@
  * xfrun - a simple quick run dialog with saved history and completion
  *
  * Copyright (c) 2006 Brian J. Tarricone <bjt23@cornell.edu>
+ *                    Jani Monoses <jani.monoses@gmail.com>
+ *                    Jannis Pohlmann <jannis@xfce.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -32,6 +34,20 @@
 #include <unistd.h>
 #endif
 
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_DBUS
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#define RUNDIALOG_DBUS_SERVICE       "org.xfce.RunDialog"
+#define RUNDIALOG_DBUS_INTERFACE     "org.xfce.RunDialog"
+#define RUNDIALOG_DBUS_PATH          "/org/xfce/RunDialog"
+#define RUNDIALOG_DBUS_METHOD_OPEN   "OpenDialog"
+#define RUNDIALOG_DBUS_ERROR_GENERAL "org.xfce.RunDialog.ErrorGeneral"
+#endif
+
 #include <gdk/gdkkeysyms.h>
 #include <gtk/gtk.h>
 
@@ -59,7 +75,200 @@
     XFRUN_N_COLS,
 };
 
+static XfrunDialog *xfrun_dialog;
 
+static void
+xfrun_shutdown (void)
+{
+  if (G_LIKELY (xfrun_dialog != NULL))
+    g_free (xfrun_dialog);
+}
+
+static void
+xfrun_dialog_show (GtkWidget *dialog)
+{
+  gtk_widget_show(dialog);
+  gtk_widget_set_size_request(dialog, -1, -1);
+}
+
+static gboolean daemon_mode = FALSE;
+
+static void
+xfrun_quit (GtkWidget *dialog)
+{
+  if (daemon_mode)
+    gtk_widget_hide (dialog);
+  else 
+    {
+      xfrun_shutdown ();
+      gtk_main_quit();
+    }
+}
+
+#ifdef HAVE_DBUS
+/*
+ * Call the org.xfce.RunDialog.OpenDialog(dir, display) method on whichever
+ * object owns that name on the session D-BUS
+ */
+static gboolean
+xfrun_remote_dialog (int argc, 
+                     char **argv)
+{
+  DBusConnection *connection;
+  DBusMessage    *method;
+  DBusMessage    *result;
+  DBusError       derror;
+
+  GdkScreen      *screen;
+
+  gchar          *directory;
+  gchar          *display;
+
+  gdk_init (&argc, &argv);
+
+  dbus_error_init (&derror);
+
+  /* Try establishing a connection to the session bus */
+  connection = dbus_bus_get (DBUS_BUS_SESSION, &derror);
+
+  if (connection == NULL) 
+    return FALSE;
+
+  /* Create org.xfce.RunDialog.OpenDialog method message */
+  method = dbus_message_new_method_call (RUNDIALOG_DBUS_SERVICE,
+                                         RUNDIALOG_DBUS_PATH,
+                                         RUNDIALOG_DBUS_INTERFACE,
+                                         RUNDIALOG_DBUS_METHOD_OPEN);
+
+  dbus_message_set_auto_start (method, TRUE);
+
+  /* Dete the active display */
+  screen = gdk_screen_get_default ();
+  display = gdk_screen_make_display_name (screen);
+
+  /* Dete working directory */
+  directory = g_get_current_dir ();
+
+  /* Append method arguments */
+  dbus_message_append_args (method, DBUS_TYPE_STRING, &directory,
+                                    DBUS_TYPE_STRING, &display,
+                                    DBUS_TYPE_INVALID);
+
+  /* Send D-BUS message */
+  result = dbus_connection_send_with_reply_and_block (connection, method, 2000, &derror);
+
+  /* Free data */
+  dbus_message_unref (method);
+  g_free (display);
+  g_free (directory);
+
+  if (result == NULL)
+    return FALSE;
+
+  dbus_message_unref (result);
+
+  return TRUE;
+}
+
+static DBusHandlerResult
+xfrun_handle_dbus_message (DBusConnection *connection,
+                           DBusMessage    *message,
+                           GtkWidget      *dialog)
+{
+  DBusMessage *reply;
+  DBusError    derror;
+  uid_t        user_id;
+  gchar       *directory;
+  gchar       *display;
+  GdkScreen   *screen;
+
+  if (dbus_message_is_method_call (message, 
+                                   RUNDIALOG_DBUS_INTERFACE,
+                                   RUNDIALOG_DBUS_METHOD_OPEN))
+    {
+      dbus_error_init (&derror);
+
+      /* Query the list of arguments to this call */
+      if (!dbus_message_get_args (message, &derror, 
+                                  DBUS_TYPE_STRING, &directory,
+                                  DBUS_TYPE_STRING, &display,
+                                  DBUS_TYPE_INVALID))
+        {
+          /* Reply an error message */
+          reply = dbus_message_new_error (message, RUNDIALOG_DBUS_ERROR_GENERAL, derror.message);
+
+          /* Free error data */
+          dbus_error_free (&derror);
+        }
+      else
+        {
+          /* Get default screen of display */
+          screen = gdk_display_get_default_screen (gdk_display_open (display));
+
+          /* Set screen for xfrun dialog */
+          gtk_window_set_screen (GTK_WINDOW (dialog), screen);
+          
+          /* Show xfrun dialog */
+          xfrun_dialog_show (dialog);
+
+          /* Reply a success message */
+          reply = dbus_message_new_method_return (message);
+        }
+
+      /* Send reply */
+      dbus_connection_send (connection, reply, NULL);
+
+      /* Destroy reply message object */
+      dbus_message_unref (reply);
+
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected"))
+    {
+      g_printerr (_("D-BUS message bus disconnected. Exiting ...\n"));
+      xfrun_quit (dialog);
+    }
+  else
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean
+xfrun_register_dbus_service (GtkWidget *dialog)
+{
+  static const struct DBusObjectPathVTable vtable = { NULL, (DBusObjectPathMessageFunction)xfrun_handle_dbus_message, NULL, NULL, NULL, NULL };
+
+  DBusConnection *connection;
+  DBusError       derror;
+
+  dbus_error_init (&derror);
+
+  /* Try connecting to the session bus */
+  connection = dbus_bus_get (DBUS_BUS_SESSION, &derror);
+
+  if (G_UNLIKELY (connection == NULL))
+    {
+      dbus_error_free (&derror);
+      return FALSE;
+    }
+
+  /* Register D-BUS connection with GLib main loop */
+  dbus_connection_setup_with_g_main (connection, NULL);
+
+  /* Try to request ownership of our D-BUS service */
+  if (dbus_bus_request_name (connection, RUNDIALOG_DBUS_SERVICE, 0, &derror) < 0)
+    {
+      dbus_error_free (&derror);
+      return FALSE;
+    }
+
+  /* Register the application object */
+  if (!dbus_connection_register_object_path (connection, RUNDIALOG_DBUS_PATH, &vtable, dialog))
+    return FALSE;
+
+  return TRUE;
+}
+#endif
+
 /* this stuff is here so i don't have to link to libxfcegui4 */
 
 static void
@@ -244,10 +453,14 @@
     gsize length = 0;
     
     histfile = xfce_resource_lookup(XFCE_RESOURCE_CACHE, "xfce4/xfrun4/history");
+
     if(histfile && g_file_get_contents(histfile, &contents, &length, NULL)) {
         lines = g_strsplit(contents, "\n", -1);
         g_free(contents);
     }
+
+    if (G_LIKELY (histfile != NULL))
+      g_free (histfile);
     
     return lines;
 }
@@ -255,10 +468,10 @@
 static gboolean
 xfrun_key_press(GtkWidget *widget,
                 GdkEventKey *evt,
-                gpointer user_data)
+                GtkWidget *window)
 {
     if(evt->keyval == GDK_Escape) {
-        gtk_main_quit();
+        xfrun_quit(window);
         return TRUE;
     }
     
@@ -490,7 +703,7 @@
     gscreen = gtk_widget_get_screen(widget);
     if(xfrun_exec_on_screen(gscreen, command, in_terminal, &error)) {
         xfrun_add_to_history(command, in_terminal);
-        gtk_main_quit();
+        xfrun_quit(xfrun_dialog->window);
     } else {
         gchar *primary = g_strdup_printf(_("The command \"%s\" failed to run:"),
                                          command);
@@ -557,36 +770,33 @@
     return GTK_TREE_MODEL(ls);
 }
 
-int
-main(int argc,
-     char **argv)
+GtkWidget*
+xfrun_setup(int argc, 
+            char **argv)
 {
-    XfrunDialog xfrun_dialog;
+    xfrun_dialog = g_malloc (sizeof (XfrunDialog));
+
     gchar title[8192], *first_item;
     GtkWidget *win, *entry, *chk, *btn, *vbox, *bbox, *hbox, *arrow;
     GtkEntryCompletion *completion;
     GtkTreeModel *completion_model;
     GtkTreeIter itr;
     
-    xfce_textdomain(GETTEXT_PACKAGE, LOCALEDIR, "UTF-8");
-    
-    gtk_init(&argc, &argv);
-    
     if(argc >= 2) {
         g_snprintf(title, 8192, _("Open %s with what program?"), argv[1]);
-        xfrun_dialog.run_argument = argv[1];
+        xfrun_dialog->run_argument = argv[1];
     } else {
         g_strlcpy(title, _("Run program"), 8192);
-        xfrun_dialog.run_argument = NULL;
+        xfrun_dialog->run_argument = NULL;
     }
     
-    xfrun_dialog.window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    xfrun_dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
     gtk_window_set_title(GTK_WINDOW(win), title);
     gtk_widget_set_size_request(win, 400, -1);
     g_signal_connect(G_OBJECT(win), "delete-event",
-                     G_CALLBACK(gtk_main_quit), NULL);
+                     G_CALLBACK(xfrun_quit), xfrun_dialog->window);
     g_signal_connect(G_OBJECT(win), "key-press-event",
-                     G_CALLBACK(xfrun_key_press), NULL);
+                     G_CALLBACK(xfrun_key_press), xfrun_dialog->window);
     
     vbox = gtk_vbox_new(FALSE, BORDER/2);
     gtk_container_set_border_width(GTK_CONTAINER(vbox), BORDER);
@@ -598,21 +808,21 @@
     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
     
     completion = gtk_entry_completion_new();
-    xfrun_dialog.completion_model = completion_model = xfrun_create_completion_model(&xfrun_dialog);
+    xfrun_dialog->completion_model = completion_model = xfrun_create_completion_model(xfrun_dialog);
     gtk_entry_completion_set_model(completion, completion_model);
     gtk_entry_completion_set_text_column(completion, XFRUN_COL_COMMAND);
     gtk_entry_completion_set_popup_completion(completion, TRUE);
     gtk_entry_completion_set_inline_completion(completion, TRUE);
     g_signal_connect(G_OBJECT(completion), "match-selected",
-                     G_CALLBACK(xfrun_match_selected), &xfrun_dialog);
+                     G_CALLBACK(xfrun_match_selected), xfrun_dialog);
     
-    xfrun_dialog.entry = entry = gtk_entry_new();
+    xfrun_dialog->entry = entry = gtk_entry_new();
     gtk_entry_set_completion(GTK_ENTRY(entry), completion);
     gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
     gtk_widget_show(entry);
     gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
     g_signal_connect(G_OBJECT(entry), "focus-out-event",
-                     G_CALLBACK(xfrun_entry_focus_out), &xfrun_dialog);
+                     G_CALLBACK(xfrun_entry_focus_out), xfrun_dialog);
     
     if(gtk_tree_model_get_iter_first(completion_model, &itr)) {
         gtk_tree_model_get(completion_model, &itr,
@@ -624,18 +834,18 @@
         }
     }
     
-    xfrun_dialog.arrow_btn = btn = gtk_button_new();
+    xfrun_dialog->arrow_btn = btn = gtk_button_new();
     gtk_container_set_border_width(GTK_CONTAINER(btn), 0);
     gtk_widget_show(btn);
     gtk_box_pack_start(GTK_BOX(hbox), btn, FALSE, FALSE, 0);
     g_signal_connect(G_OBJECT(btn), "clicked",
-                     G_CALLBACK(xfrun_menu_button_clicked), &xfrun_dialog);
+                     G_CALLBACK(xfrun_menu_button_clicked), xfrun_dialog);
     
     arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
     gtk_widget_show(arrow);
     gtk_container_add(GTK_CONTAINER(btn), arrow);
     
-    xfrun_dialog.terminal_chk = chk = gtk_check_button_new_with_mnemonic(_("Run in _terminal"));
+    xfrun_dialog->terminal_chk = chk = gtk_check_button_new_with_mnemonic(_("Run in _terminal"));
     gtk_widget_show(chk);
     gtk_box_pack_start(GTK_BOX(vbox), chk, FALSE, FALSE, 0);
     
@@ -649,7 +859,7 @@
     gtk_widget_show(btn);
     gtk_box_pack_end(GTK_BOX(bbox), btn, FALSE, FALSE, 0);
     g_signal_connect(G_OBJECT(btn), "clicked",
-                     G_CALLBACK(gtk_main_quit), NULL);
+                     G_CALLBACK(xfrun_quit), xfrun_dialog->window);
     
     btn = xfrun_create_mixed_button(GTK_STOCK_EXECUTE, _("_Run"));
     gtk_widget_show(btn);
@@ -657,13 +867,49 @@
     GTK_WIDGET_SET_FLAGS(btn, GTK_CAN_DEFAULT);
     gtk_widget_grab_default(btn);
     g_signal_connect(G_OBJECT(btn), "clicked",
-                     G_CALLBACK(xfrun_run_clicked), &xfrun_dialog);
+                     G_CALLBACK(xfrun_run_clicked), xfrun_dialog);
     
     xfrun_gtk_window_center_on_monitor_with_pointer(GTK_WINDOW(win));
-    gtk_widget_show(win);
-    gtk_widget_set_size_request(win, -1, -1);
     
-    gtk_main();
-    
-    return 0;
+    return xfrun_dialog->window;
 }
+
+int main(int argc,
+         char **argv)
+{
+  GtkWidget *dialog;
+  
+  xfce_textdomain (GETTEXT_PACKAGE, LOCALEDIR, "UTF-8");
+
+#ifdef HAVE_DBUS
+  if (argc == 1) 
+    {
+      if (xfrun_remote_dialog (argc, argv))
+        return EXIT_SUCCESS;
+    }
+  else if (argc >= 2 && !strcmp ("--daemon", argv[1])) 
+    {
+      daemon_mode = TRUE;
+      argc = 1;
+    }
+#endif
+
+  gtk_init (&argc, &argv);
+
+  dialog = xfrun_setup (argc, argv);
+
+#ifdef HAVE_DBUS
+  if (daemon_mode)
+    {
+      if (xfrun_register_dbus_service (dialog) == FALSE)
+        return EXIT_FAILURE;
+    }
+#endif
+
+  if (!daemon_mode)
+    xfrun_dialog_show (dialog);
+
+  gtk_main ();
+
+  return EXIT_SUCCESS;
+}
Index: xfce-utils/xfrun/Makefile.am
===================================================================
--- xfce-utils/xfrun/Makefile.am	(revision 20510)
+++ xfce-utils/xfrun/Makefile.am	(working copy)
@@ -1,12 +1,28 @@
 bin_PROGRAMS =  xfrun4
 
-xfrun4_SOURCES = xfrun.c
+xfrun4_SOURCES = \
+	xfrun.c 
 
 xfrun4_CFLAGS = \
 	@LIBXFCE4UTIL_CFLAGS@ \
 	@GTK_CFLAGS@ \
+	@DBUS_CFLAGS@ \
+	-DDBUS_API_SUBJECT_TO_CHANGE \
 	-DLOCALEDIR=\"$(localedir)\"
 
 xfrun4_LDADD = \
 	@LIBXFCE4UTIL_LIBS@ \
-	@GTK_LIBS@
+	@GTK_LIBS@ \
+	@DBUS_LIBS@
+
+if HAVE_DBUS
+servicedir = $(datadir)/dbus-1/services
+service_in_files = org.xfce.RunDialog.service.in
+service_DATA = $(service_in_files:.service.in=.service)
+%.service: %.service.in
+	sed -e "s,\@bindir\@,$(bindir),g" < $< > $@
+DISTCLEANFILES = \
+	$(service_DATA)
+EXTRA_DIST = \
+	$(service_in_files)
+endif
Index: xfce-utils/configure.ac
===================================================================
--- xfce-utils/configure.ac	(revision 20505)
+++ xfce-utils/configure.ac	(working copy)
@@ -95,6 +95,10 @@
 XDT_CHECK_PACKAGE([LIBXFCE4UTIL], [libxfce4util-1.0], [4.3.0])
 XDT_CHECK_PACKAGE([LIBXFCEGUI4], [libxfcegui4-1.0], [4.3.0])
 
+dnl Check for optional packages
+XDT_CHECK_OPTIONAL_PACKAGE([DBUS], [dbus-glib-1], 
+                           [0.33], [dbus], [D-BUS support])
+
 dnl configure the mcs plugin
 XDT_XFCE_MCS_PLUGIN([XFCE_MCS_MANAGER], [4.3.0])
Comment 6 Brian J. Tarricone (not reading bugmail) 2006-03-27 14:10:39 CEST
Um, please don't spam this bug with copies of the patches.
Comment 7 Brian J. Tarricone (not reading bugmail) 2006-04-06 00:07:59 CEST
No wonder I forgot about this...
Comment 8 Brian J. Tarricone (not reading bugmail) 2006-04-21 08:48:36 CEST
All right, finally getting a chance to look at this.  We have two options here:

1.  I give my comments and someone fixes the patch until I'm happy.
2.  I take what we have now and fix it myself.

#2 might take a while, because I'm busy.  So I'll proceed assuming #1, and if I happen to get to it before someone else does, that's fine too.

The problems:

1.  Use my coding style.  4-space indents, no tabs at all.  Braces on the same if/else/while/for line.  No extra indents for braces.  Braces for function definitions go on the next line.  No spaces between function calls and the opening parenthesis.  Same thing for if/else/while/for.

2.  Chill out on the comments.  No need to comment virtually every single line of code.  If "xfrun_dialog_show(dialog);" isn't obvious enough, we have a problem.

3.  Daemon mode:  First try to use the daemon() libc call (check for it in configure.ac first).  If we don't have that, fork(), and the child calls setsid() and becomes the daemon process, while the parent just _exit()s (note the leading underscore on _exit).

4.  Daemon mode should be the default.  The --no-daemon option will give the old behavior.  Obviously, if we don't HAVE_DBUS, or connecting to the session bus fails, we just display the dialog and exit afterwards.

5.  This I can do myself, but so I remember: now that we're doing daemon mode, let's ditch the crap I yanked out of libxfcegui4 (etc.) to make the code size slightly smaller.  We can live with the extra teeny amount of time to link to libgui.

This is extra credit.  I may do it myself after the rest of the patch is done:

6.  Break the functionality into two binaries: xfrun4 and xfrun4-daemon (which gets put in $(libexecdir)).  Assuming DBUS is present, xfrun4 tries to see if the service is available, and if not, fork()s and exec()s xfrun4-daemon, and exits; if the service is available, it just calls it.  xfrun4-daemon also needs a commandline switch to not run as a daemon, so xfrun4 can launch xfrun4-daemon to show the dialog if DBUS isn't available, or if the user passes --no-daemon to xfrun4.
(Hell, if we could assume DBUS is always available, xfrun4 could be a bash script.  Oh well.)

This #6 might be a bit much for now: it's easier and less error-prone to just do it with one binary, and I'd rather not add risk since we're in beta.
Comment 9 Jannis Pohlmann editbugs 2006-04-21 09:24:51 CEST
Okay, I can work on this later today.

1.-2.: No problem.
3. I'll need to read some docs about this, but it sounds doable
4. Ok
5. Ok, I'll leave it out for now.
6. Yep, later perhaps.
Comment 10 Brian J. Tarricone (not reading bugmail) 2006-07-15 21:20:32 CEST
Guys, just FYI, I'm working on this again now.  Should have it done by the end of the day, I think.  Decided on a slightly different way of implementing it.
Comment 11 Brian J. Tarricone (not reading bugmail) 2006-07-16 10:15:08 CEST
Implemented with svn rev 22460.

Bug #1598

Reported by:
Jannis Pohlmann
Reported on: 2006-03-21
Last modified on: 2009-07-14

People

Assignee:
Brian J. Tarricone (not reading bugmail)
CC List:
2 users

Version

Attachments

Header with D-BUS service description (335 bytes, text/plain)
2006-03-21 21:42 CET , Jannis Pohlmann
no flags
Incomplete patch for xfrun (9.55 KB, patch)
2006-03-21 21:43 CET , Jannis Pohlmann
no flags
D-BUS service desktop file (71 bytes, text/plain)
2006-03-21 21:44 CET , Jannis Pohlmann
no flags
Patch for adding org.xfce.RunDialog capabilities to xfrun4 (14.80 KB, patch)
2006-03-27 13:47 CEST , Jannis Pohlmann
no flags

Additional information